jnunemaker-mongomapper 0.2.0 → 0.3.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 (55) hide show
  1. data/.gitignore +1 -0
  2. data/History +17 -0
  3. data/README.rdoc +6 -3
  4. data/Rakefile +3 -2
  5. data/VERSION +1 -1
  6. data/bin/mmconsole +56 -0
  7. data/lib/mongomapper.rb +48 -17
  8. data/lib/mongomapper/associations.rb +31 -39
  9. data/lib/mongomapper/associations/base.rb +40 -22
  10. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +33 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
  12. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +34 -0
  13. data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +5 -5
  14. data/lib/mongomapper/associations/many_proxy.rb +55 -0
  15. data/lib/mongomapper/associations/proxy.rb +21 -14
  16. data/lib/mongomapper/callbacks.rb +1 -1
  17. data/lib/mongomapper/document.rb +82 -59
  18. data/lib/mongomapper/embedded_document.rb +121 -130
  19. data/lib/mongomapper/finder_options.rb +21 -6
  20. data/lib/mongomapper/key.rb +5 -7
  21. data/lib/mongomapper/observing.rb +1 -41
  22. data/lib/mongomapper/pagination.rb +52 -0
  23. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  24. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  25. data/lib/mongomapper/serialization.rb +1 -1
  26. data/mongomapper.gemspec +62 -36
  27. data/test/NOTE_ON_TESTING +1 -0
  28. data/test/functional/test_associations.rb +485 -0
  29. data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
  30. data/test/functional/test_document.rb +636 -0
  31. data/test/functional/test_pagination.rb +82 -0
  32. data/test/functional/test_rails_compatibility.rb +31 -0
  33. data/test/functional/test_validations.rb +172 -0
  34. data/test/models.rb +92 -0
  35. data/test/test_helper.rb +5 -0
  36. data/test/{serializers → unit/serializers}/test_json_serializer.rb +0 -0
  37. data/test/unit/test_association_base.rb +131 -0
  38. data/test/unit/test_document.rb +115 -0
  39. data/test/{test_embedded_document.rb → unit/test_embedded_document.rb} +158 -66
  40. data/test/{test_finder_options.rb → unit/test_finder_options.rb} +66 -0
  41. data/test/{test_key.rb → unit/test_key.rb} +13 -1
  42. data/test/unit/test_mongo_id.rb +35 -0
  43. data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
  44. data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
  45. data/test/unit/test_pagination.rb +113 -0
  46. data/test/unit/test_rails_compatibility.rb +34 -0
  47. data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
  48. data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
  49. metadata +68 -36
  50. data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
  51. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
  52. data/lib/mongomapper/rails_compatibility.rb +0 -23
  53. data/test/test_associations.rb +0 -149
  54. data/test/test_document.rb +0 -944
  55. data/test/test_rails_compatibility.rb +0 -29
