automatthew-autocode 0.9.3
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 +120 -0
- data/lib/reloadable.rb +30 -0
- data/test/autocreate.rb +38 -0
- data/test/autoinit.rb +52 -0
- data/test/autoload.rb +22 -0
- data/test/helpers.rb +8 -0
- data/test/test_lib/critter.rb +9 -0
- data/test/test_lib/doo_dad.rb +9 -0
- metadata +69 -0
data/lib/autocode.rb
ADDED
@@ -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
|
data/lib/reloadable.rb
ADDED
@@ -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
|
data/test/autocreate.rb
ADDED
@@ -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
|
data/test/autoinit.rb
ADDED
@@ -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
|
data/test/autoload.rb
ADDED
@@ -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
|
data/test/helpers.rb
ADDED
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
|
+
|