immortal 0.1.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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in immortal.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ immortal (0.1.0)
5
+ activerecord (~> 3.0.3)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.0.3)
11
+ activesupport (= 3.0.3)
12
+ builder (~> 2.1.2)
13
+ i18n (~> 0.4)
14
+ activerecord (3.0.3)
15
+ activemodel (= 3.0.3)
16
+ activesupport (= 3.0.3)
17
+ arel (~> 2.0.2)
18
+ tzinfo (~> 0.3.23)
19
+ activesupport (3.0.3)
20
+ arel (2.0.6)
21
+ builder (2.1.2)
22
+ diff-lcs (1.1.2)
23
+ i18n (0.5.0)
24
+ rspec (2.3.0)
25
+ rspec-core (~> 2.3.0)
26
+ rspec-expectations (~> 2.3.0)
27
+ rspec-mocks (~> 2.3.0)
28
+ rspec-core (2.3.1)
29
+ rspec-expectations (2.3.0)
30
+ diff-lcs (~> 1.1.2)
31
+ rspec-mocks (2.3.0)
32
+ sqlite3-ruby (1.3.2)
33
+ tzinfo (0.3.23)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ activerecord (~> 3.0.3)
40
+ immortal!
41
+ rspec (~> 2.3.0)
42
+ sqlite3-ruby (~> 1.3.2)
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Immortal
2
+
3
+ Make any ActiveRecord model paranoid by just including `Immortal`, and instead of being deleted from the database, the object will just marked as 'deleted' with a boolean field in the database.
4
+
5
+ ## Installation
6
+
7
+ Add the gem dependency to your Gemfile:
8
+
9
+ gem 'immortal'
10
+
11
+ ## Usage
12
+
13
+ class User < ActiveRecord::Base
14
+ include Immortal
15
+ end
16
+
17
+ And add a boolean field called `deleted` to that model:
18
+
19
+ class AddDeletedToUsers < ActiveRecord::Migration
20
+ def self.up
21
+ add_column :users, :deleted, :boolean
22
+ end
23
+
24
+ def self.down
25
+ remove_column :users, :deleted
26
+ end
27
+ end
28
+
29
+ ## TODO
30
+
31
+ - Add documentation in the code
32
+ - Spec associations
33
+ - Add support for a :with_deleted option in associations, like acts_as_paranoid
34
+ - Improve compatibility with acts_as_paranoid
35
+
36
+ ## Contributing
37
+
38
+ If you want to improve immortal
39
+
40
+ 1. Fork the repo
41
+ 2. Create a topic branch `git checkout -b my_feature`
42
+ 3. Push it! `git push origin my_feature`
43
+ 4. Open a pull request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/immortal.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "immortal"
6
+ s.version = '0.1.0'
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Jordi Romero", "Saimon Moore"]
9
+ s.email = ["jordi@jrom.net", "saimon@saimonmoore.net"]
10
+ s.homepage = "http://github.com/teambox/immortal"
11
+ s.summary = %q{Replacement for acts_as_paranoid for Rails 3}
12
+ s.description = %q{Typical paranoid gem built for Rails 3 and with the minimum code needed to satisfy acts_as_paranoid's API}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency 'activerecord', '~> 3.0.3'
20
+ s.add_development_dependency 'rspec', '~> 2.3.0'
21
+ s.add_development_dependency 'sqlite3-ruby', '~> 1.3.2'
22
+ end
data/lib/immortal.rb ADDED
@@ -0,0 +1,80 @@
1
+ module Immortal
2
+ def self.included(base)
3
+ base.send :extend, ClassMethods
4
+ base.send :include, InstanceMethods
5
+ base.class_eval do
6
+ class << self
7
+ alias :mortal_delete_all :delete_all
8
+ alias :delete_all :immortal_delete_all
9
+ end
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ def with_deleted
16
+ unscoped
17
+ end
18
+
19
+ def only_deleted
20
+ unscoped.where(:deleted => true)
21
+ end
22
+
23
+ def count_with_deleted(*args)
24
+ with_deleted.count(*args)
25
+ end
26
+
27
+ def count_only_deleted(*args)
28
+ only_deleted.count(*args)
29
+ end
30
+
31
+ def find_with_deleted(*args)
32
+ with_deleted.find(*args)
33
+ end
34
+
35
+ def find_only_deleted(*args)
36
+ only_deleted.find(*args)
37
+ end
38
+
39
+ def immortal_delete_all(*args)
40
+ unscoped.update_all :deleted => true
41
+ end
42
+
43
+ def delete_all!(*args)
44
+ unscoped.mortal_delete_all
45
+ end
46
+
47
+ end
48
+
49
+ module InstanceMethods
50
+ def self.included(base)
51
+ base.class_eval do
52
+ default_scope where(arel_table[:deleted].eq(nil).or(arel_table[:deleted].eq(false)))
53
+ alias :mortal_destroy :destroy
54
+ alias :destroy :immortal_destroy
55
+ end
56
+ end
57
+
58
+ def immortal_destroy(*args)
59
+ run_callbacks :destroy do
60
+ destroy_without_callbacks(*args)
61
+ end
62
+ end
63
+
64
+ def destroy!(*args)
65
+ mortal_destroy
66
+ end
67
+
68
+ def destroy_without_callbacks(*args)
69
+ self.class.unscoped.update_all({ :deleted => true }, "id = #{self.id}")
70
+ reload
71
+ freeze
72
+ end
73
+
74
+ def recover!
75
+ self.class.unscoped.update_all({ :deleted => false }, "id = #{self.id}")
76
+ reload
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,180 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Immortal do
4
+ before do
5
+ @m = ImmortalModel.create! :title => 'testing immortal'
6
+ end
7
+
8
+ it "should not be deleted from the database using #destroy" do
9
+ expect {
10
+ @m.destroy
11
+ }.to_not change(ImmortalModel, :count_with_deleted)
12
+ end
13
+
14
+ it "should be frozen using #destroy" do
15
+ @m.destroy
16
+ @m.should be_frozen
17
+ end
18
+
19
+ it "should not be dirty using #destroy" do
20
+ @m.destroy
21
+ @m.should_not be_changed
22
+ end
23
+
24
+ it "should be deleted from the database using #destroy!" do
25
+ expect {
26
+ @m.destroy!
27
+ }.to change(ImmortalModel, :count_with_deleted)
28
+ end
29
+
30
+ it "should find non deleted records" do
31
+ ImmortalModel.first.should == @m
32
+ ImmortalModel.all.should include(@m)
33
+ end
34
+
35
+ it "should not find deleted records" do
36
+ @m.destroy
37
+ ImmortalModel.first.should be_nil
38
+ ImmortalModel.all.should be_empty
39
+ end
40
+
41
+ it "should find deleted records using scope" do
42
+ @m.destroy
43
+ ImmortalModel.with_deleted.first.should == @m
44
+ ImmortalModel.with_deleted.all.should include(@m)
45
+ end
46
+
47
+ it "should find deleted records using the old method" do
48
+ ImmortalModel.find_with_deleted(@m.id).should == @m
49
+ @m.destroy
50
+ ImmortalModel.find_with_deleted(@m.id).should == @m
51
+ end
52
+
53
+ it "should count undeleted records by default" do
54
+ @m2 = ImmortalModel.create! :title => 'testing immortal again'
55
+ ImmortalModel.count_only_deleted.should == 0
56
+ ImmortalModel.only_deleted.count.should == 0
57
+
58
+ @m.destroy
59
+
60
+ ImmortalModel.count_only_deleted.should == 1
61
+ ImmortalModel.only_deleted.count.should == 1
62
+ end
63
+
64
+ it "should find only deleted records" do
65
+ @m2 = ImmortalModel.create! :title => 'testing immortal again'
66
+ expect {
67
+ ImmortalModel.find_only_deleted(@m.id)
68
+ }.to raise_error(ActiveRecord::RecordNotFound)
69
+
70
+ expect {
71
+ ImmortalModel.only_deleted.find(@m.id)
72
+ }.to raise_error(ActiveRecord::RecordNotFound)
73
+
74
+ @m.destroy
75
+
76
+ ImmortalModel.find_only_deleted(@m.id).should == @m
77
+ expect {
78
+ ImmortalModel.find_only_deleted(@m2.id)
79
+ }.to raise_error(ActiveRecord::RecordNotFound)
80
+
81
+ ImmortalModel.only_deleted.should include(@m)
82
+ ImmortalModel.only_deleted.should_not include(@m2)
83
+ end
84
+
85
+ it "should be able to count undeleted records" do
86
+ @m2 = ImmortalModel.create! :title => 'testing immortal again'
87
+ ImmortalModel.count.should == 2
88
+
89
+ @m.destroy
90
+
91
+ ImmortalModel.count.should == 1
92
+ end
93
+
94
+ it "should be able to count all the records including deleted" do
95
+ @m2 = ImmortalModel.create! :title => 'testing immortal again'
96
+ @m.destroy
97
+ ImmortalModel.count_with_deleted.should == 2
98
+ ImmortalModel.with_deleted.count.should == 2
99
+ end
100
+
101
+ it "should not exist if deleted" do
102
+ ImmortalModel.exists?(@m.id).should be_true
103
+ @m.destroy
104
+ ImmortalModel.exists?(@m.id).should be_false
105
+ end
106
+
107
+ it "should calculate without deleted" do
108
+ @m2 = ImmortalModel.create! :value => 10
109
+ @m3 = ImmortalModel.create! :value => 20
110
+ ImmortalModel.calculate(:sum, :value).should == 30
111
+ @m2.destroy
112
+ ImmortalModel.calculate(:sum, :value).should == 20
113
+ end
114
+
115
+ it "should execute the before_destroy callback when immortally destroyed" do
116
+ @m.destroy
117
+ @m.before_d.should be_true
118
+ end
119
+
120
+ it "should execute the after_destroy callback when immortally destroyed" do
121
+ @m.destroy
122
+ @m.after_d.should be_true
123
+ end
124
+
125
+ it "should not execute the before_update callback when immortally destroyed" do
126
+ @m.destroy
127
+ @m.before_u.should be_nil
128
+ end
129
+
130
+ it "should not execute the after_update callback when immortally destroyed" do
131
+ @m.destroy
132
+ @m.after_u.should be_nil
133
+ end
134
+
135
+ it "should not execute the before_destroy callback when immortally destroyed without callbacks" do
136
+ @m.destroy_without_callbacks
137
+ @m.before_d.should be_nil
138
+ end
139
+
140
+ it "should not execute the after_destroy callback when immortally destroyed without callbacks" do
141
+ @m.destroy_without_callbacks
142
+ @m.after_d.should be_nil
143
+ end
144
+
145
+ it "should immortally delete all records with delete_all" do
146
+ expect {
147
+ ImmortalModel.delete_all
148
+ }.to change(ImmortalModel, :count).by(-1)
149
+ ImmortalModel.count_with_deleted.should == 1
150
+ end
151
+
152
+ it "should immortally delete all records with delete_all!" do
153
+ expect {
154
+ ImmortalModel.delete_all!
155
+ }.to change(ImmortalModel.with_deleted, :count).by(-1)
156
+ end
157
+
158
+ it "should know if it's deleted" do
159
+ @m.should_not be_deleted
160
+ @m.destroy
161
+ @m.should be_deleted
162
+ end
163
+
164
+ it "should be recoverable" do
165
+ @m.destroy
166
+ @m = ImmortalModel.with_deleted.find(@m.id)
167
+ @m.recover!
168
+ @m.should_not be_frozen
169
+ @m.should_not be_changed
170
+ ImmortalModel.first.should == @m
171
+ end
172
+
173
+ it "should consider an object with deleted = nil as not deleted" do
174
+ @m2 = ImmortalModel.create! :deleted => nil
175
+ @m2.deleted.should be_nil
176
+ @m2.should_not be_deleted
177
+ ImmortalModel.count.should == 2
178
+ end
179
+
180
+ end
@@ -0,0 +1,62 @@
1
+ Bundler.require(:default, :development)
2
+ require 'rspec'
3
+ require 'lib/immortal'
4
+ require 'active_record'
5
+ require 'sqlite3'
6
+
7
+ RSpec.configure do |config|
8
+ config.before(:each) do
9
+ ActiveRecord::Base.connection.execute('delete from immortal_models')
10
+ end
11
+
12
+ config.after(:all) do
13
+ end
14
+ end
15
+
16
+
17
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
18
+
19
+ old_stdout = $stdout
20
+ $stdout = StringIO.new
21
+
22
+ begin
23
+ ActiveRecord::Schema.define do
24
+ create_table :immortal_models do |t|
25
+ t.string :title
26
+ t.integer :value
27
+ t.boolean :deleted, :default => false
28
+ t.timestamps
29
+ end
30
+ end
31
+ ensure
32
+ $stdout = old_stdout
33
+ end
34
+
35
+ class ImmortalModel < ActiveRecord::Base
36
+ include Immortal
37
+
38
+ attr_accessor :before_d, :after_d, :before_u, :after_u
39
+
40
+ before_destroy :set_before
41
+ after_destroy :set_after
42
+ before_update :set_before_update
43
+ after_update :set_after_update
44
+
45
+ private
46
+ def set_before
47
+ @before_d = true
48
+ end
49
+
50
+ def set_after
51
+ @after_d = true
52
+ end
53
+
54
+ def set_after_update
55
+ @after_u = true
56
+ end
57
+
58
+ def set_before_update
59
+ @before_u = true
60
+ end
61
+
62
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: immortal
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Jordi Romero
14
+ - Saimon Moore
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-12-17 00:00:00 +01:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: activerecord
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 1
31
+ segments:
32
+ - 3
33
+ - 0
34
+ - 3
35
+ version: 3.0.3
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 2
49
+ - 3
50
+ - 0
51
+ version: 2.3.0
52
+ type: :development
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: sqlite3-ruby
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ hash: 31
63
+ segments:
64
+ - 1
65
+ - 3
66
+ - 2
67
+ version: 1.3.2
68
+ type: :development
69
+ version_requirements: *id003
70
+ description: Typical paranoid gem built for Rails 3 and with the minimum code needed to satisfy acts_as_paranoid's API
71
+ email:
72
+ - jordi@jrom.net
73
+ - saimon@saimonmoore.net
74
+ executables: []
75
+
76
+ extensions: []
77
+
78
+ extra_rdoc_files: []
79
+
80
+ files:
81
+ - .gitignore
82
+ - Gemfile
83
+ - Gemfile.lock
84
+ - README.md
85
+ - Rakefile
86
+ - immortal.gemspec
87
+ - lib/immortal.rb
88
+ - spec/immortal_spec.rb
89
+ - spec/spec_helper.rb
90
+ has_rdoc: true
91
+ homepage: http://github.com/teambox/immortal
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project:
120
+ rubygems_version: 1.3.7
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Replacement for acts_as_paranoid for Rails 3
124
+ test_files:
125
+ - spec/immortal_spec.rb
126
+ - spec/spec_helper.rb