mongoid-paranoia 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. data/.gitignore +25 -0
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +7 -0
  4. data/README.md +65 -0
  5. data/Rakefile +7 -0
  6. data/lib/mongoid/paranoia.rb +133 -0
  7. data/lib/mongoid/paranoia/version.rb +5 -0
  8. data/mongoid-paranoia.gemspec +18 -0
  9. data/spec/app/models/account.rb +32 -0
  10. data/spec/app/models/acolyte.rb +16 -0
  11. data/spec/app/models/actor.rb +19 -0
  12. data/spec/app/models/actor_observer.rb +15 -0
  13. data/spec/app/models/actress.rb +2 -0
  14. data/spec/app/models/address.rb +72 -0
  15. data/spec/app/models/address_component.rb +5 -0
  16. data/spec/app/models/address_number.rb +6 -0
  17. data/spec/app/models/agency.rb +5 -0
  18. data/spec/app/models/agent.rb +12 -0
  19. data/spec/app/models/album.rb +14 -0
  20. data/spec/app/models/alert.rb +5 -0
  21. data/spec/app/models/animal.rb +25 -0
  22. data/spec/app/models/answer.rb +4 -0
  23. data/spec/app/models/appointment.rb +7 -0
  24. data/spec/app/models/article.rb +13 -0
  25. data/spec/app/models/artist.rb +66 -0
  26. data/spec/app/models/artwork.rb +4 -0
  27. data/spec/app/models/augmentation.rb +11 -0
  28. data/spec/app/models/author.rb +6 -0
  29. data/spec/app/models/band.rb +25 -0
  30. data/spec/app/models/bar.rb +9 -0
  31. data/spec/app/models/basic.rb +6 -0
  32. data/spec/app/models/bed.rb +1 -0
  33. data/spec/app/models/big_palette.rb +2 -0
  34. data/spec/app/models/birthday.rb +13 -0
  35. data/spec/app/models/book.rb +12 -0
  36. data/spec/app/models/breed.rb +4 -0
  37. data/spec/app/models/browser.rb +6 -0
  38. data/spec/app/models/building.rb +7 -0
  39. data/spec/app/models/building_address.rb +7 -0
  40. data/spec/app/models/bus.rb +7 -0
  41. data/spec/app/models/business.rb +5 -0
  42. data/spec/app/models/callback_recorder.rb +25 -0
  43. data/spec/app/models/callback_test.rb +9 -0
  44. data/spec/app/models/canvas.rb +25 -0
  45. data/spec/app/models/car.rb +1 -0
  46. data/spec/app/models/cat.rb +8 -0
  47. data/spec/app/models/category.rb +8 -0
  48. data/spec/app/models/child.rb +4 -0
  49. data/spec/app/models/child_doc.rb +22 -0
  50. data/spec/app/models/church.rb +4 -0
  51. data/spec/app/models/circle.rb +3 -0
  52. data/spec/app/models/circuit.rb +4 -0
  53. data/spec/app/models/circus.rb +7 -0
  54. data/spec/app/models/code.rb +5 -0
  55. data/spec/app/models/comment.rb +16 -0
  56. data/spec/app/models/contractor.rb +7 -0
  57. data/spec/app/models/cookie.rb +6 -0
  58. data/spec/app/models/country_code.rb +8 -0
  59. data/spec/app/models/definition.rb +7 -0
  60. data/spec/app/models/description.rb +11 -0
  61. data/spec/app/models/dictionary.rb +10 -0
  62. data/spec/app/models/division.rb +10 -0
  63. data/spec/app/models/doctor.rb +12 -0
  64. data/spec/app/models/dog.rb +6 -0
  65. data/spec/app/models/dokument.rb +5 -0
  66. data/spec/app/models/driver.rb +7 -0
  67. data/spec/app/models/drug.rb +8 -0
  68. data/spec/app/models/email.rb +6 -0
  69. data/spec/app/models/employer.rb +5 -0
  70. data/spec/app/models/entry.rb +6 -0
  71. data/spec/app/models/eraser.rb +1 -0
  72. data/spec/app/models/event.rb +22 -0
  73. data/spec/app/models/exhibition.rb +4 -0
  74. data/spec/app/models/exhibitor.rb +5 -0
  75. data/spec/app/models/eye.rb +9 -0
  76. data/spec/app/models/eye_bowl.rb +9 -0
  77. data/spec/app/models/face.rb +8 -0
  78. data/spec/app/models/favorite.rb +6 -0
  79. data/spec/app/models/filesystem.rb +4 -0
  80. data/spec/app/models/firefox.rb +4 -0
  81. data/spec/app/models/fish.rb +8 -0
  82. data/spec/app/models/folder.rb +7 -0
  83. data/spec/app/models/folder_item.rb +9 -0
  84. data/spec/app/models/fruits.rb +28 -0
  85. data/spec/app/models/game.rb +21 -0
  86. data/spec/app/models/ghost.rb +7 -0
  87. data/spec/app/models/home.rb +4 -0
  88. data/spec/app/models/house.rb +8 -0
  89. data/spec/app/models/html_writer.rb +3 -0
  90. data/spec/app/models/image.rb +22 -0
  91. data/spec/app/models/implant.rb +16 -0
  92. data/spec/app/models/item.rb +12 -0
  93. data/spec/app/models/jar.rb +7 -0
  94. data/spec/app/models/label.rb +40 -0
  95. data/spec/app/models/language.rb +5 -0
  96. data/spec/app/models/lat_lng.rb +15 -0
  97. data/spec/app/models/league.rb +11 -0
  98. data/spec/app/models/learner.rb +2 -0
  99. data/spec/app/models/line_item.rb +6 -0
  100. data/spec/app/models/location.rb +8 -0
  101. data/spec/app/models/login.rb +8 -0
  102. data/spec/app/models/manufacturer.rb +7 -0
  103. data/spec/app/models/meat.rb +4 -0
  104. data/spec/app/models/membership.rb +4 -0
  105. data/spec/app/models/mixed_drink.rb +4 -0
  106. data/spec/app/models/movie.rb +12 -0
  107. data/spec/app/models/my_hash.rb +2 -0
  108. data/spec/app/models/name.rb +23 -0
  109. data/spec/app/models/node.rb +5 -0
  110. data/spec/app/models/note.rb +12 -0
  111. data/spec/app/models/ordered_post.rb +6 -0
  112. data/spec/app/models/ordered_preference.rb +6 -0
  113. data/spec/app/models/oscar.rb +15 -0
  114. data/spec/app/models/override.rb +16 -0
  115. data/spec/app/models/ownable.rb +6 -0
  116. data/spec/app/models/owner.rb +6 -0
  117. data/spec/app/models/page.rb +5 -0
  118. data/spec/app/models/page_question.rb +4 -0
  119. data/spec/app/models/palette.rb +7 -0
  120. data/spec/app/models/paranoid_phone.rb +25 -0
  121. data/spec/app/models/paranoid_post.rb +36 -0
  122. data/spec/app/models/parent.rb +5 -0
  123. data/spec/app/models/parent_doc.rb +6 -0
  124. data/spec/app/models/passport.rb +5 -0
  125. data/spec/app/models/patient.rb +9 -0
  126. data/spec/app/models/pdf_writer.rb +3 -0
  127. data/spec/app/models/pencil.rb +1 -0
  128. data/spec/app/models/person.rb +200 -0
  129. data/spec/app/models/pet.rb +23 -0
  130. data/spec/app/models/pet_owner.rb +6 -0
  131. data/spec/app/models/phone.rb +11 -0
  132. data/spec/app/models/phone_observer.rb +6 -0
  133. data/spec/app/models/pizza.rb +7 -0
  134. data/spec/app/models/player.rb +35 -0
  135. data/spec/app/models/post.rb +42 -0
  136. data/spec/app/models/powerup.rb +11 -0
  137. data/spec/app/models/preference.rb +9 -0
  138. data/spec/app/models/princess.rb +8 -0
  139. data/spec/app/models/product.rb +15 -0
  140. data/spec/app/models/profile.rb +5 -0
  141. data/spec/app/models/pronunciation.rb +5 -0
  142. data/spec/app/models/purchase.rb +4 -0
  143. data/spec/app/models/question.rb +8 -0
  144. data/spec/app/models/quiz.rb +10 -0
  145. data/spec/app/models/rating.rb +8 -0
  146. data/spec/app/models/record.rb +46 -0
  147. data/spec/app/models/registry.rb +4 -0
  148. data/spec/app/models/role.rb +5 -0
  149. data/spec/app/models/root_category.rb +4 -0
  150. data/spec/app/models/sandwich.rb +4 -0
  151. data/spec/app/models/scheduler.rb +7 -0
  152. data/spec/app/models/seo.rb +7 -0
  153. data/spec/app/models/series.rb +4 -0
  154. data/spec/app/models/server.rb +13 -0
  155. data/spec/app/models/service.rb +22 -0
  156. data/spec/app/models/shape.rb +12 -0
  157. data/spec/app/models/shelf.rb +5 -0
  158. data/spec/app/models/shipping_container.rb +5 -0
  159. data/spec/app/models/shop.rb +6 -0
  160. data/spec/app/models/short_agent.rb +4 -0
  161. data/spec/app/models/short_quiz.rb +5 -0
  162. data/spec/app/models/slave.rb +6 -0
  163. data/spec/app/models/song.rb +8 -0
  164. data/spec/app/models/square.rb +4 -0
  165. data/spec/app/models/strategy.rb +3 -0
  166. data/spec/app/models/sub_item.rb +3 -0
  167. data/spec/app/models/survey.rb +5 -0
  168. data/spec/app/models/symptom.rb +6 -0
  169. data/spec/app/models/tag.rb +8 -0
  170. data/spec/app/models/target.rb +5 -0
  171. data/spec/app/models/template.rb +5 -0
  172. data/spec/app/models/thing.rb +9 -0
  173. data/spec/app/models/title.rb +4 -0
  174. data/spec/app/models/tool.rb +8 -0
  175. data/spec/app/models/topping.rb +5 -0
  176. data/spec/app/models/track.rb +38 -0
  177. data/spec/app/models/translation.rb +5 -0
  178. data/spec/app/models/tree.rb +9 -0
  179. data/spec/app/models/truck.rb +3 -0
  180. data/spec/app/models/user.rb +20 -0
  181. data/spec/app/models/user_account.rb +10 -0
  182. data/spec/app/models/validation_callback.rb +10 -0
  183. data/spec/app/models/vehicle.rb +11 -0
  184. data/spec/app/models/version.rb +5 -0
  185. data/spec/app/models/vet_visit.rb +5 -0
  186. data/spec/app/models/video.rb +17 -0
  187. data/spec/app/models/weapon.rb +11 -0
  188. data/spec/app/models/wiki_page.rb +17 -0
  189. data/spec/app/models/word.rb +12 -0
  190. data/spec/app/models/word_origin.rb +11 -0
  191. data/spec/app/models/writer.rb +11 -0
  192. data/spec/config/mongoid.yml +40 -0
  193. data/spec/mongoid/paranoia_spec.rb +715 -0
  194. data/spec/spec_helper.rb +80 -0
  195. metadata +271 -0
