web-dev-qa-db-ja.com

Rails 4で懸念事項を使用する方法

デフォルトのRails 4プロジェクトジェネレータは、コントローラとモデルの下にディレクトリ「立」を作成します。ルーティングに関する注意事項の使用方法についての説明はいくつかありますが、コントローラやモデルについては何もありません。

私はそれがコミュニティの現在の「DCIの傾向」と関係があると確信しており、それを試してみたいと思います。

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

607
yagooar

だから私は自分でそれを見つけました。それは実際にはかなり単純だが強力な概念です。以下の例のようにコードを再利用する必要があります。基本的に、アイデアは、モデルをクリーンアップし、それらが太りすぎて面倒になり過ぎないようにするために、コードの共通部分やコンテキスト固有の部分を抽出することです。

例として、よく知られているパターン、タグ付け可能なパターンを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

したがって、Productサンプルに従って、Taggableを任意のクラスに追加してその機能を共有することができます。

これは _ dhh _ でよく説明されています。

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

600
yagooar

モデルコードをDRYするだけでなく、脂肪モデルをスキン化するために モデルの問題 を使うことについて読んできました。例を挙げて説明します。

1)モデルコードの乾燥

Articleモデル、Eventモデル、およびCommentモデルを考えます。記事やイベントにはたくさんのコメントがあります。コメントはArticleまたはEventに属します。

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

コメントモデル:

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 /懸念に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 /懸念事項/イベントフォルダにattendable.rbおよびcommentable.rbファイルを作成します。

attendable.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'などを意味します

367
Aaditi Jain

懸念を使用することは多くの人にとって悪い考えと考えられていることを言及する価値があります。

  1. この男のように
  2. そしてこれ

いくつかの理由:

  1. 舞台裏で暗黒の魔法が起こっています - includeメソッドにパッチをあてること、依存関係処理システム全体があります - ささいな古き良きRuby mixinパターンにとっては複雑すぎる。
  2. あなたのクラスはそれほど乾燥していません。さまざまなモジュールに50のパブリックメソッドを組み込んでそれらをインクルードしても、クラスには50のパブリックメソッドが残っています。そのコードの匂いを隠し、ゴミを引き出しに入れてしまうだけです。
  3. コードベースは実際には周りのすべてのこれらの懸念をナビゲートすることは困難です。
  4. あなたのチームのすべてのメンバーが、何が本当に懸念に代わるべきかを同じ理解を持っていると確信していますか?

気になることがあるので、気にしないでください。

94
Dr.Strangelove

この記事 は私が懸念を理解するのを助けました。

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

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end
54
aminhotob

ActiveSupport::Concernがどのようにmoduleに価値を付加するのではなく、ここに挙げた例の大部分がmoduleの力を実証していると感じました。

例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

インスタンスメソッド、クラスメソッド、そしてインクルードブロックはそれほど面倒ではありません。懸念はあなたのためにそれらを適切に注入するでしょう。 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

この例でBarHostが本当に必要とするモジュールです。しかし、BarFooと依存関係を持っているので、Hostクラスはinclude Fooを持つ必要があります(しかしHostFooについて知りたいのであれば、どうしたらいいですか?).

そのため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_klassBarモジュール自体ではなくBarを含むクラスthatsにインジェクトしなければならないので、これはうまくいきません。

出典: Rails ActiveSupport :: Concern

36
shiva

懸念するファイル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

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

class Role < ActiveRecord::Base
  include TestConcern
end
6
Sajjad Murtaza