12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- # Fake implementation of prepend(), which does not support overriding
- # inherited methods nor methods that are formerly overridden by
- # another invocation of prepend().
- #
- # Here's what <Original>.prepend(<Wrapper>) does:
- #
- # - Create an anonymous stub module (hereinafter <Stub>) and define
- # <Stub>#<method> that calls #<method>_without_<Wrapper> for each
- # instance method of <Wrapper>.
- #
- # - Rename <Original>#<method> to #<method>_without_<Wrapper> for each
- # instance method of <Wrapper>.
- #
- # - Include <Stub> and <Wrapper> into <Original> in that order.
- #
- # This way, a call of <Original>#<method> is dispatched to
- # <Wrapper><method>, which may call super which is dispatched to
- # <Stub>#<method>, which finally calls
- # <Original>#<method>_without_<Wrapper> which is used to be called
- # <Original>#<method>.
- #
- # Usage:
- #
- # class Mechanize
- # # module with methods that overrides those of X
- # module Y
- # end
- #
- # unless X.respond_to?(:prepend, true)
- # require 'mechanize/prependable'
- # X.extend(Prependable)
- # end
- #
- # class X
- # prepend Y
- # end
- # end
- class Module
- def prepend(mod)
- stub = Module.new
- mod_id = (mod.name || 'Module__%d' % mod.object_id).gsub(/::/, '__')
- mod.instance_methods.each { |name|
- method_defined?(name) or next
- original = instance_method(name)
- next if original.owner != self
- name = name.to_s
- name_without = name.sub(/(?=[?!=]?\z)/) { '_without_%s' % mod_id }
- arity = original.arity
- arglist = (
- if arity >= 0
- (1..arity).map { |i| 'x%d' % i }
- else
- (1..(-arity - 1)).map { |i| 'x%d' % i } << '*a'
- end << '&b'
- ).join(', ')
- if name.end_with?('=')
- stub.module_eval %{
- def #{name}(#{arglist})
- __send__(:#{name_without}, #{arglist})
- end
- }
- else
- stub.module_eval %{
- def #{name}(#{arglist})
- #{name_without}(#{arglist})
- end
- }
- end
- module_eval {
- alias_method name_without, name
- remove_method name
- }
- }
- include stub
- include mod
- end
- private :prepend
- end unless Module.method_defined?(:prepend)
|