cuprum 0.9.1 → 0.10.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.
@@ -71,7 +71,7 @@ module Cuprum
71
71
  def normalize_status(status)
72
72
  return status unless status.is_a?(String) || status.is_a?(Symbol)
73
73
 
74
- tools.string.underscore(status).intern
74
+ tools.string_tools.underscore(status).intern
75
75
  end
76
76
 
77
77
  def resolve_status(status)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum'
4
+
5
+ module Cuprum
6
+ # Helper methods for generating Cuprum result objects.
7
+ module ResultHelpers
8
+ private
9
+
10
+ def build_result(error: nil, status: nil, value: nil)
11
+ Cuprum::Result.new(error: error, status: status, value: value)
12
+ end
13
+
14
+ def failure(error)
15
+ build_result(error: error)
16
+ end
17
+
18
+ def success(value)
19
+ build_result(value: value)
20
+ end
21
+ end
22
+ end
@@ -191,7 +191,7 @@ module Cuprum::RSpec
191
191
  ary << 'error' if expected_error? && !expected_error.nil?
192
192
 
193
193
  unless ary.empty?
194
- msg = "with the expected #{tools.array.humanize_list(ary)}"
194
+ msg = "with the expected #{tools.array_tools.humanize_list(ary)}"
195
195
  end
196
196
 
197
197
  return msg unless expected_status?
@@ -220,8 +220,8 @@ module Cuprum::RSpec
220
220
  ary << 'value' unless value_matches?
221
221
  ary << 'error' unless error_matches?
222
222
 
223
- ", but the #{tools.array.humanize_list(ary)}" \
224
- " #{tools.integer.pluralize(ary.size, 'does', 'do')} not match:"
223
+ ", but the #{tools.array_tools.humanize_list(ary)}" \
224
+ " #{tools.integer_tools.pluralize(ary.size, 'does', 'do')} not match:"
225
225
  end
226
226
 
