organizze_permanent_records 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
6
+ *.swo
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ source 'http://rubygems.org'
4
+
5
+ gem 'activerecord'
6
+
7
+ group :development do
8
+ gem 'jeweler' # Because Bundler doesn't fucking have version:bump tasks
9
+ gem 'rake'
10
+ gem 'sqlite3'
11
+ end
12
+
13
+ # Specify your gem's dependencies in organizze_permanent_records.gemspec
14
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,250 @@
1
+ require "organizze_permanent_records/version"
2
+
3
+ module OrganizzePermanentRecords
4
+ def self.included(base)
5
+
6
+ base.send :include, InstanceMethods
7
+
8
+ # Rails 3
9
+ if ActiveRecord::VERSION::MAJOR >= 3
10
+ base.extend Scopes
11
+ base.instance_eval { define_model_callbacks :revive }
12
+ # Rails 2.x.x
13
+ elsif base.respond_to?(:named_scope)
14
+ base.named_scope :deleted, :conditions => 'deleted_at IS NOT NULL'
15
+ base.named_scope :not_deleted, :conditions => { :deleted_at => nil }
16
+ base.instance_eval { define_callbacks :before_revive, :after_revive }
17
+ base.send :alias_method_chain, :destroy, :permanent_records
18
+ # Early Rails code
19
+ else
20
+ base.extend EarlyRails
21
+ base.instance_eval { define_callbacks :before_revive, :after_revive }
22
+ end
23
+ base.instance_eval do
24
+ before_revive :revive_destroyed_dependent_records
25
+ def is_permanent?
26
+ columns.detect {|c| 'deleted_at' == c.name}
27
+ end
28
+ end
29
+ end
30
+
31
+ module Scopes
32
+ def deleted
33
+ where("#{table_name}.deleted_at IS NOT NULL")
34
+ end
35
+ def not_deleted
36
+ where("#{table_name}.deleted_at IS NULL")
37
+ end
38
+ end
39
+
40
+ module EarlyRails
41
+ def with_deleted
42
+ with_scope :find => {:conditions => "#{quoted_table_name}.deleted_at IS NOT NULL"} do
43
+ yield
44
+ end
45
+ end
46
+
47
+ def with_not_deleted
48
+ with_scope :find => {:conditions => "#{quoted_table_name}.deleted_at IS NULL"} do
49
+ yield
50
+ end
51
+ end
52
+
53
+ # this next bit is basically stolen from the scope_out plugin
54
+ [:deleted, :not_deleted].each do |name|
55
+ define_method "find_#{name}" do |*args|
56
+ send("with_#{name}") { find(*args) }
57
+ end
58
+
59
+ define_method "count_#{name}" do |*args|
60
+ send("with_#{name}") { count(*args) }
61
+ end
62
+
63
+ define_method "calculate_#{name}" do |*args|
64
+ send("with_#{name}") { calculate(*args) }
65
+ end
66
+
67
+ define_method "find_all_#{name}" do |*args|
68
+ send("with_#{name}") { find(:all, *args) }
69
+ end
70
+ end
71
+ end
72
+
73
+ module InstanceMethods
74
+
75
+ def is_permanent?
76
+ respond_to?(:deleted_at)
77
+ end
78
+
79
+ def deleted?
80
+ deleted_at if is_permanent?
81
+ end
82
+
83
+ def revive
84
+ if active_record_3?
85
+ _run_revive_callbacks do
86
+ set_deleted_at nil
87
+ end
88
+ else
89
+ run_callbacks :before_revive
90
+ attempt_notifying_observers(:before_revive)
91
+ set_deleted_at nil
92
+ run_callbacks :after_revive
93
+ attempt_notifying_observers(:after_revive)
94
+ end
95
+ self
96
+ end
97
+
98
+ def destroy(force = nil)
99
+ if active_record_3?
100
+ unless is_permanent? && (:force != force)
101
+ return permanently_delete_records_after{ super() }
102
+ end
103
+ end
104
+ destroy_with_permanent_records force
105
+ end
106
+
107
+ private
108
+ def set_deleted_at(value)
109
+ return self unless is_permanent?
110
+ record = self.class
111
+ record = record.unscoped if active_record_3?
112
+ record = record.find(id)
113
+ record.deleted_at = value
114
+ begin
115
+ # we call save! instead of update_attribute so an ActiveRecord::RecordInvalid
116
+ # error will be raised if the record isn't valid. (This prevents reviving records that
117
+ # disregard validation constraints,)
118
+ record.send(:save_without_callbacks)
119
+ @attributes, @attributes_cache = record.attributes, record.attributes
120
+ rescue Exception => e
121
+ # trigger dependent record destruction (they were revived before this record,
122
+ # which cannot be revived due to validations)
123
+ record.destroy
124
+ raise e
125
+ end
126
+ end
127
+
128
+ def destroy_with_permanent_records(force = nil)
129
+ unless active_record_3?
130
+ unless is_permanent? && (:force != force)
131
+ return permanently_delete_records_after{ destroy_without_permanent_records }
132
+ end
133
+ end
134
+ unless deleted? || new_record?
135
+ set_deleted_at Time.now
136
+ end
137
+ if active_record_3?
138
+ _run_destroy_callbacks do
139
+ save_without_callbacks
140
+ end
141
+ run_callbacks :before_destroy
142
+ save_without_callbacks
143
+ run_callbacks :after_destroy
144
+ end
145
+ self
146
+ end
147
+
148
+ def revive_destroyed_dependent_records
149
+ self.class.reflections.select do |name, reflection|
150
+ 'destroy' == reflection.options[:dependent].to_s && reflection.klass.is_permanent?
151
+ end.each do |name, reflection|
152
+ cardinality = reflection.macro.to_s.gsub('has_', '')
153
+ if cardinality == 'many'
154
+ records = send(name)
155
+ records = records.unscoped if active_record_3?
156
+ records = records.find(:all,
157
+ :conditions => [
158
+ "#{reflection.quoted_table_name}.deleted_at > ?" +
159
+ " AND " +
160
+ "#{reflection.quoted_table_name}.deleted_at < ?",
161
+ deleted_at - 3.seconds,
162
+ deleted_at + 3.seconds
163
+ ]
164
+ )
165
+ elsif cardinality == 'one' or cardinality == 'belongs_to'
166
+ if active_record_3?
167
+ self.class.unscoped do
168
+ records = [] << send(name)
169
+ end
170
+ else
171
+ records = [] << send(name)
172
+ end
173
+ end
174
+ [records].flatten.compact.each do |dependent|
175
+ dependent.revive
176
+ end
177
+
178
+ # and update the reflection cache
179
+ send(name, :reload)
180
+ end
181
+ end
182
+
183
+ def attempt_notifying_observers(callback)
184
+ begin
185
+ notify_observers(callback)
186
+ rescue NoMethodError => e
187
+ # do nothing: this model isn't being observed
188
+ end
189
+ end
190
+
191
+ # return the records corresponding to an association with the `:dependent => :destroy` option
192
+ def get_dependent_records
193
+ dependent_records = {}
194
+
195
+ # check which dependent records are to be destroyed
196
+ klass = self.class
197
+ klass.reflections.each do |key, reflection|
198
+ if reflection.options[:dependent] == :destroy
199
+ next unless records = self.send(key) # skip if there are no dependent record instances
200
+ if records.respond_to? :size
201
+ next unless records.size > 0 # skip if there are no dependent record instances
202
+ else
203
+ records = [] << records
204
+ end
205
+ dependent_record = records.first
206
+ next if dependent_record.nil?
207
+ dependent_records[dependent_record.class] = records.map(&:id)
208
+ end
209
+ end
210
+ dependent_records
211
+ end
212
+
213
+ # If we force the destruction of the record, we will need to force the destruction of dependent records if the
214
+ # user specified `:dependent => :destroy` in the model.
215
+ # By default, the call to super/destroy_with_permanent_records (i.e. the &block param) will only soft delete
216
+ # the dependent records; we keep track of the dependent records
217
+ # that have `:dependent => :destroy` and call destroy(force) on them after the call to super
218
+ def permanently_delete_records_after(&block)
219
+ dependent_records = get_dependent_records
220
+ result = block.call
221
+ if result
222
+ permanently_delete_records(dependent_records)
223
+ end
224
+ result
225
+ end
226
+
227
+ # permanently delete the records (i.e. remove from database)
228
+ def permanently_delete_records(dependent_records)
229
+ dependent_records.each do |klass, ids|
230
+ ids.each do |id|
231
+ begin
232
+ record = klass
233
+ record = record.unscoped if active_record_3?
234
+ record = record.find(id)
235
+ rescue ActiveRecord::RecordNotFound
236
+ next # the record has already been deleted, possibly due to another association with `:dependent => :destroy`
237
+ end
238
+ record.deleted_at = nil
239
+ record.destroy(:force)
240
+ end
241
+ end
242
+ end
243
+
244
+ def active_record_3?
245
+ ActiveRecord::VERSION::MAJOR >= 3
246
+ end
247
+ end
248
+ end
249
+
250
+ ActiveRecord::Base.send :include, PermanentRecords
@@ -0,0 +1,3 @@
1
+ module OrganizzePermanentRecords
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "organizze_permanent_records/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "organizze_permanent_records"
7
+ s.version = OrganizzePermanentRecords::VERSION
8
+ s.authors = ["Esdras Mayrink"]
9
+ s.email = ["falecom@oesdras.com.br"]
10
+ s.homepage = ""
11
+ s.summary = %q{This gem is a clone of https://github.com/JackDanger/permanent_records. I customized it to fit my needs, this is what open source is all about, isn't it?}
12
+ s.description = %q{Never Lose Data. Rather than deleting rows this sets Record#deleted_at and gives you all the scopes you need to work with your data.}
13
+
14
+ s.rubyforge_project = "organizze_permanent_records"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: organizze_permanent_records
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Esdras Mayrink
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-28 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Never Lose Data. Rather than deleting rows this sets Record#deleted_at
15
+ and gives you all the scopes you need to work with your data.
16
+ email:
17
+ - falecom@oesdras.com.br
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - Rakefile
25
+ - lib/organizze_permanent_records.rb
26
+ - lib/organizze_permanent_records/version.rb
27
+ - organizze_permanent_records.gemspec
28
+ homepage: ''
29
+ licenses: []
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project: organizze_permanent_records
48
+ rubygems_version: 1.8.6
49
+ signing_key:
50
+ specification_version: 3
51
+ summary: This gem is a clone of https://github.com/JackDanger/permanent_records. I
52
+ customized it to fit my needs, this is what open source is all about, isn't it?
53
+ test_files: []