u-case 2.0.0.pre.2 → 2.0.0.pre.3

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
  SHA256:
3
- metadata.gz: 3028799bfa1648b6efd0ce3f4d69e9fa696a5bed0b5adfc9cf736e8cdb1dcdba
4
- data.tar.gz: 60eae622b2bfd1e6f28342c560adc129e40bb2c1fb7d9ec0e9c7f158c07db55a
3
+ metadata.gz: 1faaf3599f7b9e24aca4fcab71c448b7078756a6ca9abce84426a456673c4f50
4
+ data.tar.gz: 329095160dc157bd6ac3dda112c83637e8fe169050497bd5e3e2698c095415cd
5
5
  SHA512:
6
- metadata.gz: 1b8c49b2717af8aba8cc8eaaa1b9e84fc394e1b766e0414b46db5a91bfa4452ac0624b6da760a6c0aff7167a85311a3e93e7897e0ee88deb591b5356e9445ed2
7
- data.tar.gz: 7ab4c1acdb3828c87b4914ab946029cb283a36fbbf130208cb2b2091c18151e3b9dd2713d23c0918dea6a2f787298ed9fb07bdb724df9467df7985d0b43d4c9b
6
+ metadata.gz: f02f2e9db0044e56e9ec690d69f69d302f666b99ccff37106369a63cf6a39c0d15cefe9d07d28464a75bbb1f236b1fd6ad3eddc40f25dd473e54b0ee2d77c02d
7
+ data.tar.gz: 6b81d3c72c67596229689b3bc9cccc3a9ef20c9d7fd45d506913215d3fa759e174aa94d7105aeba6dfb184278b54fbfd1a9e5d7e56cdb4468968a06a96e4f7cd
data/README.md CHANGED
@@ -17,6 +17,7 @@ The main goals of this project are:
17
17
  ## Table of Contents <!-- omit in toc -->
18
18
  - [μ-case (Micro::Case)](#%ce%bc-case-microcase)
19
19
  - [Required Ruby version](#required-ruby-version)
20
+ - [Dependencies](#dependencies)
20
21
  - [Installation](#installation)
21
22
  - [Usage](#usage)
22
23
  - [How to define a use case?](#how-to-define-a-use-case)
@@ -25,6 +26,7 @@ The main goals of this project are:
25
26
  - [How to define custom result types?](#how-to-define-custom-result-types)
26
27
  - [Is it possible to define a custom result type without a block?](#is-it-possible-to-define-a-custom-result-type-without-a-block)
27
28
  - [How to use the result hooks?](#how-to-use-the-result-hooks)
29
+ - [Why the on_failure result hook exposes a different kind of data?](#why-the-onfailure-result-hook-exposes-a-different-kind-of-data)
28
30
  - [What happens if a result hook is declared multiple times?](#what-happens-if-a-result-hook-is-declared-multiple-times)
29
31
  - [How to compose uses cases to represents complex ones?](#how-to-compose-uses-cases-to-represents-complex-ones)
30
32
  - [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
@@ -43,6 +45,11 @@ The main goals of this project are:
43
45
 
44
46
  > \>= 2.2.0
45
47
 
48
+ ## Dependencies
49
+
50
+ This project depends on [Micro::Attribute](https://github.com/serradura/u-attributes) gem.
51
+ It is used to define the use case attributes.
52
+
46
53
  ## Installation
47
54
 
48
55
  Add this line to your application's Gemfile:
@@ -71,7 +78,7 @@ class Multiply < Micro::Case
71
78
  # 2. Define the method `call!` with its business logic
72
79
  def call!
73
80
 
74
- # 3. Wrap the use case result/output using the `Success()` and `Failure()` methods
81
+ # 3. Wrap the use case result/output using the `Success()` or `Failure()` methods
75
82
  if a.is_a?(Numeric) && b.is_a?(Numeric)
76
83
  Success(a * b)
77
84
  else
@@ -116,12 +123,12 @@ result.value # 6
116
123
 
117
124
  ### What is a `Micro::Case::Result`?
118
125
 
119
- A `Micro::Case::Result` stores use cases output data. These are their main methods:
126
+ A `Micro::Case::Result` stores the use cases output data. These are their main methods:
120
127
  - `#success?` returns true if is a successful result.
121
128
  - `#failure?` returns true if is an unsuccessful result.
122
129
  - `#value` the result value itself.
123
130
  - `#type` a Symbol which gives meaning for the result, this is useful to declare different types of failures or success.
124
- - `#on_success` or `#on_failure` are hook methods which help you define the application flow.
131
+ - `#on_success` or `#on_failure` are hook methods that help you define the application flow.
125
132
  - `#use_case` if is a failure result, the use case responsible for it will be accessible through this method. This feature is handy to handle a flow failure (this topic will be covered ahead).
126
133
 
127
134
  [⬆️ Back to Top](#table-of-contents-)
@@ -256,7 +263,7 @@ The examples below show how to use them:
256
263
 
257
264
  ```ruby
258
265
  class Double < Micro::Case
259
- attributes :number
266
+ attribute :number
260
267
 
261
268
  def call!
262
269
  return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
@@ -286,25 +293,85 @@ Double
286
293
  Double
287
294
  .call(number: -1)
288
295
  .on_success { |number| p number }
289
- .on_failure { |_msg, use_case| puts "#{use_case.class.name} was the use case responsible for the failure" }
296
+ .on_failure { |_result, use_case| puts "#{use_case.class.name} was the use case responsible for the failure" }
290
297
  .on_failure(:invalid) { |msg| raise TypeError, msg }
291
298
  .on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
292
299
 
293
- # The outputs because it is a failure:
294
- # Double was the use case responsible for the failure
295
- # (throws the error)
296
- # ArgumentError (the number must be greater than 0)
300
+ # The outputs will be:
301
+ #
302
+ # 1. Prints the message: Double was the use case responsible for the failure
303
+ # 2. Raises the exception: ArgumentError (the number must be greater than 0)
297
304
 
298
305
  # Note:
299
306
  # ----
300
307
  # The use case responsible for the failure will be accessible as the second hook argument
301
308
  ```
302
309
 
310
+ #### Why the on_failure result hook exposes a different kind of data?
311
+
312
+ Answer: To allow you to define how to handle the program flow using some
313
+ conditional statement (like an `if`, `case/when`).
314
+
315
+ ```ruby
316
+ class Double < Micro::Case
317
+ attribute :number
318
+
319
+ def call!
320
+ return Failure(:invalid) unless number.is_a?(Numeric)
321
+ return Failure(:lte_zero) if number <= 0
322
+
323
+ Success(number * 2)
324
+ end
325
+ end
326
+
327
+ #=================================#
328
+ # Using the result type and value #
329
+ #=================================#
330
+
331
+ Double
332
+ .call(-1)
333
+ .on_failure do |result, use_case|
334
+ case result.type
335
+ when :invalid then raise TypeError, 'the number must be a numeric value'
336
+ when :lte_zero then raise ArgumentError, "the number `#{result.value}` must be greater than 0"
337
+ else raise NotImplementedError
338
+ end
339
+ end
340
+
341
+ # The output will be the exception:
342
+ #
343
+ # ArgumentError (the number `-1` must be greater than 0)
344
+
345
+ #=====================================================#
346
+ # Using decomposition to access result value and type #
347
+ #=====================================================#
348
+
349
+ # The syntax to decompose an Array can be used in methods, blocks and assigments.
350
+ # If you doesn't know that, check out:
351
+ # https://ruby-doc.org/core-2.2.0/doc/syntax/assignment_rdoc.html#label-Array+Decomposition
352
+ #
353
+ # And the object exposed in the hook failure can be decomposed using this syntax. e.g:
354
+
355
+ Double
356
+ .call(-2)
357
+ .on_failure do |(value, type), use_case|
358
+ case type
359
+ when :invalid then raise TypeError, 'the number must be a numeric value'
360
+ when :lte_zero then raise ArgumentError, "the number `#{value}` must be greater than 0"
361
+ else raise NotImplementedError
362
+ end
363
+ end
364
+
365
+ # The output will be the exception:
366
+ #
367
+ # ArgumentError (the number `-2` must be greater than 0)
368
+ ```
369
+
303
370
  [⬆️ Back to Top](#table-of-contents-)
304
371
 
305
372
  #### What happens if a result hook is declared multiple times?
306
373
 
307
- Answer: The hook will be triggered if it matches the result type.
374
+ Answer: The hook always will be triggered if it matches the result type.
308
375
 
309
376
  ```ruby
310
377
  class Double < Micro::Case
@@ -337,11 +404,11 @@ result.value * 4 == accum # true
337
404
 
338
405
  ### How to compose uses cases to represents complex ones?
339
406
 
340
- In this case, this will be is a **flow**, because the idea is to use/reuse use cases as steps which will define a more complex one.
407
+ In this case, this will be a **flow**. The main idea is to use/reuse use cases as steps of a new use case.
341
408
 
342
409
  ```ruby
343
410
  module Steps
344
- class ConvertToNumbers < Micro::Case
411
+ class ConvertTextToNumbers < Micro::Case
345
412
  attribute :numbers
346
413
 
347
414
  def call!
@@ -383,7 +450,7 @@ end
383
450
  #---------------------------------------------#
384
451
 
385
452
  Add2ToAllNumbers = Micro::Case::Flow([
386
- Steps::ConvertToNumbers,
453
+ Steps::ConvertTextToNumbers,
387
454
  Steps::Add2
388
455
  ])
389
456
 
@@ -399,7 +466,7 @@ p result.value # {:numbers => [3, 3, 4, 4, 5, 6]}
399
466
  class DoubleAllNumbers
400
467
  include Micro::Case::Flow
401
468
 
402
- flow Steps::ConvertToNumbers, Steps::Double
469
+ flow Steps::ConvertTextToNumbers, Steps::Double
403
470
  end
404
471
 
405
472
  DoubleAllNumbers
@@ -411,7 +478,7 @@ DoubleAllNumbers
411
478
  #-------------------------------------------------------------#
412
479
 
413
480
  SquareAllNumbers =
414
- Steps::ConvertToNumbers >> Steps::Square
481
+ Steps::ConvertTextToNumbers >> Steps::Square
415
482
 
416
483
  SquareAllNumbers
417
484
  .call(numbers: %w[1 1 2 2 3 4])
@@ -425,10 +492,10 @@ SquareAllNumbers
425
492
  result = SquareAllNumbers.call(numbers: %w[1 1 b 2 3 4])
426
493
 
427
494
  result.failure? # true
428
- result.use_case.is_a?(Steps::ConvertToNumbers) # true
495
+ result.use_case.is_a?(Steps::ConvertTextToNumbers) # true
429
496
 
430
497
  result.on_failure do |_message, use_case|
431
- puts "#{use_case.class.name} was the use case responsible for the failure" # Steps::ConvertToNumbers was the use case responsible for the failure
498
+ puts "#{use_case.class.name} was the use case responsible for the failure" # Steps::ConvertTextToNumbers was the use case responsible for the failure
432
499
  end
433
500
  ```
434
501
 
@@ -440,7 +507,7 @@ Answer: Yes, it is.
440
507
 
441
508
  ```ruby
442
509
  module Steps
443
- class ConvertToNumbers < Micro::Case
510
+ class ConvertTextToNumbers < Micro::Case
444
511
  attribute :numbers
445
512
 
446
513
  def call!
@@ -477,9 +544,9 @@ module Steps
477
544
  end
478
545
  end
479
546
 
480
- Add2ToAllNumbers = Steps::ConvertToNumbers >> Steps::Add2
481
- DoubleAllNumbers = Steps::ConvertToNumbers >> Steps::Double
482
- SquareAllNumbers = Steps::ConvertToNumbers >> Steps::Square
547
+ Add2ToAllNumbers = Steps::ConvertTextToNumbers >> Steps::Add2
548
+ DoubleAllNumbers = Steps::ConvertTextToNumbers >> Steps::Double
549
+ SquareAllNumbers = Steps::ConvertTextToNumbers >> Steps::Square
483
550
 
484
551
  DoubleAllNumbersAndAdd2 = DoubleAllNumbers >> Steps::Add2
485
552
  SquareAllNumbersAndAdd2 = SquareAllNumbers >> Steps::Add2
@@ -515,7 +582,7 @@ end
515
582
 
516
583
  Double.call({})
517
584
 
518
- # The output (raised an error):
585
+ # The output will be the following exception:
519
586
  # ArgumentError (missing keyword: :numbers)
520
587
  ```
521
588
 
@@ -3,6 +3,18 @@
3
3
  module Micro
4
4
  class Case
5
5
  class Result
6
+ class Data
7
+ attr_reader :value, :type
8
+
9
+ def initialize(value, type)
10
+ @value, @type = value, type
11
+ end
12
+
13
+ def to_ary; [value, type]; end
14
+ end
15
+
16
+ private_constant :Data
17
+
6
18
  attr_reader :value, :type
7
19
 
8
20
  def __set__(is_success, value, type, use_case)
@@ -28,22 +40,26 @@ module Micro
28
40
  raise Error::InvalidAccessToTheUseCaseObject
29
41
  end
30
42
 
31
- def on_success(arg = nil)
32
- self.tap { yield(value) if success_type?(arg) }
43
+ def on_success(expected_type = nil)
44
+ self.tap { yield(value) if success_type?(expected_type) }
33
45
  end
34
46
 
35
- def on_failure(arg = nil)
36
- self.tap{ yield(value, @use_case) if failure_type?(arg) }
47
+ def on_failure(expected_type = nil)
48
+ return self unless failure_type?(expected_type)
49
+
50
+ data = expected_type.nil? ? Data.new(value, type).tap(&:freeze) : value
51
+
52
+ self.tap { yield(data, @use_case) }
37
53
  end
38
54
 
39
55
  private
40
56
 
41
- def success_type?(arg)
42
- success? && (arg.nil? || arg == type)
57
+ def success_type?(expected_type)
58
+ success? && (expected_type.nil? || expected_type == type)
43
59
  end
44
60
 
45
- def failure_type?(arg)
46
- failure? && (arg.nil? || arg == type)
61
+ def failure_type?(expected_type)
62
+ failure? && (expected_type.nil? || expected_type == type)
47
63
  end
48
64
 
49
65
  def is_a_use_case?(arg)
@@ -3,10 +3,6 @@
3
3
  module Micro
4
4
  class Case
5
5
  class Safe < ::Micro::Case
6
- def self.Flow(args)
7
- Flow::Reducer.build(Array(args))
8
- end
9
-
10
6
  def call
11
7
  super
12
8
  rescue => exception
@@ -10,10 +10,6 @@ module Micro
10
10
  def base.flow_reducer; Reducer; end
11
11
  end
12
12
 
13
- def self.Flow(args)
14
- Reducer.build(Array(args))
15
- end
16
-
17
13
  class Reducer < ::Micro::Case::Flow::Reducer
18
14
  def call(arg = {})
19
15
  @use_cases.reduce(initial_result(arg)) do |result, use_case|
@@ -43,6 +39,10 @@ module Micro
43
39
  end
44
40
  end
45
41
  end
42
+
43
+ def self.Flow(args)
44
+ Flow::Reducer.build(Array(args))
45
+ end
46
46
  end
47
47
  end
48
48
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  class Case
5
- VERSION = '2.0.0.pre.2'.freeze
5
+ VERSION = '2.0.0.pre.3'.freeze
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-case
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre.2
4
+ version: 2.0.0.pre.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-13 00:00:00.000000000 Z
11
+ date: 2019-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: u-attributes