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 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
@@ -0,0 +1,11 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+ require 'subload'
3
+
4
+ module A
5
+ # Normal subload, by file, looks in __DIR__/a/foo
6
+ subload :Foo
7
+ p Foo
8
+ p Foo::Bar
9
+ end
10
+
11
+ p A::Foo::Baz
data/examples/a/foo.rb ADDED
@@ -0,0 +1,7 @@
1
+ module A
2
+ class Foo
3
+ subload_with(:require)
4
+ subload :Bar
5
+ subload :Baz
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module A
2
+ class Foo
3
+ class Bar
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module A
2
+ class Foo
3
+ class Baz
4
+ end
5
+ end
6
+ end
@@ -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,2 @@
1
+ TestSubload::A = true
2
+ TestSubload::Al = true
@@ -0,0 +1 @@
1
+ TestSubload::B = true
@@ -0,0 +1 @@
1
+ TestSubload::C = true
@@ -0,0 +1,2 @@
1
+ TestSubload::D = true
2
+ TestSubload::Dl = true
@@ -0,0 +1,2 @@
1
+ TestSubload::E = true
2
+ TestSubload::El = true
@@ -0,0 +1,3 @@
1
+ class TestSubload::F
2
+ subload :A
3
+ end
@@ -0,0 +1,2 @@
1
+ TestSubload::F::A = true
2
+ TestSubload::F::Al = 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