Yesterday I spent a significant amount of time working on a cleaner way to access some of the data I had stored in some classes. Here’s a quick example:
class Character
attr_reader :stats
def initialize
@stats ={:speed => 10,
:power => 11,
:bulk => 8 }
end
end
This stats Hash would be a pretty standard way of storing all the stats, and because of the attr_reader, we have access to everything. Don’t get me wrong, this is perfectly acceptable code right here, but as I was writing, I found myself wanting to “simplify” a bit. I’ll show you:
#What I was writing character_object.stats[:speed] = 12 #What I wanted to be writing character_object.speed = 12
The thing is, what I wanted to be writing is entirely possible, for any item in the hash, with a little bit of metaprogramming.
method_missing
Whenever a method can’t be found in Ruby, another method named method_missing gets called. This means that if you define a method_missing method for your class, it’ll get called instead of the one it inherits. Here’s a proof of concept:
class Whatever
def method_missing(id, *args, &block)
puts "You tried to call #{id}. SORRY CAN'T DO THAT."
super
end
end
var = Whatever.new
var.matt #=> "You tried to call matt. SORRY"
If you ran that code, you’d expect to see exactly what’s in the comment right before the real method_missing gets called. Notice how method_missing actually receives the id of the method called (just the name), all of the arguments (via *args), and then the block if there was one. Let’s use this to our advantage and write the getters for that hash.
class Character
def initialize
@stats ={:speed => 10,
:power => 11,
:bulk => 8 }
end
def method_missing(id, *args, &block)
#The getters for the values in our hash.
return @stats[id] if @stats.has_key?(id)
super
end
end
Now that we have that one extra line of code in method_missing, any value inside the hash is accessible by it’s name. Do note that I always call super at the end of method_missing. This is so that a No_Method_Error will occur should you call a method that’s not in the hash or already predefined.
These are what are called “Ghost Methods.” It should be noted that this is a rather slow process, so if what you’re doing is time-sensitive, this might not be the greatest of ideas. Instead you should check out the define_method call, and get a little creative with that.
Update: I forgot to mention that if you are going to write Ghost Methods using the method_missing technique, you’ll want to override the respond_to? method as well. Therespond_to?method allows for you to check if a method exists for that class, so the character class should now look like so:
class Character
def initialize
@stats ={:speed => 10,
:power => 11,
:bulk => 8 }
end
def method_missing(id, *args, &block)
#The getters for the values in our hash.
return @stats[id] if @stats.has_key?(id)
super
end
def respond_to?(method_name)
return true if @stats.has_key?(method_name)
super
end
end
-
methodsofabstraction liked this
-
digitaldreambit liked this
-
hopeyoulikethis posted this




