如何在Rails 4中使用问题

yag*_*oar 619 ruby-on-rails dci ruby-on-rails-4

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

我很确定它与社区当前的"DCI趋势"有关,并且想尝试一下.

问题是,我应该如何使用此功能,是否有关于如何定义命名/类层次结构以使其工作的约定?如何在模型或控制器中包含问题?

yag*_*oar 607

所以我自己发现了它.它实际上是一个非常简单但功能强大的概念.它与代码重用有关,如下例所示.基本上,我们的想法是提取常见的和/或特定于上下文的代码块,以便清理模型并避免它们变得太胖和混乱.

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

# 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
Run Code Online (Sandbox Code Playgroud)

因此,按照Product示例,您可以将Taggable添加到您想要的任何类并共享其功能.

DHH很好地解释了这一点:

在Rails 4中,我们将邀请程序员使用默认app/models/concerns和app/controllers/concerns目录中的问题,这些目录自动成为加载路径的一部分.与ActiveSupport :: Concern包装一起,它足以支持这种轻量级因子机制.

  • DCI处理Context,使用Roles作为标识符将心智模型/用例映射到代码,并且不需要使用包装器(方法在运行时直接绑定到对象),因此这与DCI无关. (11认同)
  • 值得一提的是(并且可能包括在一个示例中)类方法应该在特别命名的模块ClassMethods中定义,并且该模块也由基类扩展为ActiveSupport :: Concern. (9认同)
  • @yagooar甚至在运行时包含它也不会使它成为DCI.如果您希望看到ruby DCI示例实现.请查看http://fulloo.info或https://github.com/runefs/Moby上的示例,或者如何使用maroon在Ruby中执行DCI以及DCI是什么(http://runefs.com)( DCI是什么.是我刚刚开始的一系列帖子) (2认同)
  • @RuneFS && ciscoheat 你们都是对的。我只是再次分析了文章和事实。而且,我上周末参加了一个 Ruby 会议,其中有一次关于 DCI 的演讲,最后我对它的哲学有了更多的了解。更改了文本,使其根本不提及 DCI。 (2认同)

Aad*_*ain 372

我一直在阅读有关使用模型问题来修饰脂肪模型以及干掉模型代码的问题.以下是对示例的解释:

1)干掉型号代码

考虑文章模型,事件模型和评论模型.文章或事件有很多评论.评论属于文章或事件.

传统上,模型可能如下所示:

评论模型:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end
Run Code Online (Sandbox Code Playgroud)

文章模型:

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
Run Code Online (Sandbox Code Playgroud)

事件模型

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
Run Code Online (Sandbox Code Playgroud)

我们可以注意到,事件和文章都有一个共同的重要代码.使用关注点,我们可以在单独的模块中提取此公共代码.

为此,在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
Run Code Online (Sandbox Code Playgroud)

现在您的模型看起来像这样:

评论模型:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end
Run Code Online (Sandbox Code Playgroud)

文章模型:

class Article < ActiveRecord::Base
  include Commentable
end
Run Code Online (Sandbox Code Playgroud)

活动模型:

class Event < ActiveRecord::Base
  include Commentable
end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

具有许多关联的模型,否则倾向于积累越来越多的代码并变得难以管理.关注点提供了一种皮肤化脂肪模块的方法,使它们更加模块化和易于理解.

可以使用以下关注点重构上述模型:在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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

现在使用Concerns,您的事件模型减少到

class Event < ActiveRecord::Base
  include Commentable
  include Attendable
end
Run Code Online (Sandbox Code Playgroud)

*在使用问题时,建议采用"域名"分组而不是"技术"分组.基于域的分组就像"可评论","可照片","可以参加".技术分组将意味着'ValidationMethods','FinderMethods'等

  • 那么关注只是一种使用继承或接口或多重继承的方法?从公共基类创建公共基类和子类有什么问题? (6认同)
  • 确实@Chloe,我有些红色,带有'虑于'目录的Rails应用实际上是'关注'...... (3认同)

Dr.*_*ove 94

值得一提的是,使用顾虑被很多人认为是坏主意.

  1. 喜欢这个家伙
  2. 还有这个

一些原因:

  1. 幕后发生了一些黑暗魔法 - 关注是修补include方法,有一个完整的依赖处理系统 - 对于一些琐碎的旧Ruby混合模式的东西太复杂了.
  2. 你的课程也不干.如果你在各种模块中填充50个公共方法并包含它们,你的类仍然有50个公共方法,只是你隐藏了代码味道,有点把你的垃圾放在抽屉里.
  3. 代码库实际上更难以解决所有这些问题.
  4. 您确定您的团队中的所有成员都有同样的理解应该真正替代关注点吗?

关注是轻松拍摄腿部的方法,小心使用它们.

  • 拒绝投票,因为这不是问题的答案。这是一个意见。我敢肯定这是一种优点,但这不应该是对StackOverflow问题的答案。 (3认同)
  • @Adam 这是一个固执的答案。想象一下,有人会问如何在 rails 中使用全局变量,肯定会提到有更好的方法来做事(即 Redis.current vs $redis)可能对主题启动器有用?软件开发本质上是一门固执己见的学科,无法绕过它。事实上,我将意见视为答案和讨论,哪个答案在 stackoverflow 上一直是最好的,这是一件好事 (3认同)
  • 我知道 SO 不是讨论这个问题的最佳场所,但是还有什么其他类型的 Ruby mixin 可以让你的课程保持干燥呢?看起来你的论点中的理由 #1 和理由 #2 是相反的,除非你只是为了更好的面向对象设计、服务层或我遗漏的其他东西?(我不反对——我建议添加替代品会有所帮助!) (2认同)
  • 使用https://github.com/AndyObtiva/super_module是一种选择,使用旧的ClassMethods模式是另一种选择.使用更多对象(如服务)来清晰地分离关注点绝对是可行的方法. (2认同)
  • 当然,与您的问题的_answer_一起提到它似乎很好。您的答案中没有任何内容实际上可以回答OP的问题。如果您只想警告某人,为什么他们不应该使用关注点或全局变量,那么您可以在他们的问题中添加一个很好的注释,但这并不能真正为您提供一个很好的答案。 (2认同)

ami*_*tob 55

这篇文章帮助我理解了问题.

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

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end
Run Code Online (Sandbox Code Playgroud)

  • 这个答案没有解释任何事情。 (2认同)

Siv*_*iva 39

我觉得这里的大多数例子都展示了权力,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
Run Code Online (Sandbox Code Playgroud)

重构后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
Run Code Online (Sandbox Code Playgroud)

您会看到实例方法,类方法和包含的块不那么混乱.担忧会为您注入适当的注意力.这是使用的一个优点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
Run Code Online (Sandbox Code Playgroud)

在这个例子中BarHost真正需要的模块.但是因为对班级的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
Run Code Online (Sandbox Code Playgroud)

现在看起来很简单.

如果你在想为什么我们不能FooBar模块本身中添加依赖?这不会起作用,因为method_injected_by_foo_to_host_klass必须在类中注入,Bar而不是Bar模块本身.

来源: Rails ActiveSupport ::关注


Saj*_*aza 6

关注make文件filename.rb

例如,我想在我的应用程序中,属性create_by存在,将值更新为1,更新为update_by

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
Run Code Online (Sandbox Code Playgroud)

之后,在您的模型中包含如下:

included do
   before_action only: [:create] do
     blaablaa(options)
   end
end
Run Code Online (Sandbox Code Playgroud)