yamazのRails日記 このページをアンテナに追加 RSSフィード

2006-07-22Rubyにおけるrequireとincludeとextend このエントリーを含むブックマーク

ソースコードを読んでていつもrequire/include/extendが混乱するので自分メモ

  • 結論

include インスタンスのメソッド/定数に対して追加変更

extend クラス及びモジュールのメソッド/定数に対して追加/変更

  • 前提

Rubyは定義(def)及びMixinによってクラス/インスタンス(オブジェクト)/モジュールそれぞれにメソッドの追加/変更ができる.

よってクラス/インスタンス/モジュールのどれに対してメソッドを追加/変更しようとしているのかを強く意識する必要がある.

  • rubyにおけるクラスとは

クラス変数及びクラスメソッドが定義されていてインスタンスが生成できる「インスタンス」.

RubyにおいてはクラスはClassクラスのインスタンス

  • rubyにおけるインスタンス(オブジェクト)とは

インスタンスとオブジェクトは同じ意味である.<クラス名>.newすると生成できる.

また<生成されたインスタンス>.classとするとクラスインスタンスが取り出せる.

# Stringクラスのインスタンスを生成

irb(main):001:0> stringInstance = String.new

=> ""

# 作成されたインスタンスのクラスを表示

irb(main):002:0> stringInstance.class.name

=> "String"

  • rubyにおけるモジュールとは

Rubyにおいてモジュールとはインスタンスを作れないクラスとほぼ同義.

インスタンスにextendまたはincludeすることで対象インスタンスを拡張できる(後述).

モジュールではモジュール定数とモジュールメソッドを定義する.

なおモジュールはModuleクラスのインスタンス.


  • クラスメソッド

クラスに所属するメソッドのこと。インスタンスを生成しなくても呼び出せる.

記法はクラス名.メソッド名(例 String.dup)

クラスメソッド一覧の取り出し方は<クラス名>.methods

irb(main):038:0> String.methods

=> ["inspect", "send", "class_eval", "clone", "public_methods", "protected_instance_methods", "__send__", "private_method_defined?", "equal?", "freeze", "autoload?", "methods", "instance_eval", "method", "dup", "include?", "private_instance_methods", "instance_variables", "extend", "protected_method_defined?", "const_defined?", "instance_of?", "name", "eql?", "public_class_method", "hash", "id", "new", "singleton_methods", "taint", "frozen?", "instance_variable_get", "constants", "kind_of?", "object_id", "to_a", "ancestors", "private_class_method", "const_missing", "type", "<", "protected_methods", "<=>", "instance_methods", "==", "method_defined?", "superclass", ">", "===", "instance_variable_set", "const_get", "display", "is_a?", ">=", "respond_to?", "autoload", "to_s", "<=", "module_eval", "class_variables", "allocate", "class", "public_instance_methods", "tainted?", "=~", "private_methods", "public_method_defined?", "instance_method", "__id__", "nil?", "untaint", "included_modules", "const_set"]


  • モジュールメソッド

モジュールに属するメソッド。クラスオブジェクト同様にインスタンスがなくても呼び出せる.

記法はモジュール名.メソッド名(例 GC.start)

モジュールメソッド一覧の取り出し方は<モジュール名>.public_methods

irb(main):039:0> GC.methods

=> ["inspect", "send", "class_eval", "clone", "public_methods", "protected_instance_methods", "__send__", "private_method_defined?", "equal?", "freeze", "disable", "autoload?", "methods", "instance_eval", "start", "method", "dup", "include?", "private_instance_methods", "instance_variables", "extend", "protected_method_defined?", "const_defined?", "instance_of?", "name", "eql?", "public_class_method", "hash", "id", "singleton_methods", "taint", "frozen?", "instance_variable_get", "constants", "kind_of?", "object_id", "to_a", "ancestors", "private_class_method", "const_missing", "type", "<", "protected_methods", "<=>", "instance_methods", "==", "method_defined?", ">", "===", "instance_variable_set", "const_get", "display", "is_a?", ">=", "respond_to?", "autoload", "to_s", "<=", "module_eval", "class_variables", "class", "public_instance_methods", "tainted?", "=~", "private_methods", "public_method_defined?", "instance_method", "__id__", "nil?", "untaint", "included_modules", "const_set", "enable"]

  • インスタンスメソッド(オブジェクトメソッド)

インスタンスに所属するメソッド.生成されたインスタンスからでないと呼び出せない.

記法はインスタンス名#メソッド名。

インスタンスメソッド一覧の取り出し方は<インスタンス>.public_methods

irb(main):042:0> String.new.methods == String.instance_methods

=> true

irb(main):043:0> String.new.methods

