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.
- checksums.yaml +4 -4
- data/README.md +313 -15
- data/decouplio.gemspec +8 -2
- data/lib/decouplio/action.rb +34 -74
- data/lib/decouplio/action_state_printer.rb +34 -0
- data/lib/decouplio/const/flows.rb +59 -0
- data/lib/decouplio/const/reserved_methods.rb +6 -13
- data/lib/decouplio/const/types.rb +1 -165
- data/lib/decouplio/const/validations/common.rb +10 -0
- data/lib/decouplio/const/validations/octo.rb +16 -0
- data/lib/decouplio/const/validations/resq.rb +12 -1
- data/lib/decouplio/ctx.rb +13 -0
- data/lib/decouplio/default_meta_store.rb +37 -0
- data/lib/decouplio/errors/action_class_error.rb +0 -3
- data/lib/decouplio/errors/base_error.rb +0 -3
- data/lib/decouplio/errors/execution_error.rb +0 -2
- data/lib/decouplio/errors/extra_key_for_pass_error.rb +0 -3
- data/lib/decouplio/errors/fail_can_not_be_first_step_error.rb +0 -3
- data/lib/decouplio/errors/fail_controversial_keys_error.rb +0 -3
- data/lib/decouplio/errors/fail_finish_him_error.rb +0 -3
- data/lib/decouplio/errors/invalid_error_class_error.rb +0 -3
- data/lib/decouplio/errors/invalid_options_for_resq_step.rb +18 -0
- data/lib/decouplio/errors/invalid_wrap_name_error.rb +0 -3
- data/lib/decouplio/errors/logic_is_not_defined_error.rb +0 -3
- data/lib/decouplio/errors/logic_redefinition_error.rb +0 -3
- data/lib/decouplio/errors/octo_block_is_not_defined_error.rb +0 -3
- data/lib/decouplio/errors/octo_case_is_not_defined_error.rb +19 -0
- data/lib/decouplio/errors/octo_controversial_keys_error.rb +0 -3
- data/lib/decouplio/errors/{error_store_error.rb → octo_finish_him_is_not_allowed_error.rb} +2 -4
- data/lib/decouplio/errors/options_validation_error.rb +13 -1
- data/lib/decouplio/errors/palp_block_is_not_defined_error.rb +0 -3
- data/lib/decouplio/errors/palp_validation_error.rb +0 -3
- data/lib/decouplio/errors/pass_controversial_keys_error.rb +0 -3
- data/lib/decouplio/errors/pass_finish_him_error.rb +0 -3
- data/lib/decouplio/errors/required_options_is_missing_for_octo_error.rb +0 -3
- data/lib/decouplio/errors/resq_definition_error.rb +0 -3
- data/lib/decouplio/errors/resq_error_class_error.rb +0 -3
- data/lib/decouplio/errors/resq_handler_method_error.rb +0 -3
- data/lib/decouplio/errors/step_controversial_keys_error.rb +0 -3
- data/lib/decouplio/errors/step_definition_error.rb +17 -0
- data/lib/decouplio/errors/step_finish_him_error.rb +0 -3
- data/lib/decouplio/errors/step_is_not_defined_for_fail_error.rb +0 -3
- data/lib/decouplio/errors/step_is_not_defined_for_pass_error.rb +0 -4
- data/lib/decouplio/errors/step_is_not_defined_for_step_error.rb +0 -4
- data/lib/decouplio/errors/step_is_not_defined_for_wrap_error.rb +0 -3
- data/lib/decouplio/errors/step_name_error.rb +0 -3
- data/lib/decouplio/errors/wrap_block_is_not_defined_error.rb +0 -3
- data/lib/decouplio/errors/wrap_controversial_keys_error.rb +0 -3
- data/lib/decouplio/errors/wrap_finish_him_error.rb +0 -3
- data/lib/decouplio/errors/wrap_klass_method_error.rb +0 -3
- data/lib/decouplio/graph.rb +9 -0
- data/lib/decouplio/logic_dsl.rb +337 -107
- data/lib/decouplio/new_flow.rb +283 -0
- data/lib/decouplio/octo_hash_case.rb +20 -5
- data/lib/decouplio/octo_options_validator.rb +10 -64
- data/lib/decouplio/step_validator.rb +200 -0
- data/lib/decouplio/steps/base_condition.rb +21 -0
- data/lib/decouplio/steps/base_if_condition.rb +11 -0
- data/lib/decouplio/steps/base_inner_action.rb +42 -0
- data/lib/decouplio/steps/base_octo.rb +26 -0
- data/lib/decouplio/steps/base_resq.rb +25 -28
- data/lib/decouplio/steps/base_resq_with_mapping.rb +34 -0
- data/lib/decouplio/steps/base_service_step.rb +39 -0
- data/lib/decouplio/steps/base_step.rb +33 -6
- data/lib/decouplio/steps/base_unless_condition.rb +11 -0
- data/lib/decouplio/steps/base_wrap.rb +27 -0
- data/lib/decouplio/steps/fail.rb +1 -28
- data/lib/decouplio/steps/if_condition_fail.rb +1 -21
- data/lib/decouplio/steps/if_condition_pass.rb +1 -19
- data/lib/decouplio/steps/inner_action_fail.rb +1 -33
- data/lib/decouplio/steps/inner_action_pass.rb +12 -30
- data/lib/decouplio/steps/inner_action_step.rb +1 -33
- data/lib/decouplio/steps/octo_by_key.rb +31 -0
- data/lib/decouplio/steps/octo_by_method.rb +31 -0
- data/lib/decouplio/steps/pass.rb +5 -26
- data/lib/decouplio/steps/resq_fail.rb +0 -2
- data/lib/decouplio/steps/resq_pass.rb +1 -3
- data/lib/decouplio/steps/resq_with_mapping_fail.rb +8 -0
- data/lib/decouplio/steps/resq_with_mapping_pass.rb +8 -0
- data/lib/decouplio/steps/service_as_fail.rb +8 -0
- data/lib/decouplio/steps/service_as_pass.rb +16 -0
- data/lib/decouplio/steps/service_as_step.rb +8 -0
- data/lib/decouplio/steps/step.rb +0 -24
- data/lib/decouplio/steps/unless_condition_fail.rb +1 -21
- data/lib/decouplio/steps/unless_condition_pass.rb +1 -19
- data/lib/decouplio/steps/wrap.rb +25 -37
- data/lib/decouplio/steps/wrap_with_class.rb +43 -0
- data/lib/decouplio/steps/wrap_with_class_method.rb +45 -0
- data/lib/decouplio/utils/prepare_resq_mappings.rb +17 -0
- data/lib/decouplio/version.rb +1 -1
- data/lib/decouplio.rb +93 -0
- metadata +32 -55
- data/.circleci/config.yml +0 -63
- data/.dockerignore +0 -12
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -116
- data/.rubocop_todo.yml +0 -147
- data/.ruby-version +0 -1
- data/.vscode/settings.json +0 -3
- data/Dockerfile +0 -12
- data/Gemfile +0 -8
- data/benchmarks/.ruby-version +0 -1
- data/benchmarks/Dockerfile +0 -12
- data/benchmarks/Gemfile +0 -12
- data/benchmarks/multi_step_benchmark.rb +0 -336
- data/benchmarks/single_step_benchmark.rb +0 -159
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/docker-compose.yml +0 -29
- data/lib/decouplio/composer.rb +0 -691
- data/lib/decouplio/const/colors.rb +0 -25
- data/lib/decouplio/const/doby_aide_options.rb +0 -16
- data/lib/decouplio/const/results.rb +0 -15
- data/lib/decouplio/const/step_options.rb +0 -16
- data/lib/decouplio/const/validations/aide.rb +0 -38
- data/lib/decouplio/const/validations/doby.rb +0 -36
- data/lib/decouplio/default_error_handler.rb +0 -39
- data/lib/decouplio/errors/aide_can_not_be_first_step_error.rb +0 -18
- data/lib/decouplio/errors/aide_controversial_keys_error.rb +0 -26
- data/lib/decouplio/errors/aide_finish_him_error.rb +0 -26
- data/lib/decouplio/errors/doby_controversial_keys_error.rb +0 -26
- data/lib/decouplio/errors/doby_finish_him_error.rb +0 -26
- data/lib/decouplio/errors/extra_key_for_fail_error.rb +0 -26
- data/lib/decouplio/errors/extra_key_for_octo_error.rb +0 -26
- data/lib/decouplio/errors/extra_key_for_resq_error.rb +0 -29
- data/lib/decouplio/errors/extra_key_for_step_error.rb +0 -23
- data/lib/decouplio/errors/extra_key_for_wrap_error.rb +0 -23
- data/lib/decouplio/errors/palp_is_not_defined_error.rb +0 -26
- data/lib/decouplio/errors/step_is_not_defined_for_aide_error.rb +0 -26
- data/lib/decouplio/errors/step_is_not_defined_for_doby_error.rb +0 -27
- data/lib/decouplio/flow.rb +0 -17
- data/lib/decouplio/options_validator.rb +0 -716
- data/lib/decouplio/processor.rb +0 -20
- data/lib/decouplio/steps/aide.rb +0 -37
- data/lib/decouplio/steps/doby.rb +0 -37
- data/lib/decouplio/steps/octo.rb +0 -27
- data/lib/decouplio/steps/service_fail.rb +0 -41
- data/lib/decouplio/steps/service_pass.rb +0 -41
- data/lib/decouplio/steps/service_step.rb +0 -41
- data/lib/decouplio/steps/shared/fail_resolver.rb +0 -40
- data/lib/decouplio/steps/shared/step_resolver.rb +0 -43
- data/lib/decouplio/validators/condition.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f372ffe111d0d286f3c1898645caeb50be16ef3131b6749a28d0d8b43629392
|
4
|
+
data.tar.gz: 717430fa9380f5f4fcd8eb399b1399b47355bdc05860d253fce1b9ea105c5764
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
5
|
+
# How to install?
|
6
6
|
|
7
|
-
|
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
|
-
|
41
|
+
require 'decouplio'
|
42
|
+
|
43
|
+
class MyAction < Decouplio::Action
|
44
|
+
end
|
11
45
|
```
|
12
46
|
|
13
|
-
|
47
|
+
### Logic block
|
14
48
|
|
15
|
-
|
49
|
+
Block inside `Action` which contains definition of business logic.
|
16
50
|
|
17
|
-
|
51
|
+
```ruby
|
52
|
+
require 'decouplio'
|
18
53
|
|
19
|
-
|
54
|
+
class MyAction < Decouplio::Action
|
55
|
+
logic do
|
56
|
+
# logic block
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
20
60
|
|
21
|
-
|
61
|
+
### Step
|
22
62
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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/
|
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").
|
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']
|
data/lib/decouplio/action.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
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
|
-
|
18
|
+
meta_store, parent_railway_flow, ctx
|
21
19
|
)
|
22
|
-
@
|
23
|
-
@ctx =
|
24
|
-
@railway_flow = parent_railway_flow
|
25
|
-
@
|
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
|
-
|
27
|
+
@success
|
34
28
|
end
|
35
29
|
|
36
30
|
def failure?
|
37
|
-
|
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
|
-
|
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 :
|
43
|
+
attr_accessor :meta_store
|
89
44
|
|
90
|
-
def
|
91
|
-
self.
|
45
|
+
def meta_store_class(klass)
|
46
|
+
self.meta_store = klass
|
92
47
|
end
|
93
48
|
|
94
|
-
def call(**params)
|
95
|
-
instance = new(
|
96
|
-
|
97
|
-
|
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.
|
75
|
+
child_class.meta_store = meta_store || Decouplio::DefaultMetaStore
|
116
76
|
end
|
117
77
|
|
118
78
|
def logic(&block)
|
119
|
-
|
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
|
-
@
|
85
|
+
@first_step = Decouplio::NewFlow.call(block)
|
126
86
|
|
127
|
-
if @
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|