我有一组 API 密钥,我想将它们加载到 rails 环境中,以便轻松频繁地访问(无需访问数据库(。我怎样才能延迟加载它:
1(假设rails环境中有一个API_KEY哈希(我使用初始值设定项初始化
(2(当我查找密钥时,我首先在哈希API_KEY查找,如果没有找到,我从数据库中获取,同时添加到API_KEY哈希中,以便所有将来的请求都可以访问它。
3(如果有人更改了api密钥,我可以在密钥存在的情况下更新API_KEY哈希。
有人可以帮助我在 rails 环境中创建、删除和更新 API_KEY 哈希(从代码中,而不是初始 rails 加载(?如果每个乘客线程单独加载轨道环境,这种方法会不会有问题?
您看到这种方法还有其他问题吗?数据集(API 密钥的数量(是有限的。
由于您实际上只是将值存储在哈希中(将其分配给常量无关紧要,因为您没有冻结它或任何东西(,我会使用 Hash.new
的块形式。我不知道您的数据库是什么样的,但是假设您将这些值存储在名为APIKey
的模型中,该模型具有属性name
和value
:
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'
会根据需要执行UPDATE
或INSERT
。
不过,这可能是在重新发明轮子;在这一点上,你可能最好使用比我聪明得多的人编写的众多配置管理宝石之一。另外,让我们不要忘记十二因素应用程序,它劝告我们将配置存储在环境中。
附言如果你定义APIKey.const_missing
你会得到荒谬的过度实现点,这样你就可以做APIKey::S3_ACCESS
而不是APIKey[:S3_ACCESS]
。