3

私はおそらくここで痛々しいほど明らかな何かを見逃していますが、答えを見つけることも、自分で解決することもできないようです. Sinatra には、self.getブロックをキャプチャするメソッドがあり、ブロックが呼び出されたときにrequest内部で変数を使用できますが、これはどのように可能ですか?

シナトラ

module Sinatra
  class Base
    class Request < Rack::Request   
    end

    attr_accessor :request

    def call!(env)
      @request = Request.new(env)
    end

    class << self
      def get(path, opts = {}, &block)
        ...
      end
    end
  end
end

アプリ

class App < Sinatra::Base
  get '/' do
    puts request
  end
end
4

1 に答える 1

5

わお。あなたは私の好奇心をそそりました、そして確かに、これを研究することは魅力的でした. 魔法は、httpscompile! ://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1529 で定義されているメソッドから始まります。

def compile!(verb, path, block, options = {})
  options.each_pair { |option, args| send(option, *args) }
  method_name = "#{verb} #{path}"
  unbound_method = generate_method(method_name, &block)
  pattern, keys = compile path
  conditions, @conditions = @conditions, []

  wrapper = block.arity != 0 ?
    proc { |a,p| unbound_method.bind(a).call(*p) } :
    proc { |a,p| unbound_method.bind(a).call }
  wrapper.instance_variable_set(:@route_name, method_name)

  [ pattern, keys, conditions, wrapper ]
end

(または任意のルート関数) に渡されたブロックを、getgenerate_method (上記の数行で定義) を介してバインドされていないメソッドに変換していることに注意してください。次に、メソッドをバインドするオブジェクトと、メソッドが呼び出される引数のリストの 2 つのパラメーターを取る proc を格納します。

先にスキップしてくださいprocess_route: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L971

def process_route(pattern, keys, conditions, block = nil, values = [])
  route = @request.path_info
  route = '/' if route.empty? and not settings.empty_path_info?
  return unless match = pattern.match(route)
  values += match.captures.to_a.map { |v| force_encoding URI.unescape(v) if v }

  if values.any?
    original, @params = params, params.merge('splat' => [], 'captures' => values)
    keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
  end

  catch(:pass) do
    conditions.each { |c| throw :pass if c.bind(self).call == false }
    block ? block[self, values] : yield(self, values)
  end
ensure
  @params = original if original
end

ここでは多くのことが行われていますが、重要なのは次のとおりです。

block[self, values]

これは、上記の格納されたブロックを self と適切な引数で呼び出します。したがって、バインドされていないメソッドは、バインドされてprocess_routeいるもの (現在のselfin process_route) にバインドされます。そして、何にprocess_route縛られていますか?Sinatra::Base私たちが知っているrequestように、元のブロックで到達できる属性アクセサーを持つのインスタンス。多田!

于 2013-05-15T01:34:04.423 に答える