cuprum 0.8.0 → 0.10.0
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 +5 -5
- data/CHANGELOG.md +85 -9
- data/DEVELOPMENT.md +58 -42
- data/README.md +414 -754
- data/lib/cuprum.rb +1 -25
- data/lib/cuprum/built_in/identity_command.rb +4 -4
- data/lib/cuprum/chaining.rb +139 -148
- data/lib/cuprum/command.rb +24 -15
- data/lib/cuprum/command_factory.rb +43 -19
- data/lib/cuprum/currying.rb +78 -0
- data/lib/cuprum/currying/curried_command.rb +109 -0
- data/lib/cuprum/error.rb +37 -0
- data/lib/cuprum/errors/command_not_implemented.rb +35 -0
- data/lib/cuprum/errors/operation_not_called.rb +35 -0
- data/lib/cuprum/operation.rb +37 -28
- data/lib/cuprum/processing.rb +45 -82
- data/lib/cuprum/result.rb +52 -127
- data/lib/cuprum/result_helpers.rb +14 -105
- data/lib/cuprum/rspec.rb +8 -0
- data/lib/cuprum/rspec/be_a_result.rb +19 -0
- data/lib/cuprum/rspec/be_a_result_matcher.rb +286 -0
- data/lib/cuprum/steps.rb +275 -0
- data/lib/cuprum/utils/instance_spy.rb +9 -2
- data/lib/cuprum/version.rb +1 -1
- metadata +14 -8
- data/lib/cuprum/errors/process_not_implemented_error.rb +0 -14
- data/lib/cuprum/utils/result_not_empty_warning.rb +0 -72
data/lib/cuprum.rb
CHANGED
@@ -4,36 +4,12 @@ module Cuprum
|
|
4
4
|
autoload :Command, 'cuprum/command'
|
5
5
|
autoload :Operation, 'cuprum/operation'
|
6
6
|
autoload :Result, 'cuprum/result'
|
7
|
-
|
8
|
-
DEFAULT_WARNING_PROC = ->(message) { Kernel.warn message }
|
9
|
-
private_constant :DEFAULT_WARNING_PROC
|
7
|
+
autoload :Steps, 'cuprum/steps'
|
10
8
|
|
11
9
|
class << self
|
12
|
-
# @return [Proc] The proc called to display a warning message. By default,
|
13
|
-
# delegates to Kernel#warn. Set this to configure the warning behavior
|
14
|
-
# (e.g. to call a Logger).
|
15
|
-
attr_writer :warning_proc
|
16
|
-
|
17
10
|
# @return [String] The current version of the gem.
|
18
11
|
def version
|
19
12
|
VERSION
|
20
13
|
end # method version
|
21
|
-
|
22
|
-
# Displays a warning message. By default, delegates to Kernel#warn. The
|
23
|
-
# warning behavior can be configured (e.g. to call a Logger) using the
|
24
|
-
# #warning_proc= method.
|
25
|
-
#
|
26
|
-
# @param message [String] The warning message to display.
|
27
|
-
#
|
28
|
-
# @see #warning_proc=
|
29
|
-
def warn message
|
30
|
-
warning_proc.call(message)
|
31
|
-
end # method warn
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def warning_proc
|
36
|
-
@warning_proc ||= DEFAULT_WARNING_PROC
|
37
|
-
end # method warning_proc
|
38
14
|
end # eigenclass
|
39
15
|
end # module
|
@@ -12,15 +12,15 @@ module Cuprum::BuiltIn
|
|
12
12
|
# #=> true
|
13
13
|
#
|
14
14
|
# @example With a result.
|
15
|
-
#
|
16
|
-
# value = Cuprum::Result.new('result value', :
|
15
|
+
# error = 'errors.messages.unknown'
|
16
|
+
# value = Cuprum::Result.new(value: 'result value', error: error)
|
17
17
|
# result = IdentityCommand.new.call(value)
|
18
18
|
# result.value
|
19
19
|
# #=> 'result value'
|
20
20
|
# result.success?
|
21
21
|
# #=> false
|
22
|
-
# result.
|
23
|
-
# #=>
|
22
|
+
# result.error
|
23
|
+
# #=> 'errors.messages.unknown'
|
24
24
|
class IdentityCommand < Cuprum::Command
|
25
25
|
private
|
26
26
|
|
data/lib/cuprum/chaining.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cuprum'
|
2
4
|
|
3
5
|
module Cuprum
|
@@ -25,8 +27,8 @@ module Cuprum
|
|
25
27
|
# chain(UrlSafeCommand.new).
|
26
28
|
# chain(PrependDateCommand.new(post.created_at)).
|
27
29
|
# call(post.title)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
+
# end
|
31
|
+
# end
|
30
32
|
#
|
31
33
|
# title = 'Greetings, programs!'
|
32
34
|
# date = '1982-07-09'
|
@@ -53,50 +55,76 @@ module Cuprum
|
|
53
55
|
#
|
54
56
|
# private
|
55
57
|
#
|
58
|
+
# def create_tag
|
59
|
+
# Command.new do |tag_name|
|
60
|
+
# tag = Tag.new(name: tag_name)
|
61
|
+
#
|
62
|
+
# return tag if tag.save
|
63
|
+
#
|
64
|
+
# Cuprum::Result.new(error: tag.errors)
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def create_tagging(taggable)
|
69
|
+
# Command.new do |tag|
|
70
|
+
# tagging = tag.build_tagging(taggable)
|
71
|
+
#
|
72
|
+
# return tagging if tagging.save
|
73
|
+
#
|
74
|
+
# Cuprum::Result.new(error: tagging.errors)
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# def find_tag
|
79
|
+
# Command.new do |tag_name|
|
80
|
+
# tag = Tag.where(name: tag_name).first
|
81
|
+
#
|
82
|
+
# tag || Cuprum::Result.new(error: 'tag not found')
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
56
86
|
# # Tries to find the tag with the given name. If that fails, creates a
|
57
87
|
# # new tag with the given name. If the tag is found, or if the new tag is
|
58
88
|
# # successfully created, then creates a tagging using the tag. If the tag
|
59
89
|
# # is not found and cannot be created, then the tagging is not created
|
60
90
|
# # and the result of the CreateTaggingCommand is a failure with the
|
61
91
|
# # appropriate error messages.
|
62
|
-
# def process
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
# Tag.create(tag_name)
|
73
|
-
# end.
|
74
|
-
# chain(:on => :success) do |tag|
|
92
|
+
# def process(taggable, tag_name)
|
93
|
+
# find_tag
|
94
|
+
# .chain(on: :failure) do
|
95
|
+
# # If the finding the tag fails, this step is called, returning a
|
96
|
+
# # result with a newly created tag.
|
97
|
+
# create_tag.call(tag_name)
|
98
|
+
# end
|
99
|
+
# .chain(:on => :success) do |tag|
|
100
|
+
# # Finally, the tag has been either found or created, so we can
|
101
|
+
# # create the tagging relation.
|
75
102
|
# tag.create_tagging(taggable)
|
76
103
|
# end
|
77
|
-
#
|
78
|
-
#
|
104
|
+
# .call(tag_name)
|
105
|
+
# end
|
106
|
+
# end
|
79
107
|
#
|
80
108
|
# post = Post.create(:title => 'Tagging Example')
|
81
109
|
# example_tag = Tag.create(:name => 'Example Tag')
|
82
110
|
#
|
83
111
|
# result = CreateTaggingCommand.new.call(post, 'Example Tag')
|
84
112
|
# result.success? #=> true
|
85
|
-
# result.
|
113
|
+
# result.error #=> nil
|
86
114
|
# result.value #=> an instance of Tagging
|
87
115
|
# post.tags.map(&:name)
|
88
116
|
# #=> ['Example Tag']
|
89
117
|
#
|
90
118
|
# result = CreateTaggingCommand.new.call(post, 'Another Tag')
|
91
119
|
# result.success? #=> true
|
92
|
-
# result.
|
120
|
+
# result.error #=> nil
|
93
121
|
# result.value #=> an instance of Tagging
|
94
122
|
# post.tags.map(&:name)
|
95
123
|
# #=> ['Example Tag', 'Another Tag']
|
96
124
|
#
|
97
125
|
# result = CreateTaggingCommand.new.call(post, 'An Invalid Tag Name')
|
98
126
|
# result.success? #=> false
|
99
|
-
# result.
|
127
|
+
# result.error #=> [{ tag: { name: ['is invalid'] }}]
|
100
128
|
# post.tags.map(&:name)
|
101
129
|
# #=> ['Example Tag', 'Another Tag']
|
102
130
|
#
|
@@ -123,12 +151,8 @@ module Cuprum
|
|
123
151
|
# Find.new(Post).call(id).
|
124
152
|
# yield_result(:on => :failure) do |result|
|
125
153
|
# redirect_to posts_path
|
126
|
-
#
|
127
|
-
# # A halted result prevents further :on => :failure commands from
|
128
|
-
# # being called.
|
129
|
-
# result.halt!
|
130
154
|
# end.
|
131
|
-
# yield_result do |result|
|
155
|
+
# yield_result(on: :success) do |result|
|
132
156
|
# # Assign our attributes and save the post.
|
133
157
|
# UpdateAttributes.new.call(result.value, attributes)
|
134
158
|
# end.
|
@@ -140,11 +164,11 @@ module Cuprum
|
|
140
164
|
# end.
|
141
165
|
# tap_result(:on => :always) do |result|
|
142
166
|
# # Chaining :on => :always ensures that the command will be run,
|
143
|
-
# # even if the previous result is failing
|
167
|
+
# # even if the previous result is failing.
|
144
168
|
# if result.failure?
|
145
169
|
# log_errors(
|
146
170
|
# :command => UpdatePostCommand,
|
147
|
-
# :
|
171
|
+
# :error => result.error
|
148
172
|
# )
|
149
173
|
# end
|
150
174
|
# end
|
@@ -172,6 +196,11 @@ module Cuprum
|
|
172
196
|
#
|
173
197
|
# @see Cuprum::Command
|
174
198
|
module Chaining
|
199
|
+
# (see Cuprum::Processing#call)
|
200
|
+
def call(*args, **kwargs, &block)
|
201
|
+
yield_chain(super)
|
202
|
+
end
|
203
|
+
|
175
204
|
# Creates a copy of the first command, and then chains the given command or
|
176
205
|
# block to execute after the first command's implementation. When #call is
|
177
206
|
# executed, each chained command will be called with the previous result
|
@@ -190,13 +219,11 @@ module Cuprum
|
|
190
219
|
# @param on [Symbol] Sets a condition on when the chained block can run,
|
191
220
|
# based on the previous result. Valid values are :success, :failure, and
|
192
221
|
# :always. If the value is :success, the block will be called only if
|
193
|
-
# the previous result succeeded
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
# previous command was a success or a failure, but not if the command
|
199
|
-
# chain has been halted.
|
222
|
+
# the previous result succeeded. If the value is :failure, the block
|
223
|
+
# will be called only if the previous result failed. If the value is
|
224
|
+
# :always, the block will be called regardless of the previous result
|
225
|
+
# status. If no value is given, the command will run whether the
|
226
|
+
# previous command was a success or a failure.
|
200
227
|
#
|
201
228
|
# @overload chain(on: nil) { |value| }
|
202
229
|
# Creates an anonymous command from the given block. The command will be
|
@@ -205,62 +232,16 @@ module Cuprum
|
|
205
232
|
# @param on [Symbol] Sets a condition on when the chained block can run,
|
206
233
|
# based on the previous result. Valid values are :success, :failure, and
|
207
234
|
# :always. If the value is :success, the block will be called only if
|
208
|
-
# the previous result succeeded
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
# previous command was a success or a failure, but not if the command
|
214
|
-
# chain has been halted.
|
215
|
-
#
|
216
|
-
# @yieldparam value [Object] The value of the previous result.
|
217
|
-
def chain command = nil, on: nil, &block
|
218
|
-
clone.chain!(command, :on => on, &block)
|
219
|
-
end # method chain
|
220
|
-
|
221
|
-
# Shorthand for command.chain(:on => :failure). Creates a copy of the first
|
222
|
-
# command, and then chains the given command or block to execute after the
|
223
|
-
# first command's implementation, but only if the previous command is
|
224
|
-
# failing.
|
225
|
-
#
|
226
|
-
# @return [Cuprum::Chaining] A copy of the command, with the chained
|
227
|
-
# command.
|
228
|
-
#
|
229
|
-
# @see #chain
|
230
|
-
#
|
231
|
-
# @overload failure(command)
|
232
|
-
# @param command [Cuprum::Command] The command to chain.
|
233
|
-
#
|
234
|
-
# @overload failure() { |value| }
|
235
|
-
# Creates an anonymous command from the given block. The command will be
|
236
|
-
# passed the value of the previous result.
|
235
|
+
# the previous result succeeded. If the value is :failure, the block
|
236
|
+
# will be called only if the previous result failed. If the value is
|
237
|
+
# :always, the block will be called regardless of the previous result
|
238
|
+
# status. If no value is given, the command will run whether the
|
239
|
+
# previous command was a success or a failure.
|
237
240
|
#
|
238
241
|
# @yieldparam value [Object] The value of the previous result.
|
239
|
-
def
|
240
|
-
clone.chain!(command, :on
|
241
|
-
end
|
242
|
-
|
243
|
-
# Shorthand for command.chain(:on => :success). Creates a copy of the first
|
244
|
-
# command, and then chains the given command or block to execute after the
|
245
|
-
# first command's implementation, but only if the previous command is
|
246
|
-
# failing.
|
247
|
-
#
|
248
|
-
# @return [Cuprum::Chaining] A copy of the command, with the chained
|
249
|
-
# command.
|
250
|
-
#
|
251
|
-
# @see #chain
|
252
|
-
#
|
253
|
-
# @overload success(command)
|
254
|
-
# @param command [Cuprum::Command] The command to chain.
|
255
|
-
#
|
256
|
-
# @overload success() { |value| }
|
257
|
-
# Creates an anonymous command from the given block. The command will be
|
258
|
-
# passed the value of the previous result.
|
259
|
-
#
|
260
|
-
# @yieldparam value [Object] The value of the previous result.
|
261
|
-
def success command = nil, &block
|
262
|
-
clone.chain!(command, :on => :success, &block)
|
263
|
-
end # method success
|
242
|
+
def chain(command = nil, on: nil, &block)
|
243
|
+
clone.chain!(command, on: on, &block)
|
244
|
+
end
|
264
245
|
|
265
246
|
# As #yield_result, but always returns the previous result when the block is
|
266
247
|
# called. The return value of the block is discarded.
|
@@ -272,9 +253,9 @@ module Cuprum
|
|
272
253
|
# @return (see #yield_result)
|
273
254
|
#
|
274
255
|
# @see #yield_result
|
275
|
-
def tap_result
|
276
|
-
clone.tap_result!(:
|
277
|
-
end
|
256
|
+
def tap_result(on: nil, &block)
|
257
|
+
clone.tap_result!(on: on, &block)
|
258
|
+
end
|
278
259
|
|
279
260
|
# Creates a copy of the command, and then chains the block to execute after
|
280
261
|
# the command implementation. When #call is executed, each chained block
|
@@ -284,24 +265,25 @@ module Cuprum
|
|
284
265
|
# @param on [Symbol] Sets a condition on when the chained block can run,
|
285
266
|
# based on the previous result. Valid values are :success, :failure, and
|
286
267
|
# :always. If the value is :success, the block will be called only if the
|
287
|
-
# previous result succeeded
|
288
|
-
#
|
289
|
-
#
|
290
|
-
# the previous result status, even if the previous result is halted. If no
|
268
|
+
# previous result succeeded. If the value is :failure, the block will be
|
269
|
+
# called only if the previous result failed. If the value is :always, the
|
270
|
+
# block will be called regardless of the previous result status. If no
|
291
271
|
# value is given, the command will run whether the previous command was a
|
292
|
-
# success or a failure
|
272
|
+
# success or a failure.
|
293
273
|
#
|
294
274
|
# @yieldparam result [Cuprum::Result] The #result of the previous command.
|
295
275
|
#
|
296
276
|
# @return [Cuprum::Chaining] A copy of the command, with the chained block.
|
297
277
|
#
|
298
278
|
# @see #tap_result
|
299
|
-
def yield_result
|
300
|
-
clone.yield_result!(:
|
301
|
-
end
|
279
|
+
def yield_result(on: nil, &block)
|
280
|
+
clone.yield_result!(on: on, &block)
|
281
|
+
end
|
302
282
|
|
303
283
|
protected
|
304
284
|
|
285
|
+
# rubocop:disable Metrics/MethodLength
|
286
|
+
|
305
287
|
# @!visibility public
|
306
288
|
#
|
307
289
|
# As #chain, but modifies the current command instead of creating a clone.
|
@@ -318,13 +300,11 @@ module Cuprum
|
|
318
300
|
# @param on [Symbol] Sets a condition on when the chained block can run,
|
319
301
|
# based on the previous result. Valid values are :success, :failure, and
|
320
302
|
# :always. If the value is :success, the block will be called only if
|
321
|
-
# the previous result succeeded
|
322
|
-
#
|
323
|
-
#
|
324
|
-
#
|
325
|
-
#
|
326
|
-
# previous command was a success or a failure, but not if the command
|
327
|
-
# chain has been halted.
|
303
|
+
# the previous result succeeded. If the value is :failure, the block
|
304
|
+
# will be called only if the previous result failed. If the value is
|
305
|
+
# :always, the block will be called regardless of the previous result
|
306
|
+
# status. If no value is given, the command will run whether the
|
307
|
+
# previous command was a success or a failure.
|
328
308
|
#
|
329
309
|
# @overload chain!(on: nil) { |value| }
|
330
310
|
# Creates an anonymous command from the given block. The command will be
|
@@ -333,34 +313,36 @@ module Cuprum
|
|
333
313
|
# @param on [Symbol] Sets a condition on when the chained block can run,
|
334
314
|
# based on the previous result. Valid values are :success, :failure, and
|
335
315
|
# :always. If the value is :success, the block will be called only if
|
336
|
-
# the previous result succeeded
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
# previous command was a success or a failure, but not if the command
|
342
|
-
# chain has been halted.
|
316
|
+
# the previous result succeeded. If the value is :failure, the block
|
317
|
+
# will be called only if the previous result failed. If the value is
|
318
|
+
# :always, the block will be called regardless of the previous result
|
319
|
+
# status. If no value is given, the command will run whether the
|
320
|
+
# previous command was a success or a failure.
|
343
321
|
#
|
344
322
|
# @yieldparam value [Object] The value of the previous result.
|
345
|
-
def chain!
|
323
|
+
def chain!(command = nil, on: nil, &block)
|
324
|
+
SleepingKingStudios::Tools::CoreTools.deprecate(
|
325
|
+
"#{self.class}#chain",
|
326
|
+
message: 'Use the #step method to compose commands.'
|
327
|
+
)
|
328
|
+
|
346
329
|
command ||= Cuprum::Command.new(&block)
|
347
330
|
|
348
331
|
chained_procs <<
|
349
332
|
{
|
350
|
-
:
|
351
|
-
:
|
333
|
+
proc: chain_command(command),
|
334
|
+
on: on
|
352
335
|
} # end hash
|
353
336
|
|
354
337
|
self
|
355
|
-
end
|
338
|
+
end
|
339
|
+
# rubocop:enable Metrics/MethodLength
|
356
340
|
|
357
341
|
def chained_procs
|
358
342
|
@chained_procs ||= []
|
359
|
-
end
|
343
|
+
end
|
360
344
|
|
361
|
-
|
362
|
-
yield_chain(super)
|
363
|
-
end # method call
|
345
|
+
# rubocop:disable Metrics/MethodLength
|
364
346
|
|
365
347
|
# @!visibility public
|
366
348
|
#
|
@@ -375,17 +357,23 @@ module Cuprum
|
|
375
357
|
# @return (see #tap_result)
|
376
358
|
#
|
377
359
|
# @see #tap_result
|
378
|
-
def tap_result!
|
360
|
+
def tap_result!(on: nil, &block)
|
361
|
+
SleepingKingStudios::Tools::CoreTools.deprecate(
|
362
|
+
"#{self.class}#tap_result",
|
363
|
+
message: 'Use the #step method to compose commands.'
|
364
|
+
)
|
365
|
+
|
379
366
|
tapped = ->(result) { result.tap { block.call(result) } }
|
380
367
|
|
381
368
|
chained_procs <<
|
382
369
|
{
|
383
|
-
:
|
384
|
-
:
|
370
|
+
proc: tapped,
|
371
|
+
on: on
|
385
372
|
} # end hash
|
386
373
|
|
387
374
|
self
|
388
|
-
end
|
375
|
+
end
|
376
|
+
# rubocop:enable Metrics/MethodLength
|
389
377
|
|
390
378
|
# @!visibility public
|
391
379
|
#
|
@@ -400,51 +388,54 @@ module Cuprum
|
|
400
388
|
# @return (see #yield_result)
|
401
389
|
#
|
402
390
|
# @see #yield_result
|
403
|
-
def yield_result!
|
391
|
+
def yield_result!(on: nil, &block)
|
392
|
+
SleepingKingStudios::Tools::CoreTools.deprecate(
|
393
|
+
"#{self.class}#yield_result",
|
394
|
+
message: 'Use the #step method to compose commands.'
|
395
|
+
)
|
396
|
+
|
404
397
|
chained_procs <<
|
405
398
|
{
|
406
|
-
:
|
407
|
-
:
|
399
|
+
proc: block,
|
400
|
+
on: on
|
408
401
|
} # end hash
|
409
402
|
|
410
403
|
self
|
411
|
-
end
|
404
|
+
end
|
412
405
|
|
413
406
|
private
|
414
407
|
|
415
|
-
def chain_command
|
408
|
+
def chain_command(command)
|
416
409
|
if command.arity.zero?
|
417
|
-
->(
|
410
|
+
->(_result) { command.call }
|
418
411
|
else
|
419
|
-
->(result) { command.
|
420
|
-
end
|
421
|
-
end
|
412
|
+
->(result) { command.call(result.value) }
|
413
|
+
end
|
414
|
+
end
|
422
415
|
|
423
|
-
def skip_chained_proc?
|
416
|
+
def skip_chained_proc?(last_result, on:)
|
424
417
|
return false if on == :always
|
425
418
|
|
426
|
-
return true if last_result.respond_to?(:halted?) && last_result.halted?
|
427
|
-
|
428
419
|
case on
|
429
420
|
when :success
|
430
421
|
!last_result.success?
|
431
422
|
when :failure
|
432
423
|
!last_result.failure?
|
433
|
-
end
|
434
|
-
end
|
424
|
+
end
|
425
|
+
end
|
435
426
|
|
436
|
-
def yield_chain
|
427
|
+
def yield_chain(first_result)
|
437
428
|
chained_procs.reduce(first_result) do |result, hsh|
|
438
|
-
next result if skip_chained_proc?(result, :
|
429
|
+
next result if skip_chained_proc?(result, on: hsh[:on])
|
439
430
|
|
440
431
|
value = hsh.fetch(:proc).call(result)
|
441
432
|
|
442
433
|
if value_is_result?(value)
|
443
|
-
value.
|
434
|
+
value.to_cuprum_result
|
444
435
|
else
|
445
|
-
build_result(value)
|
446
|
-
end
|
447
|
-
end
|
448
|
-
end
|
449
|
-
end
|
450
|
-
end
|
436
|
+
build_result(value: value)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|