デフォルトのRails 4プロジェクトジェネレータは、コントローラとモデルの下にディレクトリ「立」を作成します。ルーティングに関する注意事項の使用方法についての説明はいくつかありますが、コントローラやモデルについては何もありません。
私はそれがコミュニティの現在の「DCIの傾向」と関係があると確信しており、それを試してみたいと思います。
問題は、この機能をどのように使用することになっているのでしょうか。それを機能させるために命名/クラス階層を定義する方法に関する規約はありますか?モデルやコントローラーに懸念を含めるにはどうすればよいですか。
だから私は自分でそれを見つけました。それは実際にはかなり単純だが強力な概念です。以下の例のようにコードを再利用する必要があります。基本的に、アイデアは、モデルをクリーンアップし、それらが太りすぎて面倒になり過ぎないようにするために、コードの共通部分やコンテキスト固有の部分を抽出することです。
例として、よく知られているパターン、タグ付け可能なパターンを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ラッパーと組み合わせることで、この軽量ファクタリングメカニズムを輝かせるのに十分なサポートです。
モデルコードをDRYするだけでなく、脂肪モデルをスキン化するために モデルの問題 を使うことについて読んできました。例を挙げて説明します。
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
イベントモデルを考えます。イベントには多くの参加者とコメントがあります。
通常、イベントモデルは次のようになります。
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'などを意味します
懸念を使用することは多くの人にとって悪い考えと考えられていることを言及する価値があります。
いくつかの理由:
include
メソッドにパッチをあてること、依存関係処理システム全体があります - ささいな古き良きRuby mixinパターンにとっては複雑すぎる。気になることがあるので、気にしないでください。
この記事 は私が懸念を理解するのを助けました。
# app/models/trader.rb
class Trader
include Shared::Schedule
end
# app/models/concerns/shared/schedule.rb
module Shared::Schedule
extend ActiveSupport::Concern
...
end
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
この例で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
を含むクラスthatsにインジェクトしなければならないので、これはうまくいきません。
懸念するファイル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