console_think_twice 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +21 -0
- data/LICENSE +21 -0
- data/README.md +102 -0
- data/lib/console_think_twice/configuration.rb +45 -0
- data/lib/console_think_twice/railtie.rb +9 -0
- data/lib/console_think_twice/record_guard.rb +17 -0
- data/lib/console_think_twice/relation_guard.rb +14 -0
- data/lib/console_think_twice/version.rb +5 -0
- data/lib/console_think_twice.rb +149 -0
- metadata +86 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dccea38c9288544ae02eea01191a0ed7187ed9187f85e90fdfd37387ca656aa6
|
|
4
|
+
data.tar.gz: 8bfd0ed5ca4daa26412dba1a43bbf9f2cb3090d49fc83aa9a99454771d20598b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 869e288cb34127645618c7d0326ae421c1836f44c95f12295dbd057be52e178868397200559b544c4cca06fe02ba4dbf3ede34b90690d400585982d9cf172127
|
|
7
|
+
data.tar.gz: effa7750ad3ba639698a3e37a8ecd4c789a2f3ce2abbe74bfc093150d91cbe6d23bafb37424938a3239720198d14a79611509b20b8d94428ed73fb35389dbd7e
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.1.0]
|
|
6
|
+
|
|
7
|
+
Initial release.
|
|
8
|
+
|
|
9
|
+
- Confirmation prompts for `destroy`, `destroy!` and `delete` on a record, and for
|
|
10
|
+
`destroy_all` and `delete_all` on a relation or `has_many` collection.
|
|
11
|
+
- `force: true` on any guarded call skips the prompt.
|
|
12
|
+
- A railtie arms the guard from the Rails `console` hook, leaving web requests, jobs and
|
|
13
|
+
`rails runner` untouched.
|
|
14
|
+
- Nested prompts are suppressed for the duration of a confirmed call, so a
|
|
15
|
+
`dependent: :destroy` cascade asks once rather than once per record.
|
|
16
|
+
- A declined call raises `ConsoleThinkTwice::Aborted`, stopping a loop on the first refusal.
|
|
17
|
+
- Non-interactive input refuses rather than assuming yes.
|
|
18
|
+
- Configurable via `ConsoleThinkTwice.configure`, or turned off with `CONSOLE_THINK_TWICE=0`.
|
|
19
|
+
|
|
20
|
+
[Unreleased]: https://github.com/agomezcampero/console-think-twice/compare/v0.1.0...HEAD
|
|
21
|
+
[0.1.0]: https://github.com/agomezcampero/console-think-twice/releases/tag/v0.1.0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agustin Gomez
|
|
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,102 @@
|
|
|
1
|
+
# console_think_twice
|
|
2
|
+
|
|
3
|
+
[](https://github.com/agomezcampero/console-think-twice/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Confirm before you destroy. `console_think_twice` intercepts destructive Active Record
|
|
6
|
+
calls made from a console and asks first, so a stray `User.destroy_all` in production
|
|
7
|
+
costs you a keystroke instead of a database.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
>> User.first.destroy!
|
|
11
|
+
|
|
12
|
+
This will permanently destroy User #1 in production.
|
|
13
|
+
Cascades to: sessions, comments
|
|
14
|
+
Confirm? (y/N) n
|
|
15
|
+
ConsoleThinkTwice::Aborted: Aborted. Nothing was destroyed.
|
|
16
|
+
|
|
17
|
+
>> User.destroy_all
|
|
18
|
+
|
|
19
|
+
This will permanently destroy 200 Users in production.
|
|
20
|
+
Cascades to: sessions, comments
|
|
21
|
+
Confirm? (y/N) y
|
|
22
|
+
=> [#<User id: 1, ...>, ...]
|
|
23
|
+
|
|
24
|
+
>> User.first.destroy!(force: true)
|
|
25
|
+
=> #<User id: 2, ...>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem "console_think_twice"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Not on RubyGems yet — until it is, point at the repository:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
gem "console_think_twice", github: "agomezcampero/console-think-twice"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
In a Rails app that is all — a railtie installs the guard from the `console` hook, which
|
|
41
|
+
runs for `rails console` only. Web requests, background jobs and `rails runner` are
|
|
42
|
+
untouched, and the gem never patches anything until a console actually boots.
|
|
43
|
+
|
|
44
|
+
Outside Rails, call `ConsoleThinkTwice.install!` once your models are loaded.
|
|
45
|
+
|
|
46
|
+
## What is guarded
|
|
47
|
+
|
|
48
|
+
| Call | Prompt |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `record.destroy`, `record.destroy!` | `This will permanently destroy User #1.` |
|
|
51
|
+
| `record.delete` | `This will permanently delete, skipping callbacks, User #1.` |
|
|
52
|
+
| `Model.destroy_all`, `relation.destroy_all`, `company.users.destroy_all` | `This will permanently destroy 200 Users.` |
|
|
53
|
+
| `Model.delete_all`, `relation.delete_all` | `This will permanently delete, skipping callbacks, 200 Users.` |
|
|
54
|
+
|
|
55
|
+
`Model.destroy(id)`, `destroy_by` and `delete_by` route through those methods, so they are
|
|
56
|
+
covered too.
|
|
57
|
+
|
|
58
|
+
Anything other than `y`/`yes` raises `ConsoleThinkTwice::Aborted`, which also stops a
|
|
59
|
+
loop like `users.each(&:destroy!)` on the first refusal rather than asking 200 times.
|
|
60
|
+
|
|
61
|
+
Every guarded method takes `force: true` to skip the prompt.
|
|
62
|
+
|
|
63
|
+
### One prompt per call
|
|
64
|
+
|
|
65
|
+
A `dependent: :destroy` cascade destroys child records, and `destroy_all` destroys its
|
|
66
|
+
records one at a time — both would otherwise prompt again for every record. Nested prompts
|
|
67
|
+
are suppressed for the duration of a confirmed call, so you are asked exactly once.
|
|
68
|
+
|
|
69
|
+
### Nobody to ask
|
|
70
|
+
|
|
71
|
+
When the input stream is not a terminal — piped input, a script fed to `rails console` —
|
|
72
|
+
there is no one to answer, so the guard refuses the call instead of assuming yes. Pass
|
|
73
|
+
`force: true` for destructive calls that are meant to run unattended.
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
ConsoleThinkTwice.configure do |config|
|
|
79
|
+
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
|
|
81
|
+
config.affirmative_answers = %w[y yes si] # default: %w[y yes]
|
|
82
|
+
config.interactive = true # default: auto-detected from config.input.tty?
|
|
83
|
+
config.input = $stdin
|
|
84
|
+
config.output = $stdout
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
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.
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
bundle install
|
|
95
|
+
bundle exec rake # specs and linter
|
|
96
|
+
bundle exec rspec
|
|
97
|
+
bundle exec standardrb
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleThinkTwice
|
|
4
|
+
class Configuration
|
|
5
|
+
DISABLING_ENV_VALUES = %w[0 false no off].freeze
|
|
6
|
+
|
|
7
|
+
attr_writer :enabled, :input, :interactive, :label, :output
|
|
8
|
+
|
|
9
|
+
# Answers accepted as a confirmation; anything else aborts.
|
|
10
|
+
attr_accessor :affirmative_answers
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@affirmative_answers = %w[y yes]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def input
|
|
17
|
+
@input || $stdin
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def output
|
|
21
|
+
@output || $stdout
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def enabled?
|
|
25
|
+
return @enabled unless @enabled.nil?
|
|
26
|
+
|
|
27
|
+
!DISABLING_ENV_VALUES.include?(ENV["CONSOLE_THINK_TWICE"].to_s.strip.downcase)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Whether there is a human on the other end who can answer the prompt. Auto-detected from
|
|
31
|
+
# the input stream unless set, so piped input aborts instead of destroying unattended.
|
|
32
|
+
def interactive?
|
|
33
|
+
return @interactive unless @interactive.nil?
|
|
34
|
+
|
|
35
|
+
input.respond_to?(:tty?) && input.tty?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Shown in the prompt to make the stakes obvious, e.g. "in production".
|
|
39
|
+
def label
|
|
40
|
+
return @label unless @label.nil?
|
|
41
|
+
|
|
42
|
+
::Rails.env.to_s if defined?(::Rails) && ::Rails.respond_to?(:env)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleThinkTwice
|
|
4
|
+
module RecordGuard
|
|
5
|
+
def destroy(force: false)
|
|
6
|
+
ConsoleThinkTwice.guard(self, action: :destroy, force: force) { super() }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def destroy!(force: false)
|
|
10
|
+
ConsoleThinkTwice.guard(self, action: :destroy, force: force) { super() }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def delete(force: false)
|
|
14
|
+
ConsoleThinkTwice.guard(self, action: :delete, force: force) { super() }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleThinkTwice
|
|
4
|
+
module RelationGuard
|
|
5
|
+
def destroy_all(force: false)
|
|
6
|
+
ConsoleThinkTwice.guard(self, action: :destroy, force: force) { super() }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# CollectionProxy#delete_all takes an optional dependency strategy; Relation#delete_all takes none.
|
|
10
|
+
def delete_all(*args, force: false)
|
|
11
|
+
ConsoleThinkTwice.guard(self, action: :delete, force: force) { super(*args) }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record"
|
|
4
|
+
require "active_support/core_ext/string/inflections"
|
|
5
|
+
|
|
6
|
+
require_relative "console_think_twice/version"
|
|
7
|
+
require_relative "console_think_twice/configuration"
|
|
8
|
+
require_relative "console_think_twice/record_guard"
|
|
9
|
+
require_relative "console_think_twice/relation_guard"
|
|
10
|
+
|
|
11
|
+
# Confirmation prompts for destructive Active Record calls typed into a console.
|
|
12
|
+
#
|
|
13
|
+
# >> User.first.destroy!
|
|
14
|
+
# This will permanently destroy User #1 in production.
|
|
15
|
+
# Cascades to: sessions, company_memberships, comments, api_keys
|
|
16
|
+
# Confirm? (y/N)
|
|
17
|
+
#
|
|
18
|
+
# Every guarded call takes `force: true` to skip the prompt. In a Rails app the railtie
|
|
19
|
+
# installs this on console boot only, so the web app, jobs and `rails runner` are untouched.
|
|
20
|
+
module ConsoleThinkTwice
|
|
21
|
+
# Raised when a destructive call is declined, or when there is nobody to ask.
|
|
22
|
+
Aborted = Class.new(StandardError)
|
|
23
|
+
|
|
24
|
+
CASCADING_STRATEGIES = %i[destroy destroy_async delete_all].freeze
|
|
25
|
+
SUPPRESSION_KEY = :console_think_twice_suppressed
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
def configuration
|
|
29
|
+
@configuration ||= Configuration.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def configure
|
|
33
|
+
yield configuration
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def install!
|
|
37
|
+
return false unless configuration.enabled?
|
|
38
|
+
|
|
39
|
+
unless @installed
|
|
40
|
+
ActiveRecord::Base.prepend(RecordGuard)
|
|
41
|
+
ActiveRecord::Relation.prepend(RelationGuard)
|
|
42
|
+
ActiveRecord::Associations::CollectionProxy.prepend(RelationGuard)
|
|
43
|
+
@installed = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@active = true
|
|
47
|
+
announce
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def disable!
|
|
52
|
+
@active = false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def active?
|
|
56
|
+
!!@active
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Confirms the call, then runs it with nested prompts suppressed so that a `dependent:`
|
|
60
|
+
# cascade, or a relation destroying its records one by one, only ever asks once.
|
|
61
|
+
def guard(target, action:, force:)
|
|
62
|
+
return yield if !active? || suppressed?
|
|
63
|
+
|
|
64
|
+
count = affected_count(target)
|
|
65
|
+
return yield if count.zero?
|
|
66
|
+
|
|
67
|
+
confirm!(target, action, count) unless force
|
|
68
|
+
suppressed { yield }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def confirm!(target, action, count)
|
|
74
|
+
summary = summarize(target, action, count)
|
|
75
|
+
unless configuration.interactive?
|
|
76
|
+
raise Aborted, "#{summary} No terminal to confirm on — pass `force: true` to proceed."
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
output = configuration.output
|
|
80
|
+
output.puts
|
|
81
|
+
output.puts highlight(summary)
|
|
82
|
+
cascades = cascading_associations(model_of(target))
|
|
83
|
+
output.puts "Cascades to: #{cascades.join(", ")}" if cascades.any?
|
|
84
|
+
output.print "Confirm? (y/N) "
|
|
85
|
+
output.flush
|
|
86
|
+
|
|
87
|
+
answer = configuration.input.gets.to_s.strip.downcase
|
|
88
|
+
return if configuration.affirmative_answers.include?(answer)
|
|
89
|
+
|
|
90
|
+
raise Aborted, "Aborted. Nothing was #{(action == :delete) ? "deleted" : "destroyed"}."
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def summarize(target, action, count)
|
|
94
|
+
verb = (action == :delete) ? "permanently delete, skipping callbacks," : "permanently destroy"
|
|
95
|
+
|
|
96
|
+
subject = if target.is_a?(ActiveRecord::Base)
|
|
97
|
+
"#{target.class.name} ##{target.id}"
|
|
98
|
+
else
|
|
99
|
+
model = model_of(target).name
|
|
100
|
+
"#{count} #{(count == 1) ? model : model.pluralize}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
["This will #{verb} #{subject}", configuration.label && " in #{configuration.label}", "."].compact.join
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def affected_count(target)
|
|
107
|
+
return target.persisted? ? 1 : 0 if target.is_a?(ActiveRecord::Base)
|
|
108
|
+
|
|
109
|
+
target.size
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def model_of(target)
|
|
113
|
+
target.is_a?(ActiveRecord::Base) ? target.class : target.klass
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def cascading_associations(model)
|
|
117
|
+
model.reflect_on_all_associations
|
|
118
|
+
.select { |reflection| CASCADING_STRATEGIES.include?(reflection.options[:dependent]) }
|
|
119
|
+
.map(&:name)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def suppressed?
|
|
123
|
+
Thread.current[SUPPRESSION_KEY]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def suppressed
|
|
127
|
+
previous = Thread.current[SUPPRESSION_KEY]
|
|
128
|
+
Thread.current[SUPPRESSION_KEY] = true
|
|
129
|
+
yield
|
|
130
|
+
ensure
|
|
131
|
+
Thread.current[SUPPRESSION_KEY] = previous
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def announce
|
|
135
|
+
return unless configuration.interactive?
|
|
136
|
+
|
|
137
|
+
configuration.output.puts highlight(
|
|
138
|
+
"Think twice: destroy and delete calls ask for confirmation. Pass `force: true` to skip."
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def highlight(text)
|
|
143
|
+
output = configuration.output
|
|
144
|
+
(output.respond_to?(:tty?) && output.tty?) ? "\e[1;31m#{text}\e[0m" : text
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
require_relative "console_think_twice/railtie" if defined?(::Rails)
|
metadata
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: console_think_twice
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Agustin Gomez
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '7.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '7.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '7.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '7.0'
|
|
41
|
+
description: 'Prompts before destroy, destroy!, delete, destroy_all and delete_all
|
|
42
|
+
when they are called from a Rails console, so a stray User.destroy_all cannot wipe
|
|
43
|
+
production. Pass force: true to skip the prompt.'
|
|
44
|
+
email:
|
|
45
|
+
- agustin@gomezcampero.com
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- CHANGELOG.md
|
|
51
|
+
- LICENSE
|
|
52
|
+
- README.md
|
|
53
|
+
- lib/console_think_twice.rb
|
|
54
|
+
- lib/console_think_twice/configuration.rb
|
|
55
|
+
- lib/console_think_twice/railtie.rb
|
|
56
|
+
- lib/console_think_twice/record_guard.rb
|
|
57
|
+
- lib/console_think_twice/relation_guard.rb
|
|
58
|
+
- lib/console_think_twice/version.rb
|
|
59
|
+
homepage: https://github.com/agomezcampero/console-think-twice
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata:
|
|
63
|
+
allowed_push_host: https://rubygems.org
|
|
64
|
+
source_code_uri: https://github.com/agomezcampero/console-think-twice
|
|
65
|
+
changelog_uri: https://github.com/agomezcampero/console-think-twice/blob/main/CHANGELOG.md
|
|
66
|
+
rubygems_mfa_required: 'true'
|
|
67
|
+
post_install_message:
|
|
68
|
+
rdoc_options: []
|
|
69
|
+
require_paths:
|
|
70
|
+
- lib
|
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.1'
|
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '0'
|
|
81
|
+
requirements: []
|
|
82
|
+
rubygems_version: 3.5.23
|
|
83
|
+
signing_key:
|
|
84
|
+
specification_version: 4
|
|
85
|
+
summary: Ask for confirmation before destructive Active Record calls in a console.
|
|
86
|
+
test_files: []
|