Sinatra请求对象 [英] Sinatra Request Object

查看:94
本文介绍了Sinatra请求对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可能在这里缺少一些显而易见的东西,但我似乎找不到答案,或者自己动手。在Sinatra中,它们有一个 self.get 方法,捕获块,当一个块被调用时,你能够使用 request 变量里面,这是怎么回事?



Sinatra



  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



App



 类App< Sinatra :: Base 
get'/'do
请求
end
end


解决方案

哇。你引发了我的好奇心,并且肯定,研究这是很有意义的。魔术开始于 compile!方法中定义: https://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

注意我们把传递给 get )通过generate_method(定义上面的几行)到一个未绑定的方法。然后我们存储一个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?而不是settings.empty_path_info?
return unless match = pattern.match(route)
values + = match.captures.to_a.map {| v | force_encoding URI.unescape(v)if v}

如果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
$ b catch(:pass)do
conditions.each {| c | throw:pass if c.bind(self).call == false}
block?块自身,值]:yield(self,values)
end
确保
@params = original如果原来
end
/ pre>

这里有很多事情,但关键是:

  block [self,values] 

这会使用self调用上面存储的块,参数。因此,未绑定的方法绑定到任何 process_route 绑定到(当前 self process_route )。什么是 process_route 绑定到? Sinatra :: Base 的实例,我们知道它有一个属性访问器 request ,现在可以在原始块。 Tada!


I'm probably missing something painfully obvious here, but I can't seem to find an answer, or work it out myself. In Sinatra, they have a self.get method, which captures blocks, when a block is called, you're able to use the request variable inside, how is this possible?

Sinatra

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

App

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

解决方案

Wow. You piqued my curiousity, and sure enough, researching this was facinating. The magic starts in the compile! method defined at: https://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

Notice we turn the block passed to get (or any route function) into an unbound method via generate_method (defined a few lines above). We then store a proc which takes two parameters, an object to bind the method to, and a list of arguments, which the method is called with.

Skip ahead to 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

Theres a lot going on here, but the key is:

block[self, values]

This calls the stored block above with self, and the appropriate arguments. Thus the unbound method is bound to whatever process_route is bound to( the current self in process_route). And what is process_route bound to? An instance of Sinatra::Base, which as we know has an attribute accessor request that can now be reached in your original block. Tada!

这篇关于Sinatra请求对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