subload 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +77 -0
- data/Rakefile +108 -0
- data/examples/a.rb +11 -0
- data/examples/a/foo.rb +7 -0
- data/examples/a/foo/bar.rb +6 -0
- data/examples/a/foo/baz.rb +6 -0
- data/examples/rails/loader.rb +27 -0
- data/lib/subload.rb +97 -0
- data/test/test_subload.rb +71 -0
- data/test/test_subload/a.rb +2 -0
- data/test/test_subload/b.rb +1 -0
- data/test/test_subload/c.rb +1 -0
- data/test/test_subload/d.rb +2 -0
- data/test/test_subload/e.rb +2 -0
- data/test/test_subload/f.rb +3 -0
- data/test/test_subload/f/a.rb +2 -0
- metadata +94 -0
data/README
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
subload
|
2
|
+
|
3
|
+
A handy dandy autoload / require / load helper for your rubies. Similar to
|
4
|
+
using[1], but with a few differences of opinion, and a bit shorter.
|
5
|
+
|
6
|
+
Basically, expand path is fine, up until a point. Sometimes there's no point
|
7
|
+
(i.e. when the load path already contains most of the path you're trying to
|
8
|
+
open). When you're writing libs that users might require sub parts with
|
9
|
+
'libname/sub_part', then expand_path combined with say, rubygems, can lead to
|
10
|
+
double requires. Lets not do that. :-)
|
11
|
+
|
12
|
+
[1] http://github.com/smtlaissezfaire/using/
|
13
|
+
|
14
|
+
TODO - replace me... more...
|
15
|
+
|
16
|
+
Examples:
|
17
|
+
|
18
|
+
module A
|
19
|
+
|
20
|
+
# The nominal use case, A.autoload :B, 'a/b'
|
21
|
+
# You rarely need much else!
|
22
|
+
subload :B
|
23
|
+
|
24
|
+
# A custom path, A.autoload :C, 'a/c'
|
25
|
+
subload :C, :path => 'a/c'
|
26
|
+
# For example when 'a/c' defines several constants:
|
27
|
+
subload :Ca, :path => 'a/c'
|
28
|
+
subload :Cb, :path => 'a/c'
|
29
|
+
|
30
|
+
# An expanded path, A.autoload :D, File.join(Dir.pwd, 'a/d')
|
31
|
+
subload :D, :expand_path => true
|
32
|
+
# This has interesting uses in combination, although not generally
|
33
|
+
# recommended:
|
34
|
+
subload :E, :path => '../../path/e', :expand_path => true
|
35
|
+
|
36
|
+
# Explicitly override the mode, for this call only, A.require 'a/f'
|
37
|
+
subload :F, :mode => :require
|
38
|
+
|
39
|
+
# Set the mode for all subsiquent calls to subload in this class/module
|
40
|
+
subload_with :require
|
41
|
+
subload :G # => require 'a/g'
|
42
|
+
subload :H # => require 'a/h'
|
43
|
+
|
44
|
+
# Other features intended for library and framework developers are described
|
45
|
+
# in the class documentation.
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
Some Notes:
|
50
|
+
|
51
|
+
* File.expand_path: Expand path is good if you're traversing up directories.
|
52
|
+
It is bad if you're loading something within a library that is on the load
|
53
|
+
path. If the library is on the load path, and you require files with an
|
54
|
+
expanded path, there is the likelihood of a double require when other code
|
55
|
+
contains a require that is expectant of the load path modification.
|
56
|
+
|
57
|
+
* Frameworks: Sometimes require explicit load order control. In these cases,
|
58
|
+
overriding the loader and either tracking, or performing stateful operations
|
59
|
+
works well.
|
60
|
+
|
61
|
+
* Abusive use of override_mode: Override mode is potentially dangerous. As per
|
62
|
+
the other documentation, override mode should be reserved for use in
|
63
|
+
application code only. Libraries setting override mode could cause
|
64
|
+
additional failure cases for foreign libraries, although gratuitous addition
|
65
|
+
of loading mechanisms is not recommended.
|
66
|
+
|
67
|
+
* Generated code loading: When you're doing code generation, sometimes it is
|
68
|
+
desirable to have tow locations from which to load a class. One will contain
|
69
|
+
custom class defintions, and the other will contain generated definitions.
|
70
|
+
Using a custom loader, one can then utilise a single subload statement to
|
71
|
+
correctly load both files.
|
72
|
+
|
73
|
+
TODO Chaotic Overloading
|
74
|
+
|
75
|
+
Consider vertical vs. horizontal delegation rules and use cases for new
|
76
|
+
loaders in non-framework libraries that perform custom loads such that it is
|
77
|
+
easy to say "invoke the current load mode with the following options".
|
data/Rakefile
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'rake/clean'
|
3
|
+
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
def spec(file = Dir['*.gemspec'].first)
|
7
|
+
@spec ||=
|
8
|
+
begin
|
9
|
+
require 'rubygems/specification'
|
10
|
+
Thread.abort_on_exception = true
|
11
|
+
data = File.read(file)
|
12
|
+
spec = nil
|
13
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
14
|
+
spec.instance_variable_set(:@filename, file)
|
15
|
+
def spec.filename; @filename; end
|
16
|
+
spec
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def manifest; @manifest ||= `git ls-files`.split("\n").reject{|s|s=~/\.gemspec$|\.gitignore$/}; end
|
21
|
+
|
22
|
+
@gem_package_task_type = begin
|
23
|
+
require 'rubygems/package_task'
|
24
|
+
Gem::PackageTask
|
25
|
+
rescue LoadError
|
26
|
+
require 'rake/gempackagetask'
|
27
|
+
Rake::GemPackageTask
|
28
|
+
end
|
29
|
+
def gem_task; @gem_task ||= @gem_package_task_type.new(spec); end
|
30
|
+
gem_task.define
|
31
|
+
Rake::Task[:clobber].enhance [:clobber_package]
|
32
|
+
|
33
|
+
require 'rake/testtask'
|
34
|
+
Rake::TestTask.new(:test) do |t|
|
35
|
+
t.test_files = spec.test_files
|
36
|
+
t.ruby_opts = ['-rubygems'] if defined? Gem
|
37
|
+
t.warning = true
|
38
|
+
end unless spec.test_files.empty?
|
39
|
+
|
40
|
+
rdoc_task_type = begin
|
41
|
+
require 'rdoc/task'
|
42
|
+
RDoc::Task
|
43
|
+
rescue LoadError
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask
|
46
|
+
end
|
47
|
+
df = begin; require 'rdoc/generator/darkfish'; true; rescue LoadError; end
|
48
|
+
rdtask = rdoc_task_type.new do |rd|
|
49
|
+
rd.title = spec.name
|
50
|
+
rd.main = spec.extra_rdoc_files.first
|
51
|
+
lib_rexp = spec.require_paths.map { |p| Regexp.escape p }.join('|')
|
52
|
+
rd.rdoc_files.include(*manifest.grep(/^(?:#{lib_rexp})/))
|
53
|
+
rd.rdoc_files.include(*spec.extra_rdoc_files)
|
54
|
+
rd.template = 'darkfish' if df
|
55
|
+
end
|
56
|
+
|
57
|
+
Rake::Task[:clobber].enhance [:clobber_rdoc]
|
58
|
+
|
59
|
+
require 'yaml'
|
60
|
+
require 'rake/contrib/sshpublisher'
|
61
|
+
desc "Publish rdoc to rubyforge"
|
62
|
+
task :publish => rdtask.name do
|
63
|
+
rf_cfg = File.expand_path '~/.rubyforge/user-config.yml'
|
64
|
+
host = "#{YAML.load_file(rf_cfg)['username']}@rubyforge.org"
|
65
|
+
remote_dir = "/var/www/gforge-projects/#{spec.rubyforge_project}/#{spec.name}/"
|
66
|
+
Rake::SshDirPublisher.new(host, remote_dir, rdtask.rdoc_dir).upload
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'Generate and open documentation'
|
70
|
+
task :docs => :rdoc do
|
71
|
+
path = rdtask.send :rdoc_target
|
72
|
+
case RUBY_PLATFORM
|
73
|
+
when /darwin/ ; sh "open #{path}"
|
74
|
+
when /mswin|mingw/ ; sh "start #{path}"
|
75
|
+
else
|
76
|
+
sh "firefox #{path}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
desc "Regenerate gemspec"
|
81
|
+
task :gemspec => spec.filename
|
82
|
+
|
83
|
+
task spec.filename do
|
84
|
+
spec.files = manifest
|
85
|
+
spec.test_files = FileList['{test,spec}/**/{test,spec}_*.rb']
|
86
|
+
open(spec.filename, 'w') { |w| w.write spec.to_ruby }
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "Bump version from #{spec.version} to #{spec.version.to_s.succ}"
|
90
|
+
task :bump do
|
91
|
+
spec.version = spec.version.to_s.succ
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "Tag version #{spec.version}"
|
95
|
+
task :tag do
|
96
|
+
tagged = Dir.new('.git/refs/tags').entries.include? spec.version.to_s
|
97
|
+
if tagged
|
98
|
+
warn "Tag #{spec.version} already exists"
|
99
|
+
else
|
100
|
+
# TODO release message in tag message
|
101
|
+
sh "git tag #{spec.version}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Release #{gem_task.gem_file} to rubyforge"
|
106
|
+
task :release => [:tag, :gem, :publish] do |t|
|
107
|
+
sh "rubyforge add_release #{spec.rubyforge_project} #{spec.name} #{spec.version} #{gem_task.package_dir}/#{gem_task.gem_file}"
|
108
|
+
end
|
data/examples/a.rb
ADDED
data/examples/a/foo.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
require 'subload'
|
3
|
+
|
4
|
+
module Rails
|
5
|
+
def self.production?
|
6
|
+
# flip me, baby
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
module Loader
|
11
|
+
Subloader = lambda do |calling_object, const_name, path, options|
|
12
|
+
# Example is allowing override exclusively in a particular mode, but we
|
13
|
+
# could also be simply registering what's being loaded instead, and
|
14
|
+
# doing some other magic.
|
15
|
+
if Rails.production? && options[:eager_load]
|
16
|
+
require path
|
17
|
+
else
|
18
|
+
calling_object.__send__ :autoload, const_name, path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Subload::MODES[:rails_subloader] = Subloader
|
23
|
+
subload_with(:rails_subloader)
|
24
|
+
subload :SomethingWeDontCareAbout
|
25
|
+
subload :SomethingEssentialForThreadedBoot, :eager_load => true
|
26
|
+
end
|
27
|
+
end
|
data/lib/subload.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# TODO convert to yardoc
|
2
|
+
module Subload
|
3
|
+
# To add modes to subload, simply add them to this hash. Please use the
|
4
|
+
# a namespace convention, starting with +:projectname_operationdescription+.
|
5
|
+
MODES = {
|
6
|
+
:autoload => lambda { |o,s,p,ops| o.__send__ :autoload, s, p },
|
7
|
+
:require => lambda { |o,s,p,ops| o.__send__ :require, p },
|
8
|
+
:load => lambda { |o,s,p,ops| o.__send__ :load, p }
|
9
|
+
}
|
10
|
+
|
11
|
+
@default_mode = :autoload
|
12
|
+
class <<self
|
13
|
+
# Subload defaults to autoload, which is appropriate and performant for
|
14
|
+
# single threaded applications and libraries. Setting the default mode
|
15
|
+
# will affect all future subloads that do not specify another mode
|
16
|
+
# explicitly, proided +Subload.override_mode+ has not been set.
|
17
|
+
attr_accessor :default_mode
|
18
|
+
# Subload defaults to autoload, which is appropriate and performant for
|
19
|
+
# single threaded applications and libraries. Sometimes users may want to
|
20
|
+
# switch to eager loading or some other loading mechanism forcefully.
|
21
|
+
# Override mode should not be set in normal library code, but should be
|
22
|
+
# reserved for application code only. It may be acceptable for use in
|
23
|
+
# appropriate locations in frameworks.
|
24
|
+
attr_accessor :override_mode
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sets the subload mode locally for the current class. This is how one would
|
28
|
+
# use a custom loading mode
|
29
|
+
def subload_with(mode = nil)
|
30
|
+
@_subload_mode = mode unless mode.nil?
|
31
|
+
MODES[Subload.override_mode || @_subload_mode || Subload.default_mode]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Load the given constant name from the path corresponding to the
|
35
|
+
# conventional mapping of the class name underscored. Example:
|
36
|
+
# class A
|
37
|
+
# subload :B # performs A.autoload(:B, 'a/b')
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# You can overload various operation styles in subload using the options
|
41
|
+
# hash:
|
42
|
+
# * :path - explicitly alter the path that will be loaded, this is passed on
|
43
|
+
# unmodified.
|
44
|
+
# * :mode - explicitly override the loading mode. N.B. Subload.override_mode
|
45
|
+
# takes precidence over this.
|
46
|
+
# * :expand_path - when set to true, the path will be expanded before being
|
47
|
+
# passed on to the loading mechanism.
|
48
|
+
#
|
49
|
+
# For custom loading mechanisms, further options are passed on, as such, you
|
50
|
+
# may find other behaviors if subload is being used inside a framework.
|
51
|
+
def subload(symbol, options = {})
|
52
|
+
sub_path, mode = *options.values_at(:path, :mode)
|
53
|
+
klass = self.instance_of?(Class) || self.instance_of?(Module)
|
54
|
+
klass = klass ? self.__name__ : self.class.__name__
|
55
|
+
path = File.join(sub_path || Subload.to_path("#{klass}::#{symbol}"))
|
56
|
+
path = File.expand_path(path) if options[:expand_path]
|
57
|
+
$stdout.puts [:subload, symbol, path, mode].inspect if $DEBUG
|
58
|
+
subload_with(mode)[self, symbol, path, options]
|
59
|
+
end
|
60
|
+
|
61
|
+
LONG_UPPER_CONSTS = [/([A-Z]+)([A-Z][a-z]+)/, '\1_\2']
|
62
|
+
TAIL_UPPER_CONSTS = [/([a-z])([A-Z])/, '\1_\2']
|
63
|
+
DOUBLE_UNDERSCORE = '__'
|
64
|
+
DOUBLE_COLON = '::'
|
65
|
+
SLASH = '/'
|
66
|
+
|
67
|
+
# Subloads snake_case method, this is very similar to that of facets, rails,
|
68
|
+
# and merb, but potentially has minor differences.
|
69
|
+
def self.to_path(s)
|
70
|
+
# similar to facets/string/pathize, only supports TCPServer, etc.
|
71
|
+
s = s.to_s.dup # We're modifying, lets be careful
|
72
|
+
s.gsub!(*LONG_UPPER_CONSTS)
|
73
|
+
s.gsub!(*TAIL_UPPER_CONSTS)
|
74
|
+
s.gsub!(DOUBLE_UNDERSCORE, SLASH)
|
75
|
+
s.gsub!(DOUBLE_COLON, SLASH)
|
76
|
+
s.downcase!
|
77
|
+
s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Object
|
82
|
+
# Make sure we hit the top level, objects, classes, etc. We do not make this
|
83
|
+
# private, because source loading may want to not be, for more complex
|
84
|
+
# loading environments. External invocation is not recommended without clear
|
85
|
+
# reasoning.
|
86
|
+
include Subload
|
87
|
+
end
|
88
|
+
|
89
|
+
class Module
|
90
|
+
# If anyone can think of a better way to do this, please, I'm all ears.
|
91
|
+
# I'd like syntax for class_name?(object || klass) # => Symbol || String
|
92
|
+
if defined?(__name__)
|
93
|
+
warn("Subload clobber Class#__name__ from: #{method(:__name__).inspect}")
|
94
|
+
else
|
95
|
+
alias __name__ name
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
4
|
+
require "subload"
|
5
|
+
|
6
|
+
class TestSubload < Test::Unit::TestCase
|
7
|
+
# pretend we're in a libdir!
|
8
|
+
$:.unshift File.expand_path(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
Subload.override_mode = nil
|
12
|
+
Subload.default_mode = :autoload
|
13
|
+
subload_with(false)
|
14
|
+
end
|
15
|
+
|
16
|
+
subload :A
|
17
|
+
|
18
|
+
def test_subload_defaults
|
19
|
+
assert !defined?(Al) # Not loaded already (defaults to autoload)
|
20
|
+
assert_equal(true, A) # Autoloaded
|
21
|
+
assert defined?(Al)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_subload_require
|
25
|
+
subload :B, :mode => :require
|
26
|
+
assert defined?(B)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_subload_load
|
30
|
+
subload :C, :path => "test_subload/c.rb", :mode => :load
|
31
|
+
assert defined?(C)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_subload_with
|
35
|
+
subload_with(:require)
|
36
|
+
subload :D
|
37
|
+
subload_with(false)
|
38
|
+
|
39
|
+
assert_equal subload_with(false), subload_with
|
40
|
+
assert_equal subload_with(:autoload), subload_with
|
41
|
+
|
42
|
+
assert defined?(Dl) # Was required before we hit D
|
43
|
+
assert defined?(D)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_default_mode
|
47
|
+
Subload.default_mode = :require
|
48
|
+
assert_equal subload_with(:require), subload_with(nil)
|
49
|
+
ensure
|
50
|
+
Subload.default_mode = :autoload
|
51
|
+
subload_with(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_override_mode
|
55
|
+
assert !defined?(F)
|
56
|
+
assert !defined?(F::Al)
|
57
|
+
assert !defined?(F::A)
|
58
|
+
Subload.override_mode = :require
|
59
|
+
subload_with(:autoload)
|
60
|
+
subload :F
|
61
|
+
assert defined?(F)
|
62
|
+
assert defined?(F::Al)
|
63
|
+
assert defined?(F::A)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test___name__
|
67
|
+
assert_equal("Class", Class.__name__)
|
68
|
+
assert_equal("Module", Module.__name__)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
TestSubload::B = true
|
@@ -0,0 +1 @@
|
|
1
|
+
TestSubload::C = true
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: subload
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Tucker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-10 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rdoc
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.4.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.8.7
|
34
|
+
version:
|
35
|
+
description: An autoload/require/custom loader wrapper
|
36
|
+
email: raggi@rubyforge.org
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
files:
|
44
|
+
- README
|
45
|
+
- Rakefile
|
46
|
+
- examples/a.rb
|
47
|
+
- examples/a/foo.rb
|
48
|
+
- examples/a/foo/bar.rb
|
49
|
+
- examples/a/foo/baz.rb
|
50
|
+
- examples/rails/loader.rb
|
51
|
+
- lib/subload.rb
|
52
|
+
- test/test_subload.rb
|
53
|
+
- test/test_subload/a.rb
|
54
|
+
- test/test_subload/b.rb
|
55
|
+
- test/test_subload/c.rb
|
56
|
+
- test/test_subload/d.rb
|
57
|
+
- test/test_subload/e.rb
|
58
|
+
- test/test_subload/f.rb
|
59
|
+
- test/test_subload/f/a.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://libraggi.rubyforge.org/subload
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --line-numbers
|
67
|
+
- --inline-source
|
68
|
+
- --title
|
69
|
+
- Subload
|
70
|
+
- --main
|
71
|
+
- README
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
version:
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project: libraggi
|
89
|
+
rubygems_version: 1.3.5
|
90
|
+
signing_key:
|
91
|
+
specification_version: 2
|
92
|
+
summary: An autoload/require/custom loader wrapper
|
93
|
+
test_files:
|
94
|
+
- test/test_subload.rb
|