Module: DR::Meta

Extended by:
Meta
Included in:
Meta
Defined in:
lib/dr/ruby_ext/meta_ext.rb

Instance Method Summary collapse

Instance Method Details

#all_ancestors(obj) ⇒ Object

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.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 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) ⇒ Object

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)



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 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_objectObject

add extend_ancestors and full_extend to Object



43
44
45
46
47
48
49
50
51
52
# 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) ⇒ Object



76
77
78
79
80
81
# 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: '') ⇒ Object

like get_unbound_method except we pass a strng rather than a block



97
98
99
100
101
102
103
104
105
106
# File 'lib/dr/ruby_ext/meta_ext.rb', line 97

def get_unbound_evalmethod(method_name, method_str, args: '')
  module_eval "    def \#{method_name}(\#{args})\n      \#{method_str}\n    end\n  RUBY\n  method = instance_method method_name\n  remove_method method_name\n  method\nend\n"

#get_unbound_method(method_name, &block) ⇒ Object

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)



89
90
91
92
93
94
# 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) ⇒ Object

from http://stackoverflow.com/questions/18551058/better-way-to-turn-a-ruby-class-into-a-module-than-using-refinements See also http://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



11
12
13
14
15
16
17
18
19
20
21
# 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