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
@@ -0,0 +1,297 @@
|
|
1
|
+
require "function_chain/base_chain"
|
2
|
+
|
3
|
+
module FunctionChain
|
4
|
+
# == PullChain
|
5
|
+
# PullChain is object as represent method call chain.
|
6
|
+
# Can inner object's method call of object.
|
7
|
+
#
|
8
|
+
# Chain is object, so can call later.
|
9
|
+
#
|
10
|
+
# Supported call chain type is like a
|
11
|
+
# account.user.name
|
12
|
+
#
|
13
|
+
# Unsupported call chain type is like a
|
14
|
+
# filter3(filter2(filter1(value)))
|
15
|
+
# (RelayChain to support such type.)
|
16
|
+
#
|
17
|
+
# === Example
|
18
|
+
# Account = Struct.new(:user)
|
19
|
+
# User = Struct.new(:name)
|
20
|
+
# account = Account.new(User.new("Louis"))
|
21
|
+
#
|
22
|
+
# chain = PullChain.new(account, :user, :name, :upcase)
|
23
|
+
# chain.call # => LOUIS
|
24
|
+
#
|
25
|
+
# similar.
|
26
|
+
# # Strings separated by a slash
|
27
|
+
# PullChain.new(account, "user/name/upcase").call
|
28
|
+
#
|
29
|
+
# # use << operator.
|
30
|
+
# chain = PullChain.new(account)
|
31
|
+
# chain << :user << :name << :upcase
|
32
|
+
# chain.call
|
33
|
+
#
|
34
|
+
# # use add method.
|
35
|
+
# chain.add(:user).add(:name).add(:upcase).call
|
36
|
+
#
|
37
|
+
# # use add_all method.
|
38
|
+
# chain.add_all(:user, :name, :upcase).call
|
39
|
+
#
|
40
|
+
# can exist nil value on the way, like a following case.
|
41
|
+
# user.name = nil
|
42
|
+
# chain.call # => nil
|
43
|
+
#
|
44
|
+
# insert, insert_all method is insert_all method to chain.
|
45
|
+
# delete_at method is delete method from chain.
|
46
|
+
# clear method is delete all method from chain.
|
47
|
+
#
|
48
|
+
# === Require arguments on method
|
49
|
+
# Following example is required two arguments.
|
50
|
+
# class Foo
|
51
|
+
# def say(speaker, message)
|
52
|
+
# puts "#{speaker} said '#{message}'"
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# Solution1:Array, format is [Symbol, [*Args]].
|
57
|
+
# chain = PullChain.new(Foo.new) << [:say, ["Andres", "Hello"]]
|
58
|
+
# chain.call => Andres said 'Hello'
|
59
|
+
#
|
60
|
+
# Solution2:String
|
61
|
+
# chain = PullChain.new(foo) << "say('John', 'Goodbye')"
|
62
|
+
# chain.call => John said 'Goodbye'
|
63
|
+
#
|
64
|
+
# === Require block on method
|
65
|
+
# [1,2,3,4,5].inject(3) { |sum, n| sum + n } # => 18
|
66
|
+
#
|
67
|
+
# Solution1:Array, format is [Symbol, [*Args, Proc]].
|
68
|
+
# chain = PullChain.new([1,2,3,4,5])
|
69
|
+
# chain << [:inject, [3, lambda { |sum, n| sum + n }]]
|
70
|
+
# chain.call # => 18
|
71
|
+
#
|
72
|
+
# Solution2:String
|
73
|
+
# chain = PullChain.new([1,2,3,4,5])
|
74
|
+
# chain << "inject(3) { |sum, n| sum + n }"
|
75
|
+
# chain.call # => 18
|
76
|
+
#
|
77
|
+
# === Use result on chain
|
78
|
+
# Like a following example, can use result on chain.
|
79
|
+
# Example1:String
|
80
|
+
# Foo = Struct.new(:bar)
|
81
|
+
# Bar = Struct.new(:baz) {
|
82
|
+
# def speaker ; "Julian" end
|
83
|
+
# }
|
84
|
+
# class Baz
|
85
|
+
# def say(speaker, message) puts "#{speaker} said '#{message}'" end
|
86
|
+
# end
|
87
|
+
# foo = Foo.new(Bar.new(Baz.new))
|
88
|
+
#
|
89
|
+
# # can use bar instance in backward!
|
90
|
+
# chain = PullChain.new(foo) << "bar/baz/say(bar.speaker, 'Good!')"
|
91
|
+
# chain.call # => Julian said 'Good!'
|
92
|
+
#
|
93
|
+
# furthermore, can use variable name assigned.
|
94
|
+
# # @b is bar instance alias.
|
95
|
+
# chain = PullChain.new(foo) << "@b = bar/baz/say(b.speaker, 'Cool')"
|
96
|
+
# chain.call # => Julian said 'Cool'
|
97
|
+
#
|
98
|
+
# Example2:Array
|
99
|
+
# can access result by Proc.
|
100
|
+
# chain = PullChain.new(foo) << :bar << :baz
|
101
|
+
# chain << [:say, Proc.new { next bar.speaker, "Oh" }]
|
102
|
+
# chain.call # => Julian said 'Oh'
|
103
|
+
#
|
104
|
+
# case of use a lambda, can use result access object explicit.
|
105
|
+
# chain = PullChain.new(foo) << :bar << :baz
|
106
|
+
# arg_reader = lambda { |accessor| next accessor.bar.speaker, "Oh" }
|
107
|
+
# chain << [:say, arg_reader]
|
108
|
+
# chain.call # => Julian said 'Oh'
|
109
|
+
#
|
110
|
+
# === etc
|
111
|
+
# How to use slash in strings separated by a slash.
|
112
|
+
# like following, please escaped by backslash.
|
113
|
+
# chain = PullChain.new("AC") << "concat '\\/DC'"
|
114
|
+
# chain.call # => AC/DC
|
115
|
+
#
|
116
|
+
# Use return_nil_at_error= method, then can ignore error.
|
117
|
+
# chain = PullChain.new("Test") << :xxx
|
118
|
+
# begin
|
119
|
+
# chain.call # => undefined method `xxx'
|
120
|
+
# rescue
|
121
|
+
# end
|
122
|
+
# chain.return_nil_at_error = true
|
123
|
+
# chain.call # => nil
|
124
|
+
#
|
125
|
+
# Note:use operator in string type chain
|
126
|
+
# table = {name: %w(Bill Scott Paul)}
|
127
|
+
# PullChain.new(table, "[:name]").call # NG
|
128
|
+
# PullChain.new(table, "self[:name]").call # OK
|
129
|
+
# # Array type chain
|
130
|
+
# PullChain.new(table, [:[], [:name]]).call # OK
|
131
|
+
#
|
132
|
+
# following is also the same.
|
133
|
+
# # <<operator of String
|
134
|
+
# PullChain.new("Led", "self << ' Zeppelin'").call
|
135
|
+
# # []operator of Array
|
136
|
+
# PullChain.new(%w(Donald Walter), "self[1]").call
|
137
|
+
#
|
138
|
+
# Some classes, such Fixnum and Bignum not supported.
|
139
|
+
# # NG
|
140
|
+
# PullChain.new(999999999999999, "self % 2").call
|
141
|
+
#
|
142
|
+
class PullChain < BaseChain
|
143
|
+
attr_writer :return_nil_at_error
|
144
|
+
|
145
|
+
# Initialize chain
|
146
|
+
#
|
147
|
+
# initialize(receiver, *functions)
|
148
|
+
# receiver: starting point of method call.
|
149
|
+
# *functions: more than one symbol, string, array.
|
150
|
+
def initialize(receiver, *functions)
|
151
|
+
@start_receiver = receiver
|
152
|
+
@return_nil_at_error = false
|
153
|
+
add_all(*functions)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Call to all added method.
|
157
|
+
def call
|
158
|
+
@result_accessor = Object.new
|
159
|
+
begin
|
160
|
+
chain_elements.reduce(@start_receiver) do |receiver, chain_element|
|
161
|
+
break receiver if receiver.nil?
|
162
|
+
chain_element.call receiver
|
163
|
+
end
|
164
|
+
rescue
|
165
|
+
raise unless return_nil_at_error?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def return_nil_at_error?
|
170
|
+
@return_nil_at_error
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
attr_accessor :result_accessor
|
176
|
+
|
177
|
+
def create_common_chain_element(&block)
|
178
|
+
lambda do |receiver|
|
179
|
+
name, result = block.call(receiver)
|
180
|
+
define_result_access_method(name, result)
|
181
|
+
result
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def define_result_access_method(name, result)
|
186
|
+
result_accessor.singleton_class.class_eval do
|
187
|
+
define_method name do
|
188
|
+
result
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def create_chain_element_by_symbol(symbol)
|
194
|
+
create_common_chain_element do |receiver|
|
195
|
+
next symbol, execute(receiver, symbol)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def create_chain_element_by_array(array)
|
200
|
+
validate_array_length(array, 2, "symbol, [*args] or Proc")
|
201
|
+
validate_element_type_of_array(array, 1, [Array, Proc], "[*args] or Proc")
|
202
|
+
|
203
|
+
do_create_chain_element_by_array(array[0], array[1])
|
204
|
+
end
|
205
|
+
|
206
|
+
def do_create_chain_element_by_array(name, array_function_param)
|
207
|
+
create_common_chain_element do |receiver|
|
208
|
+
args, block = extract_args_and_block(array_function_param)
|
209
|
+
next name, execute(receiver, name, *args, &block)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def extract_args_and_block(array_function_param)
|
214
|
+
if array_function_param.is_a? Proc
|
215
|
+
return result_accessor.instance_eval(&array_function_param)
|
216
|
+
end
|
217
|
+
if array_function_param.last.is_a? Proc
|
218
|
+
return array_function_param[0...-1], array_function_param.last
|
219
|
+
end
|
220
|
+
array_function_param
|
221
|
+
end
|
222
|
+
|
223
|
+
def create_chain_element_by_string(string)
|
224
|
+
name, function = split_to_name_and_function(string)
|
225
|
+
create_common_chain_element do |receiver|
|
226
|
+
next name, execute_by_string(receiver, function)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def split_to_name_and_function(string)
|
231
|
+
name = string
|
232
|
+
function = string
|
233
|
+
|
234
|
+
md = string.match(/^@.+?=/)
|
235
|
+
if md
|
236
|
+
name = md[0][1...-1].strip
|
237
|
+
validate_variable_name_format(name)
|
238
|
+
function = string.sub(md[0], "").strip
|
239
|
+
end
|
240
|
+
|
241
|
+
return name, function
|
242
|
+
end
|
243
|
+
|
244
|
+
def validate_variable_name_format(name)
|
245
|
+
if name =~ /^[^a-zA-Z_]/
|
246
|
+
fail ArgumentError, "wrong format variable defined #{name}"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def execute(receiver, name, *args, &block)
|
251
|
+
receiver.__send__(name, *args, &block)
|
252
|
+
end
|
253
|
+
|
254
|
+
def execute_by_string(receiver, function)
|
255
|
+
begin
|
256
|
+
inject_result_accessor(receiver)
|
257
|
+
receiver.instance_eval(function)
|
258
|
+
rescue => ex
|
259
|
+
raise ex, "#{receiver}.#{function}"
|
260
|
+
ensure
|
261
|
+
eject_result_accessor(receiver)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def inject_result_accessor(receiver)
|
266
|
+
store_method_missing(receiver)
|
267
|
+
define_intercepted_method_missing(receiver)
|
268
|
+
end
|
269
|
+
|
270
|
+
def store_method_missing(receiver)
|
271
|
+
receiver.singleton_class.class_eval do
|
272
|
+
alias_method :original_method_missing, :method_missing
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def define_intercepted_method_missing(receiver)
|
277
|
+
# interception method_missing
|
278
|
+
accessor = result_accessor
|
279
|
+
receiver.singleton_class.class_eval do
|
280
|
+
define_method :method_missing do |name, *args, &block|
|
281
|
+
super(name, *args, &block) unless accessor.respond_to? name
|
282
|
+
accessor.send(name, *args, &block)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def eject_result_accessor(receiver)
|
288
|
+
# cleanup to interception
|
289
|
+
receiver.singleton_class.class_eval do
|
290
|
+
alias_method :method_missing, :original_method_missing
|
291
|
+
undef_method :original_method_missing
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
alias_method :<<, :add
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require "function_chain/base_chain"
|
2
|
+
|
3
|
+
module FunctionChain
|
4
|
+
# == RelayChain
|
5
|
+
# RelayChain is object like a connect to
|
6
|
+
# function's input from function's output.
|
7
|
+
# (methods well as can connect Proc.)
|
8
|
+
#
|
9
|
+
# Chain is object, so can call later.
|
10
|
+
#
|
11
|
+
# Supported call chain type is like a
|
12
|
+
# filter3(filter2(filter1(value))).
|
13
|
+
#
|
14
|
+
# Unsupported call chain type is like a
|
15
|
+
# account.user.name
|
16
|
+
# (PullChain to support such type.)
|
17
|
+
#
|
18
|
+
# === Example
|
19
|
+
# class Decorator
|
20
|
+
# def decorate1(value)
|
21
|
+
# "( #{value} )"
|
22
|
+
# end
|
23
|
+
# def decorate2(value)
|
24
|
+
# "{ #{value} }"
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# chain = RelayChain.new(Decorator.new, :decorate1, :decorate2)
|
28
|
+
# chain.call("Hello") # => { ( Hello ) }
|
29
|
+
#
|
30
|
+
# similar.
|
31
|
+
# # Strings separated by a slash
|
32
|
+
# chain = RelayChain.new(Decorator.new, "decorate1/decorate2")
|
33
|
+
# chain.call("Hello")
|
34
|
+
#
|
35
|
+
# # use >> operator.
|
36
|
+
# chain = RelayChain.new(Decorator.new)
|
37
|
+
# chain >> :decorate1 >> :decorate2
|
38
|
+
# chain.call("Hello")
|
39
|
+
#
|
40
|
+
# # use Method object
|
41
|
+
# chain = RelayChain.new
|
42
|
+
# chain >> decorator.method(:decorate1) >> decorator.method(:decorate2)
|
43
|
+
# chain.call("Hello")
|
44
|
+
#
|
45
|
+
# # use add method
|
46
|
+
# chain.add(:decorate1).add(:decorate2).call("Hello")
|
47
|
+
#
|
48
|
+
# # use add_all method
|
49
|
+
# chain.add_all(:decorate1, :decorate2).call("Hello")
|
50
|
+
#
|
51
|
+
# insert, insert_all method is insert function to chain.
|
52
|
+
# delete_at method is delete function from chain.
|
53
|
+
# clear method is delete all function from chain.
|
54
|
+
#
|
55
|
+
# === How to connect method of differed instance
|
56
|
+
# Example, following two class.
|
57
|
+
# Introduce how to connect method of these class.
|
58
|
+
# class Decorator
|
59
|
+
# def decorate1(value) "( #{value} )" end
|
60
|
+
# def decorate2(value) "{ #{value} }" end
|
61
|
+
# end
|
62
|
+
# class Decorator2
|
63
|
+
# def decorate(value) "[ #{value} ]" end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# Solution1:Array, format is [instance, Symbol or String of method]
|
67
|
+
# chain = RelayChain.new(Decorator.new)
|
68
|
+
# chain >> :decorate1 >> :decorate2 >> [Decorator2.new, :decorate]
|
69
|
+
# # String ver.
|
70
|
+
# # chain >> :decorate1 >> :decorate2 >> [Decorator2.new, "decorate"]
|
71
|
+
# chain.call("Hello") # => [ { ( Hello ) } ]
|
72
|
+
#
|
73
|
+
# Solution2:String, use registered instance.
|
74
|
+
# chain = RelayChain.new(Decorator.new)
|
75
|
+
# # register name and instance
|
76
|
+
# chain.add_receiver("d2", Decorator2.new)
|
77
|
+
# # use registered instance
|
78
|
+
# chain >> "/decorate1/decorate2/d2.decorate"
|
79
|
+
# chain.call("Hello") # => [ { ( Hello ) } ]
|
80
|
+
#
|
81
|
+
# # add_receiver_table method is register name and instance at once.
|
82
|
+
# chain.add_receiver_table({"x" => X.new, "y" => Y.new})
|
83
|
+
#
|
84
|
+
# === Case of method's output and method's input mismatch
|
85
|
+
# Following example, decorate output is 1, and union input is 2.
|
86
|
+
# How to do connect these methods?
|
87
|
+
# class Decorator
|
88
|
+
# def decorate(value)
|
89
|
+
# "#{value} And"
|
90
|
+
# end
|
91
|
+
# def union(value1, value2)
|
92
|
+
# "#{value1} #{value2}"
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# Solution1:define connect method.
|
97
|
+
# class Decorator
|
98
|
+
# def connect(value)
|
99
|
+
# return value, "Palmer"
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
# chain = RelayChain.new(Decorator.new)
|
103
|
+
# chain >> :decorate >> :connect >> :union
|
104
|
+
# chain.call("Emerson, Lake") # => Emerson, Lake And Palmer
|
105
|
+
#
|
106
|
+
# Solution2:add lambda or Proc to between these methods.
|
107
|
+
# lambda's format is following.
|
108
|
+
# lambda {|chain, *args| chain.call(next function's arguments) }.
|
109
|
+
# lambda's parameter:chain is chain object.
|
110
|
+
# lambda's parameter:*args is previous function's output.
|
111
|
+
# can call next function by chain object.
|
112
|
+
#
|
113
|
+
# chain = RelayChain.new(Decorator.new)
|
114
|
+
# arg_adder = lambda { |chain, value| chain.call(value, "Jerry") }
|
115
|
+
# chain >> :decorate >> arg_adder >> :union
|
116
|
+
# chain.call("Tom") # => Tom And Jerry
|
117
|
+
#
|
118
|
+
# === Appendix
|
119
|
+
# Chain stop by means of lambda.
|
120
|
+
#
|
121
|
+
# class Decorator
|
122
|
+
# def decorate1(value) "( #{value} )" end
|
123
|
+
# def decorate2(value) "{ #{value} }" end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# def create_stopper(&stop_condition)
|
127
|
+
# lambda do |chain, value|
|
128
|
+
# # if stop conditions are met then return value
|
129
|
+
# if stop_condition.call(value)
|
130
|
+
# value
|
131
|
+
# else
|
132
|
+
# chain.call(value)
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# chain = RelayChain.new(Decorator.new, :decorate1, :decorate2)
|
138
|
+
#
|
139
|
+
# # insert_all conditional chain stopper
|
140
|
+
# chain.insert(1, create_stopper { |value| value =~ /\d/ })
|
141
|
+
# chain.call("Van Halen 1984") # => ( Van Halen 1984 ) not enclosed to {}
|
142
|
+
# chain.call("Van Halen Jump") # => { ( Van Halen Jump ) } enclosed to {}
|
143
|
+
#
|
144
|
+
class RelayChain < BaseChain
|
145
|
+
# Initialize chain
|
146
|
+
#
|
147
|
+
# initialize(common_receiver = nil, *functions)
|
148
|
+
# common_receiver:used if the instance is omitted
|
149
|
+
# *functions: more than one symbol, string, array, method, proc
|
150
|
+
def initialize(common_receiver = nil, *functions)
|
151
|
+
@common_receiver = common_receiver
|
152
|
+
@index = 0
|
153
|
+
add_all(*functions)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Call to all added function.
|
157
|
+
def call(*args)
|
158
|
+
begin
|
159
|
+
return if last?
|
160
|
+
chain_element = chain_elements[@index]
|
161
|
+
@index += 1
|
162
|
+
chain_element.call(self, *args)
|
163
|
+
ensure
|
164
|
+
@index = 0
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Whether chain last
|
169
|
+
def last?
|
170
|
+
@index == chain_elements.length
|
171
|
+
end
|
172
|
+
|
173
|
+
# add receiver
|
174
|
+
# use by string type function.
|
175
|
+
#
|
176
|
+
# add_receiver(name, receiver)
|
177
|
+
# name:receiver's name
|
178
|
+
# receiver:register this receiver
|
179
|
+
def add_receiver(name, receiver)
|
180
|
+
receiver_table[name] = receiver
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
184
|
+
# register name and instance at once.
|
185
|
+
#
|
186
|
+
# add_receiver_table(table)
|
187
|
+
# table:hash {receiver's name as String => receiver, ...}
|
188
|
+
def add_receiver_table(table)
|
189
|
+
receiver_table.merge! table
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
protected
|
194
|
+
|
195
|
+
def supported_types
|
196
|
+
super() | [Method, Proc]
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def create_chain_element(function)
|
202
|
+
case function
|
203
|
+
when Method then create_chain_element_by_method(function)
|
204
|
+
when Proc then function
|
205
|
+
else super(function)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def create_common_chain_element(&block)
|
210
|
+
lambda do |chain, *args|
|
211
|
+
chain.last? ? block.call(*args) : chain.call(*block.call(*args))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def create_chain_element_by_method(method)
|
216
|
+
create_common_chain_element { |*args| method.call(*args) }
|
217
|
+
end
|
218
|
+
|
219
|
+
def create_chain_element_by_array(arr)
|
220
|
+
validate_array_length(arr, 2, "receiver, symbol or string of receiver's method name")
|
221
|
+
validate_element_type_of_array(arr, 1, [Symbol, String], "[Symbol or String]")
|
222
|
+
|
223
|
+
create_common_chain_element { |*args| arr[0].__send__(arr[1], *args) }
|
224
|
+
end
|
225
|
+
|
226
|
+
def create_chain_element_by_symbol(symbol)
|
227
|
+
create_common_chain_element do |*args|
|
228
|
+
@common_receiver.__send__(symbol, *args)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def create_chain_element_by_string(string)
|
233
|
+
create_common_chain_element do |*args|
|
234
|
+
index = string.index(".")
|
235
|
+
if index
|
236
|
+
receiver_key = string[0...index]
|
237
|
+
receiver_method = string[index + 1..-1]
|
238
|
+
receiver_table[receiver_key].send(receiver_method, *args)
|
239
|
+
else
|
240
|
+
@common_receiver.__send__(string, *args)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def receiver_table
|
246
|
+
@receiver_table ||= {}
|
247
|
+
end
|
248
|
+
|
249
|
+
alias_method :>>, :add
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "function_chain/version"
|
2
|
+
require "function_chain/pull_chain"
|
3
|
+
require "function_chain/relay_chain"
|
4
|
+
|
5
|
+
module FunctionChain
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Shortcut to
|
9
|
+
# PullChain.new(receiver, *functions).call
|
10
|
+
def pull(receiver, *functions)
|
11
|
+
PullChain.new(receiver, *functions).call
|
12
|
+
end
|
13
|
+
end
|