loadable 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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