0

質問: Ruby を使用すると、既存のクラスにカスタムメソッドを 簡単に追加できますが、カスタムプロパティを追加するにはどうすればよいですか? これが私がやろうとしていることの例です:

myarray = Array.new();
myarray.concat([1,2,3]);
myarray._meta_ = Hash.new();      # obviously, this wont work
myarray._meta_['createdby'] = 'dreftymac';
myarray._meta_['lastupdate'] = '1993-12-12';

## desired result
puts myarray._meta_['createdby']; #=> 'dreftymac'
puts myarray.inspect()            #=> [1,2,3]

目標は、上記の例で機能しないものが期待どおりに機能するようにクラス定義を構築することです。

更新:(質問を明確にする)元の質問から除外された1つの側面:通常はクラスの初期化メソッドで設定される「デフォルト値」を追加することも目標です。

更新: (なぜこれを行うのか) 通常、配列 (またはエミュレートする組み込みクラス) から継承するカスタム クラスを作成するのは非常に簡単です。この質問は、「テストのみ」のコードから派生したものであり、この一般的に受け入れられるアプローチを無視しようとするものではありません。

4

2 に答える 2

2

プロパティは単なるゲッターとセッターではありませんか? もしそうなら、あなたはただできませんでした:

class Array
  # Define the setter
  def _meta_=(value)
    @_meta_ = value
  end

  # Define the getter
  def _meta_
    @_meta_
  end
end

次に、次のことができます。

x = Array.new
x._meta_
# => nil

x._meta_ = {:name => 'Bob'}

x._meta_
# => {:name => 'Bob'}

それは役に立ちますか?

于 2010-03-08T02:23:44.327 に答える
1

Ruby では、そのインスタンスの外部にある属性 (インスタンス変数) にアクセスできないことを思い出してください。インスタンスのパブリック メソッドにのみアクセスできます。

attr_accessor説明したように、プロパティとして機能するクラスのメソッドを作成するために使用できます。

irb(main):001:0> class Array
irb(main):002:1>  attr_accessor :_meta_
irb(main):003:1> end
=> nil
irb(main):004:0> 
irb(main):005:0* x = [1,2,3]
=> [1, 2, 3]
irb(main):006:0> x._meta_ = Hash.new
=> {}
irb(main):007:0> x._meta_[:key] = 'value'
=> "value"
irb(main):008:0> 

アクセサーのデフォルトの初期化を行う簡単な方法として、基本的に自分自身を再実装attr_accessorする必要があります:

class Class
  def attr_accessor_with_default accessor, default_value
    define_method(accessor) do
      name = "@#{accessor}"
      instance_variable_set(name, default_value) unless instance_variable_defined?(name)
      instance_variable_get(name)
    end

    define_method("#{accessor}=") do |val|
      instance_variable_set("@#{accessor}", val)
    end
  end
end

class Array
    attr_accessor_with_default :_meta_, {}
end

x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_

y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_

ちょっと待って!出力が正しくありません:

{:key=>"value"}
{:foo=>"bar", :key=>"value"}

リテラル ハッシュのデフォルト値を囲むクロージャを作成しました。

より良い方法は、単純にブロックを使用することです。

class Class
  def attr_accessor_with_default accessor, &default_value_block
    define_method(accessor) do
      name = "@#{accessor}"
      instance_variable_set(name, default_value_block.call) unless instance_variable_defined?(name)
      instance_variable_get(name)
    end

    define_method("#{accessor}=") do |val|
      instance_variable_set("@#{accessor}", val)
    end
  end
end

class Array
    attr_accessor_with_default :_meta_ do Hash.new end
end

x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_

y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_

Hash.new毎回同じリテラル ハッシュを再利用するのではなく、既定値が取得されるたびに が呼び出されるため、出力は正しくなります。

{:key=>"value"}
{:foo=>"bar"}
于 2010-03-08T02:23:37.247 に答える