1

私はrails-settingsgemを使用しており、ActiveRecordクラスに関数を追加する方法を理解しようとしています(カードゲーム用に独自のライブラリを構築しています)。このgemがメタプログラミングの1つを使用していることに気付きました。 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

私が理解していないのは、彼がActiveRecord :: Baseでclass_evalを使用する理由ですが、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

2番目のclass_eval(def設定の前)は、「has_settings」が正しいすべてのクラスでその場で関数を定義することであることを理解していますか?ここで同じ質問ですが、彼は「class_eval....defsettings」の代わりに「defself.settings」を使用できると思います。

4

1 に答える 1

0

rails-settingsコードが行うことは、良い習慣と見なされます。明示的に要求された場合にのみ、サードパーティのモジュールを混乱させます。このようにして、名前空間をきちんと分離し、すべてのコードをモジュールに残します。

于 2011-01-16T11:44:40.540 に答える