mongo_mapper-unstable 2009.10.11

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 (73) hide show
  1. data/.gitignore +8 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +50 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +55 -0
  7. data/lib/mongo_mapper/associations/base.rb +83 -0
  8. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  9. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  10. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
  11. data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
  12. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  13. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
  14. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  15. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  16. data/lib/mongo_mapper/associations/proxy.rb +74 -0
  17. data/lib/mongo_mapper/associations.rb +86 -0
  18. data/lib/mongo_mapper/callbacks.rb +106 -0
  19. data/lib/mongo_mapper/dirty.rb +137 -0
  20. data/lib/mongo_mapper/document.rb +340 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  22. data/lib/mongo_mapper/embedded_document.rb +355 -0
  23. data/lib/mongo_mapper/finder_options.rb +98 -0
  24. data/lib/mongo_mapper/key.rb +36 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +51 -0
  27. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  29. data/lib/mongo_mapper/save_with_validation.rb +19 -0
  30. data/lib/mongo_mapper/serialization.rb +55 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +161 -0
  33. data/lib/mongo_mapper/validations.rb +69 -0
  34. data/lib/mongo_mapper.rb +111 -0
  35. data/mongo_mapper.gemspec +162 -0
  36. data/specs.watchr +32 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/custom_matchers.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  40. data/test/functional/associations/test_belongs_to_proxy.rb +49 -0
  41. data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
  45. data/test/functional/associations/test_many_proxy.rb +331 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +18 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +138 -0
  50. data/test/functional/test_document.rb +1051 -0
  51. data/test/functional/test_embedded_document.rb +97 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_pagination.rb +87 -0
  54. data/test/functional/test_rails_compatibility.rb +30 -0
  55. data/test/functional/test_validations.rb +279 -0
  56. data/test/models.rb +195 -0
  57. data/test/test_helper.rb +30 -0
  58. data/test/unit/serializers/test_json_serializer.rb +189 -0
  59. data/test/unit/test_association_base.rb +144 -0
  60. data/test/unit/test_document.rb +184 -0
  61. data/test/unit/test_dynamic_finder.rb +125 -0
  62. data/test/unit/test_embedded_document.rb +656 -0
  63. data/test/unit/test_finder_options.rb +261 -0
  64. data/test/unit/test_key.rb +172 -0
  65. data/test/unit/test_mongomapper.rb +28 -0
  66. data/test/unit/test_observing.rb +101 -0
  67. data/test/unit/test_pagination.rb +109 -0
  68. data/test/unit/test_rails_compatibility.rb +39 -0
  69. data/test/unit/test_serializations.rb +52 -0
  70. data/test/unit/test_support.rb +291 -0
  71. data/test/unit/test_time_zones.rb +40 -0
  72. data/test/unit/test_validations.rb +503 -0
  73. metadata +210 -0
