console_think_twice 0.1.0 → 0.3.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 +45 -2
- data/README.md +50 -5
- data/lib/console_think_twice/collection_guard.rb +17 -0
- data/lib/console_think_twice/configuration.rb +35 -2
- data/lib/console_think_twice/version.rb +1 -1
- data/lib/console_think_twice.rb +84 -12
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 236302689b1695ddb978506ea4a90c043c8cf03edb179b35ff053655c275297a
|
|
4
|
+
data.tar.gz: 84aab44ed6323c6fbbaa6cc1b2ef6872320335cd4cb8dbf6e86efb03f509a879
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 979ca9fbfa8533b0945653fc1207de763b4a80ddaac372acf37201d2ed0f4074c3686040d670d6b67ac3873ee408bf954935772e9b17e389e6a832a8cdd46817
|
|
7
|
+
data.tar.gz: 0e8e48c246db11faf5ab091e623789ef4b06253ddcf8e77bc8555df0e96310cb3e0835a79f849f8a565f73a65950617546e46f8ee91bec37e1ca29ad0e458580
|
data/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [0.
|
|
5
|
+
## [0.3.0] - 2026-09-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `config.ignored_classes` leaves the classes you name unguarded, for tables that are
|
|
10
|
+
cleared as a matter of routine — `SolidCache::Entry`, say. Takes names or classes,
|
|
11
|
+
covers subclasses, and compares names rather than resolving them, so a class that is not
|
|
12
|
+
loaded, or not in this app at all, can be listed.
|
|
13
|
+
|
|
14
|
+
## [0.2.0] - 2026-08-26
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Active Record's own destroys no longer prompt. A record marked `_destroy` by
|
|
19
|
+
`accepts_nested_attributes_for`, and the record a `has_one` assignment discards, were
|
|
20
|
+
asked about as though they had been typed — and asked from inside the enclosing call's
|
|
21
|
+
transaction, holding it and its locks open until somebody answered. Calls you type are
|
|
22
|
+
still guarded, including inside `transaction { ... }` and in a `--sandbox` console.
|
|
23
|
+
- `collection.destroy(record, other)` asks once for the whole call rather than once per
|
|
24
|
+
record, and asks before the transaction is opened rather than inside it.
|
|
25
|
+
- A blank `config.label` is left out of the prompt instead of printed. `false` rendered as
|
|
26
|
+
`"... Author #1false."`, and there was no way to turn the label off under Rails, where an
|
|
27
|
+
unset label falls back to `Rails.env`.
|
|
28
|
+
- `config.enabled` takes effect when it changes, rather than only at `install!` time.
|
|
29
|
+
- A forced call no longer counts the records it was told not to ask about, so
|
|
30
|
+
`destroy_all(force: true)` on a large table skips a needless `SELECT COUNT(*)`.
|
|
31
|
+
- A confirmed call stays confirmed inside a fiber. Suppression used `Thread.current[]`,
|
|
32
|
+
which is fiber-local, so a destroy reached through an Enumerator prompted again.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- `collection.delete(record)` is no longer guarded. It was only ever caught in passing, by
|
|
37
|
+
the guard on the record destroy underneath it, and what it means depends on the
|
|
38
|
+
association: without `dependent: :destroy` it unlinks the record rather than destroying
|
|
39
|
+
it, so the prompt could describe a permanent delete that was not one.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- `ConsoleThinkTwice.enable!`, the counterpart to `disable!`.
|
|
44
|
+
- CI runs the suite against every supported Active Record, not only the newest.
|
|
45
|
+
|
|
46
|
+
## [0.1.0] - 2026-08-26
|
|
6
47
|
|
|
7
48
|
Initial release.
|
|
8
49
|
|
|
@@ -17,5 +58,7 @@ Initial release.
|
|
|
17
58
|
- Non-interactive input refuses rather than assuming yes.
|
|
18
59
|
- Configurable via `ConsoleThinkTwice.configure`, or turned off with `CONSOLE_THINK_TWICE=0`.
|
|
19
60
|
|
|
20
|
-
[Unreleased]: https://github.com/agomezcampero/console-think-twice/compare/v0.
|
|
61
|
+
[Unreleased]: https://github.com/agomezcampero/console-think-twice/compare/v0.3.0...HEAD
|
|
62
|
+
[0.3.0]: https://github.com/agomezcampero/console-think-twice/compare/v0.2.0...v0.3.0
|
|
63
|
+
[0.2.0]: https://github.com/agomezcampero/console-think-twice/compare/v0.1.0...v0.2.0
|
|
21
64
|
[0.1.0]: https://github.com/agomezcampero/console-think-twice/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# console_think_twice
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/console_think_twice)
|
|
3
4
|
[](https://github.com/agomezcampero/console-think-twice/actions/workflows/ci.yml)
|
|
4
5
|
|
|
5
6
|
Confirm before you destroy. `console_think_twice` intercepts destructive Active Record
|
|
@@ -27,14 +28,16 @@ Confirm? (y/N) y
|
|
|
27
28
|
|
|
28
29
|
## Installation
|
|
29
30
|
|
|
31
|
+
Add it to your Gemfile:
|
|
32
|
+
|
|
30
33
|
```ruby
|
|
31
34
|
gem "console_think_twice"
|
|
32
35
|
```
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
Then `bundle install`. Or:
|
|
35
38
|
|
|
36
|
-
```
|
|
37
|
-
|
|
39
|
+
```bash
|
|
40
|
+
bundle add console_think_twice
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
In a Rails app that is all — a railtie installs the guard from the `console` hook, which
|
|
@@ -51,6 +54,7 @@ Outside Rails, call `ConsoleThinkTwice.install!` once your models are loaded.
|
|
|
51
54
|
| `record.delete` | `This will permanently delete, skipping callbacks, User #1.` |
|
|
52
55
|
| `Model.destroy_all`, `relation.destroy_all`, `company.users.destroy_all` | `This will permanently destroy 200 Users.` |
|
|
53
56
|
| `Model.delete_all`, `relation.delete_all` | `This will permanently delete, skipping callbacks, 200 Users.` |
|
|
57
|
+
| `company.users.destroy(user, other)` | `This will permanently destroy 2 Users.` |
|
|
54
58
|
|
|
55
59
|
`Model.destroy(id)`, `destroy_by` and `delete_by` route through those methods, so they are
|
|
56
60
|
covered too.
|
|
@@ -66,6 +70,22 @@ A `dependent: :destroy` cascade destroys child records, and `destroy_all` destro
|
|
|
66
70
|
records one at a time — both would otherwise prompt again for every record. Nested prompts
|
|
67
71
|
are suppressed for the duration of a confirmed call, so you are asked exactly once.
|
|
68
72
|
|
|
73
|
+
### What is not guarded
|
|
74
|
+
|
|
75
|
+
`update_all`, `upsert_all` and anything run through `connection.execute` are untouched, as is
|
|
76
|
+
`collection.delete(record)` — which unlinks the record rather than destroying it, unless the
|
|
77
|
+
association says `dependent: :destroy`.
|
|
78
|
+
|
|
79
|
+
Classes listed in `config.ignored_classes` are not guarded either — see below.
|
|
80
|
+
|
|
81
|
+
Active Record also destroys records itself, as one step of a call that is about something
|
|
82
|
+
else: a record marked `_destroy` by `accepts_nested_attributes_for`, or the old record a
|
|
83
|
+
`has_one` assignment discards. Those do not prompt. They are already covered by whatever you
|
|
84
|
+
typed to set them off, and they run inside that call's transaction — stopping to ask there
|
|
85
|
+
would hold the transaction, and its locks, open until somebody answered. A destroy you type
|
|
86
|
+
yourself is always asked about, including inside `transaction { ... }` and in a
|
|
87
|
+
`rails console --sandbox`.
|
|
88
|
+
|
|
69
89
|
### Nobody to ask
|
|
70
90
|
|
|
71
91
|
When the input stream is not a terminal — piped input, a script fed to `rails console` —
|
|
@@ -77,7 +97,9 @@ there is no one to answer, so the guard refuses the call instead of assuming yes
|
|
|
77
97
|
```ruby
|
|
78
98
|
ConsoleThinkTwice.configure do |config|
|
|
79
99
|
config.enabled = Rails.env.production? # default: true, unless CONSOLE_THINK_TWICE is 0/false/no/off
|
|
80
|
-
config.label = "production (europe)" # shown in the prompt; defaults to Rails.env
|
|
100
|
+
config.label = "production (europe)" # shown in the prompt; defaults to Rails.env,
|
|
101
|
+
# set nil or false to leave it out
|
|
102
|
+
config.ignored_classes = %w[SolidCache::Entry] # left unguarded; default: none
|
|
81
103
|
config.affirmative_answers = %w[y yes si] # default: %w[y yes]
|
|
82
104
|
config.interactive = true # default: auto-detected from config.input.tty?
|
|
83
105
|
config.input = $stdin
|
|
@@ -85,8 +107,24 @@ ConsoleThinkTwice.configure do |config|
|
|
|
85
107
|
end
|
|
86
108
|
```
|
|
87
109
|
|
|
110
|
+
### Classes to leave alone
|
|
111
|
+
|
|
112
|
+
Some tables are cleared as a matter of routine — job records, cache entries, whatever your
|
|
113
|
+
app treats as scratch. Being asked about those is noise, and noise teaches you to answer `y`
|
|
114
|
+
without reading. List them and the guard stays out of the way:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
config.ignored_classes = %w[SolidCache::Entry SolidQueue::Job]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Classes work as well as names, and a subclass of a listed class is ignored too. Names are
|
|
121
|
+
compared rather than resolved, so listing a class that this app does not have, or has not
|
|
122
|
+
autoloaded yet, is fine.
|
|
123
|
+
|
|
88
124
|
Set `CONSOLE_THINK_TWICE=0` in the environment to turn the guard off without touching
|
|
89
|
-
code. `ConsoleThinkTwice.disable!` turns it off for the rest of the current session
|
|
125
|
+
code. `ConsoleThinkTwice.disable!` turns it off for the rest of the current session and
|
|
126
|
+
`ConsoleThinkTwice.enable!` turns it back on; setting `config.enabled` takes effect straight
|
|
127
|
+
away too, without reinstalling.
|
|
90
128
|
|
|
91
129
|
## Development
|
|
92
130
|
|
|
@@ -97,6 +135,13 @@ bundle exec rspec
|
|
|
97
135
|
bundle exec standardrb
|
|
98
136
|
```
|
|
99
137
|
|
|
138
|
+
The guard reads Active Record's own call stack to tell your calls from its, so the suite is
|
|
139
|
+
run against every supported Active Record, not just the newest. Delete `Gemfile.lock`, then:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
ACTIVERECORD_VERSION=7.0 bundle install && ACTIVERECORD_VERSION=7.0 bundle exec rspec
|
|
143
|
+
```
|
|
144
|
+
|
|
100
145
|
## License
|
|
101
146
|
|
|
102
147
|
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleThinkTwice
|
|
4
|
+
# Guards the collection methods that name their records, rather than describing them with a
|
|
5
|
+
# scope. Prepended to CollectionProxy only: ActiveRecord::Relation has a `destroy` of its own
|
|
6
|
+
# that takes an id and means something different.
|
|
7
|
+
module CollectionGuard
|
|
8
|
+
# `books.destroy(a, b)` destroys the records it is handed one at a time. Asking here rather
|
|
9
|
+
# than once per record keeps it to a single prompt, and keeps the prompt outside the
|
|
10
|
+
# transaction Active Record opens to do the work.
|
|
11
|
+
def destroy(*records, force: false)
|
|
12
|
+
ConsoleThinkTwice.guard(self, action: :destroy, force: force, count: records.size) do
|
|
13
|
+
super(*records)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -21,6 +21,27 @@ module ConsoleThinkTwice
|
|
|
21
21
|
@output || $stdout
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# Classes the guard leaves alone. Bookkeeping tables that are cleared as a matter of
|
|
25
|
+
# routine — `SolidCache::Entry`, say — are noise to be asked about, and being asked
|
|
26
|
+
# about them teaches you to answer y without reading. Takes names or classes, and a
|
|
27
|
+
# subclass of a listed class is ignored too.
|
|
28
|
+
#
|
|
29
|
+
# Names are compared rather than resolved, so a class can be listed before it is
|
|
30
|
+
# autoloaded, or listed in an initializer shared by apps that do not all have it.
|
|
31
|
+
def ignored_classes=(classes)
|
|
32
|
+
@ignored_classes = Array(classes).map(&:to_s)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ignored_classes
|
|
36
|
+
@ignored_classes ||= []
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def ignores?(model)
|
|
40
|
+
return false if ignored_classes.empty?
|
|
41
|
+
|
|
42
|
+
inheritance_chain(model).any? { |name| ignored_classes.include?(name) }
|
|
43
|
+
end
|
|
44
|
+
|
|
24
45
|
def enabled?
|
|
25
46
|
return @enabled unless @enabled.nil?
|
|
26
47
|
|
|
@@ -35,11 +56,23 @@ module ConsoleThinkTwice
|
|
|
35
56
|
input.respond_to?(:tty?) && input.tty?
|
|
36
57
|
end
|
|
37
58
|
|
|
38
|
-
# Shown in the prompt to make the stakes obvious, e.g. "in production".
|
|
59
|
+
# Shown in the prompt to make the stakes obvious, e.g. "in production". Defaults to the
|
|
60
|
+
# Rails environment; assigning any value at all — nil and false included — wins over that,
|
|
61
|
+
# so the label can be turned off as well as changed.
|
|
39
62
|
def label
|
|
40
|
-
return @label
|
|
63
|
+
return @label if defined?(@label)
|
|
41
64
|
|
|
42
65
|
::Rails.env.to_s if defined?(::Rails) && ::Rails.respond_to?(:env)
|
|
43
66
|
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# The model's own name and the names of the models it inherits from, so that ignoring a
|
|
71
|
+
# class ignores its subclasses. Stops at Active Record itself, which every model shares.
|
|
72
|
+
def inheritance_chain(model)
|
|
73
|
+
Enumerator.produce(model, &:superclass)
|
|
74
|
+
.take_while { |klass| klass && klass != ::ActiveRecord::Base }
|
|
75
|
+
.map(&:name)
|
|
76
|
+
end
|
|
44
77
|
end
|
|
45
78
|
end
|
data/lib/console_think_twice.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "console_think_twice/version"
|
|
|
7
7
|
require_relative "console_think_twice/configuration"
|
|
8
8
|
require_relative "console_think_twice/record_guard"
|
|
9
9
|
require_relative "console_think_twice/relation_guard"
|
|
10
|
+
require_relative "console_think_twice/collection_guard"
|
|
10
11
|
|
|
11
12
|
# Confirmation prompts for destructive Active Record calls typed into a console.
|
|
12
13
|
#
|
|
@@ -24,6 +25,17 @@ module ConsoleThinkTwice
|
|
|
24
25
|
CASCADING_STRATEGIES = %i[destroy destroy_async delete_all].freeze
|
|
25
26
|
SUPPRESSION_KEY = :console_think_twice_suppressed
|
|
26
27
|
|
|
28
|
+
# Source files belonging to Rails itself. Matched on the path rather than resolved from
|
|
29
|
+
# Gem.loaded_specs so that a vendored or git-checkout Rails is recognised too.
|
|
30
|
+
RAILS_SOURCE = %r{/active_(?:record|support|model)/}
|
|
31
|
+
|
|
32
|
+
# The Active Record internals that destroy a record as one step of a larger operation:
|
|
33
|
+
# association bookkeeping, autosave, and nested attributes.
|
|
34
|
+
RAILS_INTERNAL_WORK = %r{/active_record/(?:associations/|autosave_association\.rb|nested_attributes\.rb)}
|
|
35
|
+
|
|
36
|
+
# This gem's own frames, skipped when looking for the caller of a guarded method.
|
|
37
|
+
GEM_SOURCE = __dir__
|
|
38
|
+
|
|
27
39
|
class << self
|
|
28
40
|
def configuration
|
|
29
41
|
@configuration ||= Configuration.new
|
|
@@ -38,8 +50,11 @@ module ConsoleThinkTwice
|
|
|
38
50
|
|
|
39
51
|
unless @installed
|
|
40
52
|
ActiveRecord::Base.prepend(RecordGuard)
|
|
53
|
+
# CollectionProxy overrides destroy_all and delete_all rather than inheriting them,
|
|
54
|
+
# so guarding Relation alone would miss every has_many collection.
|
|
41
55
|
ActiveRecord::Relation.prepend(RelationGuard)
|
|
42
56
|
ActiveRecord::Associations::CollectionProxy.prepend(RelationGuard)
|
|
57
|
+
ActiveRecord::Associations::CollectionProxy.prepend(CollectionGuard)
|
|
43
58
|
@installed = true
|
|
44
59
|
end
|
|
45
60
|
|
|
@@ -52,24 +67,77 @@ module ConsoleThinkTwice
|
|
|
52
67
|
@active = false
|
|
53
68
|
end
|
|
54
69
|
|
|
70
|
+
def enable!
|
|
71
|
+
@active = true
|
|
72
|
+
end
|
|
73
|
+
|
|
55
74
|
def active?
|
|
56
|
-
!!@active
|
|
75
|
+
!!@active && configuration.enabled?
|
|
57
76
|
end
|
|
58
77
|
|
|
59
78
|
# Confirms the call, then runs it with nested prompts suppressed so that a `dependent:`
|
|
60
79
|
# cascade, or a relation destroying its records one by one, only ever asks once.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
80
|
+
#
|
|
81
|
+
# `count` is passed when the caller already knows how many records are involved, which
|
|
82
|
+
# saves a COUNT and is the only way to size a call like `books.destroy(a, b)` that names
|
|
83
|
+
# its records rather than describing them.
|
|
84
|
+
def guard(target, action:, force:, count: nil)
|
|
85
|
+
return yield if !active? || suppressed? || ignored?(target)
|
|
86
|
+
|
|
87
|
+
# Active Record's own work is confirmed by whatever the user typed to set it off, so it
|
|
88
|
+
# runs suppressed rather than merely unguarded: `destroy!` re-enters as `destroy`, and
|
|
89
|
+
# that second hop is dispatched from persistence.rb, where nothing marks it as internal.
|
|
90
|
+
return suppressed { yield } if nested_in_active_record?(target)
|
|
91
|
+
|
|
92
|
+
unless force
|
|
93
|
+
count ||= affected_count(target)
|
|
94
|
+
return yield if count.zero?
|
|
95
|
+
|
|
96
|
+
confirm!(target, action, count)
|
|
97
|
+
end
|
|
66
98
|
|
|
67
|
-
confirm!(target, action, count) unless force
|
|
68
99
|
suppressed { yield }
|
|
69
100
|
end
|
|
70
101
|
|
|
71
102
|
private
|
|
72
103
|
|
|
104
|
+
def ignored?(target)
|
|
105
|
+
configuration.ignores?(model_of(target))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# True when Active Record itself made this call as one step of an operation already under
|
|
109
|
+
# way: a record being removed by `accepts_nested_attributes_for`, a `has_one` being
|
|
110
|
+
# replaced, a collection destroying the records it was handed. Those run inside the
|
|
111
|
+
# enclosing call's transaction, so prompting would hold that transaction — and its locks —
|
|
112
|
+
# open until somebody answers, and would ask about a record the user never named.
|
|
113
|
+
#
|
|
114
|
+
# Both conditions are required, because neither separates the two cases alone. A destroy
|
|
115
|
+
# typed inside `transaction { ... }`, or into a `--sandbox` console, has a transaction open
|
|
116
|
+
# but is still the user's own call; and `Model.destroy_all` is dispatched from inside
|
|
117
|
+
# Active Record, but only through the delegation in querying.rb rather than through the
|
|
118
|
+
# association and autosave code that does this work.
|
|
119
|
+
def nested_in_active_record?(target)
|
|
120
|
+
return false unless open_transaction?(target)
|
|
121
|
+
|
|
122
|
+
dispatch_chain.any? { |path| RAILS_INTERNAL_WORK.match?(path) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The frames Active Record itself put between the caller and us: everything above the first
|
|
126
|
+
# frame that belongs to neither this gem nor Rails, which is the code that made the call.
|
|
127
|
+
# An unrecognisable frame ends the chain, so anything we cannot read is treated as the
|
|
128
|
+
# caller's own and asked about rather than skipped.
|
|
129
|
+
def dispatch_chain
|
|
130
|
+
frames = (caller_locations(1, 50) || []).map { |location| location.path.to_s }
|
|
131
|
+
frames.drop_while { |path| path.start_with?(GEM_SOURCE) }
|
|
132
|
+
.take_while { |path| RAILS_SOURCE.match?(path) }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def open_transaction?(target)
|
|
136
|
+
model_of(target).connection.open_transactions > 0
|
|
137
|
+
rescue
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
|
|
73
141
|
def confirm!(target, action, count)
|
|
74
142
|
summary = summarize(target, action, count)
|
|
75
143
|
unless configuration.interactive?
|
|
@@ -100,7 +168,9 @@ module ConsoleThinkTwice
|
|
|
100
168
|
"#{count} #{(count == 1) ? model : model.pluralize}"
|
|
101
169
|
end
|
|
102
170
|
|
|
103
|
-
|
|
171
|
+
label = configuration.label
|
|
172
|
+
where = (label && !label.to_s.empty?) ? " in #{label}" : ""
|
|
173
|
+
"This will #{verb} #{subject}#{where}."
|
|
104
174
|
end
|
|
105
175
|
|
|
106
176
|
def affected_count(target)
|
|
@@ -119,16 +189,18 @@ module ConsoleThinkTwice
|
|
|
119
189
|
.map(&:name)
|
|
120
190
|
end
|
|
121
191
|
|
|
192
|
+
# Thread-local rather than fiber-local (which is what Thread.current[] gives), so that a
|
|
193
|
+
# confirmed call still counts as confirmed inside an Enumerator or any other fiber.
|
|
122
194
|
def suppressed?
|
|
123
|
-
Thread.current
|
|
195
|
+
Thread.current.thread_variable_get(SUPPRESSION_KEY)
|
|
124
196
|
end
|
|
125
197
|
|
|
126
198
|
def suppressed
|
|
127
|
-
previous = Thread.current
|
|
128
|
-
Thread.current
|
|
199
|
+
previous = Thread.current.thread_variable_get(SUPPRESSION_KEY)
|
|
200
|
+
Thread.current.thread_variable_set(SUPPRESSION_KEY, true)
|
|
129
201
|
yield
|
|
130
202
|
ensure
|
|
131
|
-
Thread.current
|
|
203
|
+
Thread.current.thread_variable_set(SUPPRESSION_KEY, previous)
|
|
132
204
|
end
|
|
133
205
|
|
|
134
206
|
def announce
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: console_think_twice
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Agustin Gomez
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -51,6 +51,7 @@ files:
|
|
|
51
51
|
- LICENSE
|
|
52
52
|
- README.md
|
|
53
53
|
- lib/console_think_twice.rb
|
|
54
|
+
- lib/console_think_twice/collection_guard.rb
|
|
54
55
|
- lib/console_think_twice/configuration.rb
|
|
55
56
|
- lib/console_think_twice/railtie.rb
|
|
56
57
|
- lib/console_think_twice/record_guard.rb
|