<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>lex (David.fu)</title>
    <link>https://ruby-china.org/lex</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>ActiveSupport::Concern 小结</title>
      <description>&lt;h3 id="ActiveSupport::Concern 被引入到 rails"&gt;ActiveSupport::Concern 被引入到 rails&lt;/h3&gt;
&lt;p&gt;根据这篇文章 &lt;a href="http://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns" rel="nofollow" target="_blank" title=""&gt;put-chubby-models-on-a-diet-with-concerns&lt;/a&gt; 中这样一段话把它的意思说的很明白了：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This concern can then be mixed into all the models that are taggable and you’ll have a single place to update the logic and reason about it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Taggable&lt;/code&gt; 是一个 &lt;code&gt;Module&lt;/code&gt;，里面用来处理跟&lt;code&gt;tag&lt;/code&gt;相关的逻辑。这样我们就可以把这个&lt;code&gt;Taggable&lt;/code&gt;引入到跟这个相关的&lt;code&gt;models&lt;/code&gt;里面。这样代码的可读性和维护性就提高了不少。&lt;/p&gt;

&lt;p&gt;举一个简单的例子，比如一个项目中有很多 model 都需要有有判断这个 &lt;code&gt;model&lt;/code&gt; 是否是 &lt;code&gt;active&lt;/code&gt; 的。我们一般会给这个 model 加上一个&lt;code&gt;is_active&lt;/code&gt;字段，然后再相应的 model 里面会有如下的方法。比如&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Post &amp;lt; ActiveRecord::Base

  # scopes
  scope :active, -&amp;gt; {where(is_active: true)}

  ...

  # instances methods
  def active?
    is_active
  end

  ...
end

class Advertisement &amp;lt; ActiveRecord::Base

  # scopes
  scope :active, -&amp;gt; {where(is_active: true)}

  ...

  # instances methods
  def active?
    is_active
  end

  ...
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Post&lt;/code&gt; 和 &lt;code&gt;Advertisement&lt;/code&gt; 都需要判断这个 model 是不是 &lt;code&gt;active&lt;/code&gt; 状态的，是否&lt;code&gt;active&lt;/code&gt;这个逻辑属性在各个 model 里面的表现又是一致的，这样我们就可以通过 &lt;code&gt;ActiveSupport::Concern&lt;/code&gt; 来重构。&lt;/p&gt;

&lt;p&gt;首先把相同的逻辑放到一个 &lt;code&gt;model&lt;/code&gt; 里面去，称为 &lt;code&gt;act_as_activable&lt;/code&gt;。首先定义 scope，然后定义 &lt;code&gt;ClassMethods&lt;/code&gt; 和 &lt;code&gt;instanceMethods&lt;/code&gt;。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/act_as_activable.rb or app/modeles/concerns/act_as_activable.rb&lt;/span&gt;
&lt;span class="c1"&gt;# for models with field :is_active&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActAsActivable&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;is_active: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@all_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reload&lt;/span&gt;
      &lt;span class="vi"&gt;@all_active&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# instance methods&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;active?&lt;/span&gt;
    &lt;span class="n"&gt;is_active&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们把这个&lt;code&gt;model&lt;/code&gt; &lt;code&gt;indclude&lt;/code&gt; 到需要的 model 里面去。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;

  &lt;span class="c1"&gt;# concerns&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActAsActivable&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Advertisement&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;

  &lt;span class="c1"&gt;# concerns&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActAsActivable&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们将 &lt;code&gt;act_as_activable&lt;/code&gt; 这个可重用的功能抽出来，然后多个 model 可以共用。这样就有遵循了&lt;code&gt;DRY&lt;/code&gt;原则啦。&lt;/p&gt;
&lt;h3 id="ActiveSupport::Concern 用来规范model代码逻辑。"&gt;ActiveSupport::Concern 用来规范 model 代码逻辑。&lt;/h3&gt;
&lt;p&gt;虽然我们主张 &lt;code&gt;fatter model, thinner controller&lt;/code&gt;, 但如果&lt;code&gt;model&lt;/code&gt;的代码太多，可读性和维护性则会大大下降。一个好的解决办法是把相关的逻辑代码放到 对应的 &lt;code&gt;ActiveSupport::Concern&lt;/code&gt; 里面去，以提高代码的高内聚，低耦合。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#encoding: utf-8
class Event &amp;lt; ActiveRecord::Base

  # concerns
  include ApprovalRequired
  include OptionalChinese
  include ActAsResourceable
  ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Event&lt;/code&gt; 这个 model 就引入 &lt;code&gt;ApprovalRequired&lt;/code&gt;, &lt;code&gt;OptionalChinese&lt;/code&gt;, &lt;code&gt;ActAsResourceable&lt;/code&gt; 这个三个 concern，model 的代码就清晰而且易于维护。&lt;/p&gt;