@@ -0,0 +1,52 @@
1
+ module MongoMapper
2
+ module Pagination
3
+ class PaginationProxy < BasicObject
4
+ attr_accessor :subject
5
+ attr_reader :total_entries, :per_page, :current_page
6
+ alias limit per_page
7
+
8
+ def initialize(total_entries, current_page, per_page=nil)
9
+ @total_entries = total_entries.to_i
10
+ self.per_page = per_page
11
+ self.current_page = current_page
12
+ end
13
+
14
+ def total_pages
15
+ (total_entries / per_page.to_f).ceil
16
+ end
17
+
18
+ def out_of_bounds?
19
+ current_page > total_pages
20
+ end
21
+
22
+ def previous_page
23
+ current_page > 1 ? (current_page - 1) : nil
24
+ end
25
+
26
+ def next_page
27
+ current_page < total_pages ? (current_page + 1) : nil
28
+ end
29
+
30
+ def skip
31
+ (current_page - 1) * per_page
32
+ end
33
+ alias offset skip
34
+
35
+ def method_missing(name, *args, &block)
36
+ @subject.send(name, *args, &block)
37
+ end
38
+
39
+ private
40
+ def per_page=(value)
41
+ value = 25 if value.blank?
42
+ @per_page = value.to_i
43
+ end
44
+
45
+ def current_page=(value)
46
+ value = value.to_i
47
+ value = 1 if value < 1
48
+ @current_page = value
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,15 @@
1
+ module MongoMapper
2
+ module RailsCompatibility
3
+ module Document
4
+ def self.included(model)
5
+ model.class_eval do
6
+ alias_method :new_record?, :new?
7
+ end
8
+ end
9
+
10
+ def to_param
11
+ id
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module MongoMapper
2
+ module RailsCompatibility
3
+ module EmbeddedDocument
4
+ def self.included(model)
5
+ model.class_eval do
6
+ extend ClassMethods
7
+ end
8
+
9
+ class << model
10
+ alias has_many many
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def column_names
16
+ keys.keys
17
+ end
18
+ end
19
+
20
+ def to_param
21
+ raise "Missing to_param method in #{self.class}. You should implement it to return the unique identifier of this embedded document within a document."
22
+ end
23
+ end
24
+ end
25
+ end
@@ -10,7 +10,7 @@ module MongoMapper #:nodoc:
10
10
  end
11
11
 
12
12
  def serializable_key_names
13
- key_names = @record.send :defined_key_names
13
+ key_names = @record.class.keys.keys
14
14
 
15
15
  if options[:only]
16
16
  options.delete(:except)
data/mongomapper.gemspec CHANGED
@@ -2,12 +2,14 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{mongomapper}
5
- s.version = "0.2.0"
5
+ s.version = "0.3.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["John Nunemaker"]
9
- s.date = %q{2009-07-07}
9
+ s.date = %q{2009-07-28}
10
+ s.default_executable = %q{mmconsole}
10
11
  s.email = %q{nunemaker@gmail.com}
12
+ s.executables = ["mmconsole"]
11
13
  s.extra_rdoc_files = [
12
14
  "LICENSE",
13
15
  "README.rdoc"
@@ -19,14 +21,16 @@ Gem::Specification.new do |s|
19
21
  "README.rdoc",
20
22
  "Rakefile",
21
23
  "VERSION",
24
+ "bin/mmconsole",
22
25
  "lib/mongomapper.rb",
23
26
  "lib/mongomapper/associations.rb",
24
27
  "lib/mongomapper/associations/array_proxy.rb",
25
28
  "lib/mongomapper/associations/base.rb",
29
+ "lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb",
26
30
  "lib/mongomapper/associations/belongs_to_proxy.rb",
27
- "lib/mongomapper/associations/has_many_embedded_proxy.rb",
28
- "lib/mongomapper/associations/has_many_proxy.rb",
29
- "lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb",
31
+ "lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb",
32
+ "lib/mongomapper/associations/many_embedded_proxy.rb",
33
+ "lib/mongomapper/associations/many_proxy.rb",
30
34
  "lib/mongomapper/associations/proxy.rb",
31
35
  "lib/mongomapper/callbacks.rb",
32
36
  "lib/mongomapper/document.rb",
@@ -34,25 +38,36 @@ Gem::Specification.new do |s|
34
38
  "lib/mongomapper/finder_options.rb",
35
39
  "lib/mongomapper/key.rb",
36
40
  "lib/mongomapper/observing.rb",
37
- "lib/mongomapper/rails_compatibility.rb",
41
+ "lib/mongomapper/pagination.rb",
42
+ "lib/mongomapper/rails_compatibility/document.rb",
43
+ "lib/mongomapper/rails_compatibility/embedded_document.rb",
38
44
  "lib/mongomapper/save_with_validation.rb",
39
45
  "lib/mongomapper/serialization.rb",
40
46
  "lib/mongomapper/serializers/json_serializer.rb",
41
47
  "lib/mongomapper/validations.rb",
42
48
  "mongomapper.gemspec",
43
- "test/serializers/test_json_serializer.rb",
44
- "test/test_associations.rb",
45
- "test/test_callbacks.rb",
46
- "test/test_document.rb",
47
- "test/test_embedded_document.rb",
48
- "test/test_finder_options.rb",
49
+ "test/NOTE_ON_TESTING",
50
+ "test/functional/test_associations.rb",
51
+ "test/functional/test_callbacks.rb",
52
+ "test/functional/test_document.rb",
53
+ "test/functional/test_pagination.rb",
54
+ "test/functional/test_rails_compatibility.rb",
55
+ "test/functional/test_validations.rb",
56
+ "test/models.rb",
49
57
  "test/test_helper.rb",
50
- "test/test_key.rb",
51
- "test/test_mongomapper.rb",
52
- "test/test_observing.rb",
53
- "test/test_rails_compatibility.rb",
54
- "test/test_serializations.rb",
55
- "test/test_validations.rb"
58
+ "test/unit/serializers/test_json_serializer.rb",
59
+ "test/unit/test_association_base.rb",
60
+ "test/unit/test_document.rb",
61
+ "test/unit/test_embedded_document.rb",
62
+ "test/unit/test_finder_options.rb",
63
+ "test/unit/test_key.rb",
64
+ "test/unit/test_mongo_id.rb",
65
+ "test/unit/test_mongomapper.rb",
66
+ "test/unit/test_observing.rb",
67
+ "test/unit/test_pagination.rb",
68
+ "test/unit/test_rails_compatibility.rb",
69
+ "test/unit/test_serializations.rb",
70
+ "test/unit/test_validations.rb"
56
71
  ]
