1

クラス メソッド (監査可能) に渡されたオプションをインスタンス メソッドで使用できるようにしたいと考えています。モジュールを使用して、クラス メソッドとインスタンス メソッドの両方を混在させています。

当然の選択はクラス変数を使用することですが、アクセスしようとするとエラーが発生します。

Auditable 内の初期化されていないクラス変数 @@auditable_only_once

class Document
  include Auditable
  auditable :only_once => true
end

# The mixin
module Auditable
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def auditable(options = {})

      options[:only_once] ||= false

      class_eval do
        # SET THE OPTION HERE!!
        @@auditable_only_once = options[:only_once]
      end
      end
    end

    private

    def audit(action)
      # AND READ IT BACK LATER HERE
      return if @@auditable_only_once && self.audit_item
      AuditItem.create(:auditable => self, :tag => "#{self.class.to_s}_#{action}".downcase, :user => self.student)
    end    
  end

これを少し読みやすくするためにコードの一部を削除しました。完全なコードは次のとおりです: https://gist.github.com/1004399 (編集: Gist にソリューションが含まれるようになりました)

4

1 に答える 1

0

クラス インスタンス変数の使用@@は不規則であり、それらが厳密に必要とされる機会は非常にまれです。ほとんどの場合、それらは問題や混乱を引き起こしているように見えます。通常、クラス コンテキストでは通常のインスタンス変数を問題なく使用できます。

この種のものには別のテンプレートを使用することをお勧めします。mattr_accessorActiveSupport が提供する がある場合は、その変数の代わりにそれを使用するか、コンポーネントに独自の同等のものをいつでも記述できClassMethodsます。

私が使用したアプローチの 1 つは、拡張機能をフックと実装の 2 つのモジュールに分割することです。フックは、必要に応じて残りのメソッドを追加するために使用できる基本クラスにメソッドを追加するだけですが、それ以外の場合は名前空間を汚染しません。

module ClassExtender
  def self.included(base)
    base.send(:extend, self)
  end

  def engage(options = { })
    extend ClassExtenderMethods::ClassMethods
    include ClassExtenderMethods::InstanceMethods

    self.class_extender_options.merge!(options)
  end
end

このengageメソッドは、例のように、好きなように呼び出すことができますauditable

次に、実行時に拡張機能が追加するクラス メソッドとインスタンス メソッドのコンテナー モジュールを作成します。

module ClassExtenderMethods
  module ClassMethods
    def class_extender_options
      @class_extender_options ||= {
        :default_false => false
      }
    end
  end

  module InstanceMethods
    def instance_method_example
      :example
    end
  end
end

この場合class_extender_options、特定のクラスのオプションを照会または変更するために使用できる単純なメソッドがあります。これにより、インスタンス変数を直接使用する必要がなくなります。インスタンスメソッドの例も追加されています。

簡単な例を定義できます。

class Foo
  include ClassExtender

  engage(:true => true)
end

次に、正常に動作していることをテストします。

Foo.class_extender_options
# => {:default_false=>false, :true=>true}

foo = Foo.new
foo.instance_method_example
# => :example
于 2011-06-02T15:37:45.883 に答える