dyoder-autocode 0.9.7 → 0.9.8

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 CHANGED
@@ -1,130 +1,103 @@
1
- module Autocode
2
-
3
- def self.extended( mod )
4
- included(mod)
1
+ module AutoCode
2
+
3
+ # always make sure we have a camel-cased symbol
4
+ def AutoCode.normalize( cname )
5
+ return cname unless cname.is_a? String
6
+ cname.gsub(/(_)(\w)/) { $2.upcase }.gsub(/^([a-z])/) { $1.upcase }.intern
5
7
  end
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
- @init_blocks[k] << block
23
- end
24
-
25
- return self
26
- end
27
-
28
- def autocreate_class( key = true, superclass = Class, &block )
29
- autocreate( key, Class.new( superclass ), &block )
8
+
9
+ class Loader
10
+ def initialize(options={}); @directories = options[:directories] ; end
11
+ def call(cname)
12
+ filename = Loader.snake_case( cname ) << '.rb'
13
+ if @directories.nil?
14
+ Kernel.load( filename )
15
+ else
16
+ path = @directories.map { |dir| File.join( dir.to_s, filename ) }.find { |path| File.exist?( path ) }
17
+ Kernel.load( path ) rescue nil unless path.nil?
30
18
  end
19
+ end
20
+ def Loader.snake_case(cname)
21
+ cname.to_s.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
22
+ end
23
+ end
24
+
25
+ class Creator
26
+ def initialize( options, &block )
27
+ @exemplar = (options[:exemplar]||Module.new).clone; @block = block
28
+ end
29
+ def call ; @exemplar.module_eval( &@block ) if @block ; @exemplar ; end
30
+ end
31
+
32
+ class Initializer
33
+ def initialize( &block ) ;@block = block ; end
34
+ def call( mod ) ; mod.module_eval( &@block ) ; end
35
+ end
36
+
37
+ def self.extended( mod ) ; included(mod) ; end
31
38
 
32
- def autocreate_module( key = true, &block )
33
- autocreate( key, Module.new, &block )
39
+ def self.included( mod )
40
+
41
+ mod.instance_eval do
42
+
43
+ # Initialize bookkeeping variables needed by AutoCode
44
+ @initializers ||= Hash.new { |h,k| h[k] = [] }; @reloadable ||= []
45
+
46
+ # Adds an auto_create block for the given key using the given exemplar if provided
47
+ def auto_create( key = true, options = {}, &block )
48
+ @initializers[ AutoCode.normalize( key ) ] << Creator.new( options, &block ); self
34
49
  end
35
50
 
36
- def autoinit( key, &block )
37
- if match = key.to_s.match(/^([\w\d_]+)::(.*)$/)
38
- const, rest = match[1,2]
39
- autoinit(const.to_sym) do
40
- autoinit(rest, &block)
41
- end
42
- else
43
- @init_blocks ||= Hash.new { |h,k| h[k] = [] }
44
- @init_blocks[key.to_sym] << block
45
- end
46
-
47
- return self
51
+ # Adds an auto_load block for the given key and directories
52
+ def auto_load( key = true, options = {} )
53
+ @initializers[ AutoCode.normalize( key ) ].unshift( Loader.new( options ) ); self
48
54
  end
49
55
 
50
- def autoload(key = true, options = {})
51
- snake_case = lambda {|name| name.gsub(/([a-z\d])([A-Z])/){"#{$1}_#{$2}"}.tr("-", "_").downcase }
52
- # look for load_files in either a specified directory, or in the directory
53
- # with the snakecase name of the enclosing module
54
- directories = [options[:directories] || snake_case.call(self.name.match( /^.*::([\w\d_]+)$/)[1])].flatten
55
- # create a lambda that looks for a file to load
56
- file_finder = lambda do |cname|
57
- filename = snake_case.call(cname.to_s << ".rb")
58
- path = directories.map { |dir| File.join(dir.to_s, filename) }.find { |path| File.exist?( path ) }
59
- end
60
- # if no exemplar is given, assume Module.new
61
- @load_files ||= Hash.new
62
- @load_files[key] = [file_finder, options[:exemplar] || Module.new]
63
- return self
56
+ # Adds an arbitrary initializer block for the given key
57
+ def auto_eval( key, &block )
58
+ @initializers[ AutoCode.normalize( key ) ] << Initializer.new( &block ); self
64
59
  end
