autocode 1.0.0 → 1.0.1

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.
@@ -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.