简体   繁体   中英

Ruby and Rails: Metaprogramming variables to become class methods

I'm creating a model called Configuration and I have the following code and I want to make it more dynamic by using metaprogramming.

In a table on the database for Configuration model I have the following data.

---------------------------------------------------------
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

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

Currently, the only way to access the company_name is to do the following in rails console:

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

> "MyCompany"

I think this is an unacceptable way to do things. First, it will access the database everytime someone checks for the company's name. I think if I could load it when the app starts and doesn't have to access it again because it's in the memory, then it would be better. How can I do something more dynamic so I could access the value "MyCompany" like this.

Configuration.company_name

> "MyCompany"

The reason to do this is to give allow fast customization of the application.

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

Now you can access your configuration as :

Configuration["company_name"]

If you a large number of configuration parameters, it might be beneficial to pre-load the cache by accessing a configuration parameter in an initializer file. If you have 1000s of configuration variables you might have to consider migrating the cache to memcached etc.

If you want to access the configuration parameter as a class method:

class Configuration  < ActiveRecord::Base

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

end

Now you can access the parameter as follows:

Configuration.company_name

One thing you are getting wrong here,it will be never be Configuration.company_name , thats like access a Class property instead of a Object/Instance property,

It should be a instance of the Configuration Class. It would still be somewhat acceptable to use @KandagaBoggu's method in the other answer, but database access still almost everytime or from the Active Record's query cache.But AR's query cache lives for the duration of a particular action (ie request ). You may want use something like Memcached for the objects to survive longer.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM