0

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

3 Answers 3

2

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
Sign up to request clarification or add additional context in comments.

Comments

1

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.

4 Comments

How can I access the hash using this ?
NameError (`@resource[13739]' is not allowed as an instance variable name) Got this error after using instance_variable_get
@vikas95prasad: of course, the square brackets and the number are not part of the instance variable name, so don't put them there.
@mrzasa this is how i used it and it works instance_variable_get("@#{attribute}")[comment.id]
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.