statefully 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 97acdf9e907dd51ad5227ef53828030d0943d35d
4
- data.tar.gz: 3d9564c5fa59479e584e38c94ebdf504e1e1894d
3
+ metadata.gz: 4f804d2bf7a4e93cce2e444f0bf61ecbf93fcc40
4
+ data.tar.gz: 9fdf666b486671a6a45cab5e5c4bb885ffcb2baf
5
5
  SHA512:
6
- metadata.gz: 94467d76d84d115d674c84db44022a6c4e76d81e417b1ca48eb613734802fd80c420f4a79832aa248f1622e9260c26d1bdfb8c72f9c73f078a14df230cde96b5
7
- data.tar.gz: 732e6d9e831787099ae318d706a0c6d95280ea8ee06217e1bc0b3f9a30bd058d150b8053a972ffd631a626e3e75937cac403a5fc9e14609f0413f870e6da9894
6
+ metadata.gz: d4d379b599bf47f4cfb48ec9a5d7450be76e07f8debacc0c1eb18d603fe0a846d2103b69e8cc53951350e12ed17a8ea9bac4a030728365113001b1007a0c6ac2
7
+ data.tar.gz: 9ed5595f56f566543fdc0bd69d71ef917ba674d2abf49502670c3d92d1fe021780c3304d8056ca87da4681443bf112491d3b2138b098ce16eec16533ebc7a9e8
data/lib/statefully.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'statefully/change'
1
2
  require 'statefully/diff'
3
+ require 'statefully/errors'
2
4
  require 'statefully/inspect'
3
5
  require 'statefully/state'
@@ -0,0 +1,52 @@
1
+ module Statefully
2
+ # Change is a tuple of current and previous value of a field in a {Diff}.
3
+ class Change
4
+ # Returns the current {State} field value
5
+ #
6
+ # @return [Object] current {State} field value
7
+ # @api public
8
+ # @example
9
+ # Statefully::Change.new(current: 7, previous: 8).current
10
+ # => 7
11
+ attr_reader :current
12
+
13
+ # Returns the previous {State} field value
14
+ #
15
+ # @return [Object] previous {State} field value
16
+ # @api public
17
+ # @example
18
+ # Statefully::Change.new(current: 7, previous: 8).previous
19
+ # => 8
20
+ attr_reader :previous
21
+
22
+ # Constructor for the {Change} object
23
+ # @param current [Object] current {State} field value
24
+ # @param previous [Object] previous {State} field value
25
+ # @api public
26
+ # @example
27
+ # Statefully::Change.new(current: 7, previous: 8)
28
+ # => #<Statefully::Change current=7, previous=8>
29
+ def initialize(current:, previous:)
30
+ @current = current
31
+ @previous = previous
32
+ end
33
+
34
+ # Internal-only method used to determine whether there was any change
35
+ # @api private
36
+ def none?
37
+ @current == @previous
38
+ end
39
+
40
+ # Human-readable representation of the {Change} for console inspection
41
+ #
42
+ # @return [String]
43
+ # @api semipublic
44
+ # @example
45
+ # Statefully::Change.new(current: 7, previous: 8)
46
+ # => #<Statefully::Change current=7, previous=8>
47
+ def inspect
48
+ "#<#{self.class.name} " \
49
+ "#{Inspect.from_fields(current: current, previous: previous)}>"
50
+ end
51
+ end # class Change
52
+ end # module Statefully
@@ -2,124 +2,299 @@ require 'set'
2
2
  require 'singleton'
3
3
 
4
4
  module Statefully
5
- module Diff
5
+ # {Diff} is a difference between two neighboring instances of {State}.
6
+ #
7
+ # @abstract
8
+ class Diff
9
+ # Create is the only public interface to the Diff class
10
+ #
11
+ # @param current [Statefully::State] current state
12
+ # @param previous [Statefully::State] previous state
13
+ #
14
+ # @return [Statefully::Diff] Difference between states.
15
+ # @api public
16
+ # @example
17
+ # previous = Statefully::State.create
18
+ # current = previus.succeed(key: 'val')
19
+ # Statefully::Diff.create(current, previous)
20
+ # => #<Statefully::Diff::Changed added={key: "val"}>
21
+ #
6
22
  # This method reeks of :reek:FeatureEnvy (of current).
