Hatena::ブログ(Diary)

akimatter このページをアンテナに追加 RSSフィード

2012-02-09

pushしちゃったコミットのコメントを直す方法

| 01:46 |  pushしちゃったコミットのコメントを直す方法を含むブックマーク  pushしちゃったコミットのコメントを直す方法のブックマークコメント

結論。そのリポジトリは捨てて作り直そう。

方法git filter-branchやgit rebase -i (のreword)などでコメントを変更することはできるけど、そうすると変更されたコメントの新しいコミットが作られるだけで古いコメントコミットも残る。

この古いコメントコミットは、新たに作るリポジトリにはコミットしないで、新しいコミットの方のブランチだけをコミットすればOK。

あと、新しいリポジトリに移行したいブランチはちゃんとローカルに作っておかないと、pushできないのでちゃんとbranchをローカルに作るべし。

$ git checkout 移行したいブランチ名

でoriginを削除

$ git remote rm origin

コメントの修正はfilter-branchが楽です。 ( http://www.clear-code.com/blog/2012/1/5.html で紹介されている例 )

$ git filter-branch --msg-filter 'sed -e "s/Merge.*internal.*\$/Merge/"' -f

で、ここで新しく作られたブランチと、古いブランチがちゃんと分かれていることを確認したら、それを新しいリポジトリに移行しよう。

まず新しいリポジトリを作る。

githubならadmin画面からdeleteして、もう一回同じ名前で作るのはアリ。

ただしチームのメンバーに同じ名前だけど別物だから気を付けてね、と話しておく必要がある。

次にoriginを再設定する

$ git remote add origin 新しいリポジトリのURL

移行したいブランチをpushしまくる

$ git push origin 移行したいブランチ名

それからタグ関係も一緒に移行できればいいんだけど、古いブランチの方に付けられているので、無理っぽい。

自分で新旧のコミットコメントを読んで新しい方に手動でタグを貼るしかなさそう。

これで多分オッケーなはず。

トラックバック - http://d.hatena.ne.jp/akm/20120209

2012-01-20

23:48 | ■を含むブックマーク ■のブックマークコメント

明日は勉強会やります

|  明日は勉強会やりますを含むブックマーク  明日は勉強会やりますのブックマークコメント

「イケテルRails3.1勉強会

RailsGuides から Routing 周りをみてみよう」と題してREST周りの話をします

RailsGuidesを使ったハンズオンとかもやります

お申し込みはこちら

http://atnd.org/events/24169

トラックバック - http://d.hatena.ne.jp/akm/20120120

2012-01-13

developmentを実現したいのでコードを読んでみる#5

| 13:19 |  developmentを実現したいのでコードを読んでみる#5を含むブックマーク  developmentを実現したいのでコードを読んでみる#5のブックマークコメント

昨日ログを出力するように変更したrailsを使って、ほとんど素のrails3のアプリサーバの起動、GETリクエストx3サーバの停止までにActionDispatch::Reloaderの各メソッドがどのように呼びだされるのか、developmentとproductionの場合それぞれについて出力してみました。

https://github.com/akm/rails3_reloader_study/blob/master/actiondispatch_reloader_method_invocations_development.log

https://github.com/akm/rails3_reloader_study/blob/master/actiondispatch_reloader_method_invocations_production.log

production

起動時

ActionDispatch::Reloader.to_cleanup(&#<Proc:0x00000100e78410@/Users/akima/workspace/rails/railties/lib/rails/application/bootstrap.rb:55>)

module Rails
  class Application
    module Bootstrap
      include Initializable

    # (中略)

      initializer :set_clear_dependencies_hook, :group => :all do
        ActionDispatch::Reloader.to_cleanup do
          ActiveSupport::DescendantsTracker.clear
          ActiveSupport::Dependencies.clear
        end
      end

ActionDispatch::Reloader.to_prepare(&#<Proc:0x00000100e4e250@/Users/akima/workspace/rails/activesupport/lib/active_support/i18n_railtie.rb:21>)

module I18n
  class Railtie < Rails::Railtie

    # (中略)

    initializer "i18n.callbacks" do
      ActionDispatch::Reloader.to_prepare do
        I18n::Railtie.reloader.execute_if_updated
      end
    end

ActionDispatch::Reloader.prepare!

ActionDispatch::Reloader#initialize(nil)

module Rails
  class Application
    module Finisher
      include Initializable

      # (中略)

      initializer :run_prepare_callbacks do
        ActionDispatch::Reloader.prepare!
      end
module ActionDispatch
  class Reloader
    include ActiveSupport::Callbacks

    # (中略)

    # Execute all prepare callbacks.
    def self.prepare!
      self.trace_log(:prepare!)
      new(nil).run_callbacks :prepare
    end


GET /tests/foo?idx=1

ActionDispatch::Reloader.to_prepare(&#<Proc:0x00000102279838@/Users/akima/workspace/rails/railties/lib/rails/application/finisher.rb:63 (lambda)>)

module Rails
  class Application
    module Finisher
      include Initializable

      # (中略)

      initializer :set_routes_reloader do |app|
        reloader = lambda { app.routes_reloader.execute_if_updated }
        reloader.call
        ActionDispatch::Reloader.to_prepare(&reloader)
      end

ActionDispatch::Reloader.to_cleanup(&#<Proc:0x00000100e0ceb8@/Users/akima/workspace/rails/activerecord/lib/active_record/railtie.rb:83>)

ActionDispatch::Reloader.to_prepare(&#<Proc:0x00000100d3ccb8@/Users/akima/workspace/rails/activerecord/lib/active_record/railtie.rb:94>)

module ActiveRecord
  # = Active Record Railtie
  class Railtie < Rails::Railtie

    # (中略)

    initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
      ActiveSupport.on_load(:active_record) do
        ActionDispatch::Reloader.to_cleanup do
          ActiveRecord::Base.clear_reloadable_connections!
          ActiveRecord::Base.clear_cache!
        end
      end
    end

    config.after_initialize do
      ActiveSupport.on_load(:active_record) do
        instantiate_observers

        ActionDispatch::Reloader.to_prepare do
          ActiveRecord::Base.instantiate_observers
        end
      end
    end

GET /tests/foo?idx=2

なし

GET /tests/foo?idx=3

なし

サーバを停止

なし

まとめ


development

起動時

ActionDispatch::Reloader.to_cleanup(&#<Proc:0x000001023916a8@/Users/akima/workspace/rails/railties/lib/rails/application/bootstrap.rb:55>)

ActionDispatch::Reloader.to_prepare(&#<Proc:0x00000100f58150@/Users/akima/workspace/rails/activesupport/lib/active_support/i18n_railtie.rb:21>)

これらはproductionと同じ。

ActionDispatch::Reloader#initialize(#<ActionDispatch::Callbacks:0x0000010421f318 @app=#<ActiveRecord::ConnectionAdapters::ConnectionManagement:0x0000010421f340 @app=.... )

  /Users/akima/workspace/rails/actionpack/lib/action_dispatch/middleware/stack.rb:112:in `build'
  /Users/akima/workspace/rails/railties/lib/rails/engine.rb:447:in `app'
module ActionDispatch
  class MiddlewareStack

    # (中略)

    def build(app = nil, &block)
      app ||= block
      raise "MiddlewareStack#build requires an app" unless app
      middlewares.reverse.inject(app) { |a, e| e.build(a) }
    end
module Rails
  class Engine < Railtie

    # (中略)

    def app
      @app ||= begin
        config.middleware = config.middleware.merge_into(default_middleware_stack)
        config.middleware.build(endpoint)
      end
    end

GET /tests/foo?idx=1

ActionDispatch::Reloader.prepare!

ActionDispatch::Reloader#initialize(nil)

これらはproductionの起動時に実行されるものと同じ

ActionDispatch::Reloader.to_prepare(&#<Proc:0x000001027b3358@/Users/akima/workspace/rails/railties/lib/rails/application/finisher.rb:63 (lambda)>)

productionのGET /tests/foo?idx=1で実行されるものと同じ


ActionDispatch::Reloader#call({"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/tests/foo", "QUERY_STRING"=>"idx=1", ...)

module Rack

  class Sendfile

    # (中略)

    def call(env)
      status, headers, body = @app.call(env)
      if body.respond_to?(:to_path)
        case type = variation(env)

ActionDispatch::Reloader.to_cleanup(&#<Proc:0x00000103355328@/Users/akima/workspace/rails/activerecord/lib/active_record/railtie.rb:83>)

ActionDispatch::Reloader.to_prepare(&#<Proc:0x0000010334c890@/Users/akima/workspace/rails/activerecord/lib/active_record/railtie.rb:94>)

productionのGET /tests/foo?idx=1で実行されるものと同じ


ActiveRecord::ConnectionAdapters::ConnectionManagement::Proxy#close

ActionDispatch::Reloader.cleanup!

ActionDispatch::Reloader#initialize(nil)

module Rack
  module Handler
      def service(req, res)

        # (中略)

        status, headers, body = @app.call(env)
        begin
          res.status = status.to_i
          headers.each { |k, vs|
            if k.downcase == "set-cookie"
              res.cookies.concat vs.split("\n")
            else
              # Since WEBrick won't accept repeated headers,
              # merge the values per RFC 1945 section 4.2.
              res[k] = vs.split("\n").join(", ")
            end
          }
          body.each { |part|
            res.body << part
          }
        ensure
          body.close  if body.respond_to? :close
        end
      end
module ActionDispatch
  class Reloader

    # (中略)

    # Execute all cleanup callbacks.
    def self.cleanup!
      self.trace_log(:cleanup!)
      new(nil).run_callbacks :cleanup
    end

    # (中略)

    module CleanupOnClose
      def close
        ActionDispatch::Reloader.trace_log_impl("#{self.class.name}#close")
        super if defined?(super)
      ensure
        ActionDispatch::Reloader.cleanup!
      end
    end


GET /tests/foo?idx=2

ActionDispatch::Reloader#call({"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/tests/foo", "QUERY_STRING"=>"idx=2", ...)

GET /tests/foo?idx=3

ActiveRecord::ConnectionAdapters::ConnectionManagement::Proxy#close

ActionDispatch::Reloader.cleanup!

ActionDispatch::Reloader#initialize(nil)

ActionDispatch::Reloader#call({"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/tests/foo", "QUERY_STRING"=>"idx=3", ...)


サーバを停止

ActiveRecord::ConnectionAdapters::ConnectionManagement::Proxy#close

ActionDispatch::Reloader.cleanup!

ActionDispatch::Reloader#initialize(nil)

トラックバック - http://d.hatena.ne.jp/akm/20120113

2012-01-12

developmentを実現したいのでコードを読んでみる#4

| 01:00 |  developmentを実現したいのでコードを読んでみる#4を含むブックマーク  developmentを実現したいのでコードを読んでみる#4のブックマークコメント

だんだん、ソースコードを読むだけではしんどくなってきたので、ちょっとずるをします。

先日rails new --devの使い方を書きましたが、

http://d.hatena.ne.jp/akm/20120110#1326216516

railsの調査用のコードを入れまくって、ブログに書こうとおもって、forkしたので、それを参照するように--devオプションを使ってrailsアプリを作り、それをちょっと動かしてどこから呼び出されているのかを調べちゃおうという魂胆です。


ActionDispatch::Reloaderに対してはこんな感じの出力を追加しました。

https://github.com/akm/rails/commit/b0e5006cd2797c634cb596e7c3cddfa1fc3ffb44


これによって得られた情報からどんな風に使われるのかがある程度分かってくるのですが、まだ追いきれてないのでまた明日

トラックバック - http://d.hatena.ne.jp/akm/20120112

2012-01-10

developmentを実現したいのでコードを読んでみる#3

| 00:57 |  developmentを実現したいのでコードを読んでみる#3を含むブックマーク  developmentを実現したいのでコードを読んでみる#3のブックマークコメント

昨日は、ActionDispatch::Reloader で使われている ActiveSupport::Callbacks のドキュメントを読みました。

今日はそれが ActionDispatch::Reloader でどう使われているのかを追いかけたいと思います

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/reloader.rb

まず定義されているコールバックは、 :prepare と :cleanup の2つです。

どちらも :scope => :name というオプションが指定されているので、コールバックで呼び出されるのがオブジェクト場合、それぞれ#prepare と #cleanup メソッドが呼び出されます

で、コールバックを登録するのは、 to_prepare と to_cleanup というクラスメソッドです。

なので、to_prepare, to_cleanup, prepare!, cleanup! を呼び出している箇所が分かればいいような予感がします

to_prepare
-*- mode: grep; default-directory: "~/.rvm/gems/ruby-1.9.2-head@tengine_console/gems/" -*-
Grep started at Wed Jan 11 00:42:47

grep -nr to_prepare actionpack-3.1.3/lib railties-3.1.3/lib
actionpack-3.1.3/lib/action_dispatch/middleware/callbacks.rb:11:      delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:34:    def self.to_prepare(*args, &block)
railties-3.1.3/lib/rails/application/finisher.rb:22:      initializer :add_to_prepare_blocks do
railties-3.1.3/lib/rails/application/finisher.rb:23:        config.to_prepare_blocks.each do |block|
railties-3.1.3/lib/rails/application/finisher.rb:24:          ActionDispatch::Reloader.to_prepare(&block)
railties-3.1.3/lib/rails/application/finisher.rb:59:      # Force routes to be loaded just at the end and add it to to_prepare callbacks
railties-3.1.3/lib/rails/application/finisher.rb:65:        ActionDispatch::Reloader.to_prepare(&reloader)
railties-3.1.3/lib/rails/railtie/configuration.rb:55:      # Array of callbacks defined by #to_prepare.
railties-3.1.3/lib/rails/railtie/configuration.rb:56:      def to_prepare_blocks
railties-3.1.3/lib/rails/railtie/configuration.rb:57:        @@to_prepare_blocks ||= []
railties-3.1.3/lib/rails/railtie/configuration.rb:62:      def to_prepare(&blk)
railties-3.1.3/lib/rails/railtie/configuration.rb:63:        to_prepare_blocks << blk if blk
railties-3.1.3/lib/rails/railtie.rb:77:  #     # Add a to_prepare block which is executed once in production
railties-3.1.3/lib/rails/railtie.rb:79:  #     config.to_prepare do
to_cleanup
-*- mode: grep; default-directory: "~/.rvm/gems/ruby-1.9.2-head@tengine_console/gems/" -*-
Grep started at Wed Jan 11 00:44:47

grep -nr to_cleanup actionpack-3.1.3/lib railties-3.1.3/lib
actionpack-3.1.3/lib/action_dispatch/middleware/callbacks.rb:11:      delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:40:    def self.to_cleanup(*args, &block)
railties-3.1.3/lib/rails/application/bootstrap.rb:55:        ActionDispatch::Reloader.to_cleanup do

parepare!
-*- mode: grep; default-directory: "~/.rvm/gems/ruby-1.9.2-head@tengine_console/gems/" -*-
Grep started at Wed Jan 11 00:45:24

grep -nr "prepare!" actionpack-3.1.3/lib railties-3.1.3/lib
actionpack-3.1.3/lib/action_controller/metal/testing.rb:21:      @_response.prepare!
actionpack-3.1.3/lib/action_dispatch/http/response.rb:192:    alias prepare! to_a
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:23:  # middleware stack, but are executed only when +ActionDispatch::Reloader.prepare!+
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:45:    def self.prepare!
railties-3.1.3/lib/rails/application/finisher.rb:41:        ActionDispatch::Reloader.prepare!
railties-3.1.3/lib/rails/console/app.rb:30:  ActionDispatch::Reloader.prepare!
cleanup!
-*- mode: grep; default-directory: "~/.rvm/gems/ruby-1.9.2-head@tengine_console/gems/" -*-
Grep started at Wed Jan 11 00:45:58

grep -nr "cleanup!" actionpack-3.1.3/lib railties-3.1.3/lib
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:24:  # or +ActionDispatch::Reloader.cleanup!+ are called manually.
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:50:    def self.cleanup!
actionpack-3.1.3/lib/action_dispatch/middleware/reloader.rb:62:        ActionDispatch::Reloader.cleanup!
railties-3.1.3/lib/rails/console/app.rb:29:  ActionDispatch::Reloader.cleanup!

それぞれ調べないといけないんですが、

railties-3.1.3/lib/rails/console/app.rb:29:  ActionDispatch::Reloader.cleanup!
railties-3.1.3/lib/rails/console/app.rb:30:  ActionDispatch::Reloader.prepare!

で、rails consoleでの reload! の実装として使われているのが発見できました。

https://github.com/rails/rails/blob/3-1-stable/railties/lib/rails/console/app.rb

今日ハードな一日だったのでまた明日

rails new --devの使い方

| 02:28 |  rails new --devの使い方を含むブックマーク  rails new --devの使い方のブックマークコメント

$  git clone https://github.com/rails/rails.git
$  cd rails/
$  git branch -r
$  git checkout 3-2-stable
$  cd ..
$  ruby rails/railties/bin/rails new rails3_20120110 --dev

これでcloneしたリポジトリの(3-2-stableブランチ)を参照するrailsアプリが生成できます

http://edgeguides.rubyonrails.org/3_0_release_notes.html#living-on-the-edge

2012-01-09

developmentを実現したいのでコードを読んでみる#2

| 22:28 |  developmentを実現したいのでコードを読んでみる#2を含むブックマーク  developmentを実現したいのでコードを読んでみる#2のブックマークコメント

昨日はRails::Application継承関係をはっきりさせて、初期化のあたりをどうなっているのかRails Guideのドキュメントをみつけてわーいってところまで行きました。

Rails::Railtie <|---- Rails::Engine <|---- Rails::Application

http://guides.rubyonrails.org/initialization.html


で、本題は何だったのかっていうと、

./application.rb:168:        middleware.use ::ActionDispatch::Reloader unless config.cache_classes

でございます

初期化周りは上のドキュメントコードをざっくり読んで分かった気になったけど、実はたぶん分かっていないことが分かってはいるけど、あえて分かったように振る舞ってみることで先に進んじゃいます

というわけで今日は本丸 ActionDispatch::Reloader のコードを読みます

ActionDispatch::Reloader

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/reloader.rb

分かった気になっているので、誤解を恐れず書いてみるとRack::Serverを継承したRails::Serverのインスタンスリクエストを処理する際にappをcallするのですが、そのappにはmiddlewareがわらわらとくっついていて、callの呼び出しに対してフィルタをかける感じです。

そのmiddlewareの一つが読もうとしているActionDispatch::Reloaderです。なのでcallメソッドが理解できれば良いはずなんですが、

    def call(env)
      run_callbacks :prepare
      response = @app.call(env)
      response[2].extend(CleanupOnClose)
      response
    rescue Exception
      run_callbacks :cleanup
      raise
    end

最初のrun_callbacksでいきなり躓きましたwこれはきっと、includeされているActiveSupport::Callbacksで定義されているメソッドなんでしょう。

ActiveModel::Callbacksもこいつを使ってるのは知ってたのですが、ここでも出てきやがりました。ちょっとActiveSupport::Callbacksをやっつけましょう。

ActiveSupport::Callbacks

https://github.com/rails/rails/blob/3-1-stable/activesupport/lib/active_support/callbacks.rb

ソースコードを見ると結構難しいことをたくさんやっている感じなので、まずはドキュメントを見てみましょう。

http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html のExampleを見てみると、define_callbacks でコールバックのkindだけ定義して、set_callbackでコールバックのkindに対して、いつ何をするのかを登録できる。で、実際に動くときにrun_callbacksをkindを指定して実行すると、set_callbackでkindに対して登録されたコールバックが呼び出される、って感じですね。

ポイントはrun_callbackがインスタンスメソッドなのに対して、

define_callbacks と set_callback はクラスメソッドってこと。なので、上のリンク先には後者は書いてなくて、 http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html

に説明が書いてあります

ざーっと読んだ感じだと、 ActiveSupport::Callbacks.define_callbacks の :scope オプションが難しいっすね。

http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-define_callbacks

define_callbacks :save, :scope => [:kind, :name]

という風に定義されて、

set_callback :save, :before, Audit.new

という風にコールバック用のオブジェクトが指定されていたら、 Audit#before_saveメソッドが実行されると。

define_callbacks :save, :scope => [:kind]

ならAudit#beforeで

define_callbacks :save, :scope => [:name]

ならAudit#saveなんだそうですよ。

kindが before/after/aroundで、nameが指定されたdefine_callbackに指定されたcallbacksの要素ってことですね。

おっけー。じゃあ気を取り直して ActionDispatch::Reloader を読もう!と思ったけど、明日早いのでもう寝ます。また明日ー。

トラックバック - http://d.hatena.ne.jp/akm/20120109

2012-01-08

developmentを実現したいのでコードを読んでみる#1

| 23:30 |  developmentを実現したいのでコードを読んでみる#1を含むブックマーク  developmentを実現したいのでコードを読んでみる#1のブックマークコメント

Railsのdevelopmentモードのように特定のディレクトリ以下のソースコードを適切なタイミングで読み直す機能を作りたいのですが、実際Railsって何やっているのか分からんので調べます


cache_classes

railsアプリconfig/environments/development.rb には大抵

  config.cache_classes = false

と書いてありますクラスキャッシュを無効にするって意味っすね。

この設定がどこで使われているのか、railsの設定はrailtiesに書いてあるのでgrepしてみました。

-*- mode: grep; default-directory: "~/.rvm/gems/ruby-1.9.2-head@tengine_console/gems/railties-3.1.3/lib/rails/" -*-
Grep started at Sun Jan  8 20:35:42

grep -nri cache_classes .
./application/bootstrap.rb:64:        ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
./application/configuration.rb:9:                    :cache_classes, :cache_store, :consider_all_requests_local,
./application/configuration.rb:95:        self.cache_classes = true
./application/finisher.rb:49:        if config.cache_classes && !$rails_rake_task
./application/finisher.rb:70:        if config.cache_classes && !config.dependency_loading
./application.rb:22:  # "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters",
./application.rb:168:        middleware.use ::ActionDispatch::Reloader unless config.cache_classes
./generators/rails/app/templates/config/environments/development.rb.tt:7:  config.cache_classes = false
./generators/rails/app/templates/config/environments/production.rb.tt:5:  config.cache_classes = true
./generators/rails/app/templates/config/environments/test.rb.tt:8:  config.cache_classes = true
./railtie/configuration.rb:39:      # Third configurable block to run. Does not run if config.cache_classes
実はloadが使われる

developmentモードではActiveSupport::Dependenciesはrequireではなく、loadを使ってロードします

ActiveSupport::Dependenciesが動く場合ってことは、const_missingあたりから命名規則にしたがってファイルをロードするあたりの話っすね、たぶん。

./application/bootstrap.rb:64:        ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
middleware.use ::ActionDispatch::Reloader
./application.rb:168:        middleware.use ::ActionDispatch::Reloader unless config.cache_classes

一番大事そうなのはココ。developmentモードではミドルウェアがActionDispatch::Reloaderを使うそうです。

middlewareってRackとかの話だよね?これまでちゃんと調べたことなかったので、middlewareを調べましょう!

middleware

まず、ここに登場しているmiddlewareは何かと言えば、ここに書いてある。

https://github.com/rails/rails/blob/3-1-stable/railties/lib/rails/application.rb#L146

    def default_middleware_stack
      ActionDispatch::MiddlewareStack.new.tap do |middleware|
        if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
          require "action_dispatch/http/rack_cache"
          middleware.use ::Rack::Cache, rack_cache
        end
        #...
      end
   end

ActionDispatch::MiddlewareStack.new.tapに渡されるブロック引数でした。

ActionDispatch::MiddlewareStack

これは何ぞ?と検索してみる。ActionDispatchだからactionpack以下にあるはず・・・

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/stack.rb

ココですね。

ActionDispatch::MiddlewareStackクラス定義の中に、Middlewareクラスの定義があって、ActionDispatch::MiddlewareStackクラスの具体的な記述

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/stack.rb#L53

以降に書かれています

include Enumerableとかしてるし、initializeメソッド

@middlewares = []

とかやっているので、前述のMiddlewareクラスのオブジェクトを複数個持ってなんかする奴なんでしょうな。

具体的な使われ方として、

middleware.use ::ActionDispatch::Reloader

意味するところをまずは知りたいんだけど、useメソッド定義を読むと引数ブロックを、前述のMiddleware.new引数に渡しちゃってmiddlewareを生成してそれをmiddlewaresに追加してるってことっすね。

    def use(*args, &block)
      middleware = self.class::Middleware.new(*args, &block)
      middlewares.push(middleware)
    end

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/stack.rb#L104


ActionDispatch::MiddlewareStack::Middleware

じゃあその前述のMiddlewareを知っておきたいところなんだけど、

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/stack.rb#L6

      def initialize(klass_or_name, *args, &block)
        @klass = nil

        if klass_or_name.respond_to?(:name)
          @klass = klass_or_name
          @name  = @klass.name
        else
          @name  = klass_or_name.to_s
        end

        @classcache = ActiveSupport::Dependencies::Reference
        @args, @block = args, block
      end

引数最初Classクラス名前を期待していて、Classが指定された場合は@klassに代入されるけど、そうじゃない場合は@klassはnilのまま。

それ以外はそのまま@argsに代入される。こいつはどこで使われるかって言うと、

      def build(app)
        klass.new(app, *args, &block)
      end

buildメソッドで指定されたklass.new引数として、このメソッド引数appとともに@argsの内容が渡されるんだけど、klassメソッドにはこう書いてある。

      def klass
        @klass || classcache[@name]
      end

名前を指定した場合には、classcacheから検索するようになっています

classcacheはinitializeで指定されているActiveSupport::Dependencies::Referenceですね。

middleware.use ::ActionDispatch::Reloader

もう一度考えてみると、ActionDispatch::MiddlewareStackのインスタンスであるmiddlewareに::ActionDispatch::Reloaderをクラスを指定してuseさせているので、

ActionDispatch::MiddlewareStack::Middleware.new(::ActionDispatch::Reloader)

で生成されたものがmiddlewareには記憶されている。

あとは、どこかでこれのbuildメソッドが呼び出されるタイミングがあるはずなんだけど、

実は ActionDispatch::MiddlewareStack#build で ActionDispatch::MiddlewareStack::Middleware#build が呼び出されます

https://github.com/rails/rails/blob/3-1-stable/actionpack/lib/action_dispatch/middleware/stack.rb#L109

    def build(app = nil, &block)
      app ||= block
      raise "MiddlewareStack#build requires an app" unless app
      middlewares.reverse.inject(app) { |a, e| e.build(a) }
    end

こいつはどこから呼び出されるのか?これはRails::Engine#appから呼び出されます


Rails::Engine

railtiesをActionDispatch::MiddlewareStackでgrepしてみると、以下の2つが見つかります

-*- mode: grep; default-directory: "~/.rvm/gems/ruby-1.9.2-head@tengine_console/gems/railties-3.1.3/lib/" -*-
Grep started at Sun Jan  8 22:52:59

grep -nri ActionDispatch::MiddlewareStack .
./rails/application.rb:146:      ActionDispatch::MiddlewareStack.new.tap do |middleware|
./rails/engine.rb:606:      ActionDispatch::MiddlewareStack.new

それぞれ Rails::Appliation#default_middleware_stack と Rails::Engine#default_middleware_stack から呼び出されています

Rails::Appliation? そうです。railsアプリconfig/application.erbに記述されるアレです。

module Blog
  class Application < Rails::Application

https://github.com/rails/rails/blob/master/railties/guides/code/getting_started/config/application.rb#L12


で、この Rails::ApplicationRails::Engine継承しているわけですね。

https://github.com/rails/rails/blob/3-1-stable/railties/lib/rails/application.rb#L36

じゃあこの Rails::Engine はというと、 Rails::Railtie を継承しています

https://github.com/rails/rails/blob/3-1-stable/railties/lib/rails/engine.rb#L333

Rails::Railtie は何も継承していません。

https://github.com/rails/rails/blob/3-1-stable/railties/lib/rails/railtie.rb#L113

まとめるとこういう継承をしているわけですね。

Rails::Railtie <|---- Rails::Engine <|---- Rails::Application

Rails::Applicationインスタンスがいつ生成されるのかが知りたくなるわけですが、これを追っかけるのは大変!と思っていたら強い味方発見

http://guides.rubyonrails.org/initialization.html

すばらしい!


続きはまた明日

トラックバック - http://d.hatena.ne.jp/akm/20120108
最近読んだ本
  • 情熱プログラマー ソフトウェア開発者の幸せな生き方
  • 禁煙セラピー
  • 入門git
  • 入門Git
  • もやしもん(8) (イブニングKC)
  • JRuby 徹底入門
  • 入門Subversion―Windows/Linux対応
  • Ship It! ソフトウェアプロジェクト 成功のための達人式ガイドブック
  • プログラミングRuby 第2版 言語編
  • プログラミングRuby 第2版 ライブラリ編