cuprum 0.10.0 → 1.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cuprum/command'
2
4
  require 'cuprum/errors/operation_not_called'
3
5
 
@@ -26,8 +28,8 @@ module Cuprum
26
28
  # @book = operation.value
27
29
  #
28
30
  # render :new
29
- # end # if-else
30
- # end # create
31
+ # end
32
+ # end
31
33
  #
32
34
  # Like a Command, an Operation can be defined directly by passing an
33
35
  # implementation block to the constructor or by creating a subclass that
@@ -41,7 +43,7 @@ module Cuprum
41
43
  # @example
42
44
  # class CustomOperation < CustomCommand
43
45
  # include Cuprum::Operation::Mixin
44
- # end # class
46
+ # end
45
47
  module Mixin
46
48
  # @return [Cuprum::Result] The result from the most recent call of the
47
49
  # operation.
@@ -62,32 +64,32 @@ module Cuprum
62
64
  # implementation.
63
65
  #
64
66
  # @see Cuprum::Command#call
65
- def call *args, **kwargs, &block
67
+ def call(*args, **kwargs, &block)
66
68
  reset! if called? # Clear reference to most recent result.
67
69
 
68
70
  @result = super
69
71
 
70
72
  self
71
- end # method call
73
+ end
72
74
 
73
75
  # @return [Boolean] true if the operation has been called and has a
74
76
  # reference to the most recent result; otherwise false.
75
77
  def called?
76
78
  !result.nil?
77
- end # method called?
79
+ end
78
80
 
79
81
  # @return [Object] the error (if any) from the most recent result, or nil
80
82
  # if the operation has not been called.
81
83
  def error
82
84
  called? ? result.error : nil
83
- end # method error
85
+ end
84
86
 
85
87
  # @return [Boolean] true if the most recent result had an error, or false
86
88
  # if the most recent result had no error or if the operation has not
87
89
  # been called.
88
90
  def failure?
89
91
  called? ? result.failure? : false
90
- end # method failure?
92
+ end
91
93
 
92
94
  # Clears the reference to the most recent call of the operation, if any.
93
95
  # This allows the result and any referenced data to be garbage collected.
@@ -99,7 +101,7 @@ module Cuprum
99
101
  # an error.
100
102
  def reset!
101
103
  @result = nil
102
- end # method reset
104
+ end
103
105
 
104
106
  # @return [Symbol, nil] the status of the most recent result, or nil if
105
107
  # the operation has not been called.
@@ -112,7 +114,7 @@ module Cuprum
112
114
  # been called.
113
115
  def success?
114
116
  called? ? result.success? : false
115
- end # method success?
117
+ end
116
118
 
117
119
  # Returns the most result if the operation was previously called.
118
120
  # Otherwise, returns a failing result.
@@ -130,8 +132,8 @@ module Cuprum
130
132
  # operation has not been called.
131
133
  def value
132
134
  called? ? result.value : nil
133
- end # method value
134
- end # module
135
+ end
136
+ end
135
137
  include Mixin
136
138
 
137
139
  # @!method call
@@ -156,9 +158,9 @@ module Cuprum
156
158
  # (see Cuprum::Operation::Mixin#success?)
157
159
 
158
160
  # @!method to_cuprum_result
159
- # (see Cuprum::Operation::Mixin#to_cuprum_result?)
161
+ # (see Cuprum::Operation::Mixin#to_cuprum_result)
160
162
 
161
163
  # @!method value
162
164
  # (see Cuprum::Operation::Mixin#value)
163
- end # class
164
- end # module
165
+ end
166
+ end
data/lib/cuprum/result.rb CHANGED
@@ -5,6 +5,7 @@ require 'cuprum'
5
5
  module Cuprum
6
6
  # Data object that encapsulates the result of calling a Cuprum command.
7
7
  class Result
8
+ # Enumerates the default permitted values for a Result#status.
8
9
  STATUSES = %i[success failure].freeze
9
10
 
10
11
  # @param value [Object] The value returned by calling the command.
@@ -28,8 +29,6 @@ module Cuprum
28
29
  # @return [Symbol] the status of the result, either :success or :failure.
29
30
  attr_reader :status
30
31
 
31
- # rubocop:disable Metrics/CyclomaticComplexity
32
-
33
32
  # Compares the other object to the result.
34
33
  #
35
34
  # @param other [#value, #success?] An object responding to, at minimum,
@@ -45,7 +44,6 @@ module Cuprum
45
44
 
46
45
  true
47
46
  end
48
- # rubocop:enable Metrics/CyclomaticComplexity
49
47
 
50
48
  # @return [Boolean] true if the result status is :failure, otherwise false.
51
49
  def failure?
@@ -2,16 +2,25 @@
2
2
 
