attr_object : value objects for ruby on rails
Value Objects are used to add methods on model attributes in Rails. Example, a phone_number attribute that is simply a string could have methods to return the area_code, an unformated version or a formated standardized version.
Installation
Include the gem in your Gemfile:
gem "attr_object"
Then follow these instructions to create your first attr_object :
- Create a value object with
rails generate attr_object attribute_name [type](ex:rails generate attr_object phone fixnum).typeis optional. - File is created in
app/attr_objects(ex :app/attr_objects/phone_value.rb) - Add this to your model :
attr_object :phone, :mobile, PhoneValue(ex :app/model/user.rb)
Use DelegateClass for you value objects (recommended)
DelegateClass will keep your value compatible with the base class of the value while giving you the opportunity to add methods specific to that class of object. Let's take a look at a PhoneValue that would return an unformated value, "323-216-3461" would become "3232163461".
class PhoneValue < DelegateClass(String)
def unformat
self.gsub /\D*/, ""
end
endThe naked instance of PhoneValue("323-216-3461") still returns "323-216-3461".
If you don't want to use DelegateClass
- Create a value object in
app/values(ex :app/values/position_value.rb) - Add this to your model :
attr_object :position, PositionValue(ex :app/model/user.rb) - Make sure your value object has a
to_dbmethod that returns the right type - (optional) Make it
Comparablefor sorting
class PositionValue
include Comparable
def <=>(other)
to_db <=> other.to_db
end
def to_db
@value.to_i
end
endWhy use attr_object?
To explain why, I will once again use the infamous phone attribute which a String.
Slim down your models
To deal with phone numbers, we often end up with a mess of methods like this.
class User < ActiveRecord::Base
def unformat_phone
# ...
end
def format_phone
# ...
end
def area_code
# ...
end
private
def phone_to_i
# ...
end
endattr_object helps you clean up that mess.
class User < ActiveRecord::Base
attr_object :phone, PhoneAttr
endKeep responsibilities where they belong
According to the Single Responsibility Principle, methods about the phone attribute don't belong in your User model.
Rails is not just MVC
We often build applications as if we could write code in just three different places : models, views or controllers. And if things go wild, we rely on concerns and helpers. That should not be the case.