65
60
 
66
- def autoload_class(key = true, superclass = Class, options = {})
67
- options[:exemplar] = Class.new(superclass)
68
- autoload key, options
61
+ # Convenience method for auto_create.
62
+ def auto_create_class( key = true, superclass = Object, &block )
63
+ auto_create( key,{ :exemplar => Class.new( superclass ) }, &block )
69
64
  end
70
65
 
71
- def autoload_module(key = true, options = {})
72
- options[:exemplar] = Module.new
73
- autoload key, options
66
+ # Convenience method for auto_create.
67
+ def auto_create_module( key = true, &block )
68
+ auto_create( key,{ :exemplar => Module.new }, &block )
74
69
  end
75
70
 
71
+ # Reloading stuff ...
72
+
76
73
  # Returns the list of constants that would be reloaded upon a call to reload.
77
- def reloadable( *names )
78
- ( @reloadable ||= [] ).concat(names)
79
- return self
80
- end
74
+ def reloadable( *names ) ; @reloadable + names ; end
81
75
 
82
- # Reloads all the constants that were loaded via autocode. Technically, all reload
76
+ # Reloads all the constants that were loaded via auto_code. Technically, all reload
83
77
  # is doing is undefining them (by calling +remove_const+ on each in turn); they won't get
84
78
  # reloaded until they are referenced.
85
- def reload
86
- @reloadable.each { |name| remove_const( name ) } if @reloadable
87
- @reloadable = nil
88
- return self
89
- end
79
+ def reload ; @reloadable.each { |name| remove_const( name ) } ; @reloadable = [] ; self; end
90
80
 
91
81
  # Unloads all the constants that were loaded and removes all auto* definitions.
92
- def unload
93
- reload
94
- @exemplars = @init_blocks = @load_files = nil
95
- return self
96
- end
82
+ def unload ; reload ; @initializers = Hash.new { |h,k| h[k] = [] } ; self ; end
97
83
 
98
84
  private
99
85
 
100
- define_method :const_missing do | cname | #:nodoc:
101
- cname = cname.to_sym
102
- @exemplars ||= Hash.new
103
- @init_blocks ||= Hash.new { |h,k| h[k] = [] }
104
- @load_files ||= Hash.new
105
- exemplar = @exemplars[cname] || @exemplars[true]
106
- blocks = @init_blocks[cname]
107
- blocks = @init_blocks[true] + blocks if @exemplars[cname].nil? && @init_blocks[true]
108
- load_file_finder, load_class = @load_files[cname] || @load_files[true]
109
-
110
- if load_file_finder && filename = load_file_finder.call(cname)
111
- object = load_class.clone
112
- elsif exemplar
113
- object = exemplar.clone
114
- else
115
- return old.call(cname)
116
- end
117
-
118
- (@reloadable ||= []) << cname;
119
- const_set( cname, object )
120
-
121
- blocks.each do |block|
122
- object.module_eval( &block) if block
86
+ old = method( :const_missing )
87
+ (class << self ; self ; end ).instance_eval do
88
+ define_method :const_missing do | cname |
89
+ ( @initializers[cname] + @initializers[true] ).each do |initializer|
90
+ case initializer
91
+ when Loader then initializer.call( cname ) unless const_defined?(cname)
92
+ when Creator then const_set( cname, initializer.call ) unless const_defined?(cname)
93
+ else
94
+ return old.call(cname) unless const_defined?( cname )
95
+ initializer.call( const_get( cname ) ) if const_defined?( cname )
96
+ end
97
+ end
98
+ return old.call(cname) unless const_defined?( cname )
99
+ @reloadable << cname ; const_get( cname )
123
100
  end
