jmonteiro-mongo_mapper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +38 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/jmonteiro-mongo_mapper.gemspec +195 -0
  8. data/lib/mongo_mapper.rb +128 -0
  9. data/lib/mongo_mapper/descendant_appends.rb +44 -0
  10. data/lib/mongo_mapper/document.rb +402 -0
  11. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  12. data/lib/mongo_mapper/embedded_document.rb +61 -0
  13. data/lib/mongo_mapper/finder_options.rb +127 -0
  14. data/lib/mongo_mapper/plugins.rb +19 -0
  15. data/lib/mongo_mapper/plugins/associations.rb +104 -0
  16. data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
  17. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +28 -0
  18. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  20. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +49 -0
  21. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  22. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  23. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  24. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  25. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  26. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  27. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +66 -0
  28. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  29. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  30. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  31. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  32. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  33. data/lib/mongo_mapper/plugins/equality.rb +11 -0
  34. data/lib/mongo_mapper/plugins/identity_map.rb +66 -0
  35. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  36. data/lib/mongo_mapper/plugins/keys.rb +295 -0
  37. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  38. data/lib/mongo_mapper/plugins/pagination.rb +85 -0
  39. data/lib/mongo_mapper/plugins/protected.rb +31 -0
  40. data/lib/mongo_mapper/plugins/rails.rb +80 -0
  41. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  42. data/lib/mongo_mapper/plugins/validations.rb +48 -0
  43. data/lib/mongo_mapper/support.rb +213 -0
  44. data/performance/read_write.rb +52 -0
  45. data/specs.watchr +51 -0
  46. data/test/NOTE_ON_TESTING +1 -0
  47. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  48. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  49. data/test/functional/associations/test_in_array_proxy.rb +309 -0
  50. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  51. data/test/functional/associations/test_many_documents_proxy.rb +437 -0
  52. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +175 -0
  53. data/test/functional/associations/test_many_embedded_proxy.rb +216 -0
  54. data/test/functional/associations/test_many_polymorphic_proxy.rb +340 -0
  55. data/test/functional/associations/test_one_proxy.rb +149 -0
  56. data/test/functional/test_associations.rb +44 -0
  57. data/test/functional/test_binary.rb +27 -0
  58. data/test/functional/test_callbacks.rb +81 -0
  59. data/test/functional/test_dirty.rb +156 -0
  60. data/test/functional/test_document.rb +1171 -0
  61. data/test/functional/test_embedded_document.rb +125 -0
  62. data/test/functional/test_identity_map.rb +233 -0
  63. data/test/functional/test_logger.rb +20 -0
  64. data/test/functional/test_modifiers.rb +252 -0
  65. data/test/functional/test_pagination.rb +93 -0
  66. data/test/functional/test_protected.rb +41 -0
  67. data/test/functional/test_string_id_compatibility.rb +67 -0
  68. data/test/functional/test_validations.rb +329 -0
  69. data/test/models.rb +232 -0
  70. data/test/support/custom_matchers.rb +55 -0
  71. data/test/support/timing.rb +16 -0
  72. data/test/test_helper.rb +60 -0
  73. data/test/unit/associations/test_base.rb +207 -0
  74. data/test/unit/associations/test_proxy.rb +103 -0
  75. data/test/unit/serializers/test_json_serializer.rb +189 -0
  76. data/test/unit/test_descendant_appends.rb +71 -0
  77. data/test/unit/test_document.rb +203 -0
  78. data/test/unit/test_dynamic_finder.rb +125 -0
  79. data/test/unit/test_embedded_document.rb +628 -0
  80. data/test/unit/test_finder_options.rb +325 -0
  81. data/test/unit/test_keys.rb +169 -0
  82. data/test/unit/test_mongo_mapper.rb +65 -0
  83. data/test/unit/test_pagination.rb +127 -0
  84. data/test/unit/test_plugins.rb +42 -0
  85. data/test/unit/test_rails.rb +139 -0
  86. data/test/unit/test_rails_compatibility.rb +42 -0
  87. data/test/unit/test_serialization.rb +51 -0
  88. data/test/unit/test_support.rb +350 -0
  89. data/test/unit/test_time_zones.rb +39 -0
  90. data/test/unit/test_validations.rb +492 -0
  91. metadata +260 -0
