forwarder 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: