From: Nonta <nonta / yo.rim.or.jp>
Subject: [ruby-list:41046] Re: 親クラスのインスタンスを元に子クラスのインスタンスを作りたい
Date: Sat, 20 Aug 2005 11:33:00 +0900

るびきちです。

> b.method
> とすると、4が返ってきてくれずにエラーになってしまいました。
> クラスBで改めて何かを記述する必要があるのでしょうか…。

methodというメソッド名だからだめなのです。
他のメソッド名ならばちゃんと4が返ってきます。

なぜかというと、delegate.rbでは委譲にmethod_missingを使っているからです。

method_missingはとても便利ですが注意して使う必要があります。
自分のクラスでは定義していないメソッドは、まず、スーパークラスへ投げられます。
そして、どんどんクラス階層を上がっていき、Objectへ投げられます。
Objectも知らないメソッドが初めてmethod_missingに投げられるのです。

自分のクラスでは定義していないが、スーパークラスで定義されているメソッドの
場合、method_missingに行かず、スーパークラスのメソッドが呼ばれるのです。

なお、ostruct.rbもmethod_missingを使っています。

# 昔はまりました(^^;

Object#method_missing
--- method_missing(name, args, ... )

    呼びだされたメソッドが定義されていなかった時、Ruby がこのメソッド
    を呼び出します。

    呼び出しに失敗したメソッドの名前 (Symbol) が name に
    その時の引数が arg ... に渡されます。

    デフォルトではこのメソッドは例外 NameError を発生させます。

しかし、methodというメソッドは定義されているので、うまくいかなかったのです。

Object#method
--- method(name)

    self のメソッド name をオブジェクト化した 
    Method オブジェクトを返します。name は 
    Symbol または文字列で指定します。

    Module#instance_method [Module/instance_method] も参照してください。



しかし、forwardableを使うとうまくいきます。

require 'forwardable'
class A
  def initialize()
    @x = 1
    @y = nil
  end


  attr_accessor :x
  attr_accessor :y

  def method()
    4
  end
end

class B
  extend Forwardable
  def_delegators(:@a, :method, :x, :y)
  def initialize(a = A.new)
    @a = a
  end
end

b = B.new #あたかもBはAのサブクラスかのように
a = A.new
a.y = 2
b = B.new(a)  #aのインスタンス変数の値を引き継いだb
puts b.method


るびきち☆
http://www.rubyist.net/~rubikitch/