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"
endand 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; endExample 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
endAfter executing on a record
record = User.last
UserWipeOutPlan.execute(record)User's:
first_nameandlast_nameattributes are set tonilvaluesign_in_countis set to0reset_password_tokenis randomizedaccess_tokensandconfirmed_atattributes are not changed
User's Comments:
valueis randomized- Comment's resource_files are all destroyed
User's dashboard:
orderattribute is set tonilnameattribute is ignored
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.
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:
idcreated_atupdated_atarchived_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"
endand class:
class User < ActiveRecord::Base
belongs_to :company
has_many :comments
has_one :dashboard
has_many :resource_files, through: :comments
enda Plan to handle removing of this object has to provide strategy or ignore:
-
attributes:
first_namelast_namecompany_id
-
relations:
commentsdashboard
Plan can skip providing strategy for:
- attributes
id,created_at,updated_at- ignored by default - relation
company-belongs_torelation - relation
resource_files- through relation
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
# …
endPlan 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
endis 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
endIn 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
endPlugins 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
endplugin defined in XPlan is completely ignored and not used.
WipeOut global settings can can be configured:
WipeOut.config do |config|
config.ignored_attributes << :user_id # defaults: [:id, :updated_at, :created_at, :archived_at]
endPlans can override global config:
WipeOut.build_plan do
configure do |config|
config.ignored_attributes += [:some, :attributes]
end
endSimilarly to Plugins, when Plan with config override is nested inside other Plan (see "Reusing plans") then its custom configuration is ignored.