Tratando de entender el uso de class_eval
-
11-10-2019 - |
Pregunta
Estoy usando los carriles-settings joya, y estoy tratando de entender cómo agrega funciones a clases ActiveRecord (estoy construyendo mi propia biblioteca de juegos de cartas), y me di cuenta de que esta joya utiliza una de las técnicas de programación Meta para agregar la función a la clase ActiveRecord :: Base (estoy lejos de maestro de meta-programación en ruby, pero estoy tratando de aprenderlo)
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
Lo que no entiendo es por qué se utiliza class_eval en ActiveRecord :: Base, ¿no es más fácil si basta con abrir la clase ActiveRecord :: Base y definir las funciones? Especialmente que no hay nada en el bloque dinámico (lo que entiendo por dinámica es cuando haces class_eval o instance_eval en una cadena que contiene variables)
algo como esto:
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
entiendo el segundo class_eval (antes de los ajustes def) es definir las funciones sobre la marcha en cada clase que la derecha '' has_settings? La misma pregunta aquí, creo que se podría utilizar "def self.settings" en lugar de "class_eval .... def ajustes", no?
Solución
Lo que rieles configuración código no se considera una buena práctica: sólo se mete con módulos de terceros cuando se le preguntó explícitamente a hacerlo. De esta manera también mantener los espacios de nombres ordenadamente separan y todos sus restos de código en sus módulos.