7
- def create(current, previous)
23
+ def self.create(current:, previous:)
8
24
  return current.diff if current.failed? || current.finished?
9
- changes = Builder.new(current, previous).build
25
+ changes = Builder.new(current: current, previous: previous).build
26
+ return Created.new(**changes).freeze if previous.none?
10
27
  changes.empty? ? Unchanged.instance : Changed.new(**changes).freeze
11
28
  end
12
- module_function :create
13
29
 
14
- class Changed
15
- attr_reader :added, :changed
30
+ # Check if a {Diff} is empty
31
+ #
32
+ # An empty {Diff} means that is there are no changes in properties between
33
+ # current and previous {State}.
34
+ #
35
+ # @return [Boolean]
36
+ # @api public
37
+ # @example
38
+ # Statefully::Diff::Unchanged.instance.empty?
39
+ # => true
40
+ def empty?
41
+ true
42
+ end
16
43
 
17
- def empty?
18
- false
44
+ # Hash of added properties and their values
45
+ #
46
+ # @return [Hash<Symbol, Object>]
47
+ # @api public
48
+ # @example
49
+ # Statefully::Diff::Unchanged.instance.added
50
+ # => {}
51
+ def added
52
+ {}
53
+ end
54
+
55
+ # Hash of changed properties and their current and previous values
56
+ #
57
+ # @return [Hash<Symbol, Statefully::Diff::Change>]
58
+ # @api public
59
+ # @example
60
+ # Statefully::Diff::Unchanged.instance.added.changed
61
+ # => {}
62
+ def changed
63
+ {}
64
+ end
65
+
66
+ # Check if a key has been added
67
+ #
68
+ # @param key [Symbol]
69
+ # @return [Boolean]
70
+ # @api public
71
+ # @example
72
+ # diff = Statefully::Diff::Changed.new(added: {key: 7})
73
+ # diff.added?(:key)
74
+ # => true
75
+ # diff.added?(:other)
76
+ # => false
77
+ def added?(key)
78
+ added.key?(key)
79
+ end
80
+
81
+ # Check if a key has been changed
82
+ #
83
+ # @param key [Symbol]
84
+ # @return [Boolean]
85
+ # @api public
86
+ # @example
87
+ # diff = Statefully::Diff::Changed.new(
88
+ # changed: {key: Statefully::Change.new(current: 7, previous: 8)},
89
+ # )
90
+ # diff.changed?(:key)
91
+ # => true
92
+ # diff.changed?(:other)
93
+ # => false
94
+ def changed?(key)
95
+ changed.key?(key)
96
+ end
97
+
98
+ # {Changed} is a {Diff} which contains changes between two successful
99
+ # {State}s.
100
+ class Changed < Diff
101
+ # Hash of added properties and their values
102
+ #
103
+ # @return [Hash<Symbol, Object>]
104
+ # @api public
105
+ # @example
106
+ # Statefully::Diff::Changed.new(added: {key: 7}).added
107
+ # => {:key => 7}
108
+ attr_reader :added
109
+
110
+ # Hash of changed properties and their current and previous values
111
+ #
112
+ # @return [Hash<Symbol, Change>]
113
+ # @api public
114
+ # @example
115
+ # Statefully::Diff::Changed.new(
116
+ # changed: {key: Statefully::Change.new(current: 7, previous: 8)},
117
+ # )
118
+ # => {:key=>#<Statefully::Change current=7, previous=8>}
119
+ attr_reader :changed
120
+
121
+ # Constructor for {Diff::Changed}
122
+ #
123
+ # @param added [Hash<Symbol, Object>] added fields
124
+ # @param changed [Hash<Symbol, Change>] [changed fields]
125
+ # @api public
126
+ # @example
127
+ # Statefully::Diff::Changed.new(added: {key: 7})
128
+ # => #<Statefully::Diff::Changed added={key: 7}>
129
+ def initialize(added: {}, changed: {})
130
+ @added = added.freeze
131
+ @changed = changed.freeze
19
132
  end
20
133
 
