Рубин на рельсы именованная область внедрения


Из книги 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 3

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 делать классные вещи с цепочками. =(