decouplio 1.0.0alpha7 → 1.0.0rc

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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +313 -15
  3. data/decouplio.gemspec +8 -2
  4. data/lib/decouplio/action.rb +34 -74
  5. data/lib/decouplio/action_state_printer.rb +34 -0
  6. data/lib/decouplio/const/flows.rb +59 -0
  7. data/lib/decouplio/const/reserved_methods.rb +6 -13
  8. data/lib/decouplio/const/types.rb +1 -165
  9. data/lib/decouplio/const/validations/common.rb +10 -0
  10. data/lib/decouplio/const/validations/octo.rb +16 -0
  11. data/lib/decouplio/const/validations/resq.rb +12 -1
  12. data/lib/decouplio/ctx.rb +13 -0
  13. data/lib/decouplio/default_meta_store.rb +37 -0
  14. data/lib/decouplio/errors/action_class_error.rb +0 -3
  15. data/lib/decouplio/errors/base_error.rb +0 -3
  16. data/lib/decouplio/errors/execution_error.rb +0 -2
  17. data/lib/decouplio/errors/extra_key_for_pass_error.rb +0 -3
  18. data/lib/decouplio/errors/fail_can_not_be_first_step_error.rb +0 -3
  19. data/lib/decouplio/errors/fail_controversial_keys_error.rb +0 -3
  20. data/lib/decouplio/errors/fail_finish_him_error.rb +0 -3
  21. data/lib/decouplio/errors/invalid_error_class_error.rb +0 -3
  22. data/lib/decouplio/errors/invalid_options_for_resq_step.rb +18 -0
  23. data/lib/decouplio/errors/invalid_wrap_name_error.rb +0 -3
  24. data/lib/decouplio/errors/logic_is_not_defined_error.rb +0 -3
  25. data/lib/decouplio/errors/logic_redefinition_error.rb +0 -3
  26. data/lib/decouplio/errors/octo_block_is_not_defined_error.rb +0 -3
  27. data/lib/decouplio/errors/octo_case_is_not_defined_error.rb +19 -0
  28. data/lib/decouplio/errors/octo_controversial_keys_error.rb +0 -3
  29. data/lib/decouplio/errors/{error_store_error.rb → octo_finish_him_is_not_allowed_error.rb} +2 -4
  30. data/lib/decouplio/errors/options_validation_error.rb +13 -1
  31. data/lib/decouplio/errors/palp_block_is_not_defined_error.rb +0 -3
  32. data/lib/decouplio/errors/palp_validation_error.rb +0 -3
  33. data/lib/decouplio/errors/pass_controversial_keys_error.rb +0 -3
  34. data/lib/decouplio/errors/pass_finish_him_error.rb +0 -3
  35. data/lib/decouplio/errors/required_options_is_missing_for_octo_error.rb +0 -3
  36. data/lib/decouplio/errors/resq_definition_error.rb +0 -3
  37. data/lib/decouplio/errors/resq_error_class_error.rb +0 -3
  38. data/lib/decouplio/errors/resq_handler_method_error.rb +0 -3
  39. data/lib/decouplio/errors/step_controversial_keys_error.rb +0 -3
  40. data/lib/decouplio/errors/step_definition_error.rb +17 -0
  41. data/lib/decouplio/errors/step_finish_him_error.rb +0 -3
  42. data/lib/decouplio/errors/step_is_not_defined_for_fail_error.rb +0 -3
  43. data/lib/decouplio/errors/step_is_not_defined_for_pass_error.rb +0 -4
  44. data/lib/decouplio/errors/step_is_not_defined_for_step_error.rb +0 -4
  45. data/lib/decouplio/errors/step_is_not_defined_for_wrap_error.rb +0 -3
  46. data/lib/decouplio/errors/step_name_error.rb +0 -3
  47. data/lib/decouplio/errors/wrap_block_is_not_defined_error.rb +0 -3
  48. data/lib/decouplio/errors/wrap_controversial_keys_error.rb +0 -3
  49. data/lib/decouplio/errors/wrap_finish_him_error.rb +0 -3
  50. data/lib/decouplio/errors/wrap_klass_method_error.rb +0 -3
  51. data/lib/decouplio/graph.rb +9 -0
  52. data/lib/decouplio/logic_dsl.rb +337 -107
  53. data/lib/decouplio/new_flow.rb +283 -0
  54. data/lib/decouplio/octo_hash_case.rb +20 -5
  55. data/lib/decouplio/octo_options_validator.rb +10 -64
  56. data/lib/decouplio/step_validator.rb +200 -0
  57. data/lib/decouplio/steps/base_condition.rb +21 -0
  58. data/lib/decouplio/steps/base_if_condition.rb +11 -0
  59. data/lib/decouplio/steps/base_inner_action.rb +42 -0
  60. data/lib/decouplio/steps/base_octo.rb +26 -0
  61. data/lib/decouplio/steps/base_resq.rb +25 -28
  62. data/lib/decouplio/steps/base_resq_with_mapping.rb +34 -0
  63. data/lib/decouplio/steps/base_service_step.rb +39 -0
  64. data/lib/decouplio/steps/base_step.rb +33 -6
  65. data/lib/decouplio/steps/base_unless_condition.rb +11 -0
  66. data/lib/decouplio/steps/base_wrap.rb +27 -0
  67. data/lib/decouplio/steps/fail.rb +1 -28
  68. data/lib/decouplio/steps/if_condition_fail.rb +1 -21
  69. data/lib/decouplio/steps/if_condition_pass.rb +1 -19
  70. data/lib/decouplio/steps/inner_action_fail.rb +1 -33
  71. data/lib/decouplio/steps/inner_action_pass.rb +12 -30
  72. data/lib/decouplio/steps/inner_action_step.rb +1 -33
  73. data/lib/decouplio/steps/octo_by_key.rb +31 -0
  74. data/lib/decouplio/steps/octo_by_method.rb +31 -0
  75. data/lib/decouplio/steps/pass.rb +5 -26
  76. data/lib/decouplio/steps/resq_fail.rb +0 -2
  77. data/lib/decouplio/steps/resq_pass.rb +1 -3
  78. data/lib/decouplio/steps/resq_with_mapping_fail.rb +8 -0
  79. data/lib/decouplio/steps/resq_with_mapping_pass.rb +8 -0
  80. data/lib/decouplio/steps/service_as_fail.rb +8 -0
  81. data/lib/decouplio/steps/service_as_pass.rb +16 -0
  82. data/lib/decouplio/steps/service_as_step.rb +8 -0
  83. data/lib/decouplio/steps/step.rb +0 -24
  84. data/lib/decouplio/steps/unless_condition_fail.rb +1 -21
  85. data/lib/decouplio/steps/unless_condition_pass.rb +1 -19
  86. data/lib/decouplio/steps/wrap.rb +25 -37
  87. data/lib/decouplio/steps/wrap_with_class.rb +43 -0
  88. data/lib/decouplio/steps/wrap_with_class_method.rb +45 -0
  89. data/lib/decouplio/utils/prepare_resq_mappings.rb +17 -0
  90. data/lib/decouplio/version.rb +1 -1
  91. data/lib/decouplio.rb +93 -0
  92. metadata +32 -55
  93. data/.circleci/config.yml +0 -63
  94. data/.dockerignore +0 -12
  95. data/.gitignore +0 -13
  96. data/.rspec +0 -3
  97. data/.rubocop.yml +0 -116
  98. data/.rubocop_todo.yml +0 -147
  99. data/.ruby-version +0 -1
  100. data/.vscode/settings.json +0 -3
  101. data/Dockerfile +0 -12
  102. data/Gemfile +0 -8
  103. data/benchmarks/.ruby-version +0 -1
  104. data/benchmarks/Dockerfile +0 -12
  105. data/benchmarks/Gemfile +0 -12
  106. data/benchmarks/multi_step_benchmark.rb +0 -336
  107. data/benchmarks/single_step_benchmark.rb +0 -159
  108. data/bin/console +0 -15
  109. data/bin/setup +0 -8
  110. data/docker-compose.yml +0 -29
  111. data/lib/decouplio/composer.rb +0 -691
  112. data/lib/decouplio/const/colors.rb +0 -25
  113. data/lib/decouplio/const/doby_aide_options.rb +0 -16
  114. data/lib/decouplio/const/results.rb +0 -15
  115. data/lib/decouplio/const/step_options.rb +0 -16
  116. data/lib/decouplio/const/validations/aide.rb +0 -38
  117. data/lib/decouplio/const/validations/doby.rb +0 -36
  118. data/lib/decouplio/default_error_handler.rb +0 -39
  119. data/lib/decouplio/errors/aide_can_not_be_first_step_error.rb +0 -18
  120. data/lib/decouplio/errors/aide_controversial_keys_error.rb +0 -26
  121. data/lib/decouplio/errors/aide_finish_him_error.rb +0 -26
  122. data/lib/decouplio/errors/doby_controversial_keys_error.rb +0 -26
  123. data/lib/decouplio/errors/doby_finish_him_error.rb +0 -26
  124. data/lib/decouplio/errors/extra_key_for_fail_error.rb +0 -26
  125. data/lib/decouplio/errors/extra_key_for_octo_error.rb +0 -26
  126. data/lib/decouplio/errors/extra_key_for_resq_error.rb +0 -29
  127. data/lib/decouplio/errors/extra_key_for_step_error.rb +0 -23
  128. data/lib/decouplio/errors/extra_key_for_wrap_error.rb +0 -23
  129. data/lib/decouplio/errors/palp_is_not_defined_error.rb +0 -26
  130. data/lib/decouplio/errors/step_is_not_defined_for_aide_error.rb +0 -26
  131. data/lib/decouplio/errors/step_is_not_defined_for_doby_error.rb +0 -27
  132. data/lib/decouplio/flow.rb +0 -17
  133. data/lib/decouplio/options_validator.rb +0 -716
  134. data/lib/decouplio/processor.rb +0 -20
  135. data/lib/decouplio/steps/aide.rb +0 -37
  136. data/lib/decouplio/steps/doby.rb +0 -37
  137. data/lib/decouplio/steps/octo.rb +0 -27
  138. data/lib/decouplio/steps/service_fail.rb +0 -41
  139. data/lib/decouplio/steps/service_pass.rb +0 -41
  140. data/lib/decouplio/steps/service_step.rb +0 -41
  141. data/lib/decouplio/steps/shared/fail_resolver.rb +0 -40
  142. data/lib/decouplio/steps/shared/step_resolver.rb +0 -43
  143. data/lib/decouplio/validators/condition.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b142d4fd94ec689714987c59946aab6c14d7d857a4aa11e55c624c5f7975e622
