1

Ruby on Rails v3.2.2 を使用しています。モジュールで、クラスを「動的に」開こうとして、ローカル変数を使用する Ruby on Rails の「スコープ メソッド」を次のように追加しようとしています。

module MyModule
  extend ActiveSupport::Concern

  included do
    # Note: The `CLASS_NAME` is not the class where `MyModule` is included. That
    # is, for instance, if the including class of `MyModule` is `Article` then
    # the `CLASS_NAME` is `User`.
    CLASS_NAME           = self.get_class_name.constantize # => User
    counter_cache_column = self.get_counter_cache          # => "counter_count"

    class CLASS_NAME
      def self.order_by_counter
        order("#{counter_cache_column} DESC")
      end
    end
  end
end

上記のコードを実行すると、次のエラーが発生します。

NameError
undefined local variable or method `counter_cache_column' for #<Class:0x0000010775c558>

counter_cache_columnモジュールのコンテキストで in が呼び出されないために発生します。order_by_counterスコープメソッドを適切に記述するにはどうすればよいですか?


おまけ:上記の「非常に動的な」実装についてアドバイスはありますか?

4

4 に答える 4

3

によって提供されるincludedブロックはActiveSupport::Concern、包含クラスのスコープ内で評価されます。つまり、このブロック内でクラスを「再開」したことになります。インクルード クラスがから継承する場合、、、、などの任意ActiveRecord::Baseの AR クラス マクロを使用できscopeます。has_manyattr_accessible

module MyModule
  extend ActiveSupport::Concern

  included do
    scope :order_by_counter, order("#{self.get_counter_cache} DESC")
  end

end

これは、「get_counter_cache」がクラスを含むクラスのクラス メソッドとして既に定義されていることを前提としています (これは、表示したコードからは明らかではありません)。

于 2012-10-16T12:08:14.080 に答える
1

counter_cache_columnローカル変数です。ローカル変数は、それらが定義されているスコープに対してローカルです (そのため、ローカル変数と呼ばれます)。

この場合、スコープは に渡されるブロックincludedです。

クラス定義とメソッド定義により、新しい空のスコープが作成されます。ブロックのみがネストされたスコープを作成するため、ブロックを使用してメソッドを定義する必要があります。ありがたいことに、これを行う方法があります: にブロックを渡すことによってdefine_method:

module MyModule
  extend ActiveSupport::Concern

  included do
    klass                = get_class_name.constantize # => User
    counter_cache_column = get_counter_cache          # => "counter_count"

    klass.define_singleton_method(:order_by_counter) {
      order("#{counter_cache_column} DESC")
    }
  end
end

その他のスタイルの改善を行いました。

  • selfはRubyの暗黙の受信者であり、指定する必要はありません
  • CLASS_NAME誤解を招く: クラスの名前は含まれていません。クラス自体が含まれています。
  • また、なぜそれが定数である必要があるのか​​ わかりません
于 2012-10-16T12:42:10.390 に答える
0

ローカル変数は、再オープンされたクラスには渡されません。

module MyModule
  extend ActiveSupport::Concern

  included do
    counter_cache_column = self.get_counter_cache # => "counter_count"

    class_eval <<-RUBY, __FILE__, __LINE__+1
      def self.order_by_counter               # def self.order_by_counter
        order("#{counter_cache_column} DESC") #   order("counter_count DESC")
      end                                     # end
    RUBY
  end
end
于 2012-10-16T12:09:47.350 に答える
-1

あなたが望むものを達成するための多くの迅速で汚い方法があります。たとえば、シンボル 'counter_cache_column' がそのスコープ外の何かを意味するようにしたい場合は、ローカル変数ではなくメソッドとして宣言できます。

included do
  CLASS_NAME           = self.get_class_name.constantize # => User
  def counter_cache_column; get_counter_cache end        # => "counter_count"

  class CLASS_NAME
    def self.order_by_counter
      order("#{counter_cache_column} DESC")
    end
  end
end
于 2012-10-16T12:10:27.490 に答える