【问题标题】:Ruby and Rails: Metaprogramming variables to become class methodsRuby 和 Rails:元编程变量成为类方法
【发布时间】:2012-02-20 05:13:51
【问题描述】:

我正在创建一个名为 Configuration 的模型,我有以下代码,我想通过使用元编程使其更具动态性。

在配置模型的数据库表中,我有以下数据。

---------------------------------------------------------
variable_name as string     |  value in text
                            |
company_name                |  MyCompany
welcome_text                |  Welcome to MyCompany's App!
email_order_text            |  You've just created an account with MyCompany.
year_since                  |  2012
----------------------------------------------------------

class Configuration  < ActiveRecord::Base

    #nothing here yet

end

----------------------------------------------------------

目前,访问 company_name 的唯一方法是在 rails 控制台中执行以下操作:

configuration_company_name = Configuration.find_by_variable_name("company_name")
configuration_company_name.company_name

> "MyCompany"

我认为这是一种不可接受的做事方式。首先,每当有人检查公司名称时,它都会访问数据库。我想如果我可以在应用程序启动时加载它并且因为它在内存中而不必再次访问它,那么它会更好。我怎样才能做一些更动态的事情,以便我可以像这样访问值“MyCompany”。

Configuration.company_name

> "MyCompany"

这样做的原因是允许快速自定义应用程序。

【问题讨论】:

  • 你确定这真的是个问题吗? Rails 非常擅长缓存对数据库的请求,所以我不希望每次有人想要 company_name 时都会查询数据库。正如著名的 Donald Knuth 名言:“......过早的优化是万恶之源”。
  • 我认为这是问题的一部分。当我检查 production.log 时,它不断向 MySQL 发送请求,这并不理想。主要问题是将变量作为类方法访问,而不是必须获取 find_by_variable 或 where("variable =?", variable) 这很难看。

标签: ruby-on-rails ruby model metaprogramming instance-variables


【解决方案1】:
class Configuration  < ActiveRecord::Base
  # loads all the configuration variables to an in-memory
  # static hash during the first access. 
  def self.[](n)
    @config ||= {}.tap { |h| Configuration.all.each{ h[variable_name] = c.value}}
    @config[n]
  end
end

现在您可以通过以下方式访问您的配置:

Configuration["company_name"]

如果您有大量配置参数,通过访问初始化程序文件中的配置参数来预加载缓存可能会有所帮助。如果您有 1000 个配置变量,您可能不得不考虑将缓存迁移到 memcached 等。

如果你想以类方法的形式访问配置参数:

class Configuration  < ActiveRecord::Base

 klass = class << self; self; end
 Configuration.all.each{|c| klass.send(:define_method, c.variable_name){c.value}}

end

现在您可以按如下方式访问参数:

Configuration.company_name

【讨论】:

  • 非常感谢!两种方式都完美无缺。我尝试使用define_singleton_method和define_method,但它只在控制台内工作,而不是在rails应用程序上。
【解决方案2】:

你在这里弄错了一件事,它永远不会是 Configuration.company_name ,这就像访问 Class 属性而不是 Object/Instance 属性, p>

它应该是配置类的一个实例。在另一个答案中使用@KandagaBoggu 的方法仍然是可以接受的,但是数据库访问仍然几乎每次或从 Active Record 的查询缓存中访问。但是 AR 的查询缓存在特定操作(即 request )的持续时间内存在。您可能希望使用 Memcached 之类的东西来让对象存活更长时间。

【讨论】:

  • @chim 的用例是单例模式的有效候选者。看看Rails 模块github.com/rails/rails/blob/master/railties/lib/rails.rb。或者 Sunspot 库中的配置类:github.com/sunspot/sunspot/blob/master/sunspot/lib/….
  • 谢谢!是的,数据库被访问了,但是因为在这个阶段范围和配置变量的数量正在改变,我认为现在这是一个不错的权衡。一旦我的变量更“稳定”,我可能会在基于模型的初始化中生成一个配置文件。
  • @KandadaBoggu 是的,我也这么认为,但我不完全确定他在这种情况下会选择“Singelton”,我的错。
【解决方案3】:

我们可以将这些常量值移动到yml 文件中,当服务器开始将它们加载到变量中并在需要时访问它。

【讨论】:

  • 这是个好主意,但产品的范围正在发生变化,因此我需要动态创建配置变量并在运行时访问它们。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 2013-10-22
  • 2011-02-19
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
相关资源
最近更新 更多