On Sun, Dec 25, 2011 at 10:48 PM, Intransition <transfire / gmail.com> wrote:

> Going through some old code snippets and came across this clever bit. Back
> then I was planning to use this for Facets, before I finally determined
> that using mixins wasn't actually the best approach for a core extensions
> library.
>
>   module Extension
>     def self.included(base)
>       name = base.name.split('::').last
>       core = eval("::#{name}")
>       core.send(:include, base)
>     end
>   end
>
> Example:
>
>     module Facets
>       module String
>         include Extension
>
>         def drippy
>           puts "Drippy #{self}"
>         end
>       end
>     end
>
>     "Hello".drippy
>
> Just thought I'd share.
>
>
Why do you invoke the included method by including the Extension into
Facets::String? I see this all over the place (esp in Rails) but don't get
why. It's just an ultra fancy way to invoke a method, but it pollutes
String's ancestry and makes it nonobvious what's happening. This code
doesn't also extend in the included hook, but I see that a lot, too (e.g.
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/model.rb#L17
).

How about something like this as an alternative:


# With included hook, pollutes string
module CoreExtension
  def self.included(base)
    name = base.name.split('::').last
    core = Object.const_get name
    core.send(:include, base)
  end
end

module Facets
  module String
    include CoreExtension

    def drippy
      "Drippy #{self}"
    end
  end
end

"Hello".drippy # => "Drippy Hello"
String.ancestors.include? CoreExtension # => true



# Same functionality, but more clear and doesn't add CoreExtension to
ancestry
module CoreExtension
  def self.<<(base)
    name = base.name.split('::').last
    core = Object.const_get name
    core.send(:include, base)
  end
end

module Facets
  module String
    CoreExtension << self

    def drippy
      "Drippy #{self}"
    end
  end
end

"Hello".drippy # => "Drippy Hello"
String.ancestors.include? CoreExtension # => false