@@ -0,0 +1,175 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyEmbeddedPolymorphicProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Catalog.collection.remove
7
+ TrModels::Fleet.collection.remove
8
+ end
9
+
10
+ should "default reader to empty array" do
11
+ catalog = Catalog.new
12
+ catalog.medias.should == []
13
+ end
14
+
15
+ should "allow adding to association like it was an array" do
16
+ catalog = Catalog.new
17
+ catalog.medias << Video.new
18
+ catalog.medias.push Video.new
19
+ catalog.medias.size.should == 2
20
+ end
21
+
22
+ should "be able to replace the association" do
23
+ catalog = Catalog.new
24
+ catalog.medias = [Video.new('file' => 'video.mpg', 'length' => 3600)]
25
+ catalog.save.should be_true
26
+
27
+ catalog = catalog.reload
28
+ catalog.medias.size.should == 1
29
+ catalog.medias[0].file.should == 'video.mpg'
30
+ end
31
+
32
+ context "count" do
33
+ should "default to 0" do
34
+ Catalog.new.medias.count.should == 0
35
+ end
36
+
37
+ should 'return correct count if any are embedded' do
38
+ catalog = Catalog.new
39
+ catalog.medias = [
40
+ Video.new('file' => 'video.mpg', 'length' => 3600),
41
+ Music.new('file' => 'music.mp3', 'bitrate' => '128kbps'),
42
+ Image.new('file' => 'image.png', 'width' => 800, 'height' => 600)
43
+ ]
44
+ catalog.medias.count.should == 3
45
+ catalog.save.should be_true
46
+ catalog.reload
47
+ catalog.medias.count.should == 3
48
+ end
49
+ end
50
+
51
+ should "store different associations" do
52
+ catalog = Catalog.new
53
+ catalog.medias = [
54
+ Video.new('file' => 'video.mpg', 'length' => 3600),
55
+ Music.new('file' => 'music.mp3', 'bitrate' => '128kbps'),
56
+ Image.new('file' => 'image.png', 'width' => 800, 'height' => 600)
57
+ ]
58
+ catalog.save.should be_true
59
+
60
+ catalog = catalog.reload
61
+ catalog.medias.size.should == 3
62
+ catalog.medias[0].file.should == 'video.mpg'
63
+ catalog.medias[0].length.should == 3600
64
+ catalog.medias[1].file.should == 'music.mp3'
65
+ catalog.medias[1].bitrate.should == "128kbps"
66
+ catalog.medias[2].file.should == 'image.png'
67
+ catalog.medias[2].width.should == 800
68
+ catalog.medias[2].height.should == 600
69
+ end
70
+
71
+ context "With modularized models" do
72
+ should "set associations correctly" do
73
+ fleet_attributes = {
74
+ 'name' => 'My Fleet',
75
+ 'transports' => [
76
+ {'_type' => 'TrModels::Ambulance', 'license_plate' => 'GGG123', 'icu' => true},
77
+ {'_type' => 'TrModels::Car', 'license_plate' => 'ABC123', 'model' => 'VW Golf', 'year' => 2001},
78
+ {'_type' => 'TrModels::Car', 'license_plate' => 'DEF123', 'model' => 'Honda Accord', 'year' => 2008},
79
+ ]
80
+ }
81
+
82
+ fleet = TrModels::Fleet.new(fleet_attributes)
83
+ fleet.transports.size.should == 3
84
+ fleet.transports[0].class.should == TrModels::Ambulance
85
+ fleet.transports[0].license_plate.should == 'GGG123'
86
+ fleet.transports[0].icu.should be_true
87
+ fleet.transports[1].class.should == TrModels::Car
88
+ fleet.transports[1].license_plate.should == 'ABC123'
89
+ fleet.transports[1].model.should == 'VW Golf'
90
+ fleet.transports[1].year.should == 2001
91
+ fleet.transports[2].class.should == TrModels::Car
92
+ fleet.transports[2].license_plate.should == 'DEF123'
93
+ fleet.transports[2].model.should == 'Honda Accord'
94
+ fleet.transports[2].year.should == 2008
95
+ fleet.save.should be_true
96
+
97
+ fleet = fleet.reload
98
+ fleet.transports.size.should == 3
99
+ fleet.transports[0].license_plate.should == 'GGG123'
100
+ fleet.transports[0].icu.should be_true
101
+ fleet.transports[1].license_plate.should == 'ABC123'
102
+ fleet.transports[1].model.should == 'VW Golf'
103
+ fleet.transports[1].year.should == 2001
104
+ fleet.transports[2].license_plate.should == 'DEF123'
105
+ fleet.transports[2].model.should == 'Honda Accord'
106
+ fleet.transports[2].year.should == 2008
107
+ end
108
+
109
+ should "default reader to empty array" do
110
+ fleet = TrModels::Fleet.new
111
+ fleet.transports.should == []
112
+ end
113
+
114
+ should "allow adding to association like it was an array" do
115
+ fleet = TrModels::Fleet.new
116
+ fleet.transports << TrModels::Car.new
117
+ fleet.transports.push TrModels::Bus.new
118
+ fleet.transports.size.should == 2
119
+ end
120
+
121
+ should "be able to replace the association" do
122
+ fleet = TrModels::Fleet.new
123
+ fleet.transports = [TrModels::Car.new('license_plate' => 'DCU2013', 'model' => 'Honda Civic')]
124
+ fleet.save.should be_true
125
+
126
+ fleet = fleet.reload
127
+ fleet.transports.size.should == 1
128
+ fleet.transports[0].license_plate.should == 'DCU2013'
129
+ end
130
+
131
+ should "store different associations" do
132
+ fleet = TrModels::Fleet.new
133
+ fleet.transports = [
134
+ TrModels::Car.new('license_plate' => 'ABC1223', 'model' => 'Honda Civic', 'year' => 2003),
135
+ TrModels::Bus.new('license_plate' => 'XYZ9090', 'max_passengers' => 51),
136
+ TrModels::Ambulance.new('license_plate' => 'HDD3030', 'icu' => true)
137
+ ]
138
+ fleet.save.should be_true
139
+
140
+ fleet = fleet.reload
141
+ fleet.transports.size.should == 3
142
+ fleet.transports[0].license_plate.should == 'ABC1223'
143
+ fleet.transports[0].model.should == 'Honda Civic'
144
+ fleet.transports[0].year.should == 2003
145
+ fleet.transports[1].license_plate.should == 'XYZ9090'
146
+ fleet.transports[1].max_passengers.should == 51
147
+ fleet.transports[2].license_plate.should == 'HDD3030'
148
+ fleet.transports[2].icu.should == true
149
+ end
150
+ end
151
+
152
+ context "extending the association" do
153
+ should "work using a block passed to many" do
154
+ catalog = Catalog.new
155
+ medias = catalog.medias = [
156
+ Video.new('file' => 'video.mpg', 'length' => 3600, :visible => true),
157
+ Music.new('file' => 'music.mp3', 'bitrate' => '128kbps', :visible => true),
158
+ Image.new('file' => 'image.png', 'width' => 800, 'height' => 600, :visible => false)
159
+ ]
160
+ catalog.save
161
+ catalog.medias.visible.should == [medias[0], medias[1]]
162
+ end
163
+
164
+ should "work using many's :extend option" do
165
+ fleet = TrModels::Fleet.new
166
+ transports = fleet.transports = [
167
+ TrModels::Car.new('license_plate' => 'ABC1223', 'model' => 'Honda Civic', 'year' => 2003, :purchased_on => 2.years.ago.to_date),
168
+ TrModels::Bus.new('license_plate' => 'XYZ9090', 'max_passengers' => 51, :purchased_on => 3.years.ago.to_date),
169
+ TrModels::Ambulance.new('license_plate' => 'HDD3030', 'icu' => true, :purchased_on => 1.year.ago.to_date)
170
+ ]
171
+ fleet.save
172
+ fleet.transports.to_be_replaced.should == [transports[1]]
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,216 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyEmbeddedProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ @comment_class = EDoc do
7
+ key :name, String
8
+ key :body, String
9
+ end
10
+ @post_class = Doc do
11
+ key :title, String
12
+ end
13
+ @post_class.many :comments, :class => @comment_class
14
+
15
+ @pet_class = EDoc do
16
+ key :name, String
17
+ end
18
+ @person_class = EDoc do
19
+ key :name, String
20
+ end
21
+ @person_class.key :child, @person_class
22
+ @person_class.many :pets, :class => @pet_class
23
+
24
+ @owner_class = Doc do
25
+ key :name, String
26
+ end
27
+ @owner_class.many :pets, :class => @pet_class
28
+ end
29
+
30
+ should "default reader to empty array" do
31
+ @post_class.new.comments.should == []
32
+ end
33
+
34
+ should "allow adding to association like it was an array" do
35
+ post = @post_class.new
36
+ post.comments << @comment_class.new
37
+ post.comments.push @comment_class.new
38
+ post.comments.size.should == 2
39
+ end
40
+
41
+ should "be embedded in document on save" do
42
+ frank = @comment_class.new(:name => 'Frank', :body => 'Hi!')
43
+ bill = @comment_class.new(:name => 'Bill', :body => 'Hi!')
44
+ post = @post_class.new
45
+ post.comments << frank
46
+ post.comments << bill
47
+ post.save
48
+
49
+ post.reload
50
+ post.comments.size.should == 2
51
+ post.comments[0].should == frank
52
+ post.comments[1].should == bill
53
+ end
54
+
55
+ should "allow embedding arbitrarily deep" do
56
+ @klass = Doc()
57
+ @klass.key :person, @person_class
58
+
59
+ meg = @person_class.new(:name => 'Meg')
60
+ meg.child = @person_class.new(:name => 'Steve')
61
+ meg.child.child = @person_class.new(:name => 'Linda')
62
+
63
+ doc = @klass.new(:person => meg)
64
+ doc.save
65
+ doc.reload
66
+
67
+ doc.person.name.should == 'Meg'
68
+ doc.person.child.name.should == 'Steve'
69
+ doc.person.child.child.name.should == 'Linda'
70
+ end
71
+
72
+ should "allow assignment of many embedded documents using a hash" do
73
+ person_attributes = {
74
+ 'name' => 'Mr. Pet Lover',
75
+ 'pets' => [
76
+ {'name' => 'Jimmy', 'species' => 'Cocker Spainel'},
77
+ {'name' => 'Sasha', 'species' => 'Siberian Husky'},
78
+ ]
79
+ }
80
+
81
+ owner = @owner_class.new(person_attributes)
82
+ owner.name.should == 'Mr. Pet Lover'
83
+ owner.pets[0].name.should == 'Jimmy'
84
+ owner.pets[0].species.should == 'Cocker Spainel'
85
+ owner.pets[1].name.should == 'Sasha'
86
+ owner.pets[1].species.should == 'Siberian Husky'
87
+
88
+ owner.save.should be_true
89
+ owner.reload
90
+
91
+ owner.name.should == 'Mr. Pet Lover'
92
+ owner.pets[0].name.should == 'Jimmy'
93
+ owner.pets[0].species.should == 'Cocker Spainel'
94
+ owner.pets[1].name.should == 'Sasha'
95
+ owner.pets[1].species.should == 'Siberian Husky'
96
+ end
97
+
98
+ context "embedding many embedded documents" do
99
+ setup do
100
+ @klass = Doc()
101
+ @klass.many :people, :class => @person_class
102
+ end
103
+
104
+ should "persist all embedded documents" do
105
+ meg = @person_class.new(:name => 'Meg', :pets => [
106
+ @pet_class.new(:name => 'Sparky', :species => 'Dog'),
107
+ @pet_class.new(:name => 'Koda', :species => 'Dog')
108
+ ])
109
+
110
+ doc = @klass.new
111
+ doc.people << meg
112
+ doc.save
113
+ doc.reload
114
+
115
+ doc.people.first.name.should == 'Meg'
116
+ doc.people.first.pets.should_not == []
117
+ doc.people.first.pets.first.name.should == 'Sparky'
118
+ doc.people.first.pets.first.species.should == 'Dog'
119
+ doc.people.first.pets[1].name.should == 'Koda'
120
+ doc.people.first.pets[1].species.should == 'Dog'
121
+ end
122
+
123
+ should "create a reference to the root document for all embedded documents before save" do
124
+ doc = @klass.new
125
+ meg = @person_class.new(:name => 'Meg')
126
+ pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
127
+
128
+ doc.people << meg
129
+ meg.pets << pet
130
+
131
+ doc.people.first._root_document.should == doc
132
+ doc.people.first.pets.first._root_document.should == doc
133
+ end
134
+
135
+ should "create a reference to the root document for all embedded documents" do
136
+ sparky = @pet_class.new(:name => 'Sparky', :species => 'Dog')
137
+ meg = @person_class.new(:name => 'Meg', :pets => [sparky])
138
+ doc = @klass.new
139
+ doc.people << meg
140
+ doc.save
141
+
142
+ doc.reload
143
+ doc.people.first._root_document.should == doc
144
+ doc.people.first.pets.first._root_document.should == doc
145
+ end
146
+ end
147
+
148
+ should "allow finding by id" do
149
+ sparky = @pet_class.new(:name => 'Sparky', :species => 'Dog')
150
+ meg = @owner_class.create(:name => 'Meg', :pets => [sparky])
151
+
152
+ meg.pets.find(sparky._id).should == sparky # oid
153
+ meg.pets.find(sparky.id.to_s).should == sparky # string
154
+ end
155
+
156
+ context "count" do
157
+ should "default to 0" do
158
+ @owner_class.new.pets.count.should == 0
159
+ end
160
+
161
+ should "return correct count if any are embedded" do
162
+ owner = @owner_class.new(:name => 'Meg')
163
+ owner.pets = [@pet_class.new, @pet_class.new]
164
+ owner.pets.count.should == 2
165
+ owner.save
166
+ owner.reload
167
+ owner.pets.count.should == 2
168
+ end
169
+ end
170
+
171
+ context "extending the association" do
172
+ setup do
173
+ @address_class = EDoc do
174
+ key :address, String
175
+ key :city, String
176
+ key :state, String
177
+ key :zip, Integer
178
+ end
179
+
180
+ @project_class = Doc do
181
+ key :name, String
182
+ end
183
+ end
184
+
185
+ should "work using a block passed to many" do
186
+ @project_class.many :addresses, :class => @address_class do
187
+ def find_all_by_state(state)
188
+ find_all { |a| a.state == state }
189
+ end
190
+ end
191
+
192
+ addr1 = @address_class.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
193
+ addr2 = @address_class.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
194
+ addr3 = @address_class.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
195
+ project = @project_class.create(:name => "Some Project", :addresses => [addr1, addr2, addr3])
196
+
197
+ project.addresses.find_all_by_state("CA").should == [addr1, addr2]
198
+ end
199
+
200
+ should "work using many's :extend option" do
201
+ module FindByCity
202
+ def find_by_city(city)
203
+ find_all { |a| a.city == city }
204
+ end
205
+ end
206
+ @project_class.many :addresses, :class => @address_class, :extend => FindByCity
207
+
208
+ addr1 = @address_class.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
209
+ addr2 = @address_class.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
210
+ addr3 = @address_class.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
211
+ project = @project_class.create(:name => "Some Project", :addresses => [addr1, addr2, addr3])
212
+
213
+ project.addresses.find_by_city('Burbank').should == [addr2]
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,340 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyPolymorphicProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Room.collection.remove
7
+ Message.collection.remove
8
+ end
9
+
10
+ should "default reader to empty array" do
11
+ Room.new.messages.should == []
12
+ end
13
+
14
+ should "add type key to polymorphic class base" do
15
+ Message.keys.keys.should include('_type')
16
+ end
17
+
18
+ should "allow adding to assiciation like it was an array" do
19
+ room = Room.new
20
+ room.messages << Enter.new
21
+ room.messages.push Exit.new
22
+ room.messages.concat Exit.new
23
+ room.messages.size.should == 3
24
+ end
25
+
26
+ should "be able to replace the association" do
27
+ room = Room.create(:name => 'Lounge')
28
+
29
+ lambda {
30
+ room.messages = [
31
+ Enter.new(:body => 'John entered room', :position => 1),
32
+ Chat.new(:body => 'Heyyyoooo!', :position => 2),
33
+ Exit.new(:body => 'John exited room', :position => 3)
34
+ ]
35
+ }.should change { Message.count }.by(3)
36
+
37
+ room = room.reload
38
+ messages = room.messages.all :order => "position"
39
+ messages.size.should == 3
40
+ messages[0].body.should == 'John entered room'
41
+ messages[1].body.should == 'Heyyyoooo!'
42
+ messages[2].body.should == 'John exited room'
43
+ end
44
+
45
+ should "correctly store type when using <<, push and concat" do
46
+ room = Room.new
47
+ room.messages << Enter.new(:body => 'John entered the room', :position => 1)
48
+ room.messages.push Exit.new(:body => 'John entered the room', :position => 2)
49
+ room.messages.concat Chat.new(:body => 'Holla!' , :position => 3)
50
+
51
+ room = room.reload
52
+ messages = room.messages.all :order => "position"
53
+ messages[0]._type.should == 'Enter'
54
+ messages[1]._type.should == 'Exit'
55
+ messages[2]._type.should == 'Chat'
56
+ end
57
+
58
+ context "build" do
59
+ should "assign foreign key" do
60
+ room = Room.create
61
+ message = room.messages.build
62
+ message.room_id.should == room._id
63
+ end
64
+
65
+ should "assign _type" do
66
+ room = Room.create
67
+ message = room.messages.build
68
+ message._type.should == 'Message'
69
+ end
70
+
71
+ should "allow assigning attributes" do
72
+ room = Room.create
73
+ message = room.messages.build(:body => 'Foo!')
74
+ message.body.should == 'Foo!'
75
+ end
76
+ end
77
+
78
+ context "create" do
79
+ should "assign foreign key" do
80
+ room = Room.create
81
+ message = room.messages.create
82
+ message.room_id.should == room._id
83
+ end
84
+
85
+ should "assign _type" do
86
+ room = Room.create
87
+ message = room.messages.create
88
+ message._type.should == 'Message'
89
+ end
90
+
91
+ should "save record" do
92
+ room = Room.create
93
+ lambda {
94
+ room.messages.create
95
+ }.should change { Message.count }
96
+ end
97
+
98
+ should "allow passing attributes" do
99
+ room = Room.create
100
+ message = room.messages.create(:body => 'Foo!')
101
+ message.body.should == 'Foo!'
102
+ end
103
+ end
104
+
105
+ context "count" do
106
+ should "work scoped to association" do
107
+ room = Room.create
108
+ 3.times { room.messages.create }
109
+
110
+ other_room = Room.create
111
+ 2.times { other_room.messages.create }
112
+
113
+ room.messages.count.should == 3
114
+ other_room.messages.count.should == 2
115
+ end
116
+
117
+ should "work with conditions" do
118
+ room = Room.create
119
+ room.messages.create(:body => 'Foo')
120
+ room.messages.create(:body => 'Other 1')
121
+ room.messages.create(:body => 'Other 2')
122
+
123
+ room.messages.count(:body => 'Foo').should == 1
124
+ end
125
+ end
126
+
127
+ context "Finding scoped to association" do
128
+ setup do
129
+ @lounge = Room.create(:name => 'Lounge')
130
+ @lm1 = Message.create(:body => 'Loungin!', :position => 1)
131
+ @lm2 = Message.create(:body => 'I love loungin!', :position => 2)
132
+ @lounge.messages = [@lm1, @lm2]
133
+ @lounge.save
134
+
135
+ @hall = Room.create(:name => 'Hall')
136
+ @hm1 = Message.create(:body => 'Do not fall in the hall', :position => 1)
137
+ @hm3 = Message.create(:body => 'Loungin!', :position => 3)
138
+ @hm2 = Message.create(:body => 'Hall the king!', :position => 2)
139
+ @hall.messages = [@hm1, @hm2, @hm3]
140
+ @hall.save
141
+ end
142
+
143
+ context "dynamic finders" do
144
+ should "work with single key" do
145
+ @lounge.messages.find_by_position(1).should == @lm1
146
+ @hall.messages.find_by_position(2).should == @hm2
147
+ end
148
+
149
+ should "work with multiple keys" do
150
+ @lounge.messages.find_by_body_and_position('Loungin!', 1).should == @lm1
151
+ @lounge.messages.find_by_body_and_position('Loungin!', 2).should be_nil
152
+ end
153
+
154
+ should "raise error when using !" do
155
+ lambda {
156
+ @lounge.messages.find_by_position!(222)
157
+ }.should raise_error(MongoMapper::DocumentNotFound)
158
+ end
159
+
160
+ context "find_or_create_by" do
161
+ should "not create document if found" do
162
+ lambda {
163
+ message = @lounge.messages.find_or_create_by_body('Loungin!')
164
+ message.room.should == @lounge
165
+ message.should == @lm1
166
+ }.should_not change { Message.count }
167
+ end
168
+
169
+ should "create document if not found" do
170
+ lambda {
171
+ message = @lounge.messages.find_or_create_by_body('Yo dawg!')
172
+ message.room.should == @lounge
173
+ message._type.should == 'Message'
174
+ }.should change { Message.count }.by(1)
175
+ end
176
+ end
177
+ end
178
+
179
+ context "with :all" do
180
+ should "work" do
181
+ @lounge.messages.find(:all, :order => "position").should == [@lm1, @lm2]
182
+ end
183
+
184
+ should "work with conditions" do
185
+ messages = @lounge.messages.find(:all, :body => 'Loungin!', :order => "position")
186
+ messages.should == [@lm1]
187
+ end
188
+
189
+ should "work with order" do
190
+ messages = @lounge.messages.find(:all, :order => 'position desc')
191
+ messages.should == [@lm2, @lm1]
192
+ end
193
+ end
194
+
195
+ context "with #all" do
196
+ should "work" do
197
+ @lounge.messages.all(:order => "position").should == [@lm1, @lm2]
198
+ end
199
+
200
+ should "work with conditions" do
201
+ messages = @lounge.messages.all(:body => 'Loungin!', :order => "position")
202
+ messages.should == [@lm1]
203
+ end
204
+
205
+ should "work with order" do
206
+ messages = @lounge.messages.all(:order => 'position desc')
207
+ messages.should == [@lm2, @lm1]
208
+ end
209
+ end
210
+
211
+ context "with :first" do
212
+ should "work" do
213
+ @lounge.messages.find(:first, :order => "position asc").should == @lm1
214
+ end
215
+
216
+ should "work with conditions" do
217
+ message = @lounge.messages.find(:first, :body => 'I love loungin!', :order => "position asc")
218
+ message.should == @lm2
219
+ end
220
+ end
221
+
222
+ context "with #first" do
223
+ should "work" do
224
+ @lounge.messages.first(:order => "position asc").should == @lm1
225
+ end
226
+
227
+ should "work with conditions" do
228
+ message = @lounge.messages.first(:body => 'I love loungin!', :order => "position asc")
229
+ message.should == @lm2
230
+ end
231
+ end
232
+
233
+ context "with :last" do
234
+ should "work" do
235
+ @lounge.messages.find(:last, :order => "position asc").should == @lm2
236
+ end
237
+
238
+ should "work with conditions" do
239
+ message = @lounge.messages.find(:last, :body => 'Loungin!', :order => "position asc")
240
+ message.should == @lm1
241
+ end
242
+ end
243
+
244
+ context "with #last" do
245
+ should "work" do
246
+ @lounge.messages.last(:order => "position asc").should == @lm2
247
+ end
248
+
249
+ should "work with conditions" do
250
+ message = @lounge.messages.last(:body => 'Loungin!', :order => "position asc")
251
+ message.should == @lm1
252
+ end
253
+ end
254
+
255
+ context "with one id" do
256
+ should "work for id in association" do
257
+ @lounge.messages.find(@lm2._id).should == @lm2
258
+ end
259
+
260
+ should "not work for id not in association" do
261
+ lambda {
262
+ @lounge.messages.find!(@hm2._id)
263
+ }.should raise_error(MongoMapper::DocumentNotFound)
264
+ end
265
+ end
266
+
267
+ context "with query options/criteria" do
268
+ should "work with order on association" do
269
+ @lounge.messages.should == [@lm1, @lm2]
270
+ end
271
+
272
+ should "allow overriding the order provided to the association" do
273
+ @lounge.messages.all(:order => 'position').should == [@lm1, @lm2]
274
+ end
275
+
276
+ should "allow using conditions on association" do
277
+ @hall.latest_messages.should == [@hm3, @hm2]
278
+ end
279
+ end
280
+
281
+ context "with multiple ids" do
282
+ should "work for ids in association" do
283
+ messages = @lounge.messages.find(@lm1._id, @lm2._id)
284
+ messages.should == [@lm1, @lm2]
285
+ end
286
+
287
+ should "not work for ids not in association" do
288
+ lambda {
289
+ @lounge.messages.find!(@lm1._id, @lm2._id, @hm2._id)
290
+ }.should raise_error(MongoMapper::DocumentNotFound)
291
+ end
292
+ end
293
+
294
+ context "with #paginate" do
295
+ setup do
296
+ @messages = @hall.messages.paginate(:per_page => 2, :page => 1, :order => 'position asc')
297
+ end
298
+
299
+ should "return total pages" do
300
+ @messages.total_pages.should == 2
301
+ end
302
+
303
+ should "return total entries" do
304
+ @messages.total_entries.should == 3
305
+ end
306
+
307
+ should "return the subject" do
308
+ @messages.should == [@hm1, @hm2]
309
+ end
310
+ end
311
+ end
312
+
313
+ context "extending the association" do
314
+ should "work using a block passed to many" do
315
+ room = Room.new(:name => "Amazing Room")
316
+ messages = room.messages = [
317
+ Enter.new(:body => 'John entered room', :position => 3),
318
+ Chat.new(:body => 'Heyyyoooo!', :position => 4),
319
+ Exit.new(:body => 'John exited room', :position => 5),
320
+ Enter.new(:body => 'Steve entered room', :position => 6),
321
+ Chat.new(:body => 'Anyone there?', :position => 7),
322
+ Exit.new(:body => 'Steve exited room', :position => 8)
323
+ ]
324
+ room.save
325
+ room.messages.older.should == messages[3..5]
326
+ end
327
+
328
+ should "work using many's :extend option" do
329
+
330
+ room = Room.new(:name => "Amazing Room")
331
+ accounts = room.accounts = [
332
+ Bot.new(:last_logged_in => 3.weeks.ago),
333
+ AccountUser.new(:last_logged_in => nil),
334
+ Bot.new(:last_logged_in => 1.week.ago)
335
+ ]
336
+ room.save
337
+ room.accounts.inactive.should == [accounts[1]]
338
+ end
339
+ end
340
+ end