persistent_record 0.1.3

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZGYzNzA2ZDc4Yjk3OWE3OTAwNzNkNGI2MGJkMDBmMDBmYjI1NzM5NA==
5
+ data.tar.gz: !binary |-
6
+ MmRjZTlhYzFiNTU1ZjU3ZDQ2ZjRhODM4NmY5ZTgyYzRmNTkxYzc2Zg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZTk3ZGEwNTU1MTk4YzJiZTQzNjY0MjA3Y2YzNDQyMzA4NzUzYjRhNjUzYmEy
10
+ Njg2NTgxZDQ3MzQ3ZWNhODY2MDNiNjlhMjQ4Yjc1N2ZiMDNlYjIxMmQwYjYx
11
+ MTBiZjRmNDgwMTEzMTFhOGE0MTk3MDMwZDAxYzIxYmRiODhlODM=
12
+ data.tar.gz: !binary |-
13
+ MTc5YzRkYTA4ZjAxNDVhZDI1MjBjN2NkZTQwNDE0ZWE2YjJhNjU4MWJiZjk0
14
+ Y2M3ZmMxMTNjMGZmMWMzYjk0ODUxMTg2NTZhNzAyNTlkNDE1ZDQ4ODliMDVj
15
+ ZGQ1MjkzMjhiMGIwNzFjNmQ5NmFiOTdhZTg4N2JmMTFjZmM2YjY=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/DOCUMENTATION.md ADDED
@@ -0,0 +1,26 @@
1
+ # Documentation
2
+
3
+ ---
4
+
5
+ ## Fetching records
6
+
7
+ ### `with_discarded`
8
+ Returns records including discarded records. Has an alias; `discarded`.
9
+
10
+ ### `only_discarded`
11
+ Returns only discarded records. Has an alias; `discarded!`.
12
+
13
+ ### `discarded?`
14
+ Validates whether or not record is discarded.
15
+
16
+ ### `discard`
17
+ Discards record, alias of `destroy`.
18
+
19
+ ### `restore!`
20
+ Restores current discarded record.
21
+
22
+ ### `restore` (*int|array* id, *hash* options = {})
23
+ Restores one or several records.
24
+
25
+ ### `zap!`
26
+ Deletes record entierly; **removes it from the database**.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Robin Grass
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # PersistentRecord
2
+
3
+ Introduces soft deletions for ActiveRecord. Heavily inspired by [radar/paranoia](https://github.com/radar/paranoia).
4
+
5
+ ## Installation
6
+
7
+ gem 'persistent_record', github: 'lessthanthree/persistent_record'
8
+
9
+ ## Usage
10
+
11
+ Migrate your models by adding a "deleted_at" timestamp, `rails generate migration AddDeletedAtColumnToModels deleted_at:datetime:index`
12
+
13
+ class YourModel < ActiveRecord::Base
14
+
15
+ acts_as_persistent
16
+
17
+ end
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ module PersistentRecord
2
+ VERSION = '0.1.3'
3
+ end
@@ -0,0 +1,188 @@
1
+ require "persistent_record/version"
2
+ require 'active_record' unless defined? ActiveRecord
3
+
4
+
5
+
6
+ module PersistentRecord
7
+
8
+ def self.included(source)
9
+ source.extend Query
10
+ source.extend Callbacks
11
+ end
12
+
13
+ module Query
14
+
15
+ def persistent?
16
+ true
17
+ end
18
+
19
+ def with_discarded
20
+ if ActiveRecord::VERSION::STRING >= "4.1"
21
+ unscope where: record_deleted_at_column
22
+ else
23
+ all.tap { |x| x.default_scoped = false }
24
+ end
25
+ end
26
+
27
+ def only_discarded
28
+ with_discarded.where.not(record_deleted_at_column => nil)
29
+ end
30
+
31
+ alias :discarded :with_discarded
32
+ alias :discarded! :only_discarded
33
+
34
+ def restore(id, options = {})
35
+ if id.is_a?(Array)
36
+ id.map { |one_id| restore(one_id, options) }
37
+ else
38
+ only_discarded.find(id).restore!(options)
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ module Callbacks
45
+
46
+ def self.extended(source)
47
+
48
+ source.define_callbacks :restore
49
+
50
+ source.define_singleton_method("before_restore") do |*args, &block|
51
+ set_callback(:restore, :before, *args, &block)
52
+ end
53
+
54
+ source.define_singleton_method("around_restore") do |*args, &block|
55
+ set_callback(:restore, :around, *args, &block)
56
+ end
57
+
58
+ source.define_singleton_method("after_restore") do |*args, &block|
59
+ set_callback(:restore, :after, *args, &block)
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ def destroy
67
+ callbacks_result = run_callbacks(:destroy) { touch_record_deleted_at_column(true) }
68
+ callbacks_result ? self : false
69
+ end
70
+
71
+ if ActiveRecord::VERSION::STRING >= "4.1"
72
+ def destroy!
73
+ discarded? ? super : destroy || raise(ActiveRecord::RecordNotDestroyed)
74
+ end
75
+ end
76
+
77
+ def delete
78
+ return if new_record?
79
+ touch_record_deleted_at_column(false)
80
+ end
81
+
82
+ alias :discard :destroy
83
+
84
+ def restore!(options = {})
85
+ ActiveRecord::Base.transaction do
86
+ run_callbacks(:restore) do
87
+ update_column record_deleted_at_column, nil
88
+ restore_associated_records if options[:recursive]
89
+ end
90
+ end
91
+ end
92
+
93
+ def discarded?
94
+ !!send(record_deleted_at_column)
95
+ end
96
+
97
+ private
98
+
99
+ def touch_record_deleted_at_column(with_transaction = false)
100
+ unless self.frozen?
101
+ if with_transaction
102
+ with_transaction_returning_status { touch(record_deleted_at_column) }
103
+ else
104
+ touch(record_deleted_at_column)
105
+ end
106
+ end
107
+ end
108
+
109
+ def restore_associated_records
110
+
111
+ destroyed_associations = self.class.reflect_on_all_associations.select do |association|
112
+ association.options[:dependent] == :destroy
113
+ end
114
+
115
+ destroyed_associations.each do |association|
116
+ association_data = send(association.name)
117
+ unless association_data.nil?
118
+ if association_data.persistent?
119
+ if association.collection?
120
+ association_data.only_discarded.each { |record| record.restore(:recursive => true) }
121
+ else
122
+ association_data.restore(:recursive => true)
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ class ActiveRecord::Base
133
+
134
+ def self.acts_as_persistent(options = {})
135
+
136
+ alias :destroy! :destroy
137
+ alias :delete! :delete
138
+
139
+ def zap!
140
+ dependent_reflections = self.reflections.select do |name, reflection|
141
+ reflection.options[:dependent] == :destroy
142
+ end
143
+ if dependent_reflections.any?
144
+ dependent_reflections.each do |name|
145
+ associated_records = self.send(name)
146
+ associated_records = associated_records.with_discarded if associated_records.respond_to?(:with_discarded)
147
+ associated_records.each(&:zap!)
148
+ end
149
+ end
150
+ destroy!
151
+ end
152
+
153
+ include PersistentRecord
154
+
155
+ class_attribute :record_deleted_at_column
156
+
157
+ self.record_deleted_at_column = options[:column] || :deleted_at
158
+ default_scope { where(record_deleted_at_column => nil) }
159
+
160
+ before_restore {
161
+ self.class.notify_observers(:before_restore, self) if self.class.respond_to?(:notify_observers)
162
+ }
163
+
164
+ after_restore {
165
+ self.class.notify_observers(:after_restore, self) if self.class.respond_to?(:notify_observers)
166
+ }
167
+
168
+ end
169
+
170
+ def self.persistent?
171
+ false
172
+ end
173
+
174
+ def persistent?
175
+ self.class.persistent?
176
+ end
177
+
178
+ def persisted?
179
+ persistent? ? !new_record? : super
180
+ end
181
+
182
+ private
183
+
184
+ def record_deleted_at_column
185
+ self.class.record_deleted_at_column
186
+ end
187
+
188
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'persistent_record/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+
8
+ spec.name = "persistent_record"
9
+ spec.version = PersistentRecord::VERSION
10
+ spec.authors = ["Robin Grass"]
11
+ spec.email = ["hej@carbin.se"]
12
+ spec.description = %q{Introduces soft deletions for ActiveRecord.}
13
+ spec.summary = %q{Introduces soft deletions for ActiveRecord.}
14
+ spec.homepage = "http://carbin.se/"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "activerecord", "~> 4.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: persistent_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Robin Grass
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Introduces soft deletions for ActiveRecord.
56
+ email:
57
+ - hej@carbin.se
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - DOCUMENTATION.md
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/persistent_record.rb
69
+ - lib/persistent_record/version.rb
70
+ - persistent_record.gemspec
71
+ homepage: http://carbin.se/
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.0.7
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Introduces soft deletions for ActiveRecord.
95
+ test_files: []