2

次のようなフォルダー構造があります。

app/models/
    concerns/
        quxable.rb
    foo/
        bar.rb
        baz.rb

私はRails 3にいるので、懸念事項を次のものでオートロードしました:

config.autoload_paths += Dir[Rails.root.join('app', 'models', "concerns", '**/')]

そして、ファイルは次のとおりです。

quxable.rb

module Quxable
    extend ActiveSupport::Concern        

    module ClassMethods
        def new_method
        end
    end
end

bar.rb

class Foo::Bar < ActiveRecord::Base
    include Quxable
end

baz.rb

class Foo::Baz < ActiveRecord::Base
    include Quxable
end

コンソールでこれを行うと、次の出力が得られます。

Foo::Bar.respond_to? :new_method #=> true
Foo::Baz.respond_to? :new_method #=> false
reload!
Foo::Baz.respond_to? :new_method #=> true
Foo::Bar.respond_to? :new_method #=> false

そのため、最初にアクセスしたモデルにのみ適切に含まれているように見えます。それでも、次を実行すると:

ActiveRecord::Base.descendants.select{ |c| c.included_modules.include?(Quxable) }.map(&:name)

私は得る["Foo::Bar", "Foo::Baz"]

ここで何が起こっているのか分かりますか?オートローディング/イーガーローディングで何かを推測していますが、両方のモデルが新しいクラス メソッドを取得していない理由がわかりません。

PS - 以下を使用せずにモジュールを書き直そうとしましたActiveSupport::Concern(Rails の古いバージョンを使用していて、暗がりで撮影しているという理由だけで):

def include(base)
    base.send :extend, ClassMethods
end

しかし、私はまだ同じ問題を抱えています。

編集

最初はこれを省略しました (最も単純な問題を提示しようとしているだけです)。しかし、quxable.rb実際には次のようになります。

module Quxable
    extend ActiveSupport::Concern 

    LOOKUP = {
        Foo::Bar => "something",
        Foo::Baz => "something else"
    }

    module ClassMethods
        def new_method
        end
    end
end

だから私は Class オブジェクトで定数を定義するある種の循環依存関係を作成したと推測しています。誰でも確認できますか?ただし、2番目にアクセスされるクラスのクラスメソッドを定義しないことで、サイレントに失敗するのは奇妙です。なぜだかわかりませんか?

4

2 に答える 2

2

あなたの編集に基づいて、このコードは問題があります:

LOOKUP = {
    Foo::Bar => "something",
    Foo::Baz => "something else"
}

モジュールが完了する前に Foo::Bar をインスタンス化します。よってnew_method省略します。この場合の伝統は、ルックアップで文字列を使用し、それらを .constantize して必要に応じてクラスに変換することです。

LOOKUP = {
    "Foo::Bar" => "something",
    "Foo::Baz" => "something else"
}

それから

LOOKUP.keys.first.constantize.new_method

また

result = LOOKUP[Foo::Bar.name]

それを使用する。

于 2015-12-11T18:10:32.900 に答える
1

タイプミスがあると思います。懸念事項には、ミックスインの制限を超越できる魔法が含まれています。

また、'models' のように、既に自動ロードされているディレクトリで作業している場合は、そのディレクトリ名にすべての名前を付けてください。

これを試して:

module Concerns
  module Quxable

    extend ActiveSupport::Concern

    included do
      def self.new_method
      end
    end
  end
end


module Foo
  class Baz < ActiveRecord::Base
    include Concerns::Quxable
  end
end

私が覚えている限りでは、追加の autoload ディレクティブは必要ありません。モデルの下のディレクトリで名前空間を使用するだけで機能するからです。


コメントの後に編集:

次のファイルを追加して Rails プロジェクトをセットアップしました。

アプリ/モデル/foo/doer.rb

アプリ/モデル/foo/thinker.rb

アプリ/モデル/懸念/thingable.rb

thingable.rb は次のとおりです。

module Concerns
  module Thingable
    extend ActiveSupport::Concern
    included do
      def self.thing
      end
    end
  end
end

doer.rb は次のとおりです。

module Foo
  class Doer < ActiveRecord::Base
    include Concerns::Thingable
  end
end

thinker.rb は次のとおりです。

module Foo
  class Thinker < ActiveRecord::Base
    include Concerns::Thingable
  end
end

コンソールで:

開発環境のロード (Rails 3.2.22)

2.1.3 :001 > Foo::Doer.respond_to? :もの

=>真

2.1.3 :002 > Foo::Thinker.respond_to? :もの

=>真

2.1.3 :003 > リロード!

リロード中...

=>真

2.1.3 :004 > Foo::Doer.respond_to? :もの

=>真

2.1.3 :005 > Foo::Thinker.respond_to? :もの

=>真

2.1.3 :006 >

オートローディングはまったく変更しませんでした。名前空間に基づいてファイルを検索するために Rails に依存していました。(「models」などの既知のディレクトリの下にあるディレクトリには名前空間を使用します)

自動ロードをデフォルトにリセットしてから、ファイルの場所と名前空間に Rails の規則を使用します。それがうまくいかない場合は、あなたのプロジェクトが私が知らない他のことをしている可能性があります。

詳細を提供できる場合はお知らせください。

于 2015-12-11T16:51:13.213 に答える