124
-
125
- load(filename) if filename
126
-
127
- return object
128
101
  end
129
102
  end
130
103
  end
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{auto_code}
3
+ s.version = "0.9.8"
4
+
5
+ s.specification_version = 2 if s.respond_to? :specification_version=
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Dan Yoder"]
9
+ s.date = %q{2008-05-11}
10
+ s.files = ["lib/auto_code.rb", "test/auto_create.rb", "test/auto_eval.rb", "test/auto_load", "test/auto_load.rb", "test/helpers.rb"]
11
+ s.has_rdoc = true
12
+ s.homepage = %q{http://dev.zeraweb.com/}
13
+ s.require_paths = ["lib"]
14
+ s.required_ruby_version = Gem::Requirement.new(">= 1.8.6")
15
+ s.rubygems_version = %q{1.0.1}
16
+ s.summary = %q{Utility for auto-including, reloading, and generating classes and modules.}
17
+
18
+ s.add_dependency(%q<metaid>, [">= 0"])
19
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), 'helpers.rb')
2
+
3
+ describe "auto_create should" do
4
+
5
+ before do
6
+ A.unload if defined? A and A.respond_to? :unload
7
+ module A
8
+ include AutoCode
9
+ auto_create_module :B do
10
+ include AutoCode
11
+ auto_create_class
12
+ end
13
+ end
14
+ end
15
+
16
+ specify "allow you create and initialize a given const name" do
17
+ A::B.class.should == Module
18
+ end
19
+
20
+ specify "allow you create and initialize const using a wildcard" do
21
+ A::B::C.class.should === Class
22
+ end
23
+
24
+ specify "should raise a NameError if a const doesn't match" do
25
+ lambda{ A::C }.should.raise NameError
26
+ end
27
+
28
+ end
data/test/auto_eval.rb ADDED
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), 'helpers.rb')
2
+
3
+ describe "auto_eval should" do
4
+
5
+ before do
6
+ A.unload if defined? A and A.respond_to? :unload
7
+ module A
8
+ include AutoCode
9
+ auto_create_module :B
10
+ end
11
+ A.auto_eval :B do
12
+ include AutoCode
13
+ auto_create_class
14
+ end
15
+ A.auto_eval :B do
16
+ include AutoCode
17
+ auto_eval :R do
18
+ def self.foo; end
19
+ end
20
+ end
21
+ end
22
+
23
+ specify "allow you to run blocks after an object is first created" do
24
+ A::B::C.class.should == Class
25
+ end
26
+
27
+ specify "work inside an auto_* block" do
28
+ A::B::R.class.should == Class
29
+ end
30
+
31
+ end
data/test/auto_load.rb ADDED
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), 'helpers.rb')
2
+ require 'extensions/io'
3
+
4
+
5
+ describe "auto_load should" do
6
+
7
+ before do
8
+ A.unload if defined? A and A.respond_to? :unload
9
+ FileUtils.mkdir('tmp')
10
+ @path = File.join( 'tmp', 'b.rb' )
11
+ content =<<-EOF
12
+ module A
13
+ module B
14
+ end
15
+ end
16
+ EOF
17
+ File.write( @path, content )
18
+ module A
19
+ include AutoCode
20
+ auto_create_class :B
21
+ auto_load :B, :directories => ['tmp']
22
+ auto_create_class :B
23
+ end
24
+
25
+ end
26
+
27
+ after do
28
+ FileUtils.rm( File.join( @path ) )
29
+ FileUtils.rmdir( 'tmp' )
30
+ end
31
+
32
+ specify "allow you to load a file to define a const" do
33
+ A::B.class.should == Module
34
+ end
35
+
36
+ specify "should raise a NameError if a const doesn't match" do
37
+ lambda{ A::C }.should.raise NameError
38
+ end
39
+
40
+ specify "always take precedence over auto_create" do
41
+ A::B.class.should == Module
42
+ end
43
+
44
+ specify "snake case the constant name which is used to map a constant to a filename" do
45
+ AutoCode::Loader.snake_case(:Post).should == "post"
46
+ AutoCode::Loader.snake_case(:GitHub).should == "git_hub"
47
+ AutoCode::Loader.snake_case(:GITRepository).should == "git_repository"
48
+ AutoCode::Loader.snake_case(:Git42Repository).should == "git42_repository"
49
+ AutoCode::Loader.snake_case(:GIT42Repository).should == "git42_repository"
50
+ end
51
+
52
+ end
data/test/helpers.rb CHANGED
@@ -1,8 +1,15 @@
1
1
  require 'rubygems'
