hubbado-trailblazer 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 60b8c12b372f5ab077aa00bdf7f9638739d76485423eefebfd1aab14d4c17088
4
+ data.tar.gz: 3de9bda895d56d17681e98b1c23a9fbce15f2dbb0442ee5500eae57e8aefee6d
5
+ SHA512:
6
+ metadata.gz: 852d22bce0c966280fad36f04c0eb0ec2df6318b58ccaf3d69a3c21ee15b108f1c91d2c985c715a27e5820c7aec742f5483819354d4fef57f2eaac3f7668cdf1
7
+ data.tar.gz: e6a2141bac839c57928ef4891222bdfb1584fd94e2a6eb66d319899084e893a496f9c9cd068b9f66e5592b397f9e2f18209c2cc6ed155e5ff31c27e784ef3240
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-05-29
9
+
10
+ ### Added
11
+
12
+ - Initial release of the hubbado-trailblazer gem
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hubbado
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,215 @@
1
+ # Hubbado Trailblazer
2
+
3
+ Enhanced Trailblazer operation utilities for Ruby applications with improved error handling, operation execution patterns, and ActiveRecord integration.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hubbado-trailblazer'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ POSTURE=dev ./install-gems.sh
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install hubbado-trailblazer
23
+ ```
24
+
25
+ ## Overview
26
+
27
+ This gem extends Trailblazer operations with enhanced execution patterns, automatic transaction handling, and structured error handling. It provides:
28
+
29
+ - **RunOperation**: A mixin for executing operations with structured result handling
30
+ - **Transaction**: Automatic ActiveRecord transaction wrapping for operations
31
+ - **Macros**: Custom operation macros for common patterns (policies, decorators, contract prepopulation)
32
+ - **Enhanced Error Handling**: Structured error responses with proper exception types
33
+ - **Operation Tracing**: Debug capabilities for operation execution flow
34
+
35
+ ## Usage
36
+
37
+ ### Basic Operation Execution
38
+
39
+ Create a concern to include the RunOperation functionality in your controllers:
40
+
41
+ ```ruby
42
+ module Concern
43
+ module Trailblazer
44
+ extend ActiveSupport::Concern
45
+
46
+ included do
47
+ include Hubbado::Trailblazer::RunOperation
48
+
49
+ def _run_options(options)
50
+ options.merge(
51
+ current_user: current_user || Users::Models::User.guest,
52
+ )
53
+ end
54
+
55
+ # Trailblazer takes all the parameters, no permit for us!
56
+ def _run_params(params)
57
+ params = params.to_unsafe_hash if params.respond_to?(:to_unsafe_hash)
58
+ params
59
+ end
60
+ end
61
+ end
62
+ end
63
+ ```
64
+
65
+ Then include it in your controllers:
66
+
67
+ ```ruby
68
+ class UsersController < ApplicationController
69
+ include Concern::Trailblazer
70
+
71
+ def create
72
+ run_operation Users::Create do |result|
73
+ result.success do |ctx|
74
+ redirect_to user_path(ctx[:user])
75
+ end
76
+
77
+ result.validation_failed do |ctx|
78
+ render :new, locals: { contract: ctx['contract.default'] }
79
+ end
80
+
81
+ result.policy_failed do |ctx|
82
+ redirect_to root_path, alert: 'Not authorized'
83
+ end
84
+
85
+ result.otherwise do |ctx|
86
+ redirect_to users_path, alert: 'Something went wrong'
87
+ end
88
+ end
89
+ end
90
+ end
91
+ ```
92
+
93
+ ### Transaction Handling
94
+
95
+ Wrap operations in ActiveRecord transactions that automatically rollback on failure:
96
+
97
+ ```ruby
98
+ class Users::Create < Trailblazer::Operation
99
+ step Wrap(Hubbado::Trailblazer::Transaction) {
100
+ step Model(User, :new)
101
+ step Contract::Build(constant: Users::CreateContract)
102
+ step Contract::Validate()
103
+ step Contract::Persist()
104
+ }
105
+ end
106
+ ```
107
+
108
+ ### Custom Macros
109
+
110
+ #### Policy Macro
111
+
112
+ Enhanced policy handling for use with Hubbado::Policy:
113
+
114
+ ```ruby
115
+ class Users::Update < Trailblazer::Operation
116
+ step Hubbado::Trailblazer::Macro::Policy(Users::UpdatePolicy)
117
+ # ... other steps
118
+ end
119
+ ```
120
+
121
+ #### Decorate Model Macro
122
+
123
+ Automatically decorate models using `SomeDecorator.build(model, current_user)`:
124
+
125
+ ```ruby
126
+ class Users::Show < Trailblazer::Operation
127
+ step Model(User, :find)
128
+ step Hubbado::Trailblazer::Macro::DecorateModel(UserDecorator)
129
+ end
130
+ ```
131
+
132
+ #### Prepopulate Contract Macro
133
+
134
+ Prepopulate Reform contracts with values from params without validation:
135
+
136
+ ```ruby
137
+ class Users::Edit < Trailblazer::Operation
138
+ step Model(User, :find)
139
+ step Contract::Build(constant: Users::UpdateContract)
140
+ step Hubbado::Trailblazer::Macro::PrepopulateContract()
141
+ end
142
+ ```
143
+
144
+ ## Operation Tracing
145
+
146
+ Debug operation execution by setting the `TRACE_OPERATION` environment variable:
147
+
148
+ ```bash
149
+ # Trace a specific operation
150
+ TRACE_OPERATION=Users::Create rails console
151
+
152
+ # Trace all operations
153
+ TRACE_OPERATION=_all rails console
154
+ ```
155
+
156
+ This will output detailed execution traces showing which steps pass or fail.
157
+
158
+ ## Advanced Features
159
+
160
+ ### Custom Runtime Options
161
+
162
+ The concern template methods can be customized for your application's needs:
163
+
164
+ ```ruby
165
+ def _run_options(options)
166
+ options.merge(
167
+ current_user: current_user || Users::Models::User.guest,
168
+ request: request,
169
+ session: session
170
+ )
171
+ end
172
+
173
+ def _run_params(params)
174
+ params = params.to_unsafe_hash if params.respond_to?(:to_unsafe_hash)
175
+ params
176
+ end
177
+ ```
178
+
179
+ ### Error Handling
180
+
181
+ The gem provides structured error handling with specific exception types:
182
+
183
+ - `Hubbado::Trailblazer::Errors::Unauthorized` - Raised when policies fail
184
+ - `Hubbado::Trailblazer::OperationFailed` - Raised when operations fail unexpectedly
185
+
186
+ ## Testing
187
+
188
+ Run the test suite:
189
+
190
+ ```bash
191
+ $ ./test.sh
192
+ ```
193
+
194
+ The gem uses TestBench for testing and includes comprehensive test coverage for all operation patterns and macros.
195
+
196
+ ## Requirements
197
+
198
+ - Ruby >= 3.2
199
+ - ActiveRecord
200
+ - Trailblazer Operation
201
+ - Reform (for contract testing)
202
+
203
+ ## Contributing
204
+
205
+ 1. Fork the repository
206
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
207
+ 3. Make your changes and add tests
208
+ 4. Ensure all tests pass (`./test.sh`)
209
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
210
+ 6. Push to the branch (`git push origin my-new-feature`)
211
+ 7. Create a new Pull Request
212
+
213
+ ## License
214
+
215
+ This gem is available under the MIT License. See the LICENSE file for more details.
@@ -0,0 +1,38 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "hubbado-trailblazer"
3
+ s.version = "1.0.0"
4
+ s.summary = "Enhanced Trailblazer operation utilities for Ruby applications with improved error handling, operation execution patterns, and ActiveRecord integration."
5
+
6
+ s.authors = ["Hubbado Devs"]
7
+ s.email = ["devs@hubbado.com"]
8
+ s.homepage = 'https://github.com/hubbado/hubbado-trailblazer'
9
+
10
+ s.metadata["github_repo"] = s.homepage
11
+ s.metadata["homepage_uri"] = s.homepage
12
+ s.metadata["changelog_uri"] = "#{s.homepage}/blob/master/CHANGELOG.md"
13
+
14
+ s.require_paths = ["lib"]
15
+ s.files = Dir.glob(%w[
16
+ lib/**/*.rb
17
+ *.gemspec
18
+ LICENSE*
19
+ README*
20
+ CHANGELOG*
21
+ ])
22
+ s.platform = Gem::Platform::RUBY
23
+ s.required_ruby_version = ">= 3.2"
24
+
25
+ s.add_dependency "activerecord"
26
+ s.add_dependency "evt-template_method"
27
+ s.add_dependency "hubbado-log"
28
+ s.add_dependency "trailblazer-operation"
29
+
30
+ s.add_development_dependency "debug"
31
+ s.add_development_dependency "dry-validation"
32
+ s.add_development_dependency "sqlite3"
33
+ s.add_development_dependency "hubbado-style"
34
+ s.add_development_dependency "reform"
35
+ s.add_development_dependency "test_bench"
36
+ s.add_development_dependency "trailblazer-macro"
37
+ s.add_development_dependency "trailblazer-macro-contract"
38
+ end
@@ -0,0 +1,15 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ module Errors
4
+ class Unauthorized < StandardError
5
+ attr_reader :result
6
+
7
+ def initialize(message = nil, result = nil)
8
+ @result = result
9
+
10
+ super(message)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ module Macro
4
+ def self.DecorateModel(decorator, model: :model)
5
+ task = ->((ctx, flow_options), _) do
6
+ unless ctx[model] && ctx[:current_user]
7
+ return ::Trailblazer::Activity::Left, [ctx, flow_options]
8
+ end
9
+
10
+ ctx[model] = decorator.build(ctx[model], ctx[:current_user])
11
+
12
+ [::Trailblazer::Activity::Right, [ctx, flow_options]]
13
+ end
14
+
15
+ { task: task, id: "Decorate" }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ module Macro
4
+ def self.Policy(policy_class, action, name: :default, model: :model)
5
+ ::Trailblazer::Macro::Policy.step(
6
+ Policy.build(policy_class, action, model), name: name
7
+ )
8
+ end
9
+
10
+ module Policy
11
+ def self.build(policy_class, action, model)
12
+ Condition.new(policy_class, action, model)
13
+ end
14
+
15
+ # Pundit::Condition is invoked at runtime when iterating the pipe.
16
+ class Condition
17
+ def initialize(policy_class, action, model)
18
+ @policy_class = policy_class
19
+ @action = action
20
+ @model = model
21
+ end
22
+
23
+ # Instantiate the actual policy object, and call it.
24
+ def call((options), *)
25
+ policy = build_policy(options)
26
+ result!(policy.send(@action), policy)
27
+ end
28
+
29
+ private
30
+
31
+ def build_policy(options)
32
+ @policy_class.build(options[:current_user], options[@model])
33
+ end
34
+
35
+ def result!(policy_result, policy)
36
+ data = { policy: policy, policy_result: policy_result }
37
+
38
+ ::Trailblazer::Operation::Result.new(policy_result.permitted?, data)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ module Macro
4
+ # This is used to pre-populate a contract before validating it, so that
5
+ # form options can be built based on the contract values
6
+ #
7
+ # For example, a contract, for a not yet saved assignment, might have
8
+ # timesheet approvers that depend on the client company ID in the
9
+ # contract
10
+ def self.PrepopulateContract(key:)
11
+ task = ->((ctx, flow_options), _) do
12
+ ctx[:prepopulated_contract] = key
13
+
14
+ params = ctx[:params][key]
15
+
16
+ return ::Trailblazer::Activity::Right, [ctx, flow_options] unless params
17
+
18
+ ctx['contract.default'].deserialize(params)
19
+
20
+ [::Trailblazer::Activity::Right, [ctx, flow_options]]
21
+ end
22
+
23
+ { task: task, id: "PrepopulateContract" }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,224 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ # This allows you to run an operation and then respond differently based on
4
+ # the result of the operation.
5
+ #
6
+ # run_operation MyOperation do |result|
7
+ # result.success do |ctx|
8
+ # ...
9
+ # end
10
+ #
11
+ # result.policy_failed do |ctx|
12
+ # # This is optional if not used the operation will raise an exception
13
+ # ...
14
+ # # Also optionally you can raise the default exception, it will not be raised
15
+ # # if the block is executed otherwise when the block is finished
16
+ # result.raise_policy_failed
17
+ # end
18
+ #
19
+ # result.validation_failed do |ctx|
20
+ # ...
21
+ # end
22
+ #
23
+ # result.otherwise do |ctx|
24
+ # # This is optional if not used the operation will raise an exception
25
+ # ...
26
+ # # Also optionally you can raise the default exception, it will not be raised
27
+ # # if the block is executed otherwise when the block is finished
28
+ # result.raise_operation_failed
29
+ # end
30
+ # end
31
+ #
32
+ # ctx is the context built by the operation.
33
+ #
34
+ # If there is a policy failure and you have not implemented
35
+ # `result.policy_failed` then an exception will be raised.
36
+ #
37
+ # If the operation fails (due to non-policy error) and you have not
38
+ # implemented `result.otherwise` then an exception will be raised.
39
+ #
40
+ # Note, it is on purpose that implementing `result.otherwise` is not enough
41
+ # to stop an exception being raised if the policy fails. This to prevent a
42
+ # `result.otherwise` block rendering a form when the policy failed.
43
+ #
44
+ # We also considered TrailblazerEndpoint in place of this, but it seemed to
45
+ # be complicated for what it quite a simple usecase
46
+ #
47
+ # Tracing operations
48
+ #
49
+ # Setting the ENV variable TRACE_OPERATION to the class name of an
50
+ # operation (or to "_all") will cause the operation to be run with "wtf?"
51
+ # rather than `call`, outputting the trace of the operation, and showing
52
+ # which step failed, to stdout (not logging)
53
+ #
54
+ # This is useful when debugging traces
55
+ module RunOperation
56
+ include TemplateMethod
57
+
58
+ # Implement this if params need pre-processing before being passed to the operation
59
+ # For example, a controller needs to turn params into an unsafe hash
60
+ template_method :_run_params do |params|
61
+ params
62
+ end
63
+
64
+ # Implement this to inject additional context into the operation
65
+ # For example, a a controller can pass in current_user
66
+ template_method :_run_options do |ctx|
67
+ ctx
68
+ end
69
+
70
+ def _run_runtime_options(ctx = {}, *dependencies)
71
+ [_run_options(ctx), *dependencies]
72
+ end
73
+
74
+ def run_operation(operation, params = self.params, *options)
75
+ trace_operation = TraceOperation.(operation)
76
+ operation_method = trace_operation ? 'wtf?' : 'call'
77
+
78
+ operation_arguments = { params: _run_params(params) }
79
+ operation_arguments[:request] = request if respond_to?(:request)
80
+ operation_arguments.merge!(*_run_runtime_options(*options))
81
+
82
+ ctx = operation.send(operation_method, operation_arguments)
83
+
84
+ result = Result.new(operation, ctx, trace_operation)
85
+
86
+ yield(result) if block_given?
87
+
88
+ if ctx['result.policy.default']&.failure?
89
+ result.raise_policy_failed unless result.policy_failed_executed?
90
+ elsif ctx.failure? && !result.validation_failed_executed? && !result.otherwise_executed?
91
+ result.raise_operation_failed
92
+ end
93
+
94
+ result.returned
95
+ end
96
+
97
+ class Result
98
+ include Hubbado::Log::Dependency
99
+
100
+ attr_reader :returned
101
+ attr_reader :trace_operation
102
+
103
+ def initialize(operation, ctx, trace_operation)
104
+ @operation = operation
105
+ @ctx = ctx
106
+ @trace_operation = trace_operation
107
+ end
108
+
109
+ def log_level
110
+ trace_operation ? :debug : :info
111
+ end
112
+
113
+ def success
114
+ return unless ctx.success?
115
+
116
+ @success_executed = true
117
+ @returned = yield(ctx)
118
+
119
+ logger.send(log_level, "Success block executed for operation #{operation}")
120
+ @returned
121
+ end
122
+
123
+ def policy_failed
124
+ return unless ctx['result.policy.default']&.failure?
125
+
126
+ @policy_failed_executed = true
127
+ @returned = yield(ctx)
128
+
129
+ logger.send(log_level, "Policy failed block executed for operation #{operation}")
130
+ @returned
131
+ end
132
+
133
+ def validation_failed
134
+ return if success_executed?
135
+
136
+ contract = ctx['contract.default']
137
+ # TODO: We cannot call `contract.valid?` here, since the following
138
+ # steps will have converted form errors from strings to symbols:
139
+ #
140
+ # contract = Companies::Controls::Contracts::InviteMember.example
141
+ # contract.validate({name: 'Some name', email: 'email@example.com', role: :staff)
142
+ # contract.errors.add :email, :taken
143
+ # contract.valid?
144
+ #
145
+ # This only happens if errors are added outside of the validation
146
+ return if contract.nil? || contract.errors.full_messages.empty?
147
+
148
+ @validation_failed_executed = true
149
+ @returned = yield(ctx)
150
+
151
+ if trace_operation
152
+ logger.send(
153
+ log_level,
154
+ "Validation failed: #{ctx['contract.default'].errors.full_messages.join(', ')}"
155
+ )
156
+ else
157
+ logger.send(log_level, "Validation failed")
158
+ end
159
+ @returned
160
+ end
161
+
162
+ def otherwise
163
+ return if executed?
164
+
165
+ @otherwise_executed = true
166
+ @returned = yield(ctx)
167
+
168
+ logger.send(log_level, "Otherwise block executed for operation #{operation}")
169
+ @returned
170
+ end
171
+
172
+ def raise_operation_failed
173
+ msg = "Operation #{operation.name} failed"
174
+
175
+ error_messages = ctx['contract.default']&.errors&.full_messages
176
+
177
+ if error_messages&.any?
178
+ msg += " with errors:\n\n#{error_messages.map { |e| " - #{e}" }.join("\n")}"
179
+ end
180
+
181
+ raise StandardError, msg
182
+ end
183
+
184
+ def raise_policy_failed
185
+ current_user = ctx[:current_user]
186
+ true_user = ctx[:true_user]
187
+
188
+ msg = "User #{current_user&.id}/#{true_user&.id} (#{current_user&.roles&.join ', '}) " \
189
+ "not allowed to run #{operation.name}"
190
+
191
+ raise Hubbado::Trailblazer::Errors::Unauthorized.new(msg, ctx['result.policy.default'][:policy_result])
192
+ end
193
+
194
+ def success_executed?
195
+ !!@success_executed
196
+ end
197
+
198
+ def policy_failed_executed?
199
+ !!@policy_failed_executed
200
+ end
201
+
202
+ def validation_failed_executed?
203
+ !!@validation_failed_executed
204
+ end
205
+
206
+ def otherwise_executed?
207
+ !!@otherwise_executed
208
+ end
209
+
210
+ def executed?
211
+ success_executed? ||
212
+ policy_failed_executed? ||
213
+ validation_failed_executed? ||
214
+ otherwise_executed?
215
+ end
216
+
217
+ private
218
+
219
+ attr_reader :operation
220
+ attr_reader :ctx
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,9 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ module TraceOperation
4
+ def self.call(operation)
5
+ [operation.name, '_all'].include?(ENV.fetch('TRACE_OPERATION', nil))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ class Transaction
4
+ def self.call((_ctx, _flow_options), *)
5
+ res = nil
6
+
7
+ ActiveRecord::Base.transaction do
8
+ res = yield
9
+ raise ActiveRecord::Rollback if res.first.to_h[:semantic] == :failure
10
+ end
11
+
12
+ res
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module Hubbado
2
+ module Trailblazer
3
+ OperationFailed = Class.new(StandardError)
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_record'
2
+ require 'hubbado/log'
3
+ require 'template_method'
4
+ require 'trailblazer/operation'
5
+
6
+ require 'hubbado/trailblazer'
7
+
8
+ require 'hubbado/trailblazer/transaction'
9
+ require 'hubbado/trailblazer/errors'
10
+ require 'hubbado/trailblazer/run_operation'
11
+ require 'hubbado/trailblazer/trace_operation'
12
+
13
+ require 'hubbado/trailblazer/macro/decorate_model'
14
+ require 'hubbado/trailblazer/macro/policy'
15
+ require 'hubbado/trailblazer/macro/prepopulate_contract'
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hubbado-trailblazer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Hubbado Devs
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: evt-template_method
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: hubbado-log
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: trailblazer-operation
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: debug
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: dry-validation
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: sqlite3
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: hubbado-style
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: reform
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: test_bench
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: trailblazer-macro
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: trailblazer-macro-contract
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ email:
181
+ - devs@hubbado.com
182
+ executables: []
183
+ extensions: []
184
+ extra_rdoc_files: []
185
+ files:
186
+ - CHANGELOG.md
187
+ - LICENSE
188
+ - README.md
189
+ - hubbado-trailblazer.gemspec
190
+ - lib/hubbado-trailblazer.rb
191
+ - lib/hubbado/trailblazer.rb
192
+ - lib/hubbado/trailblazer/errors.rb
193
+ - lib/hubbado/trailblazer/macro/decorate_model.rb
194
+ - lib/hubbado/trailblazer/macro/policy.rb
195
+ - lib/hubbado/trailblazer/macro/prepopulate_contract.rb
196
+ - lib/hubbado/trailblazer/run_operation.rb
197
+ - lib/hubbado/trailblazer/trace_operation.rb
198
+ - lib/hubbado/trailblazer/transaction.rb
199
+ homepage: https://github.com/hubbado/hubbado-trailblazer
200
+ licenses: []
201
+ metadata:
202
+ github_repo: https://github.com/hubbado/hubbado-trailblazer
203
+ homepage_uri: https://github.com/hubbado/hubbado-trailblazer
204
+ changelog_uri: https://github.com/hubbado/hubbado-trailblazer/blob/master/CHANGELOG.md
205
+ rdoc_options: []
206
+ require_paths:
207
+ - lib
208
+ required_ruby_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '3.2'
213
+ required_rubygems_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ requirements: []
219
+ rubygems_version: 3.6.7
220
+ specification_version: 4
221
+ summary: Enhanced Trailblazer operation utilities for Ruby applications with improved
222
+ error handling, operation execution patterns, and ActiveRecord integration.
223
+ test_files: []