Open viphat opened 6 years ago
http://craftingruby.com/posts/2015/06/24/say-no-to_chained-scopes.html
Because it's easier to mock & test
class Person < ActiveRecord::Base
enum gender: { male: 1, female: 2 }
scope :male, -> { where(gender: 1) }
scope :adult, -> { where('age >= 18') }
scope :left_handed, -> { where(right_handed: false) }
class << self
def left_handed_male_adults
left_handed.male.adult
end
end
end
class PeopleController < ApplicationController
def index
@people = Person.left_handed_male_adults
respond_to(:html)
end
end
class PeopleControllerTest < ActionController::TestCase
def test_people_index
Person.expects(:left_handed_male_adults)
get :index
assert_response :success
end
end
Instead of
class Person < ActiveRecord::Base
enum gender: { male: 1, female: 2 }
end
class PeopleController < ApplicationController
def index
@people = Person.where(gender: Person.genders[:male])
.where('age >= 18')
.where(right_handed: false)
respond_to(:html)
end
end
http://craftingruby.com/posts/2015/06/29/query-objects-through-scopes.html
class Video < ActiveRecord::Base
scope :featured_and_popular,
-> { where(featured: true).where('views_count > ?', 100) }
end
Can be refactor to (using Query Objects Pattern)
class Video < ActiveRecord::Base
scope :featured_and_popular, Videos::FeaturedAndPopularQuery
end
module Videos
class FeaturedAndPopularQuery
class << self
delegate :call, to: :new
end
def initialize(relation = Video.all)
@relation = relation
end
def call
@relation.where(featured: true).where('views_count > ?', 100)
end
end
end
Query Objects is a pattern that helps in decomposing your fat ActiveRecord models and keeping your code both slim and readable.
When to use Query Objects?
One should consider using query objects pattern when in need of performing complex queries on
ActiveRecord
relation. Usually using scopes for such purposes is not recommended. As a rule of thumb, if scope interacts with more than one column and/or joins in other tables, it should be considered to be moved to query object - as a side-effect we can limit amount of scopes defined in our models to a necessary minimum. Also whenever a chain of scopes is to be dealt with, using query object as well should be considered.How to make most out of Query Object Pattern?
RecentProjectUsersQuery
returns a users' relation when called.method_missing
. This way a query object could be used just as a regular relation — i.e.RecentProjectUsersQuery.where(first_name: “Tony”)
instead ofRecentProjectUsersQuery.call.where(first_name: “Tony”)
Source