Рубин на рельсах назвал область применения - PullRequest
3 голосов
/ 09 апреля 2010

Из книги Agile Web Development с 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)

приведет только к одному запросу к базе данных.

Как рельсы реализуют это? Я новичок в Ruby и мне интересно, есть ли специальная конструкция, которая позволяет этому случиться.

Чтобы иметь возможность связывать подобные методы, функции, сгенерированные named_scope, должны возвращать себя или объект, который может быть расширен в дальнейшем. Но как Ruby узнает, что это последний вызов функции и что он должен запросить базу данных сейчас?

Я спрашиваю об этом, потому что приведенный выше оператор фактически запрашивает базу данных, а не просто возвращает оператор SQL, полученный в результате цепочки.

Ответы [ 3 ]

7 голосов
/ 09 апреля 2010

В магии named_scope используются два трюка (или шаблона, если хотите).

Шаблон прокси - вызов именованного метода области действия для класса или ассоциации всегда возвращает экземпляр класса ActiveRecord :: NamedScope :: Scope, а не совокупность отфильтрованных AR объекты. Этот паттерн, хотя и очень полезный, иногда делает вещи немного размытыми, поскольку прокси-объекты по своей природе амбивалентны.

Ленивая загрузка - благодаря отложенной загрузке (что в данном контексте означает - попадание в базу данных только в случае необходимости) именованные области можно связать в цепочку до того момента, когда вам нужно работать с коллекцией, определяемой прицелы. Всякий раз, когда вы запрашиваете базовую коллекцию, все связанные области оцениваются и выполняется запрос к базе данных.

Последнее замечание: при работе с именованными областями (или с любой вещью, использующей делегирование какого-либо рода) в IRB нужно иметь в виду одну вещь. Каждый раз, когда вы нажимаете Enter, вычисляется то, что вы написали заранее, и для возвращаемого значения вызывается метод inspect. В случае цепочек именованных областей, хотя все выражение оценивается для экземпляра Scope, когда IRB вызывает для него метод inspect, области оцениваются и выполняется запрос к базе данных. Это вызвано тем, что метод inspect посредством делегирования распространяется через все объекты области действия до базовой коллекции.

3 голосов
/ 10 апреля 2010

Возможно, вы захотите попробовать это

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)

Это все еще делает всю ленивую загрузку, которую вы любите. То есть он не будет делать запрос, пока вы не попытаетесь получить доступ к записям. Бонус: Совместимость с Rails 3!

Названные прицелы мертвы

0 голосов
/ 09 апреля 2010

Очень круто. Я думал сделать что-то подобное в Javascript, но Javascript ведет себя довольно странно.

Утверждение:

var x = SomeObject;

не вызывает функцию toString () SomeObject. Но утверждение:

var x;
x = SomeObject;

правильно вызывает функцию toString (), как и ожидалось.

Это мешает Javascript делать классные вещи с цепочкой. = (

...