Когда переменные класса 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