Rails毎週1題(20):Rack変革
7366 ワード
Rack、Railsを改革したようだ.
Rack
Rackって何?Rackはrubyを用いてwebアプリケーションを開発するインタフェースを提供した.例えばRailsフレームワークは,rackがwebサーバとのインタラクションを担っている.簡単に言えば、Rackはrubyがwebアプリケーションを開発する規範となり、webサーバとwebフレームワークの間のインタラクティブなインタフェースを統一している.サポートされているWebサーバとWebフレームワークは、http://rack.rubyforge.org/doc/と非常に多くなっています.
Rackの仕様は非常に簡単で、callメソッドです:http://rack.rubyforge.org/doc/SPEC.html.環境を受け入れ、status、header、bodyに戻ります.これがhttpのすべてじゃないですか?
Rack Middleware
どうしてRackとweb frameworkの間で何かできないのですか?そこでRack middlewareが発展しました.Rackミドルウェアは、Rack仕様に従ういくつかのアプリケーションです.なぜRack middlewareを開発したのですか?ここでやると便利なことがたくさんあるので、実はAOPの方法です(filterと理解することもできます).
多くのmiddlewares:http://wiki.github.com/rack/rack/list-of-middlewareを見てみましょう.あるいは、あなたの最新(2.3)railsアプリケーションの下でコマンドライン:rake middlewareをノックします.
ParamsParser:
def call(env)
if params = parse_formatted_parameters(env)
env["action_controller.request.request_parameters"] = params
end
@app.call(env)
end
CookieStore:
def call(env)
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
status, headers, body = @app.call(env)
session_data = env[ENV_SESSION_KEY]
options = env[ENV_SESSION_OPTIONS_KEY]
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
session_data = marshal(session_data.to_hash)
raise CookieOverflow if session_data.size > MAX
cookie = Hash.new
cookie[:value] = session_data
unless options[:expire_after].nil?
cookie[:expires] = Time.now + options[:expire_after]
end
cookie = build_cookie(@key, cookie.merge(options))
unless headers[HTTP_SET_COOKIE].blank?
headers[HTTP_SET_COOKIE] << "
#{cookie}"
else
headers[HTTP_SET_COOKIE] = cookie
end
end
[status, headers, body]
end
Middleware、まさにこれらのことをする良い場所ではありませんか?
例えば、誰かが開発したgoogle_analytic middleware:
def call env
status, headers, response = app.call(env)
if headers["Content-Type"] =~ /text\/html|application\/xhtml\+xml/
body = ""
response.each { |part| body << part }
index = body.rindex("</body>")
if index
body.insert(index, tracking_code(options[:web_property_id]))
headers["Content-Length"] = body.length.to_s
response = [body]
end
end
[status, headers, response]
end
Rails on Rack
もう一度rake middlewareでよく見てください.
use Rack::Lock
use ActionController::Failsafe
use ActionController::Session::CookieStore, #<Proc:0x01a76984@(eval):8>
use Rails::Rack::Metal
use ActionController::ParamsParser
use Rack::MethodOverride
use Rack::Head
use ActionController::StringCoercion
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
run ActionController::Dispatcher.new
そう、Rails 2.3以降、すべてのものがmiddlewareになり、ActionController stack自体も含まれます.
Railsアプリケーションの呼び出しはmiddlewareスタックの呼び出しです.最初のRack::Lock:
def call(env)
old, env[FLAG] = env[FLAG], false
@lock.synchronize { @app.call(env) }
ensure
env[FLAG] = old
end
最後のActionController::Dispacher.new:
def call(env)
if @@cache_classes
@app.call(env)
else
Reloader.run do
# When class reloading is turned on, we will want to rebuild the
# middleware stack every time we process a request. If we don't
# rebuild the middleware stack, then the stack may contain references
# to old classes metal classes, which will b0rk class reloading.
build_middleware_stack
@app.call(env)
end
end
end
dispacherは、ActionController stackが起動し始めた場所です.上のコードの@app自体もmiddlewareです.
def build_middleware_stack
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
end
_コール(env)はいったい何をしたのか、それとも最後まで見てみましょう.
def _call(env)
@env = env
dispatch
end
def dispatch
begin
run_callbacks :before_dispatch
Routing::Routes.call(@env)
rescue Exception => exception
if controller ||= (::ApplicationController rescue Base)
controller.call_with_exception(@env, exception).to_a
else
raise exception
end
ensure
run_callbacks :after_dispatch, :enumerator => :reverse_each
end
end
ほとんどの主要オブジェクトがcall(env)インタフェースを実現していることに気づいていませんか?
Rails Metal Applications
Rails2.3はMetal Applicationに統合され、主に「少ない」requestに対応する役割を果たしています.ここでの少なさとは、データベースへのアクセスが少なく、ロジックが少ないこと(個人的にはロジックが複雑でmetalには向いていないと考えられている)、アクセス回数が多いこと(アクセス頻度がそれほど高くなければ、必要ないと思います).データベースへのアクセスが非常に少ない場合、パフォーマンスのボトルネックはすでにActionController stackにあります.問題は、なぜActionController stackを迂回しないのかということです.Metalの役割は、Rack middleware stackの一員として(上のリストで探します)、ActionController stackに達する前にrequestをブロックし、満足すれば直接戻ります.やはりソースコードを見てみましょう.
def call(env)
@metals.keys.each do |app|
result = app.call(env)
return result unless @pass_through_on.include?(result[0].to_i)
end
@app.call(env)
end
http://github.com/rails/rails/blob/master/railties/lib/rails/rack/metal.rb
その他
詳細については、http://guides.rubyonrails.org/rails_on_rack.htmlを参照してください.ここではscript/serverの実装、rackupのカスタマイズ方法、middlewareのカスタマイズ方法、metalの生成方法などが見られます.
EOF
ps:
技術はいつも天地を覆すように変化している、rails 2.3またいろいろな違いがありました.IT民工になるのは本当に疲れているが、苦労して楽しむことができれば、むしろ認めた.毎週1題はますます名実ともになってきたが、勉強を続け、書き続けよう.