227
227
  def properties_warning
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/result_helpers'
4
+
5
+ module Cuprum
6
+ # The Steps supports step by step processes that halt on a failed step.
7
+ #
8
+ # After including Cuprum::Steps, use the #steps instance method to wrap a
9
+ # series of instructions. Each instruction is then defined using the #step
10
+ # method. Steps can be defined either as a block or as a method invocation.
11
+ #
12
+ # When the steps block is evaluated, each step is called in sequence. If the
13
+ # step resolves to a passing result, the result value is returned and
14
+ # execution continues to the next step. If all of the steps pass, then the
15
+ # result of the final step is returned from the #steps block.
16
+ #
17
+ # Conversely, if any step resolves to a failing result, that failing result is
18
+ # immediately returned from the #steps block. No further steps will be called.
19
+ #
20
+ # For example, consider updating a database record using a primary key and an
21
+ # attributes hash. Broken down into its basics, this requires the following
22
+ # instructions:
23
+ #
24
+ # - Using the primary key, find the existing record in the database.
25
+ # - Update the record object with the given attributes.
26
+ # - Save the updated record back to the database.
27
+ #
28
+ # Note that each of these steps can fail for different reasons. For example,
29
+ # if a record with the given primary key does not exist in the database, then
30
+ # the first instruction will fail, and the follow up steps should not be
31
+ # executed. Further, whatever context is executing these steps probably wants
32
+ # to know which step failed, and why.
33
+ #
34
+ # @example Defining Methods As Steps
35
+ # def assign_attributes(record, attributes); end
36
+ #
37
+ # def find_record(primary_key); end
38
+ #
39
+ # def save_record(record); end
40
+ #
41
+ # def update_record(primary_key, attributes)
42
+ # steps do
43
+ # record = step :find_record, primary_key
44
+ # record = step :assign_attributes, record, attributes
45
+ # step :save_record, record
46
+ # end
47
+ # end
48
+ #
49
+ # @example Defining Blocks As Steps
50
+ # class AssignAttributes < Cuprum::Command; end
51
+ #
52
+ # class FindRecord < Cuprum::Command; end
53
+ #
54
+ # class SaveRecord < Cuprum::Command; end
55
+ #
56
+ # def update_record(primary_key, attributes)
57
+ # steps do
58
+ # record = step { FindRecord.new.call(primary_key) }
59
+ # record = step { AssignAttributes.new.call(record, attributes) }
60
+ # step { SaveRecord.new.call(record) }
61
+ # end
62
+ # end
63
+ module Steps
64
+ include Cuprum::ResultHelpers
65
+
66
+ class << self
67
+ # @!visibility private
68
+ def execute_method(receiver, method_name, *args, **kwargs, &block)
69
+ if block_given? && kwargs.empty?
70
+ receiver.send(method_name, *args, &block)
71
+ elsif block_given?
72
+ receiver.send(method_name, *args, **kwargs, &block)
73
+ elsif kwargs.empty?
74
+ receiver.send(method_name, *args)
75
+ else
76
+ receiver.send(method_name, *args, **kwargs)
77
+ end
78
+ end
79
+
80
+ # @!visibility private
81
+ def extract_result_value(result)
82
+ return result unless result.respond_to?(:to_cuprum_result)
83
+
84
+ result = result.to_cuprum_result
85
+
86
+ return result.value if result.success?
87
+
88
+ throw :cuprum_failed_step, result
89
+ end
90
+
91
+ # rubocop:disable Metrics/MethodLength
92
+ # @!visibility private
93
+ def validate_method_name(method_name)
94
+ if method_name.nil?
95
+ raise ArgumentError,
96
+ 'expected a block or a method name',
97
+ caller(1..-1)
98
+ end
99
+
100
+ unless method_name.is_a?(String) || method_name.is_a?(Symbol)
101
+ raise ArgumentError,
102
+ 'expected method name to be a String or Symbol',
103
+ caller(1..-1)
104
+ end
105
+
106
+ return unless method_name.empty?
107
+
108
+ raise ArgumentError, "method name can't be blank", caller(1..-1)
109
+ end
110
+ # rubocop:enable Metrics/MethodLength
111
+ end
112
+
113
+ # @overload step()
114
+ # Executes the block and returns the value, or halts on a failure.
115
+ #
116
+ # @yield Called with no parameters.
117
+ #
118
+ # @return [Object] the #value of the result, or the returned object.
119
+ #
120
+ # The #step method is used to evaluate a sequence of processes, and to
121
+ # fail fast and halt processing if any of the steps returns a failing
122
+ # result. Each invocation of #step should be wrapped in a #steps block,
123
+ # or used inside the #process method of a Command.
124
+ #
125
+ # If the object returned by the block is a Cuprum result or compatible
126
+ # object (such as a called operation), the value is converted to a Cuprum
127
+ # result via the #to_cuprum_result method. Otherwise, the object is
128
+ # returned directly from #step.
129
+ #
130
+ # If the returned object is a passing result, the #value of the result is
131
+ # returned by #step.
132
+ #
133
+ # If the returned object is a failing result, then #step will throw
134
+ # :cuprum_failed_result and the failing result. This is caught by the
135
+ # #steps block, and halts execution of any subsequent steps.
136
+ #
137
+ # @example Calling a Step
138
+ # # The #do_something method returns the string 'some value'.
139
+ # step { do_something() } #=> 'some value'
140
+ #
141
+ # value = step { do_something() }
142
+ # value #=> 'some value'
143
+ #
144
+ # @example Calling a Step with a Passing Result
145
+ # # The #do_something_else method returns a Cuprum result with a value
146
+ # # of 'another value'.
147
+ # step { do_something_else() } #=> 'another value'
148
+ #
149
+ # # The result is passing, so the value is extracted and returned.
150
+ # value = step { do_something_else() }
151
+ # value #=> 'another value'
152
+ #
153
+ # @example Calling a Step with a Failing Result
154
+ # # The #do_something_wrong method returns a failing Cuprum result.
155
+ # step { do_something_wrong() } # Throws the :cuprum_failed_step symbol.
156
+ #
157
+ # @overload step(method_name, *arguments, **keywords)
158
+ # Calls the method and returns the value, or halts on a failure.
159
+ #
160
+ # @param method_name [String, Symbol] The name of the method to call. Must
161
+ # be the name of a method on the current object.
162
+ # @param arguments [Array] Positional arguments to pass to the method.
163
+ # @param keywords [Hash] Keyword arguments to pass to the method.
164
+ #
165
+ # @yield A block to pass to the method.
166
+ #
167
+ # @return [Object] the #value of the result, or the returned object.
168
+ #
169
+ # The #step method is used to evaluate a sequence of processes, and to
170
+ # fail fast and halt processing if any of the steps returns a failing
171
+ # result. Each invocation of #step should be wrapped in a #steps block,
172
+ # or used inside the #process method of a Command.
173
+ #
174
+ # If the object returned by the block is a Cuprum result or compatible
175
+ # object (such as a called operation), the value is converted to a Cuprum
176
+ # result via the #to_cuprum_result method. Otherwise, the object is
177
+ # returned directly from #step.
178
+ #
179
+ # If the returned object is a passing result, the #value of the result is
180
+ # returned by #step.
181
+ #
182
+ # If the returned object is a failing result, then #step will throw
183
+ # :cuprum_failed_result and the failing result. This is caught by the
184
+ # #steps block, and halts execution of any subsequent steps.
185
+ #
186
+ # @example Calling a Step
187
+ # # The #zero method returns the integer 0.
188
+ # step :zero #=> 0
189
+ #
190
+ # value = step :zero
191
+ # value #=> 0
192
+ #
193
+ # @example Calling a Step with a Passing Result
194
+ # # The #add method adds the numbers and returns a Cuprum result with a
195
+ # # value equal to the sum.
196
+ # step :add, 2, 2
197
+ # #=> 4
198
+ #
199
+ # # The result is passing, so the value is extracted and returned.
200
+ # value = step :add, 2, 2
201
+ # value #=> 4
202
+ #
203
+ # @example Calling a Step with a Failing Result
204
+ # # The #divide method returns a failing Cuprum result when the second
205
+ # # argument is zero.
206
+ # step :divide, 1, 0
207
+ # # Throws the :cuprum_failed_step symbol, which should be caught by the
208
+ # # enclosing #steps block.
209
+ def step(method_name = nil, *args, **kwargs, &block)
210
+ result =
211
+ if !block_given? || method_name || !args.empty? || !kwargs.empty?
212
+ Cuprum::Steps.validate_method_name(method_name)
213
+
214
+ Cuprum::Steps
215
+ .execute_method(self, method_name, *args, **kwargs, &block)
216
+ else
217
+ block.call
218
+ end
219
+
220
+ Cuprum::Steps.extract_result_value(result)
221
+ end
222
+
223
+ # Returns the first failing #step result, or the final result if none fail.
224
+ #
225
+ # The #steps method is used to wrap a series of #step calls. Each step is
226
+ # executed in sequence. If any of the steps returns a failing result, that
227
+ # result is immediately returned from #steps. Otherwise, #steps wraps the
228
+ # value returned by a block in a Cuprum result.
229
+ #
230
+ # @yield Called with no parameters.
231
+ #
232
+ # @yieldreturn A Cuprum result, or an object to be wrapped in a result.
233
+ #
234
+ # @return [Cuprum::Result] the result or object returned by the block,
235
+ # wrapped in a Cuprum result.
236
+ #
237
+ # @example With A Passing Step
238
+ # result = steps do
239
+ # step { success('some value') }
240
+ # end
241
+ # result.class #=> Cuprum::Result
242
+ # result.success? #=> true
243
+ # result.value #=> 'some value'
244
+ #
245
+ # @example With A Failing Step
246
+ # result = steps do
247
+ # step { failure('something went wrong') }
248
+ # end
249
+ # result.class #=> Cuprum::Result
250
+ # result.success? #=> false
251
+ # result.error #=> 'something went wrong'
252
+ #
253
+ # @example With Multiple Steps
254
+ # result = steps do
255
+ # # This step is passing, so execution continues on to the next step.
256
+ # step { success('first step') }
257
+ #
258
+ # # This step is failing, so execution halts and returns this result.
259
+ # step { failure('second step') }
260
+ #
261
+ # # This step will never be called.
262
+ # step { success('third step') }
263
+ # end
264
+ # result.class #=> Cuprum::Result
265
+ # result.success? #=> false
266
+ # result.error #=> 'second step'
267
+ def steps
268
+ result = catch(:cuprum_failed_step) { yield }
269
+
270
+ return result if result.respond_to?(:to_cuprum_result)
271
+
272
+ success(result)
273
+ end
274
+ end
275
+ end
@@ -129,8 +129,15 @@ module Cuprum::Utils
129
129
  end # eigenclass
