u-case 2.4.0 → 2.5.0

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