For example the model:
class Person < ActiveRecord::Base attr_accessible :name, :birthday endand the PeopleController#index lists the first 10 kids:
class PeopleController < ApplicationController def index @people = if params[:page] and params[:page] > 0 Person.where(:birthday => 18.year.ago..Time.current).limit(10).offset(params[:page] * 10).order(:name) else Person.where(:birthday => 18.year.ago..Time.current).order(:name) end end endThe query does not limits the result, if no params[:page] was sent. Of course the generated query works, but the code is awkward. In addition new filter requirements will shake it up totally.
Before going on refactoring, some words how the ActiveRecord finder statement works.
The entire finder statement consists of four query methods (ActiveRecord::QueryMethods) 'where', 'order', 'limit' and 'offset', each returning a chain object, but which are only chained together to a SQL statement and fired finally. And that is important to know. In fact the query methods are only fragments and are combineable, but do not touch the database until the last method was called.
The refactoring starts with extending the model with some scopes:
class Person < ActiveRecord::Base attr_accessible :name, :birthday default_scope order(:name) scope :kids, where(:birthday => 18.year.ago..Time.current) def self.range per_page=10, page=nil return scoped if page.nil? or page <= 0 limit(per_page).offset(page * per_page) end endThe default scope always orders by the people name.
Using the first named scope 'kids' appends the condition searching for alle people beeing younger than 18 years old. A scope is just a static class method.
And that is why the second named scope 'range' is one. The default value for the 'per_page' parameter is 10 and 'page' can even be nil. If that is the case, a blank scope object is returned, preventing the chain to be polluted.
Right, the model is fatter, because there was logic added, but the controller is skinnier:
class PeopleController < ApplicationController def index @people = Person.kids.range(10, params[:page]) end endYou should scope you ActiveRecord queries, because:
The chains and their logic are reuseable
The chains can be combined
The query finders themselves are more readable
The logic is encapsulated and therefore better maintainable (think of having people queries spread all over your application and you have to change the default order)
Further articles of interest:
Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17
Keine Kommentare:
Kommentar veröffentlichen