2
2
  %w{ bacon metaid }.each { |dep| require dep }
3
- Bacon.extend Bacon::TestUnitOutput
3
+ # Bacon.extend Bacon::TestUnitOutput
4
4
  Bacon.summary_on_exit
5
5
 
6
- $:.unshift File.join(File.dirname(__FILE__) , "../lib")
7
- require 'autocode'
6
+ module Kernel
7
+ private
8
+ def specification(name, &block) Bacon::Context.new(name, &block) end
9
+ end
8
10
 
11
+ Bacon::Context.instance_eval do
12
+ alias_method :specify, :it
13
+ end
14
+
15
+ require '../lib/autocode'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dyoder-autocode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.7
4
+ version: 0.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Yoder
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-09 00:00:00 -07:00
12
+ date: 2008-05-14 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -31,19 +31,12 @@ extra_rdoc_files: []
31
31
 
32
32
  files:
33
33
  - lib/autocode.rb
34
- - test/autocreate.rb
35
- - test/autoinit.rb
36
- - test/autoload.rb
34
+ - test/auto_code.gemspec
35
+ - test/auto_create.rb
36
+ - test/auto_eval.rb
37
+ - test/auto_load.rb
38
+ - test/autocode.gemspec
37
39
  - test/helpers.rb
38
- - test/test_lib
39
- - test/test_lib/answer42_it_is.rb
40
- - test/test_lib/critter.rb
41
- - test/test_lib/doo_dad.rb
42
- - test/test_lib/humbug.rb
43
- - test/test_lib/the_class42_gang.rb
44
- - test/test_lib/the_one_and_only_class.rb
45
- - test/test_lib/the_one_and_only_module.rb
46
- - test/test_lib/the_pretender.rb
47
40
  has_rdoc: true
48
41
  homepage: http://dev.zeraweb.com/
49
42
  post_install_message:
