ruby-on-rails - 如何将常量延迟加载到rails环境中

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

我有一组 API key ,我想将其加载到 Rails 环境中,以便轻松频繁地访问(无需访问数据库)。我怎样才能延迟加载它:

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

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

3)如果有人更改了 api key ,我可以更新 API_KEY 哈希(如果该 key 存在)。

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

您还发现这种方法存在其他问题吗?数据集(API key 数量)是有限的。

最佳答案

由于您实际上只是将值存储在哈希中(将其分配给常量是无关紧要的,因为您没有卡住它或任何东西),所以我会使用 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

现在,当您使用不存在的 key 访问 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

附注如果您定义 APIKey.const_missing 这样您就可以执行 APIKey::S3_ACCESS 而不是 APIKey[:S3_ACCESS] ,您会得到荒谬的过度实现点。

关于ruby-on-rails - 如何将常量延迟加载到rails环境中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20070993/

相关文章:

ruby-on-rails - Rails 错误:未初始化常量 ApplicationController::Authentication

ruby-on-rails - 在 Rails 中验证网站所有权

ruby - gem date_validator 不工作 Ruby 1.9.3 Rails 3.2.1

ruby-on-rails - Rails - 如何显示像 Twitter 一样创建的时间

python - Python 的通用桥接框架

javascript - 谷歌地图和 DART

ruby-on-rails - Controller 测试和 stub 过滤器/操作。指导方针是什么?

jquery - 如何在 Rails select_tag 上显示 Font Awesome 图标

ruby-on-rails - CanCan 中最安全和最可靠的方式来执行访客、用户、管理员权限

facebook - 将自定义 css 文件添加到 Facebook "Activity Feed"