Martin Fowler recently blogged about “Embedded Document” pattern. It’s a very convenient way of dealing with JSON documents in web apps and we are using it for our models in the app we are currently working on.
In this post, we will see how we can abstract away the smaller patterns that occur when using Embedded Document pattern. Here is the code snippet we will be working with (based on code from Fowler’s post):
We can observe following recurring patterns in above code:
- All of these classes need to have a constructor that takes in a data object and stores it in a field.
- Many attributes require only a simple look-up in the wrapped hash. (e.g.
- Some attributes need a look-up in hash followed by application of some transformation. (e.g.
- In some cases, the nested hashes further need to be wrapped in some objects. These nested objects can appear in sequence too. (e.g.
- Some attributes/methods involve complex operations and are too specific to derive any generalizations from them. (e.g.
Let’s start by defining a class called
EmbeddedDocument which provides the base for such classes.
This will provide the necessary constructor to a subclass of
EmbeddedDocument, thus taking care of #1.
Before we move further, let us conjure up an abstraction named “embedder”. An embedder is nothing but an object with a method named
call that takes one of the possible types in JSON document (a number, a string, an array, a hash, or nil) and returns a more useful representation thereof (potentially wrapping it in some subclass of
Now we can define an identity embedder (something that returns a value as-is, without any transformations) which we will be using for scalar values such as numbers, strings, and nil.
This takes care of #2.
We can define some common transformations in a similar way, and take care of #3. We will define one for date to serve as an example.
We will deal with #4 in two steps.
Step 1: We will make a class object associated with every
EmbeddedDocument an embedder too. i.e. it will have a
call method that takes in parsed data and returns an instance wrapping it.
Step 2: We will now define a couple of functions that take one embedder and return another one. This is a very powerful technique from functional programming called “combinatory design”. What we are defining below can be referred to as “embedder combinators”.
Let’s define a method
key which will allow us to specify a key name and embedder to be used.
We have all the machinery ready! This is how our original classes will look like with the new utility:
Beautiful, isn’t it? :-)
But this is Ruby. We can do even better! Here are a couple of ways we can improve upon the above solution:
- Define a method on
EmbeddedDocument’s class that will take only absolute essentials, and define a JSON accessor method for us.
- We have many cases above wherein a field access is a simple look-up in the wrapped hash and nothing more. Why not put
method_missingto a good use here?
Here’s what our
EmbeddedDocument class looks like after above improvements:
This is even more concise and beautiful I am sure you would agree. :-)
You might wonder what use is
scalar embedder now that we have played the
method_missing card. The answer is that it’s still useful, not by itself, but in conjunction with combinators. e.g.
You can find the full code here.
Well that’s it. I hope you find the post interesting and useful. I am quite new to Ruby (as I am sure reflects from my code), so any suggestions, criticism, and other sort of feedback is most welcome.
Note: The app I talked about before is in Scala, not Ruby. I developed this utility first in Scala and then ported it to Ruby. Of course the semantics and abstractions provided by the two languages are quite different, and thus the two implementations have some key differences. This is what the Scala implementation looks like:
This blog post was originally posted at my Blogspot blog at this URL.