7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
# File 'lib/autocode.rb', line 7
def self.included( mod )
old = mod.method( :const_missing )
mod.metaclass.class_eval do
def autocreate( key, exemplar, &block )
keys = case key
when true, Symbol then [key]
when Array then key
end
@exemplars ||= Hash.new
@init_blocks ||= Hash.new { |h,k| h[k] = [] }
keys.each do |k|
@exemplars[k] = exemplar
@init_blocks[k] << block
end
return self
end
def autocreate_class( key = true, superclass = Class )
autocreate key, Class.new( superclass )
end
def autocreate_module( key = true )
autocreate key, Module.new
end
def autoinit( key, &block )
match = key.to_s.match(/^(.*)::([\w\d_]+)$/)
if match
namespace, cname = match[1,2]
const = module_eval(namespace)
const.module_eval do
@init_blocks ||= Hash.new { |h,k| h[k] = [] }
@init_blocks[cname.to_sym] << block
end
else
@init_blocks[key] << block
end
return self
end
def autoload(key = true, options = {})
snake_case = lambda {|name| name.gsub(/([a-z\d])([A-Z])/){"#{$1}_#{$2}"}.tr("-", "_").downcase }
directories = [options[:directories] || snake_case.call(self.name.match( /^.*::([\w\d_]+)$/)[1])].flatten
file_finder = lambda do |cname|
filename = snake_case.call(cname.to_s << ".rb")
path = directories.map { |dir| File.join(dir.to_s, filename) }.find { |path| File.exist?( path ) }
end
@load_files ||= Hash.new
@load_files[key] = [file_finder, options[:exemplar] || Module.new]
return self
end
def autoload_class(key = true, superclass = Class, options = {})
options[:exemplar] = Class.new(superclass)
autoload key, options
end
def autoload_module(key = true, options = {})
options[:exemplar] = Module.new
autoload key, options
end
def reloadable( *names )
( @reloadable ||= [] ).concat(names)
return self
end
def reload
@reloadable.each { |name| remove_const( name ) } if @reloadable
@reloadable = nil
return self
end
def unload
reload
@exemplars = @init_blocks = @load_files = nil
return self
end
private
define_method :const_missing do | cname |
cname = cname.to_sym
@exemplars ||= Hash.new
@init_blocks ||= Hash.new { |h,k| h[k] = [] }
@load_files ||= Hash.new
exemplar = @exemplars[cname] || @exemplars[true]
blocks = @init_blocks[cname]
blocks = @init_blocks[true] + blocks if @exemplars[cname].nil? && @init_blocks[true]
load_file_finder, load_class = @load_files[cname] || @load_files[true]
if load_file_finder && filename = load_file_finder.call(cname)
object = load_class.clone
elsif exemplar
object = exemplar.clone
else
return old.call(cname)
end
(@reloadable ||= []) << cname;
const_set( cname, object )
blocks.each do |block|
object.module_eval( &block) if block
end
load(filename) if filename
return object
end
end
end
|