subload 1.0.0

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