@@ -0,0 +1,97 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class EmbeddedDocumentTest < Test::Unit::TestCase
5
+ def setup
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ end
13
+ @document.collection.clear
14
+ end
15
+
16
+ context "Saving a document with an embedded document" do
17
+ setup do
18
+ @document.class_eval do
19
+ key :foo, Address
20
+ end
21
+
22
+ @address = Address.new(:city => 'South Bend', :state => 'IN')
23
+ @doc = @document.new(:foo => @address)
24
+ end
25
+
26
+ should "embed embedded document" do
27
+ @doc.save
28
+ @doc.foo.city.should == 'South Bend'
29
+ @doc.foo.state.should == 'IN'
30
+
31
+ from_db = @document.find(@doc.id)
32
+ from_db.foo.city.should == 'South Bend'
33
+ from_db.foo.state.should == 'IN'
34
+ end
35
+ end
36
+
37
+ context "new?" do
38
+ setup do
39
+ @document.class_eval do
40
+ key :foo, Address
41
+ end
42
+ end
43
+
44
+ should "be new until document is saved" do
45
+ address = Address.new(:city => 'South Bend', :state => 'IN')
46
+ doc = @document.new(:foo => address)
47
+ address.new?.should == true
48
+ end
49
+
50
+ should "not be new after document is saved" do
51
+ address = Address.new(:city => 'South Bend', :state => 'IN')
52
+ doc = @document.new(:foo => address)
53
+ doc.save
54
+ doc.foo.new?.should == false
55
+ end
56
+
57
+ should "not be new when document is read back" do
58
+ address = Address.new(:city => 'South Bend', :state => 'IN')
59
+ doc = @document.new(:foo => address)
60
+ doc.save
61
+ read_doc = @document.find(doc.id)
62
+ read_doc.foo.new?.should == false
63
+ end
64
+ end
65
+
66
+ context "save" do
67
+ should "save the root document" do
68
+ person = RealPerson.create
69
+
70
+ pet = Pet.new :name => 'sparky'
71
+ person.pets << pet
72
+ pet.save
73
+
74
+ doc = RealPerson.find(person.id)
75
+ doc.pets.first.should == pet
76
+ end
77
+ end
78
+
79
+ context "update_attributes" do
80
+ should "save the root document" do
81
+ person = RealPerson.create
82
+
83
+ pet = Pet.new(:name => 'sparky')
84
+ person.pets << pet
85
+ pet.save
86
+
87
+ doc = RealPerson.find(person.id)
88
+ pet = doc.pets.first
89
+ pet.update_attributes :name => 'koda'
90
+
91
+ doc = RealPerson.find(person.id)
92
+ embedded = doc.pets.first
93
+ embedded.id.should == pet.id
94
+ embedded.name.should == 'koda'
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class LoggerTest < Test::Unit::TestCase
4
+ context "with connection that has logger" do
5
+ setup do
6
+ @output = StringIO.new
7
+ @logger = Logger.new(@output)
8
+ MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, :logger => @logger)
9
+ end
10
+
11
+ should "be able to get access to that logger" do
12
+ MongoMapper.logger.should == @logger
13
+ end
14
+
15
+ should "be able to log messages" do
16
+ MongoMapper.logger.debug 'testing'
17
+ @output.string.include?('testing').should be_true
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,87 @@
1
+ require 'test_helper'
2
+
3
+ class PaginationTest < Test::Unit::TestCase
4
+ context "Paginating" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ key :age, Integer
13
+
14
+ def self.per_page; 1 end
15
+ end
16
+ @document.collection.clear
17
+
18
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
19
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
20
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
21
+ end
22
+
23
+ should "return the total pages" do
24
+ result = @document.paginate(:per_page => 2, :page => 1)
25
+ result.total_pages.should == 2
26
+ end
27
+
28
+ should "return the total pages when defaulting to the document class per_page" do
29
+ result = @document.paginate(:page => 1)
30
+ result.total_pages.should == 3
31
+ end
32
+
33
+ should "return the total of records" do
34
+ result = @document.paginate(:per_page => 2, :page => 1)
35
+ result.total_entries.should == 3
36
+ end
37
+
38
+ should "return the items" do
39
+ result = @document.paginate(:per_page => 2, :page => 1, :order => 'first_name')
40
+ result.size.should == 2
41
+ result.should == [@doc1, @doc3]
42
+ end
43
+
44
+ should "accept conditions" do
45
+ result = @document.paginate({
46
+ :conditions => {:last_name => 'Nunemaker'},
47
+ :order => "age DESC",
48
+ :per_page => 2,
49
+ :page => 1,
50
+ })
51
+ result.should == [@doc1, @doc3]
52
+ result.first.age.should == 27
53
+ end
54
+
55
+ should "withstand rigor" do
56
+ result = @document.paginate({
57
+ :per_page => 1,
58
+ :page => 1,
59
+ :order => 'age desc',
60
+ :conditions => {:last_name => 'Nunemaker'}
61
+ })
62
+ result.should == [@doc1]
63
+ result.total_entries.should == 2
64
+ result.total_pages.should == 2
65
+
66
+ result = @document.paginate({
67
+ :per_page => 1,
68
+ :page => 2,
69
+ :order => 'age desc',
70
+ :conditions => {:last_name => 'Nunemaker'}
71
+ })
72
+ result.should == [@doc3]
73
+ result.total_entries.should == 2
74
+ result.total_pages.should == 2
75
+
76
+ result = @document.paginate({
77
+ :per_page => 2,
78
+ :page => 1,
79
+ :order => 'age desc',
80
+ :conditions => {:last_name => 'Nunemaker'}
81
+ })
82
+ result.should == [@doc1, @doc3]
83
+ result.total_entries.should == 2
84
+ result.total_pages.should == 1
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ class TestRailsCompatibility < Test::Unit::TestCase
4
+ class Item
5
+ include MongoMapper::EmbeddedDocument
6
+ key :for_all, String
7
+ end
8
+
9
+ class Order
10
+ include MongoMapper::Document
11
+ many :items, :class_name => 'TestRailsCompatibility::Item'
12
+ key :order_only, String
13
+ end
14
+
15
+ context "Document" do
16
+ setup do
17
+ Order.collection.clear
18
+ end
19
+
20
+ should "have to_param that returns id" do
21
+ instance = Order.create('_id' => 1234)
22
+ instance.to_param.should == '1234'
23
+ end
24
+
25
+ should "alias new to new_record?" do
26
+ instance = Order.new
27
+ instance.new_record?.should == instance.new?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,279 @@
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 = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'test'
9
+ key :name, String, :required => true
10
+ end
11
+ @document.collection.clear
12
+ end
13
+
14
+ should "not insert document" do
15
+ doc = @document.new
16
+ doc.save
17
+ @document.count.should == 0
18
+ end
19
+
20
+ should "populate document's errors" do
21
+ doc = @document.new
22
+ doc.errors.size.should == 0
23
+ doc.save
24
+ doc.errors.full_messages.should == ["Name can't be empty"]
25
+ end
26
+ end
27
+
28
+ context "Saving a document that is invalid (destructive)" do
29
+ setup do
30
+ @document = Class.new do
31
+ include MongoMapper::Document
32
+ set_collection_name 'test'
33
+ key :name, String, :required => true
34
+ end
35
+ @document.collection.clear
36
+ end
37
+
38
+ should "raise error" do
39
+ doc = @document.new
40
+ lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
41
+ end
42
+ end
43
+
44
+ context "Saving an existing document that is invalid" do
45
+ setup do
46
+ @document = Class.new do
47
+ include MongoMapper::Document
48
+ set_collection_name 'test'
49
+ key :name, String, :required => true
50
+ end
51
+ @document.collection.clear
52
+
53
+ @doc = @document.create(:name => 'John Nunemaker')
54
+ end
55
+
56
+ should "not update document" do
57
+ @doc.name = nil
58
+ @doc.save
59
+ @document.find(@doc.id).name.should == 'John Nunemaker'
60
+ end
61
+
62
+ should "populate document's errors" do
63
+ @doc.name = nil
64
+ @doc.save
65
+ @doc.errors.full_messages.should == ["Name can't be empty"]
66
+ end
67
+ end
68
+
69
+ context "Adding validation errors" do
70
+ setup do
71
+ @document = Class.new do
72
+ include MongoMapper::Document
73
+ set_collection_name 'test'
74
+
75
+ key :action, String
76
+ def action_present
77
+ errors.add(:action, 'is invalid') if action.blank?
78
+ end
79
+ end
80
+ @document.collection.clear
81
+ end
82
+
83
+ should "work with validate_on_create callback" do
84
+ @document.validate_on_create :action_present
85
+
86
+ doc = @document.new
87
+ doc.action = nil
88
+ doc.should have_error_on(:action)
89
+
90
+ doc.action = 'kick'
91
+ doc.should_not have_error_on(:action)
92
+ doc.save
93
+
94
+ doc.action = nil
95
+ doc.should_not have_error_on(:action)
96
+ end
97
+
98
+ should "work with validate_on_update callback" do
99
+ @document.validate_on_update :action_present
100
+
101
+ doc = @document.new
102
+ doc.action = nil
103
+ doc.should_not have_error_on(:action)
104
+ doc.save
105
+
106
+ doc.action = nil
107
+ doc.should have_error_on(:action)
108
+
109
+ doc.action = 'kick'
110
+ doc.should_not have_error_on(:action)
111
+ end
112
+ end
113
+
114
+ context "validating uniqueness of" do
115
+ setup do
116
+ @document = Class.new do
117
+ include MongoMapper::Document
118
+ set_collection_name 'test'
119
+
120
+ key :name, String
121
+ validates_uniqueness_of :name
122
+ end
123
+ @document.collection.clear
124
+ end
125
+
126
+ should "not fail if object is new" do
127
+ doc = @document.new
128
+ doc.should_not have_error_on(:name)
129
+ end
130
+
131
+ should "not fail when new object is out of scope" do
132
+ document = Class.new do
133
+ include MongoMapper::Document
134
+ set_collection_name 'test'
135
+
136
+ key :name
137
+ key :adult
138
+ validates_uniqueness_of :name, :scope => :adult
139
+ end
140
+ doc = document.new("name" => "joe", :adult => true)
141
+ doc.save.should be_true
142
+
143
+ doc2 = document.new("name" => "joe", :adult => false)
144
+ doc2.should be_valid
145
+
146
+ end
147
+
148
+ should "allow to update an object" do
149
+ doc = @document.new("name" => "joe")
150
+ doc.save.should be_true
151
+
152
+ @document \
153
+ .stubs(:find) \
154
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
155
+ .returns(doc)
156
+
157
+ doc.name = "joe"
158
+ doc.valid?.should be_true
159
+ doc.should_not have_error_on(:name)
160
+ end
161
+
162
+ should "fail if object name is not unique" do
163
+ doc = @document.new("name" => "joe")
164
+ doc.save.should be_true
165
+
166
+ @document \
167
+ .stubs(:find) \
168
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
169
+ .returns(doc)
170
+
171
+ doc2 = @document.new("name" => "joe")
172
+ doc2.should have_error_on(:name)
173
+ end
174
+
175
+ context "scoped by a single attribute" do
176
+ setup do
177
+ @document = Class.new do
178
+ include MongoMapper::Document
179
+ set_collection_name 'test'
180
+
181
+ key :name, String
182
+ key :scope, String
183
+ validates_uniqueness_of :name, :scope => :scope
184
+ end
185
+ @document.collection.clear
186
+ end
187
+
188
+ should "fail if the same name exists in the scope" do
189
+ doc = @document.new("name" => "joe", "scope" => "one")
190
+ doc.save.should be_true
191
+
192
+ @document \
193
+ .stubs(:find) \
194
+ .with(:first, :conditions => {:name => 'joe', :scope => "one"}, :limit => 1) \
195
+ .returns(doc)
196
+
197
+ doc2 = @document.new("name" => "joe", "scope" => "one")
198
+ doc2.should have_error_on(:name)
199
+ end
200
+
201
+ should "pass if the same name exists in a different scope" do
202
+ doc = @document.new("name" => "joe", "scope" => "one")
203
+ doc.save.should be_true
204
+
205
+ @document \
206
+ .stubs(:find) \
207
+ .with(:first, :conditions => {:name => 'joe', :scope => "two"}, :limit => 1) \
208
+ .returns(nil)
209
+
210
+ doc2 = @document.new("name" => "joe", "scope" => "two")
211
+ doc2.should_not have_error_on(:name)
212
+ end
213
+ end
214
+
215
+ context "scoped by a multiple attributes" do
216
+ setup do
217
+ @document = Class.new do
218
+ include MongoMapper::Document
219
+ set_collection_name 'test'
220
+
221
+ key :name, String
222
+ key :first_scope, String
223
+ key :second_scope, String
224
+ validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
225
+ end
226
+ @document.collection.clear
227
+ end
228
+
229
+ should "fail if the same name exists in the scope" do
230
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
231
+ doc.save.should be_true
232
+
233
+ @document \
234
+ .stubs(:find) \
235
+ .with(:first, :conditions => {:name => 'joe', :first_scope => "one", :second_scope => "two"}, :limit => 1) \
236
+ .returns(doc)
237
+
238
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
239
+ doc2.should have_error_on(:name)
240
+ end
241
+
242
+ should "pass if the same name exists in a different scope" do
243
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
244
+ doc.save.should be_true
245
+
246
+ @document \
247
+ .stubs(:find) \
248
+ .with(:first, :conditions => {:name => 'joe', :first_scope => "one", :second_scope => "one"}, :limit => 1) \
249
+ .returns(nil)
250
+
251
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
252
+ doc2.should_not have_error_on(:name)
253
+ end
254
+ end
255
+ end
256
+
257
+ context "validates uniqueness of with :unique shortcut" do
258
+ should "work" do
259
+ @document = Class.new do
260
+ include MongoMapper::Document
261
+ set_collection_name 'test'
262
+
263
+ key :name, String, :unique => true
264
+ end
265
+ @document.collection.clear
266
+
267
+ doc = @document.create(:name => 'John')
268
+ doc.should_not have_error_on(:name)
269
+
270
+ @document \
271
+ .stubs(:find) \
272
+ .with(:first, :conditions => {:name => 'John'}, :limit => 1) \
273
+ .returns(doc)
274
+
275
+ second_john = @document.create(:name => 'John')
276
+ second_john.should have_error_on(:name, 'has already been taken')
277
+ end
278
+ end
279
+ end
data/test/models.rb ADDED
@@ -0,0 +1,195 @@
1
+ # custom type
2
+ class WindowSize
3
+ attr_reader :width, :height
4
+
5
+ def self.to_mongo(value)
6
+ value.to_a
7
+ end
8
+
9
+ def self.from_mongo(value)
10
+ value.is_a?(self) ? value : WindowSize.new(value)
11
+ end
12
+
13
+ def initialize(*args)
14
+ @width, @height = args.flatten
15
+ end
16
+
17
+ def to_a
18
+ [width, height]
19
+ end
20
+
21
+ def ==(other)
22
+ other.is_a?(self.class) && other.width == width && other.height == height
23
+ end
24
+ end
25
+
26
+
27
+ class Post
28
+ include MongoMapper::Document
29
+
30
+ key :title, String
31
+ key :body, String
32
+
33
+ has_many :comments, :as => :commentable, :class_name => 'PostComment'
34
+
35
+ timestamps!
36
+ end
37
+
38
+ class PostComment
39
+ include MongoMapper::Document
40
+
41
+ key :username, String, :default => 'Anonymous'
42
+ key :body, String
43
+
44
+ key :commentable_id, String
45
+ key :commentable_type, String
46
+ belongs_to :commentable, :polymorphic => true
47
+
48
+ timestamps!
49
+ end
50
+
51
+ class Address
52
+ include MongoMapper::EmbeddedDocument
53
+
54
+ key :address, String
55
+ key :city, String
56
+ key :state, String
57
+ key :zip, Integer
58
+ end
59
+
60
+ class Message
61
+ include MongoMapper::Document
62
+
63
+ key :body, String
64
+ key :position, Integer
65
+ key :_type, String
66
+ key :room_id, String
67
+
68
+ belongs_to :room
69
+ end
70
+
71
+ class Answer
72
+ include MongoMapper::Document
73
+
74
+ key :body, String
75
+ end
76
+
77
+ class Enter < Message; end
78
+ class Exit < Message; end
79
+ class Chat < Message; end
80
+
81
+ class Room
82
+ include MongoMapper::Document
83
+
84
+ key :name, String
85
+ many :messages, :polymorphic => true
86
+ end
87
+
88
+ class Project
89
+ include MongoMapper::Document
90
+
91
+ key :name, String
92
+ many :statuses
93
+ many :addresses
94
+ end
95
+
96
+ class Status
97
+ include MongoMapper::Document
98
+
99
+ key :project_id, String
100
+ key :target_id, String
101
+ key :target_type, String
102
+ key :name, String
103
+ key :position, Integer
104
+
105
+ belongs_to :project
106
+ belongs_to :target, :polymorphic => true
107
+ end
108
+
109
+ class RealPerson
110
+ include MongoMapper::Document
111
+
112
+ many :pets
113
+ key :name, String
114
+
115
+ def realname=(n)
116
+ self.name = n
117
+ end
118
+ end
119
+
120
+ class Person
121
+ include MongoMapper::EmbeddedDocument
122
+
123
+ key :name, String
124
+ key :child, Person
125
+
126
+ many :pets
127
+ end
128
+
129
+ class Pet
130
+ include MongoMapper::EmbeddedDocument
131
+
132
+ key :name, String
133
+ key :species, String
134
+ end
135
+
136
+ class Media
137
+ include MongoMapper::EmbeddedDocument
138
+
139
+ key :_type, String
140
+ key :file, String
141
+ end
142
+
143
+ class Video < Media
144
+ key :length, Integer
145
+ end
146
+
147
+ class Image < Media
148
+ key :width, Integer
149
+ key :height, Integer
150
+ end
151
+
152
+ class Music < Media
153
+ key :bitrate, String
154
+ end
155
+
156
+ class Catalog
157
+ include MongoMapper::Document
158
+
159
+ many :medias, :polymorphic => true
160
+ end
161
+
162
+ module TrModels
163
+ class Transport
164
+ include MongoMapper::EmbeddedDocument
165
+
166
+ key :_type, String
167
+ key :license_plate, String
168
+ end
169
+
170
+ class Car < TrModels::Transport
171
+ include MongoMapper::EmbeddedDocument
172
+
173
+ key :model, String
174
+ key :year, Integer
175
+ end
176
+
177
+ class Bus < TrModels::Transport
178
+ include MongoMapper::EmbeddedDocument
179
+
180
+ key :max_passengers, Integer
181
+ end
182
+
183
+ class Ambulance < TrModels::Transport
184
+ include MongoMapper::EmbeddedDocument
185
+
186
+ key :icu, Boolean
187
+ end
188
+
189
+ class Fleet
190
+ include MongoMapper::Document
191
+
192
+ many :transports, :polymorphic => true, :class_name => "TrModels::Transport"
193
+ key :name, String
194
+ end
195
+ end