cuprum 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +1 -1
- data/lib/cuprum.rb +3 -2
- metadata +18 -15
@@ -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
|