Getting started
Usage
Removal strategy definition is called Plan. Plan is created using DSL. Plan can be validated and executed.
Plan defines a list of attributes and relations to clear. Relations work as nested Plans and can be nested infinitely.
Given schema:
create_table "users" do |t|
t.string "first_name"
t.string "last_name"
t.string "reset_password_token"
t.string "access_tokens"
t.datetime "confirmed_at"
t.integer "sign_in_count"
end
create_table "comments" do |t|
t.integer "user_id"
t.string "value"
end
create_table "resource_files" do |t|
t.integer "comment_id"
end
create_table "dashboards" do |t|
t.integer "user_id"
t.string "order"
end
and models
class User < ActiveRecord::Base
has_many :comments
has_one :dashboard
end
class Comment < ActiveRecord::Base
has_many :resource_files
end
class ResourceFile < ActiveRecord::Base; end
class Dashboard < ActiveRecord::Base; end
Example Plan:
UserWipeOutPlan = WipeOut.build_plan do
# Set nil value by default
wipe_out :first_name, :last_name
# Custom strategy
wipe_out :sign_in_count, strategy: WipeOut::AttributeStrategies::ConstValue.new(0)
# Inline custom strategy
wipe_out :reset_password_token do
"random-value-#{SecureRandom.hex}"
end
# has_many relation
relation :comments do
# Behaves like nested Plan.
wipe_out :value, strategy: WipeOut::AttributeStrategies::Randomize.new
relation :resource_files do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
end
# has_one relation
relation :dashboard do
wipe_out :order
ignore :name
end
# Ignore is used to mark both ignored relations
# and ignored attributes
ignore :access_tokens, :confirmed_at
end
After executing on a record
record = User.last
UserWipeOutPlan.execute(record)
User's:
first_name
andlast_name
attributes are set tonil
valuesign_in_count
is set to0
reset_password_token
is randomizedaccess_tokens
andconfirmed_at
attributes are not changed
User's Comments:
value
is randomized- Comment's resource_files are all destroyed
User's dashboard:
order
attribute is set tonil
name
attribute is ignored
Validation
Plans can be validated against DB schema:
UserWipeOutPlan.validate(User)
UserWipeOutPlan.validate(User).errors
UserWipeOutPlan.validate(User).valid?
Method performs validation. It will contain errors if plan is invalid.
When new attribute is added to schema or a new relation is added to a model that is used in Plan then validation will fail.
Ignoring
Every relation or attribute which is not part of removal plan
has to be marked as ignored with ignore
.
through
and belongs_to
relations are ignored automatically and they don't have to be ignored manually.
By default these attributes are ignored:
id
created_at
updated_at
archived_at
Given schema:
create_table "users" do |t|
t.string "first_name"
t.string "last_name"
t.integer "company_id"
t.datetime "created_at"
t.datetime "updated_at"
end
and class:
class User < ActiveRecord::Base
belongs_to :company
has_many :comments
has_one :dashboard
has_many :resource_files, through: :comments
end
a Plan to handle removing of this object has to provide strategy or ignore:
attributes:
first_name
last_name
company_id
relations:
comments
dashboard
Plan can skip providing strategy for:
- attributes
id
,created_at
,updated_at
- ignored by default - relation
company
-belongs_to
relation - relation
resource_files
- through relation
Reusing Plans
Extracting
Nested plans can be extracted as independent object. An exemplary plan can be rewritten to:
CommentsWipeOutPlan = WipeOut.build_plan do
wipe_out :value, strategy: WipeOut::AttributeStrategies::Randomize.new
relation :resource_files do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
end
DashboardWipeOutPlan = WipeOut.build_plan do
relation :dashboard do
wipe_out :order
ignore :name
end
end
UserWipeOutPlan = WipeOut.build_plan do
# …
relation :comments, CommentsWipeOutPlan
relation :dashboard, DashboardWipeOutPlan
# …
end
Including
Plan can be included to other existing Plan. When Plan is included then its strategy is copied and extends current definition.
E.g.
HasAttachmentsPlan = WipeOut.build_plan do
relation(:images) do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
relation(:videos) do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
wipe_out :attachments_count
end
WipeOut.build_plan do
include_plan HasAttachmentsPlan
relation(:comments) do
wipe_out :content
include_plan HasAttachmentsPlan
end
end
is exactly the same as:
WipeOut.build_plan do
wipe_out :attachments_count
relation(:images) do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
relation(:videos) do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
relation(:comments) do
wipe_out :content, :attachments_count
relation(:images) do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
relation(:videos) do
on_execute ->(execution) { execution.record.destroy! }
ignore_all
end
end
end
Dynamic plan selection
In some cases relation needs to have multiple Plan depending on the record's state. The list of Plans have to be known upfront to provide static validation. During Plan execution callback is called determine which Plan from the defined list to use for a given record.
UserPlan = WipeOut.build_plan do
normal_plan = WipeOut.build_plan do
on_execute ->(execution) { execution.record.destroy! }
end
vip_plan = WipeOut.build_plan do
ignore … # do not remove all data yet
end
relation(:resource_files, plans: [vip_plan, normal_plan] do |resource_file|
resource_file.user.vip? ? vip_plan : normal_plan
end
end
Plugins
Plugins are used to define behaviours which are not supported by the core library.
Plugins usage can be defined by including them in a plan block.
Currently the only hooks available are:
before(:plan) { |execution| ... }
- called before plan execution, already in transactionafter(:plan) { |execution| ... }
- called after plan execution, still in transaction, last place to rollbackbefore(:execution) { |execution| ... }
- called before record is wiped outafter(:execution) { |execution| ... }
- called after record is wiped out
When Plan with plugins is nested inside other Plan (see "Reusing plans") then its plugins are ignored.
E.g. in scenario:
XPlan = WipeOut.build_plan do
plugin PluginX
wipe_out …
end
YPlan = WipeOut.build_plan do
relation :x, XPlan
end
plugin defined in XPlan
is completely ignored and not used.
Configuration
WipeOut global settings can can be configured:
WipeOut.config do |config|
config.ignored_attributes << :user_id # defaults: [:id, :updated_at, :created_at, :archived_at]
end
Plans can override global config:
WipeOut.build_plan do
configure do |config|
config.ignored_attributes += [:some, :attributes]
end
end
Similarly to Plugins, when Plan with config override is nested inside other Plan (see "Reusing plans") then its custom configuration is ignored.