脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

メタプログラミングRuby 第4章 まとめ (クラス定義)

個人的第四章のキーワード

カレントクラス

class_eval()

  • Module#class_eval()とObject#instance_eval()は全くの別物
    • instance_eval()はselfに変更を加えるだけ
    • class_eval()はselfとカレントクラスに変更を加える
  • class()とclass_eval()も違う
    • classは束縛を捨てて新しいスコープを作るがclass_eval()はフラットスコープをもつ
    • classは定数を必要とするが、class_eval()はクラスを参照する変数なら何でも使える
    • def add_method_to(some_class)
        some_class.class_eval do
          def new_method
            'Hello!'
          end
        end
      end
       
      add_method_to String
       
      "foo".new_method # => "Hello!"
      

クラスインスタンス変数

class MyClass
  @my_var = 1 # クラスインスタンス変数
  
  def self.read; @my_bar; end # ①クラスメソッド:クラスインスタンスを参照する
  def write; @my_var = 2; end # ②インスタンスメソッド:インスタンス変数を設定している
  def read; @my_var; end      # ③インスタンスメソッド:インスタンス変数を参照している
end
 
obj = MyClass.new
obj.write # ②
obj.read  # => 2 ③
MyClass.read # => 1 ①  

  • クラスインスタンス変数とクラス変数の違い
  • クラス変数は、サブクラスやインスタンスメソッドからアクセスできる
  • クラス変数はクラスに属していない(=Class階層に属している)
    • @@v = 1
       
      class MyClass
        @@v = 2
      end
       
      @@v # => 2!!
      

    • @@vはmainのコンテキストて定義されている(=Objectクラスに属している)ので、すべてのクラスでObjectクラスを継承している以上、全てのオブジェクトで@@vは共有されてしまう

  • 定数に無名クラスを割り当てると、逆に無名クラスも定数を参照するようになる
  • c = Class.new(Array) do 
      def hogehoge
        "fugafuga"
      end
    end
     
    MyClass = c
     
    c.name # => MyClass
    

特異メソッド

  • Rubyでは特定のオブジェクトにだけメソッドを追加するということができる
str = "hoge"
 
def str.title?
  self.upcase == self
end
 
str.title?   # => false
str.methods.grep(/title?/) # => ["title?"]
str.singleton_methods      # => ["title?"]

特異クラス

クラスメソッドの構文

###1
class MyClass
  def self.my_method; end
end
 
###2
def MyClass.my_other_method; end
 
###3
特異クラスをオープンしてメソッドを定義する
class MyClass
  class << self 
    def my_method
  end
end
module MyModule
  def self.my_method; "hello"; end
end
 
class MyClass
  include MyModule
end
 
MyClass.my_method  # NoMethodError!
 
#したのようにすれば動く
class MyClass
  class << self
    include MyModule
  end
end
 
MyClass.my_method  # hello
  • my_methodは、MyClassの特異クラスインスタンスメソッドであるといえる。つまり、my_methodはMyClassのクラスメソッドである。この技術をクラス拡張という。
  • 同じくオブジェクトの場合はオブジェクト拡張という

Extend

  • クラスやオブジェクトを拡張するのに特異クラスをオープンするのは自然なことではない。
  • 代わりに用意されたのがextend
  • レシーバの得意クラスにモジュールをインクルードするためのショートカットである
module MyModule
  def my_method; 'Hello'; end
end
 
obj = Object.new
obj.extend MyModule # オブジェクト拡張
obj.my_method # "Hello"
 
class MyClass
  extend MyModule
end
MyClass.my_method # "Hello"

大統一理論

  1. オブジェクトは一種類しかない。それが通常のオブジェクトかモジュールになる。
  2. モジュールは一種類しかない。それが通常のもモジュール、クラス、特異クラス、プロキシクラスのいずれかになる
  3. メソッドは一種類しかない。メソッドはモジュール(大半はクラス)にすんでいる
  4. すべてのオブジェクトはクラス含め、本物のクラスを持っている。それは、通常のクラスか特異クラスになる
  5. 全てのクラスはスーパークラスを持っている。ただし、Basic Objectにはスーパークラスはない。あらゆるクラスがBasicObjectに向かって一本の継承チェーンを持っている
  6. オブジェクトの特異クラスのスーパークラスは、オブジェクトのクラスである。クラスの特異クラスのスーパークラスはクラスのスーパークラスの特異クラスである。
  7. メソッドを呼び出すとき、Rubyはレシーバの本物のクラスに向かって右へ進み、継承チェーンを上へ進む。

クラスがモジュールをインクルードすると、モジュールのインスタンスメソッドが手に入る。クラスメソッドは入らない

メソッドエイリアス

  • ライブラリなど直接編集すべきでないメソッドがある場合は、そのメソッドをラップして、機能を追加し、全てのクライアントが新しく追加した機能をを自動で使えるようにすればよい
alias :新しい名前 :古い名前
  • aliasはキーワードであり、メソッドではいので二つの間にカンマはいらない
  • 参照を向けるのではなく、コピーする。だから、aliasコマンドの後で元となるメソッドに変更が加えられても、aliasによって作成されたメソッドには影響がない

アラウンドエイリアス

  1. メソッドエイリアスをつける
  2. 新しいメソッドを定義する
  3. 新しいメソッドから古いメソッドを呼び出す
  • RubyGemsとかはこの機能を使って互換性を保っている。