decouplio 1.0.0alpha8 → 1.0.0rc
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +313 -15
- data/decouplio.gemspec +8 -2
- data/lib/decouplio/action.rb +24 -40
- data/lib/decouplio/action_state_printer.rb +2 -5
- data/lib/decouplio/const/flows.rb +59 -0
- data/lib/decouplio/const/reserved_methods.rb +6 -13
- data/lib/decouplio/const/types.rb +0 -144
- 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 +2 -4
- 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 +340 -79
- 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 -35
- data/lib/decouplio/steps/inner_action_pass.rb +9 -29
- data/lib/decouplio/steps/inner_action_step.rb +1 -35
- 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 +30 -42
- data/.circleci/config.yml +0 -63
- data/.dockerignore +0 -12
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -108
- 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 -615
- data/lib/decouplio/const/colors.rb +0 -25
- data/lib/decouplio/const/results.rb +0 -15
- data/lib/decouplio/const/step_options.rb +0 -16
- 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/flow.rb +0 -17
- data/lib/decouplio/options_validator.rb +0 -606
- data/lib/decouplio/processor.rb +0 -20
- 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 -49
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,52 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'forwardable'
|
4
|
-
require_relative 'flow'
|
5
|
-
require_relative 'processor'
|
6
|
-
require_relative 'default_meta_store'
|
7
|
-
require_relative 'action_state_printer'
|
8
|
-
require_relative 'errors/logic_redefinition_error'
|
9
|
-
require_relative 'errors/logic_is_not_defined_error'
|
10
|
-
require_relative 'errors/execution_error'
|
11
|
-
require_relative 'const/results'
|
12
|
-
|
13
3
|
module Decouplio
|
14
4
|
class Action
|
5
|
+
extend Forwardable
|
6
|
+
def_delegator :@ctx, :[]
|
7
|
+
|
15
8
|
attr_reader :railway_flow, :ctx, :meta_store
|
9
|
+
attr_writer :success
|
16
10
|
|
17
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
20
|
@meta_store = meta_store
|
23
|
-
@ctx =
|
24
|
-
@railway_flow = parent_railway_flow
|
25
|
-
@
|
26
|
-
end
|
27
|
-
|
28
|
-
def [](key)
|
29
|
-
@ctx[key]
|
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 append_railway_flow(stp)
|
49
|
-
railway_flow << stp
|
31
|
+
!@success
|
50
32
|
end
|
51
33
|
|
52
34
|
def inspect
|
@@ -64,13 +46,15 @@ module Decouplio
|
|
64
46
|
self.meta_store = klass
|
65
47
|
end
|
66
48
|
|
67
|
-
def call(**params)
|
49
|
+
def call(_parent_meta_store: nil, _parent_railway_flow: nil, _parent_ctx: nil, **params)
|
68
50
|
instance = new(
|
69
|
-
|
70
|
-
|
71
|
-
|
51
|
+
_parent_meta_store || meta_store.new,
|
52
|
+
_parent_railway_flow || [],
|
53
|
+
_parent_ctx || Ctx[params]
|
72
54
|
)
|
73
|
-
|
55
|
+
next_step = @first_step
|
56
|
+
|
57
|
+
next_step = next_step.process(instance) while next_step
|
74
58
|
instance
|
75
59
|
end
|
76
60
|
|
@@ -92,15 +76,15 @@ module Decouplio
|
|
92
76
|
end
|
93
77
|
|
94
78
|
def logic(&block)
|
95
|
-
|
79
|
+
unless @first_step.nil?
|
96
80
|
raise Decouplio::Errors::LogicRedefinitionError.new(
|
97
81
|
errored_option: to_s
|
98
82
|
)
|
99
83
|
end
|
100
84
|
if block_given?
|
101
|
-
@
|
85
|
+
@first_step = Decouplio::NewFlow.call(block)
|
102
86
|
|
103
|
-
if @
|
87
|
+
if @first_step.nil?
|
104
88
|
raise Decouplio::Errors::LogicIsNotDefinedError.new(
|
105
89
|
errored_option: to_s
|
106
90
|
)
|
@@ -4,17 +4,12 @@ module Decouplio
|
|
4
4
|
class ActionStatePrinter
|
5
5
|
def self.call(action)
|
6
6
|
<<~INSPECT
|
7
|
-
|
8
7
|
Result: #{result(action)}
|
9
|
-
|
10
8
|
RailwayFlow:
|
11
9
|
#{railway_flow(action)}
|
12
|
-
|
13
10
|
Context:
|
14
11
|
#{action_context(action)}
|
15
|
-
|
16
12
|
#{meta_store(action)}
|
17
|
-
|
18
13
|
INSPECT
|
19
14
|
end
|
20
15
|
|
@@ -27,6 +22,8 @@ module Decouplio
|
|
27
22
|
end
|
28
23
|
|
29
24
|
def self.action_context(action)
|
25
|
+
return 'Empty' if action.ctx.empty?
|
26
|
+
|
30
27
|
action.ctx.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join("\n ")
|
31
28
|
end
|
32
29
|
|
@@ -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
|