mongoid-paranoia 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.
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