cuprum 0.10.0.rc.0 → 1.0.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuprum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0.rc.0
4
+ version: 1.0.0.rc.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob "Merlin" Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-01 00:00:00.000000000 Z
11
+ date: 2021-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sleeping_king_studios-tools
@@ -16,70 +16,70 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.8'
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.8'
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.6'
33
+ version: '3.10'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.6'
40
+ version: '3.10'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec-sleeping_king_studios
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.3'
47
+ version: '2.5'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.3'
54
+ version: '2.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.49.1
61
+ version: 1.10.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.49.1
68
+ version: 1.10.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rubocop-rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1.15'
75
+ version: '2.1'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.15'
82
+ version: '2.1'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -94,9 +94,11 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.15'
97
- description: An opinionated implementation of the Command pattern for Ruby applications.
98
- Cuprum wraps your business logic in a consistent, object-oriented interface and
99
- features status and error management, composability and control flow management.
97
+ description: |-
98
+ An opinionated implementation of the Command pattern for Ruby applications.
99
+ Cuprum wraps your business logic in a consistent, object-oriented interface
100
+ and features status and error management, composability and control flow
101
+ management.
100
102
  email:
101
103
  - merlin@sleepingkingstudios.com
102
104
  executables: []
@@ -104,6 +106,7 @@ extensions: []
104
106
  extra_rdoc_files: []
105
107
  files:
106
108
  - CHANGELOG.md
109
+ - CODE_OF_CONDUCT.md
107
110
  - DEVELOPMENT.md
108
111
  - LICENSE
109
112
  - README.md
@@ -113,7 +116,6 @@ files:
113
116
  - lib/cuprum/built_in/identity_operation.rb
114
117
  - lib/cuprum/built_in/null_command.rb
115
118
  - lib/cuprum/built_in/null_operation.rb
116
- - lib/cuprum/chaining.rb
117
119
  - lib/cuprum/command.rb
118
120
  - lib/cuprum/command_factory.rb
119
121
  - lib/cuprum/currying.rb
@@ -122,6 +124,13 @@ files:
122
124
  - lib/cuprum/errors.rb
123
125
  - lib/cuprum/errors/command_not_implemented.rb
124
126
  - lib/cuprum/errors/operation_not_called.rb
127
+ - lib/cuprum/errors/uncaught_exception.rb
128
+ - lib/cuprum/exception_handling.rb
129
+ - lib/cuprum/matcher.rb
130
+ - lib/cuprum/matcher_list.rb
131
+ - lib/cuprum/matching.rb
132
+ - lib/cuprum/matching/match_clause.rb
133
+ - lib/cuprum/middleware.rb
125
134
  - lib/cuprum/operation.rb
126
135
  - lib/cuprum/processing.rb
127
136
  - lib/cuprum/result.rb
@@ -129,6 +138,7 @@ files:
129
138
  - lib/cuprum/rspec.rb
130
139
  - lib/cuprum/rspec/be_a_result.rb
131
140
  - lib/cuprum/rspec/be_a_result_matcher.rb
141
+ - lib/cuprum/rspec/be_callable.rb
132
142
  - lib/cuprum/steps.rb
133
143
  - lib/cuprum/utils.rb
134
144
  - lib/cuprum/utils/instance_spy.rb
@@ -136,7 +146,9 @@ files:
136
146
  homepage: http://sleepingkingstudios.com
137
147
  licenses:
138
148
  - MIT
139
- metadata: {}
149
+ metadata:
150
+ bug_tracker_uri: https://github.com/sleepingkingstudios/cuprum/issues
151
+ source_code_uri: https://github.com/sleepingkingstudios/cuprum
140
152
  post_install_message:
141
153
  rdoc_options: []
142
154
  require_paths:
@@ -145,14 +157,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
145
157
  requirements:
146
158
  - - ">="
147
159
  - !ruby/object:Gem::Version
148
- version: '0'
160
+ version: 2.5.0
149
161
  required_rubygems_version: !ruby/object:Gem::Requirement
150
162
  requirements:
151
163
  - - ">"
152
164
  - !ruby/object:Gem::Version
153
165
  version: 1.3.1
154
166
  requirements: []
155
- rubygems_version: 3.1.2
167
+ rubygems_version: 3.1.4
156
168
  signing_key:
157
169
  specification_version: 4
158
170
  summary: An opinionated implementation of the Command pattern.
@@ -1,441 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cuprum'
4
-
5
- module Cuprum
6
- # Mixin to implement command chaining functionality for a command class.
7
- # Chaining commands allows you to define complex logic by composing it from
8
- # simpler commands, including branching logic and error handling.
9
- #
10
- # @example Chaining Commands
11
- # # By chaining commands together with the #chain instance method, we set up
12
- # # a series of commands to run in sequence. Each chained command is passed
13
- # # the value of the previous command.
14
- #
15
- # class GenerateUrlCommand
16
- # include Cuprum::Chaining
17
- # include Cuprum::Processing
18
- #
19
- # private
20
- #
21
- # # Acts as a pipeline, taking a value (the title of the given post) and
22
- # # calling the underscore, URL safe, and prepend date commands. By
23
- # # passing parameters to PrependDateCommand, we can customize the command
24
- # # in the pipeline to the current context (in this case, the Post).
25
- # def process post
26
- # UnderscoreCommand.new.
27
- # chain(UrlSafeCommand.new).
28
- # chain(PrependDateCommand.new(post.created_at)).
29
- # call(post.title)
30
- # end
31
- # end
32
- #
33
- # title = 'Greetings, programs!'
34
- # date = '1982-07-09'
35
- # post = Post.new(:title => title, :created_at => date)
36
- # url = GenerateUrlCommand.new.call(post).value
37
- # #=> '1982_07_09_greetings_programs'
38
- #
39
- # title = 'Plasma-based Einhanders in Popular Media'
40
- # date = '1977-05-25'
41
- # post = Post.new(:title => title, :created_at => date)
42
- # url = GenerateUrlCommand.new.call(post).value
43
- # #=> '1977_05_25_plasma_based_einhanders_in_popular_media'
44
- #
45
- # @example Conditional Chaining
46
- # # Commands can be conditionally chained based on the success or failure of
47
- # # the previous command using the on: keyword. If the command is chained
48
- # # using on: :success, it will only be called if the result is passing.
49
- # # If the command is chained using on: :failure, it will only be called if
50
- # # the command is failing. This can be used to perform error handling.
51
- #
52
- # class CreateTaggingCommand
53
- # include Cuprum::Chaining
54
- # include Cuprum::Processing
55
- #
56
- # private
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
- #
86
- # # Tries to find the tag with the given name. If that fails, creates a
87
- # # new tag with the given name. If the tag is found, or if the new tag is
88
- # # successfully created, then creates a tagging using the tag. If the tag
89
- # # is not found and cannot be created, then the tagging is not created
90
- # # and the result of the CreateTaggingCommand is a failure with the
91
- # # appropriate error messages.
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.
102
- # tag.create_tagging(taggable)
103
- # end
104
- # .call(tag_name)
105
- # end
106
- # end
107
- #
108
- # post = Post.create(:title => 'Tagging Example')
109
- # example_tag = Tag.create(:name => 'Example Tag')
110
- #
111
- # result = CreateTaggingCommand.new.call(post, 'Example Tag')
112
- # result.success? #=> true
113
- # result.error #=> nil
114
- # result.value #=> an instance of Tagging
115
- # post.tags.map(&:name)
116
- # #=> ['Example Tag']
117
- #
118
- # result = CreateTaggingCommand.new.call(post, 'Another Tag')
119
- # result.success? #=> true
120
- # result.error #=> nil
121
- # result.value #=> an instance of Tagging
122
- # post.tags.map(&:name)
123
- # #=> ['Example Tag', 'Another Tag']
124
- #
125
- # result = CreateTaggingCommand.new.call(post, 'An Invalid Tag Name')
126
- # result.success? #=> false
127
- # result.error #=> [{ tag: { name: ['is invalid'] }}]
128
- # post.tags.map(&:name)
129
- # #=> ['Example Tag', 'Another Tag']
130
- #
131
- # @example Yield Result and Tap Result
132
- # # The #yield_result method allows for advanced control over a step in the
133
- # # command chain. The block will be yielded the result at that point in the
134
- # # chain, and will wrap the returned value in a result to the next chained
135
- # # command (or return it directly if the returned value is a result).
136
- # #
137
- # # The #tap_result method inserts arbitrary code into the command chain
138
- # # without interrupting it. The block will be yielded the result at that
139
- # # point in the chain and will pass that same result to the next chained
140
- # # command after executing the block. The return value of the block is
141
- # # ignored.
142
- #
143
- # class UpdatePostCommand
144
- # include Cuprum::Chaining
145
- # include Cuprum::Processing
146
- #
147
- # private
148
- #
149
- # def process id, attributes
150
- # # First, find the referenced post.
151
- # Find.new(Post).call(id).
152
- # yield_result(:on => :failure) do |result|
153
- # redirect_to posts_path
154
- # end.
155
- # yield_result(on: :success) do |result|
156
- # # Assign our attributes and save the post.
157
- # UpdateAttributes.new.call(result.value, attributes)
158
- # end.
159
- # tap_result(:on => :success) do |result|
160
- # # Create our tags, but still return the result of our update.
161
- # attributes[:tags].each do |tag_name|
162
- # CreateTaggingCommand.new.call(result.value, tag_name)
163
- # end
164
- # end.
165
- # tap_result(:on => :always) do |result|
166
- # # Chaining :on => :always ensures that the command will be run,
167
- # # even if the previous result is failing.
168
- # if result.failure?
169
- # log_errors(
170
- # :command => UpdatePostCommand,
171
- # :error => result.error
172
- # )
173
- # end
174
- # end
175
- # end
176
- # end
177
- #
178
- # @example Protected Chaining Methods
179
- # # Using the protected chaining methods #chain!, #tap_result!, and
180
- # # #yield_result!, you can create a command class that composes other
181
- # # commands.
182
- #
183
- # # We subclass the build command, which will be executed first.
184
- # class CreateCommentCommand < BuildCommentCommand
185
- # include Cuprum::Chaining
186
- # include Cuprum::Processing
187
- #
188
- # def initialize
189
- # # After the build step is run, we validate the comment.
190
- # chain!(ValidateCommentCommand.new)
191
- #
192
- # # If the validation passes, we then save the comment.
193
- # chain!(SaveCommentCommand.new, on: :success)
194
- # end
195
- # end
196
- #
197
- # @see Cuprum::Command
198
- module Chaining
199
- # (see Cuprum::Processing#call)
200
- def call(*args, **kwargs, &block)
201
- yield_chain(super)
202
- end
203
-
204
- # Creates a copy of the first command, and then chains the given command or
205
- # block to execute after the first command's implementation. When #call is
206
- # executed, each chained command will be called with the previous result
207
- # value, and its result property will be set to the previous result. The
208
- # return value will be wrapped in a result and returned or yielded to the
209
- # next block.
210
- #
211
- # @return [Cuprum::Chaining] A copy of the command, with the chained
212
- # command.
213
- #
214
- # @see #yield_result
215
- #
216
- # @overload chain(command, on: nil)
217
- # @param command [Cuprum::Command] The command to chain.
218
- #
219
- # @param on [Symbol] Sets a condition on when the chained block can run,
220
- # based on the previous result. Valid values are :success, :failure, and
221
- # :always. If the value is :success, the block will be called only if
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.
227
- #
228
- # @overload chain(on: nil) { |value| }
229
- # Creates an anonymous command from the given block. The command will be
230
- # passed the value of the previous result.
231
- #
232
- # @param on [Symbol] Sets a condition on when the chained block can run,
233
- # based on the previous result. Valid values are :success, :failure, and
234
- # :always. If the value is :success, the block will be called only if
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.
240
- #
241
- # @yieldparam value [Object] The value of the previous result.
242
- def chain(command = nil, on: nil, &block)
243
- clone.chain!(command, on: on, &block)
244
- end
245
-
246
- # As #yield_result, but always returns the previous result when the block is
247
- # called. The return value of the block is discarded.
248
- #
249
- # @param (see #yield_result)
250
- #
251
- # @yieldparam result [Cuprum::Result] The #result of the previous command.
252
- #
253
- # @return (see #yield_result)
254
- #
255
- # @see #yield_result
256
- def tap_result(on: nil, &block)
257
- clone.tap_result!(on: on, &block)
258
- end
259
-
260
- # Creates a copy of the command, and then chains the block to execute after
261
- # the command implementation. When #call is executed, each chained block
262
- # will be yielded the previous result, and the return value wrapped in a
263
- # result and returned or yielded to the next block.
264
- #
265
- # @param on [Symbol] Sets a condition on when the chained block can run,
266
- # based on the previous result. Valid values are :success, :failure, and
267
- # :always. If the value is :success, the block will be called only if the
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
271
- # value is given, the command will run whether the previous command was a
272
- # success or a failure.
273
- #
274
- # @yieldparam result [Cuprum::Result] The #result of the previous command.
275
- #
276
- # @return [Cuprum::Chaining] A copy of the command, with the chained block.
277
- #
278
- # @see #tap_result
279
- def yield_result(on: nil, &block)
280
- clone.yield_result!(on: on, &block)
281
- end
282
-
283
- protected
284
-
285
- # rubocop:disable Metrics/MethodLength
286
-
287
- # @!visibility public
288
- #
289
- # As #chain, but modifies the current command instead of creating a clone.
290
- # This is a protected method, and is meant to be called by the command to be
291
- # chained, such as during #initialize.
292
- #
293
- # @return [Cuprum::Chaining] The current command.
294
- #
295
- # @see #chain
296
- #
297
- # @overload chain!(command, on: nil)
298
- # @param command [Cuprum::Command] The command to chain.
299
- #
300
- # @param on [Symbol] Sets a condition on when the chained block can run,
301
- # based on the previous result. Valid values are :success, :failure, and
302
- # :always. If the value is :success, the block will be called only if
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.
308
- #
309
- # @overload chain!(on: nil) { |value| }
310
- # Creates an anonymous command from the given block. The command will be
311
- # passed the value of the previous result.
312
- #
313
- # @param on [Symbol] Sets a condition on when the chained block can run,
314
- # based on the previous result. Valid values are :success, :failure, and
315
- # :always. If the value is :success, the block will be called only if
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.
321
- #
322
- # @yieldparam value [Object] The value of the previous result.
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
-
329
- command ||= Cuprum::Command.new(&block)
330
-
331
- chained_procs <<
332
- {
333
- proc: chain_command(command),
334
- on: on
335
- } # end hash
336
-
337
- self
338
- end
339
- # rubocop:enable Metrics/MethodLength
340
-
341
- def chained_procs
342
- @chained_procs ||= []
343
- end
344
-
345
- # rubocop:disable Metrics/MethodLength
346
-
347
- # @!visibility public
348
- #
349
- # As #tap_result, but modifies the current command instead of creating a
350
- # clone. This is a protected method, and is meant to be called by the
351
- # command to be chained, such as during #initialize.
352
- #
353
- # @param (see #tap_result)
354
- #
355
- # @yieldparam result [Cuprum::Result] The #result of the previous command.
356
- #
357
- # @return (see #tap_result)
358
- #
359
- # @see #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
-
366
- tapped = ->(result) { result.tap { block.call(result) } }
367
-
368
- chained_procs <<
369
- {
370
- proc: tapped,
371
- on: on
372
- } # end hash
373
-
374
- self
375
- end
376
- # rubocop:enable Metrics/MethodLength
377
-
378
- # @!visibility public
379
- #
380
- # As #yield_result, but modifies the current command instead of creating a
381
- # clone. This is a protected method, and is meant to be called by the
382
- # command to be chained, such as during #initialize.
383
- #
384
- # @param (see #yield_result)
385
- #
386
- # @yieldparam result [Cuprum::Result] The #result of the previous command.
387
- #
388
- # @return (see #yield_result)
389
- #
390
- # @see #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
-
397
- chained_procs <<
398
- {
399
- proc: block,
400
- on: on
401
- } # end hash
402
-
403
- self
404
- end
405
-
406
- private
407
-
408
- def chain_command(command)
409
- if command.arity.zero?
410
- ->(_result) { command.call }
411
- else
412
- ->(result) { command.call(result.value) }
413
- end
414
- end
415
-
416
- def skip_chained_proc?(last_result, on:)
417
- return false if on == :always
418
-
419
- case on
420
- when :success
421
- !last_result.success?
422
- when :failure
423
- !last_result.failure?
424
- end
425
- end
426
-
427
- def yield_chain(first_result)
428
- chained_procs.reduce(first_result) do |result, hsh|
429
- next result if skip_chained_proc?(result, on: hsh[:on])
430
-
431
- value = hsh.fetch(:proc).call(result)
432
-
433
- if value_is_result?(value)
434
- value.to_cuprum_result
435
- else
436
- build_result(value: value)
437
- end
438
- end
439
- end
440
- end
441
- end