u-case 2.4.0 → 2.5.0

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: d5b58d2e19bca34b4337e348d4d498b21ee12134082f897122c957ab79e19fa7
4
- data.tar.gz: 6cb9e76227b0dc96d92de0c7ae1abdfe4bce2f840f0dced7ce1cae24908c0efb
3
+ metadata.gz: 349609a8d927ab7fd8edb1eb96141dff2f78f9957f31f2b88a8975239fc0de87
4
+ data.tar.gz: 4b6293023f49f28dd8283656a0d423f7d24abbfb7f010a90ba4d0dd17f577d22
5
5
  SHA512:
6
- metadata.gz: ea7dd10cd004dc3ef7e3fbfe062afc6e7a09e172b9fca1c5d2f2300c6d876b19eeb4935d06c8650010b14ca318486226fe08ec5c9fa57a9570870caf9b470bb2
7
- data.tar.gz: 5742702a5c64d2ed8a32cb83aaa6ee8fb5cb550b7144eba248cf058e340f47751528e38214991f1c92c50370b4a6240f7e29a963d872ffe78219d2d91fa5637e
6
+ metadata.gz: 0553e560e40f71d0e8284ebbbbf7a51326d8c9bc9de6ad0de76db526466a94f633e2b038563868a2c93234800b7bdacc30a99ba57f1624ce08463fdc012d165d
7
+ data.tar.gz: 2866ca201676827b2ffc82877b1615e7ecbb6fc6e993df17007c6446b348a850266faf162a0953954ee6adfb0d35a88c303bee385d2fb52a9c228f45513655db
data/README.md CHANGED
@@ -36,10 +36,12 @@ The main project goals are:
36
36
  - [Is it possible to compose a use case flow with other ones?](#is-it-possible-to-compose-a-use-case-flow-with-other-ones)
37
37
  - [Is it possible a flow accumulates its input and merges each success result to use as the argument of the next use cases?](#is-it-possible-a-flow-accumulates-its-input-and-merges-each-success-result-to-use-as-the-argument-of-the-next-use-cases)
38
38
  - [How to understand what is happening during a flow execution?](#how-to-understand-what-is-happening-during-a-flow-execution)
39
- - [Micro::Case::Result#transitions schema.](#microcaseresulttransitions-schema)
39
+ - [`Micro::Case::Result#transitions` schema](#microcaseresulttransitions-schema)
40
40
  - [Is it possible to declare a flow which includes the use case itself?](#is-it-possible-to-declare-a-flow-which-includes-the-use-case-itself)
41
41
  - [`Micro::Case::Strict` - What is a strict use case?](#microcasestrict---what-is-a-strict-use-case)
42
42
  - [`Micro::Case::Safe` - Is there some feature to auto handle exceptions inside of a use case or flow?](#microcasesafe---is-there-some-feature-to-auto-handle-exceptions-inside-of-a-use-case-or-flow)
43
+ - [`Micro::Case::Safe::Flow`](#microcasesafeflow)
44
+ - [`Micro::Case::Result#on_exception`](#microcaseresulton_exception)
43
45
  - [`u-case/with_activemodel_validation` - How to validate use case attributes?](#u-casewith_activemodel_validation---how-to-validate-use-case-attributes)
44
46
  - [If I enabled the auto validation, is it possible to disable it only in specific use case classes?](#if-i-enabled-the-auto-validation-is-it-possible-to-disable-it-only-in-specific-use-case-classes)
45
47
  - [Kind::Validator](#kindvalidator)
@@ -710,7 +712,7 @@ class Users::ValidatePassword < Micro::Case
710
712
  end
711
713
  ```
712
714
 
713
- As you can see the `Users::ValidatePassword` expects a user as its input. So, how it receives the user?
715
+ As you can see the `Users::ValidatePassword` expects a user as its input. So, how does it receives the user?
714
716
  It receives the user from the `Users::Find` success result!
715
717
 
716
718
  And this, is the power of use cases composition because the output
@@ -773,7 +775,9 @@ And look up the `accessible_attributes` property, because it shows whats attribu
773
775
 
774
776
  > **Note:** The [`Micro::Case::Result#then`](#how-to-use-the-microcaseresultthen-method) increments the `Micro::Case::Result#transitions`.
775
777
 
776
- ##### Micro::Case::Result#transitions schema.
778
+ PS: Use the `Micro::Case::Result.disable_transition_tracking` global feature toggle to disable this feature (use once) and increase the use cases' performance.
779
+
780
+ ##### `Micro::Case::Result#transitions` schema
777
781
  ```ruby
778
782
  [
779
783
  {
@@ -906,7 +910,9 @@ end
906
910
  # Examples: https://github.com/serradura/u-case/blob/5a85fc238b63811a32737493dc6c59965f92491d/test/micro/case/safe_test.rb#L95-L123
907
911
  ```
908
912
 
909
- **Flows:**
913
+ [⬆️ Back to Top](#table-of-contents-)
914
+
915
+ #### `Micro::Case::Safe::Flow`
910
916
 
911
917
  As the safe use cases, safe flows can intercept an exception in any of its steps. These are the ways to define one:
912
918
 
@@ -940,7 +946,6 @@ module Users
940
946
  end
941
947
  end
942
948
 
943
-
944
949
  # !------------------------------------------ ! #
945
950
  # ! Deprecated: Micro::Case::Safe::Flow mixin ! #
946
951
  # !-------------------------------------------! #
@@ -961,6 +966,54 @@ end
961
966
 
962
967
  [⬆️ Back to Top](#table-of-contents-)
963
968
 
969
+ #### `Micro::Case::Result#on_exception`
970
+
971
+ In functional programming errors/exceptions are handled as regular data, the idea is to transform the output even when it happens an unexpected behavior. For many, [exceptions are very similar to the GOTO statement](https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why), jumping the application flow to paths which could be difficult to figure out how things work in a system.
972
+
973
+ To address this the `Micro::Case::Result` has a special hook `#on_exception` to helping you to handle the control flow in the case of exceptions.
974
+
975
+ > **Note** this feature will work better if you use it with a `Micro::Case::Safe` use case/flow.
976
+
977
+ How does it work?
978
+
979
+ ```ruby
980
+ class Divide < Micro::Case::Safe
981
+ attributes :a, :b
982
+
983
+ def call!
984
+ Success(division: a / b)
985
+ end
986
+ end
987
+
988
+ Divide
989
+ .call(a: 2, b: 0)
990
+ .on_success { |result| puts result[:division] }
991
+ .on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
992
+ .on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
993
+ .on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
994
+
995
+ # Output:
996
+ # -------
997
+ # Can't divide a number by 0
998
+ # Oh no, something went wrong!
999
+
1000
+ Divide.
1001
+ .call(a: 2, b: '2').
1002
+ .on_success { |result| puts result[:division] }
1003
+ .on_exception(TypeError) { puts 'Please, use only numeric attributes.' }
1004
+ .on_exception(ZeroDivisionError) { |_error| puts "Can't divide a number by 0." }
1005
+ .on_exception { |_error, _use_case| puts 'Oh no, something went wrong!' }
1006
+
1007
+ # Output:
1008
+ # -------
1009
+ # Please, use only numeric attributes.
1010
+ # Oh no, something went wrong!
1011
+ ```
1012
+
1013
+ As you can see, this hook has the same behavior of `result.on_failure(:exception)`, but, the ideia here is to have a better communication in the code, making an explicit reference when some failure happened because of an exception.
1014
+
1015
+ [⬆️ Back to Top](#table-of-contents-)
1016
+
964
1017
  ### `u-case/with_activemodel_validation` - How to validate use case attributes?
965
1018
 
966
1019
  **Requirement:**
@@ -51,20 +51,32 @@ module Micro
51
51
  __new__(result, arg).call
52
52
  end
53
53
 
54
+ FLOW_STEP = 'Flow_Step'.freeze
55
+
56
+ private_constant :FLOW_STEP
57
+
54
58
  def self.__call!
55
- return const_get(:Flow_Step) if const_defined?(:Flow_Step)
59
+ return const_get(FLOW_STEP) if const_defined?(FLOW_STEP, false)
56
60
 
57
- const_set(:Flow_Step, Class.new(self) do
58
- private def __call
59
- __call_use_case
60
- end
61
- end)
61
+ class_eval("class #{FLOW_STEP} < #{self.name}; private def __call; __call_use_case; end; end")
62
62
  end
63
63
 
64
64
  def self.call!
65
65
  self
66
66
  end
67
67
 
68
+ def self.inherited(subclass)
69
+ subclass.attributes(self.attributes_data({}))
70
+ subclass.extend ::Micro::Attributes.const_get('Macros::ForSubclasses'.freeze)
71
+
72
+ if self.send(:__flow_use_cases) && !subclass.name.to_s.end_with?(FLOW_STEP)
73
+ raise "Wooo, you can't do this! Inherits from a use case which has an inner flow violates "\
74
+ "one of the project principles: Solve complex business logic, by allowing the composition of use cases. "\
75
+ "Instead of doing this, declare a new class/constant with the steps needed.\n\n"\
76
+ "Related issue: https://github.com/serradura/u-case/issues/19\n"
77
+ end
78
+ end
79
+
68
80
  def self.__flow_reducer
69
81
  Flow::Reducer
70
82
  end
@@ -58,7 +58,9 @@ module Micro
58
58
  end
59
59
 
60
60
  def on_success(expected_type = nil)
61
- self.tap { yield(value) if success_type?(expected_type) }
61
+ yield(value) if success_type?(expected_type)
62
+
63
+ self
62
64
  end
63
65
 
64
66
  def on_failure(expected_type = nil)
@@ -66,7 +68,19 @@ module Micro
66
68
 
67
69
  data = expected_type.nil? ? Data.new(value, type).tap(&:freeze) : value
68
70
 
69
- self.tap { yield(data, @use_case) }
71
+ yield(data, @use_case)
72
+
73
+ self
74
+ end
75
+
76
+ def on_exception(expected_exception = nil)
77
+ return self unless failure_type?(:exception)
78
+
79
+ if !expected_exception || (Kind.is(Exception, expected_exception) && value.is_a?(expected_exception))
80
+ yield(value, @use_case)
81
+ end
82
+
83
+ self
70
84
  end
71
85
 
72
86
  def then(arg = nil, &block)
@@ -124,7 +138,7 @@ module Micro
124
138
 
125
139
  def __set_transition__
126
140
  use_case_class = @use_case.class
127
- use_case_attributes = Utils.symbolize_keys(@use_case.attributes)
141
+ use_case_attributes = Utils.symbolize_hash_keys(@use_case.attributes)
128
142
 
129
143
  __set_transitions_accessible_attributes__!(use_case_attributes.keys)
130
144
 
@@ -3,8 +3,8 @@
3
3
  module Micro
4
4
  class Case
5
5
  module Utils
6
- def self.symbolize_keys(hash)
7
- if Kind.of.Hash(hash).respond_to?(:transform_keys)
6
+ def self.symbolize_hash_keys(hash)
7
+ if Kind::Of::Hash(hash).respond_to?(:transform_keys)
8
8
  hash.transform_keys { |key| key.to_sym rescue key }
9
9
  else
10
10
  hash.each_with_object({}) do |(k, v), memo|
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  class Case
5
- VERSION = '2.4.0'.freeze
5
+ VERSION = '2.5.0'.freeze
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-case
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura