Here at gap Intelligence, many of our applications are built using the Ruby on Rails framework. As a developer, I like Rails for its convention over configuration approach which gives it an accessible learning curve. Additionally, there's an abundance of free resources and online tutorials to help you get up and running quickly with Ruby on Rails. However, while there are many starter tutorials, few explore some of the more intermediate concepts that are common knowledge to Rails developers. Here are a few often used Rails tools and concepts that you should know about once you're ready to move past the basics:
Active Support #delegate Module
The delegate module provides a delegate method that allows you to forward a class method to an associate object. It reduces the need to write extra methods when they can be leveraged from another related class.
For example, consider a Library class with a name attribute and a Book class:
class Library < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :library
end
To get the name of a library a book belongs to, you might use book.library.name or you could write a method:
class Book < ActiveRecord::Base
belongs_to :library
def library_name
library.name
end
end
Instead, you can simplify this by delegating a name method for Book to the associate Library class:
class Book < ActiveRecord::Base
belongs_to :library
delegate :name, to: :library, prefix: true
end
Now you can call book.libary_name without having to write additional methods.
Scopes
In Rails, scoping allows you to add a pre-defined class method for retrieving and querying objects.
Consider again the Book class defined above with a method to retrieve all Books that have been checked out from a library:
class Book < ActiveRecord::Base
belongs_to :library
delegate :name, to: :library, prefix: true
def self.checked_out
where(checked_out: true)
end
end
Instead of defining a method for this query, you can simplify your class method into one line with scope:
class Book < ActiveRecord::Base
belongs_to :library
scope :checked_out -> { where(checked_out: true) }
delegate :name, to: :library, prefix: true
end
You can even chain scopes together to execute one query!
Helpers
Helpers are modules that Rails will automatically include in your views for you. They’re useful for extracting code out of your views and keeping them DRY.
Using the Book class from above, let’s say you want to display whether a book was checked out, but you wanted to show “Available” or “Unavailable” instead of true or false. Your view page might look like:
<% if @book.checked_out %>
<p> Available </p>
<% else %>
<p> Unavailable</p>
<% end %>
If you want to display a book’s availability in across different views, this could get tedious. Instead, add a helper file for the Books class.
module BooksHelper
def availability(book)
return book.checked_out ? "Available" : "Unavailable"
end
end
Now when you want to display a book's availability you can just use the helper method:
<p><%= availability(@book) %></p>
Concerns
Concerns are modules that allow you to share model roles and functionality across classes. It’s another great way to keep your code organized and keep your models from getting bulky. For example, continuing with our Book class from above, let’s add a Video class, and a Request class that has a basic polymorphic association with Books and Video:
class Book < ActiveRecord::Base
belongs_to :library
has_many :requests as: :requestable
def make_request
requests.create(user: user, date_requested: Date.today)
end
end
class Video < ActiveRecord::Base
belongs_to :library
has_many :requests as: :requestable
def make_request
requests.create(user: user, date_requested: Date.today)
end
end
class Request < ActiveRecord::Base
belongs_to :requestable, polymorphic: true
end
As you can see, both Book and Video use the same exact method to create a request. By moving the logic for requests into a concern, we can eliminate having repetitive methods and simply include the concern module.
module Requestable
extend ActiveSupport::Concern
included do
has_many :requests as, :requestable
end
def make_request(user)
requests.create(user: user, date_requested: Date.today)
end
end
Now we can simplify our Book and Library class:
class Book < ActiveRecord::Base
include Requestable
belongs_to :library
end
class Video < ActiveRecord::Base
include Requestable
belongs_to :library
end
Delegate, Scopes, Helpers, and Concerns were all concepts that were on my (long) list of things I wish I'd known when I first dived into our Rails projects here at gap intelligence. All of them are useful Rails tools that will help you keep your project DRY, readable, and organized!
For more than 16 years, gap intelligence has served manufacturers and sellers by providing world-class services monitoring, reporting, and analyzing the 4Ps: prices, promotions, placements, and products. Email us at info@gapintelligence.com or call us at 619-574-1100 to learn more.