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 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