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.
@@ -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,5 @@
1
+ require 'loadable/system'
2
+
3
+ Loadable.register(Loadable::RubyLoader.new)
4
+ Loadable.register(Loadable::GemLoader.new)
5
+
@@ -0,0 +1,3 @@
1
+ # This is a dummy abbrev.rb script to test
2
+ # is Ruby's standard abbrev.rb can be loaded.
3
+ $NO_ABBREV = true
@@ -0,0 +1,3 @@
1
+ # This is a dummy ansi.rb script to test
2
+ # if ansi.rb from the ANSI gem can be loaded.
3
+ $NO_ANSI = true
@@ -0,0 +1,5 @@
1
+ ---
2
+ name: subpro
3
+ version: 0.0.0
4
+ load_path: [lib]
5
+
@@ -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,6 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'minitap'
4
+
5
+ MiniTest::Unit.runner = MiniTest::TapY.new
6
+
@@ -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