最简单的 sinatra 程序是这样的:
# app.rb
require 'sinatra'
get '/' do
'Hello world!'
end
只需要ruby app.rb
就可以运行,居然不需要任何启动脚本。在哪里启动的 app server?
# https://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25
at_exit { Application.run! if $!.nil? && Application.run? }
魔法就在上面那句代码里。上面这句代码是在 app.rb 里require "sinatra"
的时候被执行的。
所以启动流程应该是这样的:
ruby app.rb
at_exit
事件at_exit
回调里内容被执行:Application.run!
Rack::Handler
,然后启动服务器(https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1408-L1425)initialize(app)
理解了上面这些就自然明白了为什么可以被 Rack::Handler run了。
关于 sinatra 的两种风格,请看 saito 同学的这篇:http://ruby-china.org/topics/2110
# https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1752-L1773
module Sinatra
module Delegator #:nodoc:
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
return super(*args, &block) if respond_to? method_name
Delegator.target.send(method_name, *args, &block)
end
private method_name
end
end
delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
:before, :after, :error, :not_found, :configure, :set, :mime_type,
:enable, :disable, :use, :development?, :test?, :production?,
:helpers, :settings, :register
class << self
attr_accessor :target
end
self.target = Application
end
end
模块化风格很好理解,use/get/post 这些方法是 Base 的类方法,下面写法一目了然:
require'sinatra/base'
class Hello < Sinatra::Base
get "/" do
"hello world"
end
run!
end
而经典模式的写法让人很难一眼看出 get/post/use/run!/set 这些方法是被定义在哪里的。关键一句在这里:https://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L30
extend Sinatra::Delegator
也就是说,经典风格是把 DSL 方法定义成Sinatra::Delegator
的类方法,然后 extend 给顶层对象(top level /main object),在顶层作用域调用 DSL 方法(get/set/post/use)的时候其实调用的是Sinatra::Delegator
的类方法,然后改变的是Delegator
的 target(Sinatra::Application).
经典风格其实是模块风格的一个特例。是一个只叫 Application 的模块,同时为了方便 DSL,在顶层对象上面委托了一些常用方法。理解了经典风格,模块化风格自然就明白了。
和 Rails 里的插件机制不同,扩展 Sinatra 不能简单的用 extend 和 include,而是对应的替换为 register 和 helpers.不修改 Base 的好处是多个模块间不会相互影响,代价是写扩展难度大了一点儿,不那么直观。
简单的说,sinatra 主要分两个作用域,一个叫DSL (class) Context
,另一个叫Request Context
。register 和 helpers 分别扩展这两个作用域。详细的看这里吧:http://www.sinatrarb.com/extensions.html
好吧,我发现 sinatra 最难理解的地方应该就是这两个作用域了。