Reputation: 1332
Want to achieve the following code using metaprogramming.
@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
How can I create these methods using ruby metaprogramming and access the hash?
Can you tell me what is wrong in my code ?
['resource', 'voters', 'is_upvoted'].each do |attribute|
define_method("#{attribute}") do |comment|
instance_variable_set("@#{attribute}", comment.id)
end
end
Upvotes: 0
Views: 76
Reputation: 1332
This is how I used it and it works
['resource', 'voters', 'is_upvoted'].each do |attribute|
define_method("#{attribute}") do |comment|
instance_variable_get("@#{attribute}")[comment.id]
end
end
Upvotes: 0
Reputation: 20263
This bit seems redundant:
@resource = {}
@voters = {}
@is_upvoted = {}
Since you're already looping an array to do your metaprogramming.
You might try something like:
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
Which I believe will give you methods roughly like:
class Foo
def resource(comment)
@resource ||= {}
@resource[comment.id]
end
end
Personally, it seems not great to me to have comment.id
in your method. Because what if someday you want to use a different attribute (or something else altogether) as the key
?
So, I think I would do:
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
Now, it seems like you're going to want an easy way to set key-value pairs on your instance variable, so I guess I would try something like:
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
In which case you should be able to do (assuming you have a @comment
variable that responds to id
):
@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
Upvotes: 2
Reputation: 230336
Can you tell me what is wrong in my code ?
It's doing the equivalent of this:
def resource(comment)
@resource = comment.id
end
instance_variable_get
would be a better choice.
Upvotes: 1