module DR::Meta

Public Instance Methods

all_ancestors(obj) click to toggle source

find the ancestors of obj, its singleton class, its singleton_singleton_class. To avoid going to infinity, we only add a singleton_class when its ancestors contains new modules we have not seen.

# File lib/dr/ruby_ext/meta_ext.rb, line 27
def all_ancestors(obj)
        obj=obj.singleton_class unless Module===obj
        found=[]
        stack=[obj]
        while !stack.empty? do
                obj=stack.shift
                next if found.include?(obj)
                found<<obj
                stack.push(* obj.ancestors.select {|m| !(stack+found).include?(m)})
                sing=obj.singleton_class
                stack << sing unless sing.ancestors.select {|m| m.class==Module}.reduce(true) {|b,m| b && found.include?(m)}
        end
        return found
end
apply(*args,method: nil, to: self, **opts,&block) click to toggle source

apply is a 'useless' wrapper to .call, but it also works for UnboundMethod.

See also dr/core_ext that adds 'UnboundMethod#call'

> If we don't want to extend a module with Meta, we can still do

Meta.apply(String,method: Meta.instance_method(:include_ancestors),to: self) (note that in 'Meta.apply', the default option to 'to:' is self=Meta, that's why we need to put 'to: self' again)

# File lib/dr/ruby_ext/meta_ext.rb, line 60
def apply(*args,method: nil, to: self, **opts,&block)
        #note, in to self is Meta, except if we include it in another
        #module so that it would make sense
        method=method.unbind if method.class==Method
        case method
        when UnboundMethod
                method=method.bind(to)
        end
        #We cannot call **opts if opts is empty in case of an empty args, cf https://bugs.ruby-lang.org/issues/10708
        if opts.empty?
                method.call(*args,&block)
        else
                method.call(*args,**opts,&block)
        end
end
extend_object() click to toggle source

add extend_ancestors and full_extend to Object

# File lib/dr/ruby_ext/meta_ext.rb, line 43
def extend_object
        include_ancestors=Meta.method(:include_ancestors)
        include_complete=Meta.method(:full_include)
        Object.define_method(:extend_ancestors) do |m|
                include_ancestors.bind(singleton_class).call(m)
        end
        Object.define_method(:full_extend) do |m|
                include_complete.bind(singleton_class).call(m)
        end
end
get_bound_method(obj, method_name, &block) click to toggle source
# File lib/dr/ruby_ext/meta_ext.rb, line 76
def get_bound_method(obj, method_name, &block)
        obj.singleton_class.send(:define_method,method_name, &block)
        method = obj.method method_name
        obj.singleton_class.send(:remove_method,method_name)
        method
end
get_unbound_evalmethod(method_name, method_str, args: '') click to toggle source

like get_unbound_method except we pass a strng rather than a block

# File lib/dr/ruby_ext/meta_ext.rb, line 97
                def get_unbound_evalmethod(method_name, method_str, args: '')
                        module_eval <<-RUBY
                                def #{method_name}(#{args})
                                        #{method_str}
                                end
                        RUBY
                        method = instance_method method_name
                        remove_method method_name
                        method
                end
get_unbound_method(method_name, &block) click to toggle source

Taken from sinatra/base.rb: return an unbound method from a block, with owner the current module Conversely, from a (bound) method, calling to_proc (hence &m) gives a lambda Note: rather than doing m=get_unbound_method('',&block);m.bind(obj).call(args) one could do obj.instance_exec(args,&block)

# File lib/dr/ruby_ext/meta_ext.rb, line 89
def get_unbound_method(method_name, &block)
        define_method(method_name, &block)
        method = instance_method method_name
        remove_method method_name
        method
end
refined_module(klass,&b) click to toggle source

from stackoverflow.com/questions/18551058/better-way-to-turn-a-ruby-class-into-a-module-than-using-refinements See also stackoverflow.com/questions/28649472/ruby-refinements-subtleties

convert a class into a module using refinements ex: (Class.new { include Meta.refined_module(String) { def length; super+5; end } }).new(“foo”).length #=> 8 This uses the fact that a refining module of klass behaves as if it had klass has his direct ancestor

# File lib/dr/ruby_ext/meta_ext.rb, line 11
def refined_module(klass,&b)
        klass=klass.singleton_class unless Module===klass
        Module.new do
                #including the module rather than just returning it allow us to
                #still be able to use 'using' ('using' does not work directly on
                #refining modules only on the enclosing ones)
                include refine(klass) {
                        module_eval(&b) if block_given?
                }
        end
end