permanent_records 4.1.0 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/Rakefile +13 -6
- data/VERSION +1 -1
- data/ci +1 -1
- data/lib/permanent_records.rb +126 -85
- data/permanent_records.gemspec +21 -14
- metadata +34 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fc77ef2eb0c9943d477c2bb228a11d31c1fd0a7
|
4
|
+
data.tar.gz: 748645f6c54d8f067c20c494ed451d9cdccaffd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c72d7ef29d6a675e511d67194a265c6b2b183a7d629deed3fc63b9e911a75dbd8b59c2a7e57c81cd15d9c12af6a8f4a6115b9fe5b6cbf059139c3ee8465c7dcf
|
7
|
+
data.tar.gz: 925ce8fe43fae831d5bd73291bce29d9d0d6747c89d6a08e2d2b49239b27c328352576c73a4f3afb4c1bdae82974ba955a94fe3e7ee9c3e6d262c23eaa0421f8
|
data/.travis.yml
CHANGED
data/Rakefile
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
require 'yaml'
|
3
|
+
require 'English'
|
3
4
|
Bundler::GemHelper.install_tasks
|
4
5
|
|
5
|
-
|
6
|
+
CONFIG = YAML.load_file(
|
7
|
+
File.expand_path('spec/support/database.yml', File.dirname(__FILE__))
|
8
|
+
)
|
6
9
|
|
7
10
|
def test_database_exists?
|
8
|
-
system "psql -l | grep -q #{
|
9
|
-
|
11
|
+
system "psql -l | grep -q #{CONFIG['test'][:database]}"
|
12
|
+
$CHILD_STATUS.success?
|
10
13
|
end
|
11
14
|
|
12
15
|
def create_test_database
|
13
|
-
system "createdb #{
|
16
|
+
system "createdb #{CONFIG['test'][:database]}"
|
14
17
|
end
|
15
18
|
|
16
19
|
namespace :db do
|
@@ -19,9 +22,13 @@ namespace :db do
|
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
|
-
|
25
|
+
require 'rubocop/rake_task'
|
26
|
+
RuboCop::RakeTask.new
|
23
27
|
|
24
28
|
desc 'Run all tests'
|
25
|
-
task :
|
29
|
+
task spec: 'db:create' do
|
26
30
|
exec 'rspec'
|
27
31
|
end
|
32
|
+
|
33
|
+
task default: [:spec, :rubocop]
|
34
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.1.
|
1
|
+
4.1.1
|
data/ci
CHANGED
data/lib/permanent_records.rb
CHANGED
@@ -1,40 +1,47 @@
|
|
1
|
+
# PermanentRecords works with ActiveRecord to set deleted_at columns with a
|
2
|
+
# timestamp reflecting when a record was 'deleted' instead of actually deleting
|
3
|
+
# the record. All dependent records and associations are treated exactly as
|
4
|
+
# you'd expect: If there's a deleted_at column then the record is preserved,
|
5
|
+
# otherwise it's deleted.
|
1
6
|
module PermanentRecords
|
2
|
-
|
3
7
|
# This module defines the public api that you can
|
4
8
|
# use in your model instances.
|
5
9
|
#
|
6
10
|
# * is_permanent? #=> true/false, depending if you have a deleted_at column
|
7
11
|
# * deleted? #=> true/false, depending if you've called .destroy
|
8
|
-
# * destroy #=> sets deleted_at, your record is now in
|
12
|
+
# * destroy #=> sets deleted_at, your record is now in
|
13
|
+
# the .destroyed scope
|
9
14
|
# * revive #=> undo the destroy
|
10
|
-
module ActiveRecord
|
15
|
+
module ActiveRecord # rubocop:disable Metrics/ModuleLength
|
11
16
|
def self.included(base)
|
12
|
-
|
13
17
|
base.extend Scopes
|
14
18
|
base.extend IsPermanent
|
15
19
|
|
16
20
|
base.instance_eval do
|
17
21
|
define_model_callbacks :revive
|
18
|
-
|
19
|
-
before_revive :revive_destroyed_dependent_records
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
def is_permanent?
|
25
|
+
def is_permanent? # rubocop:disable Style/PredicateName
|
24
26
|
respond_to?(:deleted_at)
|
25
27
|
end
|
26
28
|
|
27
29
|
def deleted?
|
28
30
|
if is_permanent?
|
29
|
-
!!deleted_at
|
31
|
+
!!deleted_at # rubocop:disable Style/DoubleNegation
|
30
32
|
else
|
31
33
|
destroyed?
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
def revive(
|
37
|
+
def revive(options = nil)
|
36
38
|
with_transaction_returning_status do
|
37
|
-
|
39
|
+
if PermanentRecords.should_revive_parent_first?(options)
|
40
|
+
revival.reverse
|
41
|
+
else
|
42
|
+
revival
|
43
|
+
end.each { |p| p.call(options) }
|
44
|
+
|
38
45
|
self
|
39
46
|
end
|
40
47
|
end
|
@@ -51,23 +58,46 @@ module PermanentRecords
|
|
51
58
|
|
52
59
|
private
|
53
60
|
|
61
|
+
def revival
|
62
|
+
[
|
63
|
+
lambda do |validate|
|
64
|
+
revive_destroyed_dependent_records(validate)
|
65
|
+
end,
|
66
|
+
lambda do |validate|
|
67
|
+
run_callbacks(:revive) { set_deleted_at(nil, validate) }
|
68
|
+
end
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
# rubocop:disable Style/AccessorMethodName
|
73
|
+
def get_deleted_record
|
74
|
+
# Looking for parent on STI case
|
75
|
+
if respond_to?(:parent_id) && parent_id.present?
|
76
|
+
self.class.unscoped.find(parent_id)
|
77
|
+
else
|
78
|
+
self.class.unscoped.find(id)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# rubocop:disable Metrics/MethodLength
|
54
83
|
def set_deleted_at(value, force = nil)
|
55
84
|
return self unless is_permanent?
|
56
|
-
record =
|
85
|
+
record = get_deleted_record
|
57
86
|
record.deleted_at = value
|
58
87
|
begin
|
59
|
-
# we call save! instead of update_attribute so an
|
60
|
-
# error will be raised if the record isn't
|
61
|
-
# disregard validation
|
88
|
+
# we call save! instead of update_attribute so an
|
89
|
+
# ActiveRecord::RecordInvalid error will be raised if the record isn't
|
90
|
+
# valid. (This prevents reviving records that disregard validation
|
91
|
+
# constraints,)
|
62
92
|
if PermanentRecords.should_ignore_validations?(force)
|
63
|
-
record.save(:
|
93
|
+
record.save(validate: false)
|
64
94
|
else
|
65
95
|
record.save!
|
66
96
|
end
|
67
97
|
@attributes = record.instance_variable_get('@attributes')
|
68
|
-
rescue
|
69
|
-
# trigger dependent record destruction (they were revived before this
|
70
|
-
# which cannot be revived due to validations)
|
98
|
+
rescue => e
|
99
|
+
# trigger dependent record destruction (they were revived before this
|
100
|
+
# record, which cannot be revived due to validations)
|
71
101
|
record.destroy
|
72
102
|
raise e
|
73
103
|
end
|
@@ -81,28 +111,36 @@ module PermanentRecords
|
|
81
111
|
deleted? ? self : false
|
82
112
|
end
|
83
113
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
114
|
+
def add_record_window(_request, name, reflection)
|
115
|
+
send(name).unscope(where: :deleted_at).where(
|
116
|
+
[
|
117
|
+
"#{reflection.quoted_table_name}.deleted_at > ?" \
|
118
|
+
' AND ' \
|
119
|
+
"#{reflection.quoted_table_name}.deleted_at < ?",
|
120
|
+
deleted_at - PermanentRecords.dependent_record_window,
|
121
|
+
deleted_at + PermanentRecords.dependent_record_window
|
122
|
+
]
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
# TODO: Feel free to refactor this without polluting the ActiveRecord
|
127
|
+
# namespace.
|
128
|
+
# rubocop:disable Metrics/AbcSize
|
129
|
+
def revive_destroyed_dependent_records(force = nil)
|
130
|
+
PermanentRecords.dependent_permanent_reflections(self.class)
|
131
|
+
.each do |name, reflection|
|
132
|
+
cardinality = reflection.macro.to_s.gsub('has_', '').to_sym
|
133
|
+
case cardinality
|
134
|
+
when :many
|
135
|
+
if deleted_at
|
136
|
+
add_record_window(send(name), name, reflection)
|
137
|
+
else
|
138
|
+
send(name)
|
102
139
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
140
|
+
when :one, :belongs_to
|
141
|
+
self.class.unscoped { Array(send(name)) }
|
142
|
+
end.to_a.flatten.compact.each do |dependent|
|
143
|
+
dependent.revive(force)
|
106
144
|
end
|
107
145
|
|
108
146
|
# and update the reflection cache
|
@@ -111,46 +149,34 @@ module PermanentRecords
|
|
111
149
|
end
|
112
150
|
|
113
151
|
def attempt_notifying_observers(callback)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
# do nothing: this model isn't being observed
|
118
|
-
end
|
152
|
+
notify_observers(callback)
|
153
|
+
rescue NoMethodError # rubocop:disable Lint/HandleExceptions
|
154
|
+
# do nothing: this model isn't being observed
|
119
155
|
end
|
120
156
|
|
121
|
-
# return the records corresponding to an association with the `:dependent
|
122
|
-
|
123
|
-
|
124
|
-
|
157
|
+
# return the records corresponding to an association with the `:dependent
|
158
|
+
# => :destroy` option
|
159
|
+
def dependent_record_ids
|
125
160
|
# check which dependent records are to be destroyed
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
next unless records.size > 0 # skip if there are no dependent record instances
|
132
|
-
else
|
133
|
-
records = [] << records
|
134
|
-
end
|
135
|
-
dependent_record = records.first
|
136
|
-
next if dependent_record.nil?
|
137
|
-
dependent_records[dependent_record.class] = records.map(&:id)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
dependent_records
|
141
|
-
end
|
142
|
-
|
143
|
-
# If we force the destruction of the record, we will need to force the destruction of dependent records if the
|
144
|
-
# user specified `:dependent => :destroy` in the model.
|
145
|
-
# By default, the call to super/destroy_with_permanent_records (i.e. the &block param) will only soft delete
|
146
|
-
# the dependent records; we keep track of the dependent records
|
147
|
-
# that have `:dependent => :destroy` and call destroy(force) on them after the call to super
|
148
|
-
def permanently_delete_records_after(&block)
|
149
|
-
dependent_records = get_dependent_records
|
150
|
-
result = block.call
|
151
|
-
if result
|
152
|
-
permanently_delete_records(dependent_records)
|
161
|
+
PermanentRecords.dependent_reflections(self.class)
|
162
|
+
.reduce({}) do |records, (key, _)|
|
163
|
+
found = Array(send(key)).compact
|
164
|
+
next records unless found.size > 0
|
165
|
+
records.update found.first.class => found.map(&:id)
|
153
166
|
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# If we force the destruction of the record, we will need to force the
|
170
|
+
# destruction of dependent records if the user specified `:dependent =>
|
171
|
+
# :destroy` in the model. By default, the call to
|
172
|
+
# super/destroy_with_permanent_records (i.e. the &block param) will only
|
173
|
+
# soft delete the dependent records; we keep track of the dependent records
|
174
|
+
# that have `:dependent => :destroy` and call destroy(force) on them after
|
175
|
+
# the call to super
|
176
|
+
def permanently_delete_records_after(&_block)
|
177
|
+
dependent_records = dependent_record_ids
|
178
|
+
result = yield
|
179
|
+
permanently_delete_records(dependent_records) if result
|
154
180
|
result
|
155
181
|
end
|
156
182
|
|
@@ -158,11 +184,8 @@ module PermanentRecords
|
|
158
184
|
def permanently_delete_records(dependent_records)
|
159
185
|
dependent_records.each do |klass, ids|
|
160
186
|
ids.each do |id|
|
161
|
-
record =
|
162
|
-
|
163
|
-
rescue ::ActiveRecord::RecordNotFound
|
164
|
-
next # the record has already been deleted, possibly due to another association with `:dependent => :destroy`
|
165
|
-
end
|
187
|
+
record = klass.unscoped.where(klass.primary_key => id).first
|
188
|
+
next unless record
|
166
189
|
record.deleted_at = nil
|
167
190
|
record.destroy(:force)
|
168
191
|
end
|
@@ -170,6 +193,7 @@ module PermanentRecords
|
|
170
193
|
end
|
171
194
|
end
|
172
195
|
|
196
|
+
# ActiveRelation scopes
|
173
197
|
module Scopes
|
174
198
|
def deleted
|
175
199
|
where arel_table[:deleted_at].not_eq(nil)
|
@@ -180,22 +204,27 @@ module PermanentRecords
|
|
180
204
|
end
|
181
205
|
end
|
182
206
|
|
207
|
+
# Included into ActiveRecord for all models
|
183
208
|
module IsPermanent
|
184
|
-
def is_permanent?
|
185
|
-
columns.detect {|c| 'deleted_at' == c.name}
|
209
|
+
def is_permanent? # rubocop:disable Style/PredicateName
|
210
|
+
columns.detect { |c| 'deleted_at' == c.name }
|
186
211
|
end
|
187
212
|
end
|
188
213
|
|
189
214
|
def self.should_force_destroy?(force)
|
190
|
-
if Hash
|
215
|
+
if force.is_a?(Hash)
|
191
216
|
force[:force]
|
192
217
|
else
|
193
218
|
:force == force
|
194
219
|
end
|
195
220
|
end
|
196
221
|
|
222
|
+
def self.should_revive_parent_first?(order)
|
223
|
+
order.is_a?(Hash) && true == order[:reverse]
|
224
|
+
end
|
225
|
+
|
197
226
|
def self.should_ignore_validations?(force)
|
198
|
-
Hash
|
227
|
+
force.is_a?(Hash) && false == force[:validate]
|
199
228
|
end
|
200
229
|
|
201
230
|
def self.dependent_record_window
|
@@ -205,7 +234,19 @@ module PermanentRecords
|
|
205
234
|
def self.dependent_record_window=(time_value)
|
206
235
|
@dependent_record_window = time_value
|
207
236
|
end
|
237
|
+
|
238
|
+
def self.dependent_reflections(klass)
|
239
|
+
klass.reflections.select do |_, reflection|
|
240
|
+
# skip if there are no dependent record instances
|
241
|
+
reflection.options[:dependent] == :destroy
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.dependent_permanent_reflections(klass)
|
246
|
+
dependent_reflections(klass).select do |_name, reflection|
|
247
|
+
reflection.klass.is_permanent?
|
248
|
+
end
|
249
|
+
end
|
208
250
|
end
|
209
251
|
|
210
252
|
ActiveRecord::Base.send :include, PermanentRecords::ActiveRecord
|
211
|
-
|
data/permanent_records.gemspec
CHANGED
@@ -1,20 +1,27 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
Gem::Specification.new do |s|
|
3
|
-
s.name =
|
3
|
+
s.name = 'permanent_records'
|
4
4
|
s.version = File.read('VERSION')
|
5
5
|
s.license = 'MIT'
|
6
6
|
|
7
|
-
s.authors = [
|
8
|
-
|
9
|
-
|
10
|
-
s.
|
7
|
+
s.authors = ['Jack Danger Canty', 'David Sulc', 'Joe Nelson',
|
8
|
+
'Trond Arve Nordheim', 'Josh Teneycke', 'Maximilian Herold',
|
9
|
+
'Hugh Evans']
|
10
|
+
s.summary = 'Soft-delete your ActiveRecord records'
|
11
|
+
s.description = <<-EOS
|
12
|
+
Never Lose Data. Rather than deleting rows this sets Record#deleted_at and
|
13
|
+
gives you all the scopes you need to work with your data.
|
14
|
+
EOS
|
15
|
+
s.email = 'github@jackcanty.com'
|
11
16
|
s.extra_rdoc_files = [
|
12
|
-
|
13
|
-
|
17
|
+
'LICENSE',
|
18
|
+
'README.md'
|
14
19
|
]
|
15
|
-
s.files = `git ls-files -z`.split("\x0").reject
|
16
|
-
|
17
|
-
|
20
|
+
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
s.homepage = 'https://github.com/JackDanger/permanent_records'
|
24
|
+
s.require_paths = ['lib']
|
18
25
|
|
19
26
|
# For testing against multiple AR versions
|
20
27
|
ver = ENV['AR_TEST_VERSION']
|
@@ -22,10 +29,10 @@ Gem::Specification.new do |s|
|
|
22
29
|
|
23
30
|
s.add_runtime_dependency('activerecord', ver || '>= 4.2.0')
|
24
31
|
s.add_runtime_dependency('activesupport', ver || '>= 4.2.0')
|
25
|
-
s.add_development_dependency('rake') # For Travis-ci
|
26
|
-
s.add_development_dependency('sqlite3')
|
27
|
-
s.add_development_dependency('pry-byebug')
|
28
32
|
s.add_development_dependency('database_cleaner', '>= 1.5.1')
|
33
|
+
s.add_development_dependency('pry-byebug')
|
34
|
+
s.add_development_dependency('rake') # For Travis-ci
|
29
35
|
s.add_development_dependency('rspec', '~> 2.14.1')
|
36
|
+
s.add_development_dependency('rubocop')
|
37
|
+
s.add_development_dependency('sqlite3')
|
30
38
|
end
|
31
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: permanent_records
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Danger Canty
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date:
|
17
|
+
date: 2016-02-02 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: activerecord
|
@@ -45,21 +45,21 @@ dependencies:
|
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 4.2.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
48
|
+
name: database_cleaner
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 1.5.1
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
60
|
+
version: 1.5.1
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
|
-
name:
|
62
|
+
name: pry-byebug
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - ">="
|
@@ -73,7 +73,7 @@ dependencies:
|
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '0'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
|
-
name:
|
76
|
+
name: rake
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
79
|
- - ">="
|
@@ -87,35 +87,50 @@ dependencies:
|
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '0'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
|
-
name:
|
90
|
+
name: rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 2.14.1
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 2.14.1
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rubocop
|
91
105
|
requirement: !ruby/object:Gem::Requirement
|
92
106
|
requirements:
|
93
107
|
- - ">="
|
94
108
|
- !ruby/object:Gem::Version
|
95
|
-
version:
|
109
|
+
version: '0'
|
96
110
|
type: :development
|
97
111
|
prerelease: false
|
98
112
|
version_requirements: !ruby/object:Gem::Requirement
|
99
113
|
requirements:
|
100
114
|
- - ">="
|
101
115
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
116
|
+
version: '0'
|
103
117
|
- !ruby/object:Gem::Dependency
|
104
|
-
name:
|
118
|
+
name: sqlite3
|
105
119
|
requirement: !ruby/object:Gem::Requirement
|
106
120
|
requirements:
|
107
|
-
- - "
|
121
|
+
- - ">="
|
108
122
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
123
|
+
version: '0'
|
110
124
|
type: :development
|
111
125
|
prerelease: false
|
112
126
|
version_requirements: !ruby/object:Gem::Requirement
|
113
127
|
requirements:
|
114
|
-
- - "
|
128
|
+
- - ">="
|
115
129
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
117
|
-
description:
|
118
|
-
|
130
|
+
version: '0'
|
131
|
+
description: |
|
132
|
+
Never Lose Data. Rather than deleting rows this sets Record#deleted_at and
|
133
|
+
gives you all the scopes you need to work with your data.
|
119
134
|
email: github@jackcanty.com
|
120
135
|
executables: []
|
121
136
|
extensions: []
|
@@ -155,8 +170,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
170
|
version: '0'
|
156
171
|
requirements: []
|
157
172
|
rubyforge_project:
|
158
|
-
rubygems_version: 2.
|
173
|
+
rubygems_version: 2.5.1
|
159
174
|
signing_key:
|
160
175
|
specification_version: 4
|
161
176
|
summary: Soft-delete your ActiveRecord records
|
162
177
|
test_files: []
|
178
|
+
has_rdoc:
|