forwarder 0.1.0 → 0.1.1

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.
Files changed (4) hide show
  1. data/README.md +100 -19
  2. data/lib/forwarder.rb +7 -0
  3. data/lib/forwarder/VERSION.rb +3 -0
  4. metadata +29 -50
data/README.md CHANGED
@@ -17,16 +17,36 @@ first two replace and enhance the functionality of ```def_delegator``` and
17
17
  ```def_delegators```, while the third is a kind of a ```alias_method``` on
18
18
  steroids.
19
19
 
20
+ ## Parameters ##
21
+
22
+ The first parameter, (or paramters in the case of `forward_all`) is (are)
23
+ a symbol(s) or string(s) indicating the message to be forwarded. That is
24
+ a message of which the receiver is an instance of the module in which
25
+ `forward` was called.
26
+
27
+ Ater this we have a hash style parameter which needs a *target* specification,
28
+ indicated by `:to`, `:to_chain` or `:to_object`. It can contain an optional
29
+ `:as` parameter translating the method name and an equally optional `:with`
30
+ paramter allowing us to provide paramters to the forwarded message.
31
+ I refer to the `:as` parameter as the *translation* and the `:with` parameter
32
+ as the *parametrization*.
33
+
34
+ This might be confusing at first, but the follwing examples shall demonstrate
35
+ how simple things really are.
36
+
37
+
20
38
  ## Examples ##
21
39
 
22
- * forward
40
+ ### The forward Method ###
41
+
42
+ #### Target specified with :to ####
23
43
 
24
- Forwards to a target. The target must be specified by the ```:to``` keyword
25
- parameter and can be either a ```Symbol``` (or ```String```), thus representing
26
- an instance method or an instance variable, a lambda that will be evaluated
27
- in the instance's context. If an arbitrary object shall be the receiver of the
28
- message, than the ```:to``` keyword can be replaced by the ```:to_object```
29
- keyword parameter.
44
+ The `:to` keyword parameter and can be either a `Symbol` (or `String`), thus representing
45
+ an instance method or an instance variable of the receiver. It can also be a lambda that
46
+ will be evaluated in the receiver's context. If an arbitrary object shall be the receiver of the
47
+ message, than the `:to` keyword can be replaced by the `:to_object`, and if the target of
48
+ the message shall bet the result of chained method calls on the receiver `:to_chain` is
49
+ at your service.
30
50
 
31
51
  class Employee
32
52
  extend Forwarder
@@ -37,7 +57,9 @@ This design, implementing some wishful thinking that will probably not pass
37
57
  acceptance tests, will forward the ```complaints``` message, sent to an instance
38
58
  of ```Employee```, to the object returned by this very instance's ```boss``` method.
39
59
 
40
- The following adjustment was made, in desperate hope to fix the *bug*:
60
+ As feared the implementation did not live up to the expectations (hence the desperate
61
+ need of foraml specifications) and the following adjustment was made, in some desperate
62
+ hope to fix the *bug*:
41
63
 
42
64
  class Employee
43
65
  extend Forwarder
@@ -46,18 +68,25 @@ The following adjustment was made, in desperate hope to fix the *bug*:
46
68
 
47
69
  This behavior being clearly preferable to the one implemented before because the
48
70
  receiver of ```complaints``` is still forwarding the call to the result of the
49
- call of its ```boss``` method, but to the ```suggestion``` method.
71
+ call of its ```boss``` method, but to it's `suggestions` method. (Well that is
72
+ not precise wording, but we shall make an abstraction about how the object returned
73
+ by `boss` handles the `suggestions` message.)
50
74
 
51
75
  Finally, however, the implementation looked like this
52
76
 
53
77
  class Boss
54
78
  extend Forwarder
55
- forward_all :complaints, :problems, :tasks, to: first_employee
79
+ forward :complaints, to: first_employee
80
+ forward :problems, to: first_employee
81
+ forward :tasks, to: first_employee
56
82
  end
57
83
 
58
- However this did not work as no ```first_employee``` was defined. This seems
59
- simple enough a task, so that a method for this seems too much code bloat, here
60
- are two possible implementations with ```Forwarder```
84
+ However this did not work as no `first_employee` was defined yet. This seems
85
+ a task so simple that a method for this seems almost too much code.
86
+ Forwarder let us allow an implementation on itself.
87
+ The other thing that catches (or should, at least) the reader's eye is the terrible code repetition.
88
+ To get rid of it, we will indulge us by looking ahead to the `forward_all` method, which of course
89
+ is just short for three `forward` calls with each of its positional parameters.
61
90
 
