638

デフォルトの Rails 4 プロジェクト ジェネレーターは、コントローラーとモデルの下に "concerns" ディレクトリを作成するようになりました。ルーティングの問題の使用方法についてはいくつかの説明がありましたが、コントローラーやモデルについては何も見つかりませんでした。

コミュニティの現在の「DCI トレンド」と関係があると確信しているので、試してみたいと思います。

問題は、この機能をどのように使用すればよいか、それを機能させるために命名/クラス階層を定義する方法に関する規則はありますか? モデルまたはコントローラーに懸念事項を含めるにはどうすればよいですか?

4

6 に答える 6

627

だから自分で見つけました。これは実際には非常に単純ですが、強力な概念です。以下の例のように、コードの再利用に関係しています。基本的に、アイデアは、モデルをクリーンアップし、モデルが太りすぎたり乱雑になったりしないようにするために、コードの一般的なチャンクおよび/またはコンテキスト固有のチャンクを抽出することです。

例として、よく知られている 1 つのパターン、タグ付け可能なパターンを示します。

# app/models/product.rb
class Product
  include Taggable

  ...
end

# app/models/concerns/taggable.rb
# notice that the file name has to match the module name 
# (applying Rails conventions for autoloading)
module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, as: :taggable
    has_many :tags, through: :taggings

    class_attribute :tag_limit
  end

  def tags_string
    tags.map(&:name).join(', ')
  end

  def tags_string=(tag_string)
    tag_names = tag_string.to_s.split(', ')

    tag_names.each do |tag_name|
      tags.build(name: tag_name)
    end
  end

  # methods defined here are going to extend the class, not the instance of it
  module ClassMethods

    def tag_limit(value)
      self.tag_limit_value = value
    end

  end

end

したがって、製品サンプルに従って、必要なクラスにタグ付け可能を追加し、その機能を共有できます。

これはDHHによってかなりよく説明されています:

Rails 4 では、自動的にロード パスの一部となるデフォルトの app/models/concerns および app/controllers/concerns ディレクトリで懸念事項を使用するようにプログラマーを招待します。ActiveSupport::Concern ラッパーと組み合わせると、この軽量なファクタリング メカニズムを輝かせるのに十分なサポートになります。

于 2013-02-25T22:50:30.447 に答える
387

モデルの懸念を使用して、脂肪モデルをスキンナイズし、モデルコードをドライアップすることについて読んでいます。以下に例を挙げて説明します。

1) モデルコードのドライアップ

Article モデル、Event モデル、Comment モデルを考えてみましょう。記事やイベントには多くのコメントがあります。コメントは記事またはイベントのいずれかに属します。

伝統的に、モデルは次のようになります。

コメント モデル:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

記事のモデル:

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #return the article with least number of comments
  end
end

イベントモデル

class Event < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #returns the event with least number of comments
  end
end

お気づきのように、Event と Article の両方に共通する重要なコードがあります。懸念を使用して、この共通コードを別のモジュール Commentable に抽出できます。

このために、app/models/concerns に commentable.rb ファイルを作成します。

module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, as: :commentable
  end

  # for the given article/event returns the first comment
  def find_first_comment
    comments.first(created_at DESC)
  end

  module ClassMethods
    def least_commented
      #returns the article/event which has the least number of comments
    end
  end
end

そして今、あなたのモデルは次のようになります:

コメント モデル:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

記事のモデル:

class Article < ActiveRecord::Base
  include Commentable
end

イベント モデル:

class Event < ActiveRecord::Base
  include Commentable
end

2) スキンナイズ脂肪モデル。

イベントモデルを考えてみましょう。イベントには多くの出席者とコメントがあります。

通常、イベント モデルは次のようになります。

class Event < ActiveRecord::Base   
  has_many :comments
  has_many :attenders


  def find_first_comment
    # for the given article/event returns the first comment
  end

  def find_comments_with_word(word)
    # for the given event returns an array of comments which contain the given word
  end 

  def self.least_commented
    # finds the event which has the least number of comments
  end

  def self.most_attended
    # returns the event with most number of attendes
  end

  def has_attendee(attendee_id)
    # returns true if the event has the mentioned attendee
  end
end

多くの関連付けを持つモデルや、それ以外の場合は、ますます多くのコードが蓄積されて管理不能になる傾向があります。懸念は、脂肪モジュールをスキンナイズして、よりモジュール化して理解しやすくする方法を提供します。

上記のモデルは、次のように懸念事項を使用してリファクタリングできます: app/models/concerns/event フォルダにattendable.rbandファイルを作成します。commentable.rb