21
- def inspect
22
- "#<#{self.class.name} #{inspect_details}>"
134
+ # Check if a {Diff} resulted from creating a {State}
135
+ #
136
+ # @return [Boolean]
137
+ # @api public
138
+ # @example
139
+ # Stateful::State.created.created?
140
+ # => true
141
+ #
142
+ # Stateful::State.created.succeed.created?
143
+ # => false
144
+ def created?
145
+ false
23
146
  end
24
147
 
25
- def added?(key)
26
- added.key?(key)
148
+ # Check if a {Diff} is empty
149
+ #
150
+ # An empty {Diff} means that there are no changes in properties between
151
+ # current and previous {State}.
152
+ #
153
+ # @return [Boolean]
154
+ # @api public
155
+ # @example
156
+ # Statefully::Diff::Changed.new(added: {key: 7}).empty?
157
+ # => false
158
+ def empty?
159
+ added.empty? && changed.empty?
27
160
  end
28
161
 
29
- def changed?(key)
30
- changed.key?(key)
162
+ # Human-readable representation of the {Change} for console inspection
163
+ #
164
+ # @return [String]
165
+ # @api semipublic
166
+ # @example
167
+ # Statefully::Diff::Changed.new(added: {key: 7})
168
+ # => #<Statefully::Diff::Changed added={key: 7}>
169
+ def inspect
170
+ details = [self.class.name]
171
+ details << inspect_details unless empty?
172
+ "#<#{details.join(' ')}>"
31
173
  end
32
174
 
33
175
  private
34
176
 
177
+ # Helper method to print out added and changed fields
178
+ # @return [String]
179
+ # @api private
35
180
  def inspect_details
36
181
  [inspect_added, inspect_changed].compact.join(', ')
37
182
  end
38
183
 
184
+ # Helper method to print out added fields
185
+ # @return [String]
186
+ # @api private
39
187
  def inspect_added
40
188
  added.empty? ? nil : "added=#{Inspect.from_hash(added)}"
41
189
  end
42
190
 
191
+ # Helper method to print out changed fields
192
+ # @return [String]
193
+ # @api private
43
194
  def inspect_changed
44
195
  changed.empty? ? nil : "changed=#{Inspect.from_hash(changed)}"
45
196
  end
46
-
47
- def initialize(added:, changed:)
48
- @added = added.freeze
49
- @changed = changed.freeze
50
- end
51
197
  end # class Changed
52
198
 
53
- module NoChanges
54
- def empty?
55
- true
56
- end
57
-
58
- def added
59
- {}
199
+ module SingletonInspect
200
+ # Human-readable representation of the {Diff} singleton
201
+ #
202
+ # @return [String]
203
+ # @api private
204
+ def inspect
205
+ "#<#{self.class.name}>"
60
206
  end
61
-
62
- def changed
63
- {}
207
+ end # module SingletonInspect
208
+ private_constant :SingletonInspect
209
+
210
+ # {Created} represents a difference between a null and non-null {State}.
211
+ class Created < Changed
212
+ # Check if a {Diff} resulted from creating a {State}
213
+ #
214
+ # @return [Boolean]
215
+ # @api public
216
+ # @example
217
+ # Stateful::State.created.created?
218
+ # => true
219
+ #
220
+ # Stateful::State.created.succeed.created?
221
+ # => false
222
+ def created?
223
+ true
64
224
  end
65
- end # module NoChanges
66
- private_constant :NoChanges
225
+ end # class Created
67
226
 
68
- class Unchanged
227
+ # {Unchanged} represents a lack of difference between two {State}s.
228
+ class Unchanged < Diff
69
229
  include Singleton
70
- include NoChanges
71
-
72
- def inspect
73
- "#<#{self.class.name}>"
74
- end
230
+ include SingletonInspect
75
231
  end # class Unchanged
76
232
 
77
- class Failed
78
- include NoChanges
233
+ # {Failed} represents a difference between a succesful and failed {State}.
234
+ class Failed < Diff
235
+ # Error that caused the {State} to fail
236
+ #
237
+ # @return [StandardError]
238
+ # @api public
239
+ # @example
240
+ # Statefully::Diff::Failed.new(RuntimeError.new('Boom!')).error
241
+ # => #<RuntimeError: Boom!>
79
242
  attr_reader :error