data/test/autocreate.rb DELETED
@@ -1,60 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'helpers.rb')
2
-
3
- describe "A module where autocreate has been called" do
4
- before do
5
- module Thingy
6
- extend Autocode
7
- autocreate :Tom, Module.new do
8
- def self.peeps; true; end
9
- end
10
-
11
- autocreate [:Dick, :Harry], Class.new do
12
- def self.peeps; false; end
13
- end
14
- end
15
- end
16
-
17
- after do
18
- Thingy.unload
19
- end
20
-
21
- it "should autocreate some constants" do
22
- Thingy::Tom.peeps.should == true
23
- Thingy::Dick.peeps.should == false
24
- Thingy::Harry.peeps.should == false
25
- end
26
-
27
- it "should not autocreate unregistered constants" do
28
- lambda { Thingy::Mabob::MooCow }.should.raise NameError
29
- end
30
-
31
- it "unless autocreate was called with key of true" do
32
- module Duffel
33
- extend Autocode
34
- autocreate true, Class.new do
35
- def self.universal; true; end
36
- end
37
- end
38
-
39
- Duffel::AnyThing.universal.should == true
40
- Duffel.unload
41
- end
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
-
60
- end
data/test/autoinit.rb DELETED
@@ -1,58 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'helpers.rb')
2
-
3
- describe "thingy" do
4
- before do
5
- module Thingy
6
- extend Autocode
7
- autocreate(:Whatsit, Module.new) do
8
- extend Autocode
9
- autoload :Critter, :exemplar => Class.new, :directories => File.join(File.dirname(__FILE__), "test_lib")
10
- end
11
-
12
- autoinit(:Whatsit) do
13
- def self.in_scope; true; end
14
- end
15
-
16
- autoinit('Whatsit::Critter') do
17
- def self.outside_scope; true; end
18
- def instance; true; end
19
- # this definition is overridden by the one in the file
20
- def self.gizmo; 2; end
21
- end
22
-
23
- autocreate :Big, Module.new do
24
- extend Autocode
25
- autocreate :Bad, Module.new do
26
- extend Autocode
27
- autocreate :John, Class.new do
28
- def self.stinks?; true; end
29
- end
30
- end
31
- end
32
-
33
- autoinit('Big::Bad::John') do
34
- def self.stinks?; false; end
35
- end
36
-
37
- end
38
- end
39
-
40
- after do
41
- Thingy::Whatsit.unload
42
- Thingy::Big::Bad.unload
43
- Thingy::Big.unload
44
- Thingy.unload
45
- end
46
-
47
- it "should work with namespaced constants" do
48
- Thingy::Whatsit.in_scope.should.be.true
49
- Thingy::Whatsit::Critter.outside_scope.should.be.true
50
- Thingy::Whatsit::Critter.new.instance.should.be.true
51
- Thingy::Big::Bad::John.stinks?.should.be.false
52
- end
53
-
54
- it "should run autoinit blocks before the file loading" do
55
- Thingy::Whatsit::Critter.gizmo.should == 1
56
- end
57
-
58
- end
data/test/autoload.rb DELETED
@@ -1,86 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'helpers.rb')
2
-
3
- describe "A module where autoload has been called" do
4
-
5
- before do
6
- module Thingy
7
- module Mabob
8
- extend Autocode
9
- autoload_class :DooDad, Class, :directories => [File.join(File.dirname(__FILE__), "test_lib")]
10
- autoload_module :Humbug, :directories => [File.join(File.dirname(__FILE__), "test_lib")]
11
- end
12
- end
13
- end
14
-
15
- after do
16
- Thingy::Mabob.unload
17
- end
18
-
19
- it "should autoload where files match" do
20
- Thingy::Mabob::DooDad.should.respond_to :gizmo
21
- Thingy::Mabob::Humbug.should.respond_to :full_of_it?
22
- end
23
-
24
- it "should not autoload where it matches a file but is out of scope" do
25
- lambda { Thingy::Mabob::Answer42ItIs }.should.raise NameError
26
- lambda { Thingy::Whatsit::Critter }.should.raise NameError
27
- end
28
-
29
- it "should not autocreate those unmentioned and fileable" do
30
- lambda { Thingy::Mabob::MooCow }.should.raise NameError
31
- end
32
-
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
43
- end
44
-
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
57
- end
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
86
- end
@@ -1,9 +0,0 @@
1
- module Thingy
2
- module Mabob
3
- class Answer42ItIs
4
- def self.really?
5
- true
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Thingy
2
- module Whatsit
3
- class Critter
4
- def self.gizmo
5
- 1
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Thingy
2
- module Mabob
3
- class DooDad
4
- def self.gizmo
5
- 1
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Thingy
2
- module Mabob
3
- module Humbug
4
- def self.full_of_it?
5
- true
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Waves
2
- module TestLib
3
- class TheClass42Gang < Thingy::Mabob::DooDad
4
- def self.party?
5
- true
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Waves
2
- module TestLib
3
- class TheOneAndOnlyClass
4
- def self.help()
5
- "class help"
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Waves
2
- module TestLib
3
- module TheOneAndOnlyModule
4
- def self.help()
5
- "module help"
6
- end
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- module Haha
2
- module TestLib
3
- module ThePretender
4
- def self.help()
5
- "I am not in your waves space"
6
- end
7
- end
8
- end
9
- end