如何将延迟加载常量导入rails环境

时间:2013-11-19 11:59:45

标签: ruby-on-rails ruby-on-rails-3 api lazy-loading

我有一组API密钥,我想加载到rails环境中,以便轻松和频繁访问(无需访问数据库)。我怎么懒得加载它:

1)假设rails环境中有一个API_KEY哈希(我使用初始化程序初始化)

2)当我查找密钥时,我首先查找API_KEY哈希,如果找不到,我从数据库中获取,同时添加到API_KEY哈希,以便所有将来的请求都可以访问它。

3)如果有人更改了api密钥,我可以在密钥存在的情况下更新API_KEY哈希值。

有人可以帮我调用在rails环境中创建,删除和更新API_KEY哈希(从代码中,而不是初始rails加载)?如果每个乘客线程单独加载导轨环境,这种方法会出现问题吗?

您使用此方法看到的任何其他问题?数据集(api密钥的数量)是有限的。

1 个答案:

答案 0 :(得分:2)

因为你实际上只是在Hash中存储值(它被分配给一个常量是无关紧要的,因为你没有冻结它或任何东西)我会使用block form of Hash.new。我不知道您的数据库是什么样的,但假设您将这些值存储在名为APIKey的模型中,该模型具有属性namevalue

API_KEY = Hash.new do |hash, key_name|
  hash[key_name] = APIKey.where(:name => key_name).pluck(:value)
end

现在,当您使用不存在的密钥访问API_KEY哈希时,它将查询APIKey模型并将value属性的值分配给哈希的该元素。假设你在api_keys表中有这个:

name       value
---------  ----------------
S3_ACCESS  0123456789abcdef

然后你可以像这样访问上面定义的哈希:

puts API_KEY[:S3_ACCESS]
# Query: SELECT `value` FROM `api_keys` WHERE `name` = 'S3_ACCESS']
# => 0123456789abcdef

puts API_KEY.inspect
# => { :S3_ACCESS => "0123456789abcdef" }

puts API_KEY[:S3_ACCESS]
# No query!
# => 0123456789abcdef

如果要在运行时更新值,则可以像任何哈希一样进行更新:

API_KEY[:S3_ACCESS] = '5555555555ffffff'
# => "5555555555ffffff"

puts API_KEY.inspect
# => { :S3_ACCESS => "5555555555ffffff" }

但是,更改哈希不会更新数据库记录。

高级

如果您希望在更新哈希值时更新数据库记录,则必须覆盖Hash#[]=,如果您要去那么远,您可以直接使用ActiveRecord。例如:

class APIKey < ActiveRecord::Base
  attr_accessible :name, :value

  @@_cached_values = Hash.new do |_cached_values, key_name|
    # This will return nil if there's no record in the database with the
    # given name; alternatively you could use `first!` which would raise a
    # RecordNotFound exception which you could rescue and do something
    # useful with.
    _cached_values[key_name] = self.where(:name => key_name).pluck(:value)
  end

  def self.[](key_name)
    @@_cached_values[key_name]
  end

  def self.[]=(key_name, new_value)
    # If the database already has a value for this key_name, fetch the object;
    # otherwise initialize a new object
    api_key = self.where(:name => key_name).first_or_initialize

    # Update the value and save the record
    api_key.update_attributes!(:value => new_value)

    # Update the cached value
    @@_cached_values[key_name] = new_value
  end
end

puts APIKey[:S3_ACCESS]
# Query:  SELECT `value` FROM `api_keys` WHERE `name` = 'S3_ACCESS'
# => 0123456789abcdef

APIKey[:S3_ACCESS] = '5555555555ffffff'
# Query:  UPDATE `api_keys` SET `value` = '5555555555ffffff'
#           WHERE `name` = 'S3_ACCESS'
# => '5555555555ffffff'

APIKey[:NEW_KEY] = 'new_val'
# Query:  INSERT INTO `api_keys` (`name`, `value`)
#           VALUES ('NEW_KEY', 'new_val')
# => 'new_val'

通过这种实现,APIKey[:S3_ACCESS]的工作方式与上面的API_KEY示例相同;并且APIKey[:S3_ACCESS] = 'foo'会根据需要执行UPDATEINSERT

这可能是重新发明轮子;在这一点上,你可能最好使用比我写的更聪明的许多configuration management gems人中的一个。另外,我们不要忘记The Twelve-Factor App,我们劝告我们store config in the environment

P.S。如果您定义APIKey.const_missing,则会出现荒谬的过度实现点,因此您可以APIKey::S3_ACCESS代替APIKey[:S3_ACCESS]