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 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
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## 0.0.1
4
+
5
+ - Initial release
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "standard/rake"
4
+ require "tldr/rake"
5
+
6
+ task default: [:tldr, "standard:fix"]
@@ -0,0 +1,3 @@
1
+ module DryEraser
2
+ VERSION = "0.0.1"
3
+ end
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
@@ -0,0 +1,4 @@
1
+ module DryEraser
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []