Menggunakan Sinatra untuk proyek yang lebih besar melalui banyak file


184

Tampaknya di Sinatra semua penangan rute sedang ditulis ke dalam satu file, jika saya mengerti benar itu bertindak sebagai pengontrol satu besar / kecil. Apakah ada cara untuk membaginya menjadi file independen yang terpisah, jadi ketika katakanlah seseorang memanggil "/" - satu tindakan dijalankan, dan jika sesuatu seperti "/ posting / 2" diterima maka tindakan lain - logika serupa yang diterapkan dalam PHP ?

Jawaban:


394

Ini adalah template dasar untuk aplikasi Sinatra yang saya gunakan. (Aplikasi saya yang lebih besar memiliki 200+ file yang pecah seperti ini, tidak termasuk permata vendor'd, yang mencakup 75-100 rute eksplisit. Beberapa rute ini adalah rute Regexp yang mencakup 50+ pola rute tambahan.) Saat menggunakan Thin, Anda menjalankan aplikasi seperti ini menggunakan:
thin -R config.ru start

Sunting : Saya sekarang memelihara kerangka Biksu saya sendiri berdasarkan Riblits di bawah ini . Untuk menggunakannya untuk menyalin template saya sebagai dasar untuk proyek Anda sendiri:

# Before creating your project
monk add riblits git://github.com/Phrogz/riblits.git

# Inside your empty project directory
monk init -s riblits

Layout File:

config.ru
app.rb
pembantu /
  init.rb
  partials.rb
model /
  init.rb
  user.rb
rute /
  init.rb
  login.rb
  main.rb
dilihat /
  layout.haml
  login.haml
  main.haml

 
config.ru

root = ::File.dirname(__FILE__)
require ::File.join( root, 'app' )
run MyApp.new

 
app.rb

# encoding: utf-8
require 'sinatra'
require 'haml'

class MyApp < Sinatra::Application
  enable :sessions

  configure :production do
    set :haml, { :ugly=>true }
    set :clean_trace, true
  end

  configure :development do
    # ...
  end

  helpers do
    include Rack::Utils
    alias_method :h, :escape_html
  end
end

require_relative 'models/init'
require_relative 'helpers/init'
require_relative 'routes/init'

 
helpers / init.rb

# encoding: utf-8
require_relative 'partials'
MyApp.helpers PartialPartials

require_relative 'nicebytes'
MyApp.helpers NiceBytes

 
pembantu / bagian.rb

# encoding: utf-8
module PartialPartials
  def spoof_request(uri,env_modifications={})
    call(env.merge("PATH_INFO" => uri).merge(env_modifications)).last.join
  end

  def partial( page, variables={} )
    haml page, {layout:false}, variables
  end
end

 
pembantu / nicebytes.rb

# encoding: utf-8
module NiceBytes
  K = 2.0**10
  M = 2.0**20
  G = 2.0**30
  T = 2.0**40
  def nice_bytes( bytes, max_digits=3 )
    value, suffix, precision = case bytes
      when 0...K
        [ bytes, 'B', 0 ]
      else
        value, suffix = case bytes
          when K...M then [ bytes / K, 'kiB' ]
          when M...G then [ bytes / M, 'MiB' ]
          when G...T then [ bytes / G, 'GiB' ]
          else            [ bytes / T, 'TiB' ]
        end
        used_digits = case value
          when   0...10   then 1
          when  10...100  then 2
          when 100...1000 then 3
          else 4
        end
        leftover_digits = max_digits - used_digits
        [ value, suffix, leftover_digits > 0 ? leftover_digits : 0 ]
    end
    "%.#{precision}f#{suffix}" % value
  end
  module_function :nice_bytes  # Allow NiceBytes.nice_bytes outside of Sinatra
end

 
models / init.rb

# encoding: utf-8
require 'sequel'
DB = Sequel.postgres 'dbname', user:'bduser', password:'dbpass', host:'localhost'
DB << "SET CLIENT_ENCODING TO 'UTF8';"

require_relative 'users'

 
model / user.rb

# encoding: utf-8
class User < Sequel::Model
  # ...
end

 
rute / init.rb

# encoding: utf-8
require_relative 'login'
require_relative 'main'

 
rute / login.rb

# encoding: utf-8
class MyApp < Sinatra::Application
  get "/login" do
    @title  = "Login"
    haml :login
  end

  post "/login" do
    # Define your own check_login
    if user = check_login
      session[ :user ] = user.pk
      redirect '/'
    else
      redirect '/login'
    end
  end

  get "/logout" do
    session[:user] = session[:pass] = nil
    redirect '/'
  end
end

 
rute / main.rb

# encoding: utf-8
class MyApp < Sinatra::Application
  get "/" do
    @title = "Welcome to MyApp"        
    haml :main
  end
end

 
views / layout.haml

!!! XML
!!! 1.1
%html(xmlns="http://www.w3.org/1999/xhtml")
  %head
    %title= @title
    %link(rel="icon" type="image/png" href="/favicon.png")
    %meta(http-equiv="X-UA-Compatible" content="IE=8")
    %meta(http-equiv="Content-Script-Type" content="text/javascript" )
    %meta(http-equiv="Content-Style-Type" content="text/css" )
    %meta(http-equiv="Content-Type" content="text/html; charset=utf-8" )
    %meta(http-equiv="expires" content="0" )
    %meta(name="author" content="MeWho")
  %body{id:@action}
    %h1= @title
    #content= yield

11
Satu hal yang sangat menyenangkan tentang struktur di atas — secara khusus memasukkan require "sequel"dan DBinisialisasi models/init.rb, dan menggunakan require_relativeuntuk semua file — adalah bahwa Anda dapat melakukan cd ke modelsdirektori Anda , buka konsol dan jenis IRB require './init'dan Anda memiliki database lengkap dan pengaturan model yang dimuat untuk eksplorasi interaktif .
Phrogz

1
Contoh struktur yang bagus, cocok untuk pemula Sinatra seperti saya, tepuk tangan.
Barry Jordan

27
Saya menggunakan pendekatan yang berbeda. Kode semua logika bisnis seperti pengguna dan layanan di ruby, tanpa memerlukan 'sinatra'. Ini membuat logika berdiri sendiri. Lalu saya menggunakan file aplikasi tunggal untuk membagikan tanggung jawab ke berbagai kelas, jadi sekitar 3 baris kode per rute. Tidak ada banyak rute dalam aplikasi biasa, jadi file aplikasi saya sebenarnya tidak terlalu panjang.
Tom Andersen

1
Apakah ini praktik umum untuk mendefinisikan kelas dalam banyak file? Anda mendefinisikan ulang 'MyApp' berulang-ulang di setiap file. Saya baru rubi jadi sepertinya aneh bagi saya. Apa alasan di balik ini?
0xSina

5
@ 0xSina Tidak jarang di Ruby. Anda tidak "mendefinisikan" suatu kelas, Anda "membukanya kembali". Sebagai contoh, Arraykelas didefinisikan oleh pustaka inti, tetapi nanti Anda bisa "monkeypatch" dengan menggunakan class Array; def some_awesome_method; enddan a) semua fungsi Array sebelumnya dipertahankan, dan b) semua instance Array akan mendapatkan kode baru Anda. Kelas di Ruby hanyalah objek, dan dapat ditambah dan diubah kapan saja.
Phrogz

