Когда переменные класса Ruby/Rails инициализируются?

В настоящее время я сталкиваюсь с проблемой, что все мои объекты используют переменную экземпляра с "одинаковым" начальным значением, и я только читаю этот.

def initialize
    @available_ids = read_only_value
end

Но тут возникает сложная вещь: это "read_only_value" извлекается из API. Для простоты предположим, что значение является массивом целых чисел и изменяется один раз в месяц.

Но я не хочу делать DoS API, получая значение каждый раз, когда я создаю объект. Что если я вместо этого использую переменную класса?

class Foo
   @@available_ids = read_only_value
   def initialize
      @available_ids = @@available_ids //weird but let's go with it
   end
end

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

Но когда это происходит? Когда я запускаю приложение Rails, когда я создаю первый объект? Будет ли оно когда-нибудь обновлено? Поскольку значение меняется раз в месяц, как мне получить обновление?

PS: я думаю, что лучшим вариантом было бы сохранить идентификаторы в базе данных и проверять идентификаторы время от времени или по требованию.

4 ответа

Решение

Это происходит, когда класс загружается (читай: во время запуска Rails.)

Кроме того, вам не нужна переменная класса. Переменная экземпляра класса достаточно хороша:

class Foo
   @available_ids = read_only_value
   def initialize
      @available_ids = self.class.instance_variable_get :@available_ids
   end
end

В Rails есть встроенный кеш, поэтому что-то вроде этого должно работать: (на примере в документации)

class Foo
  def available_ids
    Rails.cache.fetch('available_ids', expires_in: 24.hours) do
      API.fetch_ids
    end
  end
end

Как насчет использования Redis/ Memcache? Кэшируйте значения API в redis/ memcache и установите срок действия на один месяц или немного меньше. В инициализаторе всегда извлекают данные из кеша, а если кеш равен нулю, то вызывает метод, который вызывает настоящий API и снова его кеширует.

В случае вашего кода доступ к API будет доступен после определения класса, что означает, что к нему будет обращаться только один раз, когда вы запустите ваше приложение. Вы можете ясно увидеть это на следующем примере:

puts "starting app"

def read_only_value
  puts "API accessed!"
  "my value"
end


class Foo
  @@available_ids = read_only_value
  def initialize
    @available_ids = @@available_ids
    puts "initializing with value: #{@available_ids}"
  end
end

puts "initializing two objects: "
Foo.new
Foo.new

#=> starting app
#=> API accessed!
#=> initializing two objects: 
#=> initializing with value: my value
#=> initializing with value: my value

При попытке управлять доступом к API лучше всего обернуть ваш API в свой собственный класс. Это позволяет вам отслеживать вызовы API и кэшировать значения независимо от объектов, использующих API:

class API

  def self.instance
    @@instance ||= API.new
  end

  def read_only_value
    if @read_only_value.nil? || (Time.now - @cached_time) > 86400 # one day, in seconds
      puts "accessing api"
      @read_only_value = "access api here"
      @cached_time = Time.now
    end
    @read_only_value
  end

end


class Foo

  def initialize
    @available_ids = API.instance.read_only_value
    puts "initializing with value: #{@available_ids}"
  end

end

puts "initializing two objects: "

Foo.new
Foo.new

#=> initializing two objects: 
#=> accessing api
#=> initializing with value: access api here
#=> initializing with value: access api here
Другие вопросы по тегам