lotus-utils 0.3.4 → 0.3.5

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: 339681343526eb287d81d4f52ca29bda0550d8e7
4
- data.tar.gz: 3e22d02c21a013f03cb1789e307e59054ba37ff1
3
+ metadata.gz: fbc9ff8be5d774d9e30695445d43995a66cbf21d
4
+ data.tar.gz: 4f5ccb96067b17d2c6f77b5b0d5eb5ccd2def30f
5
5
  SHA512:
6
- metadata.gz: da44a5d936f554ae76be3c7c8195bd416268c5c0f81898df0c4959cc4aee3ab38180fa995943fac286ff464ebed477fb07307562330b0d086162b22fc4dbb41d
7
- data.tar.gz: 6e835299ce217838cd487070b1ade2a5b7c8916af0eef233e76716257f99b258360faba93ca4ac8c29c84efc6dbccaf46db99d35b81891e00c76f92b39d62fc5
6
+ metadata.gz: d254f1b09c52c91198986bc340f17b9cfe35146bcbeed9464c4f99d390e45eb8dbe8e11cb6672b42716e7136294a00300fe0cb207636a8e0e24cbc80f1d2eeef
7
+ data.tar.gz: 18bcf46df8ab2013f334d962ed859919df1e6711562b89c2a8eec959141551f901b975c2dfdd084f00b894f53cd53f460dca6881add6d63c9cb176a13de3c35c
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Lotus::Utils
2
2
  Ruby core extentions and class utilities for Lotus
3
3
 
4
+ ## v0.3.5 - 2015-03-12
5
+ ### Added
6
+ - [Luca Guidi] Introduced `Lotus::Interactor`
7
+ - [Luca Guidi] Introduced `Lotus::Utils::BasicObject`
8
+
4
9
  ## v0.3.4 - 2015-01-30
5
10
  ### Added
6
11
  - [Alfonso Uceda Pompa] Aliased `Lotus::Utils::Attributes#get` with `#[]`
@@ -0,0 +1,429 @@
1
+ require 'lotus/utils/basic_object'
2
+ require 'lotus/utils/hash'
3
+
4
+ module Lotus
5
+ # Lotus Interactor
6
+ #
7
+ # @since 0.3.5
8
+ module Interactor
9
+ # Result of an operation
10
+ #
11
+ # @since 0.3.5
12
+ class Result < Utils::BasicObject
13
+ # Concrete methods
14
+ #
15
+ # @since 0.3.5
16
+ # @api private
17
+ #
18
+ # @see Lotus::Interactor::Result#respond_to_missing?
19
+ METHODS = {initialize: true, success?: true, fail!: true, prepare!: true, errors: true, error: true}.freeze
20
+
21
+ # Initialize a new result
22
+ #
23
+ # @param payload [Hash] a payload to carry on
24
+ #
25
+ # @return [Lotus::Interactor::Result]
26
+ #
27
+ # @since 0.3.5
28
+ # @api private
29
+ def initialize(payload = {})
30
+ @payload = _payload(payload)
31
+ @success = true
32
+ end
33
+
34
+ # Check if the current status is successful
35
+ #
36
+ # @return [TrueClass,FalseClass] the result of the check
37
+ #
38
+ # @since 0.3.5
39
+ def success?
40
+ @success && errors.empty?
41
+ end
42
+
43
+ # Force the status to be a failure
44
+ #
45
+ # @since 0.3.5
46
+ def fail!
47
+ @success = false
48
+ end
49
+
50
+ # Returns all the errors collected during an operation
51
+ #
52
+ # @return [Array] the errors
53
+ #
54
+ # @since 0.3.5
55
+ #
56
+ # @see Lotus::Interactor::Result#error
57
+ # @see Lotus::Interactor#call
58
+ # @see Lotus::Interactor#error
59
+ # @see Lotus::Interactor#error!
60
+ def errors
61
+ @payload.fetch(:_errors) { [] }
62
+ end
63
+
64
+ # Returns the first errors collected during an operation
65
+ #
66
+ # @return [nil,String] the error, if present
67
+ #
68
+ # @since 0.3.5
69
+ #
70
+ # @see Lotus::Interactor::Result#errors
71
+ # @see Lotus::Interactor#call
72
+ # @see Lotus::Interactor#error
73
+ # @see Lotus::Interactor#error!
74
+ def error
75
+ errors.first
76
+ end
77
+
78
+ # Prepare the result before to be returned
79
+ #
80
+ # @param payload [Hash] an updated payload
81
+ #
82
+ # @since 0.3.5
83
+ # @api private
84
+ def prepare!(payload)
85
+ @payload.merge!(_payload(payload))
86
+ self
87
+ end
88
+
89
+ protected
90
+ # @since 0.3.5
91
+ # @api private
92
+ def method_missing(m, *)
93
+ @payload.fetch(m) { super }
94
+ end
95
+
96
+ # @since 0.3.5
97
+ # @api private
98
+ def respond_to_missing?(method_name, include_all)
99
+ method_name = method_name.to_sym
100
+ METHODS[method_name] || @payload.key?(method_name)
101
+ end
102
+
103
+ # @since 0.3.5
104
+ # @api private
105
+ def _payload(payload)
106
+ Utils::Hash.new(payload).symbolize!
107
+ end
108
+
109
+ # @since 0.3.5
110
+ # @api private
111
+ def __inspect
112
+ " @success=#{ @success } @payload=#{ @payload.inspect }"
113
+ end
114
+ end
115
+
116
+ # Override for <tt>Module#included</tt>.
117
+ #
118
+ # @since 0.3.5
119
+ # @api private
120
+ def self.included(base)
121
+ super
122
+
123
+ base.class_eval do
124
+ prepend Interface
125
+ end
126
+ end
127
+
128
+ # Interactor interface
129
+ #
130
+ # @since 0.3.5
131
+ module Interface
132
+ # Initialize an interactor
133
+ #
134
+ # It accepts arbitrary number of arguments.
135
+ # Developers can override it.
136
+ #
137
+ # @param args [Array<Object>] arbitrary number of arguments
138
+ #
139
+ # @return [Lotus::Interactor] the interactor
140
+ #
141
+ # @since 0.3.5
142
+ #
143
+ # @example Override #initialize
144
+ # require 'lotus/interactor'
145
+ #
146
+ # class UpdateProfile
147
+ # include Lotus::Interactor
148
+ #
149
+ # def initialize(person, params)
150
+ # @person = person
151
+ # @params = params
152
+ # end
153
+ #
154
+ # def call
155
+ # # ...
156
+ # end
157
+ # end
158
+ def initialize(*args)
159
+ super
160
+ ensure
161
+ @__result = ::Lotus::Interactor::Result.new
162
+ @_errors = []
163
+ end
164
+
165
+ # Triggers the operation and return a result.
166
+ #
167
+ # All the instance variables will be available in the result.
168
+ #
169
+ # ATTENTION: This must be implemented by the including class.
170
+ #
171
+ # @return [Lotus::Interactor::Result] the result of the operation
172
+ #
173
+ # @raise [NoMethodError] if this isn't implemented by the including class.
174
+ #
175
+ # @example Instance variables in result payload
176
+ # require 'lotus/interactor'
177
+ #
178
+ # class Signup
179
+ # def initialize(params)
180
+ # @params = params
181
+ # @user = User.new(@params)
182
+ # @foo = 'bar'
183
+ # end
184
+ #
185
+ # def call
186
+ # @user = UserRepository.persist(@user)
187
+ # end
188
+ # end
189
+ #
190
+ # result = Signup.new(name: 'Luca').call
191
+ # result.success? # => true
192
+ #
193
+ # result.user # => #<User:0x007fa311105778 @id=1 @name="Luca">
194
+ # result.params # => { :name=>"Luca" }
195
+ # result.foo # => "Bar"
196
+ #
197
+ # @example Failed precondition
198
+ # require 'lotus/interactor'
199
+ #
200
+ # class Signup
201
+ # def initialize(params)
202
+ # @params = params
203
+ # @user = User.new(@params)
204
+ # end
205
+ #
206
+ # # THIS WON'T BE INVOKED BECAUSE #valid? WILL RETURN false
207
+ # def call
208
+ # @user = UserRepository.persist(@user)
209
+ # end
210
+ #
211
+ # private
212
+ # def valid?
213
+ # @params.valid?
214
+ # end
215
+ # end
216
+ #
217
+ # result = Signup.new(name: nil).call
218
+ # result.success? # => false
219
+ #
220
+ # result.user # => #<User:0x007fa311105778 @id=nil @name="Luca">
221
+ #
222
+ # @example Bad usage
223
+ # require 'lotus/interactor'
224
+ #
225
+ # class Signup
226
+ # include Lotus::Interactor
227
+ #
228
+ # # Method #call is not defined
229
+ # end
230
+ #
231
+ # Signup.new.call # => NoMethodError
232
+ def call
233
+ _call { super }
234
+ end
235
+ end
236
+
237
+ private
238
+ # Check if proceed with <tt>#call</tt> invokation.
239
+ # By default it returns <tt>true</tt>.
240
+ #
241
+ # Developers can override it.
242
+ #
243
+ # @return [TrueClass,FalseClass] the result of the check
244
+ #
245
+ # @since 0.3.5
246
+ def valid?
247
+ true
248
+ end
249
+
250
+ # Fail and interrupt the current flow.
251
+ #
252
+ # @since 0.3.5
253
+ #
254
+ # @example
255
+ # require 'lotus/interactor'
256
+ #
257
+ # class CreateEmailTest
258
+ # include Lotus::Interactor
259
+ #
260
+ # def initialize(params)
261
+ # @params = params
262
+ # @email_test = EmailTest.new(@params)
263
+ # end
264
+ #
265
+ # def call
266
+ # persist_email_test!
267
+ # capture_screenshot!
268
+ # end
269
+ #
270
+ # private
271
+ # def persist_email_test!
272
+ # @email_test = EmailTestRepository.persist(@email_test)
273
+ # end
274
+ #
275
+ # # IF THIS RAISES AN EXCEPTION WE FORCE A FAILURE
276
+ # def capture_screenshot!
277
+ # Screenshot.new(@email_test).capture!
278
+ # rescue
279
+ # fail!
280
+ # end
281
+ # end
282
+ #
283
+ # result = CreateEmailTest.new(account_id: 1).call
284
+ # result.success? # => false
285
+ def fail!
286
+ @__result.fail!
287
+ throw :fail
288
+ end
289
+
290
+ # Log an error without interrupting the flow.
291
+ #
292
+ # When used, the returned result won't be successful.
293
+ #
294
+ # @param message [String] the error message
295
+ #
296
+ # @since 0.3.5
297
+ #
298
+ # @see Lotus::Interactor#error!
299
+ #
300
+ # @example
301
+ # require 'lotus/interactor'
302
+ #
303
+ # class CreateRecord
304
+ # include Lotus::Interactor
305
+ #
306
+ # def initialize
307
+ # @logger = []
308
+ # end
309
+ #
310
+ # def call
311
+ # prepare_data!
312
+ # persist!
313
+ # sync!
314
+ # end
315
+ #
316
+ # private
317
+ # def prepare_data!
318
+ # @logger << __method__
319
+ # error "Prepare data error"
320
+ # end
321
+ #
322
+ # def persist!
323
+ # @logger << __method__
324
+ # error "Persist error"
325
+ # end
326
+ #
327
+ # def sync!
328
+ # @logger << __method__
329
+ # end
330
+ # end
331
+ #
332
+ # result = CreateRecord.new.call
333
+ # result.success? # => false
334
+ #
335
+ # result.errors # => ["Prepare data error", "Persist error"]
336
+ # result.logger # => [:prepare_data!, :persist!, :sync!]
337
+ def error(message)
338
+ @_errors << message
339
+ end
340
+
341
+ # Log an error AND interrupting the flow.
342
+ #
343
+ # When used, the returned result won't be successful.
344
+ #
345
+ # @param message [String] the error message
346
+ #
347
+ # @since 0.3.5
348
+ #
349
+ # @see Lotus::Interactor#error
350
+ #
351
+ # @example
352
+ # require 'lotus/interactor'
353
+ #
354
+ # class CreateRecord
355
+ # include Lotus::Interactor
356
+ #
357
+ # def initialize
358
+ # @logger = []
359
+ # end
360
+ #
361
+ # def call
362
+ # prepare_data!
363
+ # persist!
364
+ # sync!
365
+ # end
366
+ #
367
+ # private
368
+ # def prepare_data!
369
+ # @logger << __method__
370
+ # error "Prepare data error"
371
+ # end
372
+ #
373
+ # def persist!
374
+ # @logger << __method__
375
+ # error! "Persist error"
376
+ # end
377
+ #
378
+ # # THIS WILL NEVER BE INVOKED BECAUSE WE USE #error! IN #persist!
379
+ # def sync!
380
+ # @logger << __method__
381
+ # end
382
+ # end
383
+ #
384
+ # result = CreateRecord.new.call
385
+ # result.success? # => false
386
+ #
387
+ # result.errors # => ["Prepare data error", "Persist error"]
388
+ # result.logger # => [:prepare_data!, :persist!]
389
+ def error!(message)
390
+ error(message)
391
+ fail!
392
+ end
393
+
394
+ # @since 0.3.5
395
+ # @api private
396
+ def _call
397
+ catch :fail do
398
+ validate!
399
+ yield
400
+ end
401
+
402
+ _prepare!
403
+ end
404
+
405
+ # @since 0.3.5
406
+ def validate!
407
+ fail! unless valid?
408
+ end
409
+
410
+ # @since 0.3.5
411
+ # @api private
412
+ def _prepare!
413
+ @__result.prepare!(_instance_variables)
414
+ end
415
+
416
+ # @since 0.3.5
417
+ # @api private
418
+ def _instance_variables
419
+ Hash[].tap do |result|
420
+ instance_variables.each do |iv|
421
+ name = iv.to_s.sub(/\A@/, '')
422
+ next if name.match(/\A__/)
423
+
424
+ result[name.to_sym] = instance_variable_get(iv)
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end
@@ -0,0 +1,53 @@
1
+ module Lotus
2
+ module Utils
3
+ # BasicObject
4
+ #
5
+ # @since 0.3.5
6
+ class BasicObject < ::BasicObject
7
+ # Return the class for debugging purposes.
8
+ #
9
+ # @since 0.3.5
10
+ #
11
+ # @see http://ruby-doc.org/core/Object.html#method-i-class
12
+ def class
13
+ (class << self; self end).superclass
14
+ end
15
+
16
+ # Bare minimum inspect for debugging purposes.
17
+ #
18
+ # @return [String] the inspect string
19
+ #
20
+ # @since 0.3.5
21
+ #
22
+ # @see http://ruby-doc.org/core/Object.html#method-i-inspect
23
+ def inspect
24
+ "#<#{ self.class }:#{'%x' % (__id__ << 1)}#{ __inspect }>"
25
+ end
26
+
27
+ # Returns true if responds to the given method.
28
+ #
29
+ # @return [TrueClass,FalseClass] the result of the check
30
+ #
31
+ # @since 0.3.5
32
+ #
33
+ # @see http://ruby-doc.org/core-2.2.1/Object.html#method-i-respond_to-3F
34
+ def respond_to?(method_name, include_all = false)
35
+ respond_to_missing?(method_name, include_all)
36
+ end
37
+
38
+ private
39
+ # Must be overridden by descendants
40
+ #
41
+ # @since 0.3.5
42
+ # @api private
43
+ def respond_to_missing?(method_name, include_all)
44
+ ::Kernel.raise ::NotImplementedError
45
+ end
46
+
47
+ # @since 0.3.5
48
+ # @api private
49
+ def __inspect
50
+ end
51
+ end
52
+ end
53
+ end
@@ -3,6 +3,6 @@ module Lotus
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '0.3.4'.freeze
6
+ VERSION = '0.3.5'.freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lotus-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-30 00:00:00.000000000 Z
12
+ date: 2015-03-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -64,8 +64,10 @@ files:
64
64
  - CHANGELOG.md
65
65
  - LICENSE.md
66
66
  - README.md
67
+ - lib/lotus/interactor.rb
67
68
  - lib/lotus/utils.rb
68
69
  - lib/lotus/utils/attributes.rb
70
+ - lib/lotus/utils/basic_object.rb
69
71
  - lib/lotus/utils/callbacks.rb
70
72
  - lib/lotus/utils/class.rb
71
73
  - lib/lotus/utils/class_attribute.rb