autocode 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.gsub(/^([a-z])/) { $1.upcase }.gsub(/(_)(\w)/) { $2.upcase }.intern
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
- # Initialize bookkeeping variables needed by AutoCode
27
- @autocode = {
28
- :constructors => Hash.new { |h,k| h[k] = [] },
29
- :initializers => Hash.new { |h,k| h[k] = [] },
30
- :loaded => []
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
- @autocode[:constructors][ AutoCode.normalize( key ) ] << lambda do | cname |
36
- exemplar = ( options[:exemplar] || Module.new ).clone
37
- exemplar.module_eval( &block ) if block
38
- const_set( cname, exemplar )
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
- if options[:directories].nil?
47
- Kernel.load( filename ) if File.exist?( filename )
87
+ options[:directories] ||= '.'
88
+ path = if target = options[:target]
89
+ target.call(cname)
48
90
  else
49
- path = options[:directories].
50
- map { |dir| File.join( dir.to_s, filename ) }.
51
- find { |path| File.exist?( path ) }
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
- @autocode[:initializers][ AutoCode.normalize( key ) ] << lambda do | mod |
60
- mod.module_eval( &block )
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
- # Convenience method for auto_create.
65
- def auto_create_class( key = true, superclass = Object, &block )
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 ; @autocode[:loaded].each { |name| remove_const( name ) } ; @autocode[:loaded] = [] ; end
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
- constructors = @autocode[:constructors][true] + @autocode[:constructors][cname]
86
- constructors.pop.call( cname ) until ( const_defined?( cname ) or constructors.empty? )
87
- return old.call( cname ) unless const_defined?( cname )
88
- initializers = @autocode[:initializers][true] + @autocode[:initializers][cname]
89
- mod = const_get( cname ) ; initializers.shift.call( mod ) until initializers.empty?
90
- @autocode[:loaded] << cname ; const_get( cname )
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
+
@@ -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
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'helpers.rb')
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
- include AutoCode
11
- auto_create_class
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
- specify "allows you to create and initialize a given const name" do
17
- A::B.class.should == Module
21
+ it "allows you create and initialize a given const name" do
22
+ A::B.smurf.should == "blue"
18
23
  end
19
24
 
20
- specify "allows you to create and initialize const using a wildcard" do
21
- A::B::C.class.should === Class
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
- specify "raises a NameError if a const doesn't match" do
25
- lambda{ A::C }.should.raise NameError
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
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'helpers.rb')
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
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'helpers.rb')
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
- lambda{ A::C }.should.raise NameError
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
@@ -1,15 +1,3 @@
1
- require 'rubygems'
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
- module Kernel
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.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: 2008-06-09 00:00:00 -05:00
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.0.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.