function_chain 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.rubocop.yml +41 -0
- data/.travis.yml +10 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +421 -0
- data/Rakefile +7 -0
- data/function_chain.gemspec +27 -0
- data/lib/function_chain/base_chain.rb +110 -0
- data/lib/function_chain/pull_chain.rb +297 -0
- data/lib/function_chain/relay_chain.rb +251 -0
- data/lib/function_chain/version.rb +3 -0
- data/lib/function_chain.rb +13 -0
- data/spec/function_chain_spec.rb +404 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/flay.rake +11 -0
- data/tasks/flog.rake +18 -0
- data/tasks/rspec.rake +3 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cceaae212ecd1dba66efa5822e61947b5af1bcfa
|
4
|
+
data.tar.gz: 09a02eeac6d159f127d851d615ee543cde23402b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05d5b0e5ffae56ce85c88b5459fe7c68580e6872e86e456f2da24a17775c91e4e502905efd14ad8f3df6bd495e39b55b72537a5ce5b415ea63c25f159659a226
|
7
|
+
data.tar.gz: 22c3ec7ae4db1128e6cc75f2580a18345c5b2c8e33a051a22f6b7fa3bf44976ace7eb65253219c9490c1368481a15d395b962060c0dc0d3f037f7e0d962bffb3
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
HashSyntax:
|
2
|
+
EnforcedStyle: ruby19
|
3
|
+
SupportedStyles:
|
4
|
+
- ruby19
|
5
|
+
- hash_rockets
|
6
|
+
|
7
|
+
Documentation:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
LineLength:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
ClassLength:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
RegexpLiteral:
|
17
|
+
MaxSlashes: 0
|
18
|
+
|
19
|
+
GuardClause:
|
20
|
+
MinBodyLength: 3
|
21
|
+
|
22
|
+
TrivialAccessors:
|
23
|
+
AllowPredicates: true
|
24
|
+
|
25
|
+
RedundantReturn:
|
26
|
+
AllowMultipleReturnValues: true
|
27
|
+
|
28
|
+
RedundantBegin:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
SpaceInsideHashLiteralBraces:
|
32
|
+
EnforcedStyle: no_space
|
33
|
+
|
34
|
+
Style/Lambda:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Style/Proc:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
StringLiterals:
|
41
|
+
EnforcedStyle: double_quotes
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gem "rake", ">= 0.9"
|
4
|
+
gem "rdoc", ">= 3.9"
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "guard-rubocop"
|
8
|
+
gem "terminal-notifier-guard"
|
9
|
+
gem "flay"
|
10
|
+
gem "flog"
|
11
|
+
end
|
12
|
+
|
13
|
+
group :test do
|
14
|
+
gem "coveralls", require: false
|
15
|
+
gem "rubocop", ">= 0.19"
|
16
|
+
gem "rspec", ">= 3"
|
17
|
+
gem "simplecov", ">= 0.9"
|
18
|
+
end
|
19
|
+
|
20
|
+
gemspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kenji Suzuki
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,421 @@
|
|
1
|
+
# FunctionChain
|
2
|
+
|
3
|
+
[![Build Status](http://img.shields.io/travis/pujoheadsoft/function_chain.svg)][travis]
|
4
|
+
[![Coverage Status](http://img.shields.io/coveralls/pujoheadsoft/function_chain.svg)][coveralls]
|
5
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/pujoheadsoft/function_chain.svg)][codeclimate]
|
6
|
+
|
7
|
+
[travis]: http://travis-ci.org/pujoheadsoft/function_chain
|
8
|
+
[coveralls]: https://coveralls.io/r/pujoheadsoft/function_chain
|
9
|
+
[codeclimate]: https://codeclimate.com/github/pujoheadsoft/function_chain
|
10
|
+
|
11
|
+
## Description
|
12
|
+
**FunctionChain** objectifies of the method chain.
|
13
|
+
Chain object can following.
|
14
|
+
* Call later.
|
15
|
+
* Add method to chain
|
16
|
+
* Insert method to chain.
|
17
|
+
* Delete method from chain.
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
gem install function_chain
|
21
|
+
|
22
|
+
## PullChain & RelayChain
|
23
|
+
**PullChain & RelayChain** is FunctionChain module's classes.
|
24
|
+
PullChain & RelayChain will support call chain type, each different.
|
25
|
+
|
26
|
+
* PullChain to support the call chain type such as the following:
|
27
|
+
```ruby
|
28
|
+
account.user.name
|
29
|
+
```
|
30
|
+
If used the PullChain (detail description is [here](#pullchain))
|
31
|
+
```ruby
|
32
|
+
chain = PullChain.new(account) << :user << :name
|
33
|
+
chain.call
|
34
|
+
```
|
35
|
+
|
36
|
+
* RelayChain to support the call chain type such as the following:
|
37
|
+
```ruby
|
38
|
+
filter3(filter2(filter1(value)))
|
39
|
+
```
|
40
|
+
If used the RelayChain (detail description is [here](#relaychain))
|
41
|
+
```ruby
|
42
|
+
chain = RelayChain.new >> :filter1 >> :filter2 >> :filter3
|
43
|
+
chain.call("XXX")
|
44
|
+
```
|
45
|
+
|
46
|
+
|
47
|
+
## Usage and documentation
|
48
|
+
The following is necessary call to use the PullChain or RelayChain.
|
49
|
+
```ruby
|
50
|
+
require "function_chain"
|
51
|
+
include FunctionChain
|
52
|
+
```
|
53
|
+
**Note:** This document omit the above code from now on.
|
54
|
+
## PullChain
|
55
|
+
PullChain is object as represent method call chain.
|
56
|
+
Can inner object's method call of object.
|
57
|
+
### Example
|
58
|
+
```ruby
|
59
|
+
Account = Struct.new(:user)
|
60
|
+
User = Struct.new(:name)
|
61
|
+
account = Account.new(User.new("Louis"))
|
62
|
+
|
63
|
+
chain = PullChain.new(account, :user, :name, :upcase)
|
64
|
+
chain.call # => LOUIS
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Similar
|
68
|
+
1. **Strings separated by a slash**
|
69
|
+
```ruby
|
70
|
+
PullChain.new(account, "user/name/upcase").call
|
71
|
+
```
|
72
|
+
|
73
|
+
2. **Use << operator**
|
74
|
+
```ruby
|
75
|
+
chain = PullChain.new(account)
|
76
|
+
chain << :user << :name << :upcase
|
77
|
+
chain.call
|
78
|
+
```
|
79
|
+
|
80
|
+
3. **Use add method**
|
81
|
+
```ruby
|
82
|
+
chain.add(:user).add(:name).add(:upcase).call
|
83
|
+
```
|
84
|
+
|
85
|
+
4. **Use add_all method**
|
86
|
+
```ruby
|
87
|
+
chain.add_all(:user, :name, :upcase).call
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Can exist nil value on the way, like a following case
|
91
|
+
```ruby
|
92
|
+
user.name = nil
|
93
|
+
chain.call # => nil
|
94
|
+
```
|
95
|
+
|
96
|
+
#### Insert, Delete, Clear
|
97
|
+
insert, insert_all method is insert_all method to chain.
|
98
|
+
delete_at method is delete method from chain.
|
99
|
+
clear method is delete all method from chain.
|
100
|
+
|
101
|
+
### Require arguments on method
|
102
|
+
Following example's method is require two arguments.
|
103
|
+
What should do in this case?
|
104
|
+
```ruby
|
105
|
+
class Foo
|
106
|
+
def say(speaker, message)
|
107
|
+
puts "#{speaker} said '#{message}'"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
```
|
111
|
+
###### Solution
|
112
|
+
1. **Array, format is [Symbol, [\*Args]]**
|
113
|
+
```ruby
|
114
|
+
chain = PullChain.new(Foo.new) << [:say, ["Andres", "Hello"]]
|
115
|
+
chain.call # => Andres said 'Hello'
|
116
|
+
```
|
117
|
+
|
118
|
+
2. **String**
|
119
|
+
```ruby
|
120
|
+
chain = PullChain.new(foo) << "say('John', 'Goodbye')"
|
121
|
+
chain.call # => John said 'Goodbye'
|
122
|
+
```
|
123
|
+
|
124
|
+
### Require block on method
|
125
|
+
Following example's method is require Block.
|
126
|
+
What should do in this case?
|
127
|
+
```ruby
|
128
|
+
[1,2,3,4,5].inject(3) { |sum, n| sum + n } # => 18
|
129
|
+
```
|
130
|
+
|
131
|
+
###### Solution
|
132
|
+
|
133
|
+
1. **Array, format is [Symbol, [\*Args, Proc]].**
|
134
|
+
```ruby
|
135
|
+
chain = PullChain.new([1,2,3,4,5])
|
136
|
+
chain << [:inject, [3, lambda { |sum, n| sum + n }]]
|
137
|
+
chain.call # => 18
|
138
|
+
```
|
139
|
+
|
140
|
+
2. **String**
|
141
|
+
```ruby
|
142
|
+
chain = PullChain.new([1,2,3,4,5])
|
143
|
+
chain << "inject(3) { |sum, n| sum + n }"
|
144
|
+
chain.call # => 18
|
145
|
+
```
|
146
|
+
|
147
|
+
### Use result on chain
|
148
|
+
Like a following example, can use result on chain.
|
149
|
+
```ruby
|
150
|
+
Foo = Struct.new(:bar)
|
151
|
+
Bar = Struct.new(:baz) {
|
152
|
+
def speaker
|
153
|
+
"Julian"
|
154
|
+
end
|
155
|
+
}
|
156
|
+
class Baz
|
157
|
+
def say(speaker, message)
|
158
|
+
puts "#{speaker} said '#{message}'"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
foo = Foo.new(Bar.new(Baz.new))
|
162
|
+
```
|
163
|
+
###### Example: use result on chain
|
164
|
+
|
165
|
+
1. **String**
|
166
|
+
Can use bar instance in backward!
|
167
|
+
```ruby
|
168
|
+
chain = PullChain.new(foo) << "bar/baz/say(bar.speaker, 'Good!')"
|
169
|
+
chain.call # => Julian said 'Good!'
|
170
|
+
```
|
171
|
+
Furthermore, can use variable name assigned.
|
172
|
+
@b is bar instance alias.
|
173
|
+
```ruby
|
174
|
+
chain = PullChain.new(foo) << "@b = bar/baz/say(b.speaker, 'Cool')"
|
175
|
+
chain.call # => Julian said 'Cool'
|
176
|
+
```
|
177
|
+
|
178
|
+
2. **Array**
|
179
|
+
Can access result by Proc.
|
180
|
+
```ruby
|
181
|
+
chain = PullChain.new(foo) << :bar << :baz
|
182
|
+
chain << [:say, Proc.new { next bar.speaker, "Oh" }]
|
183
|
+
chain.call # => Julian said 'Oh'
|
184
|
+
```
|
185
|
+
Case of use a lambda, can use result access object explicit.
|
186
|
+
```ruby
|
187
|
+
chain = PullChain.new(foo) << :bar << :baz
|
188
|
+
arg_reader = lambda { |accessor| next accessor.bar.speaker, "Oh" }
|
189
|
+
chain << [:say, arg_reader]
|
190
|
+
chain.call # => Julian said 'Oh'
|
191
|
+
```
|
192
|
+
|
193
|
+
#### etc
|
194
|
+
1. **How to use slash in strings separated by a slash**
|
195
|
+
Like following, please escaped by backslash.
|
196
|
+
```ruby
|
197
|
+
chain = PullChain.new("AC") << "concat '\\/DC'"
|
198
|
+
chain.call # => AC/DC
|
199
|
+
```
|
200
|
+
|
201
|
+
2. **Use return_nil_at_error= method, then can ignore error**
|
202
|
+
```ruby
|
203
|
+
chain = PullChain.new("Test") << :xxx
|
204
|
+
begin
|
205
|
+
chain.call # => undefined method `xxx'
|
206
|
+
rescue
|
207
|
+
end
|
208
|
+
chain.return_nil_at_error = true
|
209
|
+
chain.call # => nil
|
210
|
+
```
|
211
|
+
|
212
|
+
3. **Note:use operator in chain**
|
213
|
+
* **String type chain**
|
214
|
+
```ruby
|
215
|
+
table = {name: %w(Bill Scott Paul)}
|
216
|
+
PullChain.new(table, "[:name]").call # => [:name] NG
|
217
|
+
PullChain.new(table, "self[:name]").call # => ["Bill", "Scott", "Paul"] OK
|
218
|
+
```
|
219
|
+
|
220
|
+
* **Array type chain**
|
221
|
+
```ruby
|
222
|
+
PullChain.new(table, [:[], [:name]]).call # OK
|
223
|
+
```
|
224
|
+
|
225
|
+
Following is also the same.
|
226
|
+
|
227
|
+
* **<< operator of String**
|
228
|
+
```ruby
|
229
|
+
PullChain.new("Led", "<< ' Zeppelin'").call # NG syntax error
|
230
|
+
PullChain.new("Led", "self << ' Zeppelin'").call # => "Led Zeppelin"
|
231
|
+
```
|
232
|
+
|
233
|
+
* **[] operator of Array**
|
234
|
+
```ruby
|
235
|
+
PullChain.new(%w(Donald Walter), "[1]").call # NG => [1]
|
236
|
+
PullChain.new(%w(Donald Walter), "self[1]").call # OK => Walter
|
237
|
+
```
|
238
|
+
|
239
|
+
4. **Some classes, such Fixnum and Bignum not supported**
|
240
|
+
```ruby
|
241
|
+
PullChain.new(999999999999999, "self % 2").call # NG
|
242
|
+
```
|
243
|
+
|
244
|
+
---
|
245
|
+
## RelayChain
|
246
|
+
RelayChain is object like a connect to function's input from function's output.
|
247
|
+
(methods well as can connect Proc.)
|
248
|
+
|
249
|
+
### Example
|
250
|
+
```ruby
|
251
|
+
class Decorator
|
252
|
+
def decorate1(value)
|
253
|
+
"( #{value} )"
|
254
|
+
end
|
255
|
+
def decorate2(value)
|
256
|
+
"{ #{value} }"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
chain = RelayChain.new(Decorator.new, :decorate1, :decorate2)
|
261
|
+
chain.call("Hello") # => { ( Hello ) }
|
262
|
+
```
|
263
|
+
#### Similar
|
264
|
+
1. **Strings separated by a slash**
|
265
|
+
```ruby
|
266
|
+
chain = RelayChain.new(Decorator.new, "decorate1/decorate2")
|
267
|
+
chain.call("Hello")
|
268
|
+
```
|
269
|
+
|
270
|
+
2. **Use >> operator**
|
271
|
+
```ruby
|
272
|
+
chain = RelayChain.new(Decorator.new)
|
273
|
+
chain >> :decorate1 >> :decorate2
|
274
|
+
chain.call("Hello")
|
275
|
+
```
|
276
|
+
|
277
|
+
3. **Use Method object**
|
278
|
+
```ruby
|
279
|
+
chain = RelayChain.new
|
280
|
+
chain >> decorator.method(:decorate1) >> decorator.method(:decorate2)
|
281
|
+
chain.call("Hello")
|
282
|
+
```
|
283
|
+
|
284
|
+
4. **Use add method**
|
285
|
+
```ruby
|
286
|
+
chain.add(:decorate1).add(:decorate2).call("Hello")
|
287
|
+
```
|
288
|
+
|
289
|
+
5. **Use add_all method**
|
290
|
+
```ruby
|
291
|
+
chain.add_all(:decorate1, :decorate2).call("Hello")
|
292
|
+
```
|
293
|
+
|
294
|
+
#### Insert, Delete, Clear
|
295
|
+
insert, insert_all method is insert function to chain.
|
296
|
+
delete_at method is delete function from chain.
|
297
|
+
clear method is delete all function from chain.
|
298
|
+
|
299
|
+
### How to connect method of differed instance
|
300
|
+
Example, following two class.
|
301
|
+
How to connect method of these class?
|
302
|
+
```ruby
|
303
|
+
class Decorator
|
304
|
+
def decorate1(value)
|
305
|
+
"( #{value} )"
|
306
|
+
end
|
307
|
+
def decorate2(value)
|
308
|
+
"{ #{value} }"
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
class Decorator2
|
313
|
+
def decorate(value)
|
314
|
+
"[ #{value} ]"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
```
|
318
|
+
###### Solution
|
319
|
+
1. **Array, format is [instance, Symbol or String of method]**
|
320
|
+
```ruby
|
321
|
+
# Symbol ver.
|
322
|
+
chain = RelayChain.new(Decorator.new)
|
323
|
+
chain >> :decorate1 >> :decorate2 >> [Decorator2.new, :decorate]
|
324
|
+
chain.call("Hello") # => [ { ( Hello ) } ]
|
325
|
+
|
326
|
+
# String ver.
|
327
|
+
chain = RelayChain.new(Decorator.new)
|
328
|
+
chain >> :decorate1 >> :decorate2 >> [Decorator2.new, "decorate"]
|
329
|
+
chain.call("Hello") # => [ { ( Hello ) } ]
|
330
|
+
```
|
331
|
+
|
332
|
+
2. **String, use registered instance**
|
333
|
+
```ruby
|
334
|
+
chain = RelayChain.new(Decorator.new)
|
335
|
+
|
336
|
+
# register name and instance
|
337
|
+
chain.add_receiver("d2", Decorator2.new)
|
338
|
+
|
339
|
+
# use registered instance
|
340
|
+
chain >> "/decorate1/decorate2/d2.decorate"
|
341
|
+
chain.call("Hello") # => [ { ( Hello ) } ]
|
342
|
+
|
343
|
+
# add_receiver_table method is register name and instance at once.
|
344
|
+
chain.add_receiver_table({"x" => X.new, "y" => Y.new})
|
345
|
+
```
|
346
|
+
|
347
|
+
### Case of method's output and method's input mismatch
|
348
|
+
Following example, decorate output is 1, and union input is 2.
|
349
|
+
How to do connect these methods?
|
350
|
+
```ruby
|
351
|
+
class Decorator
|
352
|
+
def decorate(value)
|
353
|
+
"#{value} And"
|
354
|
+
end
|
355
|
+
def union(value1, value2)
|
356
|
+
"#{value1} #{value2}"
|
357
|
+
end
|
358
|
+
end
|
359
|
+
```
|
360
|
+
###### Solution
|
361
|
+
1. **Define connect method.**
|
362
|
+
```ruby
|
363
|
+
class Decorator
|
364
|
+
def connect(value)
|
365
|
+
return value, "Palmer"
|
366
|
+
end
|
367
|
+
end
|
368
|
+
chain = RelayChain.new(Decorator.new)
|
369
|
+
chain >> :decorate >> :connect >> :union
|
370
|
+
chain.call("Emerson, Lake") # => Emerson, Lake And Palmer
|
371
|
+
```
|
372
|
+
|
373
|
+
2. **Add lambda or Proc to between these methods.**
|
374
|
+
lambda's format is following.
|
375
|
+
```ruby
|
376
|
+
# parameter: chain is chain object.
|
377
|
+
# parameter: \*args is previous functions output.
|
378
|
+
lambda {|chain, *args| chain.call(next functions arguments) }.
|
379
|
+
```
|
380
|
+
can call next function by chain object.
|
381
|
+
```ruby
|
382
|
+
chain = RelayChain.new(Decorator.new)
|
383
|
+
arg_adder = lambda { |chain, value| chain.call(value, "Jerry") }
|
384
|
+
chain >> :decorate >> arg_adder >> :union
|
385
|
+
chain.call("Tom") # => Tom And Jerry
|
386
|
+
```
|
387
|
+
|
388
|
+
### Appendix
|
389
|
+
**Chain stop by means of lambda.**
|
390
|
+
```ruby
|
391
|
+
class Decorator
|
392
|
+
def decorate1(value)
|
393
|
+
"( #{value} )"
|
394
|
+
end
|
395
|
+
|
396
|
+
def decorate2(value)
|
397
|
+
"{ #{value} }"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def create_stopper(&stop_condition)
|
402
|
+
lambda do |chain, value|
|
403
|
+
# if stop conditions are met then return value
|
404
|
+
if stop_condition.call(value)
|
405
|
+
value
|
406
|
+
else
|
407
|
+
chain.call(value)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
chain = RelayChain.new(Decorator.new, :decorate1, :decorate2)
|
413
|
+
|
414
|
+
# insert_all conditional chain stopper
|
415
|
+
chain.insert(1, create_stopper { |value| value =~ /\d/ })
|
416
|
+
chain.call("Van Halen 1984") # => ( Van Halen 1984 ) not enclosed to {}
|
417
|
+
chain.call("Van Halen Jump") # => { ( Van Halen Jump ) } enclosed to {}
|
418
|
+
```
|
419
|
+
|
420
|
+
## License
|
421
|
+
Released under the MIT License.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "function_chain/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "function_chain"
|
8
|
+
spec.version = FunctionChain::VERSION
|
9
|
+
spec.authors = ["Kenji Suzuki"]
|
10
|
+
spec.email = ["pujoheadsoft@gmail.com"]
|
11
|
+
spec.summary = "FunctionChain objectifies of the method chain."
|
12
|
+
spec.description = <<-EOF.gsub(/^\s+|\n/, "")
|
13
|
+
FunctionChain objectifies of the method chain.
|
14
|
+
chain objects can call later or add chain or insert chain or delete chain.
|
15
|
+
supported chain type is following: foo.bar.baz, baz(bar(foo(value))).
|
16
|
+
EOF
|
17
|
+
spec.homepage = "https://github.com/pujoheadsoft/function_chain"
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.required_ruby_version = ">= 1.9.3"
|
26
|
+
spec.required_rubygems_version = ">= 1.3.5"
|
27
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module FunctionChain
|
2
|
+
# Base class of PullChain, RelayChain
|
3
|
+
class BaseChain
|
4
|
+
# Add functions to chain
|
5
|
+
def add_all(*functions)
|
6
|
+
insert_all(chain_elements.size, *functions)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
# Add function to chain
|
11
|
+
def add(function)
|
12
|
+
insert(chain_elements.size, function)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Insert functions to chain
|
16
|
+
def insert_all(index, *functions)
|
17
|
+
functions.each_with_index { |f, i| insert(index + i, f) }
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Insert function to chain
|
22
|
+
def insert(index, function)
|
23
|
+
if function.is_a? String
|
24
|
+
do_insert_by_string(index, function)
|
25
|
+
else
|
26
|
+
do_insert(index, function)
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Delete from chain
|
32
|
+
def delete_at(index)
|
33
|
+
chain_elements.delete_at(index)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Clear function chain
|
38
|
+
def clear
|
39
|
+
chain_elements.clear
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"#{self.class}#{chain_elements.map(&:to_s)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def do_insert(index, function)
|
49
|
+
chain_element = create_chain_element(function)
|
50
|
+
chain_elements.insert(index, chain_element)
|
51
|
+
def_to_s(chain_element, function)
|
52
|
+
end
|
53
|
+
|
54
|
+
def do_insert_by_string(index, function)
|
55
|
+
function.split(%r{(?<!\\)/}).reject(&:empty?).each_with_index do |f, i|
|
56
|
+
splitted_function = f.gsub(%r{\\/}, "/")
|
57
|
+
chain_element = create_chain_element(splitted_function)
|
58
|
+
chain_elements.insert(index + i, chain_element)
|
59
|
+
def_to_s(chain_element, splitted_function)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_chain_element(function)
|
64
|
+
case function
|
65
|
+
when Symbol then create_chain_element_by_symbol(function)
|
66
|
+
when Array then create_chain_element_by_array(function)
|
67
|
+
when String then create_chain_element_by_string(function)
|
68
|
+
else
|
69
|
+
fail ArgumentError, <<-EOF.gsub(/^\s+|\n/, "")
|
70
|
+
Not supported type #{function}(#{function.class}),
|
71
|
+
supported type is #{supported_types}.
|
72
|
+
EOF
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def def_to_s(target, value)
|
77
|
+
target.singleton_class.class_eval do
|
78
|
+
define_method :to_s do
|
79
|
+
value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_array_length(arr, expect_length, expect_format)
|
85
|
+
unless expect_length == arr.length
|
86
|
+
message = "Format Wrong #{arr}, expected format is [#{expect_format}]"
|
87
|
+
fail ArgumentError, message
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_element_type_of_array(arr, i, expect_types, types_as_string)
|
92
|
+
unless target_is_a_one_of_types?(arr[i], expect_types)
|
93
|
+
fail ArgumentError, "Format Wrong #{arr}," \
|
94
|
+
" second element of array is must be #{types_as_string}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def target_is_a_one_of_types?(target, types)
|
99
|
+
types.any? { |type| target.is_a? type }
|
100
|
+
end
|
101
|
+
|
102
|
+
def chain_elements
|
103
|
+
@chain_elements ||= []
|
104
|
+
end
|
105
|
+
|
106
|
+
def supported_types
|
107
|
+
[Symbol, Array, String]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|