Рубин на рельсы именованная область внедрения
Из книги Agile Web Development With Rails
class Order < ActiveRecord::Base
named_scope :last_n_days, lambda { |days| {:conditions =>
['updated < ?' , days] } }
named_scope :checks, :conditions => {:pay_type => :check}
end
Утверждение
orders = Orders.checks.last_n_days(7)
Приведет только к одному запросу к базе данных.
Как rails реализует это? Я новичок в Ruby, и мне интересно, есть ли специальная конструкция, которая позволяет этому случиться.
Чтобы иметь возможность цеплять такие методы, функции, генерируемые named_scope, должны возвращать себя или объект, который не может быть расширен. Но откуда Руби знает, что это последняя функция позвоните и что он должен запросить базу данных сейчас?
Я задаю этот вопрос, потому что приведенная выше инструкция фактически запрашивает базу данных, а не просто возвращает инструкцию SQL, которая является результатом цепочки.
3 ответа:
В магии named_scope используются два приема (или паттерна, если хотите).
Proxy pattern - вызов именованного метода scope для класса или ассоциации Всегда возвращает экземпляр класса ActiveRecord::NamedScope::Scope, а не набор отфильтрованных AR-объектов. Этот паттерн, хотя и очень полезный, иногда делает вещи размытыми, так как прокси-объекты амбивалентны по своей природе.
Ленивая загрузка - благодаря ленивой загрузке (что в данном контексте означает - попадание в базу данных только при необходимости) именованные области могут быть привязаны к точке, когда вам нужно работать с коллекцией, определенной областями. Всякий раз, когда вы запрашиваете базовый colleciton, все связанные области вычисляются и выполняется запрос базы данных.
Последнее замечание: есть одна вещь, которую нужно иметь в виду, играя с именованными областями (или с любой вещью, которая использует делегирование какого-либо вида) в IRB. Каждый раз, когда вы нажимаете Enter, вещь, которую вы предварительно записанное вычисляется, и метод
inspect
вызывается по возвращенному значению. В случае цепных именованных областей, хотя все выражение вычисляется в экземпляр области, когда IRB вызывает методinspect
для него, области вычисляются и запускается запрос базы данных. Это вызвано тем, что методinspect
посредством делегирования распространяется через все объекты области вплоть до базовой коллекции.
Возможно, вы захотите попробовать это
class Order < ActiveRecord::Base class << self def last_n_days(n) scoped(:conditions => ['updated < ?', days]) end def checks scoped(:conditions => {:pay_type => :check}) end end end
Использование то же самое
@orders = Order.last_n_days(5) @orders = Order.checks @orders = Order.checks.last_n_days(5)
Это все еще делает всю ленивую загрузку, которую вы любите. То есть он не будет делать запрос, пока вы не попытаетесь получить доступ к записям. бонус: рельсы 3 совместимы!
Очень круто. Я думал сделать что-то подобное в Javascript, но Javascript ведет себя довольно странно.
Утверждение:
var x = SomeObject;
Не вызывает функцию SomeObject toString (). Но утверждение:
var x; x = SomeObject;
Правильно вызывает функцию toString (), как и ожидалось.
Это мешает Javascript делать классные вещи с цепочками. =(