3
3
  require 'cuprum/rspec/be_a_result_matcher'
4
4
 
5
- module RSpec
5
+ module Cuprum::RSpec
6
6
  module Matchers # rubocop:disable Style/Documentation
7
+ # Asserts that the object is a Cuprum::Result with status: :failure.
8
+ #
9
+ # @return [Cuprum::RSpec::BeAResultMatcher] the generated matcher.
7
10
  def be_a_failing_result
8
11
  be_a_result.with_status(:failure)
9
12
  end
10
13
 
14
+ # Asserts that the object is a Cuprum::Result with status: :success.
15
+ #
16
+ # @return [Cuprum::RSpec::BeAResultMatcher] the generated matcher.
11
17
  def be_a_passing_result
12
18
  be_a_result.with_status(:success).and_error(nil)
13
19
  end
14
20
 
21
+ # Asserts that the object is a Cuprum::Result.
22
+ #
23
+ # @return [Cuprum::RSpec::BeAResultMatcher] the generated matcher.
15
24
  def be_a_result
16
25
  Cuprum::RSpec::BeAResultMatcher.new
17
26
  end
@@ -46,9 +46,9 @@ module Cuprum::RSpec
46
46
  message = "expected #{actual.inspect} to #{description}"
47
47
 
48
48
  if !actual_is_result?
49
- message + ', but the object is not a result'
49
+ "#{message}, but the object is not a result"
50
50
  elsif actual_is_uncalled_operation?
51
- message + ', but the object is an uncalled operation'
51
+ "#{message}, but the object is an uncalled operation"
52
52
  elsif !properties_match?
53
53
  message + properties_failure_message
54
54
  else
@@ -182,7 +182,6 @@ module Cuprum::RSpec
182
182
  ' positives, since any other result will match.'
183
183
  end
184
184
 
185
- # rubocop:disable Metrics/CyclomaticComplexity
186
185
  # rubocop:disable Metrics/AbcSize
187
186
  def properties_description
188
187
  msg = ''
@@ -200,7 +199,6 @@ module Cuprum::RSpec
200
199
 
201
200
  msg + " and status: #{expected_status.inspect}"
202
201
  end
203
- # rubocop:enable Metrics/CyclomaticComplexity
204
202
  # rubocop:enable Metrics/AbcSize
205
203
 
206
204
  def properties_failure_message
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/rspec'
4
+
5
+ module Cuprum::RSpec
6
+ module Matchers # rubocop:disable Style/Documentation
7
+ # Asserts that the command defines a :process method.
8
+ #
9
+ # @return [RSpec::Matchers::BuiltIn::RespondTo] the generated matcher.
10
+ def be_callable
11
+ respond_to(:process, true)
12
+ end
13
+ end
14
+ end
data/lib/cuprum/steps.rb CHANGED
@@ -63,161 +63,60 @@ module Cuprum
63
63
  module Steps
64
64
  include Cuprum::ResultHelpers
65
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.
66
+ # Executes the block and returns the value, or halts on a failure.
129
67
  #
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.
68
+ # @yield Called with no parameters.
166
69
  #
167
- # @return [Object] the #value of the result, or the returned object.
70
+ # @return [Object] the #value of the result, or the returned object.
168
71
  #
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.
72
+ # The #step method is used to evaluate a sequence of processes, and to
73
+ # fail fast and halt processing if any of the steps returns a failing
74
+ # result. Each invocation of #step should be wrapped in a #steps block,
75
+ # or used inside the #process method of a Command.
173
76
  #
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.
77
+ # If the object returned by the block is a Cuprum result or compatible
78
+ # object (such as a called operation), the value is converted to a Cuprum
79
+ # result via the #to_cuprum_result method. Otherwise, the object is
80
+ # returned directly from #step.
178
81
  #
179
- # If the returned object is a passing result, the #value of the result is
180
- # returned by #step.
82
+ # If the returned object is a passing result, the #value of the result is
83
+ # returned by #step.
181
84
  #
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.
85
+ # If the returned object is a failing result, then #step will throw
86
+ # :cuprum_failed_result and the failing result. This is caught by the
87
+ # #steps block, and halts execution of any subsequent steps.
185
88
  #
186
- # @example Calling a Step
187
- # # The #zero method returns the integer 0.
188
- # step :zero #=> 0
89
+ # @example Calling a Step
90
+ # # The #do_something method returns the string 'some value'.
91
+ # step { do_something() } #=> 'some value'
189
92
  #
190
- # value = step :zero
191
- # value #=> 0
93
+ # value = step { do_something() }
94
+ # value #=> 'some value'
192
95
  #
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
96
+ # @example Calling a Step with a Passing Result
97
+ # # The #do_something_else method returns a Cuprum result with a value
98
+ # # of 'another value'.
99
+ # step { do_something_else() } #=> 'another value'
198
100
  #
