- お知らせ -
  • 当wikiのプログラムコードの表示を直してみました(ついでに長い行があると全体が下にぶっ飛ぶのも修正)。不具合があればBBSまでご連絡下さい。

Ruby/Ruby on Rails

はじめに Edit

  • Railsで自作ライブラリ(モジュール)はどこおけばいいの?→lib/ 以下
  • どうやって読み込むの?→オートロードさせる
  • オードロードの方法は?→HogeMage::Fooを使うとhoge_mage/foo.rbが自動で読まれる。
  • つまり?→モジュールHogeMage::Fooを使う時は、include HogeMage::Fooすると、その時点でlib/hoge_mage/foo.rbが読まれるのでそこに、HogeMage::Fooを書くとよい。

(クラスの場合も同様かな?)
※ActiveSupportのソース見たりメモしながら実験中

まとめ Edit

メモをまとめていいる途中

前提知識: Edit

  • requireなどを介さずにライブラリをautoloadしたい場合は一定のルールにそう必要がある
    • HogeMageクラスやモジュールをautoloadしたい(requireせずに使いたい)場合は、lib/hoge_mage.rbにHogeMageクラスやHogeMageモジュールをおく必要がある。
  • lib/以外にもファイルを置いてautoloadに対象にできる箇所はある。
    Rails::Configuration.default_load_paths 等を参照。
    全てのautoload用のパスを確認するには、ActiveSupport::Dependencies.load_paths を見ればよい。
  • (めったにないと思うが)autoload用のパスを自分で追加するには、config/environment.rbの下記の該当コメントを外して弄るとよい
      # Add additional load paths for your own custom dirs
      # config.load_paths += %W( #{RAILS_ROOT}/extras )
    

実際の例: Edit

  • lib/class_only.rbに定義したClassOnlyクラスのインスタンスメソッド(インスタンスの生成必要)や特異メソッドはautoloadにてrequireなしで呼び出せる
    例:
    ClassOnly.new.for  # ClassOnlyのインスタンスメソッド
  • クラスと同様に、lib/module_only.rbに置いたModuleOnlyモジュールをautoloadにてrequireなしでincludeしてインスタンスメソッドを呼んだり、直接ModuleOnlyの特異メソッドを呼び出せる
    例:
    Hoge.include ModuleOnly
    Hoge.foo  # ModuleOnlyのインスタンスメソッド
    ModuleOnly.bar # ModuleOnlyの特異メソッド
  • autoloadにてrequireなしで、lib/module_and_class.rbに置いたModuleAndClassモジュール内に定義したInnerClassのインスタンスを生成し、そのインスタンスメソッドを使うことができる。
    つまり、autoloadでファイルとモジュールさえ読めれば、その中のクラスなども自由に使えるということかと。
    例:
    ModuleAndClass::InnerClass.new.foo

(続く)

[TODO]:実際の例を書く予定

細かいこと Edit

以下、メモが長すぎてわかりにくいので、一通り試してまとめ作る予定です。

ソース読め! 少し挙動をメモしてみる。
ActiveSupportのDependencies#load_missing_constant参照!

  • ModuleConstMissing.const_missing_with_dependenciesやClassConstMissing.const_missingから
    ActiveSupport::Dependencies.load_missing_constantを読んでいる。
    キモはActiveSupport::Dependencies.load_missing_constantにあるっぽい
  • ライブラリがあるか見るパスは ActiveSupport::Dependencies.load_paths に入っている。
    このload_paths は config/environment.rbでおなじみのRails::Initializer.run → (Rails::Initializer.runのブロックが呼ばれる) → Rails::Initializer.process → Rails::Initializer.set_autoload_paths という感じで呼ばれ、ActiveSupport::Dependencies.load_paths にセットされる。
  • ActiveSupport::Dependencies.load_paths の初期値は、通常 config/environment.rbのRails::Initializer.run が呼ばれたときのデフォルト引数にて生成されときに、Rails::Configuration.newでdefault_log_pathにてRails用にいろいろ(lib/含む)設定されている。

