lookout-mongo_mapper 0.11.3

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 (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +33 -0
  3. data/UPGRADES +26 -0
  4. data/bin/mmconsole +59 -0
  5. data/examples/attr_accessible.rb +22 -0
  6. data/examples/attr_protected.rb +22 -0
  7. data/examples/cache_key.rb +24 -0
  8. data/examples/custom_types.rb +24 -0
  9. data/examples/identity_map.rb +33 -0
  10. data/examples/identity_map/automatic.rb +2 -0
  11. data/examples/keys.rb +40 -0
  12. data/examples/modifiers/set.rb +25 -0
  13. data/examples/plugins.rb +38 -0
  14. data/examples/querying.rb +35 -0
  15. data/examples/safe.rb +43 -0
  16. data/examples/scopes.rb +52 -0
  17. data/examples/validating/embedded_docs.rb +29 -0
  18. data/lib/mongo_mapper.rb +94 -0
  19. data/lib/mongo_mapper/connection.rb +96 -0
  20. data/lib/mongo_mapper/document.rb +42 -0
  21. data/lib/mongo_mapper/embedded_document.rb +32 -0
  22. data/lib/mongo_mapper/exceptions.rb +30 -0
  23. data/lib/mongo_mapper/extensions/array.rb +19 -0
  24. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  25. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  26. data/lib/mongo_mapper/extensions/date.rb +25 -0
  27. data/lib/mongo_mapper/extensions/float.rb +14 -0
  28. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  29. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  30. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  31. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  32. data/lib/mongo_mapper/extensions/object.rb +26 -0
  33. data/lib/mongo_mapper/extensions/object_id.rb +32 -0
  34. data/lib/mongo_mapper/extensions/set.rb +20 -0
  35. data/lib/mongo_mapper/extensions/string.rb +18 -0
  36. data/lib/mongo_mapper/extensions/time.rb +28 -0
  37. data/lib/mongo_mapper/locale/en.yml +5 -0
  38. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  39. data/lib/mongo_mapper/plugins.rb +22 -0
  40. data/lib/mongo_mapper/plugins/accessible.rb +52 -0
  41. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  42. data/lib/mongo_mapper/plugins/associations.rb +90 -0
  43. data/lib/mongo_mapper/plugins/associations/base.rb +92 -0
  44. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +54 -0
  45. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +34 -0
  46. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  47. data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
  48. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +44 -0
  49. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +133 -0
  50. data/lib/mongo_mapper/plugins/associations/many_association.rb +63 -0
  51. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  52. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +118 -0
  53. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  54. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  55. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  56. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
  57. data/lib/mongo_mapper/plugins/associations/one_association.rb +48 -0
  58. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  59. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  60. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +95 -0
  61. data/lib/mongo_mapper/plugins/associations/proxy.rb +134 -0
  62. data/lib/mongo_mapper/plugins/associations/single_association.rb +46 -0
  63. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  64. data/lib/mongo_mapper/plugins/callbacks.rb +29 -0
  65. data/lib/mongo_mapper/plugins/clone.rb +22 -0
  66. data/lib/mongo_mapper/plugins/dirty.rb +60 -0
  67. data/lib/mongo_mapper/plugins/document.rb +41 -0
  68. data/lib/mongo_mapper/plugins/dynamic_querying.rb +45 -0
  69. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  70. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +56 -0
  71. data/lib/mongo_mapper/plugins/embedded_document.rb +53 -0
  72. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  73. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  74. data/lib/mongo_mapper/plugins/indexes.rb +13 -0
  75. data/lib/mongo_mapper/plugins/inspect.rb +16 -0
  76. data/lib/mongo_mapper/plugins/keys.rb +313 -0
  77. data/lib/mongo_mapper/plugins/keys/key.rb +61 -0
  78. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  79. data/lib/mongo_mapper/plugins/modifiers.rb +134 -0
  80. data/lib/mongo_mapper/plugins/pagination.rb +16 -0
  81. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  82. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  83. data/lib/mongo_mapper/plugins/querying.rb +165 -0
  84. data/lib/mongo_mapper/plugins/querying/decorator.rb +36 -0
  85. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  86. data/lib/mongo_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  87. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  88. data/lib/mongo_mapper/plugins/sci.rb +36 -0
  89. data/lib/mongo_mapper/plugins/scopes.rb +27 -0
  90. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  91. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  92. data/lib/mongo_mapper/plugins/touch.rb +18 -0
  93. data/lib/mongo_mapper/plugins/userstamps.rb +18 -0
  94. data/lib/mongo_mapper/plugins/validations.rb +86 -0
  95. data/lib/mongo_mapper/railtie.rb +48 -0
  96. data/lib/mongo_mapper/railtie/database.rake +65 -0
  97. data/lib/mongo_mapper/translation.rb +10 -0
  98. data/lib/mongo_mapper/version.rb +4 -0
  99. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  100. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  101. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  102. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +13 -0
  103. data/test/_NOTE_ON_TESTING +1 -0
  104. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +64 -0
  105. data/test/functional/associations/test_belongs_to_proxy.rb +238 -0
  106. data/test/functional/associations/test_in_array_proxy.rb +349 -0
  107. data/test/functional/associations/test_many_documents_as_proxy.rb +231 -0
  108. data/test/functional/associations/test_many_documents_proxy.rb +866 -0
  109. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +239 -0
  110. data/test/functional/associations/test_many_embedded_proxy.rb +289 -0
  111. data/test/functional/associations/test_many_polymorphic_proxy.rb +303 -0
  112. data/test/functional/associations/test_one_as_proxy.rb +491 -0
  113. data/test/functional/associations/test_one_embedded_polymorphic_proxy.rb +208 -0
  114. data/test/functional/associations/test_one_embedded_proxy.rb +100 -0
  115. data/test/functional/associations/test_one_proxy.rb +383 -0
  116. data/test/functional/test_accessible.rb +198 -0
  117. data/test/functional/test_associations.rb +46 -0
  118. data/test/functional/test_binary.rb +27 -0
  119. data/test/functional/test_caching.rb +77 -0
  120. data/test/functional/test_callbacks.rb +232 -0
  121. data/test/functional/test_dirty.rb +301 -0
  122. data/test/functional/test_document.rb +282 -0
  123. data/test/functional/test_dynamic_querying.rb +75 -0
  124. data/test/functional/test_embedded_document.rb +288 -0
  125. data/test/functional/test_equality.rb +20 -0
  126. data/test/functional/test_identity_map.rb +513 -0
  127. data/test/functional/test_indexes.rb +50 -0
  128. data/test/functional/test_logger.rb +20 -0
  129. data/test/functional/test_modifiers.rb +537 -0
  130. data/test/functional/test_pagination.rb +91 -0
  131. data/test/functional/test_protected.rb +201 -0
  132. data/test/functional/test_querying.rb +935 -0
  133. data/test/functional/test_safe.rb +76 -0
  134. data/test/functional/test_sci.rb +240 -0
  135. data/test/functional/test_scopes.rb +171 -0
  136. data/test/functional/test_timestamps.rb +62 -0
  137. data/test/functional/test_touch.rb +125 -0
  138. data/test/functional/test_userstamps.rb +44 -0
  139. data/test/functional/test_validations.rb +414 -0
  140. data/test/models.rb +261 -0
  141. data/test/support/railtie.rb +4 -0
  142. data/test/support/railtie/autoloaded.rb +2 -0
  143. data/test/support/railtie/not_autoloaded.rb +3 -0
  144. data/test/support/railtie/parent.rb +3 -0
  145. data/test/test_active_model_lint.rb +18 -0
  146. data/test/test_helper.rb +93 -0
  147. data/test/unit/associations/test_base.rb +146 -0
  148. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  149. data/test/unit/associations/test_many_association.rb +63 -0
  150. data/test/unit/associations/test_one_association.rb +47 -0
  151. data/test/unit/associations/test_proxy.rb +100 -0
  152. data/test/unit/serializers/test_json_serializer.rb +216 -0
  153. data/test/unit/serializers/test_xml_serializer.rb +196 -0
  154. data/test/unit/test_clone.rb +69 -0
  155. data/test/unit/test_document.rb +249 -0
  156. data/test/unit/test_dynamic_finder.rb +125 -0
  157. data/test/unit/test_embedded_document.rb +682 -0
  158. data/test/unit/test_equality.rb +38 -0
  159. data/test/unit/test_exceptions.rb +12 -0
  160. data/test/unit/test_extensions.rb +380 -0
  161. data/test/unit/test_identity_map_middleware.rb +34 -0
  162. data/test/unit/test_inspect.rb +47 -0
  163. data/test/unit/test_key.rb +205 -0
  164. data/test/unit/test_keys.rb +65 -0
  165. data/test/unit/test_mongo_mapper.rb +143 -0
  166. data/test/unit/test_pagination.rb +11 -0
  167. data/test/unit/test_plugins.rb +89 -0
  168. data/test/unit/test_rails.rb +183 -0
  169. data/test/unit/test_rails_compatibility.rb +38 -0
  170. data/test/unit/test_rails_reflect_on_association.rb +118 -0
  171. data/test/unit/test_railtie.rb +66 -0
  172. data/test/unit/test_serialization.rb +166 -0
  173. data/test/unit/test_time_zones.rb +44 -0
  174. data/test/unit/test_translation.rb +27 -0
  175. data/test/unit/test_validations.rb +562 -0
  176. metadata +285 -0
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+
3
+ class TimestampsTest < Test::Unit::TestCase
4
+ context "timestamping" do
5
+ setup do
6
+ @klass = Doc do
7
+ key :first_name, String
8
+ key :last_name, String
9
+ key :age, Integer
10
+ key :date, Date
11
+ end
12
+ @klass.timestamps!
13
+ end
14
+
15
+ should "set created_at and updated_at on create" do
16
+ doc = @klass.new(:first_name => 'John', :age => 27)
17
+ doc.created_at.should be(nil)
18
+ doc.updated_at.should be(nil)
19
+ doc.save
20
+ doc.created_at.should_not be(nil)
21
+ doc.updated_at.should_not be(nil)
22
+ end
23
+
24
+ should "not overwrite created_at if it already exists" do
25
+ original_created_at = 1.month.ago
26
+ doc = @klass.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
27
+ doc.created_at.to_i.should == original_created_at.to_i
28
+ doc.updated_at.should be_nil
29
+ doc.save
30
+ doc.created_at.to_i.should == original_created_at.to_i
31
+ doc.updated_at.should_not be_nil
32
+ end
33
+
34
+ should "set updated_at on field update but leave created_at alone" do
35
+ doc = @klass.create(:first_name => 'John', :age => 27)
36
+ old_created_at = doc.created_at
37
+ old_updated_at = doc.updated_at
38
+ doc.first_name = 'Johnny'
39
+
40
+ Timecop.freeze(Time.now + 5.seconds) do
41
+ doc.save
42
+ end
43
+
44
+ doc.created_at.should == old_created_at
45
+ doc.updated_at.should_not == old_updated_at
46
+ end
47
+
48
+ should "set updated_at on document update but leave created_at alone" do
49
+ doc = @klass.create(:first_name => 'John', :age => 27)
50
+ old_created_at = doc.created_at.to_f
51
+
52
+ new_updated_at = Time.now + 5.seconds
53
+ Timecop.freeze(new_updated_at) do
54
+ @klass.update(doc._id, { :first_name => 'Johnny' })
55
+ end
56
+
57
+ doc = doc.reload
58
+ doc.created_at.to_f.should be_close(old_created_at, 0.001)
59
+ doc.updated_at.to_f.should be_close(new_updated_at.to_f, 0.001)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,125 @@
1
+ require 'test_helper'
2
+
3
+ class TouchTest < Test::Unit::TestCase
4
+ context "touch" do
5
+ context "document" do
6
+ setup do
7
+ @document = Doc { timestamps! }
8
+ end
9
+
10
+ should "update the updated_at timestamp" do
11
+ doc = @document.create
12
+ old_updated_at = doc.updated_at
13
+
14
+ Timecop.freeze(Time.now + 1.day) do
15
+ doc.touch
16
+ end
17
+
18
+ doc.reload
19
+ doc.updated_at.should_not == old_updated_at
20
+ end
21
+ end
22
+
23
+ context "embedded document" do
24
+ should "update the updated_at timestamp" do
25
+ Doc = Doc("Document") { timestamps!}
26
+ Emdoc = EDoc("EmbeddedDocument") { timestamps! }
27
+ Doc.has_many :emdocs, :class => Emdoc
28
+
29
+ doc = Doc.create
30
+ emdoc = Emdoc.new
31
+ doc.emdocs << emdoc
32
+ doc.save
33
+
34
+ old_updated_at = emdoc.updated_at
35
+ document_old_updated_at = doc.updated_at
36
+
37
+ Timecop.freeze(Time.now + 1.day) do
38
+ emdoc.touch
39
+ end
40
+
41
+ doc.reload
42
+ emdoc.updated_at.should_not == old_updated_at
43
+ doc.updated_at.should_not == document_old_updated_at
44
+ end
45
+ end
46
+
47
+ context "association" do
48
+ setup do
49
+ @post_class = Doc("Post") do
50
+ key :touched_at, DateTime
51
+ timestamps!
52
+ end
53
+ @comment_class = Doc("Comment") do
54
+ key :post_id, ObjectId
55
+ key :text, String
56
+ timestamps!
57
+ end
58
+
59
+ @post_class.many :comments, :class => @comment_class
60
+ end
61
+
62
+ should 'not be true by default' do
63
+ @comment_class.belongs_to :post, :class => @post_class
64
+ @comment_class.associations[:post].touch?.should_not be_true
65
+ end
66
+
67
+ context 'touch the parent when true' do
68
+ setup do
69
+ @comment_class.belongs_to :post, :class => @post_class, :touch => true
70
+ @post = @post_class.create(:title => 'Hello, world!')
71
+ @comment = @post.comments.build
72
+ end
73
+
74
+ should "when the child is created" do
75
+ orig_updated_at = @post.updated_at
76
+ Timecop.freeze(Time.now + 1.day) do
77
+ @comment.save
78
+ end
79
+
80
+ @post.reload.updated_at.should_not == orig_updated_at
81
+ end
82
+
83
+ should "when the child is updated" do
84
+ @comment.save
85
+ old_updated_at = @post.updated_at
86
+ Timecop.freeze(Time.now + 2.day) do
87
+ @comment.update_attributes(:text => "Something")
88
+ end
89
+ @post.reload.updated_at.should_not == old_updated_at
90
+ end
91
+
92
+ should "when the child is touched" do
93
+ @comment.save
94
+ old_updated_at = @post.updated_at
95
+ Timecop.freeze(Time.now + 3.day) do
96
+ @comment.touch
97
+ end
98
+ @post.reload.updated_at.should_not == old_updated_at
99
+ end
100
+ end
101
+
102
+ context "when set to a symbol that is a key of parent" do
103
+ should "set that key on touch events" do
104
+ @comment_class.belongs_to :post, :class => @post_class, :touch => :touched_at
105
+ post = @post_class.create(:title => 'Hello, world!')
106
+ post.touched_at.should be_nil
107
+
108
+ comment = post.comments.build
109
+ comment.save
110
+ post.reload.touched_at.should_not be_nil
111
+ end
112
+ end
113
+
114
+ should 'not touch the parent when false' do
115
+ post = @post_class.create(:title => 'Hello, world!')
116
+ comment = post.comments.build
117
+ Timecop.freeze(Time.now + 1.day) do
118
+ comment.save
119
+ end
120
+
121
+ post.reload.updated_at.should == post.created_at
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+
3
+ class UserstampsTest < Test::Unit::TestCase
4
+ class AltUser
5
+ include MongoMapper::Document
6
+ end
7
+
8
+ context "userstamping" do
9
+ setup do
10
+ @document = Doc do
11
+ userstamps!
12
+ end
13
+ @document_alt_user = Doc do
14
+ userstamps! :class_name => 'AltUser'
15
+ end
16
+ @document_alt_user_class = Doc do
17
+ userstamps! :class => AltUser
18
+ end
19
+ @docs = [@document, @document_alt_user, @document_alt_user_class]
20
+ end
21
+
22
+ should "add creator_id key" do
23
+ @docs.each{ |d| d.keys.should include('creator_id') }
24
+ end
25
+
26
+ should "add updater_id key" do
27
+ @docs.each{ |d| d.keys.should include('updater_id') }
28
+ end
29
+
30
+ should "add belongs_to creator" do
31
+ @docs.each{ |d| d.associations.keys.should include(:creator) }
32
+ end
33
+
34
+ should "add belongs_to updater" do
35
+ @docs.each{ |d| d.associations.keys.should include(:updater) }
36
+ end
37
+
38
+ should "properly set class names" do
39
+ @document.associations[:creator].class_name.should == 'User'
40
+ @document_alt_user.associations[:creator].class_name.should == 'AltUser'
41
+ @document_alt_user_class.associations[:creator].class_name.should == 'UserstampsTest::AltUser'
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,414 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context "Saving a new document that is invalid" do
5
+ setup do
6
+ @document = Doc do
7
+ key :name, String, :required => true
8
+ end
9
+ end
10
+
11
+ should "not insert document" do
12
+ doc = @document.new
13
+ doc.save
14
+ @document.count.should == 0
15
+ end
16
+
17
+ should "populate document's errors" do
18
+ doc = @document.new
19
+ doc.errors.size.should == 0
20
+ doc.save
21
+ doc.errors.full_messages.should == ["Name can't be blank"]
22
+ end
23
+ end
24
+
25
+ context "Saving a document that is invalid (destructive)" do
26
+ setup do
27
+ @document = Doc do
28
+ key :name, String, :required => true
29
+ end
30
+ end
31
+
32
+ should "raise error" do
33
+ doc = @document.new
34
+ lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
35
+ end
36
+
37
+ should "set document on exception" do
38
+ doc = @document.new
39
+ begin
40
+ doc.save!
41
+ rescue MongoMapper::DocumentNotValid => e
42
+ e.document.should == doc
43
+ end
44
+ end
45
+ end
46
+
47
+ context "Creating a document that is invalid (destructive)" do
48
+ setup do
49
+ @document = Doc do
50
+ key :name, String, :required => true
51
+ end
52
+ end
53
+
54
+ should "raise error" do
55
+ lambda { @document.create! }.should raise_error(MongoMapper::DocumentNotValid)
56
+ end
57
+
58
+ should "create a new document" do
59
+ instance = @document.create!(:name => "James")
60
+ instance.new_record?.should be_false
61
+ end
62
+ end
63
+
64
+ context "Saving an existing document that is invalid" do
65
+ setup do
66
+ @document = Doc do
67
+ key :name, String, :required => true
68
+ end
69
+
70
+ @doc = @document.create(:name => 'John Nunemaker')
71
+ end
72
+
73
+ should "not update document" do
74
+ @doc.name = nil
75
+ @doc.save
76
+ @doc.reload.name.should == 'John Nunemaker'
77
+ end
78
+
79
+ should "populate document's errors" do
80
+ @doc.name = nil
81
+ @doc.save
82
+ @doc.errors.full_messages.should == ["Name can't be blank"]
83
+ end
84
+ end
85
+
86
+ context "Adding validation errors" do
87
+ setup do
88
+ @document = Doc do
89
+ key :action, String
90
+ def action_present
91
+ errors.add(:action, 'is invalid') if action.blank?
92
+ end
93
+ end
94
+ end
95
+
96
+ should "work with validate :on => :create callback" do
97
+ @document.validate :action_present, :on => :create
98
+
99
+ doc = @document.create(:action => nil)
100
+ doc.should have_error_on(:action)
101
+
102
+ doc.action = 'kick'
103
+ doc.save
104
+ doc.should_not have_error_on(:action)
105
+ end
106
+
107
+ should "work with validate :on => :update callback" do
108
+ @document.validate :action_present, :on => :update
109
+
110
+ doc = @document.new
111
+ doc.action = nil
112
+ doc.should_not have_error_on(:action)
113
+ doc.save
114
+
115
+ doc.action = nil
116
+ doc.should have_error_on(:action)
117
+
118
+ doc.action = 'kick'
119
+ doc.should_not have_error_on(:action)
120
+ end
121
+ end
122
+
123
+ context "validating uniqueness of" do
124
+ setup do
125
+ @document = Doc do
126
+ key :name, String
127
+ validates_uniqueness_of :name
128
+ end
129
+ end
130
+
131
+ should "not fail if object is new" do
132
+ doc = @document.new
133
+ doc.should_not have_error_on(:name)
134
+ end
135
+
136
+ should "not fail when new object is out of scope" do
137
+ document = Doc do
138
+ key :name
139
+ key :adult
140
+ validates_uniqueness_of :name, :scope => :adult
141
+ end
142
+ doc = document.new("name" => "joe", :adult => true)
143
+ doc.save.should be_true
144
+
145
+ doc2 = document.new("name" => "joe", :adult => false)
146
+ doc2.should be_valid
147
+ end
148
+
149
+ should "work with i18n taken message" do
150
+ @document.create(:name => 'joe')
151
+ doc = @document.create(:name => 'joe')
152
+ doc.should have_error_on(:name, 'has already been taken')
153
+ end
154
+
155
+ should "allow to update an object" do
156
+ doc = @document.new("name" => "joe")
157
+ doc.save.should be_true
158
+
159
+ @document \
160
+ .stubs(:first) \
161
+ .with(:name => 'joe') \
162
+ .returns(doc)
163
+
164
+ doc.name = "joe"
165
+ doc.valid?.should be_true
166
+ doc.should_not have_error_on(:name)
167
+ end
168
+
169
+ should "fail if object name is not unique" do
170
+ doc = @document.new("name" => "joe")
171
+ doc.save.should be_true
172
+
173
+ @document \
174
+ .stubs(:first) \
175
+ .with(:name => 'joe') \
176
+ .returns(doc)
177
+
178
+ doc2 = @document.new("name" => "joe")
179
+ doc2.should have_error_on(:name)
180
+ end
181
+
182
+ should "allow multiple blank entries if :allow_blank => true" do
183
+ document = Doc do
184
+ key :name
185
+ validates_uniqueness_of :name, :allow_blank => :true
186
+ end
187
+
188
+ doc = document.new("name" => "")
189
+ doc.save.should be_true
190
+
191
+ document \
192
+ .stubs(:first) \
193
+ .with(:name => '') \
194
+ .returns(doc)
195
+
196
+ doc2 = document.new("name" => "")
197
+ doc2.should_not have_error_on(:name)
198
+ end
199
+
200
+ should "allow multiple nil entries if :allow_nil => true" do
201
+ document = Doc do
202
+ key :name
203
+ validates_uniqueness_of :name, :allow_nil => :true
204
+ end
205
+
206
+ doc = document.new('name' => nil)
207
+ doc.save.should be_true
208
+
209
+ doc2 = document.new('name' => nil)
210
+ doc2.should_not have_error_on(:name)
211
+ end
212
+
213
+ should "allow entries that differ only in case by default" do
214
+ document = Doc do
215
+ key :name
216
+ validates_uniqueness_of :name
217
+ end
218
+
219
+ doc = document.new("name" => "BLAMMO")
220
+ doc.save.should be_true
221
+
222
+ doc2 = document.new("name" => "blammo")
223
+ doc2.should_not have_error_on(:name)
224
+ end
225
+
226
+ context "with :case_sensitive => false" do
227
+ setup do
228
+ @document = Doc do
229
+ key :name
230
+ validates_uniqueness_of :name, :case_sensitive => false
231
+ end
232
+ end
233
+
234
+ should "fail on entries that differ only in case" do
235
+ doc = @document.new("name" => "BLAMMO")
236
+ doc.save.should be_true
237
+
238
+ doc2 = @document.new("name" => "blammo")
239
+ doc2.should have_error_on(:name)
240
+ end
241
+
242
+ should "not raise an error if value is nil" do
243
+ doc = @document.new("name" => nil)
244
+ lambda { doc.valid? }.should_not raise_error
245
+ end
246
+
247
+ should "not raise an error if special Regexp characters used" do
248
+ doc = @document.new("name" => '?')
249
+ lambda { doc.valid? }.should_not raise_error
250
+ end
251
+
252
+ should "check for uniqueness using entire string" do
253
+ doc = @document.new("name" => "John Doe")
254
+ doc.save.should be_true
255
+
256
+ doc2 = @document.new("name" => "John")
257
+ doc2.valid?.should be_true
258
+ end
259
+ end
260
+
261
+ context "scoped by a single attribute" do
262
+ setup do
263
+ @document = Doc do
264
+ key :name, String
265
+ key :scope, String
266
+ validates_uniqueness_of :name, :scope => :scope
267
+ end
268
+ end
269
+
270
+ should "fail if the same name exists in the scope" do
271
+ doc = @document.new("name" => "joe", "scope" => "one")
272
+ doc.save.should be_true
273
+
274
+ @document \
275
+ .stubs(:first) \
276
+ .with(:name => 'joe', :scope => "one") \
277
+ .returns(doc)
278
+
279
+ doc2 = @document.new("name" => "joe", "scope" => "one")
280
+ doc2.should have_error_on(:name)
281
+ end
282
+
283
+ should "pass if the same name exists in a different scope" do
284
+ doc = @document.new("name" => "joe", "scope" => "one")
285
+ doc.save.should be_true
286
+
287
+ @document \
288
+ .stubs(:first) \
289
+ .with(:name => 'joe', :scope => 'two') \
290
+ .returns(nil)
291
+
292
+ doc2 = @document.new("name" => "joe", "scope" => "two")
293
+ doc2.should_not have_error_on(:name)
294
+ end
295
+ end
296
+
297
+ context "scoped by a multiple attributes" do
298
+ setup do
299
+ @document = Doc do
300
+ key :name, String
301
+ key :first_scope, String
302
+ key :second_scope, String
303
+ validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
304
+ end
305
+ end
306
+
307
+ should "fail if the same name exists in the scope" do
308
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
309
+ doc.save.should be_true
310
+
311
+ @document \
312
+ .stubs(:first) \
313
+ .with(:name => 'joe', :first_scope => 'one', :second_scope => 'two') \
314
+ .returns(doc)
315
+
316
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
317
+ doc2.should have_error_on(:name)
318
+ end
319
+
320
+ should "pass if the same name exists in a different scope" do
321
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
322
+ doc.save.should be_true
323
+
324
+ @document \
325
+ .stubs(:first) \
326
+ .with(:name => 'joe', :first_scope => 'one', :second_scope => 'one') \
327
+ .returns(nil)
328
+
329
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
330
+ doc2.should_not have_error_on(:name)
331
+ end
332
+ end
333
+ end
334
+
335
+ context "validating associated docs" do
336
+ setup do
337
+ @child_class = EDoc do
338
+ key :name, :required => true
339
+ end
340
+
341
+ @root_class = Doc { }
342
+ @root_class.many :children, :class => @child_class
343
+ @root_class.validates_associated :children, :message => 'are invalid'
344
+ end
345
+
346
+ should "pass if there are no associated docs" do
347
+ doc = @root_class.new
348
+ doc.save.should be_true
349
+ end
350
+
351
+ should "pass if the associated doc is valid" do
352
+ doc = @root_class.new
353
+ doc.children.build(:name => 'Joe')
354
+ doc.save.should be_true
355
+ end
356
+
357
+ should "fail if the associated doc is invalid" do
358
+ doc = @root_class.new
359
+ doc.children.build
360
+ doc.should have_error_on(:children, 'are invalid')
361
+ end
362
+
363
+ end
364
+
365
+ context "validating associated docs with custom context" do
366
+ setup do
367
+ @child_class = EDoc do
368
+ key :name
369
+
370
+ validates_length_of :name, :minimum => 5, :on => :custom_context
371
+ end
372
+
373
+ @root_class = Doc { }
374
+ @root_class.many :children, :class => @child_class
375
+ @root_class.validates_associated :children, :context => :custom_context
376
+ end
377
+
378
+ should "pass if there are no associated docs" do
379
+ doc = @root_class.new
380
+ doc.valid?(:custom_context).should be_true
381
+ end
382
+
383
+ should "pass if the associated doc is valid" do
384
+ doc = @root_class.new
385
+ doc.children.build(:name => 'George')
386
+ doc.valid?(:custom_context).should be_true
387
+ end
388
+
389
+ should "fail if the associated doc is invalid" do
390
+ doc = @root_class.new
391
+ doc.children.build(:name => 'Bob')
392
+ doc.valid?(:custom_context).should_not be_true
393
+ end
394
+
395
+ end
396
+ # context "validates uniqueness of with :unique shortcut" do
397
+ # should "work" do
398
+ # @document = Doc do
399
+ # key :name, String, :unique => true
400
+ # end
401
+ #
402
+ # doc = @document.create(:name => 'John')
403
+ # doc.should_not have_error_on(:name)
404
+ #
405
+ # @document \
406
+ # .stubs(:first) \
407
+ # .with(:name => 'John') \
408
+ # .returns(doc)
409
+ #
410
+ # second_john = @document.create(:name => 'John')
411
+ # second_john.should have_error_on(:name, 'has already been taken')
412
+ # end
413
+ # end
414
+ end