zombie_record 0.4.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8fffbb5b3d7304492d0999ef54eb7db4fc783cb5
4
+ data.tar.gz: 121cc7d9627e7450500d076f025b501b52f31687
5
+ SHA512:
6
+ metadata.gz: e45075a3ee932ef550c34120d0be0d92d601e7839a16e06d418e949e4fba0673738a7f8e830e6d4be35aaca08f3ea5aa6bd95d66db594a22fd09258d794222a1
7
+ data.tar.gz: 38833db59a21a4bba05c96ba4d31fb34aefea3cd4d85912b9e5cbc1a3a71fa2dd3bb5d8c6217f82aa39d764a0b03f5c6144daf34d0f03bac67ad773d4b730dd3
@@ -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/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
@@ -0,0 +1,9 @@
1
+ ###### v0.4.0
2
+
3
+ * Allow accessing associated records on deleted objects, even if they themselves
4
+ are deleted, e.g.
5
+
6
+ # The post, comments, and category are all deleted.
7
+ post = Post.with_deleted.find(...)
8
+ post.comments
9
+ post.category
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zombie_record.gemspec
4
+ gemspec
5
+
6
+ gem "byebug"
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Daniel Schierbeck
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.
@@ -0,0 +1,51 @@
1
+ # Zombie Record
2
+
3
+ Allows restoring your Active Records from the dead!
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'zombie_record'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install zombie_record
18
+
19
+ ## Usage
20
+
21
+ Simply include the `ZombieRecord::Restorable` in your model class:
22
+
23
+ ```ruby
24
+ class Book < ActiveRecord::Base
25
+ include ZombieRecord::Restorable
26
+ end
27
+ ```
28
+
29
+ Zombie Record assumes the model's table has a `deleted_at` column with the `timestamp` type.
30
+
31
+ You can now delete and restore Book records:
32
+
33
+ ```ruby
34
+ book = Book.find(42)
35
+ book.destroy
36
+
37
+ Book.find(42) # raises ActiveRecord::RecordNotFound.
38
+
39
+ book = Book.deleted.find(42)
40
+ book.restore!
41
+
42
+ Book.find(42) # returns the Book record.
43
+ ```
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ require 'active_support'
2
+ require 'zombie_record/version'
3
+
4
+ module ZombieRecord
5
+ end
6
+
7
+ require 'zombie_record/restorable'
@@ -0,0 +1,195 @@
1
+ module ZombieRecord
2
+ module Restorable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ default_scope { where(deleted_at: nil) }
7
+
8
+ define_callbacks :restore
9
+ end
10
+
11
+ # Override Rails' #destroy for soft-delete functionality
12
+ # When changing to Rails 4, override #destroy_row with a one-liner instead.
13
+ def destroy
14
+ run_callbacks :destroy do
15
+ destroy_associations
16
+
17
+ if persisted?
18
+ time = current_time_from_proper_timezone
19
+ update_column(:deleted_at, time)
20
+
21
+ if self.class.column_names.include?("updated_at")
22
+ update_column(:updated_at, time)
23
+ end
24
+ end
25
+
26
+ @destroyed = true
27
+ freeze
28
+ end
29
+ end
30
+
31
+ # Restores a destroyed record.
32
+ #
33
+ # Returns nothing.
34
+ def restore!
35
+ if frozen?
36
+ raise "cannot restore an object that has been destroyed directly; " <<
37
+ "please make sure to load it from the database again."
38
+ end
39
+
40
+ run_callbacks :restore do
41
+ update_column(:deleted_at, nil)
42
+
43
+ restore_associated_records!
44
+ end
45
+ end
46
+
47
+ # Whether the record has been destroyed.
48
+ #
49
+ # Returns true if the record is deleted, false otherwise.
50
+ def deleted?
51
+ !deleted_at.nil?
52
+ end
53
+
54
+ # Allows accessing deleted associations from the record.
55
+ #
56
+ # Example
57
+ #
58
+ # book = Book.first.with_deleted_associations
59
+ #
60
+ # # Even deleted chapters are returned!
61
+ # book.chapters #=> [...]
62
+ #
63
+ # Returns a wrapped ActiveRecord::Base object.
64
+ def with_deleted_associations
65
+ if deleted?
66
+ WithDeletedAssociations.new(self)
67
+ else
68
+ self
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def restore_associated_records!
75
+ self.class.reflect_on_all_associations.each do |association|
76
+ # Only restore associations that are automatically destroyed alongside
77
+ # the record.
78
+ next unless association.options[:dependent] == :destroy
79
+
80
+ # Don't try to restore models that are not restorable.
81
+ next unless association.klass.ancestors.include?(Restorable)
82
+
83
+ records = deleted_records_for_association(association)
84
+
85
+ records.each do |record|
86
+ record.restore!
87
+ end
88
+ end
89
+ end
90
+
91
+ def deleted_records_for_association(association)
92
+ if association.macro == :has_one
93
+ foreign_key = association.foreign_key
94
+ association.klass.deleted.where(foreign_key => id)
95
+ elsif association.macro == :has_many
96
+ public_send(association.name).deleted
97
+ elsif association.macro == :belongs_to
98
+ associated_id = public_send(association.foreign_key)
99
+ return [] unless associated_id.present?
100
+ association.klass.deleted.where(:id => associated_id)
101
+ else
102
+ raise "association type #{association.macro} not supported"
103
+ end
104
+ end
105
+
106
+ # Wraps a deleted record and makes sure that any associated record is
107
+ # available even if it is deleted. This is done by intercepting the method
108
+ # calls, checking if the method is an association access, and then ensuring
109
+ # that deleted records are included in the resulting query.
110
+ class WithDeletedAssociations < BasicObject
111
+ def initialize(record)
112
+ @record = record
113
+ end
114
+
115
+ def method_missing(name, *args, &block)
116
+ delegate_to_record(name) { @record.public_send(name, *args, &block) }
117
+ end
118
+
119
+ # We want *all* methods to be delegated.
120
+ BasicObject.instance_methods.each do |name|
121
+ define_method(name) do |*args, &block|
122
+ @record.public_send(name, *args, &block)
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def delegate_to_record(name, &block)
129
+ if reflection = reflect_on(name)
130
+ with_deleted_associations(reflection, &block)
131
+ else
132
+ block.call
133
+ end
134
+ end
135
+
136
+ def reflect_on(name)
137
+ reflection = @record.class.reflect_on_association(name)
138
+
139
+ if reflection && restorable_reflection?(reflection)
140
+ reflection
141
+ end
142
+ end
143
+
144
+ def associated_record_class(reflection)
145
+ # Polymorphic associations don't have an easy way to access the class,
146
+ # so we'll have to do it ourselves.
147
+ if reflection.options[:polymorphic]
148
+ @record.public_send(reflection.foreign_type).constantize
149
+ else
150
+ reflection.klass
151
+ end
152
+ end
153
+
154
+ def restorable_reflection?(reflection)
155
+ associated_record_class(reflection).ancestors.include?(Restorable)
156
+ end
157
+
158
+ def with_deleted_associations(reflection, &block)
159
+ case reflection.macro
160
+ when :has_one, :belongs_to
161
+ associated_record_class(reflection).unscoped(&block).with_deleted_associations
162
+ when :has_many
163
+ block.call.with_deleted
164
+ else
165
+ raise "invalid macro #{reflection.macro.inspect}"
166
+ end
167
+ end
168
+ end
169
+
170
+ module WithDeletedAssociationsWrapper
171
+ def to_a
172
+ super.map(&:with_deleted_associations)
173
+ end
174
+ end
175
+
176
+ module ClassMethods
177
+
178
+ # Scopes the relation to only include deleted records.
179
+ #
180
+ # Returns an ActiveRecord::Relation.
181
+ def deleted
182
+ with_deleted.where("#{quoted_table_name}.deleted_at IS NOT NULL")
183
+ end
184
+
185
+ # Scopes the relation to include both active and deleted records.
186
+ #
187
+ # Returns an ActiveRecord::Relation.
188
+ def with_deleted
189
+ scoped.
190
+ tap {|relation| relation.default_scoped = false }.
191
+ extending(WithDeletedAssociationsWrapper)
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,3 @@
1
+ module ZombieRecord
2
+ VERSION = "0.4.0"
3
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZombieRecord::Restorable do
4
+ context "when the record is deleted" do
5
+ it "allows accessing a deleted has_one association" do
6
+ book = Book.create!
7
+ cover = Cover.create!(book: book)
8
+
9
+ book.destroy
10
+ book = Book.with_deleted.first
11
+
12
+ book.cover.should == cover.reload
13
+ end
14
+
15
+ it "allows accessing deleted belongs_to associations" do
16
+ book = Book.create!
17
+ chapter = book.chapters.create!
18
+
19
+ book.destroy
20
+ chapter = Chapter.with_deleted.first
21
+
22
+ chapter.book.should == book
23
+ end
24
+
25
+ it "allows accessing deleted polymorphic belongs_to associations" do
26
+ book = Book.create!
27
+ tag = Tag.create!(name: "jelly", taggable: book)
28
+
29
+ tag.destroy
30
+ tag = Tag.with_deleted.first
31
+
32
+ tag.taggable.should == book
33
+ end
34
+
35
+ it "ensures deleted associations themselves allow access to deleted records" do
36
+ book = Book.create!
37
+ chapter = book.chapters.create!
38
+ book.bookmarks.create!
39
+
40
+ book.destroy
41
+ bookmark = Bookmark.with_deleted.first
42
+
43
+ bookmark.book.chapters.should == [chapter]
44
+ chapter = bookmark.book.chapters.first
45
+
46
+ chapter.book.should == book
47
+ end
48
+
49
+ it "forwards normal method calls" do
50
+ book = Book.create!(title: "The Odyssey")
51
+ book.destroy
52
+ book = Book.with_deleted.first
53
+
54
+ book.title.should == "The Odyssey"
55
+ end
56
+ end
57
+
58
+ describe ".deleted" do
59
+ it "scopes the query to only deleted records" do
60
+ book1 = Book.create!
61
+ book2 = Book.create!
62
+
63
+ book1.destroy
64
+
65
+ Book.deleted.should == [book1]
66
+ end
67
+
68
+ it "respects associations" do
69
+ author = Author.create!
70
+ book = Book.create!(author: author)
71
+ other_book = Book.create!
72
+
73
+ book.destroy
74
+ other_book.destroy
75
+
76
+ author.books.deleted.should == [book]
77
+ end
78
+ end
79
+
80
+ describe "#destroy" do
81
+ it "sets #updated_at if it is defined" do
82
+ book = Timecop.travel(2.days.ago) { Book.create! }
83
+ updated_at = book.updated_at
84
+
85
+ book.destroy
86
+
87
+ book.updated_at.should_not == updated_at
88
+ end
89
+
90
+ it "does not set #updated_at if it is not defined" do
91
+ bookmark = Timecop.travel(2.days.ago) { Bookmark.create! }
92
+ expect { bookmark.destroy }.to_not raise_exception
93
+ end
94
+ end
95
+
96
+ describe "#restore!" do
97
+ let(:book) { Book.create! }
98
+ let(:deleted_book) { Book.deleted.find(book.id) }
99
+
100
+ it "restores the record" do
101
+ book.destroy
102
+
103
+ deleted_book.restore!
104
+ deleted_book.deleted_at.should be_nil
105
+ end
106
+
107
+ it "also restores restorable has_many associated records" do
108
+ chapter = book.chapters.create!
109
+
110
+ book.destroy
111
+ deleted_book.restore!
112
+
113
+ deleted_chapter = Chapter.with_deleted.find(chapter.id)
114
+ deleted_chapter.deleted_at.should be_nil
115
+ end
116
+
117
+ it "also restores restorable has_one associated records" do
118
+ cover = book.create_cover!
119
+
120
+ book.destroy
121
+ deleted_book.restore!
122
+
123
+ deleted_cover = Cover.with_deleted.find(cover.id)
124
+ deleted_cover.deleted_at.should be_nil
125
+ end
126
+
127
+ it "also restores restorable belongs_to associated records" do
128
+ author = Author.create!
129
+ book.update_attribute(:author, author)
130
+
131
+ book.destroy
132
+ deleted_book.restore!
133
+
134
+ deleted_author = Author.with_deleted.find(author.id)
135
+ deleted_author.deleted_at.should be_nil
136
+ end
137
+
138
+ it "does not restore hard deleted associated records" do
139
+ note = book.notes.create!
140
+
141
+ book.destroy
142
+ deleted_book.restore!
143
+
144
+ Note.where(id: note.id).should_not exist
145
+ end
146
+
147
+ it "does not restore an association if it is not destroy dependent" do
148
+ library = Library.create!
149
+ book.update_attribute(:library, library)
150
+
151
+ book.destroy
152
+ library.destroy
153
+ deleted_book.restore!
154
+
155
+ library_after_deletion = Library.with_deleted.find(library.id)
156
+ library_after_deletion.deleted_at.should_not be_nil
157
+ end
158
+
159
+ it "fails if the object itself has been destroyed" do
160
+ book.destroy
161
+
162
+ expect { book.restore! }.to raise_exception(RuntimeError)
163
+ end
164
+ end
165
+
166
+ describe "#deleted?" do
167
+ let(:book) { Book.create! }
168
+
169
+ it "returns true if the record is deleted" do
170
+ book.destroy
171
+
172
+ book.should be_deleted
173
+ end
174
+
175
+ it "returns false if the record is not deleted" do
176
+ book.should_not be_deleted
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,138 @@
1
+ require 'bundler/setup'
2
+ require 'active_record'
3
+ require 'timecop'
4
+ require 'byebug'
5
+
6
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
7
+ require 'zombie_record'
8
+
9
+ class Book < ActiveRecord::Base
10
+ include ZombieRecord::Restorable
11
+
12
+ belongs_to :library
13
+ belongs_to :author, dependent: :destroy
14
+ has_one :cover, dependent: :destroy
15
+
16
+ has_many :chapters, dependent: :destroy
17
+ has_many :bookmarks, dependent: :destroy
18
+ has_many :notes, dependent: :destroy
19
+ end
20
+
21
+ class Chapter < ActiveRecord::Base
22
+ include ZombieRecord::Restorable
23
+
24
+ belongs_to :book
25
+ end
26
+
27
+ class Bookmark < ActiveRecord::Base
28
+ include ZombieRecord::Restorable
29
+
30
+ belongs_to :book
31
+ end
32
+
33
+ class Note < ActiveRecord::Base
34
+ belongs_to :book
35
+ end
36
+
37
+ class Author < ActiveRecord::Base
38
+ include ZombieRecord::Restorable
39
+
40
+ has_many :books
41
+ end
42
+
43
+ class Tag < ActiveRecord::Base
44
+ include ZombieRecord::Restorable
45
+
46
+ belongs_to :taggable, polymorphic: true, dependent: :destroy
47
+ end
48
+
49
+ class Cover < ActiveRecord::Base
50
+ include ZombieRecord::Restorable
51
+
52
+ belongs_to :book
53
+ end
54
+
55
+
56
+ class Library < ActiveRecord::Base
57
+ include ZombieRecord::Restorable
58
+
59
+ has_many :book
60
+ end
61
+
62
+ RSpec.configure do |config|
63
+ config.around do |example|
64
+ ActiveRecord::Base.transaction do
65
+ example.run
66
+ raise ActiveRecord::Rollback
67
+ end
68
+ end
69
+
70
+ config.before :suite do
71
+ ActiveRecord::Base.establish_connection(
72
+ adapter: "mysql2",
73
+ username: "root",
74
+ host: "127.0.0.1",
75
+ port: 3306,
76
+ password: ""
77
+ )
78
+
79
+ ActiveRecord::Base.connection.create_database("zombie_record")
80
+ ActiveRecord::Base.connection.execute("use zombie_record;")
81
+
82
+ ActiveRecord::Schema.define do
83
+ self.verbose = false
84
+
85
+ create_table :books do |t|
86
+ t.integer :author_id
87
+ t.integer :library_id
88
+ t.timestamps
89
+ t.string :title
90
+ t.timestamp :deleted_at
91
+ end
92
+
93
+ create_table :chapters do |t|
94
+ t.integer :book_id
95
+ t.timestamps
96
+ t.timestamp :deleted_at
97
+ end
98
+
99
+ create_table :bookmarks do |t|
100
+ t.integer :book_id
101
+ t.timestamp :created_at
102
+ t.timestamp :deleted_at
103
+ end
104
+
105
+ create_table :notes do |t|
106
+ t.integer :book_id
107
+ t.timestamps
108
+ end
109
+
110
+ create_table :tags do |t|
111
+ t.string :name
112
+ t.string :taggable_type
113
+ t.integer :taggable_id
114
+ t.timestamp :deleted_at
115
+ end
116
+
117
+ create_table :covers do |t|
118
+ t.integer :book_id
119
+ t.timestamps
120
+ t.timestamp :deleted_at
121
+ end
122
+
123
+ create_table :authors do |t|
124
+ t.timestamps
125
+ t.timestamp :deleted_at
126
+ end
127
+
128
+ create_table :libraries do |t|
129
+ t.timestamps
130
+ t.timestamp :deleted_at
131
+ end
132
+ end
133
+ end
134
+
135
+ config.after :suite do
136
+ ActiveRecord::Base.connection.drop_database("zombie_record") rescue nil
137
+ end
138
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zombie_record/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zombie_record"
8
+ spec.version = ZombieRecord::VERSION
9
+ spec.authors = ["Daniel Schierbeck"]
10
+ spec.email = ["dasch@zendesk.com"]
11
+ spec.description = %q{Allows restoring your Active Records from the dead!}
12
+ spec.summary = %q{Allows restoring your Active Records from the dead!}
13
+ spec.homepage = "https://github.com/dasch/zombie_record"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activerecord", "~> 3.2.15"
22
+ spec.add_dependency "mysql2"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "timecop", "~> 0.7.0"
28
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zombie_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Schierbeck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-26 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: 3.2.15
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.15
27
+ - !ruby/object:Gem::Dependency
28
+ name: mysql2
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
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: timecop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.7.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.7.0
97
+ description: Allows restoring your Active Records from the dead!
98
+ email:
99
+ - dasch@zendesk.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - CHANGELOG.md
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - lib/zombie_record.rb
113
+ - lib/zombie_record/restorable.rb
114
+ - lib/zombie_record/version.rb
115
+ - spec/restorable_spec.rb
116
+ - spec/spec_helper.rb
117
+ - zombie_record.gemspec
118
+ homepage: https://github.com/dasch/zombie_record
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.2.2
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Allows restoring your Active Records from the dead!
142
+ test_files:
143
+ - spec/restorable_spec.rb
144
+ - spec/spec_helper.rb
145
+ has_rdoc: