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]

目标是构建类定义,使上面示例中不起作用的东西可以按预期工作。

更新:(澄清问题)原始问题中遗漏的一个方面:添加通常在类的初始化方法中设置的“默认值”也是一个目标。

更新:(为什么这样做)通常,只需创建一个继承自 Array 的自定义类(或您想要模拟的任何内置类)非常简单。这个问题源自一些“仅测试”代码,并不是试图忽略这种普遍接受的方法。

4

2 回答 2

2

属性不只是 getter 和 setter 吗?如果是这样,你不能这样做:

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 回答