cuprum 1.0.0 → 1.1.0.rc.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 +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +44 -1494
- data/lib/cuprum/built_in/identity_command.rb +4 -0
- data/lib/cuprum/built_in/identity_operation.rb +4 -4
- data/lib/cuprum/built_in/null_command.rb +5 -1
- data/lib/cuprum/command.rb +27 -16
- data/lib/cuprum/command_factory.rb +4 -4
- data/lib/cuprum/currying/curried_command.rb +1 -1
- data/lib/cuprum/error.rb +42 -0
- data/lib/cuprum/errors/command_not_implemented.rb +2 -3
- data/lib/cuprum/errors/multiple_errors.rb +39 -0
- data/lib/cuprum/errors/operation_not_called.rb +2 -3
- data/lib/cuprum/errors/uncaught_exception.rb +2 -2
- data/lib/cuprum/errors.rb +6 -1
- data/lib/cuprum/exception_handling.rb +4 -2
- data/lib/cuprum/map_command.rb +227 -0
- data/lib/cuprum/matcher.rb +2 -0
- data/lib/cuprum/matcher_list.rb +2 -2
- data/lib/cuprum/matching.rb +3 -1
- data/lib/cuprum/middleware.rb +20 -13
- data/lib/cuprum/operation.rb +8 -10
- data/lib/cuprum/processing.rb +22 -11
- data/lib/cuprum/result_list.rb +187 -0
- data/lib/cuprum/rspec/be_a_result_matcher.rb +6 -6
- data/lib/cuprum/steps.rb +4 -1
- data/lib/cuprum/utils/instance_spy.rb +22 -24
- data/lib/cuprum/version.rb +3 -3
- data/lib/cuprum.rb +3 -2
- metadata +20 -17
@@ -14,15 +14,15 @@ module Cuprum::BuiltIn
|
|
14
14
|
# #=> true
|
15
15
|
#
|
16
16
|
# @example With a result.
|
17
|
-
#
|
18
|
-
# value = Cuprum::Result.new('result value', :
|
17
|
+
# error = 'errors.messages.unknown'
|
18
|
+
# value = Cuprum::Result.new(value: 'result value', error: error)
|
19
19
|
# operation = IdentityOperation.new.call(value)
|
20
20
|
# operation.value
|
21
21
|
# #=> 'result value'
|
22
22
|
# operation.success?
|
23
23
|
# #=> false
|
24
|
-
# operation.
|
25
|
-
# #=>
|
24
|
+
# operation.error
|
25
|
+
# #=> 'errors.messages.unknown'
|
26
26
|
class IdentityOperation < Cuprum::BuiltIn::IdentityCommand
|
27
27
|
include Cuprum::Operation::Mixin
|
28
28
|
end
|
data/lib/cuprum/command.rb
CHANGED
@@ -5,8 +5,15 @@ require 'cuprum/processing'
|
|
5
5
|
require 'cuprum/steps'
|
6
6
|
|
7
7
|
module Cuprum
|
8
|
-
# Functional object that encapsulates a business logic operation
|
9
|
-
#
|
8
|
+
# Functional object that encapsulates a business logic operation or step.
|
9
|
+
#
|
10
|
+
# Using commands allows the developer to maintain a state or context, such as
|
11
|
+
# by passing context into the constructor. It provides a consistent interface
|
12
|
+
# by always returning a Cuprum::Result object, which tracks the status of the
|
13
|
+
# command call, the returned value, and the error object (if any). Finally, as
|
14
|
+
# a full-fledged Ruby object a Command can be passed around like any other
|
15
|
+
# object, including returned from a method (or another Command) or passed in
|
16
|
+
# as a parameter.
|
10
17
|
#
|
11
18
|
# A Command can be defined either by passing a block to the constructor, or
|
12
19
|
# by defining a subclass of Command and implementing the #process method.
|
@@ -19,13 +26,11 @@ module Cuprum
|
|
19
26
|
#
|
20
27
|
# @example A Command subclass
|
21
28
|
# class MultiplyCommand < Cuprum::Command
|
22
|
-
# def initialize
|
29
|
+
# def initialize(multiplier)
|
23
30
|
# @multiplier = multiplier
|
24
31
|
# end
|
25
32
|
#
|
26
|
-
# private
|
27
|
-
#
|
28
|
-
# def process int
|
33
|
+
# private def process(int)
|
29
34
|
# int * @multiplier
|
30
35
|
# end
|
31
36
|
# end
|
@@ -37,13 +42,11 @@ module Cuprum
|
|
37
42
|
#
|
38
43
|
# @example A Command with an error state
|
39
44
|
# class DivideCommand < Cuprum::Command
|
40
|
-
# def initialize
|
45
|
+
# def initialize(divisor)
|
41
46
|
# @divisor = divisor
|
42
47
|
# end
|
43
48
|
#
|
44
|
-
# private
|
45
|
-
#
|
46
|
-
# def process int
|
49
|
+
# private def process(int)
|
47
50
|
# if @divisor.zero?
|
48
51
|
# return Cuprum::Result.new(error: 'errors.messages.divide_by_zero')
|
49
52
|
# end
|
@@ -72,10 +75,16 @@ module Cuprum
|
|
72
75
|
|
73
76
|
# Returns a new instance of Cuprum::Command.
|
74
77
|
#
|
75
|
-
# @yield
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
78
|
+
# @yield If a block is given, the block is used to define a private #process
|
79
|
+
# method. This overwrites any existing #process method. When the command
|
80
|
+
# is called, #process will be called internally and passed the parameters.
|
81
|
+
#
|
82
|
+
# @yieldparam arguments [Array] the arguments passed to #call.
|
83
|
+
# @yieldparam keywords [Hash] the keywords passed to #call.
|
84
|
+
# @yieldparam block [Proc, nil] the block passed to call, #if any.
|
85
|
+
#
|
86
|
+
# @yieldreturn [Cuprum::Result, Object] the returned result or object is
|
87
|
+
# converted to a Cuprum::Result and returned by #call.
|
79
88
|
def initialize(&implementation)
|
80
89
|
return unless implementation
|
81
90
|
|
@@ -96,11 +105,13 @@ module Cuprum
|
|
96
105
|
#
|
97
106
|
# @return [Proc] the wrapping proc.
|
98
107
|
def to_proc
|
108
|
+
command = self
|
109
|
+
|
99
110
|
@to_proc ||= lambda do |*args, **kwargs, &block|
|
100
111
|
if kwargs.empty?
|
101
|
-
call(*args, &block)
|
112
|
+
command.call(*args, &block)
|
102
113
|
else
|
103
|
-
call(*args, **kwargs, &block)
|
114
|
+
command.call(*args, **kwargs, &block)
|
104
115
|
end
|
105
116
|
end
|
106
117
|
end
|
@@ -9,11 +9,11 @@ module Cuprum
|
|
9
9
|
#
|
10
10
|
# @example
|
11
11
|
# class SpaceFactory < Cuprum::CommandFactory
|
12
|
-
# command
|
12
|
+
# command(:build, BuildCommand)
|
13
13
|
#
|
14
|
-
# command
|
14
|
+
# command(:fly) { |launch_site:| FlyCommand.new(launch_site) }
|
15
15
|
#
|
16
|
-
# command_class
|
16
|
+
# command_class(:dream) { DreamCommand }
|
17
17
|
# end
|
18
18
|
#
|
19
19
|
# factory = SpaceFactory.new
|
@@ -81,7 +81,7 @@ module Cuprum
|
|
81
81
|
#
|
82
82
|
# @yield The block will be executed in the context of the factory
|
83
83
|
# instance.
|
84
|
-
# @yieldparam
|
84
|
+
# @yieldparam args [Array] Any arguments given to the method
|
85
85
|
# factory.name() will be passed on the block.
|
86
86
|
# @yieldreturn [Cuprum::Command] The block return an instance of a
|
87
87
|
# Cuprum::Command subclass, or else raise an error.
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'cuprum/currying'
|
4
4
|
|
5
5
|
module Cuprum::Currying
|
6
|
-
# A CurriedCommand wraps another command and passes preset
|
6
|
+
# A CurriedCommand wraps another command and passes preset params to #call.
|
7
7
|
#
|
8
8
|
# @example Currying Arguments
|
9
9
|
# # Our base command takes two arguments.
|
data/lib/cuprum/error.rb
CHANGED
@@ -7,6 +7,48 @@ module Cuprum
|
|
7
7
|
#
|
8
8
|
# Additional details can be passed by setting the #message or by using a
|
9
9
|
# subclass of Cuprum::Error.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# error = Cuprum::Error.new(message: 'Something went wrong')
|
13
|
+
# error.type
|
14
|
+
# #=> 'cuprum.error'
|
15
|
+
# error.message
|
16
|
+
# #=> 'Something went wrong'
|
17
|
+
#
|
18
|
+
# @example An Error With Custom Type
|
19
|
+
# error = Cuprum::Error.new(
|
20
|
+
# message: 'Something went wrong',
|
21
|
+
# type: 'custom.errors.generic',
|
22
|
+
# )
|
23
|
+
# error.type
|
24
|
+
# #=> 'custom.errors.generic'
|
25
|
+
#
|
26
|
+
# @example An Error Subclass
|
27
|
+
# class LightsError < Cuprum::Error
|
28
|
+
# TYPE = 'custom.errors.wrong_number_of_lights'
|
29
|
+
#
|
30
|
+
# def initialize(count)
|
31
|
+
# super(message: "There are #{count} lights!")
|
32
|
+
#
|
33
|
+
# @count = count
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# private def as_json_data
|
37
|
+
# { 'count' => count }
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# error = LightsError.new(4)
|
42
|
+
# error.type
|
43
|
+
# #=> 'custom.errors.wrong_number_of_lights'
|
44
|
+
# error.message
|
45
|
+
# #=> 'There are 4 lights!'
|
46
|
+
# error.as_json
|
47
|
+
# #=> {
|
48
|
+
# # 'data' => { 'count' => 4 },
|
49
|
+
# # 'message' => 'There are 4 lights!',
|
50
|
+
# # 'type' => 'custom.errors.wrong_number_of_lights'
|
51
|
+
# # }
|
10
52
|
class Error
|
11
53
|
# Short string used to identify the type of error.
|
12
54
|
#
|
@@ -4,14 +4,13 @@ require 'cuprum/error'
|
|
4
4
|
require 'cuprum/errors'
|
5
5
|
|
6
6
|
module Cuprum::Errors
|
7
|
-
# Error
|
8
|
-
# method.
|
7
|
+
# Error returned when a Command is called without defining a #process method.
|
9
8
|
class CommandNotImplemented < Cuprum::Error
|
10
9
|
COMPARABLE_PROPERTIES = %i[command].freeze
|
11
10
|
private_constant :COMPARABLE_PROPERTIES
|
12
11
|
|
13
|
-
# Format for generating error message.
|
14
12
|
MESSAGE_FORMAT = 'no implementation defined for %s'
|
13
|
+
private_constant :MESSAGE_FORMAT
|
15
14
|
|
16
15
|
# Short string used to identify the type of error.
|
17
16
|
TYPE = 'cuprum.errors.command_not_implemented'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/error'
|
4
|
+
require 'cuprum/errors'
|
5
|
+
|
6
|
+
module Cuprum::Errors
|
7
|
+
# Error wrapping multiple errors from a collection command.
|
8
|
+
class MultipleErrors < Cuprum::Error
|
9
|
+
# Short string used to identify the type of error.
|
10
|
+
TYPE = 'cuprum.errors.multiple_errors'
|
11
|
+
|
12
|
+
# @param errors [Array<Cuprum::Error>] The wrapped errors.
|
13
|
+
# @param message [String] Optional message describing the nature of the
|
14
|
+
# error.
|
15
|
+
def initialize(errors:, message: nil)
|
16
|
+
@errors = errors
|
17
|
+
|
18
|
+
super(
|
19
|
+
errors: errors,
|
20
|
+
message: message || default_message
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Array<Cuprum::Error>] the wrapped errors.
|
25
|
+
attr_reader :errors
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def as_json_data
|
30
|
+
{
|
31
|
+
'errors' => errors.map { |error| error&.as_json }
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_message
|
36
|
+
'the command encountered one or more errors'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -4,11 +4,10 @@ require 'cuprum/error'
|
|
4
4
|
require 'cuprum/errors'
|
5
5
|
|
6
6
|
module Cuprum::Errors
|
7
|
-
# Error
|
8
|
-
# Operation.
|
7
|
+
# Error returned when trying to access the result of an uncalled Operation.
|
9
8
|
class OperationNotCalled < Cuprum::Error
|
10
|
-
# Format for generating error message.
|
11
9
|
MESSAGE_FORMAT = '%s was not called and does not have a result'
|
10
|
+
private_constant :MESSAGE_FORMAT
|
12
11
|
|
13
12
|
# Short string used to identify the type of error.
|
14
13
|
TYPE = 'cuprum.errors.operation_not_called'
|
@@ -4,7 +4,7 @@ require 'cuprum/error'
|
|
4
4
|
require 'cuprum/errors'
|
5
5
|
|
6
6
|
module Cuprum::Errors
|
7
|
-
#
|
7
|
+
# Error returned when a command encounters an unhandled exception.
|
8
8
|
class UncaughtException < Cuprum::Error
|
9
9
|
# Short string used to identify the type of error.
|
10
10
|
TYPE = 'cuprum.collections.errors.uncaught_exception'
|
@@ -45,7 +45,7 @@ module Cuprum::Errors
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def generate_message(message)
|
48
|
-
message = "#{message} #{exception.class}: #{exception.message}"
|
48
|
+
message = "#{message.rstrip} #{exception.class}: #{exception.message}"
|
49
49
|
|
50
50
|
return message unless cause
|
51
51
|
|
data/lib/cuprum/errors.rb
CHANGED
@@ -4,5 +4,10 @@ require 'cuprum'
|
|
4
4
|
|
5
5
|
module Cuprum
|
6
6
|
# Namespace for custom Cuprum error classes.
|
7
|
-
module Errors
|
7
|
+
module Errors
|
8
|
+
autoload :CommandNotImplemented, 'cuprum/errors/command_not_implemented'
|
9
|
+
autoload :MultipleErrors, 'cuprum/errors/multiple_errors'
|
10
|
+
autoload :OperationNotCalled, 'cuprum/errors/operation_not_called'
|
11
|
+
autoload :UncaughtException, 'cuprum/errors/uncaught_exception'
|
12
|
+
end
|
8
13
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'cuprum/errors/uncaught_exception'
|
4
4
|
|
5
5
|
module Cuprum
|
6
|
-
# Utility
|
6
|
+
# Utility module for handling uncaught exceptions in commands.
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# class UnsafeCommand < Cuprum::Command
|
@@ -27,7 +27,7 @@ module Cuprum
|
|
27
27
|
# #=> a Cuprum::Errors::UncaughtException error.
|
28
28
|
# result.error.message
|
29
29
|
# #=> 'uncaught exception in SafeCommand -' \
|
30
|
-
#
|
30
|
+
# # ' StandardError: Something went wrong.'
|
31
31
|
module ExceptionHandling
|
32
32
|
# Wraps the #call method with a rescue clause matching any StandardError.
|
33
33
|
#
|
@@ -37,6 +37,8 @@ module Cuprum
|
|
37
37
|
#
|
38
38
|
# @return [Cuprum::Result] the result of calling the superclass method, or
|
39
39
|
# a failing result if a StandardError is raised.
|
40
|
+
#
|
41
|
+
# @see Cuprum::Processing#call
|
40
42
|
def call(*args, **kwargs, &block)
|
41
43
|
super
|
42
44
|
rescue StandardError => exception
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum'
|
4
|
+
|
5
|
+
module Cuprum
|
6
|
+
# Calls the command implementation with each item in the given enumerable.
|
7
|
+
#
|
8
|
+
# A regular Command is called with a set of parameters, calls the command
|
9
|
+
# implementation once with those parameters, and returns the Result. In
|
10
|
+
# contrast, a MapCommand is called with an Enumerable object, such as an
|
11
|
+
# Array, a Hash, or an Enumerator (e.g. by calling #each without a block). The
|
12
|
+
# MapCommand implementation is then called with each item in the
|
13
|
+
# Enumerable - for example, if called with an Array with three items, the
|
14
|
+
# MapCommand implementation would be called three times, once with each item.
|
15
|
+
# Finally, the Results returned by calling the implementation with each item
|
16
|
+
# are aggregated together into a Cuprum::ResultList. A ResultList behaves like
|
17
|
+
# a Result, and provides the standard methods (such as #status, #error, and
|
18
|
+
# #value), but also includes a reference to the #results used to create the
|
19
|
+
# ResultList, and their respective #errors and #values as Arrays.
|
20
|
+
#
|
21
|
+
# Like a standard Command, a MapCommand can be defined either by passing a
|
22
|
+
# block to the constructor, or by defining a subclass of MapCommand and
|
23
|
+
# implementing the #process method. If the given block or the #process method
|
24
|
+
# accepts more than one argument, the enumerable item is destructured using
|
25
|
+
# the splat operator (*); this enables using a MapCommand to map over the keys
|
26
|
+
# and values of a Hash. This is the same behavior seen when passing a block
|
27
|
+
# with multiple arguments to a native #each method.
|
28
|
+
#
|
29
|
+
# If a MapCommand is initialized with the :allow_partial keyword, the
|
30
|
+
# ResultList will be passing as long as there is at least one passing Result
|
31
|
+
# (or if the MapCommand is called with an empty Enumerable). See
|
32
|
+
# ResultList#allow_partial? for details.
|
33
|
+
#
|
34
|
+
# @example A MapCommand with a block
|
35
|
+
# titleize_command = Cuprum::MapCommand.new do |str|
|
36
|
+
# if str.nil? || str.empty?
|
37
|
+
# next failure(Cuprum::Error.new(message: "can't be blank"))
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# str.split(' ').map(&:capitalize).join(' ')
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# @example A MapCommand Subclass
|
44
|
+
# class TitleizeCommand < Cuprum::MapCommand
|
45
|
+
# private
|
46
|
+
#
|
47
|
+
# def process(str)
|
48
|
+
# if str.nil? || str.empty?
|
49
|
+
# return failure(Cuprum::Error.new(message: "can't be blank"))
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# str.split(' ').map(&:capitalize).join(' ')
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# titleize_command = TitleizeCommand.new
|
57
|
+
#
|
58
|
+
# @example With an Array with passing Results
|
59
|
+
# results = titleize_command.call(['hello world', 'greetings programs'])
|
60
|
+
# results.class
|
61
|
+
# #=> Cuprum::ResultsList
|
62
|
+
# results.status
|
63
|
+
# #=> :success
|
64
|
+
# results.value
|
65
|
+
# #=> ['Hello World', 'Greetings Programs']
|
66
|
+
# results.values
|
67
|
+
# #=> ['Hello World', 'Greetings Programs']
|
68
|
+
# results.error
|
69
|
+
# #=> nil
|
70
|
+
# results.errors
|
71
|
+
# #=> [nil, nil]
|
72
|
+
#
|
73
|
+
# @example With an Array with failing Results
|
74
|
+
# results = titleize_command.call([nil, ''])
|
75
|
+
# results.status
|
76
|
+
# #=> :failure
|
77
|
+
# results.value
|
78
|
+
# #=> [nil, nil]
|
79
|
+
# results.values
|
80
|
+
# #=> [nil, nil]
|
81
|
+
# results.error.class
|
82
|
+
# #=> Cuprum::Errors::MultipleErrors
|
83
|
+
# results.errors.map(&:class)
|
84
|
+
# #=> [Cuprum::Error, Cuprum::Error]
|
85
|
+
# results.errors.first.message
|
86
|
+
# #=> "can't be blank"
|
87
|
+
#
|
88
|
+
# @example With an Array with mixed passing and failing Results
|
89
|
+
# results = titleize_command.call([nil, 'greetings programs'])
|
90
|
+
# results.status
|
91
|
+
# #=> :failure
|
92
|
+
# results.value
|
93
|
+
# #=> [nil, "Greetings Programs"]
|
94
|
+
# results.values
|
95
|
+
# #=> [nil, "Greetings Programs"]
|
96
|
+
# results.error.class
|
97
|
+
# #=> Cuprum::Errors::MultipleErrors
|
98
|
+
# results.errors.map(&:class)
|
99
|
+
# #=> [Cuprum::Error, nil]
|
100
|
+
# results.errors.first.message
|
101
|
+
# #=> "can't be blank"
|
102
|
+
#
|
103
|
+
# @example With an Empty Array
|
104
|
+
# results = titleize_command.call([])
|
105
|
+
# results.status
|
106
|
+
# #=> :success
|
107
|
+
# results.value
|
108
|
+
# #=> []
|
109
|
+
# results.values
|
110
|
+
# #=> []
|
111
|
+
# results.error
|
112
|
+
# #=> nil
|
113
|
+
# results.errors
|
114
|
+
# #=> []
|
115
|
+
#
|
116
|
+
# @example With a Hash
|
117
|
+
# inspect_command = Cuprum::MapCommand.new do |key, value|
|
118
|
+
# "#{key.inspect} => #{value.inspect}"
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# results = inspect_command.call({ ichi: 1, "ni" => 2 })
|
122
|
+
# results.status
|
123
|
+
# #=> :success
|
124
|
+
# results.value
|
125
|
+
# #=> [':ichi => 1', '"ni" => 2']
|
126
|
+
# results.values
|
127
|
+
# #=> [':ichi => 1', '"ni" => 2']
|
128
|
+
# results.error
|
129
|
+
# #=> nil
|
130
|
+
# results.errors
|
131
|
+
# #=> [nil, nil]
|
132
|
+
#
|
133
|
+
# @example With an Enumerable
|
134
|
+
# square_command = Cuprum::MapCommand.new { |i| i ** 2 }
|
135
|
+
#
|
136
|
+
# results = square_command.call(0...4)
|
137
|
+
# results.status
|
138
|
+
# #=> :success
|
139
|
+
# results.value
|
140
|
+
# #=> [0, 1, 4, 9]
|
141
|
+
# results.values
|
142
|
+
# #=> [0, 1, 4, 9]
|
143
|
+
#
|
144
|
+
# @example With allow_partial: true
|
145
|
+
# maybe_upcase_command = Cuprum::MapCommand.new do |str|
|
146
|
+
# next str.upcase if str.is_a?(String)
|
147
|
+
#
|
148
|
+
# failure(Cuprum::Error.new(message: 'not a String'))
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# results = maybe_upcase_command.call([nil, 'greetings', 'programs'])
|
152
|
+
# results.status
|
153
|
+
# #=> :success
|
154
|
+
# results.value
|
155
|
+
# #=> [nil, 'GREETINGS', 'PROGRAMS']
|
156
|
+
# results.values
|
157
|
+
# #=> [nil, 'GREETINGS', 'PROGRAMS']
|
158
|
+
# results.error.class
|
159
|
+
# #=> Cuprum::Errors::MultipleErrors
|
160
|
+
# results.errors.map(&:class)
|
161
|
+
# #=> [Cuprum::Error, nil, nil]
|
162
|
+
# results.errors.first.message
|
163
|
+
# #=> 'not a String'
|
164
|
+
#
|
165
|
+
# @see Cuprum::Command
|
166
|
+
# @see Cuprum::ResultList
|
167
|
+
class MapCommand < Cuprum::Command
|
168
|
+
# @overload initialize(allow_partial: false)
|
169
|
+
# @param allow_partial [true, false] If true, allows for some failing
|
170
|
+
# results as long as there is at least one passing result. Defaults to
|
171
|
+
# false.
|
172
|
+
#
|
173
|
+
# @overload initialize(allow_partial: false) { |item| }
|
174
|
+
# @param allow_partial [true, false] If true, allows for some failing
|
175
|
+
# results as long as there is at least one passing result. Defaults to
|
176
|
+
# false.
|
177
|
+
# @yield The command implementation, to be called with each successive
|
178
|
+
# item in the given enumerable. This overrides the #process method, if
|
179
|
+
# any.
|
180
|
+
# @yieldparam [Object] item Each item in the given Enumerable.
|
181
|
+
#
|
182
|
+
# @overload initialize(allow_partial: false) { |key, value| }
|
183
|
+
# @param allow_partial [true, false] If true, allows for some failing
|
184
|
+
# results as long as there is at least one passing result. Defaults to
|
185
|
+
# false.
|
186
|
+
# @yield The command implementation, to be called with each successive
|
187
|
+
# item in the given enumerable. This overrides the #process method, if
|
188
|
+
# any.
|
189
|
+
# @yieldparam [Object] key Each key in the given Hash.
|
190
|
+
# @yieldparam [Object] value Each value in the given Hash.
|
191
|
+
def initialize(allow_partial: false, &implementation)
|
192
|
+
super(&implementation)
|
193
|
+
|
194
|
+
@allow_partial = allow_partial
|
195
|
+
end
|
196
|
+
|
197
|
+
# @return [true, false] if true, allows for some failing results as long as
|
198
|
+
# there is at least one passing result. Defaults to false.
|
199
|
+
def allow_partial?
|
200
|
+
@allow_partial
|
201
|
+
end
|
202
|
+
|
203
|
+
# Calls the command implementation for each item in the given Enumerable.
|
204
|
+
#
|
205
|
+
# @param enumerable [Array, Hash, Enumerable] The collection or enumerable
|
206
|
+
# object to map.
|
207
|
+
#
|
208
|
+
# @return [Cuprum::ResultList] the aggregated results.
|
209
|
+
def call(enumerable)
|
210
|
+
build_result_list(
|
211
|
+
enumerable.map { |item| splat_items? ? super(*item) : super(item) }
|
212
|
+
)
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def build_result_list(results)
|
218
|
+
Cuprum::ResultList.new(*results, allow_partial: allow_partial?)
|
219
|
+
end
|
220
|
+
|
221
|
+
def splat_items?
|
222
|
+
return @splat_items unless @splat_items.nil?
|
223
|
+
|
224
|
+
@splat_items = method(:process).arity > 1
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
data/lib/cuprum/matcher.rb
CHANGED
@@ -63,6 +63,8 @@ module Cuprum
|
|
63
63
|
class Matcher
|
64
64
|
include Cuprum::Matching
|
65
65
|
|
66
|
+
# @!parse extend Cuprum::Matching::ClassMethods
|
67
|
+
|
66
68
|
# @param match_context [Object] the execution context for a matching clause.
|
67
69
|
#
|
68
70
|
# @yield Executes the block in the context of the singleton class. This is
|
data/lib/cuprum/matcher_list.rb
CHANGED
@@ -76,10 +76,10 @@ module Cuprum
|
|
76
76
|
#
|
77
77
|
# @param result [Cuprum::Result] The result to match.
|
78
78
|
#
|
79
|
-
# @raise Cuprum::Matching::NoMatchError if none of the matchers match the
|
79
|
+
# @raise [Cuprum::Matching::NoMatchError] if none of the matchers match the
|
80
80
|
# given result.
|
81
81
|
#
|
82
|
-
# @see Cuprum::
|
82
|
+
# @see Cuprum::Matching#call.
|
83
83
|
def call(result)
|
84
84
|
unless result.respond_to?(:to_cuprum_result)
|
85
85
|
raise ArgumentError, 'result must be a Cuprum::Result'
|
data/lib/cuprum/matching.rb
CHANGED
@@ -114,6 +114,8 @@ module Cuprum
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
+
# @!parse extend ClassMethods
|
118
|
+
|
117
119
|
# @return [Object, nil] the execution context for a matching clause.
|
118
120
|
attr_reader :match_context
|
119
121
|
|
@@ -164,7 +166,7 @@ module Cuprum
|
|
164
166
|
#
|
165
167
|
# @raise [NoMatchError] if there is no clause matching the result.
|
166
168
|
#
|
167
|
-
# @see ClassMethods
|
169
|
+
# @see ClassMethods#match
|
168
170
|
# @see #match_context
|
169
171
|
def call(result)
|
170
172
|
unless result.respond_to?(:to_cuprum_result)
|
data/lib/cuprum/middleware.rb
CHANGED
@@ -62,7 +62,7 @@ module Cuprum
|
|
62
62
|
# end
|
63
63
|
# end
|
64
64
|
#
|
65
|
-
# command =
|
65
|
+
# command = ExampleCommand.new
|
66
66
|
# middleware = LoggingMiddleware.new
|
67
67
|
# result = middleware.call(command, { id: 0 })
|
68
68
|
# #=> logs "Calling command ExampleCommand"
|
@@ -81,8 +81,8 @@ module Cuprum
|
|
81
81
|
# end
|
82
82
|
# end
|
83
83
|
#
|
84
|
-
# command = Command.new { |**
|
85
|
-
# middleware =
|
84
|
+
# command = Command.new { |**options| "Options: #{options.inspect}" }
|
85
|
+
# middleware = ApiMiddleware.new
|
86
86
|
# result = middleware.call(command, { id: 0 })
|
87
87
|
# result.value
|
88
88
|
# #=> "Options: { id: 0, api_key: '12345' }"
|
@@ -92,8 +92,8 @@ module Cuprum
|
|
92
92
|
# include Cuprum::Middleware
|
93
93
|
#
|
94
94
|
# # The middleware runs the command once. On a failing result, the
|
95
|
-
# # middleware discards the failing result and returns a result
|
96
|
-
# # value of nil.
|
95
|
+
# # middleware discards the failing result and returns a passing result
|
96
|
+
# # with a value of nil.
|
97
97
|
# private def process(next_command, *args, **kwargs)
|
98
98
|
# result = super
|
99
99
|
#
|
@@ -103,16 +103,23 @@ module Cuprum
|
|
103
103
|
# end
|
104
104
|
# end
|
105
105
|
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
106
|
+
# middleware = IgnoreFailure.new
|
107
|
+
# command = Command.new do |number|
|
108
|
+
# return success('number is even') if number.even?
|
109
|
+
#
|
110
|
+
# error = Cuprum::Error.new(message: 'number is odd')
|
111
|
+
# failure(error)
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# # Calling the command without the middleware.
|
115
|
+
# result = command.call(1)
|
109
116
|
# result.success?
|
110
|
-
# #=>
|
111
|
-
# result.
|
112
|
-
# #=>
|
117
|
+
# #=> false
|
118
|
+
# result.error.message
|
119
|
+
# #=> 'number is odd'
|
113
120
|
#
|
114
|
-
#
|
115
|
-
# result
|
121
|
+
# # Calling the command with the middleware.
|
122
|
+
# result = middleware.call(command, 1)
|
116
123
|
# result.success?
|
117
124
|
# #=> true
|
118
125
|
# result.value
|