Rails active_support/concern.rb 是否有点多此一举?

bjmark · 发布于 2013年11月16日 · 最后由 hooopo 回复于 2013年11月17日 · 1831 次阅读
96

下面是源码中对问题的描述及利用concern的解决方式

module ActiveSupport
  # A typical module looks like this:
  #
  #   module M
  #     def self.included(base)
  #       base.extend ClassMethods
  #       base.class_eval do
  #         scope :disabled, -> { where(disabled: true) }
  #       end
  #     end
  #
  #     module ClassMethods
  #       ...
  #     end
  #   end
  #
  # By using <tt>ActiveSupport::Concern</tt> the above module could instead be
  # written as:
  #
  #   require 'active_support/concern'
  #
  #   module M
  #     extend ActiveSupport::Concern
  #
  #     included do
  #       scope :disabled, -> { where(disabled: true) }
  #     end
  #
  #     module ClassMethods
  #       ...
  #     end
  #   end
  #
  # Moreover, it gracefully handles module dependencies. Given a +Foo+ module
  # and a +Bar+ module which depends on the former, we would typically write the
  # following:
  #
  #   module Foo
  #     def self.included(base)
  #       base.class_eval do
  #         def self.method_injected_by_foo
  #           ...
  #         end
  #       end
  #     end
  #   end
  #
  #   module Bar
  #     def self.included(base)
  #       base.method_injected_by_foo
  #     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
  #
  # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
  # could try to hide these from +Host+ directly including +Foo+ in +Bar+:
  #
  #   module Bar
  #     include Foo
  #     def self.included(base)
  #       base.method_injected_by_foo
  #     end
  #   end
  #
  #   class Host
  #     include Bar
  #   end
  #
  # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
  # is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
  # module dependencies are properly resolved:
  #
  #   require 'active_support/concern'
  #
  #   module Foo
  #     extend ActiveSupport::Concern
  #     included do
  #       def self.method_injected_by_foo
  #         ...
  #       end
  #     end
  #   end
  #
  #   module Bar
  #     extend ActiveSupport::Concern
  #     include Foo
  #
  #     included do
  #       self.method_injected_by_foo
  #     end
  #   end
  #
  #   class Host
  #     include Bar # works, Bar takes care now of its dependencies
  #   end

首先我认为这根本不是个问题。 Bar依赖于Foo,那么先include Foo,再include Bar,通过代码说出来把事情说清楚,简单明了,易于理解。

其次,如果不想include Foo,只想include Bar,下面的方法是不是更简单些呢?

module Foo
  def self.included(base)
    base.class_eval do
      def self.method_injected_by_foo
        puts "method_injected_by_foo"
      end
    end
  end
end

module Bar
  def self.included(base)
    base.send(:include, Foo)
    base.method_injected_by_foo
  end
end

class A
  #include Foo
  include Bar
end
共收到 6 条回复
2622

1,你觉得直接include ActiveRecord::Base简单,还是按照Readme去include 一大堆module简单?

2,这样写如果include层级太多说不定会SystemStackError,而且其实和Concern比没少多少阿,Concern还是利用ruby的惯用语法,更容易理解,写法看起来更舒服

96

Rails = 别人写的代码

7063

我觉得他这样写让这种用法自然多了啊,是不错的tip

2458

作者的本意我想更多的是希望形成某种开发规约

96

@everett ,我想也是。 但总觉的这里有点过度包装。过度包装的特征是当时看着很得意,2周后再看,自己也看不懂了。

8

如果你的USB有两个插口,说明书还要求你先插A再插B才可以用,你会用么? 如果哪个插件的文档这样写“使用方法是include A,B,C…Z,注意顺序不能乱哈”一定有人觉得哪里不对了… 哪一天B C D中某一个模块去掉了或改名字或加模块了,插件作者又要通知使用者:“把那个模块去掉,要不然程序不能运行了”

本来是兼容的修改变成了不兼容了,why.

至于你最后说的那种情况,自己想怎么整就怎么整啦,又不是concern使用的场景…

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册