function_chain 0.0.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.
- 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
|
+
[][travis]
|
4
|
+
[][coveralls]
|
5
|
+
[][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
|