Query Objects
They store complex SQL queries, data aggregation, and filtering methods.
The problem
Say you have the following view.
class PostsController < ApplicationController def index @posts = Post .joins('LEFT OUTER JOIN users ON users.id = posts.author_id') .where(published: true) .where('view_count > ?', params[:min_view_count]) .where('users.first_name LIKE ?', "#{params[:author_name]}%") end
It's a lot of code for a controller, so let's refactor it to use a Query Object.
The PostsQuery object
class PostsQuery def self.call(params = {}, relation = Post.all) relation = published(relation) relation = minimal_view_count(relation, params[:view_count]) relation = author_first_name_like(relation, params[:first_name]) relation end def self.published(relation) relation.where(published: true) end def self.minimal_view_count(relation, view_count) return relation unless view_count.present? relation.where('view_count > ?', view_count) end def self.author_first_name_like(relation, first_name) return relation unless first_name.present? with_authors(relation) .where('users.first_name LIKE ?', "#{first_name}%") end def self.with_authors(relation) relation.joins('LEFT OUTER JOIN users ON users.id = posts.author_id') end end
The implementation
Nice, let's see how the controller looks now.
class PostsController < ApplicationController def index @posts = PostsQuery.call(params) end end
Final thoughts
Not only looks better, testing this approach is much simpler.
This does not only applies for Rails apps, this pattern could be used anywhere in Ruby.
Top comments (0)