Table of Contents
In this article, we are going to see the overview of Rails refactoring techniques, and focus on one of the techniques – called concerns.
Why do we need to perform
Rails refactoring?
The objectives are as follows:
- DRY(Don’t repeat yourself)
- Easy to find
- Clarity
- Easy to change
- Performance tuning
- Easy to test
As with all techniques, there are guidelines for the same.
The Guidelines are:
The four rules of Sandi Metz from the Ruby Rogues episode 87. Here is the transcript of the same:
http://rubyrogues.com/087-rr-book-clubpractical-object-oriented-design-in-ruby-with-sandi-metz/
- Your class can be no longer than 100 lines of code.
- Your methods can be no longer than five lines of code.
- You can pass no more than four parameters and you can’t just make it one big hash.
- When a call comes into your Rails controller, you can only instantiate one object to do whatever it is that needs to be done. And your view can only know about one instance variable.
Patterns & Techniques:
There are few patterns and techniques to refactor Rails models, controllers and views. Some main techniques are
- Concerns
- Decorators
- Presenters
- Services objects
Note: First, we will focus on “concerns” in this session. We will continue other Rails refactoring techniques in future session/blog.
What is concern?
- Concerns are pieces of code that allow you to better organize the code that you write.
- Default part of Rails 4, not an extra gem. But it should apply equally to 3.x.x and earlier.
- Works not just for models, but also controllers.
- Not just for code shared across models, but super to simply divide a large model file into logical chunks with associated tests
Sample:
module ModuleName extend ActiveSupport::Concern included do # your class macros end # place your instance methods here module ClassMethods # place class methods here, removing self. end end
Why use concern?
- Your model file is huge, and your matching spec is even larger.
- You’ve got duplicated code in several models
- You want a super easy and clean way to put this code into a single module containing
- Instance methods
- Class methods
- Class macros (has_many, belongs_to, validates, scope, etc.)
How to discover a domain?
- Discover a set of related behavior in a model; possibly, this behavior is in multiple models. The preference should be to discover a “domain” rather than some “technical” aspect. A domain grouping is like Taggable whereas a technical grouping would be like Finder Methods or “Validation Methods”.
- Create a file to hold the concern module:
- In case there is just one concern for a model, or if shared among models, then create the file in app/models/concerns.
- If there are multiple concerns for a single model, group them in subdirectory under models, such as app/models/user/.
- In the new module, underneath the module declaration, place extends ActiveSupport::Concern. This will tell Rails that we are creating a concern.
- If you’ve got duplicated code in several models, group them into a domain.
Steps:
- Create a file for Concern with skeleton structure in /apps/models/concerns/module_name
- Next, move instance methods to module.
- Then, move class macro code to included block.
- Move class methods to inside of module ClassMethods.
- Lastly, place include statement in original model class:
- Include ModuleName (or)
- include ModelName::ModuleName
Example:
module Searchable extend ActiveSupport::Concern included do include Elasticsearch::Model include Elasticsearch::Model::Callbacks index_name 'articles_index' #create index name mapping do indexes :id, index: :not_analyzed indexes :title, analyzer: 'snowball' indexes :description, analyzer: 'snowball' indexes :created_at, :type => 'date' end end module ClassMethods def search(params) search_definition = { sort: [created_at: {order:'desc'}], } search_definition[:query] = { query_string: { query: params[:q] } } if params[:q].present? __elasticsearch__.search(search_definition) end end end
Advantages:
- Ease and safety of refactoring – Concerns are a great first refactoring step because using concerns involves simply moving the methods and tests into separate files. Also, the code accessing those methods need not change.
- A core part of Rails 4, so one can expect familiarity among Rails developers.
- Simplicity – It is just a code organization which makes it easier to navigate to the right source and test a file; simpler than plain Ruby methods of include and extend.
- Can DRY up code when a concern is shared among multiple models or controllers.
Notes: Applies to controllers as well as models, although with controllers, one can break up a controller into several controllers that are referenced in the routes.rb file, so you may not need to use a Concern for simply reducing the size of a controller. In the controller case, concerns are useful for sharing code.
Conclusion:
This is the Concerns Rails refactoring technique in detail. For more details about our Agira practices, Check our github repo agile-practices.