lawrencepit-autocode 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/autocode.rb ADDED
@@ -0,0 +1,135 @@
1
+ module Autocode
2
+
3
+ def self.extended( mod )
4
+ included(mod)
5
+ 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 )
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
+ # create a lambda that looks for a file to load
60
+ file_finder = lambda do |cname|
61
+ filename = snake_case.call(cname.to_s << ".rb")
62
+ path = directories.map { |dir| File.join(dir.to_s, filename) }.find { |path| File.exist?( path ) }
63
+ end
64
+ # if no exemplar is given, assume Module.new
65
+ @load_files ||= Hash.new
66
+ @load_files[key] = [file_finder, options[:exemplar] || Module.new]
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
+ # Unloads all the constants that were loaded and removes all auto* definitions.
96
+ def unload
97
+ reload
98
+ @exemplars = @init_blocks = @load_files = nil
99
+ return self
100
+ end
101
+
102
+ private
103
+
104
+ define_method :const_missing do | cname | #:nodoc:
105
+ cname = cname.to_sym
106
+ @exemplars ||= Hash.new
107
+ @init_blocks ||= Hash.new { |h,k| h[k] = [] }
108
+ @load_files ||= Hash.new
109
+ exemplar = @exemplars[cname] || @exemplars[true]
110
+ blocks = @init_blocks[cname]
111
+ blocks = @init_blocks[true] + blocks if @exemplars[cname].nil? && @init_blocks[true]
112
+ load_file_finder, load_class = @load_files[cname] || @load_files[true]
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
+ blocks.each do |block|
126
+ object.module_eval( &block) if block
127
+ end
128
+
129
+ load(filename) if filename
130
+
131
+ return object
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,43 @@
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
+ end
data/test/autoinit.rb ADDED
@@ -0,0 +1,58 @@
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 "fdfdsf" 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 ADDED
@@ -0,0 +1,86 @@
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
data/test/helpers.rb ADDED
@@ -0,0 +1,7 @@
1
+ %w{ rubygems bacon metaid }.each { |dep| require dep }
2
+ Bacon.extend Bacon::TestUnitOutput
3
+ Bacon.summary_on_exit
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__) , "../lib")
6
+ require 'autocode'
7
+
@@ -0,0 +1,9 @@
1
+ module Thingy
2
+ module Whatsit
3
+ class Critter
4
+ def self.gizmo
5
+ 1
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Thingy
2
+ module Mabob
3
+ class DooDad
4
+ def self.gizmo
5
+ 1
6
+ end
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lawrencepit-autocode
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.5
5
+ platform: ruby
6
+ authors:
7
+ - Dan Yoder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-04-30 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: metaid
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description:
25
+ email:
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - lib/autocode.rb
34
+ - test/autocreate.rb
35
+ - test/autoinit.rb
36
+ - test/autoload.rb
37
+ - test/helpers.rb
38
+ - test/test_lib
39
+ - test/test_lib/critter.rb
40
+ - test/test_lib/doo_dad.rb
41
+ has_rdoc: true
42
+ homepage: http://dev.zeraweb.com/
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.8.6
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.0.1
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: Utility for auto-including, reloading, and generating classes and modules.
67
+ test_files: []
68
+