出席可能な.rb

module Attendable
  extend ActiveSupport::Concern

  included do 
    has_many :attenders
  end

  def has_attender(attender_id)
    # returns true if the event has the mentioned attendee
  end

  module ClassMethods
    def most_attended
      # returns the event with most number of attendes
    end
  end
end

commentable.rb

module Commentable
  extend ActiveSupport::Concern

  included do 
    has_many :comments
  end

  def find_first_comment
    # for the given article/event returns the first comment
  end

  def find_comments_with_word(word)
    # for the given event returns an array of comments which contain the given word
  end

  module ClassMethods
    def least_commented
      # finds the event which has the least number of comments
    end
  end
end

そして今、懸念を使用すると、イベントモデルは次のように縮小されます

class Event < ActiveRecord::Base
  include Commentable
  include Attendable
end

*懸念事項を使用している間は、「技術的」グループ化ではなく「ドメイン」ベースのグループ化を行うことをお勧めします。ドメインベースのグループ化は、「コメント可能」、「写真可能」、「参加可能」のようなものです。技術的なグループ化は、「ValidationMethods」、「FinderMethods」などを意味します

于 2014-09-15T22:50:58.713 に答える
57

この投稿は、懸念を理解するのに役立ちました。

# app/models/trader.rb
class Trader
  include Shared::Schedule
end

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end
于 2013-03-17T19:20:20.663 に答える
50

ここにある例のほとんどは、に付加価値を与えるmodule方法ではなく、 の力を示していると感じました。ActiveSupport::Concernmodule

例 1:より読みやすいモジュール。

したがって、これが典型的な方法であることを心配moduleする必要はありません。

module M
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  def instance_method
    ...
  end

  module ClassMethods
    ...
  end
end

でリファクタリングした後ActiveSupport::Concern

require 'active_support/concern'

module M
  extend ActiveSupport::Concern

  included do
    scope :disabled, -> { where(disabled: true) }
  end

  class_methods do
    ...
  end

  def instance_method
    ...
  end
end

インスタンス メソッド、クラス メソッド、およびインクルード ブロックがそれほど乱雑ではないことがわかります。懸念はあなたのためにそれらを適切に注入します。を使用する利点の 1 つActiveSupport::Concernです。


例 2:モジュールの依存関係を適切に処理します。

module Foo
  def self.included(base)
    base.class_eval do
      def self.method_injected_by_foo_to_host_klass
        ...
      end
    end
  end
end

module Bar
  def self.included(base)
    base.method_injected_by_foo_to_host_klass
  end
end

class Host
  include Foo # We need to include this dependency for Bar
  include Bar # Bar is the module that Host really needs
end

この例では、本当に必要なBarモジュールです。Hostしかし、クラスとのBar依存関係があるため、そうする必要があります(しかし、なぜ知りたいのですか?回避できますか?)。FooHostinclude FooHostFoo

そのBarため、どこにでも依存関係が追加されます。ここでは、含める順序も重要です。これにより、巨大なコード ベースに多くの複雑性/依存性が追加されます。

でリファクタリングした後ActiveSupport::Concern

require 'active_support/concern'

module Foo
  extend ActiveSupport::Concern
  included do
    def self.method_injected_by_foo_to_host_klass
      ...
    end
  end
end

module Bar
  extend ActiveSupport::Concern
  include Foo

  included do
    self.method_injected_by_foo_to_host_klass
  end
end

class Host
  include Bar # It works, now Bar takes care of its dependencies
end

今はシンプルに見えます。

モジュール自体にFoo依存関係を追加できないのはなぜですか? モジュール自体に含まれていないクラスに注入する必要があるBarため、これは機能しません。method_injected_by_foo_to_host_klassBarBar

ソース: Rails ActiveSupport::Concern

于 2015-12-03T09:11:42.950 に答える
7

懸念事項では、ファイル filename.rb を作成します

たとえば、属性 create_by が存在するアプリケーションで値を 1 で更新し、updated_by を 0 にしたい

module TestConcern 
  extend ActiveSupport::Concern

  def checkattributes   
    if self.has_attribute?(:created_by)
      self.update_attributes(created_by: 1)
    end
    if self.has_attribute?(:updated_by)
      self.update_attributes(updated_by: 0)
    end
  end

end

アクションで引数を渡したい場合

included do
   before_action only: [:create] do
     blaablaa(options)
   end
end

その後、次のようにモデルに含めます。

class Role < ActiveRecord::Base
  include TestConcern
end
于 2015-01-15T08:45:04.500 に答える