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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/Rakefile +6 -0
- data/lib/zombie_record.rb +7 -0
- data/lib/zombie_record/restorable.rb +195 -0
- data/lib/zombie_record/version.rb +3 -0
- data/spec/restorable_spec.rb +179 -0
- data/spec/spec_helper.rb +138 -0
- data/zombie_record.gemspec +28 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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:
|