hati-operation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +243 -0
- data/hati-operation.gemspec +38 -0
- data/lib/hati_operation/base.rb +151 -0
- data/lib/hati_operation/step_configs_container.rb +21 -0
- data/lib/hati_operation/version.rb +5 -0
- data/lib/hati_operation.rb +10 -0
- metadata +56 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: eaa8b845b9e4b4606f8d19f71c4af9718c794469376a242476ee3ebe30b57085
|
|
4
|
+
data.tar.gz: cd6f76883931c8b29f566b18cb76fbeae85404ab8b9d1f29e3b4f85713ea5acd
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: eb41943860ad28b45f31de2d7bc23beb3c52bea08c4a40248645116c4da20f78d98fe113354a3d0d000ef134f8b26d6e3f39a9deb59b649c5605acd11f57dd50
|
|
7
|
+
data.tar.gz: 79e6e7bd186fcd52206e8774a6240337d5b1aa81dc86f15b604ce20e3f32c3dbe9800ac6fa1a6a91892031dc330bad7c08889a94facba2017d609ac59e97cd86
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 hackico.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# HatiOperation
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/hati_operation)
|
|
4
|
+
[](#license)
|
|
5
|
+
|
|
6
|
+
HatiOperation is a lightweight Ruby toolkit that helps you compose domain logic into clear, reusable **operations**. Built on top of [hati-command](https://github.com/hackico-ai/ruby-hati-command), it serves as an **aggregator** that orchestrates multiple services and commands into cohesive business operations.
|
|
7
|
+
|
|
8
|
+
## ✨ Key Features
|
|
9
|
+
|
|
10
|
+
- **Step-based execution** – write each unit of work as a small service object and compose them with `step`
|
|
11
|
+
- **Implicit result propagation** – methods return `Success(...)` or `Failure(...)` and are automatically unpacked
|
|
12
|
+
- **Fail-fast transactions** – stop the chain as soon as a step fails
|
|
13
|
+
- **Dependency injection (DI)** – override steps at call-time for ultimate flexibility
|
|
14
|
+
- **Macro DSL** – declaratively configure validation, error mapping, transactions and more
|
|
15
|
+
- **Service aggregation** – orchestrate multiple services into cohesive business operations
|
|
16
|
+
|
|
17
|
+
## 🏗️ Architecture
|
|
18
|
+
|
|
19
|
+
HatiOperation builds on top of [hati-command](https://github.com/hackico-ai/ruby-hati-command) and serves as an **aggregator pattern** implementation:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ HatiOperation │
|
|
24
|
+
│ (Aggregator Layer) │
|
|
25
|
+
├─────────────────────────────────────────────────────────────┤
|
|
26
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
27
|
+
│ │ Service A │ │ Service B │ │ Service C │ │
|
|
28
|
+
│ │ (Command) │ │ (Command) │ │ (Command) │ │
|
|
29
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
30
|
+
├─────────────────────────────────────────────────────────────┤
|
|
31
|
+
│ hati-command │
|
|
32
|
+
│ (Foundation Layer) │
|
|
33
|
+
└─────────────────────────────────────────────────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 📋 Table of Contents
|
|
37
|
+
|
|
38
|
+
1. [Installation](#installation)
|
|
39
|
+
2. [Quick Start](#quick-start)
|
|
40
|
+
3. [Step DSL](#step-dsl)
|
|
41
|
+
4. [Dependency Injection](#dependency-injection)
|
|
42
|
+
5. [Alternative DSL Styles](#alternative-dsl-styles)
|
|
43
|
+
6. [Testing](#testing)
|
|
44
|
+
7. [Contributing](#contributing)
|
|
45
|
+
8. [License](#license)
|
|
46
|
+
|
|
47
|
+
## 🚀 Installation
|
|
48
|
+
|
|
49
|
+
Add HatiOperation to your Gemfile and bundle:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# Gemfile
|
|
53
|
+
gem 'hati_operation'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bundle install
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Alternatively:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
gem install hati_operation
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 🎯 Quick Start
|
|
67
|
+
|
|
68
|
+
The example below shows how HatiOperation can be leveraged inside a **Rails API** controller to aggregate multiple services:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# app/controllers/api/v1/withdrawal_controller.rb
|
|
72
|
+
class Api::V1::WithdrawalController < ApplicationController
|
|
73
|
+
def create
|
|
74
|
+
result = Withdrawal::Operation::Create.call(params: params.to_unsafe_h)
|
|
75
|
+
|
|
76
|
+
run_and_render(result)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def run_and_render(result)
|
|
82
|
+
if result.success?
|
|
83
|
+
render json: TransferSerializer.new.serialize(result.value), status: :created
|
|
84
|
+
else
|
|
85
|
+
error = ApiError.new(result.value)
|
|
86
|
+
render json: error.to_json, status: error.status
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 🔧 Defining the Operation
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
# app/operations/withdrawal/operation/create.rb
|
|
96
|
+
class Withdrawal::Operation::Create < HatiOperation::Base
|
|
97
|
+
# Wrap everything in DB transaction
|
|
98
|
+
ar_transaction :funds_transfer_transaction!
|
|
99
|
+
|
|
100
|
+
def call(params:)
|
|
101
|
+
params = step MyApiContract.call(params), err: ApiErr.call(422)
|
|
102
|
+
transfer = step funds_transfer_transaction(params[:account_id])
|
|
103
|
+
EventBroadcast.new.stream(transfer.to_event)
|
|
104
|
+
|
|
105
|
+
transfer.meta
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def funds_transfer_transaction(acc_id)
|
|
109
|
+
acc = Account.find_by(find_by: acc_id).presence : Failure!(err: ApiErr.call(404))
|
|
110
|
+
|
|
111
|
+
withdrawal = step WithdrawalService.call(acc), err: ApiErr.call(409)
|
|
112
|
+
transfer = step ProcessTransferService.call(withdrawal), err: ApiErr.call(503)
|
|
113
|
+
|
|
114
|
+
Success(transfer)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 🎛️ Base Operation Configuration
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
class ApiOperation < HatiOperation::Base
|
|
123
|
+
operation do
|
|
124
|
+
unexpected_err ApiErr.call(500)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 🛠️ Step DSL
|
|
130
|
+
|
|
131
|
+
The DSL gives you fine-grained control over every stage of the operation:
|
|
132
|
+
|
|
133
|
+
### Core DSL Methods
|
|
134
|
+
|
|
135
|
+
- `step` – register a dependency service
|
|
136
|
+
- `params` – validate/transform incoming parameters
|
|
137
|
+
- `on_success` – handle successful operation results
|
|
138
|
+
- `on_failure` – map and handle failure results
|
|
139
|
+
|
|
140
|
+
### Extended Configuration
|
|
141
|
+
|
|
142
|
+
> 📖 **See:** [hati-command](https://github.com/hackico-ai/ruby-hati-command) for all configuration options
|
|
143
|
+
|
|
144
|
+
- `ar_transaction` – execute inside database transaction
|
|
145
|
+
- `fail_fast` – configure fail-fast behavior
|
|
146
|
+
- `failure` – set default failure handling
|
|
147
|
+
- `unexpected_err` – configure generic error behavior
|
|
148
|
+
|
|
149
|
+
## 🔄 Dependency Injection
|
|
150
|
+
|
|
151
|
+
At runtime you can swap out any step for testing, feature-flags, or different environments:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
result = Withdrawal::Operation::Create.call(params) do
|
|
155
|
+
step broadcast: DummyBroadcastService
|
|
156
|
+
step transfer: StubbedPaymentProcessor
|
|
157
|
+
end
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 🎨 Alternative DSL Styles
|
|
161
|
+
|
|
162
|
+
### Declarative Style
|
|
163
|
+
|
|
164
|
+
Prefer more declarative code? Use the class-level DSL:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
class Withdrawal::Operation::Create < ApiOperation
|
|
168
|
+
params CreateContract, err: ApiErr.call(422)
|
|
169
|
+
|
|
170
|
+
ar_transaction :funds_transfer_transaction!
|
|
171
|
+
|
|
172
|
+
step withdrawal: WithdrawalService, err: ApiErr.call(409)
|
|
173
|
+
step transfer: ProcessTransferService, err: ApiErr.call(503)
|
|
174
|
+
step broadcast: Broadcast
|
|
175
|
+
|
|
176
|
+
on_success SerializerService.call(Transfer, status: 201)
|
|
177
|
+
on_failure ApiErrorSerializer
|
|
178
|
+
|
|
179
|
+
# requires :params keyword to access overwritten params
|
|
180
|
+
# same as params = step CreateContract.call(params), err: ApiErr.call(422)
|
|
181
|
+
def call(params:)
|
|
182
|
+
transfer = step funds_transfer_transaction!(params[:account_id])
|
|
183
|
+
broadcast.new.stream(transfer.to_event)
|
|
184
|
+
transfer.meta
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def funds_transfer_transaction!(acc_id)
|
|
188
|
+
acc = step(err: ApiErr.call(404)) { User.find(id) }
|
|
189
|
+
|
|
190
|
+
withdrawal = step withdrawal.call(acc)
|
|
191
|
+
transfer = step transfer.call(withdrawal)
|
|
192
|
+
Success(transfer)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class Api::V2::WithdrawalController < ApiController
|
|
197
|
+
def create
|
|
198
|
+
run_and_render Withdrawal::Operation::Create
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
private
|
|
202
|
+
|
|
203
|
+
def run_and_render(operation, &block)
|
|
204
|
+
render JsonResult.prepare operation.call(params.to_unsafe_h).value
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 🏗️ Full-Stack DI Example
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class Api::V2::WithdrawalController < ApplicationController
|
|
213
|
+
def create
|
|
214
|
+
run_and_render Withdrawal::Operation::Create.call(params.to_unsafe_h) do
|
|
215
|
+
step broadcast: API::V2::BroadcastService
|
|
216
|
+
step transfer: API::V2::PaymentProcessorService
|
|
217
|
+
step serializer: ExtendedTransferSerializer
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## 🧪 Testing
|
|
224
|
+
|
|
225
|
+
Run the test-suite with:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
bundle exec rspec
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
HatiOperation is fully covered by RSpec. See `spec/` for reference examples including stubbed services and DI.
|
|
232
|
+
|
|
233
|
+
## 🤝 Contributing
|
|
234
|
+
|
|
235
|
+
Bug reports and pull requests are welcome on GitHub. Please:
|
|
236
|
+
|
|
237
|
+
1. Fork the project and create your branch from `main`
|
|
238
|
+
2. Run `bundle exec rspec` to ensure tests pass
|
|
239
|
+
3. Submit a pull request with a clear description of your changes
|
|
240
|
+
|
|
241
|
+
## 📄 License
|
|
242
|
+
|
|
243
|
+
HatiOperation is released under the MIT License.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
|
|
6
|
+
require 'hati_operation/version'
|
|
7
|
+
|
|
8
|
+
Gem::Specification.new do |spec|
|
|
9
|
+
spec.name = 'hati-operation'
|
|
10
|
+
spec.version = HatiOperation::VERSION
|
|
11
|
+
spec.authors = ['Mariya Giy']
|
|
12
|
+
spec.email = %w[giy.mariya@gmail.com]
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
spec.summary = 'A Ruby gem for encapsulating business logic in reusable, testable operation classes.'
|
|
16
|
+
spec.description = 'Encapsulates business logic in isolated, reusable operation classes for clarity and testability'
|
|
17
|
+
spec.homepage = "https://github.com/hackico-ai/#{spec.name}"
|
|
18
|
+
|
|
19
|
+
spec.required_ruby_version = '>= 3.0.0'
|
|
20
|
+
|
|
21
|
+
spec.files = Dir['CHANGELOG.md', 'LICENSE', 'README.md', 'hati-operation.gemspec', 'lib/**/*']
|
|
22
|
+
spec.bindir = 'bin'
|
|
23
|
+
spec.executables = []
|
|
24
|
+
spec.require_paths = ['lib']
|
|
25
|
+
|
|
26
|
+
spec.metadata['repo_homepage'] = spec.homepage
|
|
27
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
28
|
+
|
|
29
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
30
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
31
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
32
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
33
|
+
|
|
34
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
35
|
+
|
|
36
|
+
# TODO: while in dev
|
|
37
|
+
# spec.add_dependency 'hati-command'
|
|
38
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hati_command'
|
|
4
|
+
|
|
5
|
+
# Dev version Feautures
|
|
6
|
+
# - implicit result return
|
|
7
|
+
# - object lvl step for <#value> unpacking
|
|
8
|
+
# - forced logical transactional behavior - always fail_fast! on
|
|
9
|
+
# failure step unpacking
|
|
10
|
+
# - class lvl macro for DI:
|
|
11
|
+
# * step validate: Validator
|
|
12
|
+
# * operation for customization (alias to command)
|
|
13
|
+
# - always fail fast on step unpacking
|
|
14
|
+
|
|
15
|
+
module HatiOperation
|
|
16
|
+
class Base
|
|
17
|
+
include HatiCommand::Cmd
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
alias operation command
|
|
21
|
+
|
|
22
|
+
def operation_config
|
|
23
|
+
@operation_config ||= {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def params(command, err: nil)
|
|
27
|
+
operation_config[:params] = command
|
|
28
|
+
operation_config[:params_err] = err
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def on_success(command)
|
|
32
|
+
operation_config[:on_success] = command
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def on_failure(command)
|
|
36
|
+
operation_config[:on_failure] = command
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# TODO: validate type
|
|
40
|
+
def step(**kwargs)
|
|
41
|
+
# TODO: add specific error
|
|
42
|
+
raise 'Invalid Step type. Expected HatiCommand::Cmd' unless included_modules.include?(HatiCommand::Cmd)
|
|
43
|
+
|
|
44
|
+
name, command = kwargs.first
|
|
45
|
+
|
|
46
|
+
if kwargs[:err]
|
|
47
|
+
error_name = "#{name}_error".to_sym
|
|
48
|
+
operation_config[error_name] = kwargs[:err]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# WIP: restructure
|
|
52
|
+
operation_config[name] = command
|
|
53
|
+
|
|
54
|
+
define_method(name) do
|
|
55
|
+
configs = self.class.operation_config
|
|
56
|
+
|
|
57
|
+
step_exec_stack.append({ step: name, err: configs[error_name], done: false })
|
|
58
|
+
|
|
59
|
+
step_configs[name] || configs[name]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def call(*args, **kwargs, &block)
|
|
64
|
+
reciever = nil
|
|
65
|
+
injected_params = nil
|
|
66
|
+
|
|
67
|
+
if block_given?
|
|
68
|
+
reciever = new
|
|
69
|
+
container = StepConfigContainer.new
|
|
70
|
+
|
|
71
|
+
container.instance_eval(&block)
|
|
72
|
+
# WIP: work on defaults for DSL
|
|
73
|
+
reciever.step_configs.merge!(container.configurations)
|
|
74
|
+
injected_params = reciever.step_configs[:params]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
params_modifier = injected_params || operation_config[:params]
|
|
78
|
+
# TODO: naming
|
|
79
|
+
if params_modifier
|
|
80
|
+
unless kwargs[:params]
|
|
81
|
+
raise 'If operation config :params is set, caller method must have :params keyword argument'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
params_rez = params_modifier.call(kwargs[:params])
|
|
85
|
+
reciever_configs = reciever&.step_configs || {}
|
|
86
|
+
params_err = reciever_configs[:params_err] || operation_config[:params_err]
|
|
87
|
+
|
|
88
|
+
if params_rez.failure?
|
|
89
|
+
# WIP: override or nest ???
|
|
90
|
+
params_rez.err = params_err if params_err
|
|
91
|
+
|
|
92
|
+
return params_rez
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
kwargs[:params] = params_rez.value
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
result = super(*args, __command_reciever: reciever, **kwargs)
|
|
99
|
+
# Wrap for implicit
|
|
100
|
+
rez = result.respond_to?(:success?) ? result : HatiCommand::Success.new(result)
|
|
101
|
+
|
|
102
|
+
# TODO: extract
|
|
103
|
+
success_wrap = operation_config[:on_success]
|
|
104
|
+
failure_wrap = operation_config[:on_failure]
|
|
105
|
+
|
|
106
|
+
return success_wrap&.call(rez) if success_wrap && rez.success?
|
|
107
|
+
return failure_wrap&.call(rez) if failure_wrap && rez.failure?
|
|
108
|
+
|
|
109
|
+
rez
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def step_configs
|
|
114
|
+
@step_configs ||= {}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# keep track of step macro calls
|
|
118
|
+
def step_exec_stack
|
|
119
|
+
@step_exec_stack ||= []
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# unpack result
|
|
123
|
+
# wraps implicitly
|
|
124
|
+
def step(result = nil, err: nil, &block)
|
|
125
|
+
return __step_block_call!(err: err, &block) if block_given?
|
|
126
|
+
|
|
127
|
+
last_step = step_exec_stack.last
|
|
128
|
+
err ||= last_step[:err] if last_step
|
|
129
|
+
|
|
130
|
+
if result.is_a?(HatiCommand::Result)
|
|
131
|
+
Failure!(result, err: err || result.error) if result.failure?
|
|
132
|
+
|
|
133
|
+
step_exec_stack.last[:done] = true if last_step
|
|
134
|
+
|
|
135
|
+
return result.value
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
Failure!(result, err: err) if err && result.nil?
|
|
139
|
+
|
|
140
|
+
step_exec_stack.last[:done] = true if last_step
|
|
141
|
+
|
|
142
|
+
result
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def __step_block_call!(err: nil)
|
|
146
|
+
yield
|
|
147
|
+
rescue StandardError => e
|
|
148
|
+
err ? Failure!(e, err: err) : Failure!(e)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HatiOperation
|
|
4
|
+
class StepConfigContainer
|
|
5
|
+
def configurations
|
|
6
|
+
@configurations ||= {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def step(**kwargs)
|
|
10
|
+
step_name, step_klass = kwargs.first
|
|
11
|
+
|
|
12
|
+
configurations[step_name] = step_klass
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# WIP: so far as API adapter
|
|
16
|
+
def params(command = nil, err: nil)
|
|
17
|
+
configurations[:params] = command
|
|
18
|
+
configurations[:params_err] = err
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hati_operation/version'
|
|
4
|
+
require 'hati_operation/step_configs_container'
|
|
5
|
+
require 'hati_operation/base'
|
|
6
|
+
# errors
|
|
7
|
+
# require 'hati_operation/errors/base_error'
|
|
8
|
+
# require 'hati_operation/errors/configuration_error'
|
|
9
|
+
# require 'hati_operation/errors/fail_fast_error'
|
|
10
|
+
# require 'hati_operation/errors/transaction_error'
|
metadata
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hati-operation
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mariya Giy
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Encapsulates business logic in isolated, reusable operation classes for
|
|
13
|
+
clarity and testability
|
|
14
|
+
email:
|
|
15
|
+
- giy.mariya@gmail.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- LICENSE
|
|
21
|
+
- README.md
|
|
22
|
+
- hati-operation.gemspec
|
|
23
|
+
- lib/hati_operation.rb
|
|
24
|
+
- lib/hati_operation/base.rb
|
|
25
|
+
- lib/hati_operation/step_configs_container.rb
|
|
26
|
+
- lib/hati_operation/version.rb
|
|
27
|
+
homepage: https://github.com/hackico-ai/hati-operation
|
|
28
|
+
licenses:
|
|
29
|
+
- MIT
|
|
30
|
+
metadata:
|
|
31
|
+
repo_homepage: https://github.com/hackico-ai/hati-operation
|
|
32
|
+
allowed_push_host: https://rubygems.org
|
|
33
|
+
homepage_uri: https://github.com/hackico-ai/hati-operation
|
|
34
|
+
changelog_uri: https://github.com/hackico-ai/hati-operation/blob/main/CHANGELOG.md
|
|
35
|
+
source_code_uri: https://github.com/hackico-ai/hati-operation
|
|
36
|
+
bug_tracker_uri: https://github.com/hackico-ai/hati-operation/issues
|
|
37
|
+
rubygems_mfa_required: 'true'
|
|
38
|
+
rdoc_options: []
|
|
39
|
+
require_paths:
|
|
40
|
+
- lib
|
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 3.0.0
|
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '0'
|
|
51
|
+
requirements: []
|
|
52
|
+
rubygems_version: 3.6.9
|
|
53
|
+
specification_version: 4
|
|
54
|
+
summary: A Ruby gem for encapsulating business logic in reusable, testable operation
|
|
55
|
+
classes.
|
|
56
|
+
test_files: []
|