dry_eraser 0.0.1
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/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +109 -0
- data/Rakefile +6 -0
- data/lib/dry_eraser/version.rb +3 -0
- data/lib/dry_eraser.rb +52 -0
- data/sig/dry_eraser.rbs +4 -0
- data/tmp/.keep +0 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e0718a1c418f7f1feca5be58f45f2c32ab487ff586f8b97e4175efdf9cddca0f
|
4
|
+
data.tar.gz: 05da8722b1acd307abbd9ab6abe6178df011fa31118a33ed7df2f458b063df7c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 70b6a52e481683c215be73929fbd2c57e760c0caa07557c2da77ba74e4e9103b76f9bb11a314d2db1848e8b65b9ec6932f1e6b7adc645d00210cf13b94769b17
|
7
|
+
data.tar.gz: 9dfbc408b00b82e15a47c34d974b7d1c289e4ecfdddeeefddd717ce9f2026cf5fa5aa86926a12dcee9c9f8ddac79caf7d7b461560bded5886b0aceacd416fb83
|
data/.standard.yml
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Justin Searls
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# dry_eraser
|
2
|
+
|
3
|
+
**`dry_eraser` provides a _dry_ run before you _erase_ your models.**
|
4
|
+
|
5
|
+
This gem is for people who think it's weird that Rails offers so many ways to
|
6
|
+
validate models before you create and update them, but all it gives you is a
|
7
|
+
`before_destroy` hook if before permanently destroying them.
|
8
|
+
|
9
|
+
Think of `dry_eraser` as adding validation for `ActiveRecord#destroy`. To that
|
10
|
+
end, it defines `dry_erase` and `dry_erasable?` methods for your models that
|
11
|
+
behave similarly to `validates` and `valid?`. This way, you won't have to
|
12
|
+
remember how to register a `before_destroy` callback. Or that `throw(:abort)` is
|
13
|
+
the magical incantation to cancel the callback chain. If you're suspicious of
|
14
|
+
pulling in a dependency for something like this (and you should be), the fact
|
15
|
+
its [implementation is 50 lines soaking wet](lib/dry_eraser.rb) might put you at
|
16
|
+
ease.
|
17
|
+
|
18
|
+
Here's how to use it.
|
19
|
+
|
20
|
+
## Install
|
21
|
+
|
22
|
+
Add it to your Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem "dry_eraser"
|
26
|
+
```
|
27
|
+
|
28
|
+
That's it. Rails should load it automatically.
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
Whenever there's a situation in which you know you _don't_ want a `destroy`
|
33
|
+
operation to go through, you can specify it by calling the `dry_erase` class
|
34
|
+
method on your model.
|
35
|
+
|
36
|
+
Let's take an example `Whiteboard` model. Suppose it has a boolean attribute
|
37
|
+
called `someone_wrote_do_not_erase_on_me` and you want to be sure `destroy` operations
|
38
|
+
are aborted when that attribute is true.
|
39
|
+
|
40
|
+
You could:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class Whiteboard < ActiveRecord::Base
|
44
|
+
dry_erase :no_one_said_not_to_erase_it
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def no_one_said_not_to_erase_it
|
49
|
+
if someone_wrote_do_not_erase_on_me?
|
50
|
+
errors.add(:someone_wrote_do_not_erase_on_me, "so I can't erase it")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
This way, whenever `someone_wrote_do_not_erase_on_me?` is true, `destroy` will
|
57
|
+
return `false` (just like `save` returns false when validations fail).
|
58
|
+
|
59
|
+
This, combined with the fact that `dry_erase` determines success based on the
|
60
|
+
absence or presence of `errors` on the model instance will allow you to write
|
61
|
+
code that branches on whether destroy succeeded, just like you would for `save`
|
62
|
+
or `update`:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
whiteboard = Whiteboard.create!(someone_wrote_do_not_erase_on_me: true)
|
66
|
+
if whiteboard.destroy
|
67
|
+
flash[:notice] = "Whiteboard deleted!"
|
68
|
+
redirect_to whiteboards_path
|
69
|
+
else
|
70
|
+
flash[:error] = whiteboard.errors.full_messages
|
71
|
+
render :show, status: :unprocessable_entity
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
If you don't want to call `destroy` to know whether a model is safe to destroy,
|
76
|
+
you can also call `dry_erasable?` and it'll populate the `errors` object all
|
77
|
+
the same:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
whiteboard = Whiteboard.create!(someone_wrote_do_not_erase_on_me: true
|
81
|
+
whiteboard.dry_erasable?
|
82
|
+
=> false
|
83
|
+
whiteboard.errors.full_messages.first
|
84
|
+
=> "Someone wrote do not erase on me so I can't erase it"
|
85
|
+
```
|
86
|
+
|
87
|
+
## Other stuff you can pass to `dry_erase`
|
88
|
+
|
89
|
+
The `dry_erase` method can take one or more of any of the following:
|
90
|
+
|
91
|
+
* A symbol or string name of an instance method on the model
|
92
|
+
* A class that has a no-arg constructor and a `dry_erase(model)` method
|
93
|
+
* An object that responds to a `dry_erase(model)` model
|
94
|
+
* An object (e.g. a proc or lambda) that responds to `call(model)`
|
95
|
+
|
96
|
+
You can see all of these uses in the gem's [test fixture]():
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
# You can put more than one on one line
|
100
|
+
dry_erase :must_have_content, AnnoyingCoworkerMessageEraser
|
101
|
+
|
102
|
+
# Or pass a lambda
|
103
|
+
dry_erase ->(model) { model.content == "🖍️" && model.errors.add(:base, "No crayon, c'mon!") }
|
104
|
+
|
105
|
+
# Or an instance of a class that requires static configuration
|
106
|
+
dry_erase ForeignKeyEraser.new(Classroom, :whiteboard)
|
107
|
+
```
|
108
|
+
|
109
|
+
And that's about it.
|
data/Rakefile
ADDED
data/lib/dry_eraser.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require "rails/railtie"
|
2
|
+
require "active_support/concern"
|
3
|
+
require_relative "dry_eraser/version"
|
4
|
+
|
5
|
+
module DryEraser
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer "dry_eraser.initialize" do
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
9
|
+
include DryEraser::ModelExtensions
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ModelExtensions
|
15
|
+
extend ActiveSupport::Concern
|
16
|
+
included do
|
17
|
+
class_attribute :dry_erasers, default: []
|
18
|
+
before_destroy :check_dry_erasers
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def dry_erase(*classes_or_instances_or_method_names)
|
23
|
+
self.dry_erasers += classes_or_instances_or_method_names
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def dry_erasable?
|
28
|
+
errors.clear
|
29
|
+
|
30
|
+
self.class.dry_erasers.each do |dry_eraser|
|
31
|
+
case dry_eraser
|
32
|
+
in Class
|
33
|
+
dry_eraser.new.dry_erase(self)
|
34
|
+
in ->(it) { it.respond_to?(:dry_erase) }
|
35
|
+
dry_eraser.dry_erase(self)
|
36
|
+
in ->(it) { it.respond_to?(:call) }
|
37
|
+
dry_eraser.call(self)
|
38
|
+
in Symbol | String
|
39
|
+
send(dry_eraser)
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Invalid dry eraser: #{dry_eraser.inspect}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
errors.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_dry_erasers
|
49
|
+
throw(:abort) unless dry_erasable?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/sig/dry_eraser.rbs
ADDED
data/tmp/.keep
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dry_eraser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Searls
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: railties
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.1'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- searls@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".standard.yml"
|
49
|
+
- CHANGELOG.md
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- lib/dry_eraser.rb
|
54
|
+
- lib/dry_eraser/version.rb
|
55
|
+
- sig/dry_eraser.rbs
|
56
|
+
- tmp/.keep
|
57
|
+
homepage: https://github.com/searls/dry_eraser
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata:
|
61
|
+
homepage_uri: https://github.com/searls/dry_eraser
|
62
|
+
source_code_uri: https://github.com/searls/dry_eraser
|
63
|
+
changelog_uri: https://github.com/searls/dry_eraser/blob/main/CHANGELOG.md
|
64
|
+
rubygems_mfa_required: 'true'
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 3.0.0
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubygems_version: 3.5.6
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Like Active Record's validation feature, but for destroying models
|
84
|
+
test_files: []
|