以下scirpt/consoleにて Edit

1
ActiveSupport::Dependencies.log_activity = true

しつつ、tail -f log/development.logでログを見つつ調査するとautoload時の処理が少しわかる。
(もしくは、config/environments/development.rb に上記コードを追記すると開発サーバー起動中や、scirpt/console時に自動でログに吐かれるようになる)

未定義の Hoge が呼ばれた場合 Edit

>> Hoge
NameError: uninitialized constant Hoge
	from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:443:in `load_missing_constant'
	from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:80:in `const_missing'
	from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:92:in `const_missing'
	from (irb):1

ログ:

Dependencies: called load_missing_constant(Object, :Hoge)

ここで試しにload_missing_constantの中でやっているようなメソッドActiveSupport::Dependencies.qualified_name_for と ActiveSupport::Dependencies.search_for_file を使い、ロードされるファイルのパスを確かめてみると、

>>  ActiveSupport::Dependencies.search_for_file((ActiveSupport::Dependencies.qualified_name_for "Object", "Hoge").underscore)
=> nil

lib/hoge.rbというファイルはそもそもないのですから、やはり見つからないようです。

lib/class_only.rbにClassOnlyクラスを作ってメソッドを読んだ場合 Edit

autoloadの規則に合わせて、以下のファイルとクラスを設置し、

1
2
3
4
5
6
7
8
9
10
# lib/class_only.rb
class ClassOnly
  def foo
    puts "call ClassOnly.foo in instance method"
  end

  def self.bar
    puts "call ClassOnly.bar in class method"
  end
end

script/consoleにてClassOnlyのインスタンスメソッドをautoloadで呼べることを確認。

>> ClassOnly.new.foo
call ClassOnly.foo in instance method
=> nil

ログは、

Dependencies: called load_missing_constant(Object, :ClassOnly)
Dependencies: called require_or_load("/media/cofs3/home/Project/test/rails/autoload_test/lib/class_only.rb", nil)
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/class_only
Dependencies: called load_file("/media/cofs3/home/Project/test/rails/autoload_test/lib/class_only.rb", ["ClassOnly"])
Dependencies: called new_constants_in(:Object)
Dependencies: New constants: ClassOnly
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/class_only.rb defined ClassOnly

lib/class_only.rbに置いたClassOnlyクラスのインスタンスメソッドをautoloadにて一発で呼び出せることがわかりました

次にClassOnlyのクラスメソッドを読んでみますが、すでにautoloadされているのでログは特に変化なし。

>> ClassOnly.bar
call ClassOnly.bar in class method
=> nil

上のログで、

Dependencies: called load_missing_constant(Object, :ClassOnly)

と出ているので、同様にロードされるファイルのパスを見てみると

>>  ActiveSupport::Dependencies.search_for_file((ActiveSupport::Dependencies.qualified_name_for "Object", "ClassOnly").underscore)
=> "/media/cofs3/home/Project/test/rails/autoload_test/lib/class_only.rb"

ちゃんと、実在するlib/class_only.rbを読もうとしていたようです。

lib/module_only.rbにModuleOnlyモジュールを作ってincludeしたりメソッドを読んだ場合 Edit

autoloadの規則に合わせて、以下のファイルとモジュールを設置し、

1
2
3
4
5
6
7
8
9
10
# lib/module_only.rb
module ModuleOnly
  def foo
    puts "call ModuleOnly.foo in instance method"
  end

  def self.bar
    puts "call ModuleOnly.bar in singleton method"
  end
end

script/consoleでincludeしてみます。

>> include ModuleOnly
=> Object

この時点でログを見ると、

Dependencies: called load_missing_constant(Object, :ModuleOnly)
Dependencies: called require_or_load("/media/cofs3/home/Project/test/rails/autoload_test/lib/module_only.rb", nil)
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/module_only
Dependencies: called load_file("/media/cofs3/home/Project/test/rails/autoload_test/lib/module_only.rb", ["ModuleOnly"])
Dependencies: called new_constants_in(:Object)
Dependencies: New constants: ModuleOnly
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/module_only.rb defined ModuleOnly

ちゃんと読んでいるようですね。

script/consoleで続いて以下のように ModuleOnly.foo を正しく呼べていることがわかります。
※ includeなので特異メソッドのModuleOnly.barは呼べませんね(extendでないとダメ)

>> foo
call ModuleOnly.foo in instance method
=> nil
>> bar
NameError: undefined local variable or method `bar' for #<Object:0xb7cb994c>
	from (irb):4