57
72
  s.has_rdoc = true
58
73
  s.homepage = %q{http://github.com/jnunemaker/mongomapper}
@@ -62,19 +77,27 @@ Gem::Specification.new do |s|
62
77
  s.rubygems_version = %q{1.3.1}
63
78
  s.summary = %q{Awesome gem for modeling your domain and storing it in mongo}
64
79
  s.test_files = [
65
- "test/serializers/test_json_serializer.rb",
66
- "test/test_associations.rb",
67
- "test/test_callbacks.rb",
68
- "test/test_document.rb",
69
- "test/test_embedded_document.rb",
70
- "test/test_finder_options.rb",
80
+ "test/functional/test_associations.rb",
81
+ "test/functional/test_callbacks.rb",
82
+ "test/functional/test_document.rb",
83
+ "test/functional/test_pagination.rb",
84
+ "test/functional/test_rails_compatibility.rb",
85
+ "test/functional/test_validations.rb",
86
+ "test/models.rb",
71
87
  "test/test_helper.rb",
72
- "test/test_key.rb",
73
- "test/test_mongomapper.rb",
74
- "test/test_observing.rb",
75
- "test/test_rails_compatibility.rb",
76
- "test/test_serializations.rb",
77
- "test/test_validations.rb"
88
+ "test/unit/serializers/test_json_serializer.rb",
89
+ "test/unit/test_association_base.rb",
90
+ "test/unit/test_document.rb",
91
+ "test/unit/test_embedded_document.rb",
92
+ "test/unit/test_finder_options.rb",
93
+ "test/unit/test_key.rb",
94
+ "test/unit/test_mongo_id.rb",
95
+ "test/unit/test_mongomapper.rb",
96
+ "test/unit/test_observing.rb",
97
+ "test/unit/test_pagination.rb",
98
+ "test/unit/test_rails_compatibility.rb",
99
+ "test/unit/test_serializations.rb",
100
+ "test/unit/test_validations.rb"
78
101
  ]
79
102
 
80
103
  if s.respond_to? :specification_version then
@@ -83,21 +106,24 @@ Gem::Specification.new do |s|
83
106
 
84
107
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
85
108
  s.add_runtime_dependency(%q<activesupport>, [">= 0"])
86
- s.add_runtime_dependency(%q<mongodb-mongo>, ["= 0.9"])
87
- s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
109
+ s.add_runtime_dependency(%q<mongodb-mongo>, ["= 0.10.1"])
110
+ s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.2"])
111
+ s.add_runtime_dependency(%q<deep_merge>, ["= 0.1.0"])
88
112
  s.add_development_dependency(%q<mocha>, ["= 0.9.4"])
89
113
  s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
90
114
  else
91
115
  s.add_dependency(%q<activesupport>, [">= 0"])
92
- s.add_dependency(%q<mongodb-mongo>, ["= 0.9"])
93
- s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
116
+ s.add_dependency(%q<mongodb-mongo>, ["= 0.10.1"])
117
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.2"])
118
+ s.add_dependency(%q<deep_merge>, ["= 0.1.0"])
94
119
  s.add_dependency(%q<mocha>, ["= 0.9.4"])
95
120
  s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
96
121
  end
97
122
  else
98
123
  s.add_dependency(%q<activesupport>, [">= 0"])
99
- s.add_dependency(%q<mongodb-mongo>, ["= 0.9"])
100
- s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
124
+ s.add_dependency(%q<mongodb-mongo>, ["= 0.10.1"])
125
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.2"])
126
+ s.add_dependency(%q<deep_merge>, ["= 0.1.0"])
101
127
  s.add_dependency(%q<mocha>, ["= 0.9.4"])
102
128
  s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
103
129
  end
@@ -0,0 +1 @@
1
+ I am doing my best to keep unit and functional tests separate. As I see them, functional tests hit the database and should never care about internals. Unit tests do not hit the database.
@@ -0,0 +1,485 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class AssociationsTest < Test::Unit::TestCase
5
+ def setup
6
+ clear_all_collections
7
+ end
8
+
9
+ context "Modularized Polymorphic Many Embedded" do
10
+ should "set associations correctly" do
11
+ fleet_attributes = {
12
+ "name" => "My Fleet",
13
+ "transports" => [
14
+ {"_type" => "TrModels::Ambulance", "license_plate" => "GGG123", "icu" => true},
15
+ {"_type" => "TrModels::Car", "license_plate" => "ABC123", "model" => "VW Golf", "year" => 2001},
16
+ {"_type" => "TrModels::Car", "license_plate" => "DEF123", "model" => "Honda Accord", "year" => 2008},
17
+ ]
18
+ }
19
+
20
+ fleet = TrModels::Fleet.new(fleet_attributes)
21
+ fleet.transports.size.should == 3
22
+ fleet.transports[0].class.should == TrModels::Ambulance
23
+ fleet.transports[0].license_plate.should == "GGG123"
24
+ fleet.transports[0].icu.should be_true
25
+ fleet.transports[1].class.should == TrModels::Car
26
+ fleet.transports[1].license_plate.should == "ABC123"
27
+ fleet.transports[1].model.should == "VW Golf"
28
+ fleet.transports[1].year.should == 2001
29
+ fleet.transports[2].class.should == TrModels::Car
30
+ fleet.transports[2].license_plate.should == "DEF123"
31
+ fleet.transports[2].model.should == "Honda Accord"
32
+ fleet.transports[2].year.should == 2008
33
+ fleet.save.should be_true
34
+
35
+ from_db = TrModels::Fleet.find(fleet.id)
36
+ from_db.transports.size.should == 3
37
+ from_db.transports[0].license_plate.should == "GGG123"
38
+ from_db.transports[0].icu.should be_true
39
+ from_db.transports[1].license_plate.should == "ABC123"
40
+ from_db.transports[1].model.should == "VW Golf"
41
+ from_db.transports[1].year.should == 2001
42
+ from_db.transports[2].license_plate.should == "DEF123"
43
+ from_db.transports[2].model.should == "Honda Accord"
44
+ from_db.transports[2].year.should == 2008
45
+ end
46
+
47
+ should "default reader to empty array" do
48
+ fleet = TrModels::Fleet.new
49
+ fleet.transports.should == []
50
+ end
51
+
52
+ should "allow adding to association like it was an array" do
53
+ fleet = TrModels::Fleet.new
54
+ fleet.transports << TrModels::Car.new
55
+ fleet.transports.push TrModels::Bus.new
56
+ fleet.transports.size.should == 2
57
+ end
58
+
59
+ should "store the association" do
60
+ fleet = TrModels::Fleet.new
61
+ fleet.transports = [TrModels::Car.new("license_plate" => "DCU2013", "model" => "Honda Civic")]
62
+ fleet.save.should be_true
63
+
64
+ from_db = TrModels::Fleet.find(fleet.id)
65
+ from_db.transports.size.should == 1
66
+ from_db.transports[0].license_plate.should == "DCU2013"
67
+ end
68
+
69
+ should "store different associations" do
70
+ fleet = TrModels::Fleet.new
71
+ fleet.transports = [
72
+ TrModels::Car.new("license_plate" => "ABC1223", "model" => "Honda Civic", "year" => 2003),
73
+ TrModels::Bus.new("license_plate" => "XYZ9090", "max_passengers" => 51),
74
+ TrModels::Ambulance.new("license_plate" => "HDD3030", "icu" => true)
75
+ ]
76
+ fleet.save.should be_true
77
+
78
+ from_db = TrModels::Fleet.find(fleet.id)
79
+ from_db.transports.size.should == 3
80
+ from_db.transports[0].license_plate.should == "ABC1223"
81
+ from_db.transports[0].model.should == "Honda Civic"
82
+ from_db.transports[0].year.should == 2003
83
+ from_db.transports[1].license_plate.should == "XYZ9090"
84
+ from_db.transports[1].max_passengers.should == 51
85
+ from_db.transports[2].license_plate.should == "HDD3030"
86
+ from_db.transports[2].icu.should == true
87
+ end
88
+ end
89
+
90
+ context "Polymorphic Many Embedded" do
91
+ should "default reader to empty array" do
92
+ catalog = Catalog.new
93
+ catalog.medias.should == []
94
+ end
95
+
96
+ should "allow adding to association like it was an array" do
97
+ catalog = Catalog.new
98
+ catalog.medias << Video.new
99
+ catalog.medias.push Video.new
100
+ catalog.medias.size.should == 2
101
+ end
102
+
103
+ should "store the association" do
104
+ catalog = Catalog.new
105
+ catalog.medias = [Video.new("file" => "video.mpg", "length" => 3600)]
106
+ catalog.save.should be_true
107
+
108
+ from_db = Catalog.find(catalog.id)
109
+ from_db.medias.size.should == 1
110
+ from_db.medias[0].file.should == "video.mpg"
111
+ end
112
+
113
+ should "store different associations" do
114
+ catalog = Catalog.new
115
+ catalog.medias = [
116
+ Video.new("file" => "video.mpg", "length" => 3600),
117
+ Music.new("file" => "music.mp3", "bitrate" => "128kbps"),
118
+ Image.new("file" => "image.png", "width" => 800, "height" => 600)
119
+ ]
120
+ catalog.save.should be_true
121
+
122
+ from_db = Catalog.find(catalog.id)
123
+ from_db.medias.size.should == 3
124
+ from_db.medias[0].file.should == "video.mpg"
125
+ from_db.medias[0].length.should == 3600
126
+ from_db.medias[1].file.should == "music.mp3"
127
+ from_db.medias[1].bitrate.should == "128kbps"
128
+ from_db.medias[2].file.should == "image.png"
129
+ from_db.medias[2].width.should == 800
130
+ from_db.medias[2].height.should == 600
131
+ end
132
+ end
133
+
134
+ context "Polymorphic Belongs To" do
135
+ should "default to nil" do
136
+ status = Status.new
137
+ status.target.should be_nil
138
+ end
139
+
140
+ should "store the association" do
141
+ status = Status.new
142
+ project = Project.new(:name => "mongomapper")
143
+ status.target = project
144
+ status.save.should be_true
145
+
146
+ from_db = Status.find(status.id)
147
+ from_db.target.should_not be_nil
148
+ from_db.target_id.should == project.id
149
+ from_db.target_type.should == "Project"
150
+ from_db.target.name.should == "mongomapper"
151
+ end
152
+
153
+ should "unset the association" do
154
+ status = Status.new
155
+ project = Project.new(:name => "mongomapper")
156
+ status.target = project
157
+ status.save.should be_true
158
+
159
+ from_db = Status.find(status.id)
160
+ from_db.target = nil
161
+ from_db.target_type.should be_nil
162
+ from_db.target_id.should be_nil
163
+ from_db.target.should be_nil
164
+ end
165
+ end
166
+
167
+ context "Belongs To" do
168
+ should "default to nil" do
169
+ status = Status.new
170
+ status.project.should be_nil
171
+ end
172
+
173
+ should "store the association" do
174
+ status = Status.new
175
+ project = Project.new(:name => "mongomapper")
176
+ status.project = project
177
+ status.save.should be_true
178
+
179
+ from_db = Status.find(status.id)
180
+ from_db.project.should_not be_nil
181
+ from_db.project.name.should == "mongomapper"
182
+ end
183
+
184
+ should "unset the association" do
185
+ status = Status.new
186
+ project = Project.new(:name => "mongomapper")
187
+ status.project = project
188
+ status.save.should be_true
189
+
190
+ from_db = Status.find(status.id)
191
+ from_db.project = nil
192
+ from_db.project.should be_nil
193
+ end
194
+ end
195
+
196
+ context "Many documents" do
197
+ should "default reader to empty array" do
198
+ project = Project.new
199
+ project.statuses.should == []
200
+ end
201
+
202
+ should "allow adding to association like it was an array" do
203
+ project = Project.new
204
+ project.statuses << Status.new
205
+ project.statuses.push Status.new
206
+ project.statuses.size.should == 2
207
+ end
208
+
209
+ should "store the association" do
210
+ project = Project.new
211
+ project.statuses = [Status.new("name" => "ready")]
212
+ project.save.should be_true
213
+
214
+ from_db = Project.find(project.id)
215
+ from_db.statuses.size.should == 1
216
+ from_db.statuses[0].name.should == "ready"
217
+ end
218
+
219
+ context "Finding scoped to association" do
220
+ setup do
221
+ @project1 = Project.new(:name => 'Project 1')
222
+ @brand_new = Status.create(:name => 'New')
223
+ @complete = Status.create(:name => 'Complete')
224
+ @project1.statuses = [@brand_new, @complete]
225
+ @project1.save
226
+
227
+ @project2 = Project.create(:name => 'Project 2')
228
+ @in_progress = Status.create(:name => 'In Progress')
229
+ @archived = Status.create(:name => 'Archived')
230
+ @another_complete = Status.create(:name => 'Complete')
231
+ @project2.statuses = [@in_progress, @archived, @another_complete]
232
+ @project2.save
233
+ end
234
+
235
+ context "with :all" do
236
+ should "work" do
237
+ @project1.statuses.find(:all).should == [@brand_new, @complete]
238
+ end
239
+
240
+ should "work with conditions" do
241
+ statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
242
+ statuses.should == [@complete]
243
+ end
244
+
245
+ should "work with order" do
246
+ statuses = @project1.statuses.find(:all, :order => 'name asc')
247
+ statuses.should == [@complete, @brand_new]
248
+ end
249
+ end
250
+
251
+ context "with #all" do
252
+ should "work" do
253
+ @project1.statuses.all.should == [@brand_new, @complete]
254
+ end
255
+
256
+ should "work with conditions" do
257
+ statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
258
+ statuses.should == [@complete]
259
+ end
260
+
261
+ should "work with order" do
262
+ statuses = @project1.statuses.all(:order => 'name asc')
263
+ statuses.should == [@complete, @brand_new]
264
+ end
265
+ end
266
+
267
+ context "with :first" do
268
+ should "work" do
269
+ @project1.statuses.find(:first).should == @brand_new
270
+ end
271
+
272
+ should "work with conditions" do
273
+ status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
274
+ status.should == @complete
275
+ end
276
+ end
277
+
278
+ context "with #first" do
279
+ should "work" do
280
+ @project1.statuses.first.should == @brand_new
281
+ end
282
+
283
+ should "work with conditions" do
284
+ status = @project1.statuses.first(:conditions => {:name => 'Complete'})
285
+ status.should == @complete
286
+ end
287
+ end
288
+
289
+ context "with :last" do
290
+ should "work" do
291
+ @project1.statuses.find(:last).should == @complete
292
+ end
293
+
294
+ should "work with conditions" do
295
+ status = @project1.statuses.find(:last, :conditions => {:name => 'New'})
296
+ status.should == @brand_new
297
+ end
298
+ end
299
+
300
+ context "with #last" do
301
+ should "work" do
302
+ @project1.statuses.last.should == @complete
303
+ end
304
+
305
+ should "work with conditions" do
306
+ status = @project1.statuses.last(:conditions => {:name => 'New'})
307
+ status.should == @brand_new
308
+ end
309
+ end
310
+
311
+ context "with one id" do
312
+ should "work for id in association" do
313
+ @project1.statuses.find(@complete.id).should == @complete
314
+ end
315
+
316
+ should "not work for id not in association" do
317
+ lambda {
318
+ @project1.statuses.find(@archived.id)
319
+ }.should raise_error(MongoMapper::DocumentNotFound)
320
+ end
321
+ end
322
+
323
+ context "with multiple ids" do
324
+ should "work for ids in association" do
325
+ statuses = @project1.statuses.find(@brand_new.id, @complete.id)
326
+ statuses.should == [@brand_new, @complete]
327
+ end
328
+
329
+ should "not work for ids not in association" do
330
+ lambda {
331
+ @project1.statuses.find(@brand_new.id, @complete.id, @archived.id)
332
+ }.should raise_error(MongoMapper::DocumentNotFound)
333
+ end
334
+ end
335
+
336
+ context "with #paginate" do
337
+ setup do
338
+ @statuses = @project2.statuses.paginate(:per_page => 2, :page => 1)
339
+ end
340
+
341
+ should "return total pages" do
342
+ @statuses.total_pages.should == 2
343
+ end
344
+
345
+ should "return total entries" do
346
+ @statuses.total_entries.should == 3
347
+ end
348
+
349
+ should "return the subject" do
350
+ @statuses.should == [@in_progress, @archived]
351
+ end
352
+ end
353
+ end
354
+ end
355
+
356
+ context "Many embedded documents" do
357
+ should "allow adding to association like it was an array" do
358
+ project = Project.new
359
+ project.addresses << Address.new
360
+ project.addresses.push Address.new
361
+ project.addresses.size.should == 2
362
+ end
363
+
364
+ should "be embedded in document on save" do
365
+ sb = Address.new(:city => 'South Bend', :state => 'IN')
366
+ chi = Address.new(:city => 'Chicago', :state => 'IL')
367
+ project = Project.new
368
+ project.addresses << sb
369
+ project.addresses << chi
370
+ project.save
371
+
372
+ from_db = Project.find(project.id)
373
+ from_db.addresses.size.should == 2
374
+ from_db.addresses[0].should == sb
375
+ from_db.addresses[1].should == chi
376
+ end
377
+
378
+ should "allow embedding arbitrarily deep" do
379
+ @document = Class.new do
380
+ include MongoMapper::Document
381
+ key :person, Person
382
+ end
383
+ @document.collection.clear
384
+
385
+ meg = Person.new(:name => "Meg")
386
+ meg.child = Person.new(:name => "Steve")
387
+ meg.child.child = Person.new(:name => "Linda")
388
+
389
+ doc = @document.new(:person => meg)
390
+ doc.save
391
+
392
+ from_db = @document.find(doc.id)
393
+ from_db.person.name.should == 'Meg'
394
+ from_db.person.child.name.should == 'Steve'
395
+ from_db.person.child.child.name.should == 'Linda'
396
+ end
397
+
398
+ should "allow assignment of 'many' embedded documents using a hash" do
399
+ person_attributes = {
400
+ "name" => "Mr. Pet Lover",
401
+ "pets" => [
402
+ {"name" => "Jimmy", "species" => "Cocker Spainel"},
403
+ {"name" => "Sasha", "species" => "Siberian Husky"},
404
+ ]
405
+ }
406
+
407
+ pet_lover = RealPerson.new(person_attributes)
408
+ pet_lover.name.should == "Mr. Pet Lover"
409
+ pet_lover.pets[0].name.should == "Jimmy"
410
+ pet_lover.pets[0].species.should == "Cocker Spainel"
411
+ pet_lover.pets[1].name.should == "Sasha"
412
+ pet_lover.pets[1].species.should == "Siberian Husky"
413
+ pet_lover.save.should be_true
414
+
415
+ from_db = RealPerson.find(pet_lover.id)
416
+ from_db.name.should == "Mr. Pet Lover"
417
+ from_db.pets[0].name.should == "Jimmy"
418
+ from_db.pets[0].species.should == "Cocker Spainel"
419
+ from_db.pets[1].name.should == "Sasha"
420
+ from_db.pets[1].species.should == "Siberian Husky"
421
+ end
422
+
423
+ should "allow saving embedded documents in 'many' embedded documents" do
424
+ @document = Class.new do
425
+ include MongoMapper::Document
426
+ many :people
427
+ end
428
+ @document.collection.clear
429
+
430
+ meg = Person.new(:name => "Meg")
431
+ sparky = Pet.new(:name => "Sparky", :species => "Dog")
432
+ koda = Pet.new(:name => "Koda", :species => "Dog")
433
+
434
+ doc = @document.new
435
+
436
+ meg.pets << sparky
437
+ meg.pets << koda
438
+
439
+ doc.people << meg
440
+ doc.save
441
+
442
+ from_db = @document.find(doc.id)
443
+ from_db.people.first.name.should == "Meg"
444
+ from_db.people.first.pets.should_not == []
445
+ from_db.people.first.pets.first.name.should == "Sparky"
446
+ from_db.people.first.pets.first.species.should == "Dog"
447
+ from_db.people.first.pets[1].name.should == "Koda"
448
+ from_db.people.first.pets[1].species.should == "Dog"
449
+ end
450
+ end
451
+
452
+ context "Changing association class names" do
453
+ should "work for many and belongs to" do
454
+ class AwesomeUser
455
+ include MongoMapper::Document
456
+ many :posts, :class_name => 'AssociationsTest::AwesomePost', :foreign_key => :creator_id
457
+ end
458
+
459
+ class AwesomeTag
460
+ include MongoMapper::EmbeddedDocument
461
+ key :name, String
462
+ belongs_to :post, :class_name => 'AssociationsTest::AwesomeUser'
463
+ end
464
+
465
+ class AwesomePost
466
+ include MongoMapper::Document
467
+ belongs_to :creator, :class_name => 'AssociationsTest::AwesomeUser'
468
+ many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
469
+ end
470
+
471
+ AwesomeUser.collection.clear
472
+ AwesomePost.collection.clear
473
+
474
+ user = AwesomeUser.create
475
+ tag1 = AwesomeTag.new(:name => 'awesome')
476
+ tag2 = AwesomeTag.new(:name => 'grand')
477
+ post1 = AwesomePost.create(:creator => user, :tags => [tag1])
478
+ post2 = AwesomePost.create(:creator => user, :tags => [tag2])
479
+ user.posts.should == [post1, post2]
480
+
481
+ post1_from_db = AwesomePost.find(post1.id)
482
+ post1_from_db.tags.should == [tag1]
483
+ end
484
+ end
485
+ end