loadable 1.2.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/.ruby +51 -0
- data/.yardopts +6 -0
- data/COPYING.rdoc +31 -0
- data/HISTORY.rdoc +46 -0
- data/INFRACTIONS.rdoc +401 -0
- data/README.md +192 -0
- data/lib/loadable/class_require.rb +10 -0
- data/lib/loadable/core_ext/rubygems.rb +81 -0
- data/lib/loadable/domain.rb +64 -0
- data/lib/loadable/kernel.rb +35 -0
- data/lib/loadable/loaders/gem_loader.rb +98 -0
- data/lib/loadable/loaders/original_loader.rb +63 -0
- data/lib/loadable/loaders/roll_loader.rb +21 -0
- data/lib/loadable/loaders/ruby_loader.rb +81 -0
- data/lib/loadable/loaders/vendor_loader.rb +109 -0
- data/lib/loadable/mixin.rb +100 -0
- data/lib/loadable/system.rb +11 -0
- data/lib/loadable/version.rb +20 -0
- data/lib/loadable.rb +5 -0
- data/spec/fixture/abbrev.rb +3 -0
- data/spec/fixture/ansi.rb +3 -0
- data/spec/fixture/subpro/.ruby +5 -0
- data/spec/fixture/subpro/lib/subpro.rb +1 -0
- data/spec/gem_loader_spec.rb +49 -0
- data/spec/helper.rb +6 -0
- data/spec/ruby_loader_spec.rb +38 -0
- data/spec/vendor_loader_spec.rb +30 -0
- metadata +125 -0
data/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Loadable
|
|
2
|
+
|
|
3
|
+
## 1 Overview
|
|
4
|
+
|
|
5
|
+
| Project | Loadable |
|
|
6
|
+
|--------------|----------------------------------------------------------|
|
|
7
|
+
| Author | Thomas Sawyer |
|
|
8
|
+
| License | BSD-2-Clause |
|
|
9
|
+
| Copyright | (c) 2010 Thomas Sawyer |
|
|
10
|
+
| Website | http://github.com/rubyworks/loadable |
|
|
11
|
+
| Development | http://github.com/rubyworks/loadable |
|
|
12
|
+
| Mailing-List | http://groups.google.com/group/rubyworks-mailinglist |
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## 2 Description
|
|
16
|
+
|
|
17
|
+
The Loadable gem provides a more robust and convenient means of augmenting
|
|
18
|
+
Ruby's load system, namely the `load` and `require` methods. Rather than
|
|
19
|
+
alias and override these methods, Loadable keeps a list of *load wedges*
|
|
20
|
+
that control the routing of require and load calls.
|
|
21
|
+
|
|
22
|
+
In addition, the Loadable gem includes two pre-made load wedges that can be
|
|
23
|
+
used to prevent name clashes between Ruby's standard library and gem packages
|
|
24
|
+
(see INFRACTIONS.rdoc for more on this). There is also a load wedge for
|
|
25
|
+
for developers to make it trivial to make vendored sub-projects loadable.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## 3 Usage
|
|
29
|
+
|
|
30
|
+
### 3.1 Installation
|
|
31
|
+
|
|
32
|
+
Installing via RubyGems follows the usual pattern.
|
|
33
|
+
|
|
34
|
+
$ gem install loadable
|
|
35
|
+
|
|
36
|
+
To automatically load both the Gem and Ruby wedges, and the entire Loadable
|
|
37
|
+
system, add `loadable` to your RUBYOPT environment variable.
|
|
38
|
+
|
|
39
|
+
$ export RUBYOPT="-rloadable"
|
|
40
|
+
|
|
41
|
+
Place this in your shell's configuration file, such as `~/.bashrc`.
|
|
42
|
+
|
|
43
|
+
If you do not want the default setup you can `require 'loadable/system'` instead.
|
|
44
|
+
This will load in Loadable system, but only add an `OriginalLoader` to the
|
|
45
|
+
`$LOADERS` list, leaving off the Ruby and Gem loaders.
|
|
46
|
+
|
|
47
|
+
### 3.2 Custom Loaders
|
|
48
|
+
|
|
49
|
+
Loadable was written initially to provide the specific capability of loading
|
|
50
|
+
Ruby standard libraries without potential interference from libraries
|
|
51
|
+
installed via RubyGems (see INFRACTIONS.rdoc). The code ultimately evolved
|
|
52
|
+
into a more generic tool, useful for writing any kind of plug-in load
|
|
53
|
+
router.
|
|
54
|
+
|
|
55
|
+
The code for the Ruby wedge serves as a good example of writing a load wedge.
|
|
56
|
+
(Note this is leaves out a few details of the real class for simplicity sake.)
|
|
57
|
+
|
|
58
|
+
require 'rbconfig'
|
|
59
|
+
require 'loadable/mixin'
|
|
60
|
+
|
|
61
|
+
class Loadable::RubyLoader
|
|
62
|
+
include Loadable
|
|
63
|
+
|
|
64
|
+
LOCATIONS = ::RbConfig::CONFIG.values_at(
|
|
65
|
+
'rubylibdir', 'archdir', 'sitelibdir', 'sitearchdir'
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def call(fname, options={})
|
|
69
|
+
return unless options[:from].to_s == 'ruby'
|
|
70
|
+
LOCATIONS.each do |loadpath|
|
|
71
|
+
if path = lookup(loadpath, fname, options)
|
|
72
|
+
return super(path, options)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
raise_load_error(fname)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def each(options={}, &block)
|
|
79
|
+
LOCATIONS.each do |loadpath|
|
|
80
|
+
traverse(loadpath, &block)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
To put this loader into action we simply need to register it with the Loadable
|
|
86
|
+
domain.
|
|
87
|
+
|
|
88
|
+
Loadable.register(Loadable::RubyLoader.new)
|
|
89
|
+
|
|
90
|
+
Under the hood, this simply appends the instance to the `$LOADERS` global variable.
|
|
91
|
+
|
|
92
|
+
Loaders, also called load wedges, are easy to write as their interface is very
|
|
93
|
+
simple. Any object the responds to #call, taking parameters of
|
|
94
|
+
<code>(fname, options={})</code>, can be used as a load wedge. A load wedge
|
|
95
|
+
should also support `#each(options={}, &block)` which is used to iterate over
|
|
96
|
+
all requirable files a loader supports.
|
|
97
|
+
|
|
98
|
+
The `Loadable` mixin is just a convenience module that makes writing loaders
|
|
99
|
+
a bit easier. Load wedges can be written without it, however the mixin
|
|
100
|
+
provides a few methods that are often useful to any load wedge. An example is
|
|
101
|
+
the `lookup` method used in the above example, which will search a
|
|
102
|
+
load path in accordance with the Ruby's built-in require and load lookup
|
|
103
|
+
procedures, i.e. automatically trying defualt extensions like `.rb`.
|
|
104
|
+
|
|
105
|
+
You might wonder how the single method, `#call`, handles both load and require
|
|
106
|
+
operations. The secret is in the `options` hash. If <code>options[:load]</code>
|
|
107
|
+
resolves to true, then it is a *load* operation, otherwise it is a *require*
|
|
108
|
+
operation. The `$LOADERS` global variable is iterated over in order.
|
|
109
|
+
When `#load` or `#require` is called each wedge is tried in turn. The return
|
|
110
|
+
value of `#call` controls how this loop proceeds. If the return value is `true`
|
|
111
|
+
then the load was successful, and the loop can break. If it is `false` it means
|
|
112
|
+
the loading has already been handled and the loop can also break. But if the
|
|
113
|
+
return value is `nil`, it means the wedge does not apply and the loop should
|
|
114
|
+
continue. If all wedges have been tried and all have returned `nil` then it
|
|
115
|
+
falls back to the original `#load` and `#require` calls, via an instance
|
|
116
|
+
`OriginalLoader` which should always be the last loader in the `$LOADERS` list.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## 4 Built-in Loaders
|
|
120
|
+
|
|
121
|
+
The Loadable gem provides three special loaders out-of-the-box, the `RubyLoader`,
|
|
122
|
+
the `GemLoader` and the `VendorLoader`. The first two are probably not exaclty
|
|
123
|
+
what you think they are, going just by their names, so keep reading...
|
|
124
|
+
|
|
125
|
+
### 4.1 RubyLoader
|
|
126
|
+
|
|
127
|
+
The Ruby wedge makes it possible to load a Ruby standard library without
|
|
128
|
+
interference from installed gems or other package systems. It does this by
|
|
129
|
+
checking for a `:from` option passed to the require or load methods.
|
|
130
|
+
|
|
131
|
+
require 'ostruct', :from=>'ruby'
|
|
132
|
+
|
|
133
|
+
This will load the `ostruct.rb` script from the Ruby standard library regardless
|
|
134
|
+
of whether someone else dropped an `ostruct.rb` file in their project's `lib/`
|
|
135
|
+
directory without understanding the potential consequences.
|
|
136
|
+
|
|
137
|
+
### 4.2 GemLoader
|
|
138
|
+
|
|
139
|
+
The Gem wedge is similar to the Ruby wedge, in that it isolates the loading
|
|
140
|
+
of a gem's files from other gems.
|
|
141
|
+
|
|
142
|
+
gem 'facets', '~>2.8'
|
|
143
|
+
|
|
144
|
+
require 'string/margin', :from=>'facets'
|
|
145
|
+
|
|
146
|
+
With this we can be sure that 'facets/string/margin' was loaded from the Facets
|
|
147
|
+
library regardless of whether some other gem has a 'facets/string/margin' file
|
|
148
|
+
in its `lib/` directory. If no gem has this file, it will fallback to the
|
|
149
|
+
remaining loaders. However, if we use the `:gem` options instead, it will
|
|
150
|
+
raise a load error.
|
|
151
|
+
|
|
152
|
+
require 'string/does_not_exit', :gem=>'facets'
|
|
153
|
+
|
|
154
|
+
The Gem wedge also supports version constraints, so you do not have to use
|
|
155
|
+
`gem()` method for one off requires from a given gem.
|
|
156
|
+
|
|
157
|
+
require 'string/margin', :from=>'facets', :version=>'~>2.8'
|
|
158
|
+
|
|
159
|
+
### 4.3 VendorLoader
|
|
160
|
+
|
|
161
|
+
The Vendor wedge is used to add vendored projects to the load system.
|
|
162
|
+
This is especially useful for development. Vendored projects can be added
|
|
163
|
+
in two ways, by registering an instance of VendorLoader, e.g.
|
|
164
|
+
|
|
165
|
+
Loadable.register Loadable::VendorLoader.new('vendor/*')
|
|
166
|
+
|
|
167
|
+
Or using the dedicated `Loadable.vendor(*dir)` method that Loadable provides
|
|
168
|
+
to make this more convenient.
|
|
169
|
+
|
|
170
|
+
Loadable.vendor('vendor/*')
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
## 5 Development
|
|
174
|
+
|
|
175
|
+
Source code for Loadbable is hosted by [GitHub](http://github.com/rubyworks/loadable).
|
|
176
|
+
|
|
177
|
+
If you has come across and issues, we encourage you to fork the repository and
|
|
178
|
+
submit a pull request with the fix. When submitting a pull request, it is best
|
|
179
|
+
if the changes are orgnanized into a new topic branch.
|
|
180
|
+
|
|
181
|
+
If you don't have time to code up patches yourself, please do not hesitate to
|
|
182
|
+
simply report the issue on the [issue tracker](http://github.com/rubyworks/loadable/issues).
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
## 6 Copyrights
|
|
186
|
+
|
|
187
|
+
Copyright (c) 2010 Thomas Sawyer, Rubyworks
|
|
188
|
+
|
|
189
|
+
Load is distributed under the terms of the **FreeBSD** license.
|
|
190
|
+
|
|
191
|
+
See COPYING.rdoc file for details.
|
|
192
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Gem
|
|
2
|
+
|
|
3
|
+
# TODO: Below are two implementations of the same feature. Currently
|
|
4
|
+
# we are using `Gem.search` code which calls `Gem::Specification.current_specs`.
|
|
5
|
+
# Possibly this could be replaced by simply calling `GemPathSearcher.current_files`.
|
|
6
|
+
# Hoverver, it is unclear to me at this time which is the best course of action.
|
|
7
|
+
|
|
8
|
+
# Search RubyGems for matching paths in current gem versions.
|
|
9
|
+
def self.search(fname, options={})
|
|
10
|
+
return unless defined?(::Gem)
|
|
11
|
+
matches = []
|
|
12
|
+
Gem::Specification.current_specs.each do |spec|
|
|
13
|
+
glob = File.join(spec.lib_dirs_glob, match)
|
|
14
|
+
list = Dir[glob] #.map{ |f| f.untaint }
|
|
15
|
+
list = list.map{ |d| d.chomp('/') }
|
|
16
|
+
matches.concat(list)
|
|
17
|
+
end
|
|
18
|
+
matches
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Specification
|
|
22
|
+
# Return a list of actives specs, or latest version if not active.
|
|
23
|
+
def self.current_specs
|
|
24
|
+
named = Hash.new{|h,k| h[k] = [] }
|
|
25
|
+
each{ |spec| named[spec.name] << spec }
|
|
26
|
+
list = []
|
|
27
|
+
named.each do |name, vers|
|
|
28
|
+
if spec = vers.find{ |s| s.activated? }
|
|
29
|
+
list << spec
|
|
30
|
+
else
|
|
31
|
+
spec = vers.max{ |a,b| a.version <=> b.version }
|
|
32
|
+
list << spec
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
return list
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Return full path of requireable file path given relative path.
|
|
39
|
+
def find_requirable_file(file)
|
|
40
|
+
root = full_gem_path
|
|
41
|
+
|
|
42
|
+
require_paths.each do |lib|
|
|
43
|
+
base = "#{root}/#{lib}/#{file}"
|
|
44
|
+
Gem.suffixes.each do |suf|
|
|
45
|
+
path = "#{base}#{suf}"
|
|
46
|
+
return path if File.file? path
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
=begin
|
|
55
|
+
class GemPathSearcher
|
|
56
|
+
# Return a list of matching files among active or latest gems.
|
|
57
|
+
def current_files(glob)
|
|
58
|
+
matches = {}
|
|
59
|
+
gemspecs = init_gemspecs
|
|
60
|
+
|
|
61
|
+
# loaded specs
|
|
62
|
+
gemspecs.each do |spec|
|
|
63
|
+
next unless spec.loaded?
|
|
64
|
+
next if matches.key?(spec.name)
|
|
65
|
+
files = matching_files(spec, glob)
|
|
66
|
+
matches[spec.name] = files
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# latest specs
|
|
70
|
+
gemspecs.each do |spec|
|
|
71
|
+
next if matches.key?(spec.name)
|
|
72
|
+
files = matching_files(spec, glob)
|
|
73
|
+
matches[spec.name] = files
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
matches.values.flatten.uniq
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
=end
|
|
80
|
+
|
|
81
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Loadable
|
|
2
|
+
|
|
3
|
+
# Stores active loaders.
|
|
4
|
+
$LOADERS = []
|
|
5
|
+
|
|
6
|
+
# Require/load script.
|
|
7
|
+
#
|
|
8
|
+
# @param [String] fname
|
|
9
|
+
# The script to require/load.
|
|
10
|
+
#
|
|
11
|
+
def self.call(fname, options={})
|
|
12
|
+
success = nil
|
|
13
|
+
$LOADERS.each do |wedge|
|
|
14
|
+
success = wedge.call(fname, options)
|
|
15
|
+
break unless success.nil?
|
|
16
|
+
end
|
|
17
|
+
return success
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Iterate over all requirable files.
|
|
21
|
+
#
|
|
22
|
+
# LoadSystem.each{ |file| p file }
|
|
23
|
+
#
|
|
24
|
+
# Note that esoteric load wedges may return a symbolic path rather than
|
|
25
|
+
# an actual file path.
|
|
26
|
+
#
|
|
27
|
+
def self.each(options={}, &block)
|
|
28
|
+
$LOADERS.each do |wedge|
|
|
29
|
+
wedge.each(options, &block)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Search wedges for all matching paths.
|
|
34
|
+
#
|
|
35
|
+
# LoadSystem.search('detroit-*.rb')
|
|
36
|
+
#
|
|
37
|
+
# Note that "esoteric" wedges might return a symbolic identifier rather
|
|
38
|
+
# than an actual file path.
|
|
39
|
+
#
|
|
40
|
+
# TODO: Handle default ruby extensions in search.
|
|
41
|
+
def self.search(glob, options={}, &criteria)
|
|
42
|
+
matches = []
|
|
43
|
+
$LOADERS.each do |wedge|
|
|
44
|
+
wedge.each do |path|
|
|
45
|
+
next unless criteria.call(path) if criteria
|
|
46
|
+
matches << path if File.fnmatch?(glob, path.to_s) # TODO: add `options[:flags].to_i` ?
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
matches
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Vendor location.
|
|
53
|
+
#
|
|
54
|
+
def self.vendor(*directory)
|
|
55
|
+
$LOADERS.unshift(VendorLoader.new(*directory))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Add a loader to the $LOADERS global variable.
|
|
59
|
+
#
|
|
60
|
+
def self.register(loader)
|
|
61
|
+
$LOADERS.unshift(loader)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Kernel
|
|
2
|
+
|
|
3
|
+
# Aliases for original load and require.
|
|
4
|
+
alias_method :load0, :load
|
|
5
|
+
alias_method :require0, :require
|
|
6
|
+
|
|
7
|
+
# TODO: I am not certain `:load` is the best name for this option.
|
|
8
|
+
# Something like `:eval` is more meaningful. OTOH, we could flip
|
|
9
|
+
# it about and use `:feature` or `:cache` to mean the opposite.
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
def require(fname, options={})
|
|
13
|
+
options[:load] = false unless options.key?(:load)
|
|
14
|
+
Loadable.call(fname, options)
|
|
15
|
+
#success = LoadSystem.require(fname, options)
|
|
16
|
+
#if success.nil?
|
|
17
|
+
# success = require_without_wedge(fname)
|
|
18
|
+
#end
|
|
19
|
+
#success
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
def load(fname, options={})
|
|
24
|
+
options = Hash===options ? options : {:wrap=>options}
|
|
25
|
+
options[:load] = true unless options.key?(:load)
|
|
26
|
+
Loadable.call(fname, options)
|
|
27
|
+
#success = LoadSystem.load(fname, options)
|
|
28
|
+
#if success.nil?
|
|
29
|
+
# success = load_without_wedge(fname)
|
|
30
|
+
#end
|
|
31
|
+
#success
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# TODO: Support colon notation again ?
|
|
2
|
+
|
|
3
|
+
# TODO: If :gem AND :from options are given, perhaps try both
|
|
4
|
+
# instead of either-or?
|
|
5
|
+
|
|
6
|
+
require 'loadable/mixin'
|
|
7
|
+
require 'loadable/core_ext/rubygems'
|
|
8
|
+
|
|
9
|
+
module Loadable
|
|
10
|
+
|
|
11
|
+
# The Gem Wedge allows gem files to be loaded in a isolated fashion.
|
|
12
|
+
#
|
|
13
|
+
# require 'tracepoint', :from=>'tracepoint'
|
|
14
|
+
#
|
|
15
|
+
# The example would load the tracepoint file from the tracepoint gem.
|
|
16
|
+
# It will also fallback to the RubyWedge if 'tracepoint' is not found
|
|
17
|
+
# among available gems. Loading can be limited to gems only by using
|
|
18
|
+
# the `:gem` options instead.
|
|
19
|
+
#
|
|
20
|
+
# require 'tracepoint', :gem=>'tracepoint'
|
|
21
|
+
#
|
|
22
|
+
# Now, if the `tracepoint` script is not found among availabe gems,
|
|
23
|
+
# a LoadError will be raised.
|
|
24
|
+
#
|
|
25
|
+
class GemLoader
|
|
26
|
+
|
|
27
|
+
include Loadable
|
|
28
|
+
|
|
29
|
+
# Load script from specific gem.
|
|
30
|
+
#
|
|
31
|
+
# Returns +nil+ if this loader is not applicable, which is determined
|
|
32
|
+
# by the use of `:gem => 'foo'` or `:from => 'foo'` options.
|
|
33
|
+
#
|
|
34
|
+
def call(fname, options={})
|
|
35
|
+
return unless apply?(fname, options)
|
|
36
|
+
|
|
37
|
+
gem_name = options[:gem] || options[:from]
|
|
38
|
+
|
|
39
|
+
if vers = options[:version]
|
|
40
|
+
spec = ::Gem::Specification.find_by_name(gem_name, vers)
|
|
41
|
+
else
|
|
42
|
+
spec = ::Gem::Specification.find_by_name(gem_name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if options[:gem]
|
|
46
|
+
raise_load_error(fname) unless spec
|
|
47
|
+
else
|
|
48
|
+
return unless spec
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
file = spec.find_requirable_file(fname)
|
|
52
|
+
file = spec.find_requirable_file(File.join(gem_name, fname)) unless file
|
|
53
|
+
|
|
54
|
+
if file
|
|
55
|
+
super(file, options)
|
|
56
|
+
else
|
|
57
|
+
raise_load_error(fname)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Iterate over each loadable file in specified gem.
|
|
62
|
+
#
|
|
63
|
+
def each(options={}, &block)
|
|
64
|
+
return unless apply?(nil, options)
|
|
65
|
+
|
|
66
|
+
gem_name = (options[:gem] || options[:from]).to_s
|
|
67
|
+
|
|
68
|
+
if vers = options[:version]
|
|
69
|
+
spec = ::Gem::Specification.find_by_name(gem_name, vers)
|
|
70
|
+
else
|
|
71
|
+
spec = ::Gem::Specification.find_by_name(gem_name)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return unless spec
|
|
75
|
+
|
|
76
|
+
#spec.activate
|
|
77
|
+
|
|
78
|
+
spec.require_paths.each do |path|
|
|
79
|
+
traverse(File.join(spec.full_gem_path, path), &block)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Determine if this load4 wedge is applicable given the +fname+
|
|
84
|
+
# and +options+.
|
|
85
|
+
#
|
|
86
|
+
def apply?(fname, options={})
|
|
87
|
+
return true if options[:gem]
|
|
88
|
+
return true if options[:from] && (
|
|
89
|
+
::Gem::Specification.find{ |s| options[:from].to_s == s.name }
|
|
90
|
+
)
|
|
91
|
+
return false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Copyright 2010 Thomas Sawyer, Rubyworks (BSD-2-Clause license)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'loadable/mixin'
|
|
2
|
+
require 'loadable/core_ext/rubygems'
|
|
3
|
+
|
|
4
|
+
module Loadable
|
|
5
|
+
|
|
6
|
+
# The original loader is simply an encpsulation of the Ruby's
|
|
7
|
+
# built-in #require and #load functionality. This load wedge is
|
|
8
|
+
# thus the first placed of the $LOADER list and the final
|
|
9
|
+
# fallback if no other loader succeeds.
|
|
10
|
+
#
|
|
11
|
+
class OriginalLoader
|
|
12
|
+
|
|
13
|
+
include Loadable
|
|
14
|
+
|
|
15
|
+
alias_method :original_load, :load
|
|
16
|
+
alias_method :original_require, :require
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
def call(fname, options={})
|
|
20
|
+
if options[:load]
|
|
21
|
+
original_load(fname, options[:wrap])
|
|
22
|
+
else
|
|
23
|
+
original_require(fname)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Iterate over each requirable file. Since RubyGems has become
|
|
28
|
+
# a standard part of Ruby as of 1.9, this includes active and latest
|
|
29
|
+
# versions of inactive gems.
|
|
30
|
+
#
|
|
31
|
+
# NOTE: in older versions of RubyGems, activated gem versions are in
|
|
32
|
+
# the $LOAD_PATH too. This may cause some duplicate iterations.
|
|
33
|
+
#
|
|
34
|
+
def each(options={}, &block)
|
|
35
|
+
each_loadpath(&block)
|
|
36
|
+
each_rubygems(&block)
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
def each_loadpath(options={}, &block)
|
|
44
|
+
$LOAD_PATH.uniq.each do |path|
|
|
45
|
+
path = File.expand_path(path)
|
|
46
|
+
traverse(path, &block)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
def each_rubygems(options={}, &block)
|
|
52
|
+
return unless defined?(::Gem)
|
|
53
|
+
Gem::Specification.current_specs.each do |spec|
|
|
54
|
+
spec.require_paths.each do |require_path|
|
|
55
|
+
path = File.join(spec.full_gem_path, require_path)
|
|
56
|
+
traverse(path, &block)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# TODO: Maybe add support more refined selection of locations.
|
|
2
|
+
|
|
3
|
+
# TODO: Add new vendor locations.
|
|
4
|
+
|
|
5
|
+
require 'rbconfig'
|
|
6
|
+
require 'loadable/mixin'
|
|
7
|
+
|
|
8
|
+
module Loadable
|
|
9
|
+
|
|
10
|
+
# The Ruby Wedge allows standaard libray scripts to be loaded in a isolated
|
|
11
|
+
# fashion.
|
|
12
|
+
#
|
|
13
|
+
# require 'optparse', :from=>'ruby'
|
|
14
|
+
#
|
|
15
|
+
# The example would load optparse standard library regardless of Gem installed
|
|
16
|
+
# that might have a script by the same name.
|
|
17
|
+
|
|
18
|
+
class RubyLoader
|
|
19
|
+
|
|
20
|
+
include Loadable
|
|
21
|
+
|
|
22
|
+
# Notice that rubylibdir takes precendence.
|
|
23
|
+
LOCATIONS = ::RbConfig::CONFIG.values_at(
|
|
24
|
+
'rubylibdir', 'archdir', 'sitelibdir', 'sitearchdir'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Load the the first standard Ruby library matching +file+.
|
|
28
|
+
#
|
|
29
|
+
# Returns +nil+ if this loader is not applicable, which is
|
|
30
|
+
# determined by the use of `:from => 'ruby'` option.
|
|
31
|
+
#
|
|
32
|
+
def call(file, options={})
|
|
33
|
+
return unless apply?(file, options)
|
|
34
|
+
|
|
35
|
+
LOCATIONS.each do |site|
|
|
36
|
+
if path = lookup(site, file, options)
|
|
37
|
+
return super(path, options)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
raise_load_error(file)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
def each(options={}, &block)
|
|
46
|
+
LOCATIONS.each do |path|
|
|
47
|
+
#path = File.expand_path(path)
|
|
48
|
+
traverse(path, &block)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The `#apply?` methods determines if the load wedge is applicable.
|
|
53
|
+
#
|
|
54
|
+
# Returns +true+ if this loader is not applicable, which is
|
|
55
|
+
# determined by the use of `:from => 'ruby'` option, otherwise `false`.
|
|
56
|
+
#
|
|
57
|
+
def apply?(fname, options={})
|
|
58
|
+
options[:from].to_s == 'ruby'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Retun first matching file from Ruby's standard library locations.
|
|
65
|
+
#
|
|
66
|
+
# Returns +nil+ if this loader is not applicable.
|
|
67
|
+
#
|
|
68
|
+
def find(glob, options={})
|
|
69
|
+
return unless apply?(file, options)
|
|
70
|
+
|
|
71
|
+
LOCATIONS.each do |site|
|
|
72
|
+
path = lookup(site, file, options)
|
|
73
|
+
return path if path
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Copyright 2010 Thomas Sawyer, Rubyworks (BSD-2-Clause license)
|