cuprum 0.9.1 → 0.10.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.