Using Value Object as an ActiveRecord Attribute
Two weeks ago, I was working on a simple task, to add a query param for embed url. This embed url is actually url of content page from the main service, which I also contribute to. So I am very familiar with its url's pattern and the way it works. While my task was just to add a query param, I found a that I had to add it to 2 different classes. The good engineer part of me believed that it is an unnecessary redundancy, and something, somewhere should get the touch of refactoring.
Dived deeper i saw that the problem was in attempt to validate the url. These 2 classes uses almost similar but slightly different logic. One of them only needs to validate a livestreaming url, and the other needs to validate both livestreaming and vod urls. In my first few attempts, I wrote a couple of validator classes, and I even tried to use ActiveSupport::Concern to wrap the validators. But something keep telling me, "Hey, this doesn't feel right, but that one doesn't either." "Yea, okay. But what does feel right?" Yes, you read the title correctly!
I realized that this is the job for the Value Object guy from that Domain Design Development village. Enough said, this embed url should be a Value Object. So, it should be validated using its own validation methods. My solution was initially something like this:
class EmbedUrl attr_reader :url def initialize(url) @url = url end def valid? # insert code here end def livestream? # insert code here end def vod? # insert code here end end
So, basically ActiveRecord::Attributes::ClassMethod provides a mechanism to register a custom types attributes other than the typical DB-compatible data types to be used as ActiveModel attribute, and mechanism to serialize and deserialize these custom types to the typical ones. With this feature, I can pull all the url validations into the value object instead of having them scattered in different classes. This technique can help prevent duplicate validations being written for the same problem and grouped related business logic together.