subload 1.0.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/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
|