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

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
  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