sequel-paranoid 0.6.2 → 0.7.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/plugins/paranoid.rb +149 -60
  3. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 00ea7005b5cef0b88e4dd80e5592dd83b55ec5ee
4
- data.tar.gz: 0754305ae29e1968e0cf9dba290d798e00a1ec1a
3
+ metadata.gz: e14a35d77873153c4dd8983fb9e338796ccbfab5
4
+ data.tar.gz: 40feef72715beab3225246870ffdf84e241ed44c
5
5
  SHA512:
6
- metadata.gz: 0d1c1864e7b354a34bfdaef9c082da089f141e642c0f68ee91e1437a9c875d6f7f3509f862dcbb98f61092e9a23d674276e35dd709f78d5d21c30b7ff8fc7086
7
- data.tar.gz: c80715773eda084fc7479794f10b528dfbbbb48696d73cda96de8be8932cb06a9f177d233b6c1b80299afd7a004f3cc3dc9230acf76d33829847b6fcaecb62fa
6
+ metadata.gz: e84e46393bb292be70bb7c00f9f77a77c7dde85b2468c022f5c66aaa0dabe8fab5a93b2f6744f3a609ae9176b9a7677e35599b55700c5506a2b7ffcaef249470
7
+ data.tar.gz: 489045f17951b5390d17902511793e14b2d215ed4af44f473baf8598cf1bf2e2da9e8ef7cb39d962b7d73a9ecc3d9bddaed9398caa89e49eb86f5c126802512a
@@ -2,46 +2,115 @@ require 'sequel'
2
2
 
3
3
  module Sequel::Plugins
4
4
  module Paranoid
5
+
5
6
  def self.configure(model, options = {})
6
- options = {
7
+ model.sequel_paranoid_options = options = {
7
8
  :deleted_at_field_name => :deleted_at,
8
9
  :deleted_by_field_name => :deleted_by,
10
+ :delete_method_name => :soft_delete,
9
11
  :enable_deleted_by => false,
10
12
  :deleted_scope_name => :deleted,
11
- :non_deleted_scope_name => :present,
13
+ :non_deleted_scope_name => :not_deleted,
12
14
  :ignore_deletion_scope_name => :with_deleted,
13
15
  :enable_default_scope => false,
14
- :deleted_column_default => nil
15
- }.merge(options)
16
+ :soft_delete_on_destroy => false,
17
+ :deleted_column_default => nil,
18
+ :include_validation_helpers => false,
19
+ }.update(options)
20
+
21
+ delete_attributes = proc do |*args|
22
+ destroy_options = args.first || {}
23
+
24
+ attrs = {}
25
+ # set the deletion time
26
+ attrs[options[:deleted_at_field_name]] = Time.now
27
+
28
+ # set the deletion author
29
+ if options[:enable_deleted_by] && destroy_options && destroy_options[:deleted_by]
30
+ attrs[options[:deleted_by_field_name]] = destroy_options[:deleted_by]
31
+ end
32
+
33
+ attrs
34
+ end
35
+
36
+ ds_mod = Module.new do
37
+ # scope for deleted items
38
+ define_method(options[:deleted_scope_name]) do
39
+ send(options[:ignore_deletion_scope_name]).exclude(Sequel.qualify(model.table_name, options[:deleted_at_field_name]) => options[:deleted_column_default])
40
+ end
41
+
42
+ # scope for non-deleted items
43
+ define_method(options[:non_deleted_scope_name]) do
44
+ filter(Sequel.qualify(model.table_name, options[:deleted_at_field_name]) => options[:deleted_column_default])
45
+ end
46
+
47
+ # scope for both
48
+ define_method(options[:ignore_deletion_scope_name]) do
49
+ unfiltered
50
+ end
51
+
52
+ # soft delete the records without callbacks.
53
+ define_method(options[:delete_method_name]) do |*args|
54
+ update(delete_attributes.call(*args))
55
+ end
56
+
57
+ end
58
+
59
+ im_mod = Module.new do
60
+
61
+ define_method(options[:delete_method_name]) do |*args|
62
+ self.set(delete_attributes.call(*args))
63
+ self.save
64
+ end
65
+
66
+ end
16
67
 
