rails4_acts_as_paranoid 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9ade14cf3b055ec24a6c8adf8c167172671f0bea
4
+ data.tar.gz: 6ff6c5695729f0255ebd581c856a93d3662ed6ef
5
+ SHA512:
6
+ metadata.gz: 42637e07f117676e00793d61d54fd8d3dd5437f3e73d7192ee4480a5af97cef0104f0eb71013a467c4def57d40560f930deddd8a7fe8c7c2e2b69044090e718b
7
+ data.tar.gz: 69c873c51ffa6f413db41b999bc4e96285e7c5977e1ea08f2b2648dd55c4c2542316573c6dd99bd673d5c164016a87c957a5570780d7ac48262099852b13e700
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Gonçalo Silva
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,146 @@
1
+ # ActsAsParanoid
2
+
3
+ A simple plugin which hides records instead of deleting them, being able to recover them.
4
+
5
+ This branch targets Rails 3.1.X. If you're working with another version, switch to the corresponding branch.
6
+
7
+ ## Credits
8
+
9
+ This plugin was inspired by [acts_as_paranoid](http://github.com/technoweenie/acts_as_paranoid) and [acts_as_active](http://github.com/fernandoluizao/acts_as_active).
10
+
11
+ While porting it to Rails 3, I decided to apply the ideas behind those plugins to an unified solution while removing a **lot** of the complexity found in them. I eventually ended up writing a new plugin from scratch.
12
+
13
+ ## Usage
14
+
15
+ You can enable ActsAsParanoid like this:
16
+
17
+ class Paranoiac < ActiveRecord::Base
18
+ acts_as_paranoid
19
+ end
20
+
21
+ ### Options
22
+
23
+ You can also specify the name of the column to store it's *deletion* and the type of data it holds:
24
+
25
+ - :column => 'deleted_at'
26
+ - :type => 'time'
27
+
28
+ The values shown are the defaults. While *column* can be anything (as long as it exists in your database), *type* is restricted to "boolean", "time" or "string".
29
+
30
+ If your column type is a "string", you can also specify which value to use when marking an object as deleted by passing `:deleted_value` (default is "deleted").
31
+
32
+ ### Filtering
33
+
34
+ If a record is deleted by ActsAsParanoid, it won't be retrieved when accessing the database. So, `Paranoiac.all` will **not** include the deleted_records. if you want to access them, you have 2 choices:
35
+
36
+ Paranoiac.only_deleted # retrieves the deleted records
37
+ Paranoiac.with_deleted # retrieves all records, deleted or not
38
+
39
+ ### Real deletion
40
+
41
+ In order to really delete a record, just use:
42
+
43
+ paranoiac.destroy!
44
+ Paranoiac.delete_all!(conditions)
45
+
46
+ You can also definitively delete a record by calling `destroy` or `delete_all` on it twice. If a record was already deleted (hidden by ActsAsParanoid) and you delete it again, it will be removed from the database. Take this example:
47
+
48
+ Paranoiac.first.destroy # does NOT delete the first record, just hides it
49
+ Paranoiac.only_deleted.destroy # deletes the first record from the database
50
+
51
+ ### Recovery
52
+
53
+ Recovery is easy. Just invoke `recover` on it, like this:
54
+
55
+ Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover
56
+
57
+ All associations marked as `:dependent => :destroy` are also recursively recovered. If you would like to disable this behavior, you can call `recover` with the `recursive` option:
58
+
59
+ Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recursive => false)
60
+
61
+ If you would like to change the default behavior for a model, you can use the `recover_dependent_associations` option
62
+
63
+ class Paranoiac < ActiveRecord::Base
64
+ acts_as_paranoid :recover_dependent_associations => false
65
+ end
66
+
67
+ By default when using timestamp fields to mark deletion, dependent records will be recovered if they were deleted within 5 seconds of the object upon which they depend. This restores the objects to the state before the recursive deletion without restoring other objects that were deleted earlier. This window can be changed with the `dependent_recovery_window` option
68
+
69
+ class Paranoiac < ActiveRecord::Base
70
+ acts_as_paranoid
71
+ has_many :paranoids, :dependent => :destroy
72
+ end
73
+
74
+ class Paranoid < ActiveRecord::Base
75
+ belongs_to :paranoic
76
+
77
+ # Paranoid objects will be recovered alongside Paranoic objects
78
+ # if they were deleted within 1 minute of the Paranoic object
79
+ acts_as_paranoid :dependent_recovery_window => 1.minute
80
+ end
81
+
82
+ or in the recover statement
83
+
84
+ Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recovery_window => 30.seconds)
85
+
86
+ ### Validation
87
+ ActiveRecord's built-in uniqueness validation does not account for records deleted by ActsAsParanoid. If you want to check for uniqueness among non-deleted records only, use the macro `validates_as_paranoid` in your model. Then, instead of using `validates_uniqueness_of`, use `validates_uniqueness_of_without_deleted`. This will keep deleted records from counting against the uniqueness check.
88
+
89
+ class Paranoiac < ActiveRecord::Base
90
+ acts_as_paranoid
91
+ validates_as_paranoid
92
+ validates_uniqueness_of_without_deleted :name
93
+ end
94
+
95
+ Paranoiac.create(:name => 'foo').destroy
96
+ Paranoiac.new(:name => 'foo').valid? #=> true
97
+
98
+
99
+ ### Status
100
+ Once you retrieve data using `with_deleted` scope you can check deletion status using `deleted?` helper:
101
+
102
+ Paranoiac.create(:name => 'foo').destroy
103
+ Paranoiac.with_deleted.first.deleted? #=> true
104
+
105
+ ## Caveats
106
+
107
+ Watch out for these caveats:
108
+
109
+ - You cannot use default\_scope in your model. It is possible to work around this caveat, but it's not pretty. Have a look at [this article](http://joshuaclayton.github.com/code/default_scope/activerecord/is_paranoid/multiple-default-scopes.html) if you really need to have your own default scope.
110
+ - You cannot use scopes named `with_deleted`, `only_deleted` and `paranoid_deleted_around_time`
111
+ - `unscoped` will return all records, deleted or not
112
+
113
+ # Support
114
+
115
+ This gem supports the most recent versions of Rails and Ruby.
116
+
117
+ ## Rails
118
+
119
+ For Rails 3.2 check the README at the [rails3.2](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.2) branch and add this to your Gemfile:
120
+
121
+ gem "rails3_acts_as_paranoid", "~>0.2.0"
122
+
123
+ For Rails 3.1 check the README at the [rails3.1](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.1) branch and add this to your Gemfile:
124
+
125
+ gem "rails3_acts_as_paranoid", "~>0.1.4"
126
+
127
+ For Rails 3.0 check the README at the [rails3.0](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.0) branch and add this to your Gemfile:
128
+
129
+ gem "rails3_acts_as_paranoid", "~>0.0.9"
130
+
131
+
132
+ ## Ruby
133
+
134
+ This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode). It *might* work fine in 1.8, but it's not officially supported.
135
+
136
+ # Acknowledgements
137
+
138
+ * To [cheerfulstoic](https://github.com/cheerfulstoic) for adding recursive recovery
139
+ * To [Jonathan Vaught](https://github.com/gravelpup) for adding paranoid validations
140
+ * To [Geoffrey Hichborn](https://github.com/phene) for improving the overral code quality and adding support for after_commit
141
+ * To [flah00](https://github.com/flah00) for adding support for STI-based associations (with :dependent)
142
+ * To [vikramdhillon](https://github.com/vikramdhillon) for the idea and
143
+ initial implementation of support for string column type
144
+ * To [Craig Walker](https://github.com/softcraft-development) for Rails 3.1 support and fixing various pending issues
145
+
146
+ Copyright © 2010 Gonçalo Silva, released under the MIT license
@@ -0,0 +1,378 @@
1
+ require 'active_record'
2
+ require 'validations/uniqueness_without_deleted'
3
+
4
+ module ActiveRecord
5
+ class Relation
6
+ VALUES = [
7
+ :bind_values,
8
+ :create_with_value,
9
+ :eager_load_values,
10
+ :from_value,
11
+ :group_values,
12
+ :having_values,
13
+ :includes_values,
14
+ :joins_values,
15
+ :limit_value,
16
+ :lock_value,
17
+ :offset_value,
18
+ :order_values,
19
+ :preload_values,
20
+ :readonly_value,
21
+ :reordering_value,
22
+ :reverse_order_value,
23
+ :select_values,
24
+ :uniq_value,
25
+ :where_values
26
+ ]
27
+
28
+ def paranoid?
29
+ klass.try(:paranoid?) ? true : false
30
+ end
31
+
32
+ def paranoid_deletion_attributes
33
+ { klass.paranoid_column => klass.delete_now_value }
34
+ end
35
+
36
+ alias_method :destroy!, :destroy
37
+ def destroy(id)
38
+ if paranoid?
39
+ where(id: id).update_all(paranoid_deletion_attributes)
40
+ else
41
+ destroy!(id)
42
+ end
43
+ end
44
+
45
+ alias_method :really_delete_all!, :delete_all
46
+
47
+ def delete_all!(conditions = nil)
48
+ if conditions
49
+ # This idea comes out of Rails 3.1 ActiveRecord::Record.delete_all
50
+ where(conditions).delete_all!
51
+ else
52
+ really_delete_all!
53
+ end
54
+ end
55
+
56
+ def delete_all(conditions = nil)
57
+ if paranoid?
58
+ update_all(paranoid_deletion_attributes, conditions)
59
+ else
60
+ delete_all!(conditions)
61
+ end
62
+ end
63
+
64
+ def arel=(a)
65
+ @arel = a
66
+ end
67
+
68
+ def with_deleted
69
+ wd = self.clone.unscoped
70
+ VALUES.each do |v|
71
+ if self.respond_to?(v) && wd.respond_to?("#{v}=")
72
+ wd.send("#{v}=", self.send(v))
73
+ end
74
+ end
75
+ wd
76
+ end
77
+ end
78
+ end
79
+
80
+ module ActsAsParanoid
81
+ DEFAULT_CONFIG = {
82
+ :column => "deleted_at",
83
+ :column_type => "time",
84
+ :recover_dependent_associations => true,
85
+ :dependent_recovery_window => 2.minutes
86
+ }
87
+
88
+ def self.default_config=(config={})
89
+ DEFAULT_CONFIG.merge! config
90
+ end
91
+
92
+ def paranoid?
93
+ self.included_modules.include?(InstanceMethods)
94
+ end
95
+
96
+ def is_not_paranoid_deleted
97
+ operator = non_deleted_value.nil? ? "IS" : "="
98
+ ["#{paranoid_column_reference} #{operator} ?", non_deleted_value]
99
+ end
100
+
101
+ def is_paranoid_deleted
102
+ operator = non_deleted_value.nil? ? "IS NOT" : "!="
103
+ ["#{paranoid_column_reference} #{operator} ?", non_deleted_value]
104
+ end
105
+
106
+ def non_deleted_value
107
+ primary_paranoid_column[:column_type] == "boolean" ? false : nil
108
+ end
109
+
110
+ def validates_as_paranoid
111
+ extend ParanoidValidations::ClassMethods
112
+ end
113
+
114
+ def primary_paranoid_column
115
+ self.paranoid_configuration[:primary_column]
116
+ end
117
+
118
+ def secondary_paranoid_columns
119
+ self.paranoid_configuration[:secondary_columns]
120
+ end
121
+
122
+ def build_column_config(options={})
123
+ column = DEFAULT_CONFIG.dup
124
+ column.delete(:columns)
125
+ column = column.merge(:deleted_value => "deleted") if options[:column_type] == "string"
126
+ column = column.merge(options)
127
+
128
+ unless ['time', 'boolean', 'string'].include? column[:column_type]
129
+ raise ArgumentError, "'time', 'boolean' or 'string' expected for :column_type option, got #{column[:column_type]}"
130
+ end
131
+
132
+ column
133
+ end
134
+
135
+ def acts_as_paranoid(options = {})
136
+ raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
137
+
138
+ class_attribute :paranoid_configuration, :paranoid_column_reference
139
+
140
+ options = DEFAULT_CONFIG.merge(options)
141
+ if options[:columns]
142
+ primary_column = options[:columns].first
143
+ secondary_columns = options[:columns][1..-1]
144
+ else
145
+ primary_column = options
146
+ secondary_columns = []
147
+ end
148
+ self.paranoid_configuration = {
149
+ :primary_column => build_column_config(primary_column),
150
+ :secondary_columns => secondary_columns.map { |column| build_column_config(column) }
151
+ }
152
+
153
+ self.paranoid_column_reference = "#{self.table_name}.#{primary_paranoid_column[:column]}"
154
+
155
+ return if paranoid?
156
+
157
+ # Magic!
158
+ default_scope { where(*is_not_paranoid_deleted) }
159
+
160
+ scope :paranoid_deleted_around_time, lambda {|value, window|
161
+ if self.class.respond_to?(:paranoid?) && self.class.paranoid?
162
+ if self.class.paranoid_column_type == 'time' && ![true, false].include?(value)
163
+ self.where("#{self.class.paranoid_column} > ? AND #{self.class.paranoid_column} < ?", (value - window), (value + window))
164
+ else
165
+ self.only_deleted
166
+ end
167
+ end if primary_paranoid_column[:column_type] == 'time'
168
+ }
169
+
170
+ include InstanceMethods
171
+ extend ClassMethods
172
+ end
173
+
174
+ module ClassMethods
175
+ def self.extended(base)
176
+ base.define_callbacks :recover
177
+ base.skip_time_zone_conversion_for_attributes = [base.paranoid_column, :created_at, :updated_at]
178
+ end
179
+
180
+ def before_recover(method)
181
+ set_callback :recover, :before, method
182
+ end
183
+
184
+ def after_recover(method)
185
+ set_callback :recover, :after, method
186
+ end
187
+
188
+ def with_deleted
189
+ self.unscoped
190
+ end
191
+
192
+ def only_deleted
193
+ self.unscoped.where(*is_paranoid_deleted)
194
+ end
195
+
196
+ def deletion_conditions(id_or_array)
197
+ ["id in (?)", [id_or_array].flatten]
198
+ end
199
+
200
+ def delete!(id_or_array)
201
+ delete_all!(deletion_conditions(id_or_array))
202
+ end
203
+
204
+ def delete(id_or_array)
205
+ delete_all(deletion_conditions(id_or_array))
206
+ end
207
+
208
+ def delete_all!(conditions = nil)
209
+ self.unscoped.delete_all!(conditions)
210
+ end
211
+
212
+ def delete_all(conditions = nil)
213
+ columns = secondary_paranoid_columns.push(primary_paranoid_column)
214
+
215
+ sql = columns.map do |column|
216
+ "#{column[:column]} = ?"
217
+ end.join(", ")
218
+ values = columns.map{ |column| delete_now_value(column) }
219
+
220
+ where(conditions).update_all [sql, *values]
221
+ end
222
+
223
+ def paranoid_column
224
+ primary_paranoid_column[:column].to_sym
225
+ end
226
+
227
+ def paranoid_column_type
228
+ primary_paranoid_column[:column_type].to_sym
229
+ end
230
+
231
+ def dependent_associations
232
+ self.reflect_on_all_associations.select {|a| [:destroy, :delete_all].include?(a.options[:dependent]) }
233
+ end
234
+
235
+ def delete_now_value(column=nil)
236
+ column ||= primary_paranoid_column
237
+ case column[:column_type]
238
+ when "time" then Time.now.utc
239
+ when "boolean" then true
240
+ when "string" then column[:deleted_value]
241
+ end
242
+ end
243
+ end
244
+
245
+ module InstanceMethods
246
+
247
+ def paranoid_value
248
+ self.send(self.class.paranoid_column)
249
+ end
250
+
251
+ def destroy!
252
+ return self unless check_persisted_for_delete(true)
253
+ with_transaction_returning_status do
254
+ run_callbacks :destroy do
255
+ act_on_dependent_destroy_associations
256
+ self.class.delete_all!(self.class.primary_key.to_sym => self.id)
257
+ set_paranoid_value true
258
+ end
259
+ end
260
+ end
261
+
262
+ def destroy
263
+ return self unless check_persisted_for_delete(false)
264
+ if !deleted?
265
+ with_transaction_returning_status do
266
+ run_callbacks :destroy do
267
+ self.class.delete_all(self.class.primary_key.to_sym => self.id)
268
+ set_paranoid_value false
269
+ end
270
+ end
271
+ else
272
+ destroy!
273
+ end
274
+ end
275
+
276
+ def delete!
277
+ return self unless check_persisted_for_delete(true)
278
+ with_transaction_returning_status do
279
+ act_on_dependent_destroy_associations
280
+ self.class.delete_all!(self.class.primary_key.to_sym => self.id)
281
+ set_paranoid_value true
282
+ end
283
+ end
284
+
285
+ def delete
286
+ return self unless check_persisted_for_delete(false)
287
+ if !deleted?
288
+ with_transaction_returning_status do
289
+ self.class.delete_all(self.class.primary_key.to_sym => self.id)
290
+ set_paranoid_value false
291
+ end
292
+ else
293
+ delete!
294
+ end
295
+ end
296
+
297
+ def recover(options={})
298
+ options = {
299
+ :recursive => self.class.primary_paranoid_column[:recover_dependent_associations],
300
+ :recovery_window => self.class.primary_paranoid_column[:dependent_recovery_window]
301
+ }.merge(options)
302
+
303
+ self.class.transaction do
304
+ run_callbacks :recover do
305
+ recover_dependent_associations(options[:recovery_window], options) if options[:recursive]
306
+
307
+ self.paranoid_value = self.class.non_deleted_value
308
+ self.save
309
+ end
310
+ end
311
+ end
312
+
313
+ def recover_dependent_associations(window, options)
314
+ self.class.dependent_associations.each do |association|
315
+ if association.collection? && self.send(association.name).paranoid?
316
+ self.send(association.name).unscoped do
317
+ self.send(association.name).paranoid_deleted_around_time(paranoid_value, window).each do |object|
318
+ object.recover(options) if object.respond_to?(:recover)
319
+ end
320
+ end
321
+ elsif association.macro == :has_one && association.klass.paranoid?
322
+ association.klass.unscoped do
323
+ object = association.klass.paranoid_deleted_around_time(paranoid_value, window).send('find_by_'+association.foreign_key, self.id)
324
+ object.recover(options) if object && object.respond_to?(:recover)
325
+ end
326
+ elsif association.klass.paranoid?
327
+ association.klass.unscoped do
328
+ id = self.send(association.foreign_key)
329
+ object = association.klass.paranoid_deleted_around_time(paranoid_value, window).find_by_id(id)
330
+ object.recover(options) if object && object.respond_to?(:recover)
331
+ end
332
+ end
333
+ end
334
+ end
335
+
336
+ def act_on_dependent_destroy_associations
337
+ self.class.dependent_associations.each do |association|
338
+ if association.collection? && self.send(association.name).paranoid?
339
+ association.klass.with_deleted.where(association.foreign_key.to_sym => self.id.to_json).each do |object|
340
+ object.destroy!
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ def deleted?
347
+ !(paranoid_value == self.class.non_deleted_value)
348
+ end
349
+ alias_method :destroyed?, :deleted?
350
+
351
+ private
352
+ def paranoid_value=(value)
353
+ self.send("#{self.class.paranoid_column}=", value)
354
+ end
355
+
356
+ def check_persisted_for_delete(permanent)
357
+ if !self.id
358
+ set_paranoid_value permanent
359
+ return false
360
+ end
361
+ true
362
+ end
363
+
364
+ def set_paranoid_value(permanent)
365
+ self.paranoid_value = self.class.delete_now_value
366
+ freeze if permanent
367
+ self
368
+ end
369
+ end
370
+
371
+ end
372
+
373
+
374
+ # Extend ActiveRecord's functionality
375
+ ActiveRecord::Base.send :extend, ActsAsParanoid
376
+
377
+ # Push the recover callback onto the activerecord callback list
378
+ ActiveRecord::Callbacks::CALLBACKS.push(:before_recover, :after_recover)
@@ -0,0 +1,39 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module ParanoidValidations
4
+ class UniquenessWithoutDeletedValidator < ActiveRecord::Validations::UniquenessValidator
5
+ def validate_each(record, attribute, value)
6
+ finder_class = find_finder_class_for(record)
7
+
8
+ if value && record.class.serialized_attributes.key?(attribute.to_s)
9
+ value = YAML.dump value
10
+ end
11
+
12
+ table = Arel::Table.new(record.class.table_name)
13
+ sql, params = build_relation(finder_class, table, attribute, value)
14
+
15
+ # This is the only changed line from the base class version - it does finder_class.unscoped
16
+ relation = finder_class.where(sql, *params)
17
+
18
+ Array.wrap(options[:scope]).each do |scope_item|
19
+ scope_value = record.send(scope_item)
20
+ relation = relation.where(scope_item => scope_value)
21
+ end
22
+
23
+ if record.persisted?
24
+ # TODO : This should be in Arel
25
+ relation = relation.where("#{record.class.quoted_table_name}.#{record.class.primary_key} <> ?", record.send(:id))
26
+ end
27
+
28
+ if relation.exists?
29
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
30
+ end
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ def validates_uniqueness_of_without_deleted(*attr_names)
36
+ validates_with UniquenessWithoutDeletedValidator, _merge_attributes(attr_names)
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails4_acts_as_paranoid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Goncalo Silva
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-16 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.0.beta1
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.0.beta1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails-observers
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Active Record (~>3.1) plugin which allows you to hide and restore records
42
+ without actually deleting them. Check its GitHub page for more in-depth information.
43
+ email:
44
+ - goncalossilva@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - lib/rails4_acts_as_paranoid.rb
50
+ - lib/validations/uniqueness_without_deleted.rb
51
+ - LICENSE
52
+ - README.markdown
53
+ homepage: https://github.com/softcraft-development/rails3_acts_as_paranoid
54
+ licenses: []
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.3.6
70
+ requirements: []
71
+ rubyforge_project: rails4_acts_as_paranoid
72
+ rubygems_version: 2.0.0
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Active Record (~>3.1) plugin which allows you to hide and restore records
76
+ without actually deleting them.
77
+ test_files: []
78
+ has_rdoc: