lotus-utils 0.3.4 → 0.3.5

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