active_archive 5.3.0 → 6.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 +4 -4
- data/README.md +24 -18
- data/lib/active_archive/base.rb +72 -182
- data/lib/active_archive/configuration.rb +1 -2
- data/lib/active_archive/methods.rb +6 -8
- data/lib/active_archive/table_definition.rb +1 -1
- data/lib/active_archive/version.rb +1 -1
- data/lib/generators/active_archive/templates/install.rb +0 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbe2d37d4d70cc6930e4b7fea4f2248418375434cd4d7a704327214e61b36eb5
|
4
|
+
data.tar.gz: 44bcde3d05e859deaf2839356c2c13258fe73f47225177587777f1ca18483400
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 711465df49e3c0f94edc7d0db2231ef2c2dda02f41cb494e6b78de4b2b5ccda7efb73156fc81f322c67359d4f2a7c9568ee00447951bbdf2683b1d209192358b
|
7
|
+
data.tar.gz: 849e0c7fed67835e44a7d54213c39fb3793001e5b2531c380ed370bf4ee073d7a0fac7bd5d67640c05c6616186cbfdf6c505357ed4b6aae6d82978fefea8f93d
|
data/README.md
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
ActiveArchive is a library for preventing database records from being destroyed.
|
7
7
|
|
8
|
+
**NOTE: version >= '6.0.0' has a small breaking change with the timestamp key and initializer file.**
|
9
|
+
|
8
10
|
## Installation
|
9
11
|
|
10
12
|
Add this line to your application's Gemfile:
|
@@ -24,6 +26,7 @@ Or install it yourself as:
|
|
24
26
|
## Table of Contents
|
25
27
|
|
26
28
|
* [Configurations](#configurations)
|
29
|
+
* [Usage](#usage)
|
27
30
|
* [Methods](#methods)
|
28
31
|
* [Scopes](#scopes)
|
29
32
|
|
@@ -35,7 +38,27 @@ Or install it yourself as:
|
|
35
38
|
```ruby
|
36
39
|
ActiveArchive.configure do |config|
|
37
40
|
config.all_records_archivable = false
|
38
|
-
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
To work properly, models which could be archived must have column `archived_at` with type `datetime`. If the model table has not this column, record will be destroy instead of archived.
|
46
|
+
|
47
|
+
|
48
|
+
For adding this column, you can use this migration, as example:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class AddArchivedAtColumns < ActiveRecord::Migration
|
52
|
+
def change
|
53
|
+
# Adds archived_at automatically
|
54
|
+
t.timestamp
|
55
|
+
|
56
|
+
# Does NOT add archived_at automatically
|
57
|
+
t.timestamp archive: false
|
58
|
+
|
59
|
+
# Manual column
|
60
|
+
add_column :your_model, :archived_at, :datetime
|
61
|
+
end
|
39
62
|
end
|
40
63
|
```
|
41
64
|
|
@@ -44,43 +67,26 @@ end
|
|
44
67
|
**Options:**
|
45
68
|
* `archive`
|
46
69
|
* `archive_all`
|
47
|
-
* `archive_all!`
|
48
|
-
* `destroy`
|
49
|
-
* `destroy_all`
|
50
|
-
* `destroy_all!`
|
51
|
-
* `delete`
|
52
|
-
* `delete_all`
|
53
70
|
* `unarchive`
|
54
71
|
* `unarchive_all`
|
55
|
-
* `undestroy`
|
56
|
-
* `undestroy_all`
|
57
72
|
|
58
73
|
```ruby
|
59
74
|
User.first.archive #=> archives User record and dependents
|
60
|
-
User.first.destroy #=> archives User record and dependents
|
61
|
-
User.first.delete #=> deletes User record and dependents
|
62
75
|
User.first.unarchive #=> unarchives User record and dependents
|
63
76
|
|
64
77
|
User.first.to_archival #=> returns archival state string
|
65
78
|
|
66
|
-
User.first.archive(:force) #=> destroys User record and dependents
|
67
|
-
User.first.destroy(:force) #=> destroys User record and dependents
|
68
|
-
|
69
79
|
User.archive_all #=> archives all User records and dependents
|
70
|
-
User.destroy_all #=> archives all User records and dependents
|
71
|
-
User.delete_all #=> deletes all User records and dependents
|
72
80
|
User.unarchive_all #=> unarchives all User record and dependents
|
73
81
|
```
|
74
82
|
|
75
83
|
## Scopes
|
76
84
|
|
77
85
|
**Options:**
|
78
|
-
* `default`
|
79
86
|
* `archived`
|
80
87
|
* `unarchived`
|
81
88
|
|
82
89
|
```ruby
|
83
|
-
User.all #=> returns all records
|
84
90
|
User.archived.all #=> returns only archived record
|
85
91
|
User.unarchived.all #=> returns only unarchived record
|
86
92
|
```
|
data/lib/active_archive/base.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# require "active_record/attribute_mutation_tracker"
|
4
|
-
|
5
3
|
module ActiveArchive
|
6
4
|
module Base
|
7
5
|
|
@@ -9,7 +7,16 @@ module ActiveArchive
|
|
9
7
|
base.extend Methods
|
10
8
|
base.extend Scopes
|
11
9
|
|
12
|
-
base.instance_eval
|
10
|
+
base.instance_eval do
|
11
|
+
define_model_callbacks :archive, only: %i[before after]
|
12
|
+
define_model_callbacks :unarchive, only: %i[before after]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def archival?
|
17
|
+
return destroyed? if unarchivable?
|
18
|
+
|
19
|
+
(will_save_change_to_archived_at? || saved_change_to_archived_at?) && archived?
|
13
20
|
end
|
14
21
|
|
15
22
|
def archivable?
|
@@ -20,230 +27,113 @@ module ActiveArchive
|
|
20
27
|
archivable? ? !archived_at.nil? : destroyed?
|
21
28
|
end
|
22
29
|
|
23
|
-
def
|
24
|
-
|
25
|
-
end
|
30
|
+
def archive
|
31
|
+
return destroy if unarchivable?
|
26
32
|
|
27
|
-
def unarchivable?
|
28
|
-
!archivable?
|
29
|
-
end
|
30
|
-
|
31
|
-
def unarchive(opts = nil)
|
32
33
|
with_transaction_returning_status do
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
self
|
37
|
-
end
|
38
|
-
end
|
34
|
+
run_callbacks :archive do
|
35
|
+
mark_as_archived
|
36
|
+
mark_relections_as_archived
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
def destroy(force = nil)
|
43
|
-
with_transaction_returning_status do
|
44
|
-
if unarchivable? || should_force_destroy?(force)
|
45
|
-
permanently_delete_records_after { super() }
|
46
|
-
else
|
47
|
-
destroy_with_active_archive(force)
|
48
|
-
|
49
|
-
if ::ActiveRecord::VERSION::MAJOR >= 5
|
50
|
-
archived_at_will_change!
|
51
|
-
elsif ::ActiveRecord::VERSION::MAJOR >= 4
|
52
|
-
attribute_will_change!('archived_at')
|
53
|
-
end
|
38
|
+
self
|
54
39
|
end
|
55
40
|
end
|
56
41
|
end
|
57
42
|
|
58
|
-
|
43
|
+
def unarchival?
|
44
|
+
return !destroyed? if unarchivable?
|
59
45
|
|
60
|
-
|
61
|
-
I18n.t("active_archive.archival.#{archived? ? :archived : :unarchived}")
|
46
|
+
(will_save_change_to_archived_at? || saved_change_to_archived_at?) && unarchived?
|
62
47
|
end
|
63
48
|
|
64
|
-
|
65
|
-
|
66
|
-
def unarchival
|
67
|
-
[
|
68
|
-
lambda do |validate|
|
69
|
-
unarchive_destroyed_dependent_records(validate)
|
70
|
-
end,
|
71
|
-
lambda do |validate|
|
72
|
-
run_callbacks(:unarchive) do
|
73
|
-
set_archived_at(nil, validate)
|
74
|
-
|
75
|
-
each_counter_cache do |assoc_class, counter_cache_column, assoc_id|
|
76
|
-
assoc_class.increment_counter(counter_cache_column, assoc_id)
|
77
|
-
end
|
78
|
-
|
79
|
-
true
|
80
|
-
end
|
81
|
-
end
|
82
|
-
]
|
49
|
+
def unarchived?
|
50
|
+
!archived?
|
83
51
|
end
|
84
52
|
|
85
|
-
def
|
86
|
-
|
53
|
+
def unarchivable?
|
54
|
+
!archivable?
|
87
55
|
end
|
88
56
|
|
89
|
-
|
90
|
-
|
91
|
-
return self unless archivable?
|
92
|
-
|
93
|
-
record = get_archived_record
|
94
|
-
|
95
|
-
if ::ActiveRecord::VERSION::MAJOR >= 5
|
96
|
-
record.archived_at_will_change!
|
97
|
-
elsif ::ActiveRecord::VERSION::MAJOR >= 4
|
98
|
-
record.attribute_will_change!('archived_at')
|
99
|
-
end
|
57
|
+
def unarchive
|
58
|
+
return self if unarchivable?
|
100
59
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
60
|
+
with_transaction_returning_status do
|
61
|
+
run_callbacks :unarchive do
|
62
|
+
mark_as_unarchived
|
63
|
+
mark_relections_as_unarchived
|
105
64
|
|
106
|
-
|
107
|
-
@attributes_changed_by_setter = record.send(:saved_changes)
|
108
|
-
elsif ::ActiveRecord::VERSION::MAJOR >= 5 && ::ActiveRecord::VERSION::MINOR >= 1
|
109
|
-
@changed_attributes = record.send(:saved_changes)
|
110
|
-
elsif ::ActiveRecord::VERSION::MAJOR >= 5 && ::ActiveRecord::VERSION::MINOR >= 0
|
111
|
-
@changed_attributes = record.send(:previous_changes)
|
112
|
-
elsif ::ActiveRecord::VERSION::MAJOR >= 4
|
113
|
-
@previously_changed = record.instance_variable_get('@previously_changed')
|
65
|
+
self
|
114
66
|
end
|
115
|
-
|
116
|
-
@attributes = record.instance_variable_get('@attributes')
|
117
|
-
rescue => error
|
118
|
-
record.destroy
|
119
|
-
raise error
|
120
67
|
end
|
121
68
|
end
|
122
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
123
|
-
|
124
|
-
def each_counter_cache
|
125
|
-
_reflections.each do |name, reflection|
|
126
|
-
next unless respond_to?(name.to_sym)
|
127
69
|
|
128
|
-
|
129
|
-
|
130
|
-
next if association.nil?
|
131
|
-
next unless reflection.belongs_to? && reflection.counter_cache_column
|
132
|
-
|
133
|
-
yield(association.class, reflection.counter_cache_column, send(reflection.foreign_key))
|
134
|
-
end
|
70
|
+
def to_archival
|
71
|
+
I18n.t("active_archive.archival.#{archived? ? :archived : :unarchived}")
|
135
72
|
end
|
136
73
|
|
137
|
-
|
138
|
-
run_callbacks(:destroy) do
|
139
|
-
if archived? || new_record?
|
140
|
-
save
|
141
|
-
else
|
142
|
-
set_archived_at(Time.now, force)
|
143
|
-
|
144
|
-
each_counter_cache do |assoc_class, counter_cache_column, assoc_id|
|
145
|
-
assoc_class.decrement_counter(counter_cache_column, assoc_id)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
true
|
150
|
-
end
|
74
|
+
private
|
151
75
|
|
152
|
-
|
76
|
+
def mark_as_archived
|
77
|
+
self.archived_at = Time.now
|
78
|
+
save(validate: false)
|
153
79
|
end
|
154
80
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
query = "#{qtn}.archived_at > ? AND #{qtn}.archived_at < ?"
|
159
|
-
|
160
|
-
send(name).unscope(where: :archived_at)
|
161
|
-
.where(query, archived_at - window, archived_at + window)
|
81
|
+
def mark_as_unarchived
|
82
|
+
self.archived_at = nil
|
83
|
+
save(validate: false)
|
162
84
|
end
|
163
85
|
|
164
|
-
def
|
165
|
-
|
166
|
-
|
167
|
-
destroyed_dependent_record.try(:unarchive, force)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
reload
|
172
|
-
end
|
173
|
-
|
174
|
-
# rubocop:disable Metrics/AbcSize
|
175
|
-
def destroyed_dependent_relations
|
176
|
-
dependent_permanent_reflections(self.class).map do |name, relation|
|
177
|
-
case relation.macro.to_s.gsub('has_', '').to_sym
|
178
|
-
when :many
|
179
|
-
if archived_at
|
180
|
-
add_record_window(send(name), name, relation)
|
181
|
-
else
|
182
|
-
send(name).unscope(where: :archived_at)
|
183
|
-
end
|
184
|
-
when :one, :belongs_to
|
185
|
-
self.class.unscoped { Array(send(name)) }
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
# rubocop:enable Metrics/AbcSize
|
86
|
+
def mark_relections_as_archived
|
87
|
+
self.class.reflections.each do |table_name, reflection|
|
88
|
+
next unless dependent_destroy?(reflection)
|
190
89
|
|
191
|
-
|
192
|
-
|
193
|
-
rescue NoMethodError
|
194
|
-
# do nothing
|
195
|
-
end
|
90
|
+
dependents = reflection_dependents(table_name)
|
91
|
+
next if dependents.nil?
|
196
92
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
93
|
+
action = case [reflection_marco(reflection), archivable?]
|
94
|
+
when [:one, true] then :archive
|
95
|
+
when [:one, false] then :destroy
|
96
|
+
when [:many, true] then :archive_all
|
97
|
+
when [:many, false] then :destroy_all
|
98
|
+
end
|
201
99
|
|
202
|
-
|
100
|
+
dependents.send(action)
|
203
101
|
end
|
204
102
|
end
|
205
103
|
|
206
|
-
def
|
207
|
-
|
208
|
-
|
209
|
-
permanently_delete_records(dependent_records) if result
|
210
|
-
result
|
211
|
-
end
|
104
|
+
def mark_relections_as_unarchived
|
105
|
+
self.class.reflections.each do |table_name, reflection|
|
106
|
+
next unless dependent_destroy?(reflection)
|
212
107
|
|
213
|
-
|
214
|
-
|
215
|
-
ids.each do |id|
|
216
|
-
record = klass.unscoped.where(klass.primary_key => id).first
|
217
|
-
next unless record
|
108
|
+
klass = relection_klass(table_name)
|
109
|
+
next unless klass.archivable?
|
218
110
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
111
|
+
dependents = reflection_dependents(table_name)
|
112
|
+
next if dependents.nil?
|
113
|
+
|
114
|
+
action = case reflection_marco(reflection)
|
115
|
+
when :one then :unarchive
|
116
|
+
when :many then :unarchive_all
|
117
|
+
end
|
224
118
|
|
225
|
-
|
226
|
-
klass.reflections.select do |_, reflection|
|
227
|
-
reflection.options[:dependent] == :destroy
|
119
|
+
dependents.send(action)
|
228
120
|
end
|
229
121
|
end
|
230
122
|
|
231
|
-
def
|
232
|
-
|
233
|
-
reflection.klass.archivable?
|
234
|
-
end
|
123
|
+
def dependent_destroy?(reflection)
|
124
|
+
reflection.options[:dependent] == :destroy
|
235
125
|
end
|
236
126
|
|
237
|
-
def
|
238
|
-
|
127
|
+
def reflection_dependents(table_name)
|
128
|
+
send(table_name)
|
239
129
|
end
|
240
130
|
|
241
|
-
def
|
242
|
-
|
131
|
+
def relection_klass(table_name)
|
132
|
+
table_name.classify.constantize
|
243
133
|
end
|
244
134
|
|
245
|
-
def
|
246
|
-
|
135
|
+
def reflection_marco(reflection)
|
136
|
+
reflection.macro.to_s.gsub('has_', '').to_sym
|
247
137
|
end
|
248
138
|
|
249
139
|
end
|
@@ -3,11 +3,10 @@
|
|
3
3
|
module ActiveArchive
|
4
4
|
class Configuration
|
5
5
|
|
6
|
-
attr_accessor :all_records_archivable
|
6
|
+
attr_accessor :all_records_archivable
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@all_records_archivable = false
|
10
|
-
@dependent_record_window = 3.seconds
|
11
10
|
end
|
12
11
|
|
13
12
|
end
|
@@ -8,20 +8,18 @@ module ActiveArchive
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def archive_all(conditions = nil)
|
11
|
-
(conditions
|
11
|
+
records(conditions, action: :archive)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
(conditions
|
14
|
+
def unarchive_all(conditions = nil)
|
15
|
+
records(conditions, action: :unarchive)
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
private
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def records(conditions = nil, action:)
|
21
|
+
where(conditions).find_each(&action)
|
22
22
|
end
|
23
23
|
|
24
|
-
alias_method :undestroy_all, :unarchive_all
|
25
|
-
|
26
24
|
end
|
27
25
|
end
|
@@ -10,7 +10,7 @@ module ActiveArchive
|
|
10
10
|
column(:updated_at, :datetime, options)
|
11
11
|
|
12
12
|
return unless ActiveArchive.configuration.all_records_archivable == true
|
13
|
-
return if options[:
|
13
|
+
return if options[:archive] == false
|
14
14
|
|
15
15
|
options[:null] = true
|
16
16
|
column(:archived_at, :datetime, options)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_archive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|