joshuaclayton-paranoia 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Joshua Clayton
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.
@@ -0,0 +1,12 @@
1
+ lib/paranoia.rb
2
+ MIT-LICENSE
3
+ rails/init.rb
4
+ Rakefile
5
+ README.textile
6
+ test/database.yml
7
+ test/factories.rb
8
+ test/models.rb
9
+ test/paranoia_test.rb
10
+ test/schema.rb
11
+ test/test_helper.rb
12
+ Manifest
@@ -0,0 +1,7 @@
1
+ h1. Paranoia
2
+
3
+ Essentially, paranoia is a ripoff of is_paranoid; I'm just trying to get the paranoid scope to work without using `with_exclusive_scope`, since it nullifies any other named scopes etc.
4
+
5
+ It's purely test-driven, since I'm planning on hacking on it 'til everything passes. Until then, consider this alpha software.
6
+
7
+ Copyright (c) 2009 Joshua Clayton, released under the MIT license
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+ require 'rake/testtask'
5
+
6
+ Echoe.new("paranoia", "0.0.1") do |p|
7
+ p.description = "Paranoid models"
8
+ p.url = "http://github.com/joshuaclayton/paranoia"
9
+ p.author = "Joshua Clayton"
10
+ p.email = "joshua.clayton@gmail.com"
11
+ p.ignore_pattern = ["tmp/*"]
12
+ p.development_dependencies = ["activerecord >= 2.3.0", "activesupport >= 2.1.0"]
13
+ end
@@ -0,0 +1,29 @@
1
+ require "active_record"
2
+
3
+ module Paranoia
4
+
5
+ def paranoia(opts = {})
6
+ opts[:field] ||= [:deleted_at, Proc.new { Time.now.utc }, nil]
7
+ class_inheritable_accessor :destroyed_field, :field_destroyed, :field_not_destroyed
8
+ self.destroyed_field, self.field_destroyed, self.field_not_destroyed = opts[:field]
9
+
10
+ extend ClassMethods
11
+ include InstanceMethods
12
+ end
13
+
14
+ module ClassMethods
15
+ def with_destroyed
16
+ scoped({})
17
+ end
18
+
19
+ def only_destroyed
20
+ scoped({:conditions => ["#{self.quoted_table_name}.#{self.destroyed_field} IS NOT ?", self.field_not_destroyed]})
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+
26
+ end
27
+ end
28
+
29
+ ActiveRecord::Base.send(:extend, Paranoia)
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{paranoia}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Joshua Clayton"]
9
+ s.date = %q{2009-05-30}
10
+ s.description = %q{Paranoid models}
11
+ s.email = %q{joshua.clayton@gmail.com}
12
+ s.extra_rdoc_files = ["lib/paranoia.rb", "README.textile"]
13
+ s.files = ["lib/paranoia.rb", "MIT-LICENSE", "rails/init.rb", "Rakefile", "README.textile", "test/database.yml", "test/factories.rb", "test/models.rb", "test/paranoia_test.rb", "test/schema.rb", "test/test_helper.rb", "Manifest", "paranoia.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/joshuaclayton/paranoia}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Paranoia", "--main", "README.textile"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{paranoia}
19
+ s.rubygems_version = %q{1.3.2}
20
+ s.summary = %q{Paranoid models}
21
+ s.test_files = ["test/paranoia_test.rb", "test/test_helper.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_development_dependency(%q<activerecord>, [">= 0", ">= 2.3.0"])
29
+ s.add_development_dependency(%q<activesupport>, [">= 0", ">= 2.1.0"])
30
+ else
31
+ s.add_dependency(%q<activerecord>, [">= 0", ">= 2.3.0"])
32
+ s.add_dependency(%q<activesupport>, [">= 0", ">= 2.1.0"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<activerecord>, [">= 0", ">= 2.3.0"])
36
+ s.add_dependency(%q<activesupport>, [">= 0", ">= 2.1.0"])
37
+ end
38
+ end
@@ -0,0 +1 @@
1
+ require "paranoia"
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,8 @@
1
+ Factory.define :person do |person|
2
+ person.sequence(:name) {|n| "Person #{n}" }
3
+ end
4
+
5
+ Factory.define :android do |android|
6
+ android.sequence(:name) {|n| "Person #{n}" }
7
+ android.active { true }
8
+ end
@@ -0,0 +1,95 @@
1
+ class Person < ActiveRecord::Base
2
+ validates_uniqueness_of :name
3
+ has_many :androids, :foreign_key => :owner_id, :dependent => :destroy
4
+ end
5
+
6
+ class Android < ActiveRecord::Base
7
+ validates_uniqueness_of :name
8
+ has_many :components, :dependent => :destroy
9
+ has_one :sticker
10
+ has_many :memories, :foreign_key => "parent_id"
11
+
12
+ named_scope :active, :conditions => {:active => true}
13
+ paranoia
14
+
15
+ # this code is to ensure that our destroy and restore methods
16
+ # work without triggering before/after_update callbacks
17
+ before_update :raise_hell
18
+ def raise_hell
19
+ raise RuntimeError, "hell"
20
+ end
21
+ end
22
+
23
+ class Component < ActiveRecord::Base
24
+ paranoia
25
+ belongs_to :android, :dependent => :destroy
26
+ has_many :sub_components, :dependent => :destroy
27
+ NEW_NAME = "Something Else!"
28
+
29
+ after_destroy :change_name
30
+ def change_name
31
+ self.update_attribute(:name, NEW_NAME)
32
+ end
33
+ end
34
+
35
+ class SubComponent < ActiveRecord::Base
36
+ paranoia
37
+ belongs_to :component, :dependent => :destroy
38
+ end
39
+
40
+ class Memory < ActiveRecord::Base
41
+ paranoia
42
+ belongs_to :android, :class_name => "Android", :foreign_key => "parent_id"
43
+ end
44
+
45
+ class Sticker < ActiveRecord::Base
46
+ MM_NAME = "You've got method_missing"
47
+
48
+ # this simply serves to ensure that we don't break method_missing
49
+ # if it is implemented on a class and called before paranoia
50
+ def method_missing(name, *args, &block)
51
+ self.name = MM_NAME
52
+ end
53
+
54
+ paranoia
55
+ belongs_to :android
56
+ end
57
+
58
+ class AndroidWithScopedUniqueness < ActiveRecord::Base
59
+ set_table_name :androids
60
+ validates_uniqueness_of :name, :scope => :deleted_at
61
+ paranoia
62
+ end
63
+
64
+ class Ninja < ActiveRecord::Base
65
+ validates_uniqueness_of :name, :scope => :visible
66
+ paranoia :field => [:visible, false, true]
67
+
68
+ alias_method :vanish, :destroy
69
+ end
70
+
71
+ class Pirate < ActiveRecord::Base
72
+ paranoia :field => [:alive, false, true]
73
+ end
74
+
75
+ class DeadPirate < ActiveRecord::Base
76
+ set_table_name :pirates
77
+ paranoia :field => [:alive, true, false]
78
+ end
79
+
80
+ class RandomPirate < ActiveRecord::Base
81
+ set_table_name :pirates
82
+
83
+ def after_destroy
84
+ raise "after_destroy works"
85
+ end
86
+ end
87
+
88
+ class UndestroyablePirate < ActiveRecord::Base
89
+ set_table_name :pirates
90
+ paranoia :field => [:alive, false, true]
91
+
92
+ def before_destroy
93
+ false
94
+ end
95
+ end
@@ -0,0 +1,80 @@
1
+ require "test_helper"
2
+
3
+ class ParanoiaTest < ActiveSupport::TestCase
4
+ context "A model without paranoia" do
5
+ setup { @person = Factory(:person) }
6
+
7
+ context "when destroyed" do
8
+ setup { @person.destroy }
9
+ should_change "Person.count", :by => -1
10
+ end
11
+
12
+ should "not be paranoid" do
13
+ assert_raise NoMethodError do
14
+ Person.count_with_destroyed
15
+ end
16
+ end
17
+ end
18
+
19
+ context "A model with paranoia" do
20
+ setup { @android = Factory(:android); Factory(:android, :active => false) }
21
+ teardown { DatabaseCleaner.clean }
22
+
23
+ context "when saved" do
24
+ should "trigger callbacks normally" do
25
+ assert_raise RuntimeError do
26
+ @android.update_attribute :name, "Junk"
27
+ end
28
+ end
29
+
30
+ should "not trigger callbacks when destroyed" do
31
+ assert_nothing_raised do
32
+ @android.destroy
33
+ end
34
+ end
35
+ end
36
+
37
+ context "when destroyed" do
38
+ context "as an instance" do
39
+ setup { @android.destroy }
40
+ should_change "Android.count", :by => -1
41
+
42
+ should "count the correct number of models" do
43
+ assert_equal 1, Android.count
44
+ assert_equal 2, Android.with_destroyed.count
45
+ end
46
+ end
47
+
48
+ context "without additional named scopes" do
49
+ setup { Android.destroy_all }
50
+ should_change "Android.count", :from => 2, :to => 0
51
+ should_change "Android.only_destroyed.count", :from => 0, :to => 2
52
+ end
53
+
54
+ context "with additional named scopes" do
55
+ setup { Android.active.destroy_all }
56
+ should_change "Android.count", :from => 2, :to => 1
57
+ should_change "Android.only_destroyed.count", :from => 0, :to => 1
58
+ should_change "Android.active.count", :from => 1, :to => 0
59
+ should_change "Android.active.only_destroyed.count", :from => 0, :to => 1
60
+ end
61
+ end
62
+
63
+ context "when deleted" do
64
+ context "without additional named scopes" do
65
+ setup { Android.delete_all }
66
+ should_change "Android.with_destroyed.count", :from => 2, :to => 0
67
+ should_not_change "Android.only_destroyed.count"
68
+ end
69
+
70
+ context "with additional named scopes" do
71
+ setup { Android.active.delete_all }
72
+ should_change "Android.with_destroyed.count", :from => 2, :to => 1
73
+ should_not_change "Android.only_destroyed.count"
74
+ should_change "Android.active.with_destroyed.count", :from => 1, :to => 0
75
+ should_not_change "Android.active.only_destroyed.count"
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ ActiveRecord::Schema.define(:version => 20090529222000) do
2
+ create_table :androids, :force => true do |table|
3
+ table.string :name
4
+ table.belongs_to :owner
5
+ table.boolean :active, :default => true
6
+ table.timestamps
7
+ table.datetime :deleted_at
8
+ end
9
+
10
+ create_table :people, :force => true do |table|
11
+ table.string :name
12
+ table.timestamps
13
+ end
14
+
15
+ create_table :components, :force => true do |table|
16
+ table.string :name
17
+ table.belongs_to :android
18
+ table.timestamps
19
+ table.datetime :deleted_at
20
+ end
21
+
22
+ create_table :sub_components, :force => true do |table|
23
+ table.string :name
24
+ table.belongs_to :component
25
+ table.datetime :deleted_at
26
+ end
27
+
28
+ create_table :memories, :force => true do |table|
29
+ table.string :name
30
+ table.belongs_to :parent
31
+ table.datetime :deleted_at
32
+ end
33
+
34
+ create_table :stickers, :force => true do |table|
35
+ table.string :name
36
+ table.belongs_to :android
37
+ table.datetime :deleted_at
38
+ end
39
+
40
+ create_table :ninjas, :force => true do |table|
41
+ table.string :name
42
+ table.boolean :visible, :default => false
43
+ end
44
+
45
+ create_table :pirates, :force => true do |table|
46
+ table.string :name
47
+ table.boolean :alive, :default => true
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ require "rubygems"
2
+ require "test/unit"
3
+
4
+ gem "sqlite3-ruby"
5
+
6
+ require "active_record"
7
+ require "active_support"
8
+ require "active_support/test_case"
9
+
10
+ gem "thoughtbot-shoulda", ">= 2.10.1"
11
+ gem "thoughtbot-factory_girl", ">= 1.2.1"
12
+
13
+ require "shoulda"
14
+ require "mocha"
15
+ require "factory_girl"
16
+
17
+ require File.join(File.dirname(__FILE__), "..", "rails", "init")
18
+
19
+ config = YAML::load(IO.read(File.dirname(__FILE__) + "/database.yml"))
20
+ ActiveRecord::Base.establish_connection(config["test"])
21
+
22
+ load(File.dirname(__FILE__) + "/schema.rb")
23
+
24
+ require File.join(File.dirname(__FILE__), "models")
25
+
26
+ require "database_cleaner"
27
+ DatabaseCleaner.strategy = :truncation
28
+ DatabaseCleaner.clean
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: joshuaclayton-paranoia
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Clayton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-30 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.0
27
+ version:
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ type: :development
31
+ version_requirement:
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 2.1.0
40
+ version:
41
+ description: Paranoid models
42
+ email: joshua.clayton@gmail.com
43
+ executables: []
44
+
45
+ extensions: []
46
+
47
+ extra_rdoc_files:
48
+ - lib/paranoia.rb
49
+ - README.textile
50
+ files:
51
+ - lib/paranoia.rb
52
+ - MIT-LICENSE
53
+ - rails/init.rb
54
+ - Rakefile
55
+ - README.textile
56
+ - test/database.yml
57
+ - test/factories.rb
58
+ - test/models.rb
59
+ - test/paranoia_test.rb
60
+ - test/schema.rb
61
+ - test/test_helper.rb
62
+ - Manifest
63
+ - paranoia.gemspec
64
+ has_rdoc: true
65
+ homepage: http://github.com/joshuaclayton/paranoia
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --line-numbers
69
+ - --inline-source
70
+ - --title
71
+ - Paranoia
72
+ - --main
73
+ - README.textile
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "1.2"
87
+ version:
88
+ requirements: []
89
+
90
+ rubyforge_project: paranoia
91
+ rubygems_version: 1.2.0
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Paranoid models
95
+ test_files:
96
+ - test/paranoia_test.rb
97
+ - test/test_helper.rb