lazy_load 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,7 +1,13 @@
1
1
 
2
2
  HEAD
3
3
 
4
-
4
+ 0.1.0
5
+ deprecate custom msgs and errors
6
+ let LoadErrors pass
7
+ deprecate #map
8
+ alias #dep to #const
9
+ LazyLoad::Mixin
10
+ wrapper support
5
11
 
6
12
  0.0.3
7
13
  alias #map to #dep
data/README.md CHANGED
@@ -2,13 +2,17 @@
2
2
  LazyLoad
3
3
  ========
4
4
 
5
- **Unobtrusively autoload code with callbacks and helpful errors**
5
+ **Autoload with custom callbacks**
6
6
 
7
7
  ---
8
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.
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
- 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.
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
- LazyLoad.map(:Tilt, 'tilt',
24
- 'Tilt not found. Possible fix: gem install tilt')
27
+ # same as as autoload:
25
28
 
26
- # or equivalent with a callback:
29
+ LazyLoad.const(:Tilt, 'tilt')
27
30
 
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
31
+ # or the equivalent with a callback:
37
32
 
38
- Tilt
39
- # => NameError: uninitialized constant Object::Tilt
33
+ LazyLoad.const(:Tilt) do
34
+ require 'tilt'
35
+ Tilt
36
+ end
40
37
 
41
- LazyLoad::Tilt
42
- # => Tilt
38
+ # lets you..
43
39
 
44
- # or if Tilt is not available:
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::Foo
50
- # => NameError: uninitialized constant LazyLoad::Foo
42
+ LazyLoad::Oboe
43
+ # => NameError: uninitialized constant LazyLoad::Oboe
51
44
 
52
45
  ```
53
46
 
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 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 from a list of names, or else raise a `LazyLoad::DependencyError` for the first (most preferred) one.
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
- ### Scopes
77
+ Haml # => Haml
73
78
 
74
- Use the `scope` method if you need more than one scope (typically for gem interoperability).
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
- module SomeProject
78
- Lazy = LazyLoad.scope do
79
- map(:StringIO, 'stringio')
88
+
89
+ LazyLoad.wrapper(:RDiscount) do
90
+
91
+ def new(*args)
92
+ InstanceWrapper.new(super(*args))
80
93
  end
81
-
82
- Lazy.map(:Tilt, 'tilt')
83
-
84
- Lazy::StringIO # => StringIO
85
- Lazy::Tilt # => Tilt
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
+ &nbsp;
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 = 'An unobtrusive way to autoload code with callbacks ' +
17
- 'and helpful errors.'
16
+ s.summary = 'Autoload with custom callbacks.'
18
17
 
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.'
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.3'
6
+ VERSION = '0.1.0'
7
7
 
8
- class DependencyError < NameError; end
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::Methods).reset!
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 map(name, action=nil, msg=nil, &blk)
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
- expand_groups(names).each do |name|
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 expand_groups(names)
77
- names.map do |name|
78
- @groups[name] || name
79
- end.flatten
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 Methods
85
- reset!
93
+ extend Mixin
86
94
 
87
95
  end
88
96
 
@@ -6,15 +6,28 @@ class LazyLoadTest < TestCase
6
6
  # helpers:
7
7
 
8
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'
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
- LazyLoad.group(:test_grp, :Bogus, :Unavailable, :Message)
16
- yield
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 LazyLoad::StringIO == StringIO
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(LazyLoad::DependencyError) do
38
- LazyLoad::Bogus
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 LazyLoad::Message == "love"
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(LazyLoad::DependencyError) do
52
- LazyLoad::Unavailable
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
- LazyLoad.best(:Message, :StringIO, :Unavailable)
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
- LazyLoad.best(:Bogus, :Unavailable, :StringIO, :Message).name
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 LazyLoad::DependencyError do
81
- LazyLoad.best(:Bogus, :Unavailable, :Bogus)
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 = LazyLoad.scope
92
- scope.map(:StringIO, 'not_anymore')
106
+ with_mappings do |ll|
107
+ scope = ll.scope
108
+ scope.const(:StringIO, 'not_anymore')
93
109
 
94
- assert_equal('StringIO', LazyLoad::StringIO.name)
95
- assert_raises(LazyLoad::DependencyError) do
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', LazyLoad[:test_grp]
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
- - 3
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: 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.
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: An unobtrusive way to autoload code with callbacks and helpful errors.
74
+ summary: Autoload with custom callbacks.
71
75
  test_files:
72
76
  - test/helper.rb
73
77
  - test/lazy_load_test.rb