17
68
  model.instance_eval do
18
- #
19
- # Inject the scopes for the deleted and the existing entries.
20
- #
69
+ dataset_module ds_mod
70
+ include im_mod
21
71
 
22
- dataset_module do
23
- # scope for deleted items
24
- define_method(options[:deleted_scope_name]) do
25
- send(options[:ignore_deletion_scope_name]).exclude(Sequel.qualify(model.table_name, options[:deleted_at_field_name]) => options[:deleted_column_default])
26
- end
72
+ plugin SoftDeleteOnDestroy if options[:soft_delete_on_destroy]
73
+ plugin EnableDefaultScope if options[:enable_default_scope]
74
+ plugin Validations if options[:include_validation_helpers]
75
+ end
76
+ end
27
77
 
28
- # scope for non-deleted items
29
- define_method(options[:non_deleted_scope_name]) do
30
- filter(Sequel.qualify(model.table_name, options[:deleted_at_field_name]) => options[:deleted_column_default])
31
- end
78
+ module ClassMethods
79
+ attr_accessor :sequel_paranoid_options
32
80
 
33
- # scope for both
34
- define_method(options[:ignore_deletion_scope_name]) do
35
- unfiltered
36
- end
81
+ ::Sequel::Plugins.inherited_instance_variables(self, :@sequel_paranoid_options=>:hash_dup)
82
+ end
83
+
84
+ module InstanceMethods
85
+
86
+ #
87
+ # Method for undeleting an instance.
88
+ #
89
+ def recover
90
+ opts = self.class.sequel_paranoid_options
91
+ send("#{opts[:deleted_at_field_name]}=".to_sym, opts[:deleted_column_default])
92
+
93
+ if opts[:enable_deleted_by] && self.respond_to?(opts[:deleted_by_field_name].to_sym)
94
+ send("#{opts[:deleted_by_field_name]}=", nil)
37
95
  end
38
96
 
39
- #
40
- # Overwrite the "_destroy_delete" method which is used by sequel to
41
- # delete an object. This makes sure, we run all the hook correctly and
42
- # in a transaction.
43
- #
44
- define_method("destroy") do |*args|
97
+ save
98
+ end
99
+
100
+ #
101
+ # Check if an instance is deleted.
102
+ #
103
+
104
+ def deleted?
105
+ opts = self.class.sequel_paranoid_options
106
+ send(opts[:deleted_at_field_name]) != opts[:deleted_column_default]
107
+ end
108
+
109
+ end
110
+
111
+ module SoftDeleteOnDestroy
112
+ module InstanceMethods
113
+ def destroy(*args)
45
114
  # Save the variables threadsafe (because the locks have not been
46
115
  # initialized by sequel yet).
47
116
  Thread.current["_paranoid_destroy_args_#{self.object_id}"] = args
@@ -49,28 +118,37 @@ module Sequel::Plugins
49
118
  super(*args)
50
119
  end
51
120
 
52
- define_method("_destroy_delete") do
121
+ #
122
+ # Overwrite the "_destroy_delete" method which is used by sequel to
123
+ # delete an object. This makes sure, we run all the hook correctly and
124
+ # in a transaction.
125
+ #
126
+
127
+ def _destroy_delete
53
128
  # _destroy_delete does not take arguments.
54
129
  destroy_options = Thread.current["_paranoid_destroy_args_#{self.object_id}"].first
55
130
  Thread.current["_paranoid_destroy_args_#{self.object_id}"] = nil
56
131
 
57
- # set the deletion time
58
- self.send("#{options[:deleted_at_field_name]}=", Time.now)
132
+ send(self.class.sequel_paranoid_options[:delete_method_name], destroy_options)
133
+ end
134
+ end
135
+ end
59
136
 