=> ["send", "%", "index", "collect", "[]=", "inspect", "oct", "all?", "<<", "slice", "clone", "entries", "chomp", "length", "public_methods", "upcase", "sub!", "squeeze", "__send__", "upcase!", "crypt", "delete!", "equal?", "freeze", "*", "detect", "zip", "lstrip!", "+", "center", "to_f", "methods", "instance_eval", "rindex", "map", "split", "any?", "method", "dup", "size", "sort", "strip", "instance_variables", "count", "include?", "succ!", "downcase", "min", "gsub!", "extend", "instance_of?", "squeeze!", "downcase!", "intern", "next", "find_all", "eql?", "each", "rstrip!", "each_line", "id", "hash", "sub", "slice!", "singleton_methods", "replace", "inject", "tr", "reverse", "taint", "sort_by", "lstrip", "frozen?", "instance_variable_get", "capitalize", "max", "chop!", "kind_of?", "object_id", "capitalize!", "scan", "select", "to_a", "each_byte", "type", "gsub", "unpack", "casecmp", "<", "protected_methods", "partition", "<=>", "tr_s", "empty?", "to_str", "==", "tr!", ">", "===", "rstrip", "to_sym", "match", "grep", "instance_variable_set", "display", "chomp!", "next!", "swapcase", "is_a?", "swapcase!", "ljust", ">=", "respond_to?", "upto", "between?", "reject", "to_s", "<=", "sum", "hex", "class", "insert", "reverse!", "chop", "tainted?", "private_methods", "dump", "=~", "delete", "__id__", "concat", "member?", "tr_s!", "nil?", "untaint", "succ", "find", "each_with_index", "strip!", "rjust", "[]", "to_i"]

require("foo")すると$:(FreeBSD6.1では["/usr/local/lib/ruby/site_ruby/1.8", "/usr/local/lib/ruby/site_ruby/1.8/i386-freebsd6.1", "/usr/local/lib/ruby/site_ruby", "/usr/local/lib/ruby/1.8", "/usr/local/lib/ruby/1.8/i386-freebsd6.1", "."])

からfoo.rbを探索して読み込む.読み込みはプロセスを通して1度だけ.

ここまでが前提知識.以下ではそれぞれの追加に関するイディオムを列挙してみる.

(クラスメソッドとモジュールメソッドは考え方が同じなのでクラス側は省略)

  • モジュール内のメソッドの定義方法
[foo_module.rb]
module FooModule
  # モジュールメソッドの定義
  def FooModule.module_method_bar
    ..
  end
  # mixin用のメソッドの定義
  def mixin_method
    ..
  end
end
もしくは
module FooModule
  def self.module_method_bar
    ..
  end
  # mixin用のメソッドの定義
  def mixin_method
    ..
  end
end

defの後にモジュール名もしくはselfがついているのがとても重要。

これでFooModule.module_method_bar()が呼び出せる.ただしrequire "foo_module"が必要(超重要!!)

ただしもう一つ定義したmixin_methodはインスタンスが存在しないので呼び出す方法がありません。これに関しては後述。


  • FooModuleで定義したmixin用メソッドを他の*クラスメソッド*として使えるように
 require "foo_module"
 class OtherClass
   extend FooModule
 end

もしくは

 require "foo_module"
 class OtherClass
   self.extend FooModule
 end

これでOtherClass.mixin_method()が呼び出せる.


  • FooModuleで定義したmixin用メソッドを他の*インスタンスメソッド*として使えるように
 require "foo_module"
 class OtherClass
   include FooModule
 end

もしくは

 require "foo_module"
 class OtherClass
   self.include FooModule
 end

これでOtherClass.new.mixin_method()が呼び出せる(newでオブジェクト作ってる).

  • 任意の場所でのメソッド追加/変更

Rubyは任意の場所でメソッドの追加及び変更ができます.

 Module FooModule
   def self.module_method_bar
   end
 end

これでいきなりFooModule.module_method_bar()の定義が上書きされます.

便利な反面,危険な側面もあるので,仕様については注意が必要.

irb(main):002:0> class String
irb(main):003:1>   alias to_s_org to_s
irb(main):004:1>   def to_s
irb(main):005:2>     "addstr: " + to_s_org
irb(main):006:2>   end
irb(main):007:1> end
=> nil
irb(main):008:0> "test".to_s
=> "addstr: test"

上記の例ではString#to_sを上書き定義しています(コワー)

  • モジュール内でのextend self

モジュール内でextend selfという記述を見かけますが、下記とだいたい同じ意味

module Foo
  extend self
  def foo_method
    'foo_method'
  end
end

これは

module Foo
  def foo_method
    'foo_method'
  end

  def self.foo_method
    'foo_method'
  end
end

rahaemarahaema2009/08/25 21:17extend,includeの違いについて理解することができました。ありがとうございます。
説明の中で,
-------------
これでFooModule.class_method_bar()が呼び出せる.ただしrequire "foo_module"が必要(超重要!!)
-------------
とありますが,FooModule.module_method_bar() ですよね?

yamazyamaz2009/08/25 22:01そのとおりです.さっそくなおしました.

FabianFabian2012/10/16 08:52Wow, your post makes mine look fbeele. More power to you!

cltzfjbtatcltzfjbtat2012/10/16 21:58aVWwam <a href="http://ycovclbajnwt.com/">ycovclbajnwt</a>

csbbruqacsbbruqa2012/10/19 14:15N173s9 , [url=http://ogueyuahoosn.com/]ogueyuahoosn[/url], [link=http://pokacmkzgzbz.com/]pokacmkzgzbz[/link], http://clbhpiowjrxw.com/

ycpjrwhrptcycpjrwhrptc2012/10/20 01:46xC79Ra <a href="http://hnbtwzfyxjne.com/">hnbtwzfyxjne</a>

kexcjomvkexcjomv2012/10/20 22:15oX1KTK , [url=http://oelesdpvfnet.com/]oelesdpvfnet[/url], [link=http://aoezzftpntio.com/]aoezzftpntio[/link], http://tldkoyjghjdg.com/