A design is far better when there are no exceptions. Whenever you write an if statement, Nyan Cat cries.
Yesterday I remembered this blog quote when I faced a common situation handling nested hashes in Ruby.
hash = {
company_name: "Stack Builders",
address: {
line_1: "315 Fifth Avenue",
line_2: "Suite 703A",
city: "New York",
state: "NY",
},
}
What if I want to get some of that data and use it to manually instantiate a Company
entity? Wll, attributes for that entity are not nested like the hash is:
Company.new(
name: hash.fetch(:company_name),
address_line_1: hash.fetch(:address).fetch(:line_1),
address_line_2: hash.fetch(:address).fetch(:line_2),
city: hash.fetch(:address).fetch(:city),
state: hash.fetch(:address).fetch(:state)
)
That's not the most beautiful piece of code, but it works if--and only--if all keys are present. If not fetch
will complain. You can say, "But what if we use []
method instead of fetch
?" Well, if the address
key is missing in our hash
, the code hash[:address][:line_1]
will throw a NoMethodError for NilClass
. Not ideal.
What now? "Add an if
, it will fix that." Mmmm... yes, it will solve the problem, but do you remember the quote at the beginning of this post? I want to avoid adding if
statements to this piece of code. This is where the Null Object pattern enters.
I used a very simple implementation of this pattern just to satisfy cases like this one. We can use it in many different ways and have a lot of flexibility, I just wanted to avoid the NoMethodError
being thrown again.
# null_object.rb
class NullObject
def method_missing(*args, &block)
nil
end
end
# company.rb
address = hash.fetch(:address, NullObject.new)
Company.new(
name: hash.fetch(:company_name),
address_line_1: address.fetch(:line_1),
address_line_2: address.fetch(:line_2),
city: address.fetch(:city),
state: address.fetch(:state)
)
This way the NullObject
instance is just nullifying my call to fetch
, because I don't have that :address
key. This improved my design, simplified my code, and made it safer.
Why don't you go ahead give it a try? It might be as helpful for you as it was for me. Here I created my own implementation of this pattern, a very simple one, no fanciness needed, but if you want something more sophisticated check out Naughy, by Avdi Grimm.