首先了解下mixin的概念:

mixin混入类,一个mixin可以看作是一组代码,可以添加到一个或者多个类中,在不使用继承的情况下,为这些类添加额外的功能.

在ruby中一个mixin是一个封装好的module,其他class可以include或者extend该module.事实上,

一个class可以拥有很多mixin.

model和class中的方法:

class中有实例方法和类方法两种。

module中也有两种方法称之为module方法和没有module名字的方法,其中module方法可以直接被module调用,

没有module名字的方法不能直接调用,需要mixin到一个类中。

  1. 先看下ruby中include, extend方法:

include方法,例子:

module Person
  def name
      puts "my name is Person"
  end
end
class User
   include Person
end

执行 User.new.name => “my name is Person”

这里include所做的就是将在module内定义的方法在一个class的实例变量上可用。

如果在 class user中使用extend Person,则可以使用User.name,即为User添加了一个类方法。

extend用法例子:

module User
  extend self
  def say
    'hello'
  end
end

这样定义的方法可以为User所调用, User.say => “hello”

  1. ActiveSupport::Concern 是为了管理modules之间的依赖关系,而在class中include时,不需要关心module之间的关系。

一个普通的module写法:

module M
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end
  module ClassMethods
    ...
  end
end

使用ActiveSupport::Concern重构上面的module:

require 'active_support/concern'
module M
  extend ActiveSupport::Concern
  included do
    scope :disabled, -> { where(disabled: true) }
  end
  class_methods do
    ...
  end
end
  1. 下面是一个项目中实用的例子:

Model Concern: 虽然我们主张“fatter model,thinner controller”,但是model中的代码也不宜过多,

好的解决方法就是把相关的额逻辑代码放到对应的ActiveSupport::Concern里面去,提高代码的高内聚,低耦合。

class Post < ActiveRecord::Base
  # scopes
  scope :active, -> {where(is_active: true)}
  # instances methods
  def active?
    is_active
  end
end
class Advertisement < ActiveRecord::Base
  # scopes
  scope :active, -> {where(is_active: true)}
  # instances methods
  def active?
    is_active
  end
end

两个模型具有相同逻辑的代码可以提取到ActiveConcern里面,这样多个model可以共用,遵循DRY原则.

module ActAsActivable
  extend ActiveSupport::Concern
  included do |base|  #这里的base参数代表的是include该module的class
    scope :active, -> {where(is_active: true)}
  end
  # instance methods
  def active?
    is_active
  end
end

然后我们在model中include即可:

class Post < ActiveRecord::Base
  # concerns
  include ActAsActivable
end
class Advertisement < ActiveRecord::Base
  # concerns
  include ActAsActivable
end

大牛总结:

concern将部分可重用的功能抽出来,然后多个model可以共用model的代码太多,将其相关的逻辑代码放到不同的concern里

规定使用ActiveSupport::Concern的代码风格,是希望形成开发规约定,就如controller和model的写法。

Controller Concern 在controller里使用Active Concern:

可以参考gem: https://github.com/josevalim/inherited_resources

项目实战,项目中统一使用deleted_at进行软删除,可以将scope方法和删除复原操作提取到ActiveSupport::Concern中:

app/models/concerns/self_deleteable.rb

module SelfDeletedable  
   extend ActiveSupport::Concern 
   scope :enabled, -> {where(deleted_at: nil)} 
   included do 
     def delete  
          self.update_attributes(deleted_at: Time.now)     
      end    
      def restore        
          self.update_attributes(deleted_at: nil)    
       end
   end
end

在spp/models/worker.rb中引入SelfDeletedable module

class Worder < ActiveRecord::Base
   include SelfDeletedable
end
app/controllers/workers_controller.rb:
def delete
   @worker.delete
end

参考:https://ruby-china.org/topics/19812

这里讲了如果不同的model都有共同的is_active字段管理状态scope时的重构.