Going in blind
You'll often see Ruby code which looks something like this: ErrorMessage.new(:message => "I'm afraid I can't let you do that, Dave.", :status => NOT_AUTHORIZED)
. Some languages, such as Smalltalk and Python, have what are known as "named parameters", which allow a function call to name each parameter passed in. Ruby does not have named parameters but, as in our ErrorMessage
example, we can achieve something similar through hashes.
Let's start with the concept, sans metaprogramming magic. Write a constructor for ErrorMessage
which accepts a named hash for initializing @message
, @status
, and @suggestion.
Ah, what a lovely, readable constructor. Now that we know what it looks like on the outside (which is always an important first step -- understand how an object or method will look through its interface before worrying about how it's implemented), we can use send
or instance_variable_set
to make the insides more concise. We can also abstract this concept out into a module at the same time so we can use this in multiple classes. Give that a shot: blindly assign to instance variables using the keys of the hash with instance_variable_set
.
This example demonstrates one concrete problem with this approach: @boondoggle
was never meant to exist! But because we're using instance_variable_set
, it's created anyway. Let's see how this situation changes when we use send
:
Salvation! We've prevented ourselves from setting the broken / nonexistent instance variable. This solution is certainly better but one problem remains: What if we want to set an instance variable for which there is no setter (as was created by attr_accessor
)? Sometimes we want a constructor which sets state which is purely internal; it can't be changed, nor can it be accessed directly by the outside world. For that sort of constructor we'll need to get a little fancier.