10

Benar. Untuk melihat contohnya, saya sarankan mengunduh permata Monk, yang dijelaskan di sini:

https://github.com/monkrb/monk

Anda dapat 'menginstalnya' melalui rubygems.org. Setelah Anda mendapatkan permata, buat aplikasi sampel menggunakan instruksi yang ditautkan di atas.

Perhatikan bahwa Anda tidak harus menggunakan Monk untuk pengembangan Anda yang sebenarnya kecuali Anda mau (sebenarnya saya pikir itu mungkin tidak terkini). Intinya adalah untuk melihat bagaimana Anda dapat dengan mudah menyusun aplikasi Anda dalam gaya MVC (dengan file rute seperti-controller terpisah) jika Anda mau.

Ini cukup sederhana jika Anda melihat bagaimana Monk menanganinya, sebagian besar masalah memerlukan file di direktori terpisah, sesuatu seperti (Anda harus mendefinisikan root_path):

Dir[root_path("app/**/*.rb")].each do |file|
    require file
end

7
Satu hal yang baik tentang menggunakan eksplisit init.rbversus di atas adalah bahwa Anda dapat mengontrol urutan pemuatan, jika Anda memiliki file yang saling tergantung.
Phrogz

10

Lakukan pencarian Google untuk "boilerplate Sinatra" untuk mendapatkan beberapa ide bagaimana orang lain menyusun aplikasi Sinatra mereka. Dari itu Anda mungkin dapat menemukan satu yang sesuai dengan kebutuhan Anda atau hanya membuatnya sendiri. Tidak terlalu sulit untuk dilakukan. Saat Anda mengembangkan lebih banyak aplikasi Sinatra, Anda dapat menambahkan ke boilerplate Anda.

Inilah yang saya buat dan gunakan untuk semua proyek saya:

https://github.com/rziehl/sinatra-boilerplate


7

Saya tahu ini adalah permintaan lama tapi saya masih tidak percaya tidak ada yang menyebut Padrino. Anda dapat menggunakannya sebagai kerangka kerja di atas Sinatra, atau sedikit demi sedikit hanya menambahkan permata yang menarik bagi Anda. Itu menendang sepuluh pantat pantat!


Saya setuju, Anda harus melihat Padrino, itu batu!
NicoPaez

2

Pendekatan saya untuk meng-host berbagai proyek di situs yang sama adalah dengan menggunakan sinatra/namespacecara seperti itu:

server.rb

require "sinatra"
require "sinatra/namespace"

if [ENV["LOGNAME"], ENV["USER"]] == [nil, "naki"]
    require "sinatra/reloader"
    register Sinatra::Reloader
    set :port, 8719
else
    set :environment, :production
end

for server in Dir.glob "server_*.rb"
    require_relative server
end

get "/" do
    "this route is useless"
end

server_someproject.rb

module SomeProject
    def self.foo bar
       ...
    end
    ...
end

namespace "/someproject" do
    set :views, settings.root
    get "" do
        redirect request.env["REQUEST_PATH"] + "/"
    end
    get "/" do
        haml :view_someproject
    end
    post "/foo" do
        ...
        SomeProject.foo ...
    end
end

view_someproject.haml

!!!
%html
    ...

Detail lain tentang subproyek yang saya gunakan adalah menambahkan nama, deskripsi, dan rute mereka ke beberapa jenis variabel global, yang digunakan oleh "/"untuk membuat beranda panduan, tetapi saya tidak memiliki cuplikan sekarang.


1

Baca dokumen di sini:

Ekstensi Sinatra

Tampaknya Sinatra memungkinkan Anda untuk menguraikan aplikasi Anda menjadi Modul Ruby, yang dapat ditarik melalui metode "daftar" Sinatra atau metode "pembantu", seperti:

helpers.rb

require 'sinatra/base'

module Sinatra
  module Sample
    module Helpers

      def require_logged_in()
        redirect('/login') unless session[:authenticated]
      end

    end
  end
end

routing / foos.rb

require 'sinatra/base'

module Sinatra
  module Sample
    module Routing
      module Foos

        def self.registered(app)           
          app.get '/foos/:id' do
            # invoke a helper
            require_logged_in

            # load a foo, or whatever
            erb :foos_view, :locals => { :foo => some_loaded_foo }
          end   
        end  

      end
    end     
  end
end

app.rb

#!/usr/bin/env ruby

require 'sinatra'

require_relative 'routing/foos'

class SampleApp < Sinatra::Base

  helpers Sinatra::Sample::Helpers

  register Sinatra::Sample::Routing::Foos

end

1

Ketika Monk tidak bekerja untuk saya, saya mulai mengerjakan template sendiri.

Jika Anda memikirkannya, tidak ada yang istimewa tentang mengikat satu set file. Filosofi biksu dijelaskan kepada saya di awal 2011 selama RedDotRubyConf dan mereka secara khusus mengatakan kepada saya bahwa itu benar-benar opsional untuk menggunakannya terutama sekarang karena itu hampir tidak terawat.

Ini adalah awal yang baik bagi mereka yang ingin menggunakan ActiveRecord:

MVC Sinatra sederhana

https://github.com/katgironpe/simple-sinatra-mvc


1

Kunci untuk modularitas pada Sinatra untuk proyek yang lebih besar adalah belajar menggunakan alat yang mendasarinya.

SitePoint memiliki tutorial yang sangat bagus dari mana Anda dapat melihat aplikasi dan bantuan Sinatra modular. Namun Anda harus memberi perhatian khusus pada satu detail penting. Anda menyimpan beberapa aplikasi Sinatra dan memasangnya dengan Rackup. Setelah Anda tahu cara menulis aplikasi dasar, lihat file config.ru dari tutorial itu dan amati bagaimana mereka memasang aplikasi independen Sinatra.

Setelah Anda belajar menjalankan Sinatra dengan Rack, dunia baru dari strategi modularitas akan terbuka. Ini jelas mengundang untuk mencoba sesuatu yang sangat berguna: sekarang Anda dapat mengandalkan memiliki Permata individual untuk setiap sub aplikasi , yang mungkin memungkinkan Anda untuk membuat versi modul Anda dengan mudah.

Jangan meremehkan kekuatan menggunakan modul-permata untuk aplikasi Anda. Anda dapat dengan mudah menguji perubahan eksperimental di lingkungan yang dibatasi dengan baik dan mudah menggunakannya. Sama mudahnya untuk mengembalikan kembali jika terjadi kesalahan.

Ada ribuan cara untuk mengatur kode Anda, jadi tidak ada salahnya mencoba untuk mendapatkan tata letak yang mirip dengan Rails. Namun ada juga beberapa posting yang bagus tentang bagaimana menyesuaikan struktur Anda sendiri. Posting itu mencakup kebutuhan sering lainnya dari kebanyakan pengembang web.

Jika Anda punya waktu, saya anjurkan Anda untuk mempelajari lebih lanjut tentang Rack, landasan bersama untuk semua aplikasi web berbasis Ruby. Ini mungkin memiliki dampak yang jauh lebih rendah dalam bagaimana Anda melakukan pekerjaan Anda, tetapi selalu ada tugas-tugas tertentu yang kebanyakan orang lakukan di aplikasi mereka yang lebih cocok sebagai middleware Rack.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.