destroyed_at 0.1.0

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: 27eab1991579adb70e518846349f055c1b3987eb
4
+ data.tar.gz: 0dfd9c729a38a9b6e8a39111cfb1e3ea25e0051c
5
+ SHA512:
6
+ metadata.gz: bca0bb1c670c15093a97181d81fc4437382abebb78bcfb6fb46aeafe8605df76dc1c52c875e8b6fa091a9db20d16945e3e759b19e40a0f9db929be7955ecb6d8
7
+ data.tar.gz: f691ed1739ba388af682df654ddfd9261c8efaddfcda9e805628f14e32619eaaab7f665efc630bda8814d62650164305ed2e77b1f2fb106e3f89d695bb60163e
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/.travis.yml ADDED
@@ -0,0 +1,2 @@
1
+ rvm:
2
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in destroyed_at.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # DestroyedAt #
2
+
3
+ [![Build Status](https://secure.travis-ci.org/dockyard/destroyed_at.png?branch=master)](http://travis-ci.org/dockyard/destroyed_at)
4
+ [![Dependency Status](https://gemnasium.com/dockyard/destroyed_at.png?travis)](https://gemnasium.com/dockyard/destroyed_at)
5
+ [![Code Climate](https://codeclimate.com/github/dockyard/destroyed_at.png)](https://codeclimate.com/github/dockyard/destroyed_at)
6
+
7
+ ## Looking for help? ##
8
+
9
+ If it is a bug [please open an issue on GitHub](https://github.com/dockyard/destroyed_at/issues).
10
+
11
+ ## Installation ##
12
+
13
+ Add the `destroyed_at` gem to your `Gemfile`
14
+
15
+ ```ruby
16
+ gem 'destroyed_at'
17
+ ```
18
+
19
+ You can either mixin the modules on a case-by-case basis:
20
+
21
+ ```ruby
22
+ class User < ActiveRecord::Base
23
+ include DestroyedAt
24
+ end
25
+ ```
26
+
27
+ or make the changes globally:
28
+
29
+ ```ruby
30
+ class ActiveRecord::Base
31
+ include DestroyedAt
32
+ end
33
+ ```
34
+
35
+ Each model's table that is expected to have this behavior **must** have
36
+ a `destroyed_at` column of type `DateTime`.
37
+
38
+ ## Usage ##
39
+ Allows you to "destroy" an object without deleting the record or
40
+ associated records.
41
+
42
+ ### Destroying ###
43
+ Overides the `destroy` method to set `destroyed_at` on an object. The
44
+ default scope of the class is then set to return objects that have not
45
+ been destroyed (i.e., have `nil` for their destroyed_at value).
46
+
47
+ `#destroyed?` will be `true` when your model is destroyed; it will be
48
+ `false` when your model has been undestroyed.
49
+
50
+ ## Undestroying ###
51
+ When you'd like to "undestroy" a record, call the `undestroy` method on
52
+ the instance. This will set its `destroyed_at` value to `nil`, thereby
53
+ including it in the default scope of the class again.
54
+
55
+ To include this functionality on `has_many through` relationships,
56
+ be sure to `include DestroyedAt` on the through model, as well as the
57
+ parent model.
58
+
59
+ #### Callbacks ####
60
+ `before_undestroy` and `after_undestroy` callbacks are added to your
61
+ model. They work similarly to the `before_destroy` and `after_destroy`
62
+ callbacks.
63
+
64
+ ### Destroying ###
65
+ ```ruby
66
+ class User < ActiveRecord::Base
67
+ include DestroyedAt
68
+ end
69
+
70
+ user = User.create
71
+ user.destroy
72
+ # => true
73
+ ```
74
+
75
+ ### Undestroying ###
76
+ ```ruby
77
+ class User < ActiveRecord::Base
78
+ include DestroyedAt
79
+ end
80
+
81
+ user = User.create
82
+ user.destroy
83
+ user.undestroy
84
+ # => true
85
+ ```
86
+
87
+ ## Authors ##
88
+
89
+ * [Michael Dupuis](http://twitter.com/michaeldupuisjr)
90
+ * [Brian Cardarella](http://twitter.com/bcardarella)
91
+
92
+ [We are very thankful for the many contributors](https://github.com/dockyard/destroyed_at/graphs/contributors)
93
+
94
+ ## Versioning ##
95
+
96
+ This gem follows [Semantic Versioning](http://semver.org)
97
+
98
+ ## Want to help? ##
99
+
100
+ Please do! We are always looking to improve this gem. Please see our
101
+ [Contribution Guidelines](https://github.com/dockyard/destroyed_at/blob/master/CONTRIBUTING.md)
102
+ on how to properly submit issues and pull requests.
103
+
104
+ ## Legal ##
105
+
106
+ [DockYard](http://dockyard.com), LLC &copy; 2013
107
+
108
+ [@dockyard](http://twitter.com/dockyard)
109
+
110
+ [Licensed under the MIT license](http://www.opensource.org/licenses/mit-license.php)
111
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << 'lib'
7
+ t.libs << 'test'
8
+ t.pattern = 'test/**/*_test.rb'
9
+ t.verbose = false
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'destroyed_at/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "destroyed_at"
8
+ spec.version = DestroyedAt::VERSION
9
+ spec.authors = ["Michael Dupuis Jr."]
10
+ spec.email = ["michael.dupuis@dockyard.com"]
11
+ spec.description = %q{Safe destroy for ActiveRecord.}
12
+ spec.summary = %q{Safe destroy for ActiveRecord.}
13
+ spec.homepage = "https://github.com/dockyard/destroyed_at"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "activerecord", "~> 3.2"
23
+ spec.add_development_dependency "minitest", "~> 5"
24
+ spec.add_development_dependency "m"
25
+ spec.add_development_dependency "sqlite3"
26
+ spec.add_development_dependency "byebug"
27
+ spec.add_development_dependency "timecop"
28
+ spec.add_development_dependency "database_cleaner"
29
+ end
@@ -0,0 +1,48 @@
1
+ require "destroyed_at/version"
2
+
3
+ module DestroyedAt
4
+ def self.included(klass)
5
+ klass.instance_eval do
6
+ default_scope { where(destroyed_at: nil) }
7
+ after_initialize :_set_destruction_state
8
+ define_model_callbacks :undestroy
9
+ end
10
+ end
11
+
12
+ # Set an object's destroyed_at time.
13
+ def destroy
14
+ run_callbacks(:destroy) do
15
+ destroy_associations
16
+ self.update_attribute(:destroyed_at, DateTime.current)
17
+ @destroyed = true
18
+ end
19
+ end
20
+
21
+ # Set an object's destroyed at time to nil.
22
+ def undestroy
23
+ state = nil
24
+ run_callbacks(:undestroy) do
25
+ if state = self.update_attribute(:destroyed_at, nil)
26
+ @destroyed = false
27
+ _undestroy_associations
28
+ end
29
+ end
30
+ state
31
+ end
32
+
33
+ private
34
+
35
+ def _set_destruction_state
36
+ @destroyed = destroyed_at.present?
37
+ end
38
+
39
+ def _undestroy_associations
40
+ reflections.select { |key, value| value.options[:dependent] == :destroy }.keys.each do |key|
41
+ assoc = association(key)
42
+ if assoc.options[:through] && assoc.options[:dependent] == :destroy
43
+ assoc = association(assoc.options[:through])
44
+ end
45
+ assoc.scoped.unscoped.each { |r| r.undestroy if r.respond_to? :undestroy }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module DestroyedAt
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,101 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Destroying AR models' do
4
+ it 'Calling destroy on a model should only safe destroy record' do
5
+ user = User.create
6
+ user.destroy
7
+ User.all.must_be_empty
8
+ User.unscoped.all.wont_be_empty
9
+ end
10
+
11
+ it 'Destroying a model will set the timestamp it was destroyed at' do
12
+ date = DateTime.current
13
+ Timecop.freeze(date) do
14
+ user = User.create
15
+ user.destroy
16
+ user.destroyed_at.inspect.must_equal date.inspect
17
+ end
18
+ end
19
+
20
+ it 'can undestroy records' do
21
+ user = User.create(:destroyed_at => DateTime.current)
22
+ User.all.must_be_empty
23
+ user.reload
24
+ user.undestroy
25
+ User.all.wont_be_empty
26
+ end
27
+
28
+ it 'will run destroy callbacks' do
29
+ person = Person.create
30
+ person.before_flag.wont_equal true
31
+ person.after_flag.wont_equal true
32
+ person.destroy
33
+ person.before_flag.must_equal true
34
+ person.after_flag.must_equal true
35
+ end
36
+
37
+ it 'will run undestroy callbacks' do
38
+ person = Person.create(:destroyed_at => DateTime.current)
39
+ person.before_flag.wont_equal true
40
+ person.after_flag.wont_equal true
41
+ person.undestroy
42
+ person.before_flag.must_equal true
43
+ person.after_flag.must_equal true
44
+ end
45
+
46
+ it 'will properly destroy relations' do
47
+ user = User.create(:profile => Profile.new, :car => Car.new)
48
+ user.reload
49
+ user.profile.wont_be_nil
50
+ user.car.wont_be_nil
51
+ user.destroy
52
+ Profile.count.must_equal 0
53
+ Profile.unscoped.count.must_equal 1
54
+ Car.count.must_equal 0
55
+ Car.unscoped.count.must_equal 0
56
+ end
57
+
58
+ it 'can undestroy relationships' do
59
+ user = User.create(:destroyed_at => DateTime.current)
60
+ Profile.create(:destroyed_at => DateTime.current, :user => user)
61
+ Profile.count.must_equal 0
62
+ user.undestroy
63
+ Profile.count.must_equal 1
64
+ end
65
+
66
+ it 'will not undestroy relationships that have no destroy dependency' do
67
+ user = User.create(:destroyed_at => DateTime.current, :show => Show.new(:destroyed_at => DateTime.current))
68
+ Show.count.must_equal 0
69
+ user.undestroy
70
+ Show.count.must_equal 0
71
+ end
72
+
73
+ it 'will respect has many associations' do
74
+ user = User.create(:destroyed_at => DateTime.current)
75
+ Dinner.create(:destroyed_at => DateTime.current, :user => user)
76
+ Dinner.count.must_equal 0
77
+ user.undestroy
78
+ Dinner.count.must_equal 1
79
+ end
80
+
81
+ it 'will respect has many through associations' do
82
+ #user = User.create(:destroyed_at => DateTime.current, :fleets => [Fleet.new(:destroyed_at => DateTime.current, :car => Car.new)])
83
+ user = User.create(:destroyed_at => DateTime.current)
84
+ car = Car.create
85
+ fleet = Fleet.create(:destroyed_at => DateTime.current, :car => car)
86
+ user.fleets = [fleet]
87
+ user.cars.count.must_equal 0
88
+ user.undestroy
89
+ user.cars.count.must_equal 1
90
+ end
91
+
92
+ it 'properly sets #destroyed?' do
93
+ user = User.create
94
+ user.destroy
95
+ user.destroyed?.must_equal true
96
+ user = User.unscoped.last
97
+ user.destroyed?.must_equal true
98
+ user.undestroy
99
+ user.destroyed?.must_equal false
100
+ end
101
+ end
@@ -0,0 +1,88 @@
1
+ require 'bundler/setup'
2
+ require 'destroyed_at'
3
+ require 'minitest/autorun'
4
+ require 'active_record'
5
+ require 'byebug'
6
+ require 'timecop'
7
+ require 'database_cleaner'
8
+
9
+ class Minitest::Spec
10
+ class << self
11
+ alias :context :describe
12
+ end
13
+
14
+ before do
15
+ DatabaseCleaner.start
16
+ end
17
+
18
+ after do
19
+ DatabaseCleaner.clean
20
+ end
21
+ end
22
+
23
+ ActiveRecord::Base.establish_connection(
24
+ :adapter => defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3',
25
+ :database => ':memory:'
26
+ )
27
+
28
+ DatabaseCleaner.strategy = :truncation
29
+
30
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE users (id INTEGER PRIMARY KEY, destroyed_at DATETIME, type STRING);})
31
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE profiles (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER);})
32
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE cars (id INTEGER PRIMARY KEY, user_id INTEGER);})
33
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE dinners (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER);})
34
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE shows (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER);})
35
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE fleets (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER, car_id INTEGER);})
36
+
37
+ class User < ActiveRecord::Base
38
+ include DestroyedAt
39
+ has_one :profile, :dependent => :destroy
40
+ has_one :car, :dependent => :destroy
41
+ has_many :dinners, :dependent => :destroy
42
+ has_one :show
43
+ has_many :fleets
44
+ has_many :cars, :through => :fleets, :dependent => :destroy
45
+ end
46
+
47
+ class Person < User
48
+ before_destroy :set_before_flag
49
+ after_destroy :set_after_flag
50
+
51
+ before_undestroy :set_before_flag
52
+ after_undestroy :set_after_flag
53
+
54
+ attr_accessor :before_flag, :after_flag
55
+
56
+ def set_before_flag
57
+ self.before_flag = true
58
+ end
59
+
60
+ def set_after_flag
61
+ self.after_flag = true
62
+ end
63
+ end
64
+
65
+ class Profile < ActiveRecord::Base
66
+ include DestroyedAt
67
+ belongs_to :user
68
+ end
69
+
70
+ class Car < ActiveRecord::Base
71
+ belongs_to :user
72
+ has_many :fleets
73
+ end
74
+
75
+ class Dinner < ActiveRecord::Base
76
+ include DestroyedAt
77
+ belongs_to :user
78
+ end
79
+
80
+ class Show < ActiveRecord::Base
81
+ include DestroyedAt
82
+ end
83
+
84
+ class Fleet < ActiveRecord::Base
85
+ include DestroyedAt
86
+ belongs_to :user
87
+ belongs_to :car
88
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: destroyed_at
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Dupuis Jr.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: activerecord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: m
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: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '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'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: timecop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: database_cleaner
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Safe destroy for ActiveRecord.
140
+ email:
141
+ - michael.dupuis@dockyard.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - .gitignore
147
+ - .travis.yml
148
+ - Gemfile
149
+ - README.md
150
+ - Rakefile
151
+ - destroyed_at.gemspec
152
+ - lib/destroyed_at.rb
153
+ - lib/destroyed_at/version.rb
154
+ - test/destroyed_at_test.rb
155
+ - test/test_helper.rb
156
+ homepage: https://github.com/dockyard/destroyed_at
157
+ licenses:
158
+ - MIT
159
+ metadata: {}
160
+ post_install_message:
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubyforge_project:
176
+ rubygems_version: 2.0.0
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: Safe destroy for ActiveRecord.
180
+ test_files:
181
+ - test/destroyed_at_test.rb
182
+ - test/test_helper.rb