60
- # set the deletion author
61
- if options[:enable_deleted_by] && destroy_options && destroy_options[:deleted_by]
62
- self.send("#{options[:deleted_by_field_name]}=", destroy_options[:deleted_by])
63
- end
137
+ module EnableDefaultScope
64
138
 
65
- self.save
139
+ def self.configure(model)
140
+ model.class_eval do
141
+ set_dataset(send(sequel_paranoid_options[:non_deleted_scope_name]))
66
142
  end
143
+ end
144
+
145
+ module InstanceMethods
67
146
 
68
147
  #
69
148
  # Sequel patch to allow updates to deleted instances
70
149
  # when default scope is enabled
71
150
  #
72
-
73
- define_method("_update_without_checking") do |columns|
151
+ def _update_without_checking(columns)
74
152
  # figure out correct pk conditions (see base#this)
75
153
  conditions = this.send(:joined_dataset?) ? qualified_pk_hash : pk_hash
76
154
 
@@ -80,38 +158,49 @@ module Sequel::Plugins
80
158
  # run the original update on the with_deleted dataset
81
159
  update_with_deleted_dataset.update(columns)
82
160
 
83
- end if(options[:enable_default_scope])
84
-
85
- #
86
- # Method for undeleting an instance.
87
- #
88
-
89
- define_method("recover") do
90
- self.send("#{options[:deleted_at_field_name]}=".to_sym, options[:deleted_column_default])
91
-
92
- if options[:enable_deleted_by] && self.respond_to?(options[:deleted_by_field_name].to_sym)
93
- self.send("#{options[:deleted_by_field_name]}=", nil)
94
- end
95
-
96
- self.save
97
161
  end
98
162
 
99
- #
100
- # Check if an instance is deleted.
101
- #
163
+ end
164
+ end
102
165
 
103
- define_method("deleted?") do
104
- send(options[:deleted_at_field_name]) != options[:deleted_column_default]
105
- end
166
+ module Validations
106
167
 
168
+ def self.apply(base)
169
+ base.plugin :validation_helpers
170
+ end
171
+
172
+ module InstanceMethods
107
173
  #
108
- # Inject the default scope that filters deleted entries.
174
+ # Enhance validates_unique to support :paranoid => true for paranoid
175
+ # uniqueness checking.
109
176
  #
110
-
111
- if options[:enable_default_scope]
112
- set_dataset(self.send(options[:non_deleted_scope_name]))
177
+ def validates_unique(*columns)
178
+ return super(*columns) unless columns.last.kind_of?(Hash) && columns.last.delete(:paranoid)
179
+
180
+ opts = self.class.sequel_paranoid_options
181
+ if deleted?
182
+ columns = columns.map { |c|
183
+ case c
184
+ when Array, Symbol
185
+ [ c, opts[:deleted_at_field_name] ].flatten
186
+ else
187
+ c
188
+ end
189
+ }
190
+
191
+ super(*columns) { |ds|
192
+ ds = ds.send(opts[:deleted_scope_name])
193
+ block_given? ? yield(ds) : ds
194
+ }
195
+ else
196
+ super(*columns) { |ds|
197
+ ds = ds.send(opts[:non_deleted_scope_name])
198
+ block_given? ? yield(ds) : ds
199
+ }
200
+ end
113
201
  end
114
202
  end
203
+
115
204
  end
116
205
  end
117
206
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-paranoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sascha Depold
@@ -24,7 +24,7 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- description: Use this plugin to mark a model instance as deleted without loosing its
27
+ description: Use this plugin to mark a model instance as deleted without losing its
28
28
  actual data.
29
29
  email: sascha@depold.com
30
30
  executables: []
@@ -52,8 +52,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
52
  version: '0'
53
53
  requirements: []
54
54
  rubyforge_project:
55
- rubygems_version: 2.4.5.1
55
+ rubygems_version: 2.6.11
56
56
  signing_key:
57
57
  specification_version: 4
58
- summary: A plugin for the Ruby ORM Sequel, that allows soft deletion of database entries.
58
+ summary: A plugin for the Ruby ORM Sequel that allows soft deletion of database entries.
59
59
  test_files: []