rails_ops 1.7.8 → 1.8.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +5 -5
- data/README.md +82 -36
- data/VERSION +1 -1
- data/lib/rails_ops/operation.rb +51 -34
- data/lib/rails_ops/railtie.rb +6 -4
- data/lib/rails_ops.rb +1 -0
- data/rails_ops.gemspec +5 -5
- data/test/unit/rails_ops/operation_test.rb +161 -1
- data/test/unit/rails_ops/railtie_test.rb +29 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d983d20a0a0bf3e1b7e967d0dbd16aec64f253c8a7a12e7c6b72f9f6f676b604
|
|
4
|
+
data.tar.gz: 00a7b268b2337ba5b2cf57855b9d611a7c9dbe3492cd9484345ad1f7956b2b27
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a50ef1e82a5a82e1adddb76435389babd2f34a6fe3e4f80c06bf3ec061b26235fa80541cfbfdab6fdc43f253245b388b1b376d8032e090c510fe1a4f2edc591
|
|
7
|
+
data.tar.gz: fca76d3c3c65cc564744596dd298e8a52e69320cd0eb5952750e689dc879b21dce5dab13dd6372456108ea367aa536a62e40a58d4c069af5ab78eb214167c3be
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.0 (2026-05-07)
|
|
4
|
+
|
|
5
|
+
* `Operation#run` (non-bang) now wraps the call to `run!` in a
|
|
6
|
+
SAVEPOINT whenever a database transaction is already open. This
|
|
7
|
+
ensures that partial writes performed before a validation error are
|
|
8
|
+
rolled back even though `run` swallows the error and returns
|
|
9
|
+
`false`. `run_sub` benefits from the same protection transitively.
|
|
10
|
+
Behavior outside of an open transaction is unchanged, and the
|
|
11
|
+
behavior of `run!` / `run_sub!` is unchanged.
|
|
12
|
+
* Deprecate `with_rollback_on_exception`. The helper now emits an
|
|
13
|
+
`ActiveSupport::Deprecation` warning via `RailsOps.deprecator` and
|
|
14
|
+
will be removed in RailsOps 2.0. The savepoint added to `run` makes
|
|
15
|
+
it obsolete for the common "save then do more work" pattern. For
|
|
16
|
+
non-validation errors that should trigger a rollback, raise
|
|
17
|
+
`RailsOps::Exceptions::RollbackRequired` directly.
|
|
18
|
+
|
|
3
19
|
## 1.7.8 (2026-03-12)
|
|
4
20
|
|
|
5
21
|
* Add practical examples throughout README covering non-model operations,
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
rails_ops (1.
|
|
4
|
+
rails_ops (1.8.0)
|
|
5
5
|
active_type (>= 1.3.0)
|
|
6
6
|
minitest
|
|
7
7
|
rails (> 4)
|
|
@@ -132,7 +132,7 @@ GEM
|
|
|
132
132
|
mini_mime (1.1.5)
|
|
133
133
|
minitest (5.27.0)
|
|
134
134
|
mutex_m (0.3.0)
|
|
135
|
-
net-imap (0.4.
|
|
135
|
+
net-imap (0.4.24)
|
|
136
136
|
date
|
|
137
137
|
net-protocol
|
|
138
138
|
net-pop (0.1.2)
|
|
@@ -158,8 +158,8 @@ GEM
|
|
|
158
158
|
psych (5.1.2)
|
|
159
159
|
stringio
|
|
160
160
|
racc (1.8.1)
|
|
161
|
-
rack (3.2.
|
|
162
|
-
rack-session (2.1.
|
|
161
|
+
rack (3.2.6)
|
|
162
|
+
rack-session (2.1.2)
|
|
163
163
|
base64 (>= 0.1.0)
|
|
164
164
|
rack (>= 3.0.0)
|
|
165
165
|
rack-test (2.1.0)
|
|
@@ -241,7 +241,7 @@ GEM
|
|
|
241
241
|
sqlite3 (1.6.2-x86_64-linux)
|
|
242
242
|
stringio (3.1.0)
|
|
243
243
|
thor (1.3.1)
|
|
244
|
-
timeout (0.6.
|
|
244
|
+
timeout (0.6.1)
|
|
245
245
|
tzinfo (2.0.6)
|
|
246
246
|
concurrent-ruby (~> 1.0)
|
|
247
247
|
unicode-display_width (3.1.4)
|
data/README.md
CHANGED
|
@@ -720,24 +720,22 @@ module Operations::Article
|
|
|
720
720
|
model.published_at = Time.current
|
|
721
721
|
super # Save the article
|
|
722
722
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
record_type: 'Article'
|
|
729
|
-
end
|
|
723
|
+
run_sub! Operations::Cache::Rebuild, rebuild_counters: true
|
|
724
|
+
run_sub! Operations::Notification::Send,
|
|
725
|
+
template: 'article_published',
|
|
726
|
+
record_id: model.id,
|
|
727
|
+
record_type: 'Article'
|
|
730
728
|
end
|
|
731
729
|
end
|
|
732
730
|
end
|
|
733
731
|
```
|
|
734
732
|
|
|
735
|
-
|
|
736
|
-
`
|
|
737
|
-
`RailsOps::Exceptions::
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
733
|
+
Validation errors raised by either sub-operation propagate up — `run_sub!`
|
|
734
|
+
re-raises any `validation_errors` as
|
|
735
|
+
`RailsOps::Exceptions::SubOpValidationFailed`, which escapes the parent's
|
|
736
|
+
`run` and rolls back the surrounding transaction. If the parent is invoked
|
|
737
|
+
via `run` (non-bang), the savepoint added in 1.8.0 ensures `super`'s save
|
|
738
|
+
is rolled back too. See section *Transactions* for more details.
|
|
741
739
|
|
|
742
740
|
## Contexts
|
|
743
741
|
|
|
@@ -2210,28 +2208,67 @@ end
|
|
|
2210
2208
|
Typically though, transactions are opened on a higher level and outside of
|
|
2211
2209
|
operations, e.g. in controller methods.
|
|
2212
2210
|
|
|
2213
|
-
###
|
|
2211
|
+
### Automatic Savepoint Around `run`
|
|
2214
2212
|
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2213
|
+
Since version 1.8.0, `RailsOps::Operation#run` (the non-bang variant)
|
|
2214
|
+
automatically wraps the operation in a SAVEPOINT whenever a database
|
|
2215
|
+
transaction is already open. If the operation raises a validation error
|
|
2216
|
+
partway through `perform`, the savepoint is rolled back before `run`
|
|
2217
|
+
catches the error and returns `false`. This eliminates the most common
|
|
2218
|
+
reason to reach for `with_rollback_on_exception`.
|
|
2219
2219
|
|
|
2220
2220
|
```ruby
|
|
2221
|
-
class Operations::User::
|
|
2221
|
+
class Operations::User::Create < RailsOps::Operation::Model::Create
|
|
2222
2222
|
def perform
|
|
2223
|
-
|
|
2224
|
-
|
|
2223
|
+
super # Saves the user
|
|
2224
|
+
something_else! # If this raises ActiveRecord::RecordInvalid, the
|
|
2225
|
+
# save above is rolled back automatically when the
|
|
2226
|
+
# operation is invoked through `run`.
|
|
2227
|
+
end
|
|
2228
|
+
end
|
|
2225
2229
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2230
|
+
# Caller (controller, job, etc.):
|
|
2231
|
+
ActiveRecord::Base.transaction do
|
|
2232
|
+
if Operations::User::Create.run(user: { name: 'Alice' })
|
|
2233
|
+
# success path
|
|
2234
|
+
else
|
|
2235
|
+
# validation error — partial writes have already been rolled back
|
|
2236
|
+
end
|
|
2237
|
+
end
|
|
2238
|
+
```
|
|
2231
2239
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2240
|
+
A savepoint is only created if a transaction is already open at the time
|
|
2241
|
+
of the call. Without an outer transaction (rake tasks, console sessions,
|
|
2242
|
+
some background jobs), `run` calls `run!` directly and behavior is
|
|
2243
|
+
unchanged: the caller is expected to open a transaction if it wants
|
|
2244
|
+
atomicity. `run!` itself is never wrapped — exceptions propagate naturally
|
|
2245
|
+
and the surrounding transaction (if any) rolls back.
|
|
2246
|
+
|
|
2247
|
+
`run_sub` (non-bang) benefits from the same protection transitively, since
|
|
2248
|
+
it delegates to `.run` on the sub-operation.
|
|
2249
|
+
|
|
2250
|
+
### Rollback on Exception (Non-Validation Errors)
|
|
2251
|
+
|
|
2252
|
+
The savepoint above only protects against **validation errors**
|
|
2253
|
+
(`RailsOps::Exceptions::ValidationFailed` and
|
|
2254
|
+
`ActiveRecord::RecordInvalid`). For other `StandardError` subclasses that
|
|
2255
|
+
should also trigger a rollback when `run` is used, the
|
|
2256
|
+
`with_rollback_on_exception` helper re-raises them as
|
|
2257
|
+
`RailsOps::Exceptions::RollbackRequired`, which is not part of
|
|
2258
|
+
`validation_errors` and therefore propagates up through `run` and rolls
|
|
2259
|
+
back the surrounding transaction:
|
|
2260
|
+
|
|
2261
|
+
```ruby
|
|
2262
|
+
class Operations::User::ComplexUpdate < RailsOps::Operation::Model::Update
|
|
2263
|
+
def perform
|
|
2264
|
+
super # Saves the user — validation errors here are handled by the
|
|
2265
|
+
# automatic savepoint in `run`.
|
|
2266
|
+
|
|
2267
|
+
with_rollback_on_exception do
|
|
2268
|
+
ExternalApi.call!(model) # raises a custom StandardError on failure;
|
|
2269
|
+
# converted into RollbackRequired so the
|
|
2270
|
+
# outer transaction rolls back even when
|
|
2271
|
+
# the operation is invoked via `run`.
|
|
2235
2272
|
end
|
|
2236
2273
|
end
|
|
2237
2274
|
end
|
|
@@ -2250,7 +2287,7 @@ end
|
|
|
2250
2287
|
```
|
|
2251
2288
|
|
|
2252
2289
|
**Important**: `with_rollback_on_exception` only works within an existing
|
|
2253
|
-
transaction. It doesn't create a transaction
|
|
2290
|
+
transaction. It doesn't create a transaction — it just ensures exceptions
|
|
2254
2291
|
cause rollback:
|
|
2255
2292
|
|
|
2256
2293
|
```ruby
|
|
@@ -2260,7 +2297,9 @@ class Operations::Order::Process < RailsOps::Operation::Model::Update
|
|
|
2260
2297
|
super # Order is saved and committed in its own transaction
|
|
2261
2298
|
|
|
2262
2299
|
with_rollback_on_exception do
|
|
2263
|
-
model
|
|
2300
|
+
ExternalApi.charge!(model) # Raises a custom StandardError on failure;
|
|
2301
|
+
# the order has already been committed and
|
|
2302
|
+
# cannot be rolled back.
|
|
2264
2303
|
end
|
|
2265
2304
|
end
|
|
2266
2305
|
end
|
|
@@ -2269,10 +2308,13 @@ end
|
|
|
2269
2308
|
class Operations::Order::Process < RailsOps::Operation::Model::Update
|
|
2270
2309
|
def perform
|
|
2271
2310
|
ActiveRecord::Base.transaction do
|
|
2272
|
-
super # Order is saved
|
|
2311
|
+
super # Order is saved within the surrounding transaction
|
|
2273
2312
|
|
|
2274
2313
|
with_rollback_on_exception do
|
|
2275
|
-
model
|
|
2314
|
+
ExternalApi.charge!(model) # Custom StandardError is converted into
|
|
2315
|
+
# RollbackRequired, which propagates
|
|
2316
|
+
# through `run` and rolls back the
|
|
2317
|
+
# transaction including `super`.
|
|
2276
2318
|
end
|
|
2277
2319
|
end
|
|
2278
2320
|
end
|
|
@@ -2322,9 +2364,13 @@ complete.
|
|
|
2322
2364
|
|
|
2323
2365
|
### Important Notes on Transactions
|
|
2324
2366
|
|
|
2325
|
-
1. **Validation Errors**: When
|
|
2326
|
-
|
|
2327
|
-
|
|
2367
|
+
1. **Validation Errors**: When `run` (without bang) is called inside an
|
|
2368
|
+
open transaction, RailsOps wraps the operation in a SAVEPOINT so that
|
|
2369
|
+
any partial writes are rolled back before the caught validation error
|
|
2370
|
+
is converted into a `false` return value. `run_sub` benefits from the
|
|
2371
|
+
same protection. Use `run!` / `run_sub!` when you want validation
|
|
2372
|
+
errors to propagate and roll back the surrounding transaction
|
|
2373
|
+
directly.
|
|
2328
2374
|
|
|
2329
2375
|
2. **External Services**: Be careful when calling external services within
|
|
2330
2376
|
transactions. Long-running external calls can cause database locks:
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.8.0
|
data/lib/rails_ops/operation.rb
CHANGED
|
@@ -14,6 +14,12 @@ class RailsOps::Operation
|
|
|
14
14
|
Symbol
|
|
15
15
|
].freeze
|
|
16
16
|
|
|
17
|
+
# Tracks which call sites have already emitted the deprecation warning
|
|
18
|
+
# for {with_rollback_on_exception}. Keyed by `"<path>:<lineno>"` so the
|
|
19
|
+
# warning fires at most once per call location across the process,
|
|
20
|
+
# avoiding log spam in long-running servers and per-request hot paths.
|
|
21
|
+
WITH_ROLLBACK_DEPRECATION_SEEN = Concurrent::Map.new
|
|
22
|
+
|
|
17
23
|
attr_reader :params
|
|
18
24
|
attr_reader :context
|
|
19
25
|
|
|
@@ -102,8 +108,26 @@ class RailsOps::Operation
|
|
|
102
108
|
|
|
103
109
|
# Runs the operation using {run!} but rescues certain exceptions. Returns
|
|
104
110
|
# `true` on success, otherwise `false`.
|
|
111
|
+
#
|
|
112
|
+
# If a database transaction is already open when {run} is called, the call
|
|
113
|
+
# to {run!} is wrapped in a savepoint via
|
|
114
|
+
# `ActiveRecord::Base.transaction(requires_new: true)`. This ensures that
|
|
115
|
+
# any database writes performed by the operation are rolled back if a
|
|
116
|
+
# validation error is raised, even though that error is then caught here
|
|
117
|
+
# and converted into a `false` return value. This eliminates the most
|
|
118
|
+
# common reason for using {with_rollback_on_exception}.
|
|
119
|
+
#
|
|
120
|
+
# When no transaction is open, behavior is identical to calling {run!}
|
|
121
|
+
# directly: the caller is responsible for atomicity.
|
|
105
122
|
def run
|
|
106
|
-
|
|
123
|
+
if ActiveRecord::Base.connection.transaction_open?
|
|
124
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
|
125
|
+
run!
|
|
126
|
+
end
|
|
127
|
+
else
|
|
128
|
+
run!
|
|
129
|
+
end
|
|
130
|
+
|
|
107
131
|
return true
|
|
108
132
|
rescue *validation_errors
|
|
109
133
|
return false
|
|
@@ -186,44 +210,37 @@ class RailsOps::Operation
|
|
|
186
210
|
end
|
|
187
211
|
end
|
|
188
212
|
|
|
189
|
-
# Yields the given block and rethrows any possible
|
|
213
|
+
# Yields the given block and rethrows any possible `StandardError` as a
|
|
190
214
|
# {RailsOps::Exceptions::RollbackRequired} exception.
|
|
191
215
|
#
|
|
192
|
-
#
|
|
216
|
+
# @deprecated Since 1.8.0, validation errors raised inside {run} no
|
|
217
|
+
# longer leak partial database writes: {run} wraps the call to
|
|
218
|
+
# {run!} in a SAVEPOINT whenever an outer transaction is open, so
|
|
219
|
+
# any prior `model.save!` is rolled back automatically before
|
|
220
|
+
# {run} returns `false`. This helper is therefore obsolete for
|
|
221
|
+
# the common "save then do more work" pattern and will be
|
|
222
|
+
# removed in RailsOps 2.0.
|
|
193
223
|
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
#
|
|
224
|
+
# To convert a non-validation `StandardError` into a rollback
|
|
225
|
+
# signal that escapes {run}'s rescue, raise
|
|
226
|
+
# {RailsOps::Exceptions::RollbackRequired} directly, e.g.
|
|
227
|
+
# `fail RailsOps::Exceptions::RollbackRequired, e, e.backtrace`.
|
|
197
228
|
#
|
|
198
|
-
#
|
|
199
|
-
# model.save! # Throws validation error
|
|
200
|
-
# end
|
|
201
|
-
# end
|
|
202
|
-
#
|
|
203
|
-
# User::Create.run(user: { some: :values })
|
|
204
|
-
#
|
|
205
|
-
# Since this operation is run without the bang method, validation errors are
|
|
206
|
-
# caught and won't result in the transaction beeing rolled back. However, the
|
|
207
|
-
# `super` call already saved the user while the exception happens only at
|
|
208
|
-
# the manual call to `model.save!`. Thus the user will still be in the DB,
|
|
209
|
-
# despite the fact that the second update didn't run.
|
|
210
|
-
#
|
|
211
|
-
# The correct example would therefore be:
|
|
212
|
-
#
|
|
213
|
-
# class User::Create < RailsOps::Operation::Model::Create
|
|
214
|
-
# def perform
|
|
215
|
-
# super # Saves the user
|
|
216
|
-
#
|
|
217
|
-
# with_rollback_on_exception do
|
|
218
|
-
# model.some_field = 'some value'
|
|
219
|
-
# model.save! # Throws validation error
|
|
220
|
-
# end
|
|
221
|
-
# end
|
|
222
|
-
# end
|
|
223
|
-
#
|
|
224
|
-
# This method is one possible solution for issue #28535. There might be a more
|
|
225
|
-
# elegant and transparent approach as explained in the issue.
|
|
229
|
+
# Originally introduced for issue #28535.
|
|
226
230
|
def with_rollback_on_exception(&_block)
|
|
231
|
+
location = caller_locations(1, 1)&.first
|
|
232
|
+
location_key = location && "#{location.path}:#{location.lineno}"
|
|
233
|
+
if location_key.nil? || WITH_ROLLBACK_DEPRECATION_SEEN.put_if_absent(location_key, true).nil?
|
|
234
|
+
RailsOps.deprecator.warn(
|
|
235
|
+
'`with_rollback_on_exception` is deprecated and will be removed ' \
|
|
236
|
+
'in RailsOps 2.0. Validation errors raised inside `run` are now ' \
|
|
237
|
+
'rolled back automatically via a SAVEPOINT, so this helper is no ' \
|
|
238
|
+
'longer required for the common "save then do more work" pattern. ' \
|
|
239
|
+
'For non-validation errors that should trigger a rollback, raise ' \
|
|
240
|
+
'`RailsOps::Exceptions::RollbackRequired` directly.',
|
|
241
|
+
caller_locations(1)
|
|
242
|
+
)
|
|
243
|
+
end
|
|
227
244
|
yield
|
|
228
245
|
rescue StandardError => e
|
|
229
246
|
fail RailsOps::Exceptions::RollbackRequired, e, e.backtrace
|
data/lib/rails_ops/railtie.rb
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
module RailsOps
|
|
2
2
|
# @private
|
|
3
3
|
class Railtie < Rails::Railtie
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
# Register the deprecator early so that app-level deprecation config
|
|
5
|
+
# (`config.active_support.deprecation`,
|
|
6
|
+
# `config.active_support.report_deprecations`, …) is applied to it.
|
|
7
|
+
initializer 'rails_ops.deprecator', before: :load_environment_config do |app|
|
|
8
8
|
if app.respond_to?(:deprecators)
|
|
9
9
|
app.deprecators[:rails_ops] = RailsOps.deprecator
|
|
10
10
|
end
|
|
11
|
+
end
|
|
11
12
|
|
|
13
|
+
initializer 'rails_ops' do
|
|
12
14
|
# ---------------------------------------------------------------
|
|
13
15
|
# Load hookup config eagerly at application startup unless
|
|
14
16
|
# in development mode.
|
data/lib/rails_ops.rb
CHANGED
data/rails_ops.gemspec
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: rails_ops 1.
|
|
2
|
+
# stub: rails_ops 1.8.0 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "rails_ops".freeze
|
|
6
|
-
s.version = "1.
|
|
6
|
+
s.version = "1.8.0"
|
|
7
7
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.require_paths = ["lib".freeze]
|
|
10
10
|
s.authors = ["Sitrox".freeze]
|
|
11
|
-
s.date = "2026-
|
|
12
|
-
s.files = [".github/workflows/rubocop.yml".freeze, ".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, "Appraisals".freeze, "CHANGELOG.md".freeze, "CLAUDE.md".freeze, "Gemfile".freeze, "Gemfile.lock".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "gemfiles/rails_6.0.gemfile".freeze, "gemfiles/rails_6.1.gemfile".freeze, "gemfiles/rails_7.0.gemfile".freeze, "gemfiles/rails_7.1.gemfile".freeze, "gemfiles/rails_7.2.gemfile".freeze, "gemfiles/rails_8.0.gemfile".freeze, "lib/generators/operation/USAGE".freeze, "lib/generators/operation/operation_generator.rb".freeze, "lib/generators/operation/templates/controller.erb".freeze, "lib/generators/operation/templates/controller_wrapper.erb".freeze, "lib/generators/operation/templates/create.erb".freeze, "lib/generators/operation/templates/destroy.erb".freeze, "lib/generators/operation/templates/load.erb".freeze, "lib/generators/operation/templates/update.erb".freeze, "lib/generators/operation/templates/view.erb".freeze, "lib/rails_ops.rb".freeze, "lib/rails_ops/authorization_backend/abstract.rb".freeze, "lib/rails_ops/authorization_backend/can_can_can.rb".freeze, "lib/rails_ops/configuration.rb".freeze, "lib/rails_ops/context.rb".freeze, "lib/rails_ops/controller_mixin.rb".freeze, "lib/rails_ops/exceptions.rb".freeze, "lib/rails_ops/hooked_job.rb".freeze, "lib/rails_ops/hookup.rb".freeze, "lib/rails_ops/hookup/dsl.rb".freeze, "lib/rails_ops/hookup/dsl_validator.rb".freeze, "lib/rails_ops/hookup/hook.rb".freeze, "lib/rails_ops/log_subscriber.rb".freeze, "lib/rails_ops/mixins.rb".freeze, "lib/rails_ops/mixins/authorization.rb".freeze, "lib/rails_ops/mixins/log_settings.rb".freeze, "lib/rails_ops/mixins/model.rb".freeze, "lib/rails_ops/mixins/model/authorization.rb".freeze, "lib/rails_ops/mixins/model/nesting.rb".freeze, "lib/rails_ops/mixins/param_authorization.rb".freeze, "lib/rails_ops/mixins/policies.rb".freeze, "lib/rails_ops/mixins/require_context.rb".freeze, "lib/rails_ops/mixins/routes.rb".freeze, "lib/rails_ops/mixins/schema_validation.rb".freeze, "lib/rails_ops/mixins/sub_ops.rb".freeze, "lib/rails_ops/model_mixins.rb".freeze, "lib/rails_ops/model_mixins/ar_extension.rb".freeze, "lib/rails_ops/model_mixins/marshalling.rb".freeze, "lib/rails_ops/model_mixins/parent_op.rb".freeze, "lib/rails_ops/model_mixins/sti_fixes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes/virtual_column_wrapper.rb".freeze, "lib/rails_ops/model_mixins/virtual_has_one.rb".freeze, "lib/rails_ops/model_mixins/virtual_model_name.rb".freeze, "lib/rails_ops/operation.rb".freeze, "lib/rails_ops/operation/model.rb".freeze, "lib/rails_ops/operation/model/create.rb".freeze, "lib/rails_ops/operation/model/destroy.rb".freeze, "lib/rails_ops/operation/model/load.rb".freeze, "lib/rails_ops/operation/model/update.rb".freeze, "lib/rails_ops/profiler.rb".freeze, "lib/rails_ops/profiler/node.rb".freeze, "lib/rails_ops/railtie.rb".freeze, "lib/rails_ops/scoped_env.rb".freeze, "lib/rails_ops/virtual_model.rb".freeze, "rails_ops.gemspec".freeze, "test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/controllers/group_controller.rb".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/ability.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/computer.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/cpu.rb".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/mainboard.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/hookup.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/hookup_test.rb".freeze, "test/unit/rails_ops/mixins/controller_test.rb".freeze, "test/unit/rails_ops/mixins/model/deep_nesting_test.rb".freeze, "test/unit/rails_ops/mixins/model/marshalling_test.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/auth_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/destroy_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze, "test/unit/rails_ops/profiler_test.rb".freeze]
|
|
11
|
+
s.date = "2026-05-07"
|
|
12
|
+
s.files = [".github/workflows/rubocop.yml".freeze, ".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, "Appraisals".freeze, "CHANGELOG.md".freeze, "CLAUDE.md".freeze, "Gemfile".freeze, "Gemfile.lock".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "gemfiles/rails_6.0.gemfile".freeze, "gemfiles/rails_6.1.gemfile".freeze, "gemfiles/rails_7.0.gemfile".freeze, "gemfiles/rails_7.1.gemfile".freeze, "gemfiles/rails_7.2.gemfile".freeze, "gemfiles/rails_8.0.gemfile".freeze, "lib/generators/operation/USAGE".freeze, "lib/generators/operation/operation_generator.rb".freeze, "lib/generators/operation/templates/controller.erb".freeze, "lib/generators/operation/templates/controller_wrapper.erb".freeze, "lib/generators/operation/templates/create.erb".freeze, "lib/generators/operation/templates/destroy.erb".freeze, "lib/generators/operation/templates/load.erb".freeze, "lib/generators/operation/templates/update.erb".freeze, "lib/generators/operation/templates/view.erb".freeze, "lib/rails_ops.rb".freeze, "lib/rails_ops/authorization_backend/abstract.rb".freeze, "lib/rails_ops/authorization_backend/can_can_can.rb".freeze, "lib/rails_ops/configuration.rb".freeze, "lib/rails_ops/context.rb".freeze, "lib/rails_ops/controller_mixin.rb".freeze, "lib/rails_ops/exceptions.rb".freeze, "lib/rails_ops/hooked_job.rb".freeze, "lib/rails_ops/hookup.rb".freeze, "lib/rails_ops/hookup/dsl.rb".freeze, "lib/rails_ops/hookup/dsl_validator.rb".freeze, "lib/rails_ops/hookup/hook.rb".freeze, "lib/rails_ops/log_subscriber.rb".freeze, "lib/rails_ops/mixins.rb".freeze, "lib/rails_ops/mixins/authorization.rb".freeze, "lib/rails_ops/mixins/log_settings.rb".freeze, "lib/rails_ops/mixins/model.rb".freeze, "lib/rails_ops/mixins/model/authorization.rb".freeze, "lib/rails_ops/mixins/model/nesting.rb".freeze, "lib/rails_ops/mixins/param_authorization.rb".freeze, "lib/rails_ops/mixins/policies.rb".freeze, "lib/rails_ops/mixins/require_context.rb".freeze, "lib/rails_ops/mixins/routes.rb".freeze, "lib/rails_ops/mixins/schema_validation.rb".freeze, "lib/rails_ops/mixins/sub_ops.rb".freeze, "lib/rails_ops/model_mixins.rb".freeze, "lib/rails_ops/model_mixins/ar_extension.rb".freeze, "lib/rails_ops/model_mixins/marshalling.rb".freeze, "lib/rails_ops/model_mixins/parent_op.rb".freeze, "lib/rails_ops/model_mixins/sti_fixes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes/virtual_column_wrapper.rb".freeze, "lib/rails_ops/model_mixins/virtual_has_one.rb".freeze, "lib/rails_ops/model_mixins/virtual_model_name.rb".freeze, "lib/rails_ops/operation.rb".freeze, "lib/rails_ops/operation/model.rb".freeze, "lib/rails_ops/operation/model/create.rb".freeze, "lib/rails_ops/operation/model/destroy.rb".freeze, "lib/rails_ops/operation/model/load.rb".freeze, "lib/rails_ops/operation/model/update.rb".freeze, "lib/rails_ops/profiler.rb".freeze, "lib/rails_ops/profiler/node.rb".freeze, "lib/rails_ops/railtie.rb".freeze, "lib/rails_ops/scoped_env.rb".freeze, "lib/rails_ops/virtual_model.rb".freeze, "rails_ops.gemspec".freeze, "test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/controllers/group_controller.rb".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/ability.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/computer.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/cpu.rb".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/mainboard.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/hookup.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/hookup_test.rb".freeze, "test/unit/rails_ops/mixins/controller_test.rb".freeze, "test/unit/rails_ops/mixins/model/deep_nesting_test.rb".freeze, "test/unit/rails_ops/mixins/model/marshalling_test.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/auth_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/destroy_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze, "test/unit/rails_ops/profiler_test.rb".freeze, "test/unit/rails_ops/railtie_test.rb".freeze]
|
|
13
13
|
s.homepage = "https://github.com/sitrox/rails_ops".freeze
|
|
14
14
|
s.licenses = ["MIT".freeze]
|
|
15
15
|
s.rubygems_version = "3.4.6".freeze
|
|
16
16
|
s.summary = "An operations service layer for rails projects.".freeze
|
|
17
|
-
s.test_files = ["test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/controllers/group_controller.rb".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/ability.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/computer.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/cpu.rb".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/mainboard.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/hookup.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/hookup_test.rb".freeze, "test/unit/rails_ops/mixins/controller_test.rb".freeze, "test/unit/rails_ops/mixins/model/deep_nesting_test.rb".freeze, "test/unit/rails_ops/mixins/model/marshalling_test.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/auth_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/destroy_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze, "test/unit/rails_ops/profiler_test.rb".freeze]
|
|
17
|
+
s.test_files = ["test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/controllers/group_controller.rb".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/ability.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/computer.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/cpu.rb".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/mainboard.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/hookup.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/hookup_test.rb".freeze, "test/unit/rails_ops/mixins/controller_test.rb".freeze, "test/unit/rails_ops/mixins/model/deep_nesting_test.rb".freeze, "test/unit/rails_ops/mixins/model/marshalling_test.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/auth_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/destroy_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze, "test/unit/rails_ops/profiler_test.rb".freeze, "test/unit/rails_ops/railtie_test.rb".freeze]
|
|
18
18
|
|
|
19
19
|
s.specification_version = 4
|
|
20
20
|
|
|
@@ -193,8 +193,168 @@ class RailsOps::OperationTest < ActiveSupport::TestCase
|
|
|
193
193
|
end
|
|
194
194
|
end
|
|
195
195
|
end.new
|
|
196
|
-
|
|
196
|
+
RailsOps.deprecator.silence do
|
|
197
|
+
assert_raises RailsOps::Exceptions::RollbackRequired do
|
|
198
|
+
op.run
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_with_rollback_on_exception_emits_deprecation_warning
|
|
204
|
+
# Use a fresh call site to bypass the per-process deduplication cache
|
|
205
|
+
# used by `with_rollback_on_exception`.
|
|
206
|
+
op = Class.new(RailsOps::Operation) do
|
|
207
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
208
|
+
def perform
|
|
209
|
+
with_rollback_on_exception do
|
|
210
|
+
# No-op; we only care about the deprecation warning being
|
|
211
|
+
# emitted on entry.
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
RUBY
|
|
215
|
+
end.new
|
|
216
|
+
|
|
217
|
+
messages = []
|
|
218
|
+
previous_behavior = RailsOps.deprecator.behavior
|
|
219
|
+
RailsOps.deprecator.behavior = ->(msg, _callstack, _name, _gem) { messages << msg }
|
|
220
|
+
begin
|
|
197
221
|
op.run
|
|
222
|
+
ensure
|
|
223
|
+
RailsOps.deprecator.behavior = previous_behavior
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
assert_match(/with_rollback_on_exception.*deprecated/, messages.join("\n"))
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def test_with_rollback_on_exception_deduplicates_per_call_site
|
|
230
|
+
op = Class.new(RailsOps::Operation) do
|
|
231
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
232
|
+
def perform
|
|
233
|
+
with_rollback_on_exception do
|
|
234
|
+
# No-op
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
RUBY
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
messages = []
|
|
241
|
+
previous_behavior = RailsOps.deprecator.behavior
|
|
242
|
+
RailsOps.deprecator.behavior = ->(msg, _callstack, _name, _gem) { messages << msg }
|
|
243
|
+
begin
|
|
244
|
+
op.new.run
|
|
245
|
+
op.new.run
|
|
246
|
+
op.new.run
|
|
247
|
+
ensure
|
|
248
|
+
RailsOps.deprecator.behavior = previous_behavior
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
assert_equal 1, messages.size, 'expected the warning to fire once per call site'
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# When `run` is called inside an outer transaction and the operation does a
|
|
255
|
+
# save followed by a validation error, the save must be rolled back even
|
|
256
|
+
# though `run` swallows the error and returns `false`. Without the savepoint
|
|
257
|
+
# wrapping inside `run`, the partial write would leak into the outer
|
|
258
|
+
# transaction.
|
|
259
|
+
def test_run_rolls_back_partial_writes_when_inside_outer_transaction
|
|
260
|
+
op_class = Class.new(RailsOps::Operation::Model::Create) do
|
|
261
|
+
model Group
|
|
262
|
+
|
|
263
|
+
def perform
|
|
264
|
+
super
|
|
265
|
+
fail RailsOps::Exceptions::ValidationFailed, 'post-save check failed'
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
ActiveRecord::Base.transaction do
|
|
270
|
+
count_before = Group.count
|
|
271
|
+
refute op_class.run(group: { name: 'partial', color: 'red' })
|
|
272
|
+
assert_equal count_before, Group.count, 'expected save to be rolled back'
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# `run!` keeps its existing behavior: validation errors propagate, the
|
|
277
|
+
# savepoint logic in `run` is not on the call path.
|
|
278
|
+
def test_run_bang_still_raises_inside_transaction
|
|
279
|
+
op_class = Class.new(RailsOps::Operation::Model::Create) do
|
|
280
|
+
model Group
|
|
281
|
+
|
|
282
|
+
def perform
|
|
283
|
+
super
|
|
284
|
+
fail RailsOps::Exceptions::ValidationFailed, 'post-save check failed'
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
assert_raises RailsOps::Exceptions::ValidationFailed do
|
|
289
|
+
ActiveRecord::Base.transaction do
|
|
290
|
+
op_class.run!(group: { name: 'partial', color: 'red' })
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Successful `run` calls inside a transaction still commit the model save.
|
|
296
|
+
def test_run_persists_model_on_success_inside_transaction
|
|
297
|
+
op_class = Class.new(RailsOps::Operation::Model::Create) do
|
|
298
|
+
model Group
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
ActiveRecord::Base.transaction do
|
|
302
|
+
count_before = Group.count
|
|
303
|
+
assert op_class.run(group: { name: 'fine', color: 'green' })
|
|
304
|
+
assert_equal count_before + 1, Group.count
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# `run_sub` (non-bang) delegates to `run`, so a child operation that saves
|
|
309
|
+
# and then fails validation must not leak partial state into the parent
|
|
310
|
+
# transaction.
|
|
311
|
+
def test_run_sub_rolls_back_partial_writes_in_child_op
|
|
312
|
+
child_op = Class.new(RailsOps::Operation::Model::Create) do
|
|
313
|
+
model Group
|
|
314
|
+
|
|
315
|
+
def perform
|
|
316
|
+
super
|
|
317
|
+
fail RailsOps::Exceptions::ValidationFailed, 'post-save check failed'
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
parent_op = Class.new(RailsOps::Operation) do
|
|
322
|
+
attr_reader :sub_result
|
|
323
|
+
|
|
324
|
+
define_method(:perform) do
|
|
325
|
+
@sub_result = run_sub(child_op, group: { name: 'child', color: 'blue' })
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
ActiveRecord::Base.transaction do
|
|
330
|
+
count_before = Group.count
|
|
331
|
+
op = parent_op.new
|
|
332
|
+
op.run!
|
|
333
|
+
refute op.sub_result, 'expected run_sub to return false on child validation error'
|
|
334
|
+
assert_equal count_before, Group.count, 'expected child op save to be rolled back'
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# The same protection applies when the failing exception is
|
|
339
|
+
# `ActiveRecord::RecordInvalid` (raised by `model.save!` on a real
|
|
340
|
+
# model validation failure), which is the more common production case.
|
|
341
|
+
def test_run_rolls_back_partial_writes_on_active_record_record_invalid
|
|
342
|
+
op_class = Class.new(RailsOps::Operation::Model::Create) do
|
|
343
|
+
model Group
|
|
344
|
+
|
|
345
|
+
def perform
|
|
346
|
+
super
|
|
347
|
+
# Trigger a real ActiveRecord::RecordInvalid on a separate record.
|
|
348
|
+
invalid = Group.new
|
|
349
|
+
invalid.errors.add(:base, 'invalid')
|
|
350
|
+
fail ActiveRecord::RecordInvalid, invalid
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
ActiveRecord::Base.transaction do
|
|
355
|
+
count_before = Group.count
|
|
356
|
+
refute op_class.run(group: { name: 'partial', color: 'red' })
|
|
357
|
+
assert_equal count_before, Group.count, 'expected save to be rolled back'
|
|
198
358
|
end
|
|
199
359
|
end
|
|
200
360
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class RailsOps::RailtieTest < ActiveSupport::TestCase
|
|
4
|
+
include TestHelper
|
|
5
|
+
|
|
6
|
+
def test_deprecator_is_registered_with_rails
|
|
7
|
+
skip 'Rails.application.deprecators is unavailable on this Rails version' \
|
|
8
|
+
unless Rails.application.respond_to?(:deprecators)
|
|
9
|
+
|
|
10
|
+
registered = Rails.application.deprecators[:rails_ops]
|
|
11
|
+
assert_same RailsOps.deprecator, registered,
|
|
12
|
+
'expected RailsOps.deprecator to be registered as Rails.application.deprecators[:rails_ops]'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_deprecator_honors_silenced_flag_set_via_app_config
|
|
16
|
+
skip 'Rails.application.deprecators is unavailable on this Rails version' \
|
|
17
|
+
unless Rails.application.respond_to?(:deprecators)
|
|
18
|
+
|
|
19
|
+
previous_silenced = RailsOps.deprecator.silenced
|
|
20
|
+
begin
|
|
21
|
+
Rails.application.deprecators.silenced = true
|
|
22
|
+
assert RailsOps.deprecator.silenced,
|
|
23
|
+
'expected RailsOps.deprecator to be silenced when ' \
|
|
24
|
+
'Rails.application.deprecators.silenced = true'
|
|
25
|
+
ensure
|
|
26
|
+
Rails.application.deprecators.silenced = previous_silenced
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_ops
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sitrox
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-05-07 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: active_type
|
|
@@ -258,6 +258,7 @@ files:
|
|
|
258
258
|
- test/unit/rails_ops/operation/update_lazy_auth_test.rb
|
|
259
259
|
- test/unit/rails_ops/operation_test.rb
|
|
260
260
|
- test/unit/rails_ops/profiler_test.rb
|
|
261
|
+
- test/unit/rails_ops/railtie_test.rb
|
|
261
262
|
homepage: https://github.com/sitrox/rails_ops
|
|
262
263
|
licenses:
|
|
263
264
|
- MIT
|
|
@@ -374,3 +375,4 @@ test_files:
|
|
|
374
375
|
- test/unit/rails_ops/operation/update_lazy_auth_test.rb
|
|
375
376
|
- test/unit/rails_ops/operation_test.rb
|
|
376
377
|
- test/unit/rails_ops/profiler_test.rb
|
|
378
|
+
- test/unit/rails_ops/railtie_test.rb
|