easyload 0.1.0

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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ source "http://rubygems.org"
3
+
4
+ # Specify your gem's dependencies in Easyload.gemspec
5
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'bundler'
3
+ require 'rspec/core/rake_task'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+ RSpec::Core::RakeTask.new
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
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Easyload
3
+ VERSION = '0.1.0'
4
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Example
3
+
4
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ class Thing
3
+
4
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ ::SpecRoot.load_count += 1
4
+
5
+ module LoadCounter
6
+ ABOUT = "load counter: #{::SpecRoot.load_count}"
7
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ class Nested
3
+
4
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ class Leaf
3
+ ABOUT = 'A leaf?'
4
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ class Node
3
+ ABOUT = 'A node?'
4
+ end
@@ -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