4
- data.tar.gz: fc96e94c4d3358fda4eca1e80bfaeb7021a444bc5d9d1ff41b2b1b5f2283dbda
3
+ metadata.gz: 3f372ffe111d0d286f3c1898645caeb50be16ef3131b6749a28d0d8b43629392
4
+ data.tar.gz: 717430fa9380f5f4fcd8eb399b1399b47355bdc05860d253fce1b9ea105c5764
5
5
  SHA512:
6
- metadata.gz: 1fc456438780d78c73280883a8b780148568e0a3ac5a2d21162575c73be6a7401378d6a37367aa31f28878226a274a27803da6ecf2b8b2e976b04b536829f3c1
7
- data.tar.gz: 760d3c4a61d0985b4b49633f3c010107a5833e241ac133613439f9f9f031417e022af02ccef23e77681db22d6fdaeb3d186a18d46237bcd216a22dc0388f77eb
6
+ metadata.gz: 011e581a2643ebfd78a93de90dd5e425cf062fe555d8c0f115a12066cf2a612118ba41e9975f97c7decced6bf1d44f805062b1170b558c9e32fd16bdd818c47e
7
+ data.tar.gz: 072a87e219c5a90ab64c3aebcd5bb61459108f3cdc3c69e1f0d28a772860cba47a9cd0a36912b8e412bda6d45e9b264aac8bc6ef962fcdb2fd0856e4c4e3f649
data/README.md CHANGED
@@ -2,37 +2,335 @@
2
2
 
