Ruby/Rails class 变量什么时候初始化?

When do Ruby/Rails class variables get initilalized?

我目前面临的问题是,我的所有对象都使用具有 "same" 初始值的实例变量,而我只读了这个。

def initialize
    @available_ids = read_only_value
end

但棘手的事情来了:这个 "read_only_value" 是从 API 中检索到的。为了简单起见,我们假设该值是一个整数数组,并且每月更改一次。

但我不想通过在每次创建对象时检索值来拒绝 API。如果我改用 class 变量呢?

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:我想最好的选择是将 ID 保存在数据库中,并偶尔或按需检查一次 ID。

使用redis/memcache怎么样?在 redis/memcache 中缓存 api 值并将到期时间设置为一个月或更短。在初始化器中总是从缓存中提取,如果缓存为零,则调用调用真正的方法 api 并再次缓存它。

加载 class 时发生(阅读:在 Rails 启动期间。)

此外,您不需要 class 变量。 Class 实例变量就足够了:

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

对于您的代码,API 将在定义 class 时被访问,这意味着它只会在您启动应用程序时被访问一次。你可以通过这个例子清楚地看到这一点:

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 包装在自己的 class 中。这样做可以让您独立于使用 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

Rails 有一个内置的 cache,所以按照这些思路应该可以工作:(基于文档中的示例)

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