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 <<-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) ⇒ 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