“rack是一个最简化的web application标准”,这句话在很多资料里面见到过很多次,并且都会给出一个5行代码的例子,然后一个web application华丽丽的就起来了。确实比较简单,看看这个一页纸的specification就知道了。但是看了很多次之后还是没有什么感觉,一个简单的specification当然不能帮助你启动程序,需要的是它的实现,而且rack只是application的标准,还需要第三方的web server作为支持。还好rack这个gem帮我们实现了这个specification和相关的一些功能,并且打包了常用的web server,比如webrick和thin。最后还提供了一个命令行工具:rackup。
看一个rack gem里面提供的rackup的例子:
// lobster.ru
require 'rack/lobster' use Rack::ShowExceptions run Rack::Lobster.new
然后调用rackup lobster.ru就可以了。其中使用到了use和run这样的DSL,那么这些DSL是怎么实现的,rackup的过程是如何发生的呢,且看我慢慢道来吧。
先看看rackup这个可执行文件:
// rackup
1 #!/usr/bin/env ruby 2 3 require "rack" 4 Rack::Server.start
恩~没什么特别的,看看Rack::Server里面做了什么
// server.rb
1 require 'optparse' 2 3 module Rack 4 class Server 5 ... 6 def self.start(options = nil) 7 new(options).start 8 end 9 ... 10 end 11 end
OK,又代理到了实例方法start
1 def start &blk 2 #一些parse参数的代码 3 wrapped_app 4 5 daemonize_app if options[:daemonize] 6 ... 7 8 server.run wrapped_app, options, &blk 9 end
恩~,这里的warped_app是用来生成我们的rack app的核心代码
1 def wrapped_app 2 @wrapped_app ||= build_app app 3 end
又引出了build_app和app这两个函数,先看app
1 def app 2 @app ||= begin 3 if !::File.exist? options[:config] 4 abort "configuration #{options[:config]} not found" 5 end 6 7 app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) 8 self.options.merge! options 9 app 10 end 11 end
重点来了,options这个函数把命令行中传入的lobster.ru这个文件放到了options[:config]以供使用。然后再使用Rack::Builder.parse_file这个方法来解析ru配置文件。
1 def self.parse_file(config, opts = Server::Options.new) 2 options = {} 3 if config =~ /\.ru$/ 4 cfgfile = ::File.read(config) 5 #对cfgfile做点处理 6 app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app", 7 TOPLEVEL_BINDING, config 8 else 9 #这种情况下config文件只是定义了一个rack app的类 10 require config 11 app = Object.const_get(::File.basename(config, '.rb').capitalize) 12 end 13 return app, options 14 end
there you go,第6行使用了eval来把ru配置文件里面的那些代码在一个Rack::Builder的实例的上下文中执行,于是Rack::Builder中定义的方法run,use就都可用了。use是用来添加middleware,run应该是用来启动程序了吧?其实不是。。真正启动server的代码在上面列出来了,就是在Server的实例方法start的最后一行。顺便说一句,第6行的那个to_app还提供了url mapping的功能,不过还没仔细看。
后面的代码就不再赘述了。大概说下,就是在构造好了rack app上添加middleware,然后使用default的web server来运行它。
最后给个链接,里面给出了很多rack相关的链接,很是不错:http://jasonseifer.com/2009/04/08/32-rack-resources-to-get-you-started