62
91
  class Boss
63
92
  extend Forwarder
@@ -65,12 +94,23 @@ are two possible implementations with ```Forwarder```
65
94
  forward_all :complaints, :problems, :tasks, to: :first_employee
66
95
  end
67
96
 
97
+ Here we see the first use case of a *parametrization*, paired with a *translation*.
98
+ Please note that the first does not necessarily imply the second, the following example
99
+ might be reasonable code.
100
+
101
+ class Train
102
+ extend Forwarder
103
+ forward :signal, to: :@signaller, with: {strength: 10, tone: 42}
104
+ end
105
+
68
106
  As a side note, I do not enourage the exposure of instance variables as in the
69
- example above, but I do not like to impose. As ```Forwardable``` can delegate to
70
- instance variables I decided to provide the same functionality with
71
- ```Forwarder```.
107
+ examples above, but it still might make your code shorter, which is an asset
108
+ of its own of course. Furthermore it allows a faster transation from `Forwardable`
109
+ if it is used to delegate to instance variables.
72
110
 
73
- Or alternatively
111
+ The above `Boss` case was badely written of course as `Array#first` gives us the
112
+ perfect opportunity to get rid of the `with:` parameter, which is somehow a little
113
+ bit of a code smell, I admit. Let us do better:
74
114
 
75
115
  class Boss
76
116
  extend Forwarder
@@ -79,14 +119,55 @@ Or alternatively
79
119
  end
80
120
 
81
121
 
82
- The above, however is a little bit verbose, we can shorten it with the `:to_chain`
83
- parameter
122
+ ### forward_all ###
123
+
124
+ `forward_all` allows us to forward more then one message to a target. It is a shortcut
125
+ for calling `forward` to each of its method parameters. As one can see in the next
126
+ example it supports all kinds of target parameters, :to, :to_chain, but also :to_object
127
+
128
+ #### Target :to_chain ####
129
+
130
+ The example above is still too verbose. For what we know there is no need to define a
131
+ delagation for the `first_employee` method. And this is the use case where `:to_chain`
132
+ seems the right tool to use, let us see it's application at work:
84
133
 
85
134
  class Boss
86
135
  extend Forwarder
87
136
  forward_all :complaints, :problems, :tasks, to_chain: [:@employees, :first]
88
137
  end
89
138
 
139
+ Now we forward, almost anonymously to `@employeees.first` without defining a method, or
140
+ forwarder to bridge this gap.
141
+
142
+ As you might guess, the `complaints` message is sent to the result of sending `first`
143
+ to the `@employees` instance variable. As (no pun intended) with the `to:` version
144
+ of `forward`, one can change the message name with the `as:` parameter, aka *translation*.
145
+
146
+ It is uncommon, but not impossible to use a *translation* in `forward_all`
147
+
148
+ class Boss
149
+ extend Forwarder
150
+ forward_all :complaints, :problems, :tasks,
151
+ to_chain: [:@employees, :first],
152
+ as: :request
153
+ end
154
+
155
+ Here we go, seems quite a realistic model to me.
156
+
157
+
158
+ ## Performance ##
159
+
160
+ If you are concerned about performance, but you should not yet, I have good news for you. Using
161
+ `Forwarder` will be a performance hit. Now why should that be good news? Well it is good news
162
+ for two reasons. Firstly by using `Forwarder` the performance hit notwithstanding you show that
163
+ you are not concerned by premature optimization but much more with clean, concise and readnale
164
+ design. Secondly if you run into performance issues and profiling shows that a forward target
165
+ is hit frequently, chances are that you found one of your performance bottlenecks. Just implement
166
+ the forward manually as a method and you shoud see quite some improvement. Now I am sure
167
+ you'd wish that all your performance issues are *that* *easy* to fix.
168
+
169
+ As I said: *Two* pieces of Good News!
170
+
90
171
  ## License ##
91
172
 
92
173
  This software is licensed under the MIT license, which shall be attached to any deliverable of
@@ -1,8 +1,10 @@
1
1
  require 'forwardable'
2
2
 
3
+ require 'forwarder/VERSION'
3
4
  require 'forwarder/meta'
