automatthew-auto_code 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/auto_code.rb +104 -0
- data/test/auto_create.rb +28 -0
- data/test/auto_eval.rb +21 -0
- data/test/auto_load.rb +52 -0
- data/test/helpers.rb +15 -0
- metadata +66 -0
data/lib/auto_code.rb
ADDED
@@ -0,0 +1,104 @@
|
|
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
|
7
|
+
end
|
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?
|
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
|
38
|
+
|
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
|
49
|
+
end
|
50
|
+
|
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
|
54
|
+
end
|
55
|
+
|
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
|
59
|
+
end
|
60
|
+
|
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 )
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convenience method for auto_create.
|
67
|
+
def auto_create_module( key = true, &block )
|
68
|
+
auto_create( key,{ :exemplar => Module.new }, &block )
|
69
|
+
end
|
70
|
+
|
71
|
+
# Reloading stuff ...
|
72
|
+
|
73
|
+
# Returns the list of constants that would be reloaded upon a call to reload.
|
74
|
+
def reloadable( *names ) ; @reloadable + names ; end
|
75
|
+
|
76
|
+
# Reloads all the constants that were loaded via auto_code. Technically, all reload
|
77
|
+
# is doing is undefining them (by calling +remove_const+ on each in turn); they won't get
|
78
|
+
# reloaded until they are referenced.
|
79
|
+
def reload ; @reloadable.each { |name| remove_const( name ) } ; @reloadable = [] ; self; end
|
80
|
+
|
81
|
+
# Unloads all the constants that were loaded and removes all auto* definitions.
|
82
|
+
def unload ; reload ; @initializers = Hash.new { |h,k| h[k] = [] } ; self ; end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
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 )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/test/auto_create.rb
ADDED
@@ -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,21 @@
|
|
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
|
+
end
|
16
|
+
|
17
|
+
specify "allow you to run blocks after an object is first created" do
|
18
|
+
A::B::C.class.should == Class
|
19
|
+
end
|
20
|
+
|
21
|
+
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
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
%w{ bacon metaid }.each { |dep| require dep }
|
3
|
+
# Bacon.extend Bacon::TestUnitOutput
|
4
|
+
Bacon.summary_on_exit
|
5
|
+
|
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 '../lib/auto_code'
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: automatthew-auto_code
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Yoder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-05-13 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/auto_code.rb
|
34
|
+
- test/auto_code.gemspec
|
35
|
+
- test/auto_create.rb
|
36
|
+
- test/auto_eval.rb
|
37
|
+
- test/auto_load.rb
|
38
|
+
- test/helpers.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://dev.zeraweb.com/
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.8.6
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.0.1
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: Utility for auto-including, reloading, and generating classes and modules.
|
65
|
+
test_files: []
|
66
|
+
|