Sử dụng Sinatra cho các dự án lớn hơn thông qua nhiều tệp


184

Dường như ở Sinatra, tất cả các trình xử lý tuyến đường đang được ghi vào một tệp duy nhất, nếu tôi hiểu đúng thì nó hoạt động như một bộ điều khiển lớn / nhỏ. Có cách nào để tách nó thành các tệp độc lập riêng biệt không, vì vậy khi giả sử ai đó gọi "/" - một hành động được thực thi và nếu nhận được "/ post / 2" thì một hành động khác - logic tương tự được áp dụng trong PHP ?

Câu trả lời:


394

Đây là một mẫu cơ bản cho các ứng dụng Sinatra mà tôi sử dụng. . ứng dụng như thế này bằng cách sử dụng:
thin -R config.ru start

Chỉnh sửa : Bây giờ tôi đang duy trì bộ xương Monk của riêng mình dựa trên Riblits dưới đây . Để sử dụng nó để sao chép mẫu của tôi làm cơ sở cho các dự án của riêng bạn:

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

# Inside your empty project directory
monk init -s riblits

Bố cục tệp:

cấu hình
ứng dụng
người giúp việc /
  init.rb
  partials.rb
mô hình /
  init.rb
  người dùng
tuyến đường /
  init.rb
  đăng nhập.rb
  chính.rb
lượt xem/
  bố trí.haml
  đăng nhập.haml
  chính.haml

 
cấu hình

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

 
ứng dụng

# 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'

 
người trợ giúp / init.rb

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

require_relative 'nicebytes'
MyApp.helpers NiceBytes

 
người trợ giúp / partials.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

 
người trợ giúp / 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

 
mô hình / 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'

 
mô hình / user.rb

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

 
tuyến đường / init.rb

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

 
tuyến đường / 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

 
tuyến đường / main.rb

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

 
lượt xem / 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
Một điều đặc biệt thú vị về cấu trúc trên, đặc biệt là cài đặt require "sequel"DBkhởi tạo models/init.rb, và sử dụng require_relativecho tất cả các tệp, đó là bạn có thể cd vào modelsthư mục của mình , mở bảng điều khiển IRB và nhập require './init'và bạn đã tải cơ sở dữ liệu và mô hình đầy đủ để khám phá tương tác .
Phrogz

1
Cấu trúc ví dụ tuyệt vời, hoàn hảo cho một noob Sinatra như tôi, chúc mừng.
Barry Jordan

27
Tôi đã sử dụng một cách tiếp cận khác. Mã tất cả logic kinh doanh như người dùng và dịch vụ trong ruby, không yêu cầu 'sinatra'. Điều này làm cho logic đứng trên chính nó. Sau đó, tôi sử dụng một tệp ứng dụng duy nhất để loại bỏ trách nhiệm cho các lớp khác nhau, do đó, khoảng 3 dòng mã trên mỗi tuyến. Không có nhiều tuyến đường trong ứng dụng thông thường, vì vậy tệp ứng dụng của tôi thực sự không quá dài.
Tom Andersen

1
Đây có phải là một cách phổ biến để định nghĩa một lớp trong nhiều tệp không? Bạn đang định nghĩa lại 'MyApp' nhiều lần trong mỗi tệp. Tôi mới làm quen với ruby ​​nên có vẻ lạ với tôi. Lý do đằng sau điều này là gì?
0xSina

5
@ 0xSina Không có gì lạ ở Ruby. Bạn không "định nghĩa" một lớp, bạn "mở lại". Ví dụ, Arraylớp được xác định bởi thư viện lõi, nhưng sau đó bạn có thể "bắt chước" bằng cách sử dụng class Array; def some_awesome_method; endvà a) tất cả chức năng Array trước đó được giữ nguyên và b) tất cả các phiên bản Array sẽ nhận được mã mới của bạn. Các lớp trong Ruby chỉ là các đối tượng và có thể được tăng cường và thay đổi bất cứ lúc nào.
Phrogz

10

Chắc chắn rồi. Để xem ví dụ về điều này, tôi khuyên bạn nên tải xuống Monk gem, được mô tả ở đây:

https://github.com/monkrb/monk

Bạn có thể 'cài đặt đá quý' thông qua rubygems.org. Khi bạn có đá quý, hãy tạo một ứng dụng mẫu bằng cách sử dụng các hướng dẫn được liên kết ở trên.

Lưu ý rằng bạn không phải sử dụng Monk cho sự phát triển thực tế của mình trừ khi bạn muốn (thực tế tôi nghĩ nó có thể không hiện tại). Vấn đề là để xem làm thế nào bạn có thể dễ dàng cấu trúc ứng dụng của mình theo kiểu MVC (với các tệp tuyến giống như bộ điều khiển) nếu bạn muốn.

Thật đơn giản nếu bạn nhìn vào cách Monk xử lý nó, chủ yếu là vấn đề yêu cầu các tệp trong các thư mục riêng biệt, đại loại như (bạn sẽ phải xác định root_path):

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

