Попытка понять использование class_eval - PullRequest
1 голос
/ 16 января 2011

Я использую гем rails-settings и пытаюсь понять, как вы добавляете функции в классы ActiveRecord (я создаю свою собственную библиотеку для карточных игр), и я заметил, что этот гем использует один из методов метапрограммирования, чтобы добавить функцию в класс ActiveRecord :: Base (я далек от мастера метапрограммирования в ruby, но я пытаюсь научиться этому)

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_eval или instance_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) - это определение функций на лету для каждого класса, который правильно имеет has_settings? Тот же вопрос, я думаю, он мог бы использовать «def self.settings» вместо «class_eval .... def settings», нет?

1 Ответ

0 голосов
/ 16 января 2011

То, что делает код rails-settings , считается хорошей практикой: он только портит сторонние модули, когда его явно просят сделать это. Таким образом, вы также будете аккуратно разделять пространства имен, и весь ваш код останется в ваших модулях.

...