lazy_load 0.0.2
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/.gitignore +7 -0
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/README.md +93 -0
- data/Rakefile +17 -0
- data/lazy_load.gemspec +28 -0
- data/lib/lazy_load.rb +74 -0
- data/test/helper.rb +15 -0
- data/test/lazy_load_test.rb +103 -0
- metadata +73 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jostein Berre Eliassen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
LazyLoad
|
3
|
+
========
|
4
|
+
|
5
|
+
**Unobtrusively autoload code with callbacks and helpful errors**
|
6
|
+
|
7
|
+
---
|
8
|
+
|
9
|
+
LazyLoad is a more elaborate alternative to the [autoload](http://ruby-doc.org/core/classes/Module.html#M000443) method. For instance, it allows setting up callbacks to be invoked when a certain constant is referenced. It is used in scenarios where you need to "soft require" something -- typically to find the "best available" implementation, or fail gracefully when dependencies are not met.
|
10
|
+
|
11
|
+
Unlike autoload, LazyLoad is scoped, and it does therefore not pollute or monkey patch. What this means is: When you register a callback for the `Foo` constant, referencing `LazyLoad::Foo` will trigger the callback. Simply referencing `Foo` will not trigger the callback.
|
12
|
+
|
13
|
+
### Samples
|
14
|
+
|
15
|
+
```bash
|
16
|
+
|
17
|
+
gem install lazy_load`
|
18
|
+
|
19
|
+
```
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
|
23
|
+
LazyLoad.map(:Tilt, 'tilt',
|
24
|
+
'Tilt not found. Possible fix: gem install tilt')
|
25
|
+
|
26
|
+
# or equivalent with a callback:
|
27
|
+
|
28
|
+
LazyLoad.map(:Tilt) do
|
29
|
+
begin
|
30
|
+
require 'tilt'
|
31
|
+
Tilt
|
32
|
+
rescue LoadError
|
33
|
+
raise(LazyLoad::DependencyError,
|
34
|
+
'Tilt not found. Possible fix: gem install tilt')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Tilt
|
39
|
+
# => NameError: uninitialized constant Object::Tilt
|
40
|
+
|
41
|
+
LazyLoad::Tilt
|
42
|
+
# => Tilt
|
43
|
+
|
44
|
+
# or if Tilt is not available:
|
45
|
+
|
46
|
+
LazyLoad::Tilt
|
47
|
+
# => LazyLoad::DependencyError: Tilt not found. Possible fix: gem install tilt'
|
48
|
+
|
49
|
+
LazyLoad::Foo
|
50
|
+
# => NameError: uninitialized constant LazyLoad::Foo
|
51
|
+
|
52
|
+
```
|
53
|
+
|
54
|
+
Notice how, when a block is used, it must return the constant. The help message is optional. LazyLoad has no mappings by default.
|
55
|
+
|
56
|
+
|
57
|
+
### Errors
|
58
|
+
|
59
|
+
Referencing constant beneath `LazyLoad` for which there is no mapping, resuslts in the usual `NameError`. Referencing an unavailable constant typically gives a `LazyLoad::DependencyError`, which conveniently is also a subclass of `NameError`.
|
60
|
+
|
61
|
+
|
62
|
+
### Best available
|
63
|
+
|
64
|
+
You can use the `best` method to return the first constant from a list of names, or else raise a `LazyLoad::DependencyError` for the first (most preferred) one.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
LazyLoad.best(:Kramdown, :RDiscount, :Redcarpet)
|
68
|
+
|
69
|
+
```
|
70
|
+
|
71
|
+
|
72
|
+
### Scopes
|
73
|
+
|
74
|
+
Use the `scope` method if you need more than one scope (typically for gem interoperability).
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
module SomeProject
|
78
|
+
Lazy = LazyLoad.scope do
|
79
|
+
map(:StringIO, 'stringio')
|
80
|
+
end
|
81
|
+
|
82
|
+
Lazy.map(:Tilt, 'tilt')
|
83
|
+
|
84
|
+
Lazy::StringIO # => StringIO
|
85
|
+
Lazy::Tilt # => Tilt
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
Feedback and suggestions are welcome through Github.
|
90
|
+
|
91
|
+
---
|
92
|
+
|
93
|
+
© 2011 Jostein Berre Eliassen. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
task :test do
|
3
|
+
$LOAD_PATH.unshift './lib'
|
4
|
+
require 'lazy_load'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
begin; require 'turn'; rescue LoadError; end
|
7
|
+
Dir.glob("test/**/*_test.rb").each { |test| require "./#{test}" }
|
8
|
+
end
|
9
|
+
|
10
|
+
task :shell do
|
11
|
+
system 'pry -I lib -r lazy_load --trace'
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :test
|
15
|
+
|
16
|
+
|
17
|
+
|
data/lazy_load.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'lazy_load'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
|
6
|
+
s.name = 'lazy_load'
|
7
|
+
|
8
|
+
s.author = 'Jostein Berre Eliassen'
|
9
|
+
s.email = 'josteinpost@gmail.com'
|
10
|
+
s.homepage = "http://github.com/jbe/lazy_load"
|
11
|
+
s.license = "MIT"
|
12
|
+
|
13
|
+
s.version = LazyLoad::VERSION
|
14
|
+
s.platform = Gem::Platform::RUBY
|
15
|
+
|
16
|
+
s.summary = 'An unobtrusive way to autoload code with callbacks ' +
|
17
|
+
'and helpful errors.'
|
18
|
+
|
19
|
+
s.description = 'LazyLoad is a more elaborate alternative to the ' +
|
20
|
+
'autoload method. For instance, it allows setting ' +
|
21
|
+
'up callbacks to be invoked when a certain constant ' +
|
22
|
+
'is referenced. It does not monkey patch or ' +
|
23
|
+
'pollute the global namespace.'
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
27
|
+
s.require_path = 'lib'
|
28
|
+
end
|
data/lib/lazy_load.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module LazyLoad
|
5
|
+
|
6
|
+
VERSION = '0.0.2'
|
7
|
+
|
8
|
+
class DependencyError < NameError; end
|
9
|
+
|
10
|
+
module Methods
|
11
|
+
|
12
|
+
def reset!
|
13
|
+
@messages = {}
|
14
|
+
@actions = {}
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def scope(&blk)
|
19
|
+
mod = Module.new.extend(LazyLoad::Methods).reset!
|
20
|
+
mod.module_eval(&blk) if blk
|
21
|
+
mod
|
22
|
+
end
|
23
|
+
|
24
|
+
def map(name, action=nil, msg=nil, &blk)
|
25
|
+
@messages[name] = msg
|
26
|
+
@actions[name] = blk || action || raise(
|
27
|
+
ArgumentError, "missing require path or callback")
|
28
|
+
end
|
29
|
+
|
30
|
+
def unmap(name)
|
31
|
+
@messages.delete(name)
|
32
|
+
@actions.delete(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def const_missing(name)
|
36
|
+
k = case action = @actions[name]
|
37
|
+
when String then helpful_require(name)
|
38
|
+
when Proc then action.call
|
39
|
+
when nil then super
|
40
|
+
else raise "Invalid action for dependency #{action.inspect}"
|
41
|
+
end
|
42
|
+
const_set(name, k)
|
43
|
+
end
|
44
|
+
|
45
|
+
def helpful_require(name)
|
46
|
+
begin
|
47
|
+
require @actions[name]
|
48
|
+
Kernel.const_get(name)
|
49
|
+
rescue LoadError
|
50
|
+
raise(DependencyError, @messages[name] ||
|
51
|
+
"failed to require #{@actions[name].inspect}.")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return the first available dependency from the
|
56
|
+
# list of constant names.
|
57
|
+
#
|
58
|
+
def best(*names)
|
59
|
+
names.each do |name|
|
60
|
+
begin
|
61
|
+
return const_get(name)
|
62
|
+
rescue NameError; end
|
63
|
+
end
|
64
|
+
const_get(names.first)
|
65
|
+
end
|
66
|
+
alias :first_available :best
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
extend Methods
|
71
|
+
reset!
|
72
|
+
|
73
|
+
end
|
74
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
class TestCase < MiniTest::Unit::TestCase
|
3
|
+
|
4
|
+
def self.test(name, &block)
|
5
|
+
test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
|
6
|
+
defined = instance_method(test_name) rescue false
|
7
|
+
#raise "#{test_name} already defined in #{self}" if defined
|
8
|
+
|
9
|
+
block ||= proc { skip }
|
10
|
+
define_method(test_name, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
class LazyLoadTest < TestCase
|
4
|
+
|
5
|
+
|
6
|
+
# helpers:
|
7
|
+
|
8
|
+
def with_mappings(*args)
|
9
|
+
LazyLoad.map(:StringIO, 'stringio', 'stringio not in stdlib?')
|
10
|
+
LazyLoad.map(:Bogus, 'thishastobebogusnow', 'bogus lib not found')
|
11
|
+
LazyLoad.map(:Message) { 'love' }
|
12
|
+
LazyLoad.map(:Unavailable) do
|
13
|
+
raise LazyLoad::DependencyError, 'not available'
|
14
|
+
end
|
15
|
+
yield
|
16
|
+
LazyLoad.reset!
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# basic
|
21
|
+
|
22
|
+
test 'raises name error' do
|
23
|
+
assert_raises(NameError) do
|
24
|
+
LazyLoad::Nonexistant
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'requires existing' do
|
29
|
+
with_mappings do
|
30
|
+
assert LazyLoad::StringIO == StringIO
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
test 'require fails with message when unavailable' do
|
35
|
+
with_mappings do
|
36
|
+
assert_raises(LazyLoad::DependencyError) do
|
37
|
+
LazyLoad::Bogus
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
test 'callback' do
|
43
|
+
with_mappings do
|
44
|
+
assert LazyLoad::Message == "love"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
test 'callback with dependency error' do
|
49
|
+
with_mappings do
|
50
|
+
assert_raises(LazyLoad::DependencyError) do
|
51
|
+
LazyLoad::Unavailable
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# first_available
|
58
|
+
|
59
|
+
test 'returns first when first available is first' do
|
60
|
+
with_mappings do
|
61
|
+
assert_equal(
|
62
|
+
'love',
|
63
|
+
LazyLoad.best(:Message, :StringIO, :Unavailable)
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
test 'returns third when first available is third' do
|
69
|
+
with_mappings do
|
70
|
+
assert_equal(
|
71
|
+
'StringIO',
|
72
|
+
LazyLoad.best(:Bogus, :Unavailable, :StringIO, :Message).name
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
test 'fails with first message when none available' do
|
78
|
+
with_mappings do
|
79
|
+
assert_raises LazyLoad::DependencyError do
|
80
|
+
LazyLoad.best(:Bogus, :Unavailable, :Bogus)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# new_scope
|
87
|
+
|
88
|
+
test 'scope independent of source' do
|
89
|
+
with_mappings do
|
90
|
+
scope = LazyLoad.scope
|
91
|
+
scope.map(:StringIO, 'not_anymore')
|
92
|
+
|
93
|
+
assert_equal('StringIO', LazyLoad::StringIO.name)
|
94
|
+
assert_raises(LazyLoad::DependencyError) do
|
95
|
+
scope::StringIO
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lazy_load
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jostein Berre Eliassen
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-06-06 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: LazyLoad is a more elaborate alternative to the autoload method. For instance, it allows setting up callbacks to be invoked when a certain constant is referenced. It does not monkey patch or pollute the global namespace.
|
22
|
+
email: josteinpost@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- .gitignore
|
31
|
+
- CHANGELOG
|
32
|
+
- LICENSE
|
33
|
+
- README.md
|
34
|
+
- Rakefile
|
35
|
+
- lazy_load.gemspec
|
36
|
+
- lib/lazy_load.rb
|
37
|
+
- test/helper.rb
|
38
|
+
- test/lazy_load_test.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/jbe/lazy_load
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.7
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: An unobtrusive way to autoload code with callbacks and helpful errors.
|
71
|
+
test_files:
|
72
|
+
- test/helper.rb
|
73
|
+
- test/lazy_load_test.rb
|