I am currently facing the issue, that all my objects using an instance variable with the "same" inital value and I only read this one.
def initialize
@available_ids = read_only_value
end
But here comes the tricky thing: This "read_only_value" is retrieved from a API. To make it easy, let's assume the value is an array of ints and gets changed once a month.
But I don't want to DoS the API by retrieving the value every time I create an object. What if I use a class variable instead?
class Foo
@@available_ids = read_only_value
def initialize
@available_ids = @@available_ids //weird but let's go with it
end
end
I assume this will solve the issue with spamming the API, since it's only execute once the variable gets initialized...
But when does this happen? When I start my Rails application, when I create the first object? Will it ever be updated? Since the value changes once a month how do I get the update?
PS: I guess the best option would be to save the ids in a database and check the ids once in a while or on demand.
It happens when the class is loaded (read: during Rails startup.)
Also, you don't need the class variable. Class instance variable is good enough:
class Foo
@available_ids = read_only_value
def initialize
@available_ids = self.class.instance_variable_get :@available_ids
end
end
Rails has a built-in cache , so something along these lines should work: (based on the example in the docs)
class Foo
def available_ids
Rails.cache.fetch('available_ids', expires_in: 24.hours) do
API.fetch_ids
end
end
end
How about using redis/memcache? Cache the api values in redis/memcache and set expiry to one month or little shorter. In the initializer always pull from cache, and if cache is nil, then call the method that calls the real api and again caches it.
In the case of your code, the API would be accessed when the class gets defined, which means it would be accessed only once, when you start your app. You can see this clearly with this example:
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
When trying to manage API access, the best practice is to wrap your API in its own class. Doing so lets you keep track of API calls and cache values independently of the objects using the 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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.