Wednesday, 20 December 2017

ObjectifyHash Gem - My favorite over engineered solution for a stupid problem

Years after the initial development, I was finally allowed to bring this little gem to the public as part of my employer's effort to bring forth more open source code - hopefully more of these will come out soon.
Objectifyhash a very over-engineered solution for a stupid problem: dealing with nested objects on JSON responses from REST services. I find this specially painful when you need to navigate these representations, search for things in them and be able to do actions, one always ends up with code that looks like 

def find_thing_on_x things
  things.detect do |t| t['identification']['name'] == 'thing'; end
end
....
res = get 'https://my-service.com/stuff'

my_thing = find_thing_on_x res['data']['stuff']['things']

Not only is this ugly to look at, but very functional looking feels very un-Ruby like, not mentioning that on non-english keyboards like mine, brackets are hidden behind an awkward Alt Gr + numbers combo.
So, what I wanted to do in this case would be that either get the Stuff JSON mapped to a Stuff class on my Namespace so I can access handy Stuff related methods, or, if that doesn't exists, to get a generic object that allows me to handle Stuff more easily  AND (this is the fun part that other gems at the time didn't cover) repeat recursively for all nested objects inside.
That means, on the example above, it would create a Data class, a Data::Stuff and da Data::Stuff::Thing - since "things" is an array, it leaves it as an array, but each member becomes a Thing! 

The above example becomes much more beautiful with:

stuff = get('https://my-service.com/stuff').to_obj
my_thing = stuff.find_thing
OR
my_thing = stuff.data.stuff.find_things

I usually go with the second example here, as finding things is an action of Stuff and not from Data, but whatever.

So, all JSON objects become ruby objects of the class named after their key within the namespace of the previous key - easy, right?
It also has an exception handling, where you can specify several keys to map to specific existing classes on your code, this is how you inject your own methods such as the "find_things" above.

But this isn't even the part which makes it over-engineered, I opted to dynamically assigning methods to the instances with the values they should return, this means there's no setter methods and has brought some pains into the our work, the funnest of them all are when we have conflicts in Namespace or inherited methods from basic object, the supreme ofender: {"file": {"id": 123}} in which creating a File with an id method didn't work as aspected, so there's a few gotchas here , in this situation expect File_ and _id_ to happen, but overall I've enjoyed working with this tool.

Checkout the github page with more details and I hope there's more equally crazy people like me to enjoy this gem.

No comments:

Post a Comment