回答:
だから私はそれを自分で見つけました。それは実際にはかなりシンプルですが強力なコンセプトです。以下の例のように、コードの再利用に関係しています。基本的に、アイデアは、モデルをクリーンアップして、モデルが太りすぎて乱雑にならないようにするために、一般的なコードやコンテキスト固有のコードのチャンクを抽出することです。
例として、よく知られたパターン、タグ付け可能なパターンを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 / concernsとapp / controllers / concernsディレクトリを使用して、自動的にロードパスに含まれる懸念事項を使用するようにプログラマーを招待します。ActiveSupport :: Concernラッパーと組み合わせることで、この軽量のファクタリングメカニズムを際立たせるのに十分なサポートが得られます。
モデルの懸念事項を使用して脂肪モデルをスキンナイズすること、およびモデルコードをDRYすることについて読んでいます。以下に例を挙げて説明します。
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
イベントモデルについて考えます。イベントには多くの参加者とコメントがあります。
通常、イベントモデルは次のようになります。
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
多くの関連付けがあるモデルや、そうでなければモデルがますます多くのコードを蓄積し、管理不能になる傾向があります。懸念事項は、脂肪モジュールをスキン化してモジュール化し、理解を容易にする方法を提供します。
上記のモデルは以下のように懸念を使用してリファクタリングすることができます作成attendable.rb
し、commentable.rb
アプリ/モデル/懸念/イベントフォルダ内のファイルを
ttendable.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」などを意味します
def self.my_class_method
)、インスタンスメソッド、およびクラススコープ内のメソッド呼び出しとディレクティブ。必要なしmodule ClassMethods
add_item
、たとえば、2つの懸念事項の両方が実装されている場合、うんざりしています。一部のバリデーターが機能しなくなったときにRailsが壊れたと思いましたが、誰かがany?
懸念を抱いて実装していました。私は別の解決策を提案します。別の言語のインターフェースのような懸念を使用します。機能を定義する代わりに、その機能を処理する個別のクラスインスタンスへの参照を定義します。次に、1つのことを行うより小さくてよりきちんとしたクラスがあります...
多くの人が懸念を使用することは悪い考えであると考えていることに言及する価値があります。
いくつかの理由:
include
方法であり、依存関係処理システム全体があります-ささいな古き良きRubyミックスインパターンでは複雑すぎます。懸念は脚で自分を撃つ簡単な方法です、それらに注意してください。
ここでの例のほとんどは、にmodule
どのようにActiveSupport::Concern
付加価値を与えるかというよりも、の力を示していると感じました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
インスタンスメソッド、クラスメソッド、含まれているブロックはそれほど複雑ではありません。懸念はあなたのためにそれらを適切に注入します。これがを使用する利点の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
とFoo
の依存関係があるHost
必要がありますinclude Foo
(しかし、なぜHost
知りたいのFoo
ですか?それを回避できますか?)。
したがって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_klass
含まれてBar
いないクラスに注入する必要があるため、これは機能しませんBar
。
懸念事項として、ファイル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