The Map Gem
There’s one Ruby gem that make it into practically every Ruby project I write, my friend Ara’s Map. Really, it’s a coincidence that I know Ara, this gem speeds up development and I would be using it anyway. At it’s core, Map is a fancy hash:
m = Map.new(x:1, y:42, z: 'foo')
=> {"x"=>1, "y"=>42, "z"=>"foo"}
m[:x]
=> 1
m[:a] = 999
=> 999
m
=> {"x"=>1, "y"=>42, "z"=>"foo", "a"=>999}
In fact, it tells the world it’s a hash:
m.kind_of? Hash
=> true
And it works like a HashWithIndifferentAccess:
m[:y]
=> 42
m['y']
=> 42
The first thing that makes Map different then a Hash is that each key automatically becomes an accessor:
m.x
=> 1
m.z = 1066
=> 1066
m
=> {"x"=>1, "y"=>42, "z"=>1066, "a"=>999}
This behavior extends to nested data:
m = Map.new(x: 1, foo: {bar: 42 })
=> {"x"=>1, "foo"=>{"bar"=>42}}
m.foo.bar
=> 42
If you’re not sure a key is there you can use Map#get:
m.q
NoMethodError: undefined method `q' for {"x"=>1, "foo"=>{"bar"=>42}}
m.get(:q)
=> nil
# Nested as well
m.foo.baz
NoMethodError: undefined method `baz' for {"bar"=>42}
m.get(:foo,:baz)
=> nil
All together, this means I can take something hash-like and turn it into something that acts more like an object, without having to define a one-off class:
# Get some JSON from an API
response = http.request(request)
# decode it into a Hash and use that to build a map
thing = Map.for(MultiJson.decode(response.body))
if thing.status != 'OK
... # Whatever, whatever.
else
name = "#{thing.first} #{thing.last}"
city = thing.billing_address.city
# and so on
end
When you need more that a one-off, Map is also a nice to inherent from, allowing you to create a hash-like object with a bit of behavior.
class Customer < Map
def full_name
"#{first_name} #{last_name}"
end
end
c = Customer.new first_name: 'Alice', last_name: 'Smith'
=> {"first_name"=>"Alice", "last_name"=>"Smith"}
c.full_name
=> "Alice Smith"
I really like that I can get a class with what are, in effect, nested attributes without having to write a hairy initializer.
Map has some other nice features including a handy options parser and depth first iteration. Check out the README for more.
Whether you use it to create a one-off object or as the basis for a class, Map provides a clean way to treat hash like data in a object fashion.
Comments