199
- # # The result is passing, so the value is extracted and returned.
200
- # value = step :add, 2, 2
201
- # value #=> 4
101
+ # # The result is passing, so the value is extracted and returned.
102
+ # value = step { do_something_else() }
103
+ # value #=> 'another value'
202
104
  #
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)
105
+ # @example Calling a Step with a Failing Result
106
+ # # The #do_something_wrong method returns a failing Cuprum result.
107
+ # step { do_something_wrong() } # Throws the :cuprum_failed_step symbol.
108
+ def step
109
+ raise ArgumentError, 'expected a block' unless block_given?
110
+
111
+ result = yield
213
112
 
214
- Cuprum::Steps
215
- .execute_method(self, method_name, *args, **kwargs, &block)
216
- else
217
- block.call
218
- end
113
+ return result unless result.respond_to?(:to_cuprum_result)
219
114
 
220
- Cuprum::Steps.extract_result_value(result)
115
+ result = result.to_cuprum_result
116
+
117
+ return result.value if result.success?
118
+
119
+ throw :cuprum_failed_step, result
221
120
  end
222
121
 
223
122
  # Returns the first failing #step result, or the final result if none fail.
@@ -264,8 +163,10 @@ module Cuprum
264
163
  # result.class #=> Cuprum::Result
265
164
  # result.success? #=> false
266
165
  # result.error #=> 'second step'
267
- def steps
268
- result = catch(:cuprum_failed_step) { yield }
166
+ def steps(&block)
167
+ raise ArgumentError, 'no block given' unless block_given?
168
+
169
+ result = catch(:cuprum_failed_step) { block.call }
269
170
 
270
171
  return result if result.respond_to?(:to_cuprum_result)
271
172
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cuprum/utils'
2
4
 
3
5
  module Cuprum::Utils
@@ -28,14 +30,14 @@ module Cuprum::Utils
28
30
  # expect(spy).to receive(:call)
29
31
  #
30
32
  # CustomCommand.new.call
31
- # end # spy_on
33
+ # end
32
34
  module InstanceSpy
33
35
  # Minimal class that implements a #call method to mirror method calls to
34
36
  # instances of an instrumented command class.
35
37
  class Spy
36
38
  # Empty method that accepts any arguments and an optional block.
37
- def call *_args, &block; end
38
- end # class
39
+ def call(*_args, &block); end
40
+ end
39
41
 
40
42
  class << self
41
43
  # Retires all spies. Subsequent calls to the #call method on command
@@ -44,7 +46,7 @@ module Cuprum::Utils
44
46
  Thread.current[:cuprum_instance_spies] = nil
45
47
 
46
48
  nil
47
- end # method clear_spies
49
+ end
48
50
 
49
51
  # Finds or creates a spy object for the given module or class. Each time
50
52
  # that the #call method is called for an object of the given type, the
@@ -68,41 +70,39 @@ module Cuprum::Utils
68
70
  # @yield [Cuprum::Utils::InstanceSpy::Spy] The instance spy.
69
71
  #
70
72
  # @return [nil] nil.
71
- def spy_on command_class
73
+ def spy_on(command_class)
72
74
  guard_spy_class!(command_class)
73
75
 
74
76
  instrument_call!
75
77
 
76
78
  if block_given?
77
- begin
78
- instance_spy = assign_spy(command_class)
79
+ instance_spy = assign_spy(command_class)
79
80
 
80
- yield instance_spy
81
- end # begin-ensure
81
+ yield instance_spy
82
82
  else
83
83
  assign_spy(command_class)
84
- end # if-else
85
- end # method spy_on
84
+ end
85
+ end
86
86
 
87
87
  private
88
88
 
89
- def assign_spy command_class
89
+ def assign_spy(command_class)
90
90
  existing_spy = spies[command_class]
91
91
 
92
92
  return existing_spy if existing_spy
93
93
 
94
94
  spies[command_class] = build_spy
95
- end # method assign_spy
95
+ end
96
96
 
97
97
  def build_spy
98
98
  Cuprum::Utils::InstanceSpy::Spy.new
99
- end # method build_spy
99
+ end
100
100
 
101
- def call_spies_for command, *args, &block
101
+ def call_spies_for(command, *args, &block)
102
102
  spies_for(command).each { |spy| spy.call(*args, &block) }
103
- end # method call_spies_for
103
+ end
104
104
 
105
- def guard_spy_class! command_class
105
+ def guard_spy_class!(command_class)
106
106
  return if command_class.is_a?(Module) && !command_class.is_a?(Class)
107
107
 
108
108
  return if command_class.is_a?(Class) &&
@@ -111,25 +111,25 @@ module Cuprum::Utils
111
111
  raise ArgumentError,
112
112
  'must be a class inheriting from Cuprum::Command',
113
113
  caller(1..-1)
114
- end # method guard_spy_class!
114
+ end
115
115
 
116
116
  def instrument_call!
117
117
  return if Cuprum::Command < Cuprum::Utils::InstanceSpy
118
118
 
119
119
  Cuprum::Command.prepend(Cuprum::Utils::InstanceSpy)
120
- end # method instrument_call!
120
+ end
121
121
 
122
122
  def spies
123
123
  Thread.current[:cuprum_instance_spies] ||= {}
124
- end # method spies
124
+ end
125
125
 
126
- def spies_for command
126
+ def spies_for(command)
127
127
  spies.select { |mod, _| command.is_a?(mod) }.map { |_, spy| spy }
128
- end # method spies_for
129
- end # eigenclass
128
+ end
129
+ end
130
130
 
131
- # (see Cuprum::Command#call)
132
- def call *args, **kwargs, &block
131
+ # (see Cuprum::Processing#call)
132
+ def call(*args, **kwargs, &block)
133
133
  if kwargs.empty?
134
134
  Cuprum::Utils::InstanceSpy.send(:call_spies_for, self, *args, &block)
135
135
  else
@@ -140,6 +140,6 @@ module Cuprum::Utils
140
140
  end
141
141
 
142
142
  super
143
- end # method call
144
- end # module
145
- end # module
143
+ end
144
+ end
145
+ end
data/lib/cuprum/utils.rb CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cuprum'
2
4
 
3
5
  module Cuprum
4
6
  # Namespace for utility modules.
5
7
  module Utils; end
6
- end # module
8
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cuprum
2
4
  # @api private
3
5
  #
@@ -6,15 +8,15 @@ module Cuprum
6
8
  # @see http://semver.org/
7
9
  module Version
8
10
  # Major version.
9
- MAJOR = 0
11
+ MAJOR = 1
10
12
  # Minor version.
11
- MINOR = 10
13
+ MINOR = 0
12
14
  # Patch version.
13
15
  PATCH = 0
14
16
  # Prerelease version.
15
- PRERELEASE = nil
17
+ PRERELEASE = :rc
16
18
  # Build metadata.
17
- BUILD = nil
19
+ BUILD = 1
18
20
 
19
21
  class << self
20
22
  # Generates the gem version string from the Version constants.
@@ -28,17 +30,17 @@ module Cuprum
28
30
  str = "#{MAJOR}.#{MINOR}.#{PATCH}"
29
31
 
30
32
  prerelease = value_of(:PRERELEASE)
31
- str << ".#{prerelease}" if prerelease
33
+ str = "#{str}.#{prerelease}" if prerelease
32
34
 
33
35
  build = value_of(:BUILD)
34
- str << ".#{build}" if build
36
+ str = "#{str}.#{build}" if build
35
37
 
36
38
  str
37
- end # class method to_version
39
+ end
38
40
 
39
41
  private
40
42
 
41
- def value_of constant
43
+ def value_of(constant)
42
44
  return nil unless const_defined?(constant)
43
45
 
44
46
  value = const_get(constant)
@@ -46,9 +48,10 @@ module Cuprum
46
48
  return nil if value.respond_to?(:empty?) && value.empty?
47
49
 
48
50
  value
49
- end # method value_of
50
- end # eigenclass
51
- end # module
51
+ end
52
+ end
53
+ end
52
54
 
55
+ # The current version of the gem.
53
56
  VERSION = Version.to_gem_version
54
- end # module
57
+ end
data/lib/cuprum.rb CHANGED
@@ -1,15 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # A lightweight, functional-lite toolkit for making business logic a first-class
2
4
  # citizen of your application.
3
5
  module Cuprum
4
- autoload :Command, 'cuprum/command'
5
- autoload :Operation, 'cuprum/operation'
6
- autoload :Result, 'cuprum/result'
7
- autoload :Steps, 'cuprum/steps'
6
+ autoload :Command, 'cuprum/command'
7
+ autoload :Error, 'cuprum/error'
8
+ autoload :Matcher, 'cuprum/matcher'
9
+ autoload :Middleware, 'cuprum/middleware'
10
+ autoload :Operation, 'cuprum/operation'
11
+ autoload :Result, 'cuprum/result'
12
+ autoload :Steps, 'cuprum/steps'
8
13
 
9
14
  class << self
10
15
  # @return [String] The current version of the gem.
11
16
  def version
12
17
  VERSION
13
- end # method version
14
- end # eigenclass
15
- end # module
18
+ end
19
+ end
20
+ end