7
Một điều tuyệt vời khi sử dụng một tường minh init.rbso với ở trên là bạn có thể kiểm soát thứ tự tải, trong trường hợp bạn có các tệp phụ thuộc lẫn nhau.
Phrogz

10

Thực hiện tìm kiếm trên Google cho "Sinatra soạn sẵn" để có một số ý tưởng về cách những người khác đang trình bày các ứng dụng Sinatra của họ. Từ đó bạn có thể tìm thấy một cái phù hợp với nhu cầu của bạn hoặc đơn giản là làm cho riêng bạn. Nó không quá khó để làm. Khi bạn phát triển thêm ứng dụng Sinatra, bạn có thể thêm vào bản tóm tắt của mình.

Đây là những gì tôi đã thực hiện và sử dụng cho tất cả các dự án của mình:

https://github.com/rziehl/sinatra-boiler khắc


7

Tôi biết đây là một truy vấn cũ nhưng tôi vẫn không thể tin rằng không ai nhắc đến Padrino Bạn có thể sử dụng nó làm khung trên đỉnh Sinatra hoặc chỉ thêm những viên đá quý mà bạn quan tâm. Nó đá mười mông mông!


Tôi đồng ý, bạn nên xem Padrino, nó đá!
NicoPaez

2

Cách tiếp cận của tôi để lưu trữ các dự án khác nhau trên cùng một trang là sử dụng sinatra/namespacetheo cách đó:

máy chủ

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
    ...

Một chi tiết khác về các tiểu dự án tôi đã sử dụng là thêm tên, mô tả và tuyến đường của chúng vào một loại biến toàn cầu, được sử dụng "/"để tạo trang chủ hướng dẫn, nhưng hiện tại tôi không có đoạn trích.


1

Đọc tài liệu ở đây:

Phần mở rộng Sinatra

Dường như Sinatra cho phép bạn phân tách ứng dụng của mình thành Ruby Modules, có thể được kéo vào thông qua phương thức "đăng ký" Sinatra hoặc phương thức "người trợ giúp", như vậy:

người giúp đỡ.rb

require 'sinatra/base'

module Sinatra
  module Sample
    module Helpers

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

    end
  end
end

định tuyến / 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

ứng dụng

#!/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

Khi Monk không làm việc cho tôi, tôi bắt đầu tự mình làm việc trên các mẫu.

Nếu bạn nghĩ về nó, không có gì đặc biệt về việc buộc một tập hợp các tập tin. Triết lý nhà sư đã được giải thích cho tôi vào đầu năm 2011 trong RedDotRubyConf và họ đã đặc biệt nói với tôi rằng nó thực sự là tùy chọn để sử dụng nó đặc biệt là bây giờ nó hầu như không được duy trì.

Đây là một khởi đầu tốt cho những ai muốn sử dụng ActiveRecord:

MVC đơn giản

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


1

Chìa khóa cho tính mô đun trên Sinatra cho các dự án lớn hơn là học cách sử dụng các công cụ cơ bản.

SitePoint có một hướng dẫn rất tốt từ đó bạn có thể thấy các ứng dụng và trợ giúp mô-đun Sinatra. Tuy nhiên, bạn nên đặc biệt chú ý đến một chi tiết quan trọng. Bạn giữ nhiều ứng dụng Sinatra và gắn kết chúng với Rackup. Một khi bạn biết làm thế nào để viết một ứng dụng cơ bản, hãy nhìn vào config.ru tệp của hướng dẫn đó và quan sát cách chúng gắn các ứng dụng Sinatra độc lập.

Khi bạn học cách chạy Sinatra với Rack, một thế giới chiến lược mô đun hoàn toàn mới sẽ mở ra. Điều này rõ ràng mời gọi thử một cái gì đó thực sự hữu ích: bây giờ bạn có thể dựa vào việc có các viên Đá quý riêng cho mỗi ứng dụng phụ , những gì có thể cho phép bạn dễ dàng phiên bản các mô-đun của mình.

Đừng đánh giá thấp sức mạnh của việc sử dụng các mô-đun đá quý cho ứng dụng của bạn. Bạn có thể dễ dàng kiểm tra các thay đổi thử nghiệm trong một môi trường được phân định rõ ràng và dễ dàng triển khai chúng. Tương tự dễ dàng trở lại nếu có sự cố.

Có hàng ngàn cách để tổ chức mã của bạn, vì vậy sẽ không hại gì khi cố gắng có được bố cục tương tự như Rails. Tuy nhiên, cũng có một số bài viết tuyệt vời về cách tùy chỉnh cấu trúc của riêng bạn. Bài đăng đó bao gồm các nhu cầu thường xuyên khác của hầu hết các nhà phát triển web.

Nếu bạn có thời gian, tôi khuyến khích bạn tìm hiểu thêm về Rack, nền tảng chung cho bất kỳ ứng dụng web dựa trên Ruby nào. Nó có thể có tác động ít hơn nhiều trong cách bạn thực hiện công việc của mình, nhưng luôn có một số nhiệm vụ nhất định mà hầu hết mọi người thực hiện trên ứng dụng của họ phù hợp hơn với phần mềm trung gian Rack.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.