dry_eraser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []