如何在Rails 4中使用concerns

29 浏览
0 Comments

如何在Rails 4中使用concerns

默认的Rails 4项目生成器现在会在控制器和模型下创建\"concerns\"目录。我找到了一些关于如何使用路由关心的解释,但没有关于控制器或模型的。

我非常确定这与社区中当前的\"DCI潮流\"有关,并希望尝试一下。

问题是,我应该如何使用这个功能,是否有规定的命名/类层次结构定义方式才能使其正常工作?我如何在模型或控制器中包含关心?

admin 更改状态以发布 2023年5月21日
0
0 Comments

我一直在研究如何使用模型关注将臃肿的模型精简化,并使您的模型代码保持DRY。以下是一些带有示例的解释:

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

我们可以发现,活动和文章都有一段重复的代码。使用关注(concerns),我们可以将这段共同的代码提取到一个单独的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.rbcommentable.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

现在使用 Concerns,你的 Event 模型可以简化为

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

* 在使用 Concerns 时,建议采用“基于领域”而不是“基于技术”的分组。基于领域的分组是如“Commentable”、“Photoable”、“Attendable”,而基于技术的分组则是“ValidationMethods”、“FinderMethods”等。

0
0 Comments

所以我自己找出了它所指的概念。它实际上是一个相当简单但功能强大的概念。它与示例中的代码重用有关。基本上,这个想法是提取公共的和/或上下文特定的代码块,以清理模型并避免它们变得过于臃肿和杂乱。

作为一个例子,我会放一个众所周知的模式,即可标记模式:

# 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包装器一起,这足以使这种轻量级的因素机制发挥作用。

0