lazy_load 0.0.3 → 0.1.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/CHANGELOG +7 -1
- data/README.md +65 -42
- data/lazy_load.gemspec +5 -7
- data/lib/lazy_load.rb +38 -30
- data/test/lazy_load_test.rb +54 -33
- metadata +8 -4
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -2,13 +2,17 @@
|
|
2
2
|
LazyLoad
|
3
3
|
========
|
4
4
|
|
5
|
-
**
|
5
|
+
**Autoload with custom callbacks**
|
6
6
|
|
7
7
|
---
|
8
8
|
|
9
|
-
LazyLoad is a more elaborate alternative to
|
9
|
+
LazyLoad is a slightly more elaborate alternative to [autoload](http://ruby-doc.org/core/classes/Module.html#M000443). It helps deal with "soft" dependencies, where dependencies are "preferred", rather than strictly needed. It is very simple, but provides:
|
10
10
|
|
11
|
-
|
11
|
+
* Custom callbacks
|
12
|
+
* "Best available" dependency selection
|
13
|
+
* A simple, optional wrapper mechanism
|
14
|
+
|
15
|
+
Unlike [autoload](http://ruby-doc.org/core/classes/Module.html#M000443), LazyLoad is scoped. This means that when you register a callback for the `Foo` constant, referencing `LazyLoad::Foo` will trigger the callback. Simply referencing `Foo` will not trigger the callback. In other words, internals are not monkey patched. However, you can also use it as a mixin, thereby eliminating the verbosity.
|
12
16
|
|
13
17
|
### Samples
|
14
18
|
|
@@ -20,72 +24,91 @@ Unlike autoload, LazyLoad is scoped, and it does therefore not pollute or monkey
|
|
20
24
|
|
21
25
|
```ruby
|
22
26
|
|
23
|
-
|
24
|
-
'Tilt not found. Possible fix: gem install tilt')
|
27
|
+
# same as as autoload:
|
25
28
|
|
26
|
-
|
29
|
+
LazyLoad.const(:Tilt, 'tilt')
|
27
30
|
|
28
|
-
|
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
|
31
|
+
# or the equivalent with a callback:
|
37
32
|
|
38
|
-
Tilt
|
39
|
-
|
33
|
+
LazyLoad.const(:Tilt) do
|
34
|
+
require 'tilt'
|
35
|
+
Tilt
|
36
|
+
end
|
40
37
|
|
41
|
-
|
42
|
-
# => Tilt
|
38
|
+
# lets you..
|
43
39
|
|
44
|
-
#
|
45
|
-
|
46
|
-
LazyLoad::Tilt
|
47
|
-
# => LazyLoad::DependencyError: Tilt not found. Possible fix: gem install tilt'
|
40
|
+
LazyLoad::Tilt # => Tilt (or LoadError)
|
48
41
|
|
49
|
-
LazyLoad::
|
50
|
-
# => NameError: uninitialized constant LazyLoad::
|
42
|
+
LazyLoad::Oboe
|
43
|
+
# => NameError: uninitialized constant LazyLoad::Oboe
|
51
44
|
|
52
45
|
```
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
### Errors
|
58
|
-
|
59
|
-
Referencing a 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`.
|
47
|
+
Note that the return value of the callback becomes the constant. Any object can be returneed.
|
60
48
|
|
61
49
|
|
62
50
|
### Best available
|
63
51
|
|
64
|
-
You can use the `best` method to return the first constant
|
52
|
+
You can use the `best` method to return the first constant for which the callback does not raise a `LoadError`, or else raise the `LoadError` of the first (most preferred) constant:
|
65
53
|
|
66
54
|
```ruby
|
55
|
+
|
67
56
|
LazyLoad.best(:Kramdown, :RDiscount, :Redcarpet)
|
68
57
|
|
58
|
+
# or using named groups:
|
59
|
+
|
60
|
+
LazyLoad.group(:markdown, :Kramdown, :RDiscount, :Redcarpet)
|
61
|
+
LazyLoad.best(:markdown)
|
62
|
+
|
69
63
|
```
|
70
64
|
|
65
|
+
### Mixin
|
66
|
+
|
67
|
+
You can endow objects with the LazyLoad methods by extending the `LazyLoad::Mixin.` Each object will have its own scope of callbacks.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
|
71
|
+
module WidgetFactoryFactory
|
72
|
+
|
73
|
+
extend LazyLoad::Mixin
|
74
|
+
|
75
|
+
const :Haml, 'haml'
|
71
76
|
|
72
|
-
|
77
|
+
Haml # => Haml
|
73
78
|
|
74
|
-
|
79
|
+
end
|
80
|
+
|
81
|
+
```
|
82
|
+
|
83
|
+
### Wrappers
|
84
|
+
|
85
|
+
LazyLoad has super simple wrapper support, which lets you quickly define wrapper classes that are automatically wrapped around their corresponding constants. These wrappers get a default `initialize` method, which sets `@wrapped` to the object they wrap, and are set to forward method calls there using `method_missing`.
|
75
86
|
|
76
87
|
```ruby
|
77
|
-
|
78
|
-
|
79
|
-
|
88
|
+
|
89
|
+
LazyLoad.wrapper(:RDiscount) do
|
90
|
+
|
91
|
+
def new(*args)
|
92
|
+
InstanceWrapper.new(super(*args))
|
80
93
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
94
|
+
|
95
|
+
class InstanceWrapper < LazyLoad::Wrapper
|
96
|
+
|
97
|
+
def render(*args); to_html(*args); end
|
98
|
+
|
99
|
+
def go_shopping
|
100
|
+
puts 'sure, why not'
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
86
105
|
end
|
106
|
+
|
87
107
|
```
|
88
108
|
|
109
|
+
|
110
|
+
|
111
|
+
|
89
112
|
Feedback and suggestions are welcome through Github.
|
90
113
|
|
91
114
|
---
|
data/lazy_load.gemspec
CHANGED
@@ -13,14 +13,12 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.version = LazyLoad::VERSION
|
14
14
|
s.platform = Gem::Platform::RUBY
|
15
15
|
|
16
|
-
s.summary = '
|
17
|
-
'and helpful errors.'
|
16
|
+
s.summary = 'Autoload with custom callbacks.'
|
18
17
|
|
19
|
-
s.description =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
'pollute the global namespace.'
|
18
|
+
s.description = %{LazyLoad is a slightly more elaborate alternative
|
19
|
+
to the autoload method. It provides custom callbacks,
|
20
|
+
"best available" dependency selection, and an optional
|
21
|
+
simple wrapper mechanism.}
|
24
22
|
|
25
23
|
s.files = `git ls-files`.split("\n")
|
26
24
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
data/lib/lazy_load.rb
CHANGED
@@ -3,86 +3,94 @@
|
|
3
3
|
|
4
4
|
module LazyLoad
|
5
5
|
|
6
|
-
VERSION = '0.0
|
6
|
+
VERSION = '0.1.0'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
module Methods
|
8
|
+
module Mixin
|
11
9
|
|
12
10
|
def reset!
|
13
|
-
@messages = {}
|
14
11
|
@actions = {}
|
15
12
|
@groups = {}
|
13
|
+
@wrappers = {}
|
16
14
|
self
|
17
15
|
end
|
18
16
|
|
17
|
+
def self.extended(obj)
|
18
|
+
obj.reset!
|
19
|
+
end
|
20
|
+
|
19
21
|
def scope(&blk)
|
20
|
-
mod = Module.new.extend(LazyLoad::
|
22
|
+
mod = Module.new.extend(LazyLoad::Mixin).reset!
|
21
23
|
mod.module_eval(&blk) if blk
|
22
24
|
mod
|
23
25
|
end
|
24
26
|
|
25
|
-
def
|
26
|
-
@messages[name] = msg
|
27
|
+
def const(name, action=nil, &blk)
|
27
28
|
@actions[name] = blk || action || raise(
|
28
29
|
ArgumentError, "missing require path or callback")
|
29
30
|
end
|
31
|
+
alias :dep :const
|
30
32
|
|
31
33
|
def group(name, *constants)
|
32
34
|
@groups[name] = constants
|
33
35
|
end
|
34
36
|
|
35
|
-
alias :dep :map
|
36
|
-
|
37
37
|
def unmap(name)
|
38
|
-
@messages.delete(name)
|
39
38
|
@actions.delete(name)
|
40
39
|
end
|
41
40
|
|
42
41
|
def const_missing(name)
|
43
42
|
k = case action = @actions[name]
|
44
|
-
when String then helpful_require(name)
|
45
43
|
when Proc then action.call
|
46
44
|
when nil then super
|
45
|
+
when String
|
46
|
+
require @actions[name]
|
47
|
+
Kernel.const_get(name)
|
47
48
|
else raise "Invalid action for dependency #{action.inspect}"
|
48
49
|
end
|
50
|
+
k = @wrappers[name].new(k) if @wrappers[name]
|
49
51
|
const_set(name, k)
|
50
52
|
end
|
51
53
|
|
52
|
-
def helpful_require(name)
|
53
|
-
begin
|
54
|
-
require @actions[name]
|
55
|
-
Kernel.const_get(name)
|
56
|
-
rescue LoadError
|
57
|
-
raise(DependencyError, @messages[name] ||
|
58
|
-
"failed to require #{@actions[name].inspect}.")
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
54
|
# Return the first available dependency from the
|
63
55
|
# list of constant names.
|
64
56
|
#
|
65
57
|
def best(*names)
|
66
|
-
|
58
|
+
names.map do |name|
|
59
|
+
@groups[name] || name
|
60
|
+
end.flatten.each do |name|
|
67
61
|
begin
|
68
62
|
return const_get(name)
|
69
|
-
rescue NameError; end
|
63
|
+
rescue NameError, LoadError; end
|
70
64
|
end
|
71
65
|
const_get(names.first)
|
72
66
|
end
|
73
67
|
alias :first_available :best
|
74
68
|
alias :[] :best
|
75
69
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
70
|
+
def wrap(name, &blk)
|
71
|
+
kls = Class.new(Wrapper)
|
72
|
+
kls.class_eval(&blk)
|
73
|
+
@wrappers[name] = kls
|
74
|
+
end
|
75
|
+
|
76
|
+
class Wrapper
|
77
|
+
def initialize(wrapped)
|
78
|
+
@wrapped = wrapped
|
79
|
+
end
|
80
|
+
attr_reader :wrapped
|
81
|
+
|
82
|
+
def method_missing(*args)
|
83
|
+
@wrapped.send(*args)
|
84
|
+
end
|
85
|
+
|
86
|
+
def respond_to?(name)
|
87
|
+
super || @wrapped.respond_to?(name)
|
88
|
+
end
|
80
89
|
end
|
81
90
|
|
82
91
|
end
|
83
92
|
|
84
|
-
extend
|
85
|
-
reset!
|
93
|
+
extend Mixin
|
86
94
|
|
87
95
|
end
|
88
96
|
|
data/test/lazy_load_test.rb
CHANGED
@@ -6,15 +6,28 @@ class LazyLoadTest < TestCase
|
|
6
6
|
# helpers:
|
7
7
|
|
8
8
|
def with_mappings(*args)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
|
10
|
+
mappings = Proc.new do
|
11
|
+
const(:StringIO, 'stringio')
|
12
|
+
const(:Bogus, 'thishastobebogusnow')
|
13
|
+
const(:Message) { 'love' }
|
14
|
+
const(:Boat) { 'sank' }
|
15
|
+
const(:Unavailable) { raise LoadError, 'not available' }
|
16
|
+
group(:test_grp, :Bogus, :Unavailable, :Message)
|
17
|
+
wrap(:Boat) { def actually; 'floats'; end }
|
14
18
|
end
|
15
|
-
|
16
|
-
|
19
|
+
|
20
|
+
LazyLoad.module_eval(&mappings)
|
21
|
+
yield(LazyLoad)
|
17
22
|
LazyLoad.reset!
|
23
|
+
|
24
|
+
yield LazyLoad.scope(&mappings)
|
25
|
+
|
26
|
+
mod = Module.new
|
27
|
+
mod.extend(LazyLoad::Mixin)
|
28
|
+
mod.module_eval(&mappings)
|
29
|
+
yield mod
|
30
|
+
|
18
31
|
end
|
19
32
|
|
20
33
|
|
@@ -27,29 +40,29 @@ class LazyLoadTest < TestCase
|
|
27
40
|
end
|
28
41
|
|
29
42
|
test 'requires existing' do
|
30
|
-
with_mappings do
|
31
|
-
assert
|
43
|
+
with_mappings do |ll|
|
44
|
+
assert ll::StringIO == StringIO
|
32
45
|
end
|
33
46
|
end
|
34
47
|
|
35
48
|
test 'require fails with message when unavailable' do
|
36
|
-
with_mappings do
|
37
|
-
assert_raises(
|
38
|
-
|
49
|
+
with_mappings do |ll|
|
50
|
+
assert_raises(LoadError) do
|
51
|
+
ll::Bogus
|
39
52
|
end
|
40
53
|
end
|
41
54
|
end
|
42
55
|
|
43
56
|
test 'callback' do
|
44
|
-
with_mappings do
|
45
|
-
assert
|
57
|
+
with_mappings do |ll|
|
58
|
+
assert ll::Message == "love"
|
46
59
|
end
|
47
60
|
end
|
48
61
|
|
49
62
|
test 'callback with dependency error' do
|
50
|
-
with_mappings do
|
51
|
-
assert_raises(
|
52
|
-
|
63
|
+
with_mappings do |ll|
|
64
|
+
assert_raises(LoadError) do
|
65
|
+
ll::Unavailable
|
53
66
|
end
|
54
67
|
end
|
55
68
|
end
|
@@ -58,27 +71,30 @@ class LazyLoadTest < TestCase
|
|
58
71
|
# first_available
|
59
72
|
|
60
73
|
test 'returns first when first available is first' do
|
61
|
-
with_mappings do
|
74
|
+
with_mappings do |ll|
|
62
75
|
assert_equal(
|
63
76
|
'love',
|
64
|
-
|
77
|
+
ll.best(:Message, :StringIO, :Unavailable)
|
65
78
|
)
|
66
79
|
end
|
67
80
|
end
|
68
81
|
|
69
82
|
test 'returns third when first available is third' do
|
70
|
-
with_mappings do
|
83
|
+
with_mappings do |ll|
|
71
84
|
assert_equal(
|
72
85
|
'StringIO',
|
73
|
-
|
86
|
+
ll.best(:Bogus, :Unavailable, :StringIO, :Message).name
|
74
87
|
)
|
75
88
|
end
|
76
89
|
end
|
77
90
|
|
78
91
|
test 'fails with first message when none available' do
|
79
|
-
with_mappings do
|
80
|
-
assert_raises
|
81
|
-
|
92
|
+
with_mappings do |ll|
|
93
|
+
assert_raises LoadError do
|
94
|
+
ll.best(:Bogus, :Air, :Unavailable, :Bogus)
|
95
|
+
end
|
96
|
+
assert_raises NameError do
|
97
|
+
ll.best(:Air, :Bogus, :Unavailable, :Bogus)
|
82
98
|
end
|
83
99
|
end
|
84
100
|
end
|
@@ -87,20 +103,25 @@ class LazyLoadTest < TestCase
|
|
87
103
|
# new_scope
|
88
104
|
|
89
105
|
test 'scope independent of source' do
|
90
|
-
with_mappings do
|
91
|
-
scope =
|
92
|
-
scope.
|
106
|
+
with_mappings do |ll|
|
107
|
+
scope = ll.scope
|
108
|
+
scope.const(:StringIO, 'not_anymore')
|
93
109
|
|
94
|
-
assert_equal('StringIO',
|
95
|
-
assert_raises(
|
96
|
-
scope::StringIO
|
97
|
-
end
|
110
|
+
assert_equal('StringIO', ll::StringIO.name)
|
111
|
+
assert_raises(LoadError) { scope::StringIO }
|
98
112
|
end
|
99
113
|
end
|
100
114
|
|
101
115
|
test 'groups' do
|
102
|
-
with_mappings do
|
103
|
-
assert_equal 'love',
|
116
|
+
with_mappings do |ll|
|
117
|
+
assert_equal 'love', ll[:test_grp]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
test 'wrapper' do
|
122
|
+
with_mappings do |ll|
|
123
|
+
assert_equal 'sank', ll::Boat.wrapped
|
124
|
+
assert_equal 'floats', ll::Boat.actually
|
104
125
|
end
|
105
126
|
end
|
106
127
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.3
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jostein Berre Eliassen
|
@@ -18,7 +18,11 @@ date: 2011-06-23 00:00:00 +02:00
|
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
21
|
-
description:
|
21
|
+
description: |-
|
22
|
+
LazyLoad is a slightly more elaborate alternative
|
23
|
+
to the autoload method. It provides custom callbacks,
|
24
|
+
"best available" dependency selection, and an optional
|
25
|
+
simple wrapper mechanism.
|
22
26
|
email: josteinpost@gmail.com
|
23
27
|
executables: []
|
24
28
|
|
@@ -67,7 +71,7 @@ rubyforge_project:
|
|
67
71
|
rubygems_version: 1.3.7
|
68
72
|
signing_key:
|
69
73
|
specification_version: 3
|
70
|
-
summary:
|
74
|
+
summary: Autoload with custom callbacks.
|
71
75
|
test_files:
|
72
76
|
- test/helper.rb
|
73
77
|
- test/lazy_load_test.rb
|