&lt;p&gt;一句话总结就是，使用 &lt;code&gt;ActiveSupport::Concern&lt;/code&gt; 会使 model 代码简洁又可读，中看更中用。&lt;/p&gt;

&lt;p&gt;如果用这个大神&lt;a href="http://hi.baidu.com/rainchen/item/ef36c917a23a9117e2f986f4" rel="nofollow" target="_blank" title=""&gt;rainchen&lt;/a&gt; 的总结就是&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;concern 将部分可重用的功能抽出来，然后多个 model 可以共用&lt;/li&gt;
&lt;li&gt;model 的代码太多，将其相关的逻辑代码放到不同的 concern 里&lt;/li&gt;
&lt;li&gt;规定使用 ActiveSupport::Concern 的代码风格，是希望形成开发规约定，就如 controller 和 model 的写法。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="ActiveSupport::Concern 由来"&gt;ActiveSupport::Concern 由来&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;mixin&lt;/code&gt; 是把一个模块 (Module)Mixin 到某个对象中，以实现实现多重继承。那如果不用 &lt;code&gt;concern&lt;/code&gt;, &lt;code&gt;ActAsActivable&lt;/code&gt; 这个 model 会写成怎么样呢？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/act_as_activable.rb or app/modeles/concerns/act_as_activable.rb&lt;/span&gt;
&lt;span class="c1"&gt;# for models with field :is_active&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActAsActivable&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;InstanceMethods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;is_active: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;InstanceMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;active?&lt;/span&gt;
      &lt;span class="n"&gt;is_active&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all_active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@all_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reload&lt;/span&gt;
      &lt;span class="vi"&gt;@all_active&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这算是长久以来形成的一个 &lt;code&gt;common pattern&lt;/code&gt;。使用这个钩子 &lt;code&gt;self.included&lt;/code&gt; 让里面的代码在被 &lt;code&gt;include&lt;/code&gt; 的时候调用。&lt;code&gt;base.send(:include, InstanceMethods)&lt;/code&gt;, 用 &lt;code&gt;send&lt;/code&gt; 方法 &lt;code&gt;include&lt;/code&gt; &lt;code&gt;InstanceMethods&lt;/code&gt;, 然后 &lt;code&gt;extend&lt;/code&gt; &lt;code&gt;ClassMethods&lt;/code&gt;, 就是把这些方法放到 &lt;code&gt;base&lt;/code&gt; 的 &lt;code&gt;eigenclass&lt;/code&gt; 里面。最后用 &lt;code&gt;class_eval&lt;/code&gt; 打开这个类写 scope.&lt;/p&gt;

&lt;p&gt;这个 &lt;code&gt;common pattern&lt;/code&gt; 在不用 concern 的 gem 里面还是比较常见的。&lt;/p&gt;
&lt;h3 id="controller  的 concern"&gt;controller  的 concern&lt;/h3&gt;
&lt;p&gt;在 rails 4 中不仅提倡在 model 中使用 concern，而且还提倡在 controller 里面也使用。最典型的一个 gem 就是这个 &lt;a href="https://github.com/josevalim/inherited_resources" rel="nofollow" target="_blank" title=""&gt;inherited_resources&lt;/a&gt;. 当然这是一个 gem，但是意思是差不多的，就是 DON'T Repeat Yourself -- 它将 controller 里面的最典型的 7 个 aciton 封装起来。极大的精简了 controller 里面的代码。&lt;/p&gt;
&lt;h3 id="TODO: ActiveSupport::Concern 源码"&gt;TODO: ActiveSupport::Concern 源码&lt;/h3&gt;&lt;h5 id="根据下面这篇文章研究下 源码"&gt;根据下面这篇文章研究下 源码&lt;/h5&gt;
&lt;p&gt;&lt;a href="http://www.zhubert.com/blog/2013/06/13/activesupport-concern-digression/" rel="nofollow" target="_blank" title=""&gt;activesupport-concern-digression&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="references:"&gt;references:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://api.rubyonrails.org/classes/ActiveSupport/Concern.html" rel="nofollow" target="_blank" title=""&gt;rails 4.1 ActiveSupport::Concern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ihower.tw/blog/archives/3949" rel="nofollow" target="_blank" title=""&gt;ihower rails 3 ActiveSupport::Concern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://engineering.appfolio.com/2013/06/17/ruby-mixins-activesupportconcern/" rel="nofollow" target="_blank" title=""&gt;ruby-mixins-activesupportconcern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.andywaite.com/2012/12/23/exploring-concerns-for-rails-4/" rel="nofollow" target="_blank" title=""&gt;exploring-concerns-for-rails-4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>lex</author>
      <pubDate>Sun, 08 Jun 2014 13:51:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/19812</link>
      <guid>https://ruby-china.org/topics/19812</guid>
    </item>
  </channel>
</rss>
