At Agira, Technology Simplified, Innovation Delivered, and Empowering Business is what we are passionate about. We always strive to build solutions that boost your productivity.

, , ,

Rails Refactoring Techniques – Concerns

  • By Bharanidharan Arumugam
  • March 1, 2017
  • 961 Views

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.

Bharanidharan Arumugam

Technical Architect | Tech Lead | Mentor | - An enthusiastic & vibrant Full stack developer has 7 plus years of experience in Web development arena. Owns legitimate knowledge in Ruby, Ruby On Rails, AngularJs, NodeJs, MEAN Stack, CMS Services. Apart all, A Modest human who strictly says "No To Harming Humans".