>> exit

lib/module_only.rbに置いたModuleOnlyモジュールをautoloadにてincludeしてインスタンスメソッドを呼び出せることがわかりました

さてここで一旦script/consoleを終了して、今度はモジュールの特異メソッドを呼べるか確認してみます。

>> ModuleOnly.bar 
call ModuleOnly.bar in singleton method
=> nil

ログ:

Dependencies: called load_missing_constant(Object, :ModuleOnly)
Dependencies: called require_or_load("/media/cofs3/home/Project/test/rails/autoload_test/lib/module_only.rb", nil)
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/module_only
Dependencies: called load_file("/media/cofs3/home/Project/test/rails/autoload_test/lib/module_only.rb", ["ModuleOnly"])
Dependencies: called new_constants_in(:Object)
Dependencies: New constants: ModuleOnly
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/module_only.rb defined ModuleOnly

行けてますね。

lib/module_only.rbに置いたModuleOnlyモジュールの特異メソッドをautoloadにて一発で呼び出せることがわかりました

ここまではモジュールも大体クラスと同じような処理がされているとわかります。

lib/module_and_class.rbにModuleAndClassモジュールとその中にInnerClassクラスを作って利用した場合 Edit

autoloadの規則にそったファイル名のモジュールを作り、そのモジュール内に別のクラスを作って利用してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# lib/module_and_class.rb
module ModuleAndClass
  def self.bar
    puts "call ModuleAndClass.bar in singleton method"
  end

  class InnerClass
    def foo
      puts "call ModuleAndClass::InnerClass.foo in instance method"
    end
    def self.bar
      puts "call ModuleAndClass::InnerClass.bar in class method"
    end
  end
end

script/consoleにて、

>> ModuleAndClass::InnerClass.new.foo
call ModuleAndClass::InnerClass.foo in instance method
=> nil

いけますね!

autoloadにて一発で、lib/module_and_class.rbに置いたModuleAndClassモジュール内に定義したInnerClassのインスタンスを生成しインスタンスメソッドを使うことができるとわかりました
つまり、autoloadでファイルとモジュールさえ読めれば、その中のクラスなども使えるということかと

以下はログです:

Dependencies: called load_missing_constant(Object, :ModuleAndClass)
Dependencies: called require_or_load("/media/cofs3/home/Project/test/rails/autoload_test/lib/module_and_class.rb", nil)
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/module_and_class
Dependencies: called load_file("/media/cofs3/home/Project/test/rails/autoload_test/lib/module_and_class.rb", ["ModuleAndClass"])
Dependencies: called new_constants_in(:Object)
Dependencies: New constants: ModuleAndClass
Dependencies: loading /media/cofs3/home/Project/test/rails/autoload_test/lib/module_and_class.rb defined ModuleAndClass

モジュールの中にクラスがあっても、該当ファイルを探して、モジュールを定義するところまでが ActiveSupport::Dependencies の仕事みたいですね。

(ディレクトリを含んだ実験に続きます)

参考リンク Edit

参考リンク Edit


No comment. Comments/Ruby/Ruby on Rails/libディレクトリとオートロードと自作ライブラリ?

Name:

Front page   Edit Freeze Diff Backup Upload Copy Rename Reload   New Pages Search Recent changes   Help   RSS of recent changes
Last-modified: 2010-05-19 Wed 15:50:20 JST (2740d)