@@ -0,0 +1,8 @@
1
+ class Song
2
+ include Mongoid::Document
3
+ field :title
4
+ embedded_in :artist
5
+
6
+ attr_accessor :before_add_called
7
+
8
+ end
@@ -0,0 +1,4 @@
1
+ class Square < Shape
2
+ field :width, type: Integer, default: 0
3
+ field :height, type: Integer, default: 0
4
+ end
@@ -0,0 +1,3 @@
1
+ class Strategy
2
+ include Mongoid::Document
3
+ end
@@ -0,0 +1,3 @@
1
+ class SubItem < Item
2
+ embedded_in :parent
3
+ end
@@ -0,0 +1,5 @@
1
+ class Survey
2
+ include Mongoid::Document
3
+ embeds_many :questions
4
+ accepts_nested_attributes_for :questions, reject_if: ->(a){ a[:content].blank? }, allow_destroy: true
5
+ end
@@ -0,0 +1,6 @@
1
+ class Symptom
2
+ include Mongoid::Document
3
+ field :name, type: String
4
+ embedded_in :person
5
+ default_scope asc(:name)
6
+ end
@@ -0,0 +1,8 @@
1
+ class Tag
2
+ include Mongoid::Document
3
+ field :text, type: String
4
+ has_and_belongs_to_many :actors
5
+ has_and_belongs_to_many :articles
6
+ has_and_belongs_to_many :posts
7
+ has_and_belongs_to_many :related, class_name: "Tag", inverse_of: :related
8
+ end
@@ -0,0 +1,5 @@
1
+ class Target
2
+ include Mongoid::Document
3
+ field :name, type: String
4
+ embedded_in :targetable, polymorphic: true
5
+ end
@@ -0,0 +1,5 @@
1
+ class Template
2
+ include Mongoid::Document
3
+ field :active, type: Boolean, default: false
4
+ validates :active, presence: true
5
+ end
@@ -0,0 +1,9 @@
1
+ class Thing
2
+ include Mongoid::Document
3
+ before_destroy :dont_do_it
4
+ embedded_in :actor
5
+
6
+ def dont_do_it
7
+ false
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ class Title
2
+ include Mongoid::Document
3
+ belongs_to :paranoid_post
4
+ end
@@ -0,0 +1,8 @@
1
+ class Tool
2
+ include Mongoid::Document
3
+ embedded_in :palette
4
+ accepts_nested_attributes_for :palette
5
+ end
6
+
7
+ require "app/models/eraser"
8
+ require "app/models/pencil"
@@ -0,0 +1,5 @@
1
+ class Topping
2
+ include Mongoid::Document
3
+ field :name, type: String
4
+ belongs_to :pizza
5
+ end
@@ -0,0 +1,38 @@
1
+ class Track
2
+ include Mongoid::Document
3
+ field :name, type: String
4
+
5
+ field :before_create_called, type: Boolean, default: false
6
+ field :before_save_called, type: Boolean, default: false
7
+ field :before_update_called, type: Boolean, default: false
8
+ field :before_validation_called, type: Boolean, default: false
9
+ field :before_destroy_called, type: Boolean, default: false
10
+
11
+ embedded_in :record
12
+
13
+ before_create :before_create_stub
14
+ before_save :before_save_stub
15
+ before_update :before_update_stub
16
+ before_validation :before_validation_stub
17
+ before_destroy :before_destroy_stub
18
+
19
+ def before_create_stub
20
+ self.before_create_called = true
21
+ end
22
+
23
+ def before_save_stub
24
+ self.before_save_called = true
25
+ end
26
+
27
+ def before_update_stub
28
+ self.before_update_called = true
29
+ end
30
+
31
+ def before_validation_stub
32
+ self.before_validation_called = true
33
+ end
34
+
35
+ def before_destroy_stub
36
+ self.before_destroy_called = true
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ class Translation
2
+ include Mongoid::Document
3
+ field :language
4
+ embedded_in :name
5
+ end
@@ -0,0 +1,9 @@
1
+ class Tree
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+ field :evergreen, type: Boolean
6
+
7
+ scope :verdant, where(evergreen: true)
8
+ default_scope asc(:name)
9
+ end
@@ -0,0 +1,3 @@
1
+ class Truck < Vehicle
2
+ embeds_one :bed
3
+ end
@@ -0,0 +1,20 @@
1
+ class User
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+ field :last_login, type: DateTime
6
+ field :account_expires, type: Date
7
+
8
+ has_one :account, foreign_key: :creator_id, validate: false
9
+ has_many :posts, foreign_key: :author_id, validate: false
10
+ has_many :descriptions
11
+
12
+ has_and_belongs_to_many :followed_shops, inverse_of: :followers, class_name: "Shop"
13
+ has_and_belongs_to_many :businesses, class_name: "Business", validate: false
14
+ has_one :shop
15
+
16
+ belongs_to :next, class_name: "User"
17
+
18
+ accepts_nested_attributes_for :posts
19
+ index name: 1
20
+ end
@@ -0,0 +1,10 @@
1
+ class UserAccount
2
+ include Mongoid::Document
3
+ field :username, type: String
4
+ field :name, type: String
5
+ field :email, type: String
6
+ validates_uniqueness_of :username, message: "is not unique"
7
+ validates_uniqueness_of :email, message: "is not unique", case_sensitive: false
8
+ validates_length_of :name, minimum: 2, allow_nil: true
9
+ has_and_belongs_to_many :people
10
+ end
@@ -0,0 +1,10 @@
1
+ class ValidationCallback
2
+ include Mongoid::Document
3
+ field :history, type: Array, default: []
4
+ validate do
5
+ self.history << :validate
6
+ end
7
+
8
+ before_validation { self.history << :before_validation }
9
+ after_validation { self.history << :after_validation }
10
+ end
@@ -0,0 +1,11 @@
1
+ class Vehicle
2
+ include Mongoid::Document
3
+ belongs_to :shipping_container
4
+ belongs_to :driver
5
+
6
+ accepts_nested_attributes_for :driver
7
+ accepts_nested_attributes_for :shipping_container
8
+ end
9
+
10
+ require "app/models/car"
11
+ require "app/models/truck"
@@ -0,0 +1,5 @@
1
+ class Version
2
+ include Mongoid::Document
3
+ field :number, type: Integer
4
+ embedded_in :memorable, polymorphic: true
5
+ end
@@ -0,0 +1,5 @@
1
+ class VetVisit
2
+ include Mongoid::Document
3
+ field :date, type: Date
4
+ embedded_in :pet
5
+ end
@@ -0,0 +1,17 @@
1
+ class Video
2
+ include Mongoid::Document
3
+ field :title, type: String
4
+ field :year, type: Integer
5
+ field :release_dates, type: Set
6
+ field :genres, type: Array
7
+
8
+ embedded_in :person
9
+ belongs_to :post
10
+ belongs_to :game
11
+
12
+ default_scope asc(:title)
13
+
14
+ attr_accessible :title, as: [ :default, :admin ]
15
+ attr_accessible :year, as: [ :default ]
16
+ attr_accessible :person_attributes, as: [ :default ]
17
+ end
@@ -0,0 +1,11 @@
1
+ class Weapon
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ belongs_to :player, inverse_of: :weapons
7
+
8
+ after_build do
9
+ self.name = "Holy Hand Grenade (#{player.frags})"
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ class WikiPage
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Mongoid::Versioning
5
+
6
+ field :title, type: String
7
+ field :transient_property, type: String, versioned: false
8
+ field :author, type: String
9
+ field :description, type: String, localize: true
10
+ max_versions 5
11
+
12
+ attr_protected :author
13
+
14
+ has_many :comments, dependent: :destroy, validate: false
15
+ has_many :child_pages, class_name: "WikiPage", dependent: :delete, inverse_of: :parent_pages
16
+ belongs_to :parent_pages, class_name: "WikiPage", inverse_of: :child_pages
17
+ end
@@ -0,0 +1,12 @@
1
+ class Word
2
+ include Mongoid::Document
3
+ field :name, type: String
4
+
5
+ belongs_to :dictionary
6
+
7
+ embeds_many :definitions, validate: false
8
+ embeds_many :word_origins, validate: false
9
+ embeds_one :pronunciation, validate: false
10
+
11
+ accepts_nested_attributes_for :definitions, allow_destroy: true
12
+ end
@@ -0,0 +1,11 @@
1
+ class WordOrigin
2
+ include Mongoid::Document
3
+
4
+ field :_id, type: Integer, default: ->{ origin_id }
5
+
6
+ field :origin_id, type: Integer
7
+ field :country, type: String
8
+ field :city, type: String
9
+
10
+ embedded_in :word
11
+ end
@@ -0,0 +1,11 @@
1
+ class Writer
2
+ include Mongoid::Document
3
+ field :speed, type: Integer, default: 0
4
+
5
+ embedded_in :canvas
6
+
7
+ def write; end
8
+ end
9
+
10
+ require "app/models/pdf_writer"
11
+ require "app/models/html_writer"
@@ -0,0 +1,40 @@
1
+ test:
2
+ sessions:
3
+ default:
4
+ database: mongoid_test
5
+ hosts:
6
+ - <%=ENV["MONGOID_SPEC_HOST"]%>:<%=ENV["MONGOID_SPEC_PORT"]%>
7
+ options:
8
+ consistency: :strong
9
+ mongohq_single:
10
+ database: <%=ENV["MONGOHQ_SINGLE_NAME"]%>
11
+ username: <%=ENV["MONGOHQ_SINGLE_USER"]%>
12
+ password: <%=ENV["MONGOHQ_SINGLE_PASS"]%>
13
+ hosts:
14
+ - <%=ENV["MONGOHQ_SINGLE_URL"]%>
15
+ options:
16
+ safe: true
17
+ consistency: :strong
18
+ mongohq_repl:
19
+ database: <%=ENV["MONGOHQ_REPL_NAME"]%>
20
+ username: <%=ENV["MONGOHQ_REPL_USER"]%>
21
+ password: <%=ENV["MONGOHQ_REPL_PASS"]%>
22
+ hosts:
23
+ - <%=ENV["MONGOHQ_REPL_1_URL"]%>
24
+ - <%=ENV["MONGOHQ_REPL_2_URL"]%>
25
+ options:
26
+ consistency: :strong
27
+ safe: true
28
+ mongohq_repl_uri:
29
+ uri: <%= ENV["MONGOHQ_REPL_URI"]%>
30
+ options:
31
+ allow_dynamic_fields: true
32
+ identity_map_enabled: false
33
+ include_root_in_json: false
34
+ include_type_for_serialization: false
35
+ preload_models: false
36
+ scope_overwrite_exception: false
37
+ raise_not_found_error: true
38
+ skip_version_check: true
39
+ use_activesupport_time_zone: true
40
+ use_utc: false
@@ -0,0 +1,715 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Paranoia do
4
+
5
+ describe ".scoped" do
6
+
7
+ it "returns a scoped criteria" do
8
+ ParanoidPost.scoped.selector.should eq({ "deleted_at" => nil })
9
+ end
10
+ end
11
+
12
+ describe ".deleted" do
13
+
14
+ context "when called on a root document" do
15
+
16
+ let(:post) do
17
+ ParanoidPost.create(title: "testing")
18
+ end
19
+
20
+ before do
21
+ post.destroy
22
+ end
23
+
24
+ let(:deleted) do
25
+ ParanoidPost.deleted
26
+ end
27
+
28
+ it "returns the deleted documents" do
29
+ deleted.should eq([ post ])
30
+ end
31
+ end
32
+
33
+ context "when called on an embedded document" do
34
+
35
+ let(:person) do
36
+ Person.create
37
+ end
38
+
39
+ let(:phone) do
40
+ person.paranoid_phones.create
41
+ end
42
+
43
+ before do
44
+ phone.destroy
45
+ person.reload
46
+ end
47
+
48
+ it "returns the deleted documents" do
49
+ person.paranoid_phones.deleted.should eq([ phone ])
50
+ end
51
+
52
+ it "returns the correct count" do
53
+ person.paranoid_phones.deleted.count.should eq(1)
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "#destroy!" do
59
+
60
+ context "when the document is a root" do
61
+
62
+ let(:post) do
63
+ ParanoidPost.create(title: "testing")
64
+ end
65
+
66
+ before do
67
+ post.destroy!
68
+ end
69
+
70
+ let(:raw) do
71
+ ParanoidPost.collection.find(_id: post.id).first
72
+ end
73
+
74
+ it "hard deletes the document" do
75
+ raw.should be_nil
76
+ end
77
+
78
+ it "executes the before destroy callbacks" do
79
+ post.before_destroy_called.should be_true
80
+ end
81
+
82
+ it "executes the after destroy callbacks" do
83
+ post.after_destroy_called.should be_true
84
+ end
85
+ end
86
+
87
+ context "when the document is embedded" do
88
+
89
+ let(:person) do
90
+ Person.create
91
+ end
92
+
93
+ let(:phone) do
94
+ person.paranoid_phones.create(number: "911")
95
+ end
96
+
97
+ before do
98
+ phone.destroy!
99
+ end
100
+
101
+ let(:raw) do
102
+ Person.collection.find(_id: person.id).first
103
+ end
104
+
105
+ it "hard deletes the document" do
106
+ raw["paranoid_phones"].should be_empty
107
+ end
108
+
109
+ it "executes the before destroy callbacks" do
110
+ phone.before_destroy_called.should be_true
111
+ end
112
+
113
+ it "executes the after destroy callbacks" do
114
+ phone.after_destroy_called.should be_true
115
+ end
116
+ end
117
+
118
+ context "when the document has a dependent relation" do
119
+
120
+ let(:post) do
121
+ ParanoidPost.create(title: "test")
122
+ end
123
+
124
+ let!(:author) do
125
+ post.authors.create(name: "poe")
126
+ end
127
+
128
+ before do
129
+ post.destroy!
130
+ end
131
+
132
+ it "cascades the dependent option" do
133
+ expect {
134
+ author.reload
135
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe "#destroy" do
141
+
142
+ context "when the document is a root" do
143
+
144
+ let(:post) do
145
+ ParanoidPost.create(title: "testing")
146
+ end
147
+
148
+ before do
149
+ post.destroy
150
+ end
151
+
152
+ let(:raw) do
153
+ ParanoidPost.collection.find(_id: post.id).first
154
+ end
155
+
156
+ it "soft deletes the document" do
157
+ raw["deleted_at"].should be_within(1).of(Time.now)
158
+ end
159
+
160
+ it "does not return the document in a find" do
161
+ expect {
162
+ ParanoidPost.find(post.id)
163
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
164
+ end
165
+
166
+ it "executes the before destroy callbacks" do
167
+ post.before_destroy_called.should be_true
168
+ end
169
+
170
+ it "executes the after destroy callbacks" do
171
+ post.after_destroy_called.should be_true
172
+ end
173
+ end
174
+
175
+ context "when the document is embedded" do
176
+
177
+ let(:person) do
178
+ Person.create
179
+ end
180
+
181
+ let(:phone) do
182
+ person.paranoid_phones.create(number: "911")
183
+ end
184
+
185
+ before do
186
+ phone.destroy
187
+ end
188
+
189
+ let(:raw) do
190
+ Person.collection.find(_id: person.id).first
191
+ end
192
+
193
+ it "soft deletes the document" do
194
+ raw["paranoid_phones"].first["deleted_at"].should be_within(1).of(Time.now)
195
+ end
196
+
197
+ it "does not return the document in a find" do
198
+ expect {
199
+ person.paranoid_phones.find(phone.id)
200
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
201
+ end
202
+
203
+ it "does not include the document in the relation" do
204
+ person.paranoid_phones.scoped.should be_empty
205
+ end
206
+
207
+ it "executes the before destroy callbacks" do
208
+ phone.before_destroy_called.should be_true
209
+ end
210
+
211
+ it "executes the after destroy callbacks" do
212
+ phone.after_destroy_called.should be_true
213
+ end
214
+ end
215
+
216
+ context "when the document has a dependent: :delete relation" do
217
+
218
+ let(:post) do
219
+ ParanoidPost.create(title: "test")
220
+ end
221
+
222
+ let!(:author) do
223
+ post.authors.create(name: "poe")
224
+ end
225
+
226
+ before do
227
+ post.destroy
228
+ end
229
+
230
+ it "cascades the dependent option" do
231
+ expect {
232
+ author.reload
233
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
234
+ end
235
+ end
236
+
237
+ context "when the document has a dependent: :restrict relation" do
238
+
239
+ let(:post) do
240
+ ParanoidPost.create(title: "test")
241
+ end
242
+
243
+ let!(:title) do
244
+ post.titles.create
245
+ end
246
+
247
+ before do
248
+ begin
249
+ post.destroy
250
+ rescue Mongoid::Errors::DeleteRestriction
251
+ end
252
+ end
253
+
254
+ it "does not destroy the document" do
255
+ post.should_not be_destroyed
256
+ end
257
+ end
258
+ end
259
+
260
+ describe "#destroyed?" do
261
+
262
+ context "when the document is a root" do
263
+
264
+ let(:post) do
265
+ ParanoidPost.create(title: "testing")
266
+ end
267
+
268
+ context "when the document is hard deleted" do
269
+
270
+ before do
271
+ post.destroy!
272
+ end
273
+
274
+ it "returns true" do
275
+ post.should be_destroyed
276
+ end
277
+ end
278
+
279
+ context "when the document is soft deleted" do
280
+
281
+ before do
282
+ post.destroy
283
+ end
284
+
285
+ it "returns true" do
286
+ post.should be_destroyed
287
+ end
288
+ end
289
+ end
290
+
291
+ context "when the document is embedded" do
292
+
293
+ let(:person) do
294
+ Person.create
295
+ end
296
+
297
+ let(:phone) do
298
+ person.paranoid_phones.create(number: "911")
299
+ end
300
+
301
+ context "when the document is hard deleted" do
302
+
303
+ before do
304
+ phone.destroy!
305
+ end
306
+
307
+ it "returns true" do
308
+ phone.should be_destroyed
309
+ end
310
+ end
311
+
312
+ context "when the document is soft deleted" do
313
+
314
+ before do
315
+ phone.destroy
316
+ end
317
+
318
+ it "returns true" do
319
+ phone.should be_destroyed
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ describe "#deleted?" do
326
+
327
+ context "when the document is a root" do
328
+
329
+ let(:post) do
330
+ ParanoidPost.create(title: "testing")
331
+ end
332
+
333
+ context "when the document is hard deleted" do
334
+
335
+ before do
336
+ post.destroy!
337
+ end
338
+
339
+ it "returns true" do
340
+ post.should be_deleted
341
+ end
342
+ end
343
+
344
+ context "when the document is soft deleted" do
345
+
346
+ before do
347
+ post.destroy
348
+ end
349
+
350
+ it "returns true" do
351
+ post.should be_deleted
352
+ end
353
+ end
354
+ end
355
+
356
+ context "when the document is embedded" do
357
+
358
+ let(:person) do
359
+ Person.create
360
+ end
361
+
362
+ let(:phone) do
363
+ person.paranoid_phones.create(number: "911")
364
+ end
365
+
366
+ context "when the document is hard deleted" do
367
+
368
+ before do
369
+ phone.destroy!
370
+ end
371
+
372
+ it "returns true" do
373
+ phone.should be_deleted
374
+ end
375
+ end
376
+
377
+ context "when the document is soft deleted" do
378
+
379
+ before do
380
+ phone.destroy
381
+ end
382
+
383
+ it "returns true" do
384
+ phone.should be_deleted
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ describe "#delete!" do
391
+
392
+ context "when the document is a root" do
393
+
394
+ let(:post) do
395
+ ParanoidPost.create(title: "testing")
396
+ end
397
+
398
+ before do
399
+ post.delete!
400
+ end
401
+
402
+ let(:raw) do
403
+ ParanoidPost.collection.find(_id: post.id).first
404
+ end
405
+
406
+ it "hard deletes the document" do
407
+ raw.should be_nil
408
+ end
409
+ end
410
+
411
+ context "when the document is embedded" do
412
+
413
+ let(:person) do
414
+ Person.create
415
+ end
416
+
417
+ let(:phone) do
418
+ person.paranoid_phones.create(number: "911")
419
+ end
420
+
421
+ before do
422
+ phone.delete!
423
+ end
424
+
425
+ let(:raw) do
426
+ Person.collection.find(_id: person.id).first
427
+ end
428
+
429
+ it "hard deletes the document" do
430
+ raw["paranoid_phones"].should be_empty
431
+ end
432
+ end
433
+
434
+ context "when the document has a dependent relation" do
435
+
436
+ let(:post) do
437
+ ParanoidPost.create(title: "test")
438
+ end
439
+
440
+ let!(:author) do
441
+ post.authors.create(name: "poe")
442
+ end
443
+
444
+ before do
445
+ post.delete!
446
+ end
447
+
448
+ it "cascades the dependent option" do
449
+ expect {
450
+ author.reload
451
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
452
+ end
453
+ end
454
+ end
455
+
456
+ describe "#delete" do
457
+
458
+ context "when the document is a root" do
459
+
460
+ let(:post) do
461
+ ParanoidPost.create(title: "testing")
462
+ end
463
+
464
+ before do
465
+ post.delete
466
+ end
467
+
468
+ let(:raw) do
469
+ ParanoidPost.collection.find(_id: post.id).first
470
+ end
471
+
472
+ it "soft deletes the document" do
473
+ raw["deleted_at"].should be_within(1).of(Time.now)
474
+ end
475
+
476
+ it "does not return the document in a find" do
477
+ expect {
478
+ ParanoidPost.find(post.id)
479
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
480
+ end
481
+
482
+ it "clears out the persistence options" do
483
+ Mongoid::Threaded.persistence_options(ParanoidPost).should be_nil
484
+ end
485
+
486
+ it "clears out the identity map" do
487
+ Mongoid::IdentityMap.should be_empty
488
+ end
489
+ end
490
+
491
+ context "when the document is embedded" do
492
+
493
+ let(:person) do
494
+ Person.create
495
+ end
496
+
497
+ let(:phone) do
498
+ person.paranoid_phones.create(number: "911")
499
+ end
500
+
501
+ before do
502
+ phone.delete
503
+ end
504
+
505
+ let(:raw) do
506
+ Person.collection.find(_id: person.id).first
507
+ end
508
+
509
+ it "soft deletes the document" do
510
+ raw["paranoid_phones"].first["deleted_at"].should be_within(1).of(Time.now)
511
+ end
512
+
513
+ it "does not return the document in a find" do
514
+ expect {
515
+ person.paranoid_phones.find(phone.id)
516
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
517
+ end
518
+
519
+ it "does not include the document in the relation" do
520
+ person.paranoid_phones.scoped.should be_empty
521
+ end
522
+ end
523
+
524
+ context "when the document has a dependent relation" do
525
+
526
+ let(:post) do
527
+ ParanoidPost.create(title: "test")
528
+ end
529
+
530
+ let!(:author) do
531
+ post.authors.create(name: "poe")
532
+ end
533
+
534
+ before do
535
+ post.delete
536
+ end
537
+
538
+ it "cascades the dependent option" do
539
+ expect {
540
+ author.reload
541
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
542
+ end
543
+ end
544
+
545
+ context "when the document has a dependent: :restrict relation" do
546
+
547
+ let(:post) do
548
+ ParanoidPost.create(title: "test")
549
+ end
550
+
551
+ let!(:title) do
552
+ post.titles.create
553
+ end
554
+
555
+ before do
556
+ begin
557
+ post.delete
558
+ rescue Mongoid::Errors::DeleteRestriction
559
+ end
560
+ end
561
+
562
+ it "does not destroy the document" do
563
+ post.should_not be_destroyed
564
+ end
565
+ end
566
+ end
567
+
568
+ describe "#remove" do
569
+
570
+ let(:post) do
571
+ ParanoidPost.new
572
+ end
573
+
574
+ let!(:time) do
575
+ Time.now
576
+ end
577
+
578
+ before do
579
+ post.remove
580
+ end
581
+
582
+ it "sets the deleted flag" do
583
+ post.should be_destroyed
584
+ end
585
+ end
586
+
587
+ describe "#restore" do
588
+
589
+ context "when the document is a root" do
590
+
591
+ let(:post) do
592
+ ParanoidPost.create(title: "testing")
593
+ end
594
+
595
+ before do
596
+ post.delete
597
+ post.restore
598
+ end
599
+
600
+ it "removes the deleted at time" do
601
+ post.deleted_at.should be_nil
602
+ end
603
+
604
+ it "persists the change" do
605
+ post.reload.deleted_at.should be_nil
606
+ end
607
+
608
+ it "marks document again as persisted" do
609
+ post.persisted?.should be_true
610
+ end
611
+ end
612
+
613
+ context "when the document is embedded" do
614
+
615
+ let(:person) do
616
+ Person.create
617
+ end
618
+
619
+ let(:phone) do
620
+ person.paranoid_phones.create(number: "911")
621
+ end
622
+
623
+ before do
624
+ phone.delete
625
+ phone.restore
626
+ end
627
+
628
+ it "removes the deleted at time" do
629
+ phone.deleted_at.should be_nil
630
+ end
631
+
632
+ it "persists the change" do
633
+ person.reload.paranoid_phones.first.deleted_at.should be_nil
634
+ end
635
+ end
636
+ end
637
+
638
+ describe ".scoped" do
639
+
640
+ let(:scoped) do
641
+ ParanoidPost.scoped
642
+ end
643
+
644
+ it "returns a scoped criteria" do
645
+ scoped.selector.should eq({ "deleted_at" => nil })
646
+ end
647
+ end
648
+
649
+ describe "#set" do
650
+
651
+ let!(:post) do
652
+ ParanoidPost.create
653
+ end
654
+
655
+ let(:time) do
656
+ 20.days.ago
657
+ end
658
+
659
+ before do
660
+ post.set(:deleted_at, time)
661
+ end
662
+
663
+ it "persists the change" do
664
+ post.reload.deleted_at.should be_within(1).of(time)
665
+ end
666
+ end
667
+
668
+ describe ".unscoped" do
669
+
670
+ let(:unscoped) do
671
+ ParanoidPost.unscoped
672
+ end
673
+
674
+ it "returns an unscoped criteria" do
675
+ unscoped.selector.should eq({})
676
+ end
677
+ end
678
+
679
+ describe "#to_param" do
680
+
681
+ let(:post) do
682
+ ParanoidPost.new(title: "testing")
683
+ end
684
+
685
+ context "when the document is new" do
686
+
687
+ it "still returns nil" do
688
+ post.to_param.should be_nil
689
+ end
690
+ end
691
+
692
+ context "when the document is not deleted" do
693
+
694
+ before do
695
+ post.save
696
+ end
697
+
698
+ it "returns the id as a string" do
699
+ post.to_param.should eq(post.id.to_s)
700
+ end
701
+ end
702
+
703
+ context "when the document is deleted" do
704
+
705
+ before do
706
+ post.save
707
+ post.delete
708
+ end
709
+
710
+ it "returns the id as a string" do
711
+ post.to_param.should eq(post.id.to_s)
712
+ end
713
+ end
714
+ end
715
+ end