130
130
 
131
131
  # (see Cuprum::Command#call)
132
- def call *args, &block
133
- Cuprum::Utils::InstanceSpy.send(:call_spies_for, self, *args, &block)
132
+ def call *args, **kwargs, &block
133
+ if kwargs.empty?
134
+ Cuprum::Utils::InstanceSpy.send(:call_spies_for, self, *args, &block)
135
+ else
136
+ # :nocov:
137
+ Cuprum::Utils::InstanceSpy
138
+ .send(:call_spies_for, self, *args, **kwargs, &block)
139
+ # :nocov:
140
+ end
134
141
 
135
142
  super
136
143
  end # method call
@@ -8,13 +8,13 @@ module Cuprum
8
8
  # Major version.
9
9
  MAJOR = 0
10
10
  # Minor version.
11
- MINOR = 9
11
+ MINOR = 10
12
12
  # Patch version.
13
- PATCH = 1
13
+ PATCH = 0
14
14
  # Prerelease version.
15
- PRERELEASE = nil
15
+ PRERELEASE = :rc
16
16
  # Build metadata.
17
- BUILD = nil
17
+ BUILD = 0
18
18
 
19
19
  class << self
20
20
  # Generates the gem version string from the Version constants.
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.9.1
4
+ version: 0.10.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: 2019-12-08 00:00:00.000000000 Z
11
+ date: 2020-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sleeping_king_studios-tools
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.7'
19
+ version: '0.8'
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.7'
26
+ version: '0.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,8 @@ files:
116
116
  - lib/cuprum/chaining.rb
