Ruby元编程实现动态方法?

Ruby metaprogramming to achieve dynamic methods?

想使用元编程实现以下代码。

@resource = {}
@voters = {}
@is_upvoted = {}

def resource(comment)
  @resource[comment.id]
end

def voters(comment)
  @voters[comment.id]
end

def is_upvoted(comment)
  @is_upvoted[comment.id]
end

如何使用 ruby 元编程创建这些方法并访问哈希?

你能告诉我我的代码有什么问题吗?

['resource', 'voters', 'is_upvoted'].each do |attribute|
  define_method("#{attribute}") do |comment|
    instance_variable_set("@#{attribute}", comment.id)
  end
end

Can you tell me what is wrong in my code ?

它的作用相当于:

def resource(comment)
  @resource = comment.id
end

instance_variable_get会是更好的选择。

这个有点多余:

@resource = {}
@voters = {}
@is_upvoted = {}

因为您已经在循环数组来进行元编程。

您可以尝试类似的方法:

class Foo 

  %w(
    resource
    voters
    is_upvoted
  ).each do |attr_sym|
    define_method attr_sym do |comment|
      instance_variable_set("@#{attr_sym}", {}) unless instance_variable_get("@#{attr_sym}")
      instance_variable_get("@#{attr_sym}")[comment.id]
    end
  end

end

我相信这会给你大致如下的方法:

class Foo 

  def resource(comment)
    @resource ||= {}
    @resource[comment.id]
  end

end

就我个人而言,在您的方法中使用 comment.id 似乎不太好。因为如果有一天您想使用不同的属性(或其他属性)作为 key?

怎么办?

所以,我想我会这样做:

class Foo 

  %w(
    resource
    voters
    is_upvoted
  ).each do |attr_sym|
    define_method attr_sym do |key|
      instance_variable_set("@#{attr_sym}", {}) unless instance_variable_get("@#{attr_sym}")
      instance_variable_get("@#{attr_sym}")[key]
    end
  end

end

现在,您似乎想要一种在实例变量上设置键值对的简单方法,所以我想我会尝试类似的方法:

class Foo 

  %w(
    resource
    voters
    is_upvoted
  ).each do |attr_sym|
    define_method attr_sym do |key=nil|
      instance_variable_set("@#{attr_sym}", {}) unless instance_variable_get("@#{attr_sym}")
      hsh = instance_variable_get("@#{attr_sym}")
      return hsh[key] if key
      hsh
    end
  end

end

在这种情况下你应该能够做到(假设你有一个响应 id@comment 变量):

@comment.id 
 => 1 
foo = Foo.new
 => #<Foo:0x000056536d7504b0>
foo.resource 
 => {}
foo.resource[@comment.id] = :bar
 => :bar 
foo.resource 
 => {1=>:bar}
foo.resource[@comment.id]
 => :bar

我就是这样使用它的,而且效果很好

['resource', 'voters', 'is_upvoted'].each do |attribute|
  define_method("#{attribute}") do |comment|
    instance_variable_get("@#{attribute}")[comment.id]
  end
end