autocode 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/autocode.rb +96 -41
- data/lib/rack/reload.rb +17 -0
- data/test/auto_create.rb +49 -9
- data/test/auto_eval.rb +2 -1
- data/test/auto_load.rb +27 -9
- data/test/helpers.rb +2 -14
- data/test/introspection.rb +15 -0
- data/test/least_surprise.rb +61 -0
- metadata +7 -3
data/lib/autocode.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
module AutoCode
|
2
2
|
|
3
|
+
def self.monitor
|
4
|
+
require 'monitor'
|
5
|
+
@monitor ||= Monitor.new
|
6
|
+
end
|
7
|
+
|
3
8
|
# always make sure we have a camel-cased symbol
|
4
9
|
def AutoCode.normalize( cname )
|
5
10
|
return cname unless cname.is_a? Symbol or cname.is_a? String
|
6
|
-
camel_case( cname )
|
11
|
+
camel_case( cname ).intern
|
7
12
|
end
|
8
13
|
|
9
14
|
def AutoCode.camel_case( cname )
|
10
|
-
cname.to_s.
|
15
|
+
cname.to_s.split('_').map do |word|
|
16
|
+
"#{word.slice(/^\w/).upcase}#{word.slice(/^\w(\w+)/, 1)}"
|
17
|
+
end.join
|
11
18
|
end
|
12
19
|
|
13
20
|
def AutoCode.snake_case( cname )
|
@@ -19,23 +26,57 @@ module AutoCode
|
|
19
26
|
def self.included( mod )
|
20
27
|
|
21
28
|
mod.instance_eval do
|
22
|
-
|
23
|
-
# First make sure we haven't already done this once
|
24
|
-
return unless @autocode.nil?
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
AutoCode.monitor.enter
|
31
|
+
begin
|
32
|
+
# First make sure we haven't already done this once
|
33
|
+
return unless @autocode.nil?
|
34
|
+
|
35
|
+
# Initialize bookkeeping variables needed by AutoCode
|
36
|
+
@autocode = {
|
37
|
+
:constructors => Hash.new { |h,k| h[k] = [] },
|
38
|
+
:initializers => Hash.new { |h,k| h[k] = [] },
|
39
|
+
:loaded => []
|
40
|
+
}
|
41
|
+
ensure
|
42
|
+
AutoCode.monitor.exit
|
43
|
+
end
|
44
|
+
|
45
|
+
def auto_constructor( key = true, &block)
|
46
|
+
@autocode[:constructors][ AutoCode.normalize( key ) ] << block
|
47
|
+
end
|
32
48
|
|
33
|
-
# Adds an auto_create block for the given key using the given exemplar if provided
|
34
49
|
def auto_create( key = true, options = {}, &block )
|
35
|
-
|
36
|
-
exemplar =
|
37
|
-
exemplar.
|
38
|
-
|
50
|
+
auto_constructor( key ) do | cname |
|
51
|
+
exemplar = options[:exemplar] || Module.new
|
52
|
+
new_constant = exemplar.clone
|
53
|
+
new_constant.send(:include, AutoCode)
|
54
|
+
ret = const_set( cname, new_constant )
|
55
|
+
new_constant.module_eval( &block ) if block
|
56
|
+
ret
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Convenience method for auto_creating classes.
|
61
|
+
def auto_create_class( key = true, parent = Object, &block )
|
62
|
+
auto_constructor( key ) do | cname |
|
63
|
+
parent = const_get(parent) unless parent.is_a? Class
|
64
|
+
new_constant = Class.new( parent )
|
65
|
+
new_constant.send(:include, AutoCode)
|
66
|
+
ret = const_set( cname, new_constant )
|
67
|
+
new_constant.module_eval( &block ) if block
|
68
|
+
ret
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convenience method for auto_creating modules.
|
73
|
+
def auto_create_module( key = true, &block )
|
74
|
+
auto_constructor( key ) do | cname |
|
75
|
+
new_constant = Module.new
|
76
|
+
new_constant.send(:include, AutoCode)
|
77
|
+
ret = const_set( cname, new_constant )
|
78
|
+
new_constant.module_eval( &block ) if block
|
79
|
+
ret
|
39
80
|
end
|
40
81
|
end
|
41
82
|
|
@@ -43,54 +84,68 @@ module AutoCode
|
|
43
84
|
def auto_load( key = true, options = {} )
|
44
85
|
@autocode[:constructors][ AutoCode.normalize( key ) ] << lambda do | cname |
|
45
86
|
filename = AutoCode.snake_case( cname ) << '.rb'
|
46
|
-
|
47
|
-
|
87
|
+
options[:directories] ||= '.'
|
88
|
+
path = if target = options[:target]
|
89
|
+
target.call(cname)
|
48
90
|
else
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
Kernel.load( path ) unless path.nil?
|
91
|
+
options[:directories].
|
92
|
+
map { |dir| File.join( dir.to_s, filename ) }.
|
93
|
+
find { |path| File.exist?( path ) }
|
53
94
|
end
|
95
|
+
Kernel.load( path ) unless path.nil?
|
54
96
|
end
|
55
97
|
end
|
56
98
|
|
57
99
|
# Adds an arbitrary initializer block for the given key
|
58
100
|
def auto_eval( key, &block )
|
59
|
-
|
60
|
-
|
101
|
+
if key.is_a?( Symbol) && const_defined?( AutoCode.normalize( key ) )
|
102
|
+
const_get( key ).module_eval( &block)
|
103
|
+
else
|
104
|
+
@autocode[:initializers][ AutoCode.normalize( key ) ] << lambda do | mod |
|
105
|
+
mod.module_eval( &block )
|
106
|
+
end
|
61
107
|
end
|
62
108
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
auto_create( key,{ :exemplar => Class.new( superclass ) }, &block )
|
67
|
-
end
|
68
|
-
|
69
|
-
# Convenience method for auto_create.
|
70
|
-
def auto_create_module( key = true, &block )
|
71
|
-
auto_create( key,{ :exemplar => Module.new }, &block )
|
109
|
+
|
110
|
+
def auto_const?(cname)
|
111
|
+
true unless @autocode[:constructors][ AutoCode.normalize( cname ) ].empty?
|
72
112
|
end
|
73
113
|
|
74
114
|
# Returns the list of constants that would be reloaded upon a call to reload.
|
75
115
|
def reloadable ; @autocode[:loaded] ; end
|
76
116
|
|
77
117
|
# Reloads (via #remove_const) all the constants that were loaded via auto_code.
|
78
|
-
def reload
|
118
|
+
def reload
|
119
|
+
Autocode.monitor.synchronize do
|
120
|
+
@autocode[:loaded].each { |name| remove_const( name ) } ; @autocode[:loaded] = []
|
121
|
+
end
|
122
|
+
end
|
79
123
|
|
80
124
|
private
|
81
125
|
|
82
126
|
old = method( :const_missing )
|
83
127
|
(class << self ; self ; end ).instance_eval do
|
84
128
|
define_method( :const_missing ) do | cname |
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
129
|
+
|
130
|
+
Autocode.monitor.synchronize do
|
131
|
+
# check to see if some other thread loaded the constant before
|
132
|
+
# we entered the lock.
|
133
|
+
return const_get( cname ) if const_defined?( cname )
|
134
|
+
actions = []
|
135
|
+
@autocode[:constructors].each do |matcher,action|
|
136
|
+
actions.concat action if (matcher == true || matcher == cname || matcher === cname.to_s )
|
137
|
+
end
|
138
|
+
actions.reverse!.find { | c | c.call( cname ) and const_defined?( cname ) }
|
139
|
+
return old.call( cname ) unless const_defined?( cname )
|
140
|
+
initializers = @autocode[:initializers][true] + @autocode[:initializers][cname]
|
141
|
+
mod = const_get( cname ); initializers.each { |init| init.call( mod ) }
|
142
|
+
@autocode[:loaded] << cname
|
143
|
+
mod
|
144
|
+
end
|
91
145
|
end
|
92
146
|
end
|
93
147
|
end
|
94
148
|
end
|
95
149
|
end
|
96
|
-
Autocode = AutoCode
|
150
|
+
Autocode = AutoCode
|
151
|
+
|
data/lib/rack/reload.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
module Rack
|
4
|
+
class Reload
|
5
|
+
|
6
|
+
def initialize(app, *reloadable)
|
7
|
+
@app = app
|
8
|
+
@reloadable = reloadable
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@reloadable.each { |mod| mod.reload }
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/test/auto_create.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require "#{File.dirname(__FILE__)}/helpers"
|
2
2
|
|
3
3
|
describe "auto_create" do
|
4
4
|
|
@@ -7,22 +7,62 @@ describe "auto_create" do
|
|
7
7
|
module A
|
8
8
|
include AutoCode
|
9
9
|
auto_create_module :B do
|
10
|
-
|
11
|
-
|
10
|
+
|
11
|
+
def self.smurf; "blue" ; end
|
12
|
+
|
13
|
+
auto_create_module true do
|
14
|
+
def self.pixie; "brown" ; end
|
15
|
+
end
|
16
|
+
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
|
-
|
17
|
-
A::B.
|
21
|
+
it "allows you create and initialize a given const name" do
|
22
|
+
A::B.smurf.should == "blue"
|
18
23
|
end
|
19
24
|
|
20
|
-
|
21
|
-
A::B::C.
|
25
|
+
it "allows you create and initialize const using a wildcard" do
|
26
|
+
A::B::C.pixie.should == "brown"
|
22
27
|
end
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
it "allows you to refer to auto-constructed constants using symbols" do
|
30
|
+
module K
|
31
|
+
include AutoCode
|
32
|
+
auto_create_class true, :C
|
33
|
+
auto_create_class :C
|
34
|
+
end
|
35
|
+
K::C
|
36
|
+
K::D.superclass.should == K::C
|
37
|
+
end
|
38
|
+
|
39
|
+
it "lets you use Module.name in the init block" do
|
40
|
+
module L
|
41
|
+
include AutoCode
|
42
|
+
auto_create_class true do
|
43
|
+
auto_create_class self.name.split("::").last.upcase.reverse do
|
44
|
+
def self.foo; "bar" ; end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
L::WONK::KNOW.foo.should == "bar"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises a NameError if a const doesn't match" do
|
52
|
+
lambda{ A::C }.should raise_error( NameError)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can take a Regexp as a key" do
|
56
|
+
module M
|
57
|
+
include AutoCode
|
58
|
+
auto_create_module(/V\d+/) do
|
59
|
+
def self.thing
|
60
|
+
name.split("::").last.reverse
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
M::V2.thing.should == "2V"
|
65
|
+
M::V3.thing.should == "3V"
|
26
66
|
end
|
27
67
|
|
28
68
|
end
|
data/test/auto_eval.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require "#{File.dirname(__FILE__)}/helpers"
|
2
2
|
|
3
3
|
describe "auto_eval" do
|
4
4
|
|
@@ -27,5 +27,6 @@ describe "auto_eval" do
|
|
27
27
|
specify "allows you to define nested auto_eval declarations" do
|
28
28
|
A::B::C::D.should == true
|
29
29
|
end
|
30
|
+
|
30
31
|
|
31
32
|
end
|
data/test/auto_load.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require "#{File.dirname(__FILE__)}/helpers"
|
2
2
|
require 'extensions/io'
|
3
3
|
|
4
4
|
|
@@ -15,12 +15,6 @@ describe "auto_load" do
|
|
15
15
|
end
|
16
16
|
EOF
|
17
17
|
File.write( @path, content )
|
18
|
-
module A
|
19
|
-
include AutoCode
|
20
|
-
auto_create_class :B
|
21
|
-
auto_load :B, :directories => ['tmp']
|
22
|
-
end
|
23
|
-
|
24
18
|
end
|
25
19
|
|
26
20
|
after do
|
@@ -29,15 +23,39 @@ describe "auto_load" do
|
|
29
23
|
end
|
30
24
|
|
31
25
|
specify "allows you to load a file to define a const" do
|
26
|
+
module A
|
27
|
+
include AutoCode
|
28
|
+
auto_create_class :B
|
29
|
+
auto_load :B, :directories => ['tmp']
|
30
|
+
end
|
32
31
|
A::B.class.should == Module
|
33
32
|
end
|
34
33
|
|
35
34
|
specify "should implement LIFO semantics" do
|
35
|
+
module A
|
36
|
+
include AutoCode
|
37
|
+
auto_create_class :B
|
38
|
+
auto_load :B, :directories => ['tmp']
|
39
|
+
end
|
36
40
|
A::B.class.should == Module
|
37
41
|
end
|
38
42
|
|
39
43
|
specify "raises a NameError if a const doesn't match" do
|
40
|
-
|
44
|
+
module A
|
45
|
+
include AutoCode
|
46
|
+
auto_create_class :B
|
47
|
+
auto_load :B, :directories => ['tmp']
|
48
|
+
end
|
49
|
+
lambda{ A::C }.should raise_error( NameError )
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can use a lambda to specify the target file" do
|
53
|
+
module A
|
54
|
+
include AutoCode
|
55
|
+
auto_create_class :B
|
56
|
+
auto_load :B, :target => lambda {|cname| "tmp/#{cname.to_s.downcase}.rb" }
|
57
|
+
end
|
58
|
+
A::B.class.should == Module
|
41
59
|
end
|
42
60
|
|
43
|
-
end
|
61
|
+
end
|
data/test/helpers.rb
CHANGED
@@ -1,15 +1,3 @@
|
|
1
|
-
|
2
|
-
%w{ bacon }.each { |dep| require dep }
|
3
|
-
# Bacon.extend Bacon::TestUnitOutput
|
4
|
-
Bacon.summary_on_exit
|
1
|
+
$:.unshift "#{File.dirname(__FILE__)}/../lib"
|
5
2
|
|
6
|
-
|
7
|
-
private
|
8
|
-
def specification(name, &block) Bacon::Context.new(name, &block) end
|
9
|
-
end
|
10
|
-
|
11
|
-
Bacon::Context.instance_eval do
|
12
|
-
alias_method :specify, :it
|
13
|
-
end
|
14
|
-
|
15
|
-
require "#{File.dirname(__FILE__)}/../lib/autocode"
|
3
|
+
require 'autocode'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/helpers"
|
2
|
+
|
3
|
+
describe "A module that has included AutoCode" do
|
4
|
+
|
5
|
+
it "can tell you whether a subconstant has an auto-constructor" do
|
6
|
+
module A
|
7
|
+
include AutoCode
|
8
|
+
auto_create_module :B
|
9
|
+
end
|
10
|
+
|
11
|
+
A.auto_const?(:B).should be_true
|
12
|
+
A.auto_const?(:C).should_not be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/helpers"
|
2
|
+
|
3
|
+
describe "An auto_created module" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
Object.instance_eval { remove_const(:A) if const_defined?(:A) }
|
7
|
+
module A
|
8
|
+
module Foo; end
|
9
|
+
include AutoCode
|
10
|
+
auto_create_class :B do
|
11
|
+
def kobold
|
12
|
+
"koboldy"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "automatically includes AutoCode" do
|
19
|
+
A::B.included_modules.should include( AutoCode )
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can be initialized by later auto_eval declarations" do
|
23
|
+
A.auto_eval(:B) { def smurf; "smurfy" ; end }
|
24
|
+
A::B.new.kobold.should == "koboldy"
|
25
|
+
A::B.new.smurf.should == "smurfy"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can be referenced in auto_* declarations without getting born" do
|
29
|
+
A.auto_create_class :C, :B
|
30
|
+
A.const_defined?(:B).should == false
|
31
|
+
A::C.new.kobold.should == "koboldy"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "auto_eval" do
|
37
|
+
|
38
|
+
it "does a module_eval for constants that are already defined" do
|
39
|
+
A.auto_eval :Foo do
|
40
|
+
def self.yell; "Brick!" ; end
|
41
|
+
end
|
42
|
+
A::Foo.yell.should == "Brick!"
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
from_waves = lambda do
|
49
|
+
app.auto_create_module( :Views ) do
|
50
|
+
include AutoCode
|
51
|
+
auto_create_class :Default, Waves::Views::Base
|
52
|
+
auto_load :Default, :directories => [ :views ]
|
53
|
+
auto_create_class true, :Default
|
54
|
+
auto_load true, :directories => [ :views ]
|
55
|
+
end
|
56
|
+
app.auto_eval :Views do
|
57
|
+
auto_eval :Default do
|
58
|
+
include ViewMethods
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: autocode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Yoder
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date:
|
14
|
+
date: 2009-08-30 00:00:00 -07:00
|
15
15
|
default_executable:
|
16
16
|
dependencies: []
|
17
17
|
|
@@ -25,10 +25,14 @@ extra_rdoc_files: []
|
|
25
25
|
|
26
26
|
files:
|
27
27
|
- lib/autocode.rb
|
28
|
+
- lib/rack
|
29
|
+
- lib/rack/reload.rb
|
28
30
|
- test/auto_create.rb
|
29
31
|
- test/auto_eval.rb
|
30
32
|
- test/auto_load.rb
|
31
33
|
- test/helpers.rb
|
34
|
+
- test/introspection.rb
|
35
|
+
- test/least_surprise.rb
|
32
36
|
- test/normalize.rb
|
33
37
|
has_rdoc: true
|
34
38
|
homepage: http://dev.zeraweb.com/
|
@@ -52,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
56
|
requirements: []
|
53
57
|
|
54
58
|
rubyforge_project: autocode
|
55
|
-
rubygems_version: 1.
|
59
|
+
rubygems_version: 1.3.1
|
56
60
|
signing_key:
|
57
61
|
specification_version: 2
|
58
62
|
summary: Utility for auto-including, reloading, and generating classes and modules.
|