easyload 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +5 -0
- data/README.md +64 -0
- data/Rakefile +6 -0
- data/easyload.gemspec +23 -0
- data/lib/easyload.rb +20 -0
- data/lib/easyload/singleton_extensions.rb +72 -0
- data/lib/easyload/version.rb +4 -0
- data/spec/basic_spec.rb +56 -0
- data/spec/deep/example.rb +4 -0
- data/spec/deep/example/thing.rb +4 -0
- data/spec/examples/load_counter.rb +7 -0
- data/spec/examples/nested.rb +4 -0
- data/spec/examples/nested/leaf.rb +4 -0
- data/spec/examples/node.rb +4 -0
- data/spec/naming_spec.rb +23 -0
- metadata +101 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
Easyload
|
2
|
+
========
|
3
|
+
An alternative to autoload that relies on your project's directory structure to determine its
|
4
|
+
module hierarchy, recursively.
|
5
|
+
|
6
|
+
This is an opinionated loading method that attempts to simplify your development process by
|
7
|
+
enforcing that your directory and file names follow convention and are consistent with your
|
8
|
+
project's module hierarchy. Additionally, it allows for painless refactoring and reduces code
|
9
|
+
repetition by removing the need of declaring your module hierarchy in every single source
|
10
|
+
file. The project's directory structure determines that; it's just extra work to keep the two
|
11
|
+
in sync.
|
12
|
+
|
13
|
+
Easyload frowns upon the require statement, and would much rather that your library know how
|
14
|
+
to load itself. Simply reference the constant that you want, and it's there.
|
15
|
+
|
16
|
+
|
17
|
+
General Use
|
18
|
+
-----------
|
19
|
+
To easyload a project, it must define at least one top level module to be used as the easyload
|
20
|
+
root. From that root, and the search path that it defines, all child constants are easyloaded
|
21
|
+
according to your directory structure.
|
22
|
+
|
23
|
+
For example, with a directory structure of:
|
24
|
+
lib/
|
25
|
+
my_easyloaded_module.rb
|
26
|
+
my_easyloaded_module/
|
27
|
+
child_module.rb
|
28
|
+
child_module/
|
29
|
+
leafy.rb
|
30
|
+
node.rb
|
31
|
+
|
32
|
+
`lib/my_easyloaded_module.rb`:
|
33
|
+
module MyEasyloadedModule
|
34
|
+
import Easyload
|
35
|
+
end
|
36
|
+
|
37
|
+
`lib/my_easyloaded_module/child_module.rb`:
|
38
|
+
module ChildModule
|
39
|
+
...
|
40
|
+
end
|
41
|
+
|
42
|
+
`lib/my_easyloaded_module/child_module/leafy.rb`:
|
43
|
+
class Leafy
|
44
|
+
...
|
45
|
+
end
|
46
|
+
|
47
|
+
`lib/my_easyloaded_module/node.rb`:
|
48
|
+
class Node
|
49
|
+
...
|
50
|
+
end
|
51
|
+
|
52
|
+
Would result in the following module/class hierarchy being easyloadable:
|
53
|
+
MyEasyloadedModule
|
54
|
+
MyEasyloadedModule::ChildModule
|
55
|
+
MyEasyloadedModule::ChildModule::Leafy
|
56
|
+
MyEasyloadedModule::Node
|
57
|
+
|
58
|
+
|
59
|
+
Easyload Configuration
|
60
|
+
----------------------
|
61
|
+
When a class or module includes `Easyload`, the
|
62
|
+
`Easyload::SingletonExtensions` module is mixed into that class or module's singleton methods,
|
63
|
+
providing the easyload API. See [`Easyload::SingletonExtensions`](http://rdoc.info/github/nevir/easyload/master/Easyload/SingletonExtensions)
|
64
|
+
for a reference of what configuration can be performed on an easyloaded class or module.
|
data/Rakefile
ADDED
data/easyload.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'easyload/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'easyload'
|
7
|
+
s.version = Easyload::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Ian MacLeod']
|
10
|
+
s.email = ['ian@nevir.net']
|
11
|
+
s.homepage = ''
|
12
|
+
s.summary = 'A recursive and opinionated alternative to autoload.'
|
13
|
+
s.description = 'An alternative to autoload that relies on your project\'s directory structure to determine its module hierarchy, recursively.'
|
14
|
+
|
15
|
+
s.rubyforge_project = 'easyload'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ['lib']
|
21
|
+
|
22
|
+
s.add_development_dependency('rspec', ['~> 2.5.0'])
|
23
|
+
end
|
data/lib/easyload.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'easyload/singleton_extensions'
|
3
|
+
|
4
|
+
module Easyload
|
5
|
+
autoload :VERSION, 'version'
|
6
|
+
|
7
|
+
# Handle module/class inclusions in a clean manner, and try to guess our easyload root.
|
8
|
+
#
|
9
|
+
# @private
|
10
|
+
def self.included(in_mod)
|
11
|
+
class << in_mod
|
12
|
+
include SingletonExtensions
|
13
|
+
end
|
14
|
+
|
15
|
+
if in_mod.name
|
16
|
+
components = in_mod.name.split('::').map {|n| in_mod.easyload_path_component_for_sym(n)}
|
17
|
+
in_mod.easyload_from(components.join('/'))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Easyload
|
3
|
+
# The singleton methods that are defined for a module that includes {Easyload}.
|
4
|
+
module SingletonExtensions
|
5
|
+
# The root path that easyload should use when loading a child constant for this module.
|
6
|
+
#
|
7
|
+
# Defaults to the path component form of the class/module's name
|
8
|
+
attr_reader :easyload_root
|
9
|
+
|
10
|
+
# Sets easyload_root.
|
11
|
+
def easyload_from(root_path)
|
12
|
+
@easyload_root = root_path.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
# Converts a CamelCased symbol into a path component.
|
16
|
+
#
|
17
|
+
# * A +Single+ symbol becomes +single+.
|
18
|
+
# * +MultiWordSymbols+ become +multi_word_symbols+.
|
19
|
+
# * +ACRONYMS+ are treated like words: +acronyms+.
|
20
|
+
# * +ABCFoo+ is considered to be a mix of acronyms and words: +abc_foo+.
|
21
|
+
def easyload_path_component_for_sym(sym)
|
22
|
+
path = sym.to_s.dup
|
23
|
+
path.gsub!(/([A-Z]+)([A-Z][a-z]+)/, '\1_\2_')
|
24
|
+
path.gsub!(/([A-Z][a-z]+)/, '\1_')
|
25
|
+
path.gsub!(/_+/, '_')
|
26
|
+
path.chomp!('_')
|
27
|
+
path.downcase!
|
28
|
+
end
|
29
|
+
|
30
|
+
# The meat of easyloading happens here.
|
31
|
+
#
|
32
|
+
# @private
|
33
|
+
def const_missing(sym)
|
34
|
+
if not self.instance_variable_defined? :@easyload_root
|
35
|
+
$stderr.puts "You must call easyload_from() before you can easyload #{self}::#{sym}"
|
36
|
+
return super(sym)
|
37
|
+
end
|
38
|
+
|
39
|
+
path_component = self.easyload_path_component_for_sym(sym)
|
40
|
+
easyload_path = File.join(@easyload_root, "#{path_component}.rb")
|
41
|
+
|
42
|
+
# Search for the file to include
|
43
|
+
$LOAD_PATH.each do |load_root|
|
44
|
+
full_load_path = File.join(load_root, easyload_path)
|
45
|
+
if File.exists? full_load_path
|
46
|
+
self.module_eval(File.read(full_load_path))
|
47
|
+
|
48
|
+
# Did we get our target constant?
|
49
|
+
if self.const_defined? sym
|
50
|
+
target_const = self.const_get(sym)
|
51
|
+
class << target_const
|
52
|
+
include SingletonExtensions
|
53
|
+
end
|
54
|
+
|
55
|
+
target_const.easyload_from(File.join(self.easyload_root, path_component))
|
56
|
+
|
57
|
+
return self.const_get(sym)
|
58
|
+
|
59
|
+
# Warn but still break the load process. We don't want to support ambiguous load
|
60
|
+
# paths.
|
61
|
+
else
|
62
|
+
$stderr.puts "Attempted to easyload #{sym} from '#{full_load_path}', but it doesn't appear to exist in that source file."
|
63
|
+
end
|
64
|
+
|
65
|
+
break
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
return super(sym)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/basic_spec.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'easyload'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
module Examples
|
7
|
+
include Easyload
|
8
|
+
end
|
9
|
+
|
10
|
+
module Deep
|
11
|
+
module Example
|
12
|
+
include Easyload
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module SpecRoot
|
17
|
+
class << self
|
18
|
+
attr_accessor :load_count
|
19
|
+
end
|
20
|
+
self.load_count = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'A basic easyload module' do
|
24
|
+
it 'should default to the class/module name for the easyload root' do
|
25
|
+
Examples.easyload_root.should == 'examples'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should be able to load constants one level deep' do
|
29
|
+
Examples::Node::ABOUT.should == 'A node?'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should raise standard NameErrors for unknown constants' do
|
33
|
+
expect {
|
34
|
+
Examples::Missing
|
35
|
+
}.to raise_error(NameError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should not load files twice' do
|
39
|
+
SpecRoot.load_count.should == 0
|
40
|
+
|
41
|
+
Examples::LoadCounter::ABOUT.should == 'load counter: 1'
|
42
|
+
SpecRoot.load_count.should == 1
|
43
|
+
|
44
|
+
Examples::LoadCounter::ABOUT.should == 'load counter: 1'
|
45
|
+
SpecRoot.load_count.should == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be able to load nested modules' do
|
49
|
+
Examples::Nested::Leaf::ABOUT.should == 'A leaf?'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should be able to automatically guess a nested module name' do
|
53
|
+
Deep::Example.easyload_root.should == 'deep/example'
|
54
|
+
thing = Deep::Example::Thing.new
|
55
|
+
end
|
56
|
+
end
|
data/spec/naming_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'easyload'
|
3
|
+
|
4
|
+
module NamingExamples
|
5
|
+
Easyload
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'Easyload file names' do
|
9
|
+
it 'should use a single lowercase name for a single word constant' do
|
10
|
+
Examples.easyload_path_component_for_sym(:Single).should == 'single'
|
11
|
+
Examples.easyload_path_component_for_sym(:A).should == 'a'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should break CamelCase constants into lowercase words separated by underscores' do
|
15
|
+
Examples.easyload_path_component_for_sym(:TwoWords).should == 'two_words'
|
16
|
+
Examples.easyload_path_component_for_sym(:OneTwoThreeFour).should == 'one_two_three_four'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should treat strings of adjacent capital letters as acronyms' do
|
20
|
+
Examples.easyload_path_component_for_sym(:ABC).should == 'abc'
|
21
|
+
Examples.easyload_path_component_for_sym(:WTFWord).should == 'wtf_word'
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: easyload
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Ian MacLeod
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-02-13 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 5
|
31
|
+
- 0
|
32
|
+
version: 2.5.0
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: An alternative to autoload that relies on your project's directory structure to determine its module hierarchy, recursively.
|
36
|
+
email:
|
37
|
+
- ian@nevir.net
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- Gemfile
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- easyload.gemspec
|
50
|
+
- lib/easyload.rb
|
51
|
+
- lib/easyload/singleton_extensions.rb
|
52
|
+
- lib/easyload/version.rb
|
53
|
+
- spec/basic_spec.rb
|
54
|
+
- spec/deep/example.rb
|
55
|
+
- spec/deep/example/thing.rb
|
56
|
+
- spec/examples/load_counter.rb
|
57
|
+
- spec/examples/nested.rb
|
58
|
+
- spec/examples/nested/leaf.rb
|
59
|
+
- spec/examples/node.rb
|
60
|
+
- spec/naming_spec.rb
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: ""
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project: easyload
|
89
|
+
rubygems_version: 1.3.7
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: A recursive and opinionated alternative to autoload.
|
93
|
+
test_files:
|
94
|
+
- spec/basic_spec.rb
|
95
|
+
- spec/deep/example.rb
|
96
|
+
- spec/deep/example/thing.rb
|
97
|
+
- spec/examples/load_counter.rb
|
98
|
+
- spec/examples/nested.rb
|
99
|
+
- spec/examples/nested/leaf.rb
|
100
|
+
- spec/examples/node.rb
|
101
|
+
- spec/naming_spec.rb
|