wipe_out 1.0.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/.github/workflows/ci.yml +50 -0
- data/.gitignore +7 -0
- data/.markdownlint.json +7 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +58 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/standardrb +29 -0
- data/bin/yard +29 -0
- data/bin/yardoc +29 -0
- data/bin/yri +29 -0
- data/docs/development.md +57 -0
- data/docs/getting_started.md +350 -0
- data/docs/releasing.md +14 -0
- data/docs/yard_plugin.rb +12 -0
- data/lib/wipe_out.rb +65 -0
- data/lib/wipe_out/attribute_strategies/const_value.rb +13 -0
- data/lib/wipe_out/attribute_strategies/nullify.rb +5 -0
- data/lib/wipe_out/attribute_strategies/randomize.rb +13 -0
- data/lib/wipe_out/callback.rb +25 -0
- data/lib/wipe_out/callbacks_observer.rb +23 -0
- data/lib/wipe_out/config.rb +30 -0
- data/lib/wipe_out/execute.rb +31 -0
- data/lib/wipe_out/execution/context.rb +34 -0
- data/lib/wipe_out/execution/execute_plan.rb +53 -0
- data/lib/wipe_out/plans/built_plan.rb +35 -0
- data/lib/wipe_out/plans/dsl.rb +117 -0
- data/lib/wipe_out/plans/plan.rb +63 -0
- data/lib/wipe_out/plans/union.rb +19 -0
- data/lib/wipe_out/plugin.rb +31 -0
- data/lib/wipe_out/plugins/logger.rb +42 -0
- data/lib/wipe_out/validate.rb +48 -0
- data/lib/wipe_out/validators/attributes.rb +49 -0
- data/lib/wipe_out/validators/base.rb +13 -0
- data/lib/wipe_out/validators/defined_relations.rb +26 -0
- data/lib/wipe_out/validators/relations_plans.rb +26 -0
- data/lib/wipe_out/version.rb +3 -0
- data/wipe_out.gemspec +47 -0
- metadata +274 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eafa15a844d625664e5cd81d01e921514a84f34a2d488efedd950a5fadc77adb
|
4
|
+
data.tar.gz: 92ebfff20053d46b9e2629e1c00dc3097073874083bc0d46bb077e43d1418346
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2177346bc816df8cd6a270abd66e8c660db21297e4fead9500c912792f2bb4f76fd12666ae7029091b4a3c71a430974087992d9615c155d6811f6165e03b3b27
|
7
|
+
data.tar.gz: dc02b071110c0c5963f44a132de42def2ee24e317b4623eb0106eee64739160c93e1350065083b023e5e5ab3a7812819febc864ca07474eb00206de0fbc36448
|
@@ -0,0 +1,50 @@
|
|
1
|
+
name: CI Suite
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- master
|
6
|
+
pull_request:
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
lint:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- uses: ruby/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 3.0
|
16
|
+
bundler-cache: true
|
17
|
+
- run: bin/standardrb --format progress --no-fix
|
18
|
+
docs:
|
19
|
+
runs-on: ubuntu-latest
|
20
|
+
steps:
|
21
|
+
- uses: actions/checkout@v2
|
22
|
+
- uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: 3.0
|
25
|
+
bundler-cache: true
|
26
|
+
- name: NODE SETUP ACTION
|
27
|
+
uses: actions/setup-node@v2
|
28
|
+
with:
|
29
|
+
node-version: '16.x'
|
30
|
+
- run: yarn global add markdownlint-cli@0.27.1
|
31
|
+
- run: export PATH="${PATH}:$(yarn global bin)"
|
32
|
+
- run: markdownlint --config .markdownlint.json -i .yarn -i vendor .
|
33
|
+
- run: bin/yard
|
34
|
+
test:
|
35
|
+
strategy:
|
36
|
+
fail-fast: false
|
37
|
+
matrix:
|
38
|
+
include:
|
39
|
+
- gemfile: Gemfile
|
40
|
+
ruby: 3.0
|
41
|
+
runs-on: ubuntu-latest
|
42
|
+
steps:
|
43
|
+
- run: echo BUNDLE_GEMFILE=${{ matrix.gemfile }} > $GITHUB_ENV
|
44
|
+
- uses: actions/checkout@v2
|
45
|
+
- uses: ruby/setup-ruby@v1
|
46
|
+
with:
|
47
|
+
ruby-version: ${{ matrix.ruby }}
|
48
|
+
bundler-cache: true
|
49
|
+
bundler: ${{ matrix.bundler || 'default' }}
|
50
|
+
- run: bin/rspec
|
data/.gitignore
ADDED
data/.markdownlint.json
ADDED
data/.rspec
ADDED
data/.yardopts
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2021 Spa Worldwide Ltd
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# WipeOut
|
2
|
+
|
3
|
+
Library for removing and clearing data in Rails ActiveRecord models.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
1. Add WipeOut to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem "wipe_out", "~> 1.0"
|
11
|
+
```
|
12
|
+
|
13
|
+
Check newest release at [here](https://rubygems.org/gems/wipe_out).
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
Quick example:
|
18
|
+
|
19
|
+
Given the following model:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
# == Schema Info
|
23
|
+
#
|
24
|
+
# Table name: users
|
25
|
+
#
|
26
|
+
# id :integer(11) not null, primary key
|
27
|
+
# name :varchar(11) not null
|
28
|
+
# orders_count :integer(11) not null
|
29
|
+
class User < ActiveRecord::Base
|
30
|
+
end
|
31
|
+
|
32
|
+
```
|
33
|
+
|
34
|
+
We can define custom wipe out plan:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
UserWipeOutPlan = WipeOut.build_plan do
|
38
|
+
wipe_out :name
|
39
|
+
ignore :orders_count
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
and execute it:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
User.last.then { |user| UserWipeOutPlan.execute(user) }
|
47
|
+
```
|
48
|
+
|
49
|
+
It will overwrite data inside `name` but leave, `orders_count` untouched.
|
50
|
+
|
51
|
+
There is also support for relations and making sure that policies are defined
|
52
|
+
for any added columns.
|
53
|
+
|
54
|
+
Read more in [getting started](./docs/getting_started.md) doc.
|
55
|
+
|
56
|
+
## Contributing && Development
|
57
|
+
|
58
|
+
See [development.md](./docs/development.md)
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/standardrb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'standardrb' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("standard", "standardrb")
|
data/bin/yard
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'yard' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("yard", "yard")
|
data/bin/yardoc
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'yardoc' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("yard", "yardoc")
|
data/bin/yri
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'yri' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("yard", "yri")
|
data/docs/development.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Development
|
2
|
+
|
3
|
+
So, you want to hack on GraphQL Ruby! Here are some tips for getting started.
|
4
|
+
|
5
|
+
* [Setup](#setup) your development environment
|
6
|
+
* [Run tests](#run-tests)
|
7
|
+
* [Debug](#debug)
|
8
|
+
* [Coding guidelines](#coding-guidelines)
|
9
|
+
* [Releasing](#releasing)
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
|
13
|
+
Follow the steps below to setup wipe_out locally:
|
14
|
+
|
15
|
+
* Make sure you're running on Ruby 3.0.0 or newer
|
16
|
+
* sqlite is installed (required for tests)
|
17
|
+
|
18
|
+
```bash
|
19
|
+
git clone https://github.com/GlobalAppTesting/wipe_out
|
20
|
+
cd wipe_out
|
21
|
+
bundle install
|
22
|
+
```
|
23
|
+
|
24
|
+
## Run tests
|
25
|
+
|
26
|
+
```bash
|
27
|
+
./bin/rspec
|
28
|
+
```
|
29
|
+
|
30
|
+
## Debug
|
31
|
+
|
32
|
+
By default `pry` is included so feel free to run tests and put `binding.pry`
|
33
|
+
wherever you like.
|
34
|
+
|
35
|
+
## Coding guidelines
|
36
|
+
|
37
|
+
* Please make sure to run `./bin/standardrb --fix`
|
38
|
+
* Markdown files are linted too via [markdownlint](https://github.com/DavidAnson/markdownlint)
|
39
|
+
* Each change should be covered by tests and add CHANGELOG info
|
40
|
+
|
41
|
+
## Releasing
|
42
|
+
|
43
|
+
Releasing will be done manually for now.
|
44
|
+
|
45
|
+
1. Bump version in `lib/wipe_out/version.rb`
|
46
|
+
1. Ensure CHANGELOG.md is matching new version and has details about published changes.
|
47
|
+
Make sure that breaking changes contain update instructions.
|
48
|
+
1. Commit all changes `git commit -m "Release: vX.Y.Z"`
|
49
|
+
1. Tag commit `git tag vX.Y.Z`
|
50
|
+
1. Push changes and tag
|
51
|
+
|
52
|
+
```bash
|
53
|
+
git push origin master
|
54
|
+
git push origin vX.Y.Z
|
55
|
+
```
|
56
|
+
|
57
|
+
1. (TODO) Publish release on [Rubygems](https://rubygems.org/)
|
@@ -0,0 +1,350 @@
|
|
1
|
+
# Getting started
|
2
|
+
|
3
|
+
## Usage
|
4
|
+
|
5
|
+
Removal strategy definition is called _Plan_. _Plan_ is created using DSL.
|
6
|
+
Plan can be validated and executed.
|
7
|
+
|
8
|
+
_Plan_ defines a list of attributes and relations to clear.
|
9
|
+
Relations work as nested _Plans_ and can be nested infinitely.
|
10
|
+
|
11
|
+
Given schema:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
create_table "users" do |t|
|
15
|
+
t.string "first_name"
|
16
|
+
t.string "last_name"
|
17
|
+
t.string "reset_password_token"
|
18
|
+
t.string "access_tokens"
|
19
|
+
t.datetime "confirmed_at"
|
20
|
+
t.integer "sign_in_count"
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table "comments" do |t|
|
24
|
+
t.integer "user_id"
|
25
|
+
t.string "value"
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table "resource_files" do |t|
|
29
|
+
t.integer "comment_id"
|
30
|
+
end
|
31
|
+
|
32
|
+
create_table "dashboards" do |t|
|
33
|
+
t.integer "user_id"
|
34
|
+
t.string "order"
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
and models
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class User < ActiveRecord::Base
|
42
|
+
has_many :comments
|
43
|
+
has_one :dashboard
|
44
|
+
end
|
45
|
+
|
46
|
+
class Comment < ActiveRecord::Base
|
47
|
+
has_many :resource_files
|
48
|
+
end
|
49
|
+
|
50
|
+
class ResourceFile < ActiveRecord::Base; end
|
51
|
+
class Dashboard < ActiveRecord::Base; end
|
52
|
+
```
|
53
|
+
|
54
|
+
Example Plan:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
UserWipeOutPlan = WipeOut.build_plan do
|
58
|
+
# Set nil value by default
|
59
|
+
wipe_out :first_name, :last_name
|
60
|
+
# Custom strategy
|
61
|
+
wipe_out :sign_in_count, strategy: WipeOut::AttributeStrategies::ConstValue.new(0)
|
62
|
+
# Inline custom strategy
|
63
|
+
wipe_out :reset_password_token do
|
64
|
+
"random-value-#{SecureRandom.hex}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# has_many relation
|
68
|
+
relation :comments do
|
69
|
+
# Behaves like nested Plan.
|
70
|
+
wipe_out :value, strategy: WipeOut::AttributeStrategies::Randomize.new
|
71
|
+
|
72
|
+
relation :resource_files do
|
73
|
+
on_execute ->(execution) { execution.record.destroy! }
|
74
|
+
ignore_all
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# has_one relation
|
79
|
+
relation :dashboard do
|
80
|
+
wipe_out :order
|
81
|
+
ignore :name
|
82
|
+
end
|
83
|
+
|
84
|
+
# Ignore is used to mark both ignored relations
|
85
|
+
# and ignored attributes
|
86
|
+
ignore :access_tokens, :confirmed_at
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
After executing on a record
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
record = User.last
|
94
|
+
UserWipeOutPlan.execute(record)
|
95
|
+
```
|
96
|
+
|
97
|
+
User's:
|
98
|
+
|
99
|
+
* `first_name` and `last_name` attributes are set to `nil` value
|
100
|
+
* `sign_in_count` is set to `0`
|
101
|
+
* `reset_password_token` is randomized
|
102
|
+
* `access_tokens` and `confirmed_at` attributes are not changed
|
103
|
+
|
104
|
+
User's Comments:
|
105
|
+
|
106
|
+
* `value` is randomized
|
107
|
+
* Comment's resource_files are all destroyed
|
108
|
+
|
109
|
+
User's dashboard:
|
110
|
+
|
111
|
+
* `order` attribute is set to `nil`
|
112
|
+
* `name` attribute is ignored
|
113
|
+
|
114
|
+
## Validation
|
115
|
+
|
116
|
+
_Plans_ can be validated against DB schema:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
UserWipeOutPlan.validate(User)
|
120
|
+
UserWipeOutPlan.validate(User).errors
|
121
|
+
UserWipeOutPlan.validate(User).valid?
|
122
|
+
```
|
123
|
+
|
124
|
+
Method performs validation. It will contain errors if plan is invalid.
|
125
|
+
|
126
|
+
When new attribute is added to schema or a new relation is added to a model
|
127
|
+
that is used in _Plan_ then validation will fail.
|
128
|
+
|
129
|
+
### Ignoring
|
130
|
+
|
131
|
+
Every relation or attribute which is not part of removal plan
|
132
|
+
has to be marked as ignored with `ignore`.
|
133
|
+
|
134
|
+
`through` and `belongs_to` relations are ignored automatically and they don't have to be ignored manually.
|
135
|
+
|
136
|
+
By default these attributes are ignored:
|
137
|
+
|
138
|
+
* `id`
|
139
|
+
* `created_at`
|
140
|
+
* `updated_at`
|
141
|
+
* `archived_at`
|
142
|
+
|
143
|
+
Given schema:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
create_table "users" do |t|
|
147
|
+
t.string "first_name"
|
148
|
+
t.string "last_name"
|
149
|
+
t.integer "company_id"
|
150
|
+
t.datetime "created_at"
|
151
|
+
t.datetime "updated_at"
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
and class:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
class User < ActiveRecord::Base
|
159
|
+
belongs_to :company
|
160
|
+
has_many :comments
|
161
|
+
has_one :dashboard
|
162
|
+
has_many :resource_files, through: :comments
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
a _Plan_ to handle removing of this object has to provide strategy or ignore:
|
167
|
+
|
168
|
+
* attributes:
|
169
|
+
|
170
|
+
* `first_name`
|
171
|
+
* `last_name`
|
172
|
+
* `company_id`
|
173
|
+
|
174
|
+
* relations:
|
175
|
+
|
176
|
+
* `comments`
|
177
|
+
* `dashboard`
|
178
|
+
|
179
|
+
_Plan_ can skip providing strategy for:
|
180
|
+
|
181
|
+
* attributes `id`, `created_at`, `updated_at` - ignored by default
|
182
|
+
* relation `company` - `belongs_to` relation
|
183
|
+
* relation `resource_files` - through relation
|
184
|
+
|
185
|
+
### Reusing _Plans_
|
186
|
+
|
187
|
+
#### Extracting
|
188
|
+
|
189
|
+
Nested plans can be extracted as independent object. An exemplary plan can be rewritten to:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
CommentsWipeOutPlan = WipeOut.build_plan do
|
193
|
+
wipe_out :value, strategy: WipeOut::AttributeStrategies::Randomize.new
|
194
|
+
|
195
|
+
relation :resource_files do
|
196
|
+
on_execute ->(execution) { execution.record.destroy! }
|
197
|
+
ignore_all
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
DashboardWipeOutPlan = WipeOut.build_plan do
|
202
|
+
relation :dashboard do
|
203
|
+
wipe_out :order
|
204
|
+
ignore :name
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
UserWipeOutPlan = WipeOut.build_plan do
|
209
|
+
# …
|
210
|
+
relation :comments, CommentsWipeOutPlan
|
211
|
+
relation :dashboard, DashboardWipeOutPlan
|
212
|
+
# …
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
#### Including
|
217
|
+
|
218
|
+
_Plan_ can be included to other existing _Plan_. When _Plan_ is included then
|
219
|
+
its strategy is copied and extends current definition.
|
220
|
+
|
221
|
+
E.g.
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
HasAttachmentsPlan = WipeOut.build_plan do
|
225
|
+
relation(:images) do
|
226
|
+
on_execute ->(execution) { execution.record.destroy! }
|
227
|
+
ignore_all
|
228
|
+
end
|
229
|
+
relation(:videos) do
|
230
|
+
on_execute ->(execution) { execution.record.destroy! }
|
231
|
+
ignore_all
|
232
|
+
end
|
233
|
+
wipe_out :attachments_count
|
234
|
+
end
|
235
|
+
|
236
|
+
WipeOut.build_plan do
|
237
|
+
include_plan HasAttachmentsPlan
|
238
|
+
relation(:comments) do
|
239
|
+
wipe_out :content
|
240
|
+
include_plan HasAttachmentsPlan
|
241
|
+
end
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
is exactly the same as:
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
WipeOut.build_plan do
|
249
|
+
wipe_out :attachments_count
|
250
|
+
|
251
|
+
relation(:images) do
|
252
|
+
on_execute ->(execution) { execution.record.destroy! }
|
253
|
+
ignore_all
|
254
|
+
end
|
255
|
+
relation(:videos) do
|
256
|
+
on_execute ->(execution) { execution.record.destroy! }
|
257
|
+
ignore_all
|
258
|
+
end
|
259
|
+
|
260
|
+
relation(:comments) do
|
261
|
+
wipe_out :content, :attachments_count
|
262
|
+
|
263
|
+
relation(:images) do
|
264
|
+
on_execute ->(execution) { execution.record.destroy! }
|
265
|
+
ignore_all
|
266
|
+
end
|
267
|
+
relation(:videos) do
|
268
|
+
on_execute ->(execution) { execution.record.destroy! }
|
269
|
+
ignore_all
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
```
|
274
|
+
|
275
|
+
### Dynamic plan selection
|
276
|
+
|
277
|
+
In some cases relation needs to have multiple _Plan_ depending on the record's state.
|
278
|
+
The list of _Plans_ have to be known upfront to provide static validation.
|
279
|
+
During _Plan_ execution callback is called determine which _Plan_ from the defined list
|
280
|
+
to use for a given record.
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
UserPlan = WipeOut.build_plan do
|
284
|
+
normal_plan = WipeOut.build_plan do
|
285
|
+
on_execute ->(execution) { execution.record.destroy! }
|
286
|
+
end
|
287
|
+
vip_plan = WipeOut.build_plan do
|
288
|
+
ignore … # do not remove all data yet
|
289
|
+
end
|
290
|
+
|
291
|
+
relation(:resource_files, plans: [vip_plan, normal_plan] do |resource_file|
|
292
|
+
resource_file.user.vip? ? vip_plan : normal_plan
|
293
|
+
end
|
294
|
+
end
|
295
|
+
```
|
296
|
+
|
297
|
+
### Plugins
|
298
|
+
|
299
|
+
Plugins are used to define behaviours which are not supported by the
|
300
|
+
core library.
|
301
|
+
|
302
|
+
Plugins usage can be defined by including them in a plan block.
|
303
|
+
|
304
|
+
Currently the only hooks available are:
|
305
|
+
|
306
|
+
* `before(:plan) { |execution| ... }` - called before plan execution, already in transaction
|
307
|
+
* `after(:plan) { |execution| ... }` - called after plan execution, still in transaction, last place to rollback
|
308
|
+
* `before(:execution) { |execution| ... }` - called before record is wiped out
|
309
|
+
* `after(:execution) { |execution| ... }` - called after record is wiped out
|
310
|
+
|
311
|
+
When _Plan_ with plugins is nested inside other _Plan_ (see "Reusing plans")
|
312
|
+
then its plugins are ignored.
|
313
|
+
|
314
|
+
E.g. in scenario:
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
XPlan = WipeOut.build_plan do
|
318
|
+
plugin PluginX
|
319
|
+
wipe_out …
|
320
|
+
end
|
321
|
+
|
322
|
+
YPlan = WipeOut.build_plan do
|
323
|
+
relation :x, XPlan
|
324
|
+
end
|
325
|
+
```
|
326
|
+
|
327
|
+
plugin defined in `XPlan` is completely ignored and not used.
|
328
|
+
|
329
|
+
## Configuration
|
330
|
+
|
331
|
+
WipeOut global settings can can be configured:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
WipeOut.config do |config|
|
335
|
+
config.ignored_attributes << :user_id # defaults: [:id, :updated_at, :created_at, :archived_at]
|
336
|
+
end
|
337
|
+
```
|
338
|
+
|
339
|
+
_Plans_ can override global config:
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
WipeOut.build_plan do
|
343
|
+
configure do |config|
|
344
|
+
config.ignored_attributes += [:some, :attributes]
|
345
|
+
end
|
346
|
+
end
|
347
|
+
```
|
348
|
+
|
349
|
+
Similarly to Plugins, when _Plan_ with config override is nested inside other _Plan_
|
350
|
+
(see "Reusing plans") then its custom configuration is ignored.
|