いろいろと道草を食ったがいよいよ、config/boot.rb に挑戦することにした。何度も書いているが、boot.rb の構成は次のようになっている。
RAILS_ROOT の設定 Rails モジュールの定義 Rails.boot! の実行 今回はその Rails モジュールの中身を見てみよう。Rails モジュールの中身は全てクラスの定義だ。どういうクラスかというと、次のようになる。 module Rails class << self class Boot class VendorBoot < Boot class GemBoot < Boot end module Rails の中身は全てクラスやメソッドの定義だけだ。実行は、Rails.boot! をきっかけに次々にオブジェクトやメソッドを呼び出していくことになる。また、実装は Rails モジュールの中にカプセル化され、外部からはそれを気にする必要がない。 こうやって外側から眺めてあれこれ言っていても話は進まないので、面倒くさいなという思いに逆らって中を覗いてみることにしよう。最初は class << self で定義される一連のクラスメソッドを見てみる。boot.rb は 最後に、Rails.boot! が実行されることで一連の処理が始まるが、その Rails.boot! クラスメソッドの定義がここにあるからだ。 次が、boot.rb から class << self の部分を抜き出したものだ。 module Rails class << self def boot! unless booted? preinitialize pick_boot.run end end def booted? defined? Rails::Initializer end def pick_boot (vendor_rails? ? VendorBoot : GemBoot).new end def vendor_rails? File.exist?("#{RAILS_ROOT}/vendor/rails") end def preinitialize load(preinitializer_path) if File.exist?(preinitializer_path) end def preinitializer_path "#{RAILS_ROOT}/config/preinitializer.rb" end end これを見ると、class << self の構成は次のようになっている。 class << self def boot! unless booted? preinitialize pick_boot.run end def booted? def pick_boot def vendor_rails? def preinitialize def preinitializer_path end boot! メソッドだけは中身を省略しなかった。boot.rb の動作は Rails.boot! から始まっているからだ。上の構成図を見ると boot.rb はそう難しいことをしているわけではないのが分かる。 boot! メソッドで実行しているのは、既にブートされているかどうかをチェックして(booted?)、ブートがまだのようだったら、本初期化の前の前段階の初期化をして(preinitialize)、vendor boot、か gem boot かを選んで、Rails本体の初期化を呼び出す(pick_boot)だけだ。 ただ、奇妙なのは普通の手続き型のプログラムと違って、やたらに関数化されているということだ。事実、boot! メソッド以外のメソッドの中身は皆、たった一行だ。これは著者がやたらと関数を作りたがる関数プログラミングの信奉者か、それともこの奇妙なスタイルに深い意味があるのかのどちらかだ。 ところで、プログラムを関数で定義することの利点の一つは、関数の記述は前後関係を問わないということだ。前回のエントリーで述べたように、代入の代わりに関数を用いると、これはコードの置き場所をどこに置いてもよくなるため、アルゴリズムの本質に関係ないパーサの側の事情による前後関係を考えることから開放される。次の例のような記述は、class << self のどこに置いても構わないのだ。 def preinitializer_path "#{RAILS_ROOT}/config/preinitializer.rb" end したがって、boot! のような抽象的なメソッドの定義を先頭に持ってくることができる。はじめに、こういうことをやるよと記述して、段々にそれに必要な部品を作っていくというような、トップダウンの記述ができる。また、読み返すときもそのモジュールが何をやっているのかは先頭の関数を見れば大体分かるので読解が楽になる。 また、プログラムの変更があっても部品の関数の変更だけで、メインの boot! 関数にまったく手を入れる必要がない。関数としてカプセル化されることによってコードの部品化が容易になるからだ。 class << self に限らず、boot.rb の記述は全体としてこのようなトップダウンの記述がなされている。 class << self の個々のメソッドの内容については、ソースを読めば分かるように書いてあるので、これ以上立ち入らない。しかし、class << self の次のメソッドについては、Ruby の使い方がおもしろかったので付け加える。 def pick_boot (vendor_rails? ? VendorBoot : GemBoot).new end pick_boot メソッドは、vendor の Rails があれば(vendor_rails?) それを実行し、なければ、RubyGems の Rails を実行するという動作をするが、おもしろいのは、 (vendor_rails? ? VendorBoot : GemBoot).new の部分だ。三項演算子を使って、vender_rails? メソッドが真を返せば VendorBoot クラスを、そうでなければ、GemBoot クラスを返させている。値として帰ったクラスには new メソッドが適用され、結果的には pick_boot 関数は、VendorBoot クラスのインスタンスか、GemBoot クラスのインスタンスが返ることになる。おもしろいのは、クラスをオブジェクトのように扱っているところだ。 一体こんなことが可能なのだろうかと思って、irb で実験してみた。 irb(main):001:0> class A irb(main):002:1> def say; 'hello'; end irb(main):003:1> end => nil irb(main):004:0> a = A.new; a.say => "hello" irb(main):005:0> b = A; b.new.say => "hello" irb(main):006:0> ほんとうに、変数 b に、クラスAが代入できて、b からインスタンスを作ることができた。Rubyは、クラス までがオブジェクトなのだ。 ここまで調べたことをまとめると、Rails.boot! メソッドは Rails メソッドが既にブートされているかどうかをチェックし、ブートされていなければ vendor の Rails があるかどうかを調べ、あれば VendorBoot クラスのオブジェクトに run のメッセージを送り、なければ、GemBoot クラスのオブジェクトに run メッセージを送るという動作をしていることになる。 したがって、次に考えるのは VendorBoot オブジェクトや、GemBoot オブジェクトとはなにかということになるが、それは次の記事で調べてみる。
by tnomura9
| 2008-10-18 18:15
| Ruby
|
Comments(0)
|
カテゴリ
新型コロナウイルス 主インデックス Haskell 記事リスト 圏論記事リスト 考えるということのリスト 考えるということ ラッセルのパラドックス Haskell Prelude Ocaml ボーカロイド 圏論 jQuery デモ HTML Python ツールボックス XAMPP Ruby ubuntu WordPress 脳の話 話のネタ リンク 幸福論 キリスト教 心の話 メモ 電子カルテ Dojo JavaScript C# NetWalker ed と sed HTML Raspberry Pi C 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||