翻译来自 Sinatra: Up and Running。
在阅读这个专题之前,你应该已经对Sinatra的classic用法风格比较熟悉了。
现在我们更加深入地探讨一下Sinatra是如何运行的。一旦你了解其后台的原理,那么,充分利用可用的API甚至是拓展它就变得非常容易了。为了达到这个目标,我们会研究Sinatra的源代码,看看到底发生了什么。
提示
Sinatra遵循语义化版本规则,即它一直向下兼容除非主版本升级(版本的第一个数字变大)。因此,在Sinatra 1.2.3下写的程序,也可以运行在1.3.0的版本中。语义化版本控制要求一个官方且全面的API使用规范,在Sinatra里就是README,你可以在这里找到。 你可以在这里了解更多有关语义化版本。
#Application 与 Delegation
让我们从一个小实验开始。我们应该已经知道网页的参数是通过一个叫params的哈希表传递给routes,并被routes使用。简单的例子就是登录表单一般都会有params[:username] 和 params[:password]。
我们定义的各种routes(get ‘/home’,post ‘/login’, 等等)并不是一个函数定义,而是调用了更深层次的Sinatra函数。这些routes的做法,是以block或者叫closure的形式传递给Sinatra去处理和执行。
这样的话,检查这个block被执行的上下文将非常有趣。Ruby里的Block应该与其被定义的作用域具有相同的函数与变量。那么,例子3-1的输出会是什么呢?
Example 3-1. 检查params作用域的脚本
如上所示,params只是在get里面才可见。这也揭示了Sinatra运作的秘密就是它处理self变量的方式。
###内部的Self
在Ruby里,没被赋给变量或常量的函数调用,都传给了self。一般来说为了简洁,self都被隐藏了。在例子3-2中,我们调用jobs和self.jobs;输出相同是因为这些调用发生在用一个作用域里面,因而self也是相同的。
Example 3-2. 展示self的一种用法
当要通过scope gates的时候,self的唯一性就变得非常重要了;scope gates是指在执行代码中,作用域发生改变的代码片段,往往self也会相应改变。在Ruby里,定义类,定义模块和函数都是scope gates。
例子3-3展示了如何得到self从Sinatra routes内部和外部检测的结果;在Figure3-1可以看到结果。
Example 3-3. 检测不同作用域下的self
提示
技术上讲,Rubu里的closures会开辟一个新的嵌套的作用域,我们以后再讲。
Figure 3-1. 检查不同作用域下的self
例子3-3告诉我们,routes里面的函数调用实际上都传给了一个Sinatra::Application类的实例。更进一步,如果你刷新那个页面,你会发现object id一直在变化,这说明对于每一个request请求都有不同的实例来处理。
提示
同样值得注意的是外部作用域是main,前面在调用jobs和self.jobs时也一样。
###“get”是哪来的?
现在我们知道routes是把block传递给一个Sinatra::Application的实例处理。那么,route方法到底在哪里实现的呢?我们打开IRB,用Ruby reflection API来大致了解一下例子3-4中展示的类的结构。
Example 3-4. 使用 reflection
有趣的是,像get,post这类的方法实际上被定义了两次。一次是在Sinatra::Delegator,这是一个mixin。因此这些方法在程序的所有地方都可以使用。这个delegator mixin会直接把同样的函数调用传递给Sinatra::Application,Sinatra::Application从Sinatra::Base继承了这些方法。
提示
Mixins是Ruby从Common Lisp继承的一种技术,这种技术能让你除了选择一个父类以外,还可以加入一些其他的像类一样的的对象到继承链里面。这就让你能够继承现存的类和对象,而不会意外重写其他函数。
我们再深入研究一下Sinatra::Base和Sinatra::Application。例子3-5展示了如何在不用DSL的情况下正确定义routes,以及如果我们在错误的模块下定义routes会发生什么。
Example 3-5. 继续使用 reflection
###探索实现方法
书上前面内容在介绍了使用顶级DSL的classic风格Sinatra,如果有必要你可以简单从官方文档学习,本文不再赘述。这里讲的是modular style。
Sinatra也可以在不用顶级DSL的情况下使用,只需要sinatra/base和一点mixin结构的知识。也意味着顶级DSL不是必须的。
例子3-4和3-5说明了在base.rb里包含对get方法的定义(这个文件也是Sinatra的核心)。在该文件的后面,你可以找到Application和Delegator的实现代码。它们的实现非常简单。而所有Sinatra的主要功能都是在Base类中实现的。
提示
base.rb现在的开发版本可以在这里找到。