Переменная класса Ruby и назначения доступа

У меня есть модуль с несколькими переменными класса. Я ищу реализацию геттера на уровне класса, которая будет создавать экземпляр переменной @@ только тогда, когда класс пытается получить к ней доступ, как показано ниже.

    module MyProducts
      @@products = nil

      def self.get_product(id)
        # i'm looking for a way that the first call on @@products does a find via AR                 like the following
        # @@products = Product.all
        # this module is in the lib directory of a Rails 2.3.5 app
        @@products.find do |prod|
          prod.id.eql?(id)
        end
      end
    end

Я ищу, чтобы это было прозрачным, чтобы мне не приходилось изменять весь модуль. Существует около 10 переменных уровня класса с похожими функциями, все результаты вызова ActiveRecord .find


person kwbock    schedule 22.09.2011    source источник


Ответы (2)


Просто используйте оператор ||=. Он будет оценивать правильное выражение только в том случае, если правая часть равна nil или false.

def foo
  @@any ||= some_value
end

При первом вызове метода он инициализирует переменную с результатом some_value, а последующие вызовы вернут значение @@any без необходимости повторного вычисления some_value.

Обновить

Вот небольшой скрипт, который показывает вам, как это сделать. Если вы выполните это, вы увидите, что метод complex_function вызывается один раз, поскольку оба оператора печати возвращают 1. Однако из вашего комментария я вижу, что ваша Product является активной записью, поэтому не используйте этот подход для того, что вы просите , это будет очень неэффективно (читайте последнюю часть моего ответа)

#!/usr/bin/env ruby

module Foo
  def self.products
    @@products ||=complex_function
  end

  @@a = 0

  def self.complex_function
    @@a += 1
  end
end

p Foo.products
p Foo.products

Завершение обновления

Однако ваш подход к сохранению Product.all довольно неэффективен:

  • Сначала он извлечет все продукты из базы данных, что может занять много памяти, если у вас много продуктов.
  • Во-вторых, ваш код итерации будет намного медленнее, чем db, когда у вас много продуктов.

Замените весь метод вызовом Product.find(id).

Если ваша модель Product не хранится в базе данных (возможно, в ActiveResource), игнорируйте мой предыдущий комментарий.

Вы также можете взглянуть на mattr_accessor и на этот ТАК вопрос Разница между mattr_accessor и cattr_accessor в ActiveSupport?

Наконец, обратите внимание на эту статью что объясняет вышеупомянутую технику, называемую мемоизацией

person Fabio    schedule 22.09.2011
comment
я ищу что-то подобное, но рельсы (v2.3.5) не знают, как разрешить Product.all, когда он загружает библиотеку при запуске. Я пытаюсь решить эту проблему, потому что это затрудняет разработку, потому что он заполняет массив @@products продуктами, но я не могу пройти через отношения в режиме разработки. Я не знаю, возможно ли это, но я искал что-то вроде функции, похожей на то, если бы это была просто переменная экземпляра, где я мог бы сделать def MyModule.@@products @@products ||= Products.all end извините, если форматирование не работает - person kwbock; 23.09.2011
comment
@grizzgreen Я обновил свой ответ полным примером. Однако не делайте это для классов ActiveRecord. Используйте средства поиска AR или, если вам нужны сложные запросы, используйте named_scopes с лямбдами. - person Fabio; 23.09.2011
comment
Да, я знаю, что нельзя делать это для классов AR, к сожалению, я не писал этот код, и у меня очень мало времени, чтобы закончить эту демонстрацию (приложение было законсервировано в течение 3 лет). Если бы я преобразовал функции, которые вызываются в другом месте, чтобы они сами выполняли поиск, AR обналичивал бы результаты этих запросов или только sql? Все запросы находятся в функции загрузки, поэтому было бы просто преобразовать это. - person kwbock; 23.09.2011

Лучше не использовать переменные класса - у них очень странное поведение. См. http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html

В любом случае вы можете использовать civars (переменные экземпляра класса):

module MyProducts
  class << self
    @products = nil
  end
end
person Eric G    schedule 22.09.2011