loadable 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,109 @@
|
|
1
|
+
# TODO: Support `:from` option.
|
2
|
+
|
3
|
+
require 'loadable/mixin'
|
4
|
+
|
5
|
+
module Loadable
|
6
|
+
|
7
|
+
# Add vendored projects to load path. For example:
|
8
|
+
#
|
9
|
+
# Loadable.vendor(project_root_directoy, 'vendor')
|
10
|
+
#
|
11
|
+
# Then any projects in the vendor directory will be accessible
|
12
|
+
# via require and load. This method looks for a .gemspec or .ruby
|
13
|
+
# file in the project to determine it's proper load paths, baring
|
14
|
+
# either of these it falls back to using `lib/`.
|
15
|
+
|
16
|
+
class VendorLoader
|
17
|
+
|
18
|
+
include Loadable
|
19
|
+
|
20
|
+
#
|
21
|
+
def initialize(*directory)
|
22
|
+
raise ArgumentError if directory.empty?
|
23
|
+
|
24
|
+
@_yaml_loaded ||= !(require 'yaml').nil?
|
25
|
+
|
26
|
+
settings = (Hash === directory.last ? directory.pop : {})
|
27
|
+
directory = File.expand_path(File.join(*directory))
|
28
|
+
|
29
|
+
@load_path = []
|
30
|
+
|
31
|
+
if settings[:direct]
|
32
|
+
@load_path.concat(Dir.glob(directory))
|
33
|
+
else
|
34
|
+
Dir.glob(directory).each do |dir|
|
35
|
+
next unless File.directory?(dir)
|
36
|
+
build_loadpath(dir)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if @load_path.empty?
|
41
|
+
# TODO: if load_path empty should we fallback to direct path ?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Load script from vendored locations.
|
46
|
+
#
|
47
|
+
def call(fname, options={})
|
48
|
+
@load_path.each do |path|
|
49
|
+
file = lookup(path, fname, options)
|
50
|
+
return super(file, options) if file
|
51
|
+
end
|
52
|
+
raise_load_error(fname)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Iterate over each loadable file in vendored locations.
|
56
|
+
#
|
57
|
+
def each(options={}, &block)
|
58
|
+
@load_path.uniq.each do |path|
|
59
|
+
path = File.expand_path(path)
|
60
|
+
traverse(path, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Build up load_path given a directory to check. Looks for a `.ruby`
|
67
|
+
# or `.gemspec` file to get project's local load paths, otherwise if
|
68
|
+
# looks for a `lib` directory.
|
69
|
+
#
|
70
|
+
def build_loadpath(dir)
|
71
|
+
if dotruby_file = Dir.glob(File.join(dir, '.ruby')).first
|
72
|
+
@load_path.concat(loadpath_dotruby(dotruby_file, dir))
|
73
|
+
|
74
|
+
elsif defined?(::Gem) && gemspec = Dir.glob(File.join(dir, '{*,}.gemspec')).first
|
75
|
+
@load_path.concat(loadpath_gemspec(gemspec_file, dir))
|
76
|
+
|
77
|
+
elsif path = Dir.glob(File.join(dir, 'lib')).first
|
78
|
+
@load_path << path
|
79
|
+
|
80
|
+
else
|
81
|
+
# TODO: is this an error, just ignore, or add dir directly ?
|
82
|
+
end
|
83
|
+
|
84
|
+
return @load_path
|
85
|
+
end
|
86
|
+
|
87
|
+
# Build the load_path given a '.ruby` file.
|
88
|
+
#
|
89
|
+
def loadpath_dotruby(dotruby_file, dir)
|
90
|
+
data = YAML.load(dotruby_file)
|
91
|
+
path = data['load_path'] || ['lib']
|
92
|
+
path.map do |lp|
|
93
|
+
File.join(dir, lp)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Build the load_path given a '.gemspec` file.
|
98
|
+
#
|
99
|
+
def loadpath_gemspec(gemspec_file, dir)
|
100
|
+
# TODO: handle YAML gemspecs
|
101
|
+
spec = Gem::Specification.load(gemspec_file)
|
102
|
+
spec.require_paths.map do |rp|
|
103
|
+
File.join(dir, rp)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Loadable is used in two ways.
|
2
|
+
|
3
|
+
# First, it can be used as a mixin for loaders (also called load wedges). Loaders
|
4
|
+
# are used to safely inject new import logic into Ruby's require/load system.
|
5
|
+
#
|
6
|
+
# Secondly, Loadable's class methods are used to override Ruby's built-in
|
7
|
+
# require and load Kernel methods.
|
8
|
+
#
|
9
|
+
# Active load wedges are stored in `$LOADERS` global variable.
|
10
|
+
#
|
11
|
+
# IMPORTANT: This must be loaded before `loadable/kernel.rb`.
|
12
|
+
|
13
|
+
module Loadable
|
14
|
+
|
15
|
+
# TODO: Some extensions are platform specific --only add the ones needed
|
16
|
+
# for the current platform.
|
17
|
+
|
18
|
+
# Script extensions recognized by Ruby (MRI and variants).
|
19
|
+
RB_EXTS = ['.rb', '.rbw', '.so', '.bundle', '.dll', '.sl', '.jar'] #, '']
|
20
|
+
|
21
|
+
alias :require :require
|
22
|
+
alias :load :load
|
23
|
+
|
24
|
+
#
|
25
|
+
def call(fname, options={})
|
26
|
+
if options[:load]
|
27
|
+
load(fname, options[:wrap])
|
28
|
+
else
|
29
|
+
require(fname)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# A load wedge should provide a means for iterating over all requirable
|
34
|
+
# files that its `#call` method could load.
|
35
|
+
#
|
36
|
+
# If a load wedge is "esoteric", in that it doesn't actually load
|
37
|
+
# a file, then it's `#each` method can iterate over a list of suitable
|
38
|
+
# symbolic identifiers, or if otherwise necessary nothing at all. But
|
39
|
+
# be sure to document this prominantly!!!
|
40
|
+
#
|
41
|
+
def each(options={}, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Name of wedge. By default it is simply the class name.
|
45
|
+
def name
|
46
|
+
self.class.name
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Given a base-path, and a path relative to it determine if a matching
|
52
|
+
# file exists. Unless +:load+ option is +true+, this will check for each
|
53
|
+
# viable Ruby suffix. If a match is found the full path to the file
|
54
|
+
# is returned, otherwise +nil+.
|
55
|
+
|
56
|
+
def lookup(base_path, relative_path, options={})
|
57
|
+
exts = default_file_extensions
|
58
|
+
if options[:load] or exts.include?(File.extname(relative_path))
|
59
|
+
abspath = File.join(base_path, relative_path)
|
60
|
+
File.exist?(abspath) ? abspath : nil
|
61
|
+
else
|
62
|
+
exts.each do |ext|
|
63
|
+
abspath = File.join(base_path, relative_path + ext)
|
64
|
+
return abspath if File.exist?(abspath)
|
65
|
+
end
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# This helper method provides a fast way to traverse a directory
|
71
|
+
# recursively iteratating over each file.
|
72
|
+
|
73
|
+
def traverse(dir, base=dir, &block)
|
74
|
+
return unless File.directory?(dir)
|
75
|
+
Dir.new(dir).each do |file|
|
76
|
+
next if file == '.' or file == '..'
|
77
|
+
path = File.join(dir, file)
|
78
|
+
if File.directory?(path)
|
79
|
+
traverse(path, base, &block)
|
80
|
+
else
|
81
|
+
block.call(path.sub(base+'/',''))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# This method is used by `#lookup` to handle defualt file extensions.
|
87
|
+
# By default it returns the value of the `RB_EXTS` constant.
|
88
|
+
|
89
|
+
def default_file_extensions
|
90
|
+
RB_EXTS
|
91
|
+
end
|
92
|
+
|
93
|
+
# Raise LoadError with an error message patterned after Ruby's standard
|
94
|
+
# error message when a script can't be required or loaded.
|
95
|
+
|
96
|
+
def raise_load_error(fname)
|
97
|
+
raise LoadError, "no such file to load -- #{fname}"
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'loadable/version'
|
2
|
+
require 'loadable/mixin'
|
3
|
+
require 'loadable/domain'
|
4
|
+
require 'loadable/loaders/original_loader'
|
5
|
+
require 'loadable/loaders/ruby_loader'
|
6
|
+
require 'loadable/loaders/gem_loader'
|
7
|
+
require 'loadable/loaders/vendor_loader'
|
8
|
+
require 'loadable/kernel'
|
9
|
+
|
10
|
+
Loadable.register(Loadable::OriginalLoader.new)
|
11
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RbConfig
|
2
|
+
|
3
|
+
class LoadWedge
|
4
|
+
|
5
|
+
#
|
6
|
+
def self.metadata
|
7
|
+
@metadata ||= (
|
8
|
+
require 'yaml'
|
9
|
+
YAML.load_file(File.dirname(__FILE__) + '/load_wedge.yml')
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
def self.method_missing(name)
|
15
|
+
metadata[name.to_s.downcase] || super(name)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/loadable.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$SUBPRO = true
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'loadable'
|
3
|
+
|
4
|
+
# TODO: techincally these tests need to be isolated in separate processes
|
5
|
+
# to truly work right, but we won't overly fret about it presently b/c
|
6
|
+
# MiniTest randomizes execution order, so they will get fully tested
|
7
|
+
# over the course a handful of runs.
|
8
|
+
|
9
|
+
describe "GemLoader" do
|
10
|
+
|
11
|
+
# setup alternate loadpath
|
12
|
+
dir = File.expand_path(File.dirname(__FILE__) + '/fixture')
|
13
|
+
|
14
|
+
before do
|
15
|
+
$LOAD_PATH.unshift(dir)
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
$LOAD_PATH.delete(dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should load local library when neither `:gem` or `:from` option is supplied" do
|
23
|
+
require 'ansi'
|
24
|
+
assert $NO_ANSI, "Local `ansi.rb' library not loaded."
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should load the gem when `:from` option is supplied" do
|
28
|
+
require 'ansi', :gem=>'ansi'
|
29
|
+
assert ANSI, "Ruby `ansi.rb' library not loaded."
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should load from the gem when the `:gem` option is supplied" do
|
33
|
+
require 'ansi', :from=>'ansi'
|
34
|
+
assert ANSI, "Ruby `ansi.rb' library not loaded."
|
35
|
+
end
|
36
|
+
|
37
|
+
# This isn't a 100% thurough.
|
38
|
+
it "should iterate over current gems files" do
|
39
|
+
loader = Loadable::GemLoader.new
|
40
|
+
|
41
|
+
list = []
|
42
|
+
loader.each(:gem=>'ansi'){ |f| list << f }
|
43
|
+
|
44
|
+
assert list.include?('ansi.rb')
|
45
|
+
assert list.include?('ansi/code.rb')
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'loadable'
|
3
|
+
|
4
|
+
describe "RubyLoader" do
|
5
|
+
|
6
|
+
# setup alternate loadpath
|
7
|
+
dir = File.expand_path(File.dirname(__FILE__) + '/fixture')
|
8
|
+
|
9
|
+
before do
|
10
|
+
$LOAD_PATH.unshift(dir)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
$LOAD_PATH.delete(dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should load local library without the ruby prefix" do
|
18
|
+
require 'abbrev'
|
19
|
+
assert $NO_ABBREV, "Local `abbrev.rb' library not loaded."
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should load the standard ruby library with the `:from` ruby option" do
|
23
|
+
require 'abbrev', :from=>'ruby'
|
24
|
+
assert Abbrev, "Ruby `abbrev.rb' library not loaded."
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should iterate over standard ruby libs" do
|
28
|
+
loader = Loadable::RubyLoader.new
|
29
|
+
|
30
|
+
list = []
|
31
|
+
loader.each{ |f| list << f }
|
32
|
+
|
33
|
+
assert list.include?('abbrev.rb')
|
34
|
+
assert list.include?('ostruct.rb')
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'loadable'
|
3
|
+
|
4
|
+
describe "VendorLoader" do
|
5
|
+
|
6
|
+
dir = File.dirname(__FILE__) + '/fixture'
|
7
|
+
|
8
|
+
it "should be able to vendor a directory directly" do
|
9
|
+
loader = Loadable::VendorLoader.new(dir, :direct=>true)
|
10
|
+
list = []
|
11
|
+
loader.each{ |f| list << f }
|
12
|
+
list.must_include('abbrev.rb')
|
13
|
+
list.must_include('ansi.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be able to vendor subprojects" do
|
17
|
+
loader = Loadable::VendorLoader.new(dir, '*')
|
18
|
+
list = []
|
19
|
+
loader.each{ |f| list << f }
|
20
|
+
list.must_include('subpro.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should vendor using the Loadable class method" do
|
24
|
+
Loadable.vendor(dir, '*')
|
25
|
+
require 'subpro'
|
26
|
+
assert $SUBPRO, "Vendored project not loaded."
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: loadable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Thomas Sawyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: detroit
|
16
|
+
requirement: &21512620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *21512620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: minitest
|
27
|
+
requirement: &21517660 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *21517660
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: minitap
|
38
|
+
requirement: &21751300 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *21751300
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &21755040 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *21755040
|
58
|
+
description: Loadable modifieds Ruby's load/require system to handle "load wedges",
|
59
|
+
which work much like routes in web frameworks, but in this case determine which
|
60
|
+
files get loaded.
|
61
|
+
email:
|
62
|
+
- transfire@gmail.com
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files:
|
66
|
+
- HISTORY.rdoc
|
67
|
+
- COPYING.rdoc
|
68
|
+
- INFRACTIONS.rdoc
|
69
|
+
- README.md
|
70
|
+
files:
|
71
|
+
- .yardopts
|
72
|
+
- .ruby
|
73
|
+
- lib/loadable/class_require.rb
|
74
|
+
- lib/loadable/core_ext/rubygems.rb
|
75
|
+
- lib/loadable/domain.rb
|
76
|
+
- lib/loadable/kernel.rb
|
77
|
+
- lib/loadable/loaders/gem_loader.rb
|
78
|
+
- lib/loadable/loaders/original_loader.rb
|
79
|
+
- lib/loadable/loaders/roll_loader.rb
|
80
|
+
- lib/loadable/loaders/ruby_loader.rb
|
81
|
+
- lib/loadable/loaders/vendor_loader.rb
|
82
|
+
- lib/loadable/mixin.rb
|
83
|
+
- lib/loadable/system.rb
|
84
|
+
- lib/loadable/version.rb
|
85
|
+
- lib/loadable.rb
|
86
|
+
- spec/fixture/abbrev.rb
|
87
|
+
- spec/fixture/ansi.rb
|
88
|
+
- spec/fixture/subpro/.ruby
|
89
|
+
- spec/fixture/subpro/lib/subpro.rb
|
90
|
+
- spec/gem_loader_spec.rb
|
91
|
+
- spec/helper.rb
|
92
|
+
- spec/ruby_loader_spec.rb
|
93
|
+
- spec/vendor_loader_spec.rb
|
94
|
+
- HISTORY.rdoc
|
95
|
+
- README.md
|
96
|
+
- COPYING.rdoc
|
97
|
+
- INFRACTIONS.rdoc
|
98
|
+
homepage: http://rubyworks.github.com/loadable
|
99
|
+
licenses: []
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 1.8.5
|
119
|
+
signing_key:
|
120
|
+
specification_version: 3
|
121
|
+
summary: Safely Customize Ruby's Load System
|
122
|
+
test_files:
|
123
|
+
- spec/gem_loader_spec.rb
|
124
|
+
- spec/vendor_loader_spec.rb
|
125
|
+
- spec/ruby_loader_spec.rb
|