dyoder-autocode 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/autocode.rb +108 -107
- data/test/autocreate.rb +17 -0
- data/test/autoinit.rb +3 -4
- data/test/autoload.rb +51 -19
- metadata +1 -1
data/lib/autocode.rb
CHANGED
@@ -1,134 +1,135 @@
|
|
1
1
|
module Autocode
|
2
|
-
|
2
|
+
|
3
3
|
def self.extended( mod )
|
4
4
|
included(mod)
|
5
5
|
end
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
6
|
+
|
7
|
+
def self.included( mod )
|
8
|
+
|
9
|
+
old = mod.method( :const_missing )
|
10
|
+
mod.metaclass.class_eval do
|
11
|
+
|
12
|
+
def autocreate( key, exemplar, &block )
|
13
|
+
keys = case key
|
14
|
+
when true, Symbol then [key]
|
15
|
+
when Array then key
|
16
|
+
end
|
17
|
+
|
18
|
+
@exemplars ||= Hash.new
|
19
|
+
@init_blocks ||= Hash.new { |h,k| h[k] = [] }
|
20
|
+
keys.each do |k|
|
21
|
+
@exemplars[k] = exemplar
|
22
22
|
@init_blocks[k] << block
|
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
|
-
|
23
|
+
end
|
24
|
+
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
|
28
|
+
def autocreate_class( key = true, superclass = Class )
|
29
|
+
autocreate key, Class.new( superclass )
|
30
|
+
end
|
31
|
+
|
32
|
+
def autocreate_module( key = true )
|
33
|
+
autocreate key, Module.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def autoinit( key, &block )
|
37
|
+
# See whether we're dealing with a namespaced constant,
|
38
|
+
# The match groupings for, e.g. "X::Y::Z", would be
|
39
|
+
# ["X::Y", "Z"]
|
40
|
+
match = key.to_s.match(/^(.*)::([\w\d_]+)$/)
|
41
|
+
if match
|
42
|
+
namespace, cname = match[1,2]
|
43
|
+
const = module_eval(namespace)
|
44
|
+
const.module_eval do
|
45
|
+
@init_blocks ||= Hash.new { |h,k| h[k] = [] }
|
46
|
+
@init_blocks[cname.to_sym] << block
|
47
|
+
end
|
48
|
+
else
|
49
|
+
@init_blocks[key] << block
|
50
|
+
end
|
51
|
+
return self
|
52
|
+
end
|
53
|
+
|
54
|
+
def autoload(key = true, options = {})
|
55
|
+
snake_case = lambda {|name| name.gsub(/([a-z\d])([A-Z])/){"#{$1}_#{$2}"}.tr("-", "_").downcase }
|
56
|
+
# look for load_files in either a specified directory, or in the directory
|
57
|
+
# with the snakecase name of the enclosing module
|
58
|
+
directories = [options[:directories] || snake_case.call(self.name.match( /^.*::([\w\d_]+)$/)[1])].flatten
|
59
59
|
# create a lambda that looks for a file to load
|
60
60
|
file_finder = lambda do |cname|
|
61
61
|
filename = snake_case.call(cname.to_s << ".rb")
|
62
62
|
path = directories.map { |dir| File.join(dir.to_s, filename) }.find { |path| File.exist?( path ) }
|
63
63
|
end
|
64
64
|
# if no exemplar is given, assume Module.new
|
65
|
-
|
65
|
+
@load_files ||= Hash.new
|
66
66
|
@load_files[key] = [file_finder, options[:exemplar] || Module.new]
|
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
|
-
|
67
|
+
return self
|
68
|
+
end
|
69
|
+
|
70
|
+
def autoload_class(key = true, superclass = Class, options = {})
|
71
|
+
options[:exemplar] = Class.new(superclass)
|
72
|
+
autoload key, options
|
73
|
+
end
|
74
|
+
|
75
|
+
def autoload_module(key = true, options = {})
|
76
|
+
options[:exemplar] = Module.new
|
77
|
+
autoload key, options
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the list of constants that would be reloaded upon a call to reload.
|
81
|
+
def reloadable( *names )
|
82
|
+
( @reloadable ||= [] ).concat(names)
|
83
|
+
return self
|
84
|
+
end
|
85
|
+
|
86
|
+
# Reloads all the constants that were loaded via autocode. Technically, all reload
|
87
|
+
# is doing is undefining them (by calling +remove_const+ on each in turn); they won't get
|
88
|
+
# reloaded until they are referenced.
|
89
|
+
def reload
|
90
|
+
@reloadable.each { |name| remove_const( name ) } if @reloadable
|
91
|
+
@reloadable = nil
|
92
|
+
return self
|
93
|
+
end
|
94
|
+
|
95
95
|
# Unloads all the constants that were loaded and removes all auto* definitions.
|
96
|
-
|
97
|
-
|
96
|
+
def unload
|
97
|
+
reload
|
98
98
|
@exemplars = @init_blocks = @load_files = nil
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
99
|
+
return self
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
define_method :const_missing do | cname | #:nodoc:
|
105
105
|
cname = cname.to_sym
|
106
106
|
@exemplars ||= Hash.new
|
107
107
|
@init_blocks ||= Hash.new { |h,k| h[k] = [] }
|
108
|
-
|
108
|
+
@load_files ||= Hash.new
|
109
109
|
exemplar = @exemplars[cname] || @exemplars[true]
|
110
110
|
blocks = @init_blocks[cname]
|
111
111
|
blocks = @init_blocks[true] + blocks if @exemplars[cname].nil? && @init_blocks[true]
|
112
112
|
load_file_finder, load_class = @load_files[cname] || @load_files[true]
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
113
|
+
|
114
|
+
if load_file_finder && filename = load_file_finder.call(cname)
|
115
|
+
object = load_class.clone
|
116
|
+
elsif exemplar
|
117
|
+
object = exemplar.clone
|
118
|
+
else
|
119
|
+
return old.call(cname)
|
120
|
+
end
|
121
|
+
|
122
|
+
(@reloadable ||= []) << cname;
|
123
|
+
const_set( cname, object )
|
124
|
+
|
125
125
|
blocks.each do |block|
|
126
126
|
object.module_eval( &block) if block
|
127
127
|
end
|
128
|
+
|
128
129
|
load(filename) if filename
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
130
|
+
|
131
|
+
return object
|
132
|
+
end
|
133
|
+
end
|
133
134
|
end
|
134
135
|
end
|
data/test/autocreate.rb
CHANGED
@@ -40,4 +40,21 @@ describe "A module where autocreate has been called" do
|
|
40
40
|
Duffel.unload
|
41
41
|
end
|
42
42
|
|
43
|
+
it "should be possible to include autocode" do
|
44
|
+
module Waves
|
45
|
+
include Autocode
|
46
|
+
autocreate(:TestLib, Module.new) do
|
47
|
+
include Autocode
|
48
|
+
autocreate_class
|
49
|
+
autoload_class
|
50
|
+
end
|
51
|
+
end
|
52
|
+
Waves::TestLib::TheOneAndOnlyClass.help().should == "class help"
|
53
|
+
Waves::TestLib::AnyThing.name.should == "Waves::TestLib::AnyThing"
|
54
|
+
lambda { Waves::TestLib::TheOneAndOnlyModule }.should.raise TypeError
|
55
|
+
Waves::TestLib::ThePretender.name.should == "Waves::TestLib::ThePretender"
|
56
|
+
lambda { Waves::TestLib::ThePretender.help() }.should.raise NoMethodError
|
57
|
+
Waves.unload
|
58
|
+
end
|
59
|
+
|
43
60
|
end
|
data/test/autoinit.rb
CHANGED
@@ -12,11 +12,11 @@ describe "thingy" do
|
|
12
12
|
autoinit(:Whatsit) do
|
13
13
|
def self.in_scope; true; end
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
autoinit('Whatsit::Critter') do
|
17
17
|
def self.outside_scope; true; end
|
18
18
|
def instance; true; end
|
19
|
-
# this definition
|
19
|
+
# this definition is overridden by the one in the file
|
20
20
|
def self.gizmo; 2; end
|
21
21
|
end
|
22
22
|
|
@@ -54,6 +54,5 @@ describe "thingy" do
|
|
54
54
|
it "should run autoinit blocks before the file loading" do
|
55
55
|
Thingy::Whatsit::Critter.gizmo.should == 1
|
56
56
|
end
|
57
|
-
|
58
|
-
|
57
|
+
|
59
58
|
end
|
data/test/autoload.rb
CHANGED
@@ -6,28 +6,14 @@ describe "A module where autoload has been called" do
|
|
6
6
|
module Thingy
|
7
7
|
module Mabob
|
8
8
|
extend Autocode
|
9
|
-
autoload_class
|
9
|
+
autoload_class :DooDad, Class, :directories => [File.join(File.dirname(__FILE__), "test_lib")]
|
10
10
|
autoload_module :Humbug, :directories => [File.join(File.dirname(__FILE__), "test_lib")]
|
11
11
|
end
|
12
12
|
end
|
13
|
-
module Waves
|
14
|
-
module TestLib
|
15
|
-
extend Autocode
|
16
|
-
autoload true, :exemplar => Module.new
|
17
|
-
end
|
18
|
-
end
|
19
|
-
module Whatever
|
20
|
-
module TestLib
|
21
|
-
extend Autocode
|
22
|
-
autoload
|
23
|
-
end
|
24
|
-
end
|
25
13
|
end
|
26
14
|
|
27
15
|
after do
|
28
16
|
Thingy::Mabob.unload
|
29
|
-
Waves::TestLib.unload
|
30
|
-
Whatever::TestLib.unload
|
31
17
|
end
|
32
18
|
|
33
19
|
it "should autoload where files match" do
|
@@ -36,6 +22,7 @@ describe "A module where autoload has been called" do
|
|
36
22
|
end
|
37
23
|
|
38
24
|
it "should not autoload where it matches a file but is out of scope" do
|
25
|
+
lambda { Thingy::Mabob::Answer42ItIs }.should.raise NameError
|
39
26
|
lambda { Thingy::Whatsit::Critter }.should.raise NameError
|
40
27
|
end
|
41
28
|
|
@@ -43,12 +30,57 @@ describe "A module where autoload has been called" do
|
|
43
30
|
lambda { Thingy::Mabob::MooCow }.should.raise NameError
|
44
31
|
end
|
45
32
|
|
46
|
-
it "should autoload using
|
47
|
-
Waves
|
33
|
+
it "should autoload using super class" do
|
34
|
+
module Waves
|
35
|
+
module TestLib
|
36
|
+
extend Autocode
|
37
|
+
autoload_class true, Thingy::Mabob::DooDad
|
38
|
+
end
|
39
|
+
end
|
40
|
+
Waves::TestLib::TheClass42Gang.party?.should == true
|
41
|
+
Waves::TestLib::TheClass42Gang.gizmo.should == 1
|
42
|
+
Waves::TestLib.unload
|
48
43
|
end
|
49
44
|
|
50
|
-
it "should
|
51
|
-
|
45
|
+
it "should autoload using defaults" do
|
46
|
+
module Waves
|
47
|
+
module TestLib
|
48
|
+
extend Autocode
|
49
|
+
autoload
|
50
|
+
end
|
51
|
+
end
|
52
|
+
Waves::TestLib::TheOneAndOnlyModule.help().should == "module help"
|
53
|
+
lambda { Waves::TestLib::TheOneAndOnlyClass }.should.raise TypeError
|
54
|
+
Waves::TestLib::ThePretender.name.should == "Waves::TestLib::ThePretender"
|
55
|
+
lambda { Waves::TestLib::ThePretender.help() }.should.raise NoMethodError
|
56
|
+
Waves::TestLib.unload
|
52
57
|
end
|
53
58
|
|
59
|
+
it "should autoload class using defaults" do
|
60
|
+
module Waves
|
61
|
+
module TestLib
|
62
|
+
extend Autocode
|
63
|
+
autoload_class
|
64
|
+
end
|
65
|
+
end
|
66
|
+
Waves::TestLib::TheOneAndOnlyClass.help().should == "class help"
|
67
|
+
lambda { Waves::TestLib::TheOneAndOnlyModule }.should.raise TypeError
|
68
|
+
Waves::TestLib::ThePretender.name.should == "Waves::TestLib::ThePretender"
|
69
|
+
lambda { Waves::TestLib::ThePretender.help() }.should.raise NoMethodError
|
70
|
+
Waves::TestLib.unload
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should autoload module using defaults" do
|
74
|
+
module Waves
|
75
|
+
module TestLib
|
76
|
+
extend Autocode
|
77
|
+
autoload_module
|
78
|
+
end
|
79
|
+
end
|
80
|
+
Waves::TestLib::TheOneAndOnlyModule.help().should == "module help"
|
81
|
+
lambda { Waves::TestLib::TheOneAndOnlyClass }.should.raise TypeError
|
82
|
+
Waves::TestLib::ThePretender.name.should == "Waves::TestLib::ThePretender"
|
83
|
+
lambda { Waves::TestLib::ThePretender.help() }.should.raise NoMethodError
|
84
|
+
Waves::TestLib.unload
|
85
|
+
end
|
54
86
|
end
|