最近读 Sinatra 源码受益良多,文章中有问题的地方还请多多指教
Sinatra 通过helpers
和register
函数进行扩展
首先看一下helpers
函数,用来是来扩展 Base 类的实例方法。
def helpers(*extensions, &block)
class_eval(&block) if block_given?
include(*extensions) if extensions.any?
end
在看一下register
函数,用来扩展 Base 类的类方法
def register(*extensions, &block)
extensions << Module.new(&block) if block_given?
@extensions += extensions
extensions.each do |extension|
extend extension
extension.registered(self) if extension.respond_to?(:registered)
end
end
两个方法都支持通过代码块扩展,也支持通过 Module 来扩展。
两种扩展方法对应 Sinatra 的两种编程风格。
Sinatra 编程的两种风格:
经典风格:
require'sinatra'
get '/' do
"hello world"
end
模块化风格:
require'sinatra/base'
class Hello < Sinatra::Base
get "/" do
"hello world"
end
run!
end
为两种风格编写扩展:
Sinatra 扩展也分为两种:
helper 型 dsl 型
helper 型:
require 'sinatra/base'
module Sinatra
module FormatHelper
def escape_html(text)
Rack::Utils.escape_html(text)
end
end
helpers FormatHelper
end
classic style 使用 extension
require 'sinatra'
get '/' do
escape_html("x > y")
end
modular style 使用 extension
require 'sinatra/base'
class Hello < Sinatra::Base
helpers Sinatra::FormatHelper
get '/' do
escape_html("x > y")
end
run!
end
这里的helpers
其实相当于include
dsl 型:
require 'sinatra/base'
module Sinatra
module Devise
def authenticate!
before {
halt 403, "You Bastards!"
}
end
end
register Devise
end
classic style 使用 dsl extension
require 'sinatra'
authenticate!
get '/' do
escape_html("x > y")
end
modular style 使用 dsl extension
require 'sinatra/base'
class Hello < Sinatra::Base
register Sinatra::Devise
authenticate!
get '/' do
"hello world"
end
run!
end
上面就是两种扩展的用法,我们在深入一点
在Sinatra/main
的最后几行可以看到extend Sinatra::Delegator
,其实当 require 'sinatra'
的时候就是执行的上面那行代码,作为整个 Sinatra 的入口。
看一下Delegator
模块,默认的 target 是Application
,也就是helpers
和register
等 Sinatra 的一干关键字get post ...
等都是通过动态派发给Application
类执行的。
module Delegator
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, :link, :unlink,
: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
Delegator
模块功能就是定义派发给Application
的函数(不管实现,只是转发)
整个通过Delegator.delegate
注册的方法的实现都在Helper
这个模块里,在Base
类里include
各种方法的实现,然后Application
在继承Base
,有点绕~。
class Base
include Rack::Utils
include Helpers
include Templates
...
end
通过一张图理清楚他们的关系
定义自己的关键字,像get post
这样。
扩展方法首先在Delegator
模块调用delegate
方法的时候添加你扩展的关键字名称,类似delegate :get, :patch, ... , :my_fun
, 然后在Helper
模块里定义my_fun
的实现
module Helpers
my_fun
p "my_fun"
end
...
end
这样就可以像关键字一样使用 my_fun 了,当然也可以通过上面介绍的两个方法进行扩展。
参考 https://ruby-china.org/topics/2110 http://saito.im/note/Sinatra-Extensions/