80
243
 
244
+ # Constructor for {Diff::Failed}
245
+ #
246
+ # @param error [StandardError] error that caused the {State} to fail
247
+ # @api semipublic
248
+ # @example
249
+ # Statefully::Diff::Failed.new(RuntimeError.new('Boom!'))
250
+ # => #<Statefully::Diff::Failed error=#<RuntimeError: Boom!>>
81
251
  def initialize(error)
82
252
  @error = error
83
253
  end
84
254
 
255
+ # Human-readable representation of the {Diff::Failed}
256
+ #
257
+ # @return [String]
258
+ # @api semipublic
259
+ # @example
260
+ # Statefully::Diff::Failed.new(RuntimeError.new('Boom!'))
261
+ # => #<Statefully::Diff::Failed error=#<RuntimeError: Boom!>>
85
262
  def inspect
86
263
  "#<#{self.class.name} error=#{error.inspect}>"
87
264
  end
88
265
  end # class Failed
89
266
 
90
- class Finished < Unchanged
267
+ # {Failed} represents a difference between a succesful and finished {State}.
268
+ class Finished < Diff
269
+ include Singleton
270
+ include SingletonInspect
91
271
  end # class Finished
92
272
 
93
- class Change
94
- attr_reader :current, :previous
95
-
96
- def initialize(current, previous)
97
- @current = current
98
- @previous = previous
99
- end
100
-
101
- def none?
102
- @current == @previous
103
- end
104
-
105
- def inspect
106
- "#<#{self.class.name} " \
107
- "#{Inspect.from_fields(current: current, previous: previous)}>"
108
- end
109
- end # class Change
110
-
111
273
  class Builder
112
- def initialize(current, previous)
274
+ # Constructor for the {Builder} object
275
+ #
276
+ # @param current [State] current {State}
277
+ # @param previous [State] previous {State}
278
+ # @api private
279
+ def initialize(current:, previous:)
113
280
  @current = current
114
281
  @previous = previous
115
282
  end
116
283
 
284
+ # Build a Hash of added and changed {State} fields
285
+ #
286
+ # @return [Hash]
287
+ # @api private
117
288
  def build
118
289
  empty? ? {} : { added: added, changed: changed }
119
290
  end
120
291
 
121
292
  private
122
293
 
294
+ # List added fields
295
+ #
296
+ # @return [Hash]
297
+ # @api private
123
298
  def added
124
299
  @added ||=
125
300
  (current_keys - previous_keys)
@@ -127,6 +302,10 @@ module Statefully
127
302
  .to_h
128
303
  end
129
304
 
305
+ # List changed fields
306
+ #
307
+ # @return [Hash]
308
+ # @api private
130
309
  def changed
131
310
  @changed ||=
132
311
  (current_keys & previous_keys)
@@ -135,22 +314,43 @@ module Statefully
135
314
  .reject { |_, val| val.none? }
136
315
  end
137
316
 
317
+ # Change for individual key
318
+ #
319
+ # @param [Symbol] key name
320
+ #
321
+ # @return [Change]
322
+ # @api private
138
323
  def change_for(key)
139
- Change.new(@current.fetch(key), @previous.fetch(key)).freeze
324
+ Change.new(
325
+ current: @current.fetch(key),
326
+ previous: @previous.fetch(key),
327
+ ).freeze
140
328
  end
141
329
 
330
+ # Check if the nothing has changed
331
+ #
332
+ # @return [Boolean]
333
+ # @api private
142
334
  def empty?
143
335
  added.empty? && changed.empty?
144
336
  end
145
337
 
338
+ # Return the set of keys for the current {State}
339
+ #
340
+ # @return [Set<Symbol>]
341
+ # @api private
146
342
  def current_keys
147
343
  Set.new(@current.keys)
148
344
  end
149
345
 
346
+ # Return the set of keys for previous {State}
347
+ #
348
+ # @return [Set<Symbol>]
349
+ # @api private
150
350
  def previous_keys
151
351
  Set.new(@previous.keys)
152
352
  end
153
353
  end # class Builder
154
354
  private_constant :Builder
155
- end # module Diff
355
+ end # class Diff
156
356
  end # module Statefully