В данном переводе рассмотрены нововведения в следующей версии ActiveRecrod для Ruby on Rails 3, а так-же описана часть модуля, которая будет исключена в пользу поддержки новых интерфейсов.
Следующие методы будут считаться устаревшими в релизе Rails 3.1 (но не Rails 3.0), и будут полностью исключены из Rails 3.2 (хотя можно будет установить специальный плагин для их дальнейшего использования). Имейте в виду это предупреждение, т.к. оно влечет за собой значительные изменения в коде.
В кратце, передача хеша
Рассмотрим это более подробно. На данный момент ActiveRecord предоставляет следующие методы для поиска:
Следующий код иллюстрирует использование более не поддерживаемых опций:
Но вот такой код по прежнему будет работать:
В добавок ко всему, передача хеша options методу
Поддержку потеряет так-же передача options методами
Динамический
ActiveRecord в Rails 3 получит слудующие методы для поиска (в скобках указан существующий эквивалент из хеша
Каждый из вышеозначенных методов возвращает объект класса
Можно также применить несколько finder’ов к существующим Relation’ам:
Отношение (
Важно знать, что вызов метода
Возможно, из предыдущих примеров уже стало ясно, что
Это очень полезно на ряду с фрагментным кешированием. Так, в контроллере достаточно вызвать:
В предыдущем примере
Принужденная загрузка —
В случае, когда нам не нужна ленивая загрузка, достаточно лишь вызывать, к примеру,
Важно помнить, что здесь
Точно также, методы
Использование метода
Метод
Теперь выглядит как:
Но, ввиду того что хеш options будет исключен, на самом деле придется писать используя новые методы для поиска, т.е. вот так:
Внутренне,
Если необходимо построить сложный запрос, начав с «чистого» Relation, необходимо использовать
К слову, говоря о внутренностях,
Код выше может дать более прозрачное представление тому, что происходит внутри
Или даже named scope:
Что потеряет поддержку в Rails 3.1?
Следующие методы будут считаться устаревшими в релизе Rails 3.1 (но не Rails 3.0), и будут полностью исключены из Rails 3.2 (хотя можно будет установить специальный плагин для их дальнейшего использования). Имейте в виду это предупреждение, т.к. оно влечет за собой значительные изменения в коде.
В кратце, передача хеша
options, содержащего :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :having, :from, :lock любому методу класса, предоставленного ActiveRecord’ом отныне считается устаревшим.Рассмотрим это более подробно. На данный момент ActiveRecord предоставляет следующие методы для поиска:
- find(id_or_array_of_ids, options)
- find(:first, options)
- find(:all, options)
- first(options)
- all(options)
- update_all(updates, conditions, options)
А также методы для вычислений:
- count(column, options)
- average(column, options)
- minimum(column, options)
- maximum(column, options)
- sum(column, options)
- calculate(operation, column, options)
Начиная с версии Rails 3.0, передача любых опций этим методам считается устаревшим, и будет полностью исключена в Rails 3.2. Более того, методы find(:first) и find(:all) (без каких-либо дополнительных опций) также будет исключены в пользу first и all. В виде исключения из правил count() по прежнему будет принимать опцию :distinct.Следующий код иллюстрирует использование более не поддерживаемых опций:
User.find(:all, :limit => 1)
User.find(:all)
User.find(:first)
User.first(:conditions => {:name => 'lifo'})
User.all(:joins => :items)Но вот такой код по прежнему будет работать:
User.find(1)
User.find(1,2,3)
User.find_by_name('lifo')В добавок ко всему, передача хеша options методу
named_scope также потеряет поддержку:named_scope :red, :conditions => { :colour => 'red' }
named_scope :red, lambda {|colour| {:conditions => { :colour => colour }} }
Поддержку потеряет так-же передача options методами
with_scope, with_exclusive_scope и default_scope:with_scope(:find => {:conditions => {:name => 'lifo'}) { ... }
with_exclusive_scope(:find => {:limit =>1}) { ... }
default_scope :order => "id DESC"
Динамический
scoped_by_ аналогично уйдет в историю:red_items = Item.scoped_by_colour('red')
red_old_items = Item.scoped_by_colour_and_age('red', 2)Новый API
ActiveRecord в Rails 3 получит слудующие методы для поиска (в скобках указан существующий эквивалент из хеша
options):
- where (:conditions)
- select
- group
- order
- limit
- joins
- includes (:include)
- lock
- readonly
- from
Цепочки
Каждый из вышеозначенных методов возвращает объект класса
Relation. В принципе, Relation очень схож с анонимными named_scope. Все эти методы также определены и в нём самом, чт�� предоставляет возможно создавать цепочки вызовов:lifo = User.where(:name => 'lifo')
new_users = User.order('users.id DESC').limit(20).includes(:items)Можно также применить несколько finder’ов к существующим Relation’ам:
cars = Car.where(:colour => 'black')
rich_ppls_cars = cars.order('cars.price DESC').limit(10)Почти Model
Отношение (
Relation) ведет себя точно так как и модель, когда дело доходит до использования первичных CRUD методов. Любой из приведенных ниже методов можно вызвать на объекте класса Relation:
- new (attributes)
- create (attributes)
- create! (attributes)
- find (id_or_array)
- destroy (id_or_array)
- destroy_all
- delete (id_or_array)
- delete_all
- update (ids, updates)
- update_all (updates)
- exists?
Следующий код работает так как и ожидается:red_items = Item.where(:colour => 'red')
red_items.find(1)
item = red_items.new
item.colour #=> 'red'
red_items.exists? #=> true
red_items.update_all :colour => 'black'
red_items.exists? #=> falseВажно знать, что вызов метода
update или delete/destroy «сбросит» Relation, т.е. удалит записи в кеше, используемые для оптимизации методов (таких как relation.size).Ленивая загрузка
Возможно, из предыдущих примеров уже стало ясно, что
Relations подгружаются «лениво» — т.е. над ними необходимо вызывать методы работы с коллекцией. Это очень похоже на то, как уже работают ассоциации (associations) и named_scope’ы. cars = Car.where(:colour => 'black') # Relations ленивый, поэтому запрос еще не выполняется
cars.each {|c| puts c.name } # Выполняется запрос "select * из таблицы cars где ..."Это очень полезно на ряду с фрагментным кешированием. Так, в контроллере достаточно вызвать:
def index
@recent_items = Item.limit(10).order('created_at DESC')
endА во view:<% cache('recent_items') do %>
<% @recent_items.each do |item| %>
...
<% end %>
<% end %>В предыдущем примере
@recent_items наполняется из БД только в момент вызова @recent_items.each из view. Так как контроллер не выполняет запрос у БД, фрагментное кеширование становится гораздо более эффективным, не требуя никакой дополнительно работы.Принужденная загрузка — all, first & last
В случае, когда нам не нужна ленивая загрузка, достаточно лишь вызывать, к примеру,
all на объекте типа Relation:cars = Car.where(:colour => 'black').allВажно помнить, что здесь
all возвращает Array, а не Relation. Это похоже на то, как сейчас, в Rails 2.3 работают named_scope и associations.Точно также, методы
first и last вернут объект типа ActiveRecord (или nil):cars = Car.order('created_at ASC')
oldest_car = cars.first
newest_car = cars.lastnamed_scope → scope
Использование метода
named_scope считаеться устаревшим в Rails 3.0, в пользу scope. Но единственное что действительно изменилось, так это то, что теперь не нужно писать приставку named_. Передача опций для поиска будет окончательно исключена из Rails 3.1.Метод
named_scope был просто переименован в scope. Т.е. следующее определение:class Item
named_scope :red, :conditions => { :colour => 'red' }
named_scope :since, lambda {|time| {:conditions => ["created_at > ?", time] }}
endТеперь выглядит как:
class Item
scope :red, :conditions => { :colour => 'red' }
scope :since, lambda {|time| {:conditions => ["created_at > ?", time] }}
endНо, ввиду того что хеш options будет исключен, на самом деле придется писать используя новые методы для поиска, т.е. вот так:
class Item
scope :red, where(:colour => 'red')
scope :since, lambda {|time| where("created_at > ?", time) }
endВнутренне,
named scope'ы являются надстройками над Relation, делая тем самым очень простые вариации для использования вперемешку с finder-методами:red_items = Item.red
available_red_items = red_items.where("quantity > ?", 0)
old_red_items = Item.red.since(10.days.ago)Model.scoped
Если необходимо построить сложный запрос, начав с «чистого» Relation, необходимо использовать
Model.scoped.cars = Car.scoped
rich_ppls_cars = cars.order('cars.price DESC').limit(10)
white_cars = cars.where(:colour => 'red')К слову, говоря о внутренностях,
ActiveRecord::Base теперь содержит следующие делегаты:delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scopedКод выше может дать более прозрачное представление тому, что происходит внутри
ActiveRecord. В добавок к этому любые динамический методы, aka find_by_name, find_all_by_name_and_colour, так-же делегируются Relation’у.with_scope и with_exclusive_scope
with_scope и with_exclusive_scope теперь надстроены поверх Relation’a, предоставляя возможность использовать с ними любой relation:with_scope(where(:name => 'lifo')) do
...
end
Или даже named scope:
with_exclusive_scope(Item.red) do
...
end
