truck 0.8.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.
- checksums.yaml +7 -0
- data/.gitignore +26 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +81 -0
- data/Rakefile +14 -0
- data/lib/truck.rb +38 -0
- data/lib/truck/autoloader.rb +131 -0
- data/lib/truck/const_resolver.rb +91 -0
- data/lib/truck/context.rb +84 -0
- data/lib/truck/string_inflections.rb +30 -0
- data/lib/truck/version.rb +3 -0
- data/test/integration/autoloading_test.rb +39 -0
- data/test/support/fakes_filesystem.rb +182 -0
- data/test/support/tests_autoloading.rb +13 -0
- data/test/test_helper.rb +35 -0
- data/test/unit/autoloader_test.rb +68 -0
- data/test/unit/context_test.rb +86 -0
- data/truck.gemspec +26 -0
- metadata +140 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: c8475ef41616dc1d5e77921e3c1a4a1c2f49e4ee
|
|
4
|
+
data.tar.gz: 690aa4f1b04fcbb4df44fc03da487edf43c10171
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 24820f2e0c852256e6f66316070067444de7a27d7819265219840b55a898b911a2979a756b887d10125047db779d4f2224b41f04b7291b0b8762950d5007f11d
|
|
7
|
+
data.tar.gz: 3925f6a1600e036edb2cdbda4621dc6aef8a937b79d03bfe37bf5ae37510cafdb4223c7bda5d877d45a3427f2a1ea0dc1694a7ed53dd2efa062171f306f83792
|
data/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
|
18
|
+
*.bundle
|
|
19
|
+
*.so
|
|
20
|
+
*.o
|
|
21
|
+
*.a
|
|
22
|
+
mkmf.log
|
|
23
|
+
tags
|
|
24
|
+
.gems
|
|
25
|
+
.ruby-version
|
|
26
|
+
.rbenv-gemsets
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Nathan Ladd
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Truck
|
|
2
|
+
|
|
3
|
+
Truck is an alternative autoloader that doesn't pollute the global namespace. Specifically, it does not load constants into `Object`; rather, it loads them into *Contexts* that you define. This has two main advantages:
|
|
4
|
+
|
|
5
|
+
1. `reload!` is very fast; `Object.send(:remove_const, :MyContext)` does the trick
|
|
6
|
+
2. You can have multiple autoloaders running in parallel contexts
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
gem 'truck'
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
|
|
16
|
+
$ bundle
|
|
17
|
+
|
|
18
|
+
Or install it yourself as:
|
|
19
|
+
|
|
20
|
+
$ gem install truck
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Unlike ActiveSupport's autoloader, truck requires a bit of setup for each context. An example:
|
|
25
|
+
|
|
26
|
+
Truck.define_context :MyContext, root: "/path/to/context/root"
|
|
27
|
+
|
|
28
|
+
Also, after defining all your contexts, you'll want to boot everyone up:
|
|
29
|
+
|
|
30
|
+
Truck.boot!
|
|
31
|
+
|
|
32
|
+
You'll want to define all your contexts, then fork/spawn threads, then have every sub process invoke `Truck.boot!` separately.
|
|
33
|
+
|
|
34
|
+
There is no notion of autoload paths; if you want multiple autoload paths, you'd define multiple contexts. In this example, a top level module called `MyContext` would get defined. Suppose you had a class called `Foo` living in `/path/to/context/root/foo.rb`:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# /path/to/context/root/foo.rb
|
|
38
|
+
class Foo
|
|
39
|
+
def self.bar
|
|
40
|
+
Bar.hello_world
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# /path/to/context/root/bar.rb
|
|
47
|
+
class Foo
|
|
48
|
+
def self.hello_world
|
|
49
|
+
"hello, world!"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`Foo` can reference `Bar` without an explicit require. So how does the world outside of `MyContext` reference objects?
|
|
55
|
+
|
|
56
|
+
MyContext.resolve_const("Bar")
|
|
57
|
+
|
|
58
|
+
This works with namespaced constant names, too:
|
|
59
|
+
|
|
60
|
+
MyContext.resolve_const("Foo::Bar::Baz")
|
|
61
|
+
|
|
62
|
+
`MyContext` has some other interesting methods on it:
|
|
63
|
+
|
|
64
|
+
# Wipe the whole context and reload it (also aliased as reload!)
|
|
65
|
+
MyContext.reset!
|
|
66
|
+
|
|
67
|
+
# Kill the context
|
|
68
|
+
MyContext.shutdown!
|
|
69
|
+
|
|
70
|
+
# Eagerly load the entire context into memory (good for production)
|
|
71
|
+
MyContext.eager_load!
|
|
72
|
+
|
|
73
|
+
These methods are also of course on `Truck` as well, and invoke the same operations on every context.
|
|
74
|
+
|
|
75
|
+
## Contributing
|
|
76
|
+
|
|
77
|
+
1. Fork it ( https://github.com/ntl/truck/fork )
|
|
78
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
79
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
80
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
81
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rake/testtask"
|
|
3
|
+
|
|
4
|
+
Rake::TestTask.new do |t|
|
|
5
|
+
t.pattern = 'test/**/*_test.rb'
|
|
6
|
+
t.libs << 'test'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
desc "Update ctags"
|
|
10
|
+
task :ctags do
|
|
11
|
+
`ctags -R --languages=Ruby --totals -f tags`
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
task default: 'test'
|
data/lib/truck.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative "truck/version"
|
|
2
|
+
require_relative "truck/string_inflections"
|
|
3
|
+
require_relative "truck/autoloader"
|
|
4
|
+
require_relative "truck/context"
|
|
5
|
+
require_relative "truck/const_resolver"
|
|
6
|
+
|
|
7
|
+
module Truck
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
attr :contexts
|
|
11
|
+
@contexts = {}
|
|
12
|
+
|
|
13
|
+
def define_context(name, **params)
|
|
14
|
+
contexts[name] = Context.new(name, **params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def boot!
|
|
18
|
+
contexts.each_value(&:boot!)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reset!
|
|
22
|
+
each_booted_context &:reset!
|
|
23
|
+
end
|
|
24
|
+
alias_method :reload!, :reset!
|
|
25
|
+
|
|
26
|
+
def shutdown!
|
|
27
|
+
each_booted_context &:shutdown!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Error = Class.new StandardError
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def each_booted_context(&block)
|
|
35
|
+
return to_enum(:each_booted_context) unless block_given?
|
|
36
|
+
contexts.each_value.select(&:booted?).each(&block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module Truck
|
|
2
|
+
using Truck::StringInflections
|
|
3
|
+
|
|
4
|
+
class Autoloader
|
|
5
|
+
attr :base_nibbles, :context, :from, :dir_paths
|
|
6
|
+
|
|
7
|
+
def initialize(from)
|
|
8
|
+
@from = from
|
|
9
|
+
@context, @base_nibbles = fetch_context_and_base_nibbles
|
|
10
|
+
@dir_paths = [nil]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def <<(const_name)
|
|
14
|
+
raise_name_error!(const_name) unless context
|
|
15
|
+
@dir_paths = each_possible_const(const_name).reduce [] do |new_paths, possible_const|
|
|
16
|
+
resolved_const = context.resolve_const possible_const
|
|
17
|
+
throw :const, resolved_const if resolved_const
|
|
18
|
+
new_paths << possible_const if possible_namespace?(possible_const)
|
|
19
|
+
new_paths
|
|
20
|
+
end
|
|
21
|
+
raise_name_error!(const_name) if dir_paths.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def each_possible_const(const_name)
|
|
25
|
+
return to_enum(:each_possible_const, const_name) unless block_given?
|
|
26
|
+
dir_paths.each do |dir_path|
|
|
27
|
+
base_nibbles.map { |e| yield constify(e, *dir_path, const_name) }
|
|
28
|
+
yield constify(*dir_path, const_name)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def possible_namespace?(possible_const)
|
|
33
|
+
context.root.join(possible_const.to_snake_case).directory?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def constify(*nibbles)
|
|
37
|
+
nibbles.compact.join '::'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def raise_name_error!(const_name = dir_paths.last)
|
|
41
|
+
message = "uninitialized constant #{const_name}"
|
|
42
|
+
message << " (in #{context.mod})" if context
|
|
43
|
+
raise NameError, message
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# given "Foo::Bar::Baz", return ["Foo::Bar::Baz", "Foo::Bar", etc.]
|
|
47
|
+
def fetch_context_and_base_nibbles
|
|
48
|
+
each_base_nibble.to_a.reverse.reduce [] do |ary, (mod, const)|
|
|
49
|
+
owner = Truck.contexts.each_value.detect { |c| c.mod == mod }
|
|
50
|
+
return [owner, ary] if owner
|
|
51
|
+
ary.map! do |e| e.insert 0, '::'; e.insert 0, const; end
|
|
52
|
+
ary << const
|
|
53
|
+
end
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# given "Foo::Bar::Baz", return ["Foo", "Bar", "Baz"]
|
|
58
|
+
def each_base_nibble
|
|
59
|
+
return to_enum(:each_base_nibble) unless block_given?
|
|
60
|
+
from.name.split('::').reduce Object do |mod, const|
|
|
61
|
+
mod = mod.const_get const
|
|
62
|
+
yield [mod, const]
|
|
63
|
+
mod
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module ThreadedState
|
|
68
|
+
def autoloaders
|
|
69
|
+
@autoloaders ||= {}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def current_autoloader
|
|
73
|
+
autoloaders[current_thread_id]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def set_current_autoloader(to:)
|
|
77
|
+
autoloaders[current_thread_id] = to
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def current_thread_id
|
|
81
|
+
Thread.current.object_id
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
extend ThreadedState
|
|
85
|
+
|
|
86
|
+
module HandleConstMissing
|
|
87
|
+
def handle(*args)
|
|
88
|
+
found_const = catch :const do
|
|
89
|
+
handle! *args and return NullModule
|
|
90
|
+
end
|
|
91
|
+
throw :const, found_const
|
|
92
|
+
rescue NameError => name_error; raise name_error
|
|
93
|
+
ensure
|
|
94
|
+
set_current_autoloader(to: nil) if found_const or name_error
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def handle!(const_name, from:)
|
|
98
|
+
autoloader = current_autoloader || new(from)
|
|
99
|
+
autoloader << String(const_name)
|
|
100
|
+
set_current_autoloader to: autoloader
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
extend HandleConstMissing
|
|
104
|
+
|
|
105
|
+
module NullModule
|
|
106
|
+
extend self
|
|
107
|
+
|
|
108
|
+
def method_missing(*)
|
|
109
|
+
Autoloader.current_autoloader.raise_name_error!
|
|
110
|
+
ensure
|
|
111
|
+
Autoloader.set_current_autoloader to: nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class Module
|
|
118
|
+
def const_missing(const)
|
|
119
|
+
catch :const do
|
|
120
|
+
Truck::Autoloader.handle const, from: self
|
|
121
|
+
end
|
|
122
|
+
rescue NameError => name_error
|
|
123
|
+
if name_error.class == NameError
|
|
124
|
+
# Reraise the error to keep our junk out of the backtrace
|
|
125
|
+
raise NameError, name_error.message
|
|
126
|
+
else
|
|
127
|
+
# NoMethodError inherits from NameError
|
|
128
|
+
raise name_error
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Truck
|
|
2
|
+
using StringInflections
|
|
3
|
+
|
|
4
|
+
class Context
|
|
5
|
+
class ConstResolver
|
|
6
|
+
attr :current_path, :autoload_paths, :context, :expanded_const
|
|
7
|
+
|
|
8
|
+
def initialize(context:, expanded_const:, autoload_paths: [''])
|
|
9
|
+
@autoload_paths = autoload_paths
|
|
10
|
+
@context = context
|
|
11
|
+
@expanded_const = expanded_const
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def autoload_paths=(paths)
|
|
15
|
+
autoload_paths.clear
|
|
16
|
+
autoload_paths.concat paths
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def const_get
|
|
20
|
+
walk_const_parts.reduce context.mod do |mod, const_part|
|
|
21
|
+
return nil unless mod.const_defined?(const_part)
|
|
22
|
+
mod.const_get const_part
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def each_autoload_path
|
|
27
|
+
autoload_paths.each do |autoload_path|
|
|
28
|
+
@current_path = context.root.join autoload_path
|
|
29
|
+
yield
|
|
30
|
+
end
|
|
31
|
+
@current_path = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def each_possible_rb_file
|
|
35
|
+
each_autoload_path do
|
|
36
|
+
base_path = current_path.join expanded_const.to_snake_case
|
|
37
|
+
each_rb_file_from_base_path base_path do |rb_file|
|
|
38
|
+
yield rb_file if File.exist?(rb_file)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def each_rb_file_from_base_path(base_path)
|
|
44
|
+
base_path.ascend do |path|
|
|
45
|
+
return if path == context.root
|
|
46
|
+
rb_file = path.sub_ext '.rb'
|
|
47
|
+
yield rb_file if rb_file.file?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def resolve
|
|
52
|
+
check_already_loaded or resolve!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def resolve!
|
|
56
|
+
each_possible_rb_file do |rb_file|
|
|
57
|
+
context.load_file rb_file
|
|
58
|
+
check_loaded rb_file
|
|
59
|
+
end
|
|
60
|
+
const_get
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def walk_const_parts(const = expanded_const)
|
|
64
|
+
return to_enum(:walk_const_parts, const) if block_given?
|
|
65
|
+
const.split '::'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def check_already_loaded
|
|
69
|
+
walk_const_parts.reduce context.mod do |mod, const_part|
|
|
70
|
+
return nil unless mod.const_defined? const_part
|
|
71
|
+
mod.const_get const_part
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def check_loaded(rb_file)
|
|
76
|
+
expected_const = expected_const_defined_in_rb_file rb_file
|
|
77
|
+
walk_const_parts(expected_const).reduce context.mod do |mod, const_part|
|
|
78
|
+
mod.const_defined?(const_part) or
|
|
79
|
+
raise AutoloadError.new(const: expected_const, rb_file: rb_file)
|
|
80
|
+
mod.const_get const_part
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def expected_const_defined_in_rb_file(rb_file, autoload_path: current_path)
|
|
85
|
+
rel_path = rb_file.sub_ext('').relative_path_from(autoload_path).to_s
|
|
86
|
+
matcher = %r{\A(#{rel_path.to_camel_case})}i
|
|
87
|
+
expanded_const.match(matcher).captures.fetch 0
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module Truck
|
|
2
|
+
class Context
|
|
3
|
+
attr :mod, :name, :root
|
|
4
|
+
|
|
5
|
+
def initialize(name, parent: nil, root:)
|
|
6
|
+
@mod = build_mod
|
|
7
|
+
@name = name
|
|
8
|
+
@root = Pathname(root) if root
|
|
9
|
+
@parent = parent
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def owner(const)
|
|
14
|
+
owner, _ = Autoloader.owner_and_ascending_nibbles const
|
|
15
|
+
owner
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def boot!
|
|
20
|
+
parent.const_set name, mod
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def booted?
|
|
24
|
+
parent.const_defined? name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def eager_load!
|
|
28
|
+
Dir[root.join('**/*.rb')].each do |rb_file|
|
|
29
|
+
load_file rb_file
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def load_file(rb_file)
|
|
34
|
+
mod.module_eval File.read(rb_file), rb_file.to_s
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parent
|
|
38
|
+
return Object unless @parent
|
|
39
|
+
Truck.contexts.fetch(@parent.to_sym).mod
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def reset!
|
|
43
|
+
shutdown!
|
|
44
|
+
@mod = build_mod
|
|
45
|
+
boot!
|
|
46
|
+
end
|
|
47
|
+
alias_method :reload!, :reset!
|
|
48
|
+
|
|
49
|
+
def resolve_const(expanded_const)
|
|
50
|
+
build_const_resolver(expanded_const).resolve
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def shutdown!
|
|
54
|
+
parent.send(:remove_const, name)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def build_const_resolver(expanded_const)
|
|
60
|
+
ConstResolver.new(
|
|
61
|
+
context: self,
|
|
62
|
+
expanded_const: String(expanded_const).dup.freeze,
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def build_mod
|
|
67
|
+
Module.new
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class AutoloadError < NameError
|
|
73
|
+
attr :const, :rb_file
|
|
74
|
+
|
|
75
|
+
def initialize(const:, rb_file:)
|
|
76
|
+
@const = const
|
|
77
|
+
@rb_file = rb_file
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def message
|
|
81
|
+
"Expected #{rb_file} to define #{const}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Truck
|
|
2
|
+
module StringInflections
|
|
3
|
+
refine String do
|
|
4
|
+
def to_camel_case
|
|
5
|
+
str = "_#{self}"
|
|
6
|
+
str.gsub!(%r{_[a-z]}) { |snake| snake.slice(1).upcase }
|
|
7
|
+
str.gsub!('/', '::')
|
|
8
|
+
str
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_snake_case
|
|
12
|
+
str = gsub '::', '/'
|
|
13
|
+
# Convert FOOBar => FooBar
|
|
14
|
+
str.gsub! %r{[[:upper:]]{2,}} do |uppercase|
|
|
15
|
+
bit = uppercase[0]
|
|
16
|
+
bit << uppercase[1...-1].downcase
|
|
17
|
+
bit << uppercase[-1]
|
|
18
|
+
bit
|
|
19
|
+
end
|
|
20
|
+
str.gsub! %r{[[:lower:]][[:upper:]]+[[:lower:]]} do |camel|
|
|
21
|
+
bit = camel[0]
|
|
22
|
+
bit << '_'
|
|
23
|
+
bit << camel[1..-1].downcase
|
|
24
|
+
end
|
|
25
|
+
str.downcase!
|
|
26
|
+
str
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class AutoloadingTest < Minitest::Test
|
|
4
|
+
include FakesFilesystem
|
|
5
|
+
include TestsAutoloading
|
|
6
|
+
|
|
7
|
+
def test_simple_case
|
|
8
|
+
assert_equal 'hello from A::AA', Foo::A.references_aa
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_negative_case
|
|
12
|
+
assert_raises NameError do
|
|
13
|
+
Foo::A.references_abracadabra
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_deeply_nested_module_with_implicit_namespaces
|
|
18
|
+
assert_equal 'hello from C::CA::CAA::CAAA', Foo::A.references_caaa
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_reference_from_within_constant
|
|
22
|
+
assert_equal 'hello from A', Foo::D.references_a
|
|
23
|
+
assert_equal 'hello from B::BA', Foo::D.new.references_b_ba
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_invoking_method_inside_implicit_namespace_raises_error
|
|
27
|
+
exception = assert_raises NameError do
|
|
28
|
+
Foo::C.hello
|
|
29
|
+
end
|
|
30
|
+
assert_equal "uninitialized constant C (in Foo)", exception.message
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_raises_name_error_if_no_context_found
|
|
34
|
+
exception = assert_raises NameError do
|
|
35
|
+
Abracadabra
|
|
36
|
+
end
|
|
37
|
+
assert_equal "uninitialized constant Abracadabra", exception.message
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
module FakesFilesystem
|
|
2
|
+
def before_setup
|
|
3
|
+
super
|
|
4
|
+
FakeFS::FileSystem.clear
|
|
5
|
+
FakeFS.activate!
|
|
6
|
+
World.build!
|
|
7
|
+
monkeypatch_assert!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def after_teardown
|
|
11
|
+
unmonkeypatch_assert!
|
|
12
|
+
FakeFS.deactivate!
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def monkeypatch_assert!
|
|
19
|
+
Minitest::Assertions.module_eval do
|
|
20
|
+
alias_method :orig_diff, :diff
|
|
21
|
+
def diff(*args)
|
|
22
|
+
FakeFS.without { orig_diff *args }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def unmonkeypatch_assert!
|
|
28
|
+
Minitest::Assertions.module_eval do
|
|
29
|
+
undef_method :diff
|
|
30
|
+
alias_method :diff, :orig_diff
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module World
|
|
35
|
+
extend self
|
|
36
|
+
|
|
37
|
+
def build!
|
|
38
|
+
Dir.mkdir '/foo'
|
|
39
|
+
|
|
40
|
+
File.write "/foo/a.rb", <<-FILE
|
|
41
|
+
module A
|
|
42
|
+
def self.message
|
|
43
|
+
"hello from A"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.references_aa
|
|
47
|
+
AA.message
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.references_abracadabra
|
|
51
|
+
Abracadabra.message
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.references_caaa
|
|
55
|
+
C::CA::CAA::CAAA.message
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
module AB
|
|
59
|
+
def self.message
|
|
60
|
+
"hello from A::AB"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
module ABA
|
|
64
|
+
def self.message
|
|
65
|
+
"hello from A::AB::ABA"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.references_abb
|
|
69
|
+
ABB.message
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
FILE
|
|
75
|
+
|
|
76
|
+
Dir.mkdir "/foo/a"
|
|
77
|
+
File.write "/foo/a/aa.rb", <<-FILE
|
|
78
|
+
module A
|
|
79
|
+
module AA
|
|
80
|
+
def self.message
|
|
81
|
+
"hello from A::AA"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
FILE
|
|
86
|
+
|
|
87
|
+
Dir.mkdir "/foo/a/ab"
|
|
88
|
+
File.write "/foo/a/ab/abb.rb", <<-FILE
|
|
89
|
+
module A
|
|
90
|
+
module AB
|
|
91
|
+
module ABB
|
|
92
|
+
def self.message
|
|
93
|
+
"hello from A::AB::ABB"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
FILE
|
|
99
|
+
|
|
100
|
+
Dir.mkdir "/foo/b"
|
|
101
|
+
File.write "/foo/b/ba.rb", <<-FILE
|
|
102
|
+
module B
|
|
103
|
+
module BA
|
|
104
|
+
def self.message
|
|
105
|
+
"hello from B::BA"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
FILE
|
|
110
|
+
|
|
111
|
+
Dir.mkdir "/foo/c"
|
|
112
|
+
Dir.mkdir "/foo/c/ca"
|
|
113
|
+
Dir.mkdir "/foo/c/ca/caa"
|
|
114
|
+
File.write "/foo/c/ca/caa/caaa.rb", <<-FILE
|
|
115
|
+
module C
|
|
116
|
+
module CA
|
|
117
|
+
module CAA
|
|
118
|
+
module CAAA
|
|
119
|
+
def self.message
|
|
120
|
+
"hello from C::CA::CAA::CAAA"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
FILE
|
|
127
|
+
|
|
128
|
+
File.write "/foo/d.rb", <<-FILE
|
|
129
|
+
class D
|
|
130
|
+
def self.references_a
|
|
131
|
+
A.message
|
|
132
|
+
end
|
|
133
|
+
def references_b_ba
|
|
134
|
+
B::BA.message
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
FILE
|
|
138
|
+
|
|
139
|
+
Dir.mkdir "/bar"
|
|
140
|
+
File.write "/bar/a.rb", <<-FILE
|
|
141
|
+
module A
|
|
142
|
+
def self.message
|
|
143
|
+
"hello from Bar::A"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
FILE
|
|
147
|
+
|
|
148
|
+
File.write "/bar/b.rb", <<-FILE
|
|
149
|
+
module B
|
|
150
|
+
def self.message
|
|
151
|
+
"hello from Bar::B"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
FILE
|
|
155
|
+
|
|
156
|
+
Dir.mkdir "/my_app"
|
|
157
|
+
Dir.mkdir "/my_app/lib"
|
|
158
|
+
File.write "/my_app/lib/a.rb", <<-FILE
|
|
159
|
+
module A
|
|
160
|
+
def self.message
|
|
161
|
+
"hello from MyApp::A"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def self.references_b_ba
|
|
165
|
+
B::BA.message
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
FILE
|
|
169
|
+
|
|
170
|
+
Dir.mkdir "/my_app/lib/b"
|
|
171
|
+
File.write "/my_app/lib/b/ba.rb", <<-FILE
|
|
172
|
+
module B
|
|
173
|
+
module BA
|
|
174
|
+
def self.message
|
|
175
|
+
"hello from MyApp::B::BA"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
FILE
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module TestsAutoloading
|
|
2
|
+
def setup
|
|
3
|
+
@foo = Truck.define_context :Foo, root: "/foo"
|
|
4
|
+
@bar = Truck.define_context :Bar, root: '/bar', parent: :Foo
|
|
5
|
+
Truck.boot!
|
|
6
|
+
assert_nil Truck::Autoloader.current_autoloader
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def teardown
|
|
10
|
+
assert_nil Truck::Autoloader.current_autoloader,
|
|
11
|
+
"Each test in this file must clean up after itself"
|
|
12
|
+
end
|
|
13
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "bundler"
|
|
2
|
+
Bundler.setup
|
|
3
|
+
|
|
4
|
+
require "minitest/autorun"
|
|
5
|
+
require "minitest/reporters"
|
|
6
|
+
|
|
7
|
+
require "fakefs/safe"
|
|
8
|
+
require "ostruct"
|
|
9
|
+
require "pathname"
|
|
10
|
+
require "stringio"
|
|
11
|
+
|
|
12
|
+
Minitest::Reporters.use! Minitest::Reporters::DefaultReporter.new
|
|
13
|
+
|
|
14
|
+
require_relative "../lib/truck"
|
|
15
|
+
|
|
16
|
+
$LOAD_PATH << "test/support"
|
|
17
|
+
|
|
18
|
+
Minitest::Test.class_eval do
|
|
19
|
+
autoload :FakesFilesystem, "fakes_filesystem"
|
|
20
|
+
autoload :TestsAutoloading, "tests_autoloading"
|
|
21
|
+
|
|
22
|
+
def after_teardown
|
|
23
|
+
Truck.shutdown!
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Binding
|
|
28
|
+
def method_missing(sym, *)
|
|
29
|
+
return super unless sym == :pry
|
|
30
|
+
FakeFS.without do
|
|
31
|
+
require 'pry'
|
|
32
|
+
pry
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class AutoloaderTest < Minitest::Test
|
|
4
|
+
include FakesFilesystem
|
|
5
|
+
include TestsAutoloading
|
|
6
|
+
|
|
7
|
+
def test_throws_constant_when_found
|
|
8
|
+
const = assert_catches :const do
|
|
9
|
+
Truck::Autoloader.handle :A, from: Foo
|
|
10
|
+
end
|
|
11
|
+
assert_equal 'Foo::A', const.name
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_raises_error_when_const_not_found
|
|
15
|
+
exception = assert_raises NameError do
|
|
16
|
+
Truck::Autoloader.handle :Abracadabra, from: Foo
|
|
17
|
+
end
|
|
18
|
+
assert_equal 'uninitialized constant Abracadabra (in Foo)', exception.message
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_simple_implicit_namespace_case
|
|
22
|
+
Truck::Autoloader.handle :B, from: Foo
|
|
23
|
+
const = assert_catches :const do
|
|
24
|
+
Truck::Autoloader.handle :BA, from: Foo
|
|
25
|
+
end
|
|
26
|
+
assert_equal 'Foo::B::BA', const.name
|
|
27
|
+
assert_nil Truck::Autoloader.current_autoloader
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_cleanup_after_implicit_namespace
|
|
31
|
+
Truck::Autoloader.handle :B, from: Foo
|
|
32
|
+
assert_raises NameError do
|
|
33
|
+
Truck::Autoloader.handle :Abracadabra, from: Foo
|
|
34
|
+
end
|
|
35
|
+
assert_nil Truck::Autoloader.current_autoloader
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_deeply_nested_module_with_implicit_namespaces
|
|
39
|
+
%i(C CA CAA).each do |implicit_namespace|
|
|
40
|
+
Truck::Autoloader.handle implicit_namespace, from: Foo
|
|
41
|
+
end
|
|
42
|
+
const = assert_catches :const do
|
|
43
|
+
Truck::Autoloader.handle :CAAA, from: Foo
|
|
44
|
+
end
|
|
45
|
+
assert_equal 'Foo::C::CA::CAA::CAAA', const.name
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_shallowly_nested_module_with_implicit_namespaces
|
|
49
|
+
@foo.resolve_const 'A::AB::ABA'
|
|
50
|
+
|
|
51
|
+
const = assert_catches :const do
|
|
52
|
+
Truck::Autoloader.handle :ABB, from: Foo::A::AB::ABA
|
|
53
|
+
end
|
|
54
|
+
assert_equal 'Foo::A::AB::ABB', const.name
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def assert_catches(thrown)
|
|
60
|
+
val = catch thrown do
|
|
61
|
+
yield
|
|
62
|
+
:not_found
|
|
63
|
+
end
|
|
64
|
+
refute_equal :not_found, val, "Expected block to throw #{thrown.inspect}"
|
|
65
|
+
val
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ContextTest < Minitest::Test
|
|
4
|
+
include FakesFilesystem
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
@context = Truck.define_context :Foo, root: "/foo"
|
|
8
|
+
@context.boot!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_resolving_constant
|
|
12
|
+
mod = @context.resolve_const(:A)
|
|
13
|
+
assert_autoloaded_module 'A', mod
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_resolving_deep_constant
|
|
17
|
+
mod = @context.resolve_const('A::AA')
|
|
18
|
+
assert_autoloaded_module 'A::AA', mod
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_resolving_inner_constant
|
|
22
|
+
mod = @context.resolve_const('A::AB::ABA')
|
|
23
|
+
assert_autoloaded_module 'A::AB::ABA', mod
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_parent_namespace_defined_in_child
|
|
27
|
+
mod = @context.resolve_const('B::BA')
|
|
28
|
+
assert_autoloaded_module 'B::BA', mod
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_returns_nil_if_constant_isnt_defined
|
|
32
|
+
assert_nil @context.resolve_const(:Abracadabra)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_returns_constant_that_has_already_been_resolved
|
|
36
|
+
@context.resolve_const(:A)
|
|
37
|
+
File.write '/foo/a.rb', "raise 'should not load this file'"
|
|
38
|
+
@context.resolve_const(:A)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_raises_error_if_corresponding_file_did_not_define_constant
|
|
42
|
+
File.write "/foo/abracadabra.rb", ""
|
|
43
|
+
|
|
44
|
+
exception = assert_raises(Truck::AutoloadError) do
|
|
45
|
+
@context.resolve_const(:Abracadabra)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
expected_message = %r{Expected /foo/abracadabra\.rb to define Abracadabra}
|
|
49
|
+
assert_match expected_message, exception.message
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_nesting_context
|
|
53
|
+
nested_context = Truck.define_context :Bar, root: "/bar", parent: 'Foo'
|
|
54
|
+
nested_context.boot!
|
|
55
|
+
|
|
56
|
+
mod = nested_context.resolve_const('A')
|
|
57
|
+
assert_autoloaded_module 'Bar::A', mod
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_eager_load
|
|
61
|
+
refute Foo.const_defined?(:A)
|
|
62
|
+
@context.eager_load!
|
|
63
|
+
assert Foo.const_defined?(:A)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_reload_drops_constant_references
|
|
67
|
+
@context.eager_load!
|
|
68
|
+
assert Foo.const_defined?(:A)
|
|
69
|
+
@context.reload!
|
|
70
|
+
refute Foo.const_defined?(:A)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_shutdown_removes_const
|
|
74
|
+
assert @context.booted?
|
|
75
|
+
@context.shutdown!
|
|
76
|
+
refute @context.booted?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def assert_autoloaded_module(expected_name, mod)
|
|
82
|
+
assert_kind_of Module, mod
|
|
83
|
+
assert_equal "Foo::#{expected_name}", mod.name
|
|
84
|
+
assert_equal "hello from #{expected_name}", mod.message
|
|
85
|
+
end
|
|
86
|
+
end
|
data/truck.gemspec
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'truck/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "truck"
|
|
8
|
+
spec.version = Truck::VERSION
|
|
9
|
+
spec.authors = ["ntl"]
|
|
10
|
+
spec.email = ["nathanladd+github@gmail.com"]
|
|
11
|
+
spec.summary = %q{Truck is an alternative autoloader that doesn't pollute the global namespace. Specifically, it does not load constants into `Object`; rather, it loads them into *Contexts* that you define.}
|
|
12
|
+
spec.description = %q{Truck is an alternative autoloader that doesn't pollute the global namespace.}
|
|
13
|
+
spec.homepage = "https://github.com/ntl/truck"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
22
|
+
spec.add_development_dependency "fakefs", "~> 0.5"
|
|
23
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
|
24
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.0"
|
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: truck
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.8.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ntl
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-04-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.6'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.6'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: fakefs
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.5'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.5'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: minitest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '5.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '5.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: minitest-reporters
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '10.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '10.0'
|
|
83
|
+
description: Truck is an alternative autoloader that doesn't pollute the global namespace.
|
|
84
|
+
email:
|
|
85
|
+
- nathanladd+github@gmail.com
|
|
86
|
+
executables: []
|
|
87
|
+
extensions: []
|
|
88
|
+
extra_rdoc_files: []
|
|
89
|
+
files:
|
|
90
|
+
- ".gitignore"
|
|
91
|
+
- Gemfile
|
|
92
|
+
- LICENSE.txt
|
|
93
|
+
- README.md
|
|
94
|
+
- Rakefile
|
|
95
|
+
- lib/truck.rb
|
|
96
|
+
- lib/truck/autoloader.rb
|
|
97
|
+
- lib/truck/const_resolver.rb
|
|
98
|
+
- lib/truck/context.rb
|
|
99
|
+
- lib/truck/string_inflections.rb
|
|
100
|
+
- lib/truck/version.rb
|
|
101
|
+
- test/integration/autoloading_test.rb
|
|
102
|
+
- test/support/fakes_filesystem.rb
|
|
103
|
+
- test/support/tests_autoloading.rb
|
|
104
|
+
- test/test_helper.rb
|
|
105
|
+
- test/unit/autoloader_test.rb
|
|
106
|
+
- test/unit/context_test.rb
|
|
107
|
+
- truck.gemspec
|
|
108
|
+
homepage: https://github.com/ntl/truck
|
|
109
|
+
licenses:
|
|
110
|
+
- MIT
|
|
111
|
+
metadata: {}
|
|
112
|
+
post_install_message:
|
|
113
|
+
rdoc_options: []
|
|
114
|
+
require_paths:
|
|
115
|
+
- lib
|
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ">="
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
requirements: []
|
|
127
|
+
rubyforge_project:
|
|
128
|
+
rubygems_version: 2.2.2
|
|
129
|
+
signing_key:
|
|
130
|
+
specification_version: 4
|
|
131
|
+
summary: Truck is an alternative autoloader that doesn't pollute the global namespace.
|
|
132
|
+
Specifically, it does not load constants into `Object`; rather, it loads them into
|
|
133
|
+
*Contexts* that you define.
|
|
134
|
+
test_files:
|
|
135
|
+
- test/integration/autoloading_test.rb
|
|
136
|
+
- test/support/fakes_filesystem.rb
|
|
137
|
+
- test/support/tests_autoloading.rb
|
|
138
|
+
- test/test_helper.rb
|
|
139
|
+
- test/unit/autoloader_test.rb
|
|
140
|
+
- test/unit/context_test.rb
|