Попытка понять использование class_eval
-
11-10-2019 - |
Вопрос
Я использую Рельсы Gem, и я пытаюсь понять, как вы добавляете функции в классы ActiveRecord (я строю свою собственную библиотеку для карточных игр), и я заметил, что этот драгоценный камень использует один из методов метапрограммирования, чтобы добавить функцию в ActiveRecord :: Базовый класс (я далеко от мета-программирования мастера в Руби, но я пытаюсь узнать это)
module RailsSettings
class Railtie < Rails::Railtie
initializer 'rails_settings.initialize', :after => :after_initialize do
Railtie.extend_active_record
end
end
class Railtie
def self.extend_active_record
ActiveRecord::Base.class_eval do
def self.has_settings
class_eval do
def settings
RailsSettings::ScopedSettings.for_thing(self)
end
scope :with_settings, :joins => "JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}')",
:select => "DISTINCT #{self.table_name}.*"
scope :with_settings_for, lambda { |var| { :joins => "JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}') AND
settings.var = '#{var}'" } }
scope :without_settings, :joins => "LEFT JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}')",
:conditions => 'settings.id IS NULL'
scope :without_settings_for, lambda { |var| { :joins => "LEFT JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}') AND
settings.var = '#{var}'",
:conditions => 'settings.id IS NULL' } }
end
end
end
end
end
end
Что я не понимаю, так это то, почему он использует class_eval на Activerecord :: Base, разве он не проще, если он просто откроет Activerecord :: Base Class и определит функции? Особенно, что в блоке нет ничего динамичного (я имею в виду под динамикой, когда вы делаете class_eval или exante_eval на строке, содержащей переменные)
что-то вроде этого:
module ActiveRecord
class Base
def self.has_settings
class_eval do
def settings
RailsSettings::ScopedSettings.for_thing(self)
end
scope :with_settings, :joins => "JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}')",
:select => "DISTINCT #{self.table_name}.*"
scope :with_settings_for, lambda { |var| { :joins => "JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}') AND
settings.var = '#{var}'" } }
scope :without_settings, :joins => "LEFT JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}')",
:conditions => 'settings.id IS NULL'
scope :without_settings_for, lambda { |var| { :joins => "LEFT JOIN settings ON (settings.thing_id = #{self.table_name}.#{self.primary_key} AND
settings.thing_type = '#{self.base_class.name}') AND
settings.var = '#{var}'",
:conditions => 'settings.id IS NULL' } }
end
end
end
end
Я понимаю, что второй class_eval (до настройки DEF) - это определение функций на лету на каждом классе, которые «есть», верно? Тот же вопрос здесь, я думаю, он мог бы использовать «def self.settings» вместо «class_eval .... def settings», нет?
Решение
какая Рельсы Код считается хорошей практикой: он связывается со сторонними модулями только тогда, когда его явно просят сделать это. Таким образом, вы также держите пространства имен аккуратно разделены, и весь ваш код остается в ваших модулях.