缺省的Rails 4项目生成器现在在控制器和模型下创建目录“关注点”。我找到了一些关于如何使用路由关注点的解释,但没有关于控制器或模型的解释。
我很确定这与当前社区中的“DCI趋势”有关,我想尝试一下。
问题是,我应该如何使用这个功能,为了使它工作,如何定义命名/类层次结构是否有一个约定?如何在模型或控制器中包含关注点?
缺省的Rails 4项目生成器现在在控制器和模型下创建目录“关注点”。我找到了一些关于如何使用路由关注点的解释,但没有关于控制器或模型的解释。
我很确定这与当前社区中的“DCI趋势”有关,我想尝试一下。
问题是,我应该如何使用这个功能,为了使它工作,如何定义命名/类层次结构是否有一个约定?如何在模型或控制器中包含关注点?
当前回答
在关注点中,创建文件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
其他回答
这篇文章帮助我理解了人们的担忧。
# app/models/trader.rb
class Trader
include Shared::Schedule
end
# app/models/concerns/shared/schedule.rb
module Shared::Schedule
extend ActiveSupport::Concern
...
end
值得一提的是,使用关注点被很多人认为是一个坏主意。
就像这个人 还有这个
一些原因:
There is some dark magic happening behind the scenes - Concern is patching include method, there is a whole dependency handling system - way too much complexity for something that's trivial good old Ruby mixin pattern. Your classes are no less dry. If you stuff 50 public methods in various modules and include them, your class still has 50 public methods, it's just that you hide that code smell, sort of put your garbage in the drawers. Codebase is actually harder to navigate with all those concerns around. Are you sure all members of your team have same understanding what should really substitute concern?
忧虑很容易让你伤到自己的腿,小心对待它们。
我觉得这里的大多数示例都展示了模块的强大功能,而不是ActiveSupport::Concern如何为模块增加价值。
例1:更多可读模块。
所以不用担心这是一个典型的模块。
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有依赖关系,主机类必须包括Foo(但是等等为什么主机想知道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
现在看起来很简单。
如果你在想为什么我们不能在Bar模块中添加Foo依赖?这不会起作用,因为method_injected_by_foo_to_host_klass必须被注入到一个类中,包括Bar而不是Bar模块本身。
来源:Rails ActiveSupport::Concern
在关注点中,创建文件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
所以我自己发现了。这实际上是一个非常简单但强大的概念。它与代码重用有关,如下面的例子所示。基本上,这个想法是提取公共和/或特定于上下文的代码块,以清理模型,避免它们变得过于臃肿和混乱。
作为一个例子,我将给出一个众所周知的模式,即可标记模式:
# 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目录 自动成为加载路径的一部分。连同 关注点包装器,它的支持足以做到这一点 轻量级保理机制大放异彩。