117
117
  - lib/cuprum/command.rb
118
118
  - lib/cuprum/command_factory.rb
119
+ - lib/cuprum/currying.rb
120
+ - lib/cuprum/currying/curried_command.rb
119
121
  - lib/cuprum/error.rb
120
122
  - lib/cuprum/errors.rb
121
123
  - lib/cuprum/errors/command_not_implemented.rb
@@ -123,9 +125,11 @@ files:
123
125
  - lib/cuprum/operation.rb
124
126
  - lib/cuprum/processing.rb
125
127
  - lib/cuprum/result.rb
128
+ - lib/cuprum/result_helpers.rb
126
129
  - lib/cuprum/rspec.rb
127
130
  - lib/cuprum/rspec/be_a_result.rb
128
131
  - lib/cuprum/rspec/be_a_result_matcher.rb
132
+ - lib/cuprum/steps.rb
129
133
  - lib/cuprum/utils.rb
130
134
  - lib/cuprum/utils/instance_spy.rb
131
135
  - lib/cuprum/version.rb
@@ -144,11 +148,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
148
  version: '0'
145
149
  required_rubygems_version: !ruby/object:Gem::Requirement
146
150
  requirements:
147
- - - ">="
151
+ - - ">"
148
152
  - !ruby/object:Gem::Version
149
- version: '0'
153
+ version: 1.3.1
150
154
  requirements: []
151
- rubygems_version: 3.0.3
155
+ rubygems_version: 3.1.2
152
156
  signing_key:
153
157
  specification_version: 4
154
158
  summary: An opinionated implementation of the Command pattern.