4
5
  module Forwarder
5
6
 
7
+ # delegates (forwards) a message to an object (indicated by :to)
6
8
  def forward message, opts={}, &blk
7
9
  opts = parse_opts opts, blk
8
10
  # p opts: opts
@@ -24,11 +26,15 @@ module Forwarder
24
26
 
25
27
  private
26
28
 
29
+ # Triggered by the presence of :to_object in forward's parameters
27
30
  def forwarding_to_object message, opts
28
31
  target = opts[ :to_object ]
29
32
  forwarding_with message, opts.merge( to: Meta::ObjectContainer.new(target), with: opts.fetch( :with, [] ) )
30
33
  end
31
34
 
35
+ # Handles the cases, where Forwardable can be used behind the scenes
36
+ # as a matter of fact :to was indicating a method or instance variable
37
+ # and :as was passed in (or defaults to the original message).
32
38
  def forward_with_forwardable message, opts
33
39
  to = opts.fetch :to
34
40
  extend Forwardable
@@ -36,6 +42,7 @@ module Forwarder
36
42
  def_delegator to, as, message
37
43
  end
38
44
 
45
+ # Triggered by the presence of :to_chain in forward's parameters
39
46
  def forward_with_chain message, opts
40
47
  return false unless opts[:to_chain]
41
48
  forwarding_with message, opts
@@ -0,0 +1,3 @@
1
+ module Forwarder
2
+ VERSION = "0.1.1"
3
+ end # module Forwarder
metadata CHANGED
@@ -1,77 +1,56 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: forwarder
3
- version: !ruby/object:Gem::Version
4
- hash: 27
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 0
10
- version: 0.1.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Robert Dober
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-01-22 00:00:00 +01:00
19
- default_executable:
12
+ date: 2012-01-24 00:00:00.000000000Z
20
13
  dependencies: []
21
-
22
- description: |-
23
- Ruby's core Forwardable gets the job done(barely) and produces most unreadable code.
24
- This is a nonintrusive (as is Forwardable) module that allos to delegate methods to instance variables,
25
- objects returned by instance_methods, other methods of the same receiver (method_alias on steroids)
26
- and some more sophisticated use cases
14
+ description: ! "Ruby's core Forwardable gets the job done(barely) and produces most
15
+ unreadable code.\n This is a nonintrusive (as is Forwardable) module that allos
16
+ to delegate methods to instance variables,\n objects returned by instance_methods,
17
+ other methods of the same receiver (method_alias on steroids)\n and some more
18
+ sophisticated use cases"
27
19
  email: robert.dober@gmail.com
28
20
  executables: []
29
-
30
21
  extensions: []
31
-
32
22
  extra_rdoc_files: []
33
-
34
- files:
35
- - lib/forwarder/meta.rb
36
- - lib/forwarder/helpers.rb
23
+ files:
37
24
  - lib/forwarder.rb
25
+ - lib/forwarder/VERSION.rb
26
+ - lib/forwarder/helpers.rb
27
+ - lib/forwarder/meta.rb
38
28
  - LICENSE
39
29
  - README.md
40
- has_rdoc: true
41
30
  homepage: https://github.com/RobertDober/Forwarder
42
- licenses:
43
- - - MIT
31
+ licenses:
32
+ - MIT
44
33
  post_install_message:
45
34
  rdoc_options: []
46
-
47
- require_paths:
35
+ require_paths:
48
36
  - lib
49
- required_ruby_version: !ruby/object:Gem::Requirement
37
+ required_ruby_version: !ruby/object:Gem::Requirement
50
38
  none: false
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- hash: 57
55
- segments:
56
- - 1
57
- - 8
58
- - 7
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
59
42
  version: 1.8.7
60
- required_rubygems_version: !ruby/object:Gem::Requirement
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
44
  none: false
62
- requirements:
63
- - - ">="
64
- - !ruby/object:Gem::Version
65
- hash: 3
66
- segments:
67
- - 0
68
- version: "0"
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
69
49
  requirements: []
70
-
71
50
  rubyforge_project:
72
- rubygems_version: 1.3.7
51
+ rubygems_version: 1.8.10
73
52
  signing_key:
74
53
  specification_version: 3
75
54
  summary: Making Delegation finally readable
76
55
  test_files: []
77
-
56
+ has_rdoc: