cuprum 0.6.0 → 0.7.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.
@@ -2,19 +2,19 @@ require 'cuprum/utils'
2
2
 
3
3
  module Cuprum::Utils
4
4
  # Utility module for instrumenting calls to the #call method of any instance
5
- # of a function class. This can be used to unobtrusively test the
6
- # functionality of code that calls a function without providing a reference to
7
- # the function instance, such as chained functions or methods that create and
8
- # call a function instance.
5
+ # of a command class. This can be used to unobtrusively test the
6
+ # functionality of code that calls a command without providing a reference to
7
+ # the command instance, such as chained commands or methods that create and
8
+ # call a command instance.
9
9
  #
10
- # @example Observing calls to instances of a function.
10
+ # @example Observing calls to instances of a command.
11
11
  # spy = Cuprum::Utils::InstanceSpy.spy_on(CustomCommand)
12
12
  #
13
13
  # expect(spy).to receive(:call).with(1, 2, 3, :four => '4')
14
14
  #
15
15
  # CustomCommand.new.call(1, 2, 3, :four => '4')
16
16
  #
17
- # @example Observing calls to a chained function.
17
+ # @example Observing calls to a chained command.
18
18
  # spy = Cuprum::Utils::InstanceSpy.spy_on(ChainedCommand)
19
19
  #
20
20
  # expect(spy).to receive(:call)
@@ -31,14 +31,14 @@ module Cuprum::Utils
31
31
  # end # spy_on
32
32
  module InstanceSpy
33
33
  # Minimal class that implements a #call method to mirror method calls to
34
- # instances of an instrumented function class.
34
+ # instances of an instrumented command class.
35
35
  class Spy
36
36
  # Empty method that accepts any arguments and an optional block.
37
37
  def call *_args, █ end
38
38
  end # class
39
39
 
40
40
  class << self
41
- # Retires all spies. Subsequent calls to the #call method on function
41
+ # Retires all spies. Subsequent calls to the #call method on command
42
42
  # instances will not be mirrored to existing spy objects.
43
43
  def clear_spies
44
44
  Thread.current[:cuprum_instance_spies] = nil
@@ -50,7 +50,7 @@ module Cuprum::Utils
50
50
  # that the #call method is called for an object of the given type, the
51
51
  # spy's #call method will be invoked with the same arguments and block.
52
52
  #
53
- # @param function_class [Class, Module] The type of function to spy on.
53
+ # @param command_class [Class, Module] The type of command to spy on.
54
54
  # Must be either a Module, or a Class that extends Cuprum::Command.
55
55
  #
56
56
  # @raise [ArgumentError] If the argument is neither a Module nor a Class
@@ -59,54 +59,54 @@ module Cuprum::Utils
59
59
  # @note Calling this method for the first time will prepend the
60
60
  # Cuprum::Utils::InstanceSpy module to Cuprum::Command.
61
61
  #
62
- # @overload spy_on(function_class)
62
+ # @overload spy_on(command_class)
63
63
  # @return [Cuprum::Utils::InstanceSpy::Spy] The instance spy.
64
64
  #
65
- # @overload spy_on(function_class, &block)
65
+ # @overload spy_on(command_class, &block)
66
66
  # Yields the instance spy to the block, and returns nil.
67
67
  #
68
68
  # @yield [Cuprum::Utils::InstanceSpy::Spy] The instance spy.
69
69
  #
70
70
  # @return [nil] nil.
71
- def spy_on function_class
72
- guard_spy_class!(function_class)
71
+ def spy_on command_class
72
+ guard_spy_class!(command_class)
73
73
 
74
74
  instrument_call!
75
75
 
76
76
  if block_given?
77
77
  begin
78
- instance_spy = assign_spy(function_class)
78
+ instance_spy = assign_spy(command_class)
79
79
 
80
80
  yield instance_spy
81
81
  end # begin-ensure
82
82
  else
83
- assign_spy(function_class)
83
+ assign_spy(command_class)
84
84
  end # if-else
85
85
  end # method spy_on
86
86
 
87
87
  private
88
88
 
89
- def assign_spy function_class
90
- existing_spy = spies[function_class]
89
+ def assign_spy command_class
90
+ existing_spy = spies[command_class]
91
91
 
92
92
  return existing_spy if existing_spy
93
93
 
94
- spies[function_class] = build_spy
94
+ spies[command_class] = build_spy
95
95
  end # method assign_spy
96
96
 
97
97
  def build_spy
98
98
  Cuprum::Utils::InstanceSpy::Spy.new
99
99
  end # method build_spy
100
100
 
101
- def call_spies_for function, *args, &block
102
- spies_for(function).each { |spy| spy.call(*args, &block) }
101
+ def call_spies_for command, *args, &block
102
+ spies_for(command).each { |spy| spy.call(*args, &block) }
103
103
  end # method call_spies_for
104
104
 
105
- def guard_spy_class! function_class
106
- return if function_class.is_a?(Module) && !function_class.is_a?(Class)
105
+ def guard_spy_class! command_class
106
+ return if command_class.is_a?(Module) && !command_class.is_a?(Class)
107
107
 
108
- return if function_class.is_a?(Class) &&
109
- function_class <= Cuprum::Command
108
+ return if command_class.is_a?(Class) &&
109
+ command_class <= Cuprum::Command
110
110
 
111
111
  raise ArgumentError,
112
112
  'must be a class inheriting from Cuprum::Command',
@@ -123,8 +123,8 @@ module Cuprum::Utils
123
123
  Thread.current[:cuprum_instance_spies] ||= {}
124
124
  end # method spies
125
125
 
126
- def spies_for function
127
- spies.select { |mod, _| function.is_a?(mod) }.map { |_, spy| spy }
126
+ def spies_for command
127
+ spies.select { |mod, _| command.is_a?(mod) }.map { |_, spy| spy }
128
128
  end # method spies_for
129
129
  end # eigenclass
130
130
 
@@ -0,0 +1,65 @@
1
+ require 'cuprum/utils'
2
+
3
+ module Cuprum::Utils
4
+ # Helper class for building a warning message when a command returns a result,
5
+ # but the command's current result already has errors, a set status, or is
6
+ # halted.
7
+ class ResultNotEmptyWarning
8
+ MESSAGE = '#process returned a result, but '.freeze
9
+ private_constant :MESSAGE
10
+
11
+ # @param result [Cuprum::Result] The result for which to generate the
12
+ # warning message.
13
+ def initialize result
14
+ @result = result
15
+ end # constructor
16
+
17
+ # @return [String] The warning message for the given result.
18
+ def message
19
+ # byebug
20
+ MESSAGE + humanize_list(warnings).freeze
21
+ end # method message
22
+
23
+ private
24
+
25
+ attr_reader :result
26
+
27
+ def errors_not_empty_warning
28
+ return nil if result.errors.empty?
29
+
30
+ "there were already errors #{@result.errors.inspect}".freeze
31
+ end # method errors_not_empty_warning
32
+
33
+ def halted_warning
34
+ return nil unless result.halted?
35
+
36
+ 'the command was halted'.freeze
37
+ end # method halted_warning
38
+
39
+ def humanize_list list, empty_value: ''
40
+ return empty_value if list.size.zero?
41
+
42
+ return list.first.to_s if list.size == 1
43
+
44
+ return "#{list.first} and #{list.last}" if list.size == 2
45
+
46
+ "#{list[0...-1].join ', '}, and #{list.last}"
47
+ end # method humanize_list
48
+
49
+ def status_set_warning
50
+ status = result.send(:status)
51
+
52
+ return nil if status.nil?
53
+
54
+ "the status was set to #{status.inspect}".freeze
55
+ end # method status_set_warning
56
+
57
+ def warnings
58
+ [
59
+ errors_not_empty_warning,
60
+ status_set_warning,
61
+ halted_warning
62
+ ].compact
63
+ end # method warnings
64
+ end # class
65
+ end # module
@@ -8,13 +8,13 @@ module Cuprum
8
8
  # Major version.
9
9
  MAJOR = 0
10
10
  # Minor version.
11
- MINOR = 6
11
+ MINOR = 7
12
12
  # Patch version.
13
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.6.0
4
+ version: 0.7.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: 2017-11-30 00:00:00.000000000 Z
11
+ date: 2018-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -94,7 +94,6 @@ files:
94
94
  - LICENSE
95
95
  - README.md
96
96
  - lib/cuprum.rb
97
- - lib/cuprum/basic_command.rb
98
97
  - lib/cuprum/built_in.rb
99
98
  - lib/cuprum/built_in/identity_command.rb
100
99
  - lib/cuprum/built_in/identity_operation.rb
@@ -104,9 +103,12 @@ files:
104
103
  - lib/cuprum/command.rb
105
104
  - lib/cuprum/not_implemented_error.rb
106
105
  - lib/cuprum/operation.rb
106
+ - lib/cuprum/processing.rb
107
107
  - lib/cuprum/result.rb
108
+ - lib/cuprum/result_helpers.rb
108
109
  - lib/cuprum/utils.rb
109
110
  - lib/cuprum/utils/instance_spy.rb
111
+ - lib/cuprum/utils/result_not_empty_warning.rb
110
112
  - lib/cuprum/version.rb
111
113
  homepage: http://sleepingkingstudios.com
112
114
  licenses:
@@ -123,9 +125,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
125
  version: '0'
124
126
  required_rubygems_version: !ruby/object:Gem::Requirement
125
127
  requirements:
126
- - - ">="
128
+ - - ">"
127
129
  - !ruby/object:Gem::Version
128
- version: '0'
130
+ version: 1.3.1
129
131
  requirements: []
130
132
  rubyforge_project:
131
133
  rubygems_version: 2.6.13
