1

Я создаю модель под названием «Конфигурация», и у меня есть следующий код, и я хочу сделать его более динамичным с помощью метапрограммирования.Ruby and Rails: переменные метапрограммирования становятся методами класса

В таблице базы данных для модели конфигурации у меня есть следующие данные.

--------------------------------------------------------- 
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 должен сделать следующее в рельсах консоли:

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

> "MyCompany" 

Я считаю, что это неприемлемо способ делать вещи. Во-первых, он будет обращаться к базе данных каждый раз, когда кто-то проверяет имя компании. Я думаю, что если я смогу загрузить его, когда приложение запустится, и ему не придется снова обращаться к нему, потому что он в памяти, тогда было бы лучше. Как я могу сделать что-то более динамичное, чтобы я мог получить доступ к значению «MyCompany», как это.

Configuration.company_name 

> "MyCompany" 

Причина этого заключается в том, чтобы дать возможность быстрой настройки приложения.

+1

Вы уверены, что это действительно проблема? Rails довольно хорош в отношении запросов кэширования к базе данных, поэтому я не ожидал запроса базы данных каждый раз, когда кто-то захочет имя company_name. Как и знаменитая цитата Дональда Кнута: «... преждевременная оптимизация - это корень всего зла». –

+0

Я думаю, что это часть проблемы. Когда я проверяю production.log, он продолжает отправлять запрос в MySQL, что не является идеальным. Основная проблема заключается в доступе к переменным как методу класса вместо того, чтобы получить find_by_variable или где («переменная =?», Переменная), которая является уродливой. –

+0

Просто используйте хэш. – lucapette

ответ

5
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"] 

Если вам большое количество параметров конфигурации, это может быть полезно для предварительной загрузки кэша путем доступа к параметрам конфигурации в файле инициализации , Если у вас есть 1000s переменных конфигурации вы, возможно, придется рассмотреть вопрос о переносе кэша в 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 
+0

СПАСИБО много! Оба способа отлично работали.Я попытался использовать define_singleton_method и define_method, но он работал только в консоли, а не в приложении rails. –

0

Одна вещь, которую вы получаете здесь не так, это не будет никогда быть Configuration.company_name, то будет, как получить доступ к свойству класса вместо того, Объект/экземпляр property,

Это должен быть экземпляр класса конфигурации. Было бы приемлемо использовать метод @ KandagaBoggu в другом ответе, но доступ к базе данных по-прежнему почти каждый раз или из кэша запросов Active Record. Но в кэше запросов AR сохраняется продолжительность определенного действия (то есть запрос). Возможно, вам захочется использовать что-то вроде Memcached, чтобы объекты выживали дольше.

+0

Использование прецедента chim является действительным кандидатом на одноэлементный шаблон. Взгляните на модуль «Rails» https://github.com/rails/rails/blob/master/railties/lib/rails.rb. Или классы конфигурации в библиотеке Sunspot: https://github.com/sunspot/sunspot/blob/master/sunspot/lib/light_config.rb. –

+0

Спасибо! Да, доступ к db, но поскольку область и количество переменных конфигурации меняются на данном этапе, я думаю, что сейчас это компромисс OK. Я, вероятно, сгенерирую конфигурационный файл в инициализации на основе модели, как только мои переменные будут более «стабильными». –

+0

@ KandadaBoggu да, я так и думал, но я не был полностью уверен, что он собирался «Singelton» в этом случае, мой плохой. –

0

Мы можем перенести эти постоянные значения в файл yml, когда сервер начинает загружать их в переменную и обращаться к ней в случае необходимости.

+0

Это было бы хорошей идеей, но область действия продукта меняется, поэтому мне нужно создать переменные config на лету и получить к ним доступ во время выполнения. Благодаря! –