3
3
  Decouplio is a zero dependency, thread safe and framework agnostic gem designed to encapsulate application business logic. It's reverse engineered through TDD and inspired by such frameworks and gems like Trailblazer, Interactor.
4
4
 
5
- ## Installation
5
+ # How to install?
6
6
 
7
- Add this line to your application's Gemfile:
7
+ ```
8
+ gem install decouplio --pre
9
+ ```
10
+
11
+ ### Gemfile
12
+ ```
13
+ gem 'decouplio', '~> 1.0.0rc'
14
+ ```
15
+
16
+ # Compatibility
17
+ Ruby:
18
+ - 2.7
19
+ - 3.0
20
+
21
+ # Quick reference to docs
22
+
23
+ |Options/Step type|[**step**](https://differencialx.github.io/decouplio.github.io/step/)|[**fail**](https://differencialx.github.io/decouplio.github.io/fail/)|[**pass**](https://differencialx.github.io/decouplio.github.io/pass/)|[**wrap**](https://differencialx.github.io/decouplio.github.io/wrap/)|[**octo**](https://differencialx.github.io/decouplio.github.io/octo/)|[**resq**](https://differencialx.github.io/decouplio.github.io/resq/)|
24
+ |:-|:-:|:-:|:-:|:-:|:-:|:-:|
25
+ ||[**on_success**](https://differencialx.github.io/decouplio.github.io/on_success/)|[**on_success**](https://differencialx.github.io/decouplio.github.io/on_success/)||[**on_success**](https://differencialx.github.io/decouplio.github.io/on_success/)|[**on_success**](https://differencialx.github.io/decouplio.github.io/on_success/)|-|
26
+ ||[**on_failure**](https://differencialx.github.io/decouplio.github.io/on_failure/)|[**on_failure**](https://differencialx.github.io/decouplio.github.io/on_failure/)||[**on_failure**](https://differencialx.github.io/decouplio.github.io/on_failure/)|[**on_failure**](https://differencialx.github.io/decouplio.github.io/on_failure/)|-|
27
+ ||[**on_error**](https://differencialx.github.io/decouplio.github.io/on_error/)|[**on_error**](https://differencialx.github.io/decouplio.github.io/on_error/)|[**on_error**](https://differencialx.github.io/decouplio.github.io/on_error/)|[**on_error**](https://differencialx.github.io/decouplio.github.io/on_error/)|[**on_error**](https://differencialx.github.io/decouplio.github.io/on_error/)|-|
28
+ ||[**finish_him**](https://differencialx.github.io/decouplio.github.io/finish_him/)|[**finish_him**](https://differencialx.github.io/decouplio.github.io/finish_him/)|[**finish_him**](https://differencialx.github.io/decouplio.github.io/finish_him/)|[**finish_him**](https://differencialx.github.io/decouplio.github.io/finish_him/)||-|
29
+ ||[**if/unless**](https://differencialx.github.io/decouplio.github.io/if_unless/)|[**if/unless**](https://differencialx.github.io/decouplio.github.io/if_unless/)|[**if/unless**](https://differencialx.github.io/decouplio.github.io/if_unless/)|[**if/unless**](https://differencialx.github.io/decouplio.github.io/if_unless/)|[**if/unless**](https://differencialx.github.io/decouplio.github.io/if_unless/)|-|
30
+
31
+ #### [Documentation is HERE](https://differencialx.github.io/decouplio.github.io/)
32
+
33
+ # Quick start
34
+ ## What should you know before start?
35
+
36
+ ### Action
37
+
38
+ Action is a class which encapsulates business logic. To create one just create a class and inherit it from `Decouplio::Action` class
8
39
 
9
40
  ```ruby
10
- gem 'decouplio'
41
+ require 'decouplio'
42
+
43
+ class MyAction < Decouplio::Action
44
+ end
11
45
  ```
12
46
 
13
- And then execute:
47
+ ### Logic block
14
48
 
15
- $ bundle
49
+ Block inside `Action` which contains definition of business logic.
16
50
 
17
- Or install it by yourself:
51
+ ```ruby
52
+ require 'decouplio'
18
53
 
19
- $ gem install decouplio
54
+ class MyAction < Decouplio::Action
55
+ logic do
56
+ # logic block
57
+ end
58
+ end
59
+ ```
20
60
 
21
- ## Currently decouplio is in alpha testing, more features will come soon.
61
+ ### Step
22
62
 
23
- ### Compatibility
24
- Ruby:
25
- - 2.7
26
- - 3.0
63
+ Step is an atomic part of business logic and it defines inside `Logic block`.
64
+
65
+ ```ruby
66
+ require 'decouplio'
67
+
68
+ class MyAction < Decouplio::Action
69
+ logic do
70
+ step :hello_world
71
+ end
72
+
73
+ def hello_world
74
+ ctx[:result] = 'Hello world'
75
+ end
76
+ end
77
+
78
+ MyAction.call[:result] # => Hello world
79
+ ```
80
+
81
+ |Step types|
82
+ |:-:|
83
+ |[**step**](https://differencialx.github.io/decouplio.github.io/step/)|
84
+ |[**fail**](https://differencialx.github.io/decouplio.github.io/fail/)|
85
+ |[**pass**](https://differencialx.github.io/decouplio.github.io/pass/)|
86
+ |[**wrap**](https://differencialx.github.io/decouplio.github.io/wrap/)|
87
+ |[**octo**](https://differencialx.github.io/decouplio.github.io/octo/)|
88
+ |[**resq**](https://differencialx.github.io/decouplio.github.io/resq/)|
89
+
90
+
91
+ ### Context
92
+ Action context is an object which is used to share data between steps. It's accessible only inside step.
93
+ - To access the action context inside step you need to call `ctx` method
94
+ - `ctx` behaves like a `Hash`.
95
+ - To assign some value to `ctx` just do `ctx[:some_key] = 'some value'`
96
+ - To access `ctx` value use `ctx[:some_value]` or use a shortcut `c.some_value`
97
+
98
+ NOTE: you **can't** assign context value using `c.<some key>` shortcut.
99
+
100
+ ```ruby
101
+ require 'decouplio'
102
+
103
+ class CtxIntroduction < Decouplio::Action
104
+ logic do
105
+ step :calculate_result
106
+ end
107
+
108
+ def calculate_result
109
+ ctx[:result] = c.one + c.two
110
+ # OR
111
+ # c[:result] = c[:one] + c[:two]
112
+ #OR
113
+ # ctx[:result] = ctx[:one] + ctx[:two]
114
+ end
115
+ end
116
+
117
+ action_result = CtxIntroduction.call(one: 1, two: 2)
118
+
119
+ action_result[:result] # => 3
120
+ ```
121
+
122
+ ### Success/Failure track
123
+ Execution flow of action is changing depending on step result.
124
+ - If step returns truthy value(**not** `nil|false`), when next `success` track step will be executed.
125
+ - If step returns falsy value(`nil|false`), when next `failure` track step will be executed.
126
+
127
+ |Success track|Failure track|
128
+ |-|-|
129
+ |[**step**](https://differencialx.github.io/decouplio.github.io/step/)|[**fail**](https://differencialx.github.io/decouplio.github.io/fail/)|
130
+ |[**pass**](https://differencialx.github.io/decouplio.github.io/pass/)||
131
+ |[**wrap**](https://differencialx.github.io/decouplio.github.io/wrap/)||
132
+ |[**octo**](https://differencialx.github.io/decouplio.github.io/octo/)||
133
+
134
+ ```ruby
135
+ require 'decouplio'
136
+
137
+ class Divider < Decouplio::Action
138
+ logic do
139
+ step :validate_divider
140
+ step :divide
141
+ fail :failure_message
142
+ end
143
+
144
+ def validate_divider
145
+ !ctx[:divider].zero?
146
+ end
147
+
148
+ def divide
149
+ ctx[:result] = c.number / c.divider
150
+ end
27
151
 
28
- ### [Documentation is HERE](https://differencialx.github.io/decouplio.github.io/)
152
+ def failure_message
153
+ ctx[:error_message] = 'Division by zero is not allowed'
154
+ end
155
+ end
156
+
157
+ divider_success = Divider.call(number: 4, divider: 2)
158
+ divider_success.success? # => true
159
+ divider_success.failure? # => false
160
+ divider_success[:result] # => 2
161
+ divider_success[:error_message] # => nil
162
+ divider_success.railway_flow # => [:validate_divider, :divide]
163
+ puts divider_success # =>
164
+ # Result: success
165
+
166
+ # RailwayFlow:
167
+ # validate_divider -> divide
168
+
169
+ # Context:
170
+ # :number => 4
171
+ # :divider => 2
172
+ # :result => 2
173
+
174
+ # Status: NONE
175
+
176
+ # Errors:
177
+ # NONE
178
+
179
+ divider_failure = Divider.call(number: 4, divider: 0)
180
+ divider_failure.success? #=> false
181
+ divider_failure.failure? #=> true
182
+ divider_failure[:result] # => nil
183
+ divider_failure[:error_message] # => 'Division by zero is not allowed'
184
+ divider_failure.railway_flow# => [:validate_divider, :failure_message]
185
+ divider_failure # =>
186
+ # Result: failure
187
+
188
+ # RailwayFlow:
189
+ # validate_divider -> failure_message
190
+
191
+ # Context:
192
+ # :number => 4
193
+ # :divider => 0
194
+ # :error_message => "Division by zero is not allowed"
195
+
196
+ # Status: NONE
197
+
198
+ # Errors:
199
+ # NONE
200
+
201
+ ```
202
+
203
+ ### Railway flow
204
+
205
+ During execution `Decouplio` is recording executed steps, so you check which steps were executed. It becomes in handy during debugging and writing test.
206
+
207
+ ```ruby
208
+ class RailwayAction < Decouplio::Action
209
+ logic do
210
+ step :step1
211
+ step :step2
212
+ step :step3
213
+ end
214
+
215
+ def step1
216
+ ctx[:step1] = 'Step1'
217
+ end
218
+
219
+ def step2
220
+ ctx[:step2] = 'Step2'
221
+ end
222
+
223
+ def step3
224
+ ctx[:step3] = 'Step3'
225
+ end
226
+ end
227
+
228
+ railway_action = RailwayAction.call
229
+ railway_action.railway_flow.inspect # => [:step1, :step2, :step3]
230
+
231
+ railway_action # =>
232
+ # Result: success
233
+
234
+ # RailwayFlow:
235
+ # step1 -> step2 -> step3
236
+
237
+ # Context:
238
+ # :step1 => "Step1"
239
+ # :step2 => "Step2"
240
+ # :step3 => "Step3"
241
+
242
+ # Status: NONE
243
+
244
+ # Errors:
245
+ # NONE
246
+ ```
247
+
248
+ ### Meta Store
249
+
250
+ Generally `metastore` is a PORO, which is accessible inside steps by calling `meta_store` method or it's alias `ms`. It was created to help developers to standardize things and keep meta info about action, because sometimes `success?` or `failure?` is not enough to make a decision about what to do next. I defined default metastore class which can manage custom action `status` and standardizes the way how error messages should be added.
251
+
252
+ That's how default `metastore` class looks like
253
+ ```ruby
254
+ # frozen_string_literal: true
255
+ module Decouplio
256
+ class DefaultMetaStore
257
+ attr_accessor :status, :errors
258
+
259
+ def initialize
260
+ @errors = {}
261
+ @status = nil
262
+ end
263
+
264
+ def add_error(key, messages)
265
+ @errors.store(
266
+ key,
267
+ (@errors[key] || []) + [messages].flatten
268
+ )
269
+ end
270
+
271
+ # This method is used to print metastore status to console
272
+ # when you checking action output
273
+ def to_s
274
+ <<~METASTORE
275
+ Status: #{@status || 'NONE'}
276
+
277
+ Errors:
278
+ #{errors_string}
279
+ METASTORE
280
+ end
281
+
282
+ private
283
+
284
+ def errors_string
285
+ return 'NONE' if @errors.empty?
286
+
287
+ @errors.map do |k, v|
288
+ "#{k.inspect} => #{v.inspect}"
289
+ end.join("\n ")
290
+ end
291
+ end
292
+ end
293
+ ```
294
+ So it's allows you do this
295
+ ```ruby
296
+ class MetaStoreAction < Decouplio::Action
297
+ logic do
298
+ step :always_fails
299
+ fail :handle_fail
300
+ end
301
+
302
+ # Decouplio has to constants which are accessible inside steps
303
+ # PASS = true
304
+ # FAIL = false
305
+ # You can use then to force step to fail or pass instead of `true` of `false`
306
+
307
+ def always_fails
308
+ FAIL
309
+ end
310
+
311
+ def handle_fail
312
+ ms.status = :failed_and_i_duno_why
313
+ ms.add_error(:something_went_wrong, 'Something went wrong')
314
+ ms.add_error(:something_went_wrong, 'And I duno why :(')
315
+ end
316
+ end
317
+
318
+ MetaStoreAction.call #=>
319
+ # Result: failure
320
+ # RailwayFlow:
321
+ # always_fails -> handle_fail
322
+ # Context:
323
+ # Empty
324
+ # Status: :failed_and_i_duno_why
325
+ # Errors:
326
+ # :something_went_wrong => ["Something went wrong", "And I duno why :("]
327
+ ```
328
+ **NOTE: you can always define your own metastore class accordingly to your needs. [DOCS ARE HERE](https://differencialx.github.io/decouplio.github.io/meta_store/)**
29
329
 
30
330
  ## Contributing
31
331
 
32
332
  Bug reports and pull requests are welcome on GitHub at https://github.com/differencialx/decouplio/issues.
33
333
 
34
- More detailed description for contribution process will be added later.
35
-
36
334
  ## License
37
335
 
38
336
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/decouplio.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  if spec.respond_to?(:metadata)
19
19
  spec.metadata['homepage_uri'] = spec.homepage
20
- spec.metadata['source_code_uri'] = 'https://github.com/differencialx/decouplio/blob/master/docs'
20
+ spec.metadata['source_code_uri'] = 'https://github.com/differencialx/decouplio/'
21
21
  spec.metadata['changelog_uri'] = 'https://github.com/differencialx/decouplio/blob/master/docs/CHANGELOG.md'
22
22
  else
23
23
  raise 'RubyGems 2.0 or newer is required to protect against ' \
@@ -25,8 +25,14 @@ Gem::Specification.new do |spec|
25
25
  end
26
26
 
27
27
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ `git ls-files -z`.split("\x0").select { |f| f.match(%r{^(lib)/|.gemspec|LICENSE|Rakefile|README}) }
29
29
  end
30
+
31
+ # spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
+ # `git ls-files -z`.split("\x0").reject do |f|
33
+ # f.match(%r{(test|spec|features|benchmarks|bin|.rubocop|.gitignore|Dockerfile|.ruby-version|docker|.vscode|.circle)(\/)*})
34
+ # end
35
+ # end
30
36
  spec.bindir = 'exe'
31
37
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
38
  spec.require_paths = ['lib']
@@ -1,83 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
- require_relative 'flow'
5
- require_relative 'processor'
6
- require_relative 'default_error_handler'
7
- require_relative 'errors/logic_redefinition_error'
8
- require_relative 'errors/logic_is_not_defined_error'
9
- require_relative 'errors/error_store_error'
10
- require_relative 'errors/execution_error'
11
- require_relative 'const/results'
12
-
13
3
  module Decouplio
14
4
  class Action
15
5
  extend Forwardable
16
- def_delegators :@error_store, :errors, :add_error, :error_status
17
- attr_reader :railway_flow, :ctx, :error_store
6
+ def_delegator :@ctx, :[]
7
+
8
+ attr_reader :railway_flow, :ctx, :meta_store
9
+ attr_writer :success
10
+
11
+ alias ms meta_store
12
+ alias c ctx
13
+
14
+ PASS = true
15
+ FAIL = false
18
16
 
19
17
  def initialize(
20
- parent_railway_flow: nil, parent_ctx: nil, error_store:, **params
18
+ meta_store, parent_railway_flow, ctx
21
19
  )
22
- @error_store = error_store
23
- @ctx = parent_ctx || params
24
- @railway_flow = parent_railway_flow || []
25
- @failure = false
26
- end
27
-
28
- def [](key)
29
- @ctx[key]
20
+ @meta_store = meta_store
21
+ @ctx = ctx
22
+ @railway_flow = parent_railway_flow
23
+ @success = true
30
24
  end
31
25
 
32
26
  def success?
33
- !@failure
27
+ @success
34
28
  end
35
29
 
36
30
  def failure?
37
- @failure
38
- end
39
-
40
- def fail_action
41
- @failure = true
42
- end
43
-
44
- def pass_action
45
- @failure = false
46
- end
47
-
48
- def previous_step
49
- railway_flow[PREVIOUS_STEP_INDEX]
50
- end
51
-
52
- def append_railway_flow(stp)
53
- railway_flow << stp
31
+ !@success
54
32
  end
55
33
 
56
34
  def inspect
57
- <<~INSPECT
58
- Result: #{success? ? 'success' : 'failure'}
59
-
60
- Railway Flow:
61
- #{railway_flow.join(' -> ')}
62
-
63
- Context:
64
- #{ctx.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join("\n ")}
65
-
66
- Errors:
67
- #{
68
- if errors.is_a?(Hash)
69
- if errors.empty?
70
- 'None'
71
- else
72
- errors.map do |k, v|
73
- "#{k.inspect} => #{v.inspect}"
74
- end.join("\n ")
75
- end
76
- else
77
- errors
78
- end
79
- }
80
- INSPECT
35
+ Decouplio::ActionStatePrinter.call(self)
81
36
  end
82
37
 
83
38
  def to_s
@@ -85,16 +40,21 @@ module Decouplio
85
40
  end
86
41
 
87
42
  class << self
88
- attr_accessor :error_store
43
+ attr_accessor :meta_store
89
44
 
90
- def error_store_class(klass)
91
- self.error_store = klass
45
+ def meta_store_class(klass)
46
+ self.meta_store = klass
92
47
  end
93
48
 
94
- def call(**params)
95
- instance = new(error_store: error_store.new, **params)
96
- Decouplio::Processor.call(instance: instance, **@flow)
97
- # TODO: process block with after actions
49
+ def call(_parent_meta_store: nil, _parent_railway_flow: nil, _parent_ctx: nil, **params)
50
+ instance = new(
51
+ _parent_meta_store || meta_store.new,
52
+ _parent_railway_flow || [],
53
+ _parent_ctx || Ctx[params]
54
+ )
55
+ next_step = @first_step
56
+
57
+ next_step = next_step.process(instance) while next_step
98
58
  instance
99
59
  end
100
60
 
@@ -112,19 +72,19 @@ module Decouplio
112
72
  private
113
73
 
114
74
  def inherited(child_class)
115
- child_class.error_store = error_store || Decouplio::DefaultErrorHandler
75
+ child_class.meta_store = meta_store || Decouplio::DefaultMetaStore
116
76
  end
117
77
 
118
78
  def logic(&block)
119
- if @flow && !@flow[:first_step].nil?
79
+ unless @first_step.nil?
120
80
  raise Decouplio::Errors::LogicRedefinitionError.new(
121
81
  errored_option: to_s
122
82
  )
123
83
  end
124
84
  if block_given?
125
- @flow = Decouplio::Flow.call(logic: block, action_class: self)
85
+ @first_step = Decouplio::NewFlow.call(block)
126
86
 
127
- if @flow && @flow[:first_step].nil?
87
+ if @first_step.nil?
128
88
  raise Decouplio::Errors::LogicIsNotDefinedError.new(
129
89
  errored_option: to_s
130
90
  )
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decouplio
4
+ class ActionStatePrinter
5
+ def self.call(action)
6
+ <<~INSPECT
7
+ Result: #{result(action)}
8
+ RailwayFlow:
9
+ #{railway_flow(action)}
10
+ Context:
11
+ #{action_context(action)}
12
+ #{meta_store(action)}
13
+ INSPECT
14
+ end
15
+
16
+ def self.result(action)
17
+ action.success? ? 'success' : 'failure'
18
+ end
19
+
20
+ def self.railway_flow(action)
21
+ action.railway_flow.join(' -> ')
22
+ end
23
+
24
+ def self.action_context(action)
25
+ return 'Empty' if action.ctx.empty?
26
+
27
+ action.ctx.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join("\n ")
28
+ end
29
+
30
+ def self.meta_store(action)
31
+ action.ms.to_s
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decouplio
4
+ module Const
5
+ module Flows
6
+ MAIN_FLOW = [
7
+ Decouplio::Steps::Step,
8
+ Decouplio::Steps::Fail,
9
+ Decouplio::Steps::Pass,
10
+ Decouplio::Steps::InnerActionStep,
11
+ Decouplio::Steps::InnerActionFail,
12
+ Decouplio::Steps::InnerActionPass,
13
+ Decouplio::Steps::OctoByKey,
14
+ Decouplio::Steps::OctoByMethod,
15
+ Decouplio::Steps::ServiceAsStep,
16
+ Decouplio::Steps::ServiceAsFail,
17
+ Decouplio::Steps::ServiceAsPass,
18
+ Decouplio::Steps::ServiceAsPass,
19
+ Decouplio::Steps::WrapWithClass,
20
+ Decouplio::Steps::WrapWithClassMethod,
21
+ Decouplio::Steps::Wrap
22
+ ].freeze
23
+ PASS_FLOW = [
24
+ Decouplio::Steps::Step,
25
+ Decouplio::Steps::Pass,
26
+ Decouplio::Steps::InnerActionStep,
27
+ Decouplio::Steps::InnerActionPass,
28
+ Decouplio::Steps::ServiceAsStep,
29
+ Decouplio::Steps::ServiceAsPass,
30
+ Decouplio::Steps::ServiceAsPass,
31
+ Decouplio::Steps::OctoByKey,
32
+ Decouplio::Steps::OctoByMethod,
33
+ Decouplio::Steps::WrapWithClass,
34
+ Decouplio::Steps::WrapWithClassMethod,
35
+ Decouplio::Steps::Wrap
36
+ ].freeze
37
+ FAIL_FLOW = [
38
+ Decouplio::Steps::Fail,
39
+ Decouplio::Steps::InnerActionFail,
40
+ Decouplio::Steps::ServiceAsFail
41
+ ].freeze
42
+ RESQ_CLASSES = [
43
+ Decouplio::Steps::ResqPass,
44
+ Decouplio::Steps::ResqFail,
45
+ Decouplio::Steps::ResqWithMappingPass,
46
+ Decouplio::Steps::ResqWithMappingFail
47
+ ].freeze
48
+ WRAP_CLASSES = [
49
+ Decouplio::Steps::WrapWithClass,
50
+ Decouplio::Steps::WrapWithClassMethod,
51
+ Decouplio::Steps::Wrap
52
+ ].freeze
53
+ OCTO_CLASSES = [
54
+ Decouplio::Steps::OctoByMethod,
55
+ Decouplio::Steps::OctoByKey
56
+ ].freeze
57
+ end
58
+ end
59
+ end
@@ -1,21 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'results'
4
-
5
3
  module Decouplio
6
4
  module Const
7
5
  module ReservedMethods
8
- NAMES = [
9
- :[],
10
- :success?,
11
- :failure?,
12
- :fail_action,
13
- :pass_action,
14
- :append_railway_flow,
15
- :inspect,
16
- :to_s,
17
- Decouplio::Const::Results::STEP_PASS,
18
- Decouplio::Const::Results::STEP_FAIL
6
+ NAMES = %i[
7
+ \[\]
8
+ inspect
9
+ to_s
10
+ PASS
11
+ FAIL
19
12
  ].freeze
20
13
  end
21
14
  end