@@ -1,212 +0,0 @@
1
- require 'cuprum/not_implemented_error'
2
-
3
- module Cuprum
4
- # Functional object that encapsulates a business logic operation with a
5
- # standardized interface and returns a result object.
6
- class BasicCommand
7
- # Returns a new instance of Cuprum::BasicCommand.
8
- #
9
- # @yield [*arguments, **keywords, &block] If a block is given, the
10
- # #call method will wrap the block and set the result #value to the return
11
- # value of the block. This overrides the implementation in #process, if
12
- # any.
13
- def initialize &implementation
14
- define_singleton_method :process, &implementation if implementation
15
- end # method initialize
16
-
17
- # @overload call(*arguments, **keywords, &block)
18
- # Executes the logic encoded in the constructor block, or the #process
19
- # method if no block was passed to the constructor, and returns a
20
- # Cuprum::Result object with the return value of the block or #process,
21
- # the success or failure status, and any errors generated.
22
- #
23
- # @param arguments [Array] Arguments to be passed to the implementation.
24
- #
25
- # @param keywords [Hash] Keywords to be passed to the implementation.
26
- #
27
- # @return [Cuprum::Result] The result object for the command.
28
- #
29
- # @yield If a block argument is given, it will be passed to the
30
- # implementation.
31
- #
32
- # @raise [Cuprum::NotImplementedError] Unless a block was passed to the
33
- # constructor or the #process method was overriden by a Command
34
- # subclass.
35
- def call *args, &block
36
- wrap_result { |result| merge_results(result, process(*args, &block)) }
37
- end # method call
38
-
39
- private
40
-
41
- # @!visibility public
42
- #
43
- # Generates an empty errors object. When the function is called, the result
44
- # will have its #errors property initialized to the value returned by
45
- # #build_errors. By default, this is an array. If you want to use a custom
46
- # errors object type, override this method in a subclass.
47
- #
48
- # @return [Array] An empty errors object.
49
- def build_errors
50
- []
51
- end # method build_errors
52
-
53
- def convert_value_to_result value
54
- return nil unless value_is_result?(value)
55
-
56
- if value.respond_to?(:result) && value_is_result?(value.result)
57
- return value.result
58
- end # if
59
-
60
- value
61
- end # method convert_value_to_result
62
-
63
- # @!visibility public
64
- #
65
- # Provides a reference to the current result's errors object. Messages or
66
- # error objects added to this will be included in the #errors method of the
67
- # returned result object.
68
- #
69
- # @return [Array, Object] The errors object.
70
- #
71
- # @see Cuprum::Result#errors.
72
- #
73
- # @note This is a private method, and only available when executing the
74
- # function implementation as defined in the constructor block or the
75
- # #process method.
76
- def errors
77
- @result&.errors
78
- end # method errors
79
-
80
- # @!visibility public
81
- #
82
- # Marks the current result as failed. Calling #failure? on the returned
83
- # result object will evaluate to true, whether or not the result has any
84
- # errors.
85
- #
86
- # @see Cuprum::Result#failure!.
87
- #
88
- # @note This is a private method, and only available when executing the
89
- # function implementation as defined in the constructor block or the
90
- # #process method.
91
- def failure!
92
- @result&.failure!
93
- end # method failure!
94
-
95
- # @!visibility public
96
- #
97
- # Marks the current result as halted.
98
- #
99
- # @see Cuprum::Result#halt!.
100
- #
101
- # @note This is a private method, and only available when executing the
102
- # function implementation as defined in the constructor block or the
103
- # #process method.
104
- def halt!
105
- @result&.halt!
106
- end # method halt!
107
-
108
- # :nocov:
109
- def humanize_list list, empty_value: ''
110
- return empty_value if list.size.zero?
111
-
112
- return list.first.to_s if list.size == 1
113
-
114
- return "#{list.first} and #{list.last}" if list.size == 2
115
-
116
- "#{list[0...-1].join ', '}, and #{list.last}"
117
- end # method humanize_list
118
- # :nocov:
119
-
120
- def merge_results result, other
121
- if value_is_result?(other)
122
- Cuprum.warn(result_not_empty_warning) unless result.empty?
123
-
124
- convert_value_to_result(other)
125
- else
126
- result.value = other
127
-
128
- result
129
- end # if-else
130
- end # method merge_results
131
-
132
- # @!visibility public
133
- # @overload process(*arguments, **keywords, &block)
134
- # The implementation of the function, to be executed when the #call method
135
- # is called. Can add errors to or set the status of the result, and the
136
- # value of the result will be set to the value returned by #process. Do
137
- # not call this method directly.
138
- #
139
- # @param arguments [Array] The arguments, if any, passed from #call.
140
- #
141
- # @param keywords [Hash] The keywords, if any, passed from #call.
142
- #
143
- # @yield The block, if any, passed from #call.
144
- #
145
- # @return [Object] the value of the result object to be returned by #call.
146
- #
147
- # @raise [Cuprum::NotImplementedError] Unless a block was passed to the
148
- # constructor or the #process method was overriden by a Command
149
- # subclass.
150
- #
151
- # @note This is a private method.
152
- def process *_args
153
- raise Cuprum::NotImplementedError, nil, caller(1..-1)
154
- end # method process
155
-
156
- def result_not_empty_warning # rubocop:disable Metrics/MethodLength
157
- warnings = []
158
-
159
- unless @result.errors.empty?
160
- warnings << "there were already errors #{@result.errors.inspect}"
161
- end # unless
162
-
163
- status = @result.send(:status)
164
- unless status.nil?
165
- warnings << "the status was set to #{status.inspect}"
166
- end # unless
167
-
168
- if @result.halted?
169
- warnings << 'the function was halted'
170
- end # if
171
-
172
- message = '#process returned a result, but '
173
- message <<
174
- humanize_list(warnings, :empty_value => 'the result was not empty')
175
-
176
- message
177
- end # method result_not_empty_warning
178
-
179
- # @!visibility public
180
- #
181
- # Marks the current result as passing. Calling #success? on the returned
182
- # result object will evaluate to true, whether or not the result has any
183
- # errors.
184
- #
185
- # @see Cuprum::Result#success!.
186
- #
187
- # @note This is a private method, and only available when executing the
188
- # function implementation as defined in the constructor block or the
189
- # #process method.
190
- def success!
191
- @result&.success!
192
- end # method success!
193
-
194
- def value_is_result? value
195
- value.respond_to?(:value) && value.respond_to?(:success?)
196
- end # method value
197
-
198
- def wrap_result
199
- result = Cuprum::Result.new(:errors => build_errors)
200
-
201
- begin
202
- @result = result
203
-
204
- result = yield result
205
- ensure
206
- @result = nil
207
- end # begin-ensure
208
-
209
- result
210
- end # method wrap_result
211
- end # class
212
- end # module