automatthew-autocode 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,120 @@
1
+ require 'reloadable'
2
+
3
+ module Autocode
4
+
5
+ def self.extended( mod ) #:nodoc:
6
+
7
+ old = mod.method( :const_missing )
8
+ mod.metaclass.class_eval do
9
+
10
+ def autocreate( key, exemplar, &block )
11
+ keys = case key
12
+ when true, Symbol then [key]
13
+ when Array then key
14
+ end
15
+
16
+ keys.each do |k|
17
+ exemplars[k] = exemplar
18
+ init_blocks[k] << block
19
+ end
20
+
21
+ return self
22
+ end
23
+
24
+ def autoinit( key, &block )
25
+ # See whether we're dealing with a namespaced constant,
26
+ # The match groupings for, e.g. "X::Y::Z", would be
27
+ # ["X::Y", "Z"]
28
+ match = key.to_s.match(/^(.*)::([\w\d_]+)$/)
29
+
30
+ if match
31
+ namespace, cname = match[1,2]
32
+ const = module_eval(namespace)
33
+ const.module_eval do
34
+ @init_blocks ||= Hash.new { |h,k| h[k] = [] }
35
+ @init_blocks[cname.to_sym] << block
36
+ end
37
+ else
38
+ @init_blocks ||= Hash.new { |h,k| h[k] = [] }
39
+ @init_blocks[key] << block
40
+ end
41
+ return self
42
+ end
43
+
44
+ def autoload( key, options )
45
+ # look for load_files in either a specified directory, or in the directory
46
+ # with the snakecase name of the enclosing module
47
+ directories = [options[:directories] || default_directory(self.name)].flatten
48
+ # create a lambda that looks for a file to load
49
+ file_finder = lambda do |cname|
50
+ filename = default_file_name(cname)
51
+ dirname = directories.find do |dir|
52
+ File.exist?(File.join(dir.to_s, filename))
53
+ end
54
+ dirname ? File.join(dirname.to_s, filename) : nil
55
+ end
56
+ # if no exemplar is given, assume Module.new
57
+ load_files[key] = [file_finder, options[:exemplar] || Module.new]
58
+ return self
59
+ end
60
+
61
+ def autoload_class(key, superclass=nil, options={})
62
+ options[:exemplar] = Class.new(superclass)
63
+ autoload key, options
64
+ end
65
+
66
+
67
+ define_method :const_missing do | cname | #:nodoc:
68
+ cname = cname.to_sym
69
+ exemplar = exemplars[cname] || exemplars[true]
70
+ blocks = init_blocks[cname]
71
+ blocks = init_blocks[true] + blocks if exemplars[cname].nil? && init_blocks[true]
72
+ load_file_finder, load_class = load_files[cname] || load_files[true]
73
+
74
+ if load_file_finder && filename = load_file_finder.call(cname)
75
+ object = load_class.clone
76
+ elsif exemplar
77
+ object = exemplar.clone
78
+ else
79
+ return old.call(cname)
80
+ end
81
+
82
+ (@reloadable ||= []) << cname;
83
+ const_set( cname, object )
84
+
85
+ blocks.each do |block|
86
+ object.module_eval( &block) if block
87
+ end
88
+ load(filename) if filename
89
+ return object
90
+ end
91
+
92
+ # helper methods. May need to make them private.
93
+ def exemplars
94
+ @exemplars ||= Hash.new
95
+ end
96
+
97
+ def init_blocks
98
+ @init_blocks ||= Hash.new { |h,k| h[k] = [] }
99
+ end
100
+
101
+ def load_files
102
+ @load_files ||= Hash.new
103
+ end
104
+
105
+ def reloadable
106
+ @reloadable ||= []
107
+ end
108
+
109
+ def default_file_name(cname)
110
+ ( cname.to_s.gsub(/([a-z\d])([A-Z\d])/){ "#{$1}_#{$2}"} << ".rb" ).downcase
111
+ end
112
+
113
+ def default_directory(module_name)
114
+ m = self.name.match( /^.*::([\w\d_]+)$/)
115
+ m[1].snake_case
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'metaid'
3
+
4
+ # Reloadable simply makes it possible for a module's code to be reloaded. *Important*: Only code loaded via Autoload or Autocreate will be reloaded. Also, the module itself is not reloaded, only the modules and classes within it that were loaded via *Autocode*.
5
+ #
6
+ # To use Reloadable, simply extend a given module with Reloadable. This will add two methods to the module: reloadable and reload. These are described below.
7
+
8
+ module Reloadable
9
+
10
+ def self.extended( mod ) #:nodoc:
11
+
12
+ mod.metaclass.class_eval do
13
+
14
+ # Returns the list of constants that would be reloaded upon a call to reload.
15
+ def reloadable( *names )
16
+ ( @reloadable ||= [] ).concat(names)
17
+ return self
18
+ end
19
+
20
+ # Reloads all the constants that were loaded via *Autocode*. Technically, all reload is doing is undefining them (by calling +remove_const+ on each in turn); they won't get reloaded until they are referenced.
21
+ def reload
22
+ ( @reloadable ||=[] ).each { |name| remove_const( name ) }
23
+ @reloadable = []; return self
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,38 @@
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
+ it "should autocreate some constants" do
18
+ Thingy::Tom.peeps.should == true
19
+ Thingy::Dick.peeps.should == false
20
+ Thingy::Harry.peeps.should == false
21
+ end
22
+
23
+ it "should not autocreate unregistered constants" do
24
+ lambda { Thingy::Mabob::MooCow }.should.raise NameError
25
+ end
26
+
27
+ it "unless autocreate was called with key of true" do
28
+ module Duffel
29
+ extend Autocode
30
+ autocreate true, Class.new do
31
+ def self.universal; true; end
32
+ end
33
+ end
34
+
35
+ Duffel::AnyThing.universal.should == true
36
+ end
37
+
38
+ end
@@ -0,0 +1,52 @@
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 overrides 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
+ it "fdfdsf" do
41
+ Thingy::Whatsit.in_scope.should.be.true
42
+ Thingy::Whatsit::Critter.outside_scope.should.be.true
43
+ Thingy::Whatsit::Critter.new.instance.should.be.true
44
+ Thingy::Big::Bad::John.stinks?.should.be.false
45
+ end
46
+
47
+ it "should run autoinit blocks before the file loading" do
48
+ Thingy::Whatsit::Critter.gizmo.should == 1
49
+ end
50
+
51
+
52
+ end
@@ -0,0 +1,22 @@
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 true, :exemplar => Class.new, :directories => [File.join(File.dirname(__FILE__), "test_lib")]
10
+ end
11
+ end
12
+ end
13
+
14
+ it "should autoload where files match" do
15
+ Thingy::Mabob::DooDad.should.respond_to :gizmo
16
+ end
17
+
18
+ it "should not autocreate those unmentioned and fileable" do
19
+ lambda { Thingy::Mabob::MooCow }.should.raise NameError
20
+ end
21
+
22
+ end
@@ -0,0 +1,8 @@
1
+ %w{ rubygems bacon}.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
+
8
+ # Dir.chdir(File.dirname(__FILE__))
@@ -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,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: automatthew-autocode
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.3
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
+ - lib/reloadable.rb
35
+ - test/autocreate.rb
36
+ - test/autoinit.rb
37
+ - test/autoload.rb
38
+ - test/helpers.rb
39
+ - test/test_lib
40
+ - test/test_lib/critter.rb
41
+ - test/test_lib/doo_dad.rb
42
+ has_rdoc: true
43
+ homepage: http://dev.zeraweb.com/
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.8.6
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.0.1
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: Utility for auto-including, reloading, and generating classes and modules.
68
+ test_files: []
69
+