Переменная класса Ruby и при назначении доступа - PullRequest
0 голосов
/ 23 сентября 2011

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

    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

Ответы [ 2 ]

0 голосов
/ 23 сентября 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
0 голосов
/ 23 сентября 2011

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

def foo
  @@any ||= some_value
end

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

Обновление

Вот небольшой скрипт, который показывает вам, как это сделать.Если вы выполните его, то увидите, что метод complex_function вызывается один раз, поскольку оба оператора print оба возвращают 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довольно неэффективно:

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

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

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

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

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

...