One of my favorite techniques to DRY up a Rails application is to pull out common functionality into a simple “vocabularized method,” by which I mean a simple descriptive method call that can be made from within your model or controller definition. For instance, in some applications I might have multiple models that have an “avatar.”
These avatars all behave the same and should do the same things so I don’t want to just repeat myself in the code. Instead I set up a file called has_avatar.rb
inside my config/initializers
folder. We use Paperclip for attachment handling at the moment, so I am going to create a wrapper for the specific Paperclip functionality I need for the avatars. Here’s the code:
module HasAvatar STYLES = { :large => ["200x200#", :png], :medium => ["100x100#", :png], :small => ["70x70#", :png], :little => ["50x50#", :png], :tiny => ["24x24#", :png] } def self.included(base) base.extend ClassMethods end module ClassMethods def has_avatar has_attached_file :avatar, PAPERCLIP_DEFAULTS.merge( :styles => HasAvatar::STYLES, :default_style => :medium, :default_url => "https://assets.presentlyapp.com/images/avatars/missing_:style.png", :path => ":account/avatars/:class/:login/:style.:extension" ) end end end ActiveRecord::Base.send :include, HasAvatar
We define a module, HasAvatar
that will add a new class method called has_avatar
into whatever class it is included. I defined a constant STYLES
that lets me access the style hash outside of the attachment definition. A final piece of DRYness in the code is the PAPERCLIP_DEFAULTS
constant which is just the default setup for all attachments (S3 Bucket, etc.) and I override the options I need for the avatars by merge
-ing them in.
class User < ActiveRecord::Base has_avatar end class Group < ActiveRecord::Base has_avatar end
Now both users and groups will have all of the expected Paperclip functionality without having to repeat ourselves. This is a simple example but it shows the general practices behind building your application vocabulary, which is just my made-up term for the abstract reusable components that are specific only to this application. Of course, if it’s useful outside of the application, you might want to just go ahead and pluginize it!
The usefulness of these abstracted methods also comes in through the inherently polymorphic nature of Ruby. Throughout my code I can write helpers, views and more to support avatars without caring whether the object in question is a User
or a Group
. Basically, the DRYer you start the DRYer you stay.