fcoury-mongomapper 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.5
1
+ 0.4.0
@@ -2,33 +2,12 @@ module MongoMapper
2
2
  module Associations
3
3
  module ClassMethods
4
4
  def belongs_to(association_id, options = {})
5
- association = create_association(:belongs_to, association_id, options)
6
-
7
- ref_id = "#{association_id}_id"
8
- key ref_id, String
9
-
10
- define_method("#{ref_id}=") do |value|
11
- write_attribute(ref_id, value)
12
- end
13
-
14
- if options[:polymorphic]
15
- ref_type = "#{association_id}_type"
16
- key ref_type, String
17
-
18
- define_method("#{ref_type}=") do |value|
19
- write_attribute(ref_type, value)
20
- end
21
- end
22
-
23
- define_association_methods(association)
24
-
5
+ create_association(:belongs_to, association_id, options)
25
6
  self
26
7
  end
27
8
 
28
9
  def many(association_id, options = {})
29
- association = create_association(:many, association_id, options)
30
- define_association_methods(association)
31
-
10
+ create_association(:many, association_id, options)
32
11
  self
33
12
  end
34
13
 
@@ -37,31 +16,47 @@ module MongoMapper
37
16
  end
38
17
 
39
18
  private
40
- def create_association(type, name, options)
41
- association = Associations::Base.new(type, name, options)
42
- associations[association.name] = association
43
- association
44
- end
45
-
46
- def define_association_methods(association)
47
- define_method(association.name) do
48
- get_proxy(association)
19
+ def create_association(type, name, options)
20
+ association = Associations::Base.new(type, name, options)
21
+ associations[association.name] = association
22
+ define_association_methods(association)
23
+ define_association_keys(association)
24
+ association
49
25
  end
50
26
 
51
- define_method("#{association.name}=") do |value|
52
- get_proxy(association).replace(value)
53
- value
27
+ def define_association_methods(association)
28
+ define_method(association.name) do
29
+ get_proxy(association)
30
+ end
31
+
32
+ define_method("#{association.name}=") do |value|
33
+ get_proxy(association).replace(value)
34
+ value
35
+ end
36
+ end
37
+
38
+ def define_association_keys(association)
39
+ if association.many?
40
+ if association.polymorphic?
41
+ association.klass.send :key, association.type_key_name, String
42
+ end
43
+ else
44
+ key "#{association.name}_id", String
45
+
46
+ if association.polymorphic?
47
+ key association.type_key_name, String
48
+ end
49
+ end
54
50
  end
55
- end
56
51
  end
57
52
 
58
53
  module InstanceMethods
59
54
  def get_proxy(association)
60
- proxy = self.instance_variable_get(association.ivar)
61
- if proxy.nil?
55
+ unless proxy = self.instance_variable_get(association.ivar)
62
56
  proxy = association.proxy_class.new(self, association)
63
57
  self.instance_variable_set(association.ivar, proxy)
64
58
  end
59
+
65
60
  proxy
66
61
  end
67
62
  end
@@ -10,45 +10,47 @@ module MongoMapper
10
10
  end
11
11
 
12
12
  def klass
13
- class_name.constantize
13
+ @klass ||= class_name.constantize
14
14
  end
15
15
 
16
16
  def class_name
17
17
  @class_name ||= begin
18
18
  if cn = options[:class_name]
19
19
  cn
20
- elsif @type == :many
20
+ elsif many?
21
21
  name.to_s.singularize.camelize
22
22
  else
23
23
  name.to_s.camelize
24
24
  end
25
25
  end
26
26
  end
27
-
27
+
28
+ def many?
29
+ @many_type ||= @type == :many
30
+ end
31
+
32
+ def polymorphic?
33
+ @options[:polymorphic]
34
+ end
35
+
36
+ def type_key_name
37
+ @type_key_name ||= many? ? '_type' : "#{name}_type"
38
+ end
39
+
28
40
  def ivar
29
41
  @ivar ||= "@_#{name}"
30
42
  end
31
43
 
32
44
  def proxy_class
33
- case @type
34
- when :belongs_to
35
- if @options[:polymorphic]
36
- PolymorphicBelongsToProxy
37
- else
38
- BelongsToProxy
39
- end
40
- when :many
41
- if self.klass.embeddable?
42
- if @options[:polymorphic]
43
- PolymorphicHasManyEmbeddedProxy
44
- else
45
- HasManyEmbeddedProxy
46
- end
47
- else
48
- HasManyProxy
49
- end
50
- end
51
- end
45
+ @proxy_class ||= begin
46
+ if many?
47
+ return HasManyProxy unless self.klass.embeddable?
48
+ polymorphic? ? PolymorphicHasManyEmbeddedProxy : HasManyEmbeddedProxy
49
+ else
50
+ polymorphic? ? PolymorphicBelongsToProxy : BelongsToProxy
51
+ end
52
+ end # end begin
53
+ end # end proxy_class
52
54
  end
53
55
  end
54
56
  end
@@ -9,11 +9,11 @@ module MongoMapper
9
9
  end
10
10
 
11
11
  protected
12
- def find_target
13
- (@_values || []).map do |e|
14
- @association.klass.new(e)
12
+ def find_target
13
+ (@_values || []).map do |e|
14
+ @association.klass.new(e)
15
+ end
15
16
  end
16
- end
17
17
  end
18
18
  end
19
19
  end
@@ -12,17 +12,18 @@ module MongoMapper
12
12
  o.save
13
13
  o
14
14
  end
15
+
15
16
  reload_target
16
17
  end
17
18
 
18
19
  protected
19
- def find_target
20
- @association.klass.find(:all, {:conditions => {self.foreign_key => @owner.id}})
21
- end
20
+ def find_target
21
+ @association.klass.find(:all, {:conditions => {self.foreign_key => @owner.id}})
22
+ end
22
23
 
23
- def foreign_key
24
- @association.options[:foreign_key] || @owner.class.name.underscore.gsub("/", "_") + "_id"
25
- end
24
+ def foreign_key
25
+ @association.options[:foreign_key] || @owner.class.name.underscore.gsub("/", "_") + "_id"
26
+ end
26
27
  end
27
28
  end
28
29
  end
@@ -19,13 +19,13 @@ module MongoMapper
19
19
  end
20
20
 
21
21
  protected
22
- def find_target
23
- ref_id = @owner.__send__(:read_attribute, "#{@association.name}_id")
24
- ref_type = @owner.__send__(:read_attribute, "#{@association.name}_type")
25
- if ref_id && ref_type
26
- ref_type.constantize.find(ref_id)
22
+ def find_target
23
+ ref_id = @owner.__send__(:read_attribute, "#{@association.name}_id")
24
+ ref_type = @owner.__send__(:read_attribute, "#{@association.name}_type")
25
+ if ref_id && ref_type
26
+ ref_type.constantize.find(ref_id)
27
+ end
27
28
  end
28
- end
29
29
  end
30
30
  end
31
31
  end
@@ -1,40 +1,47 @@
1
1
  module MongoMapper
2
2
  module Associations
3
- class PolymorphicHasManyEmbeddedProxy < Proxy
3
+ class PolymorphicHasManyEmbeddedProxy < ArrayProxy
4
4
  def replace(v)
5
- @_values = v.map do |e|
6
- ref_type = "#{@association.name}_type"
7
- if e.kind_of?(EmbeddedDocument)
8
- e.class.send(:key, ref_type, String)
9
- {ref_type => e.class.name}.merge(e.attributes)
5
+ @_values = v.map do |doc_or_hash|
6
+ if doc_or_hash.kind_of?(EmbeddedDocument)
7
+ doc = doc_or_hash
8
+ {@association.type_key_name => doc.class.name}.merge(doc.attributes)
10
9
  else
11
- e
10
+ doc_or_hash
12
11
  end
13
12
  end
14
13
 
15
14
  @target = nil
16
-
17
15
  reload_target
18
16
  end
17
+
18
+ def <<(*docs)
19
+ load_target if @owner.new?
20
+
21
+ flatten_deeper(docs).each do |doc|
22
+ doc.send("#{@association.type_key_name}=", doc.class)
23
+ @target << doc
24
+ end
25
+
26
+ self
27
+ end
28
+ alias_method :push, :<<
29
+ alias_method :concat, :<<
19
30
 
20
31
  protected
21
- def find_target
22
- (@_values || []).map do |e|
23
- ref_type = "#{@association.name}_type"
24
- class_name = e[ref_type]
25
- if class_name
26
- current = Kernel
27
- parts = class_name.split("::")
28
- parts.each do |p|
29
- current = current.const_get(p)
30
- end
31
- klass = current
32
+ def find_target
33
+ (@_values || []).map do |hash|
34
+ polymorphic_class(hash).new(hash)
35
+ end
36
+ end
37
+
38
+ def polymorphic_class(doc)
39
+ if class_name = doc[@association.type_key_name]
40
+ class_name.constantize
32
41
  else
33
42
  @association.klass
34
43
  end
35
- klass.new(e)
36
44
  end
37
- end
38
45
  end
39
46
  end
40
47
  end
@@ -10,7 +10,6 @@ module MongoMapper
10
10
  def initialize(owner, association)
11
11
  @owner= owner
12
12
  @association = association
13
-
14
13
  reset
15
14
  end
16
15
 
@@ -38,23 +37,31 @@ module MongoMapper
38
37
  end
39
38
 
40
39
  protected
41
- def method_missing(method, *args)
42
- if load_target
43
- if block_given?
44
- @target.send(method, *args) { |*block_args| yield(*block_args) }
45
- else
46
- @target.send(method, *args)
40
+ def method_missing(method, *args)
41
+ if load_target
42
+ if block_given?
43
+ @target.send(method, *args) { |*block_args| yield(*block_args) }
44
+ else
45
+ @target.send(method, *args)
46
+ end
47
47
  end
48
48
  end
49
- end
50
49
 
51
- def load_target
52
- @target ||= find_target
53
- end
50
+ def load_target
51
+ @target ||= find_target
52
+ end
54
53
 
55
- def find_target
56
- raise NotImplementedError
57
- end
54
+ def find_target
55
+ raise NotImplementedError
56
+ end
57
+
58
+ # Array#flatten has problems with recursive arrays. Going one level
59
+ # deeper solves the majority of the problems.
60
+ def flatten_deeper(array)
61
+ array.collect do |element|
62
+ (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
63
+ end.flatten
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -11,19 +11,19 @@ module MongoMapper
11
11
  include SaveWithValidation
12
12
  include DocumentRailsCompatibility
13
13
  extend ClassMethods
14
-
14
+
15
15
  key :_id, String
16
16
  key :created_at, Time
17
17
  key :updated_at, Time
18
18
  end
19
-
19
+
20
20
  descendants << model
21
21
  end
22
22
 
23
23
  def self.descendants
24
24
  @descendants ||= Set.new
25
25
  end
26
-
26
+
27
27
  module ClassMethods
28
28
  def find(*args)
29
29
  options = args.extract_options!
@@ -19,6 +19,14 @@ module MongoMapper
19
19
  end
20
20
 
21
21
  module ClassMethods
22
+ def inherited(subclass)
23
+ (@subclasses ||= []) << subclass
24
+ end
25
+
26
+ def subclasses
27
+ @subclasses || []
28
+ end
29
+
22
30
  def keys
23
31
  @keys ||= if parent = parent_model
24
32
  parent.keys.dup
@@ -27,13 +35,24 @@ module MongoMapper
27
35
  end
28
36
  end
29
37
 
30
- def key(name, type, options={})
38
+ def key(name, type, options={})
31
39
  key = Key.new(name, type, options)
32
40
  keys[key.name] = key
41
+
42
+ add_to_subclasses(name, type, options)
33
43
  apply_validations_for(key)
34
44
  create_indexes_for(key)
45
+
35
46
  key
36
47
  end
48
+
49
+ def add_to_subclasses(name, type, options)
50
+ return if subclasses.blank?
51
+
52
+ subclasses.each do |subclass|
53
+ subclass.key name, type, options
54
+ end
55
+ end
37
56
 
38
57
  def ensure_index(name_or_array, options={})
39
58
  keys_to_index = if name_or_array.is_a?(Array)
@@ -148,7 +167,7 @@ module MongoMapper
148
167
 
149
168
  def method_missing(method, *args, &block)
150
169
  attribute = method.to_s
151
-
170
+
152
171
  if reader?(attribute)
153
172
  read_attribute(attribute)
154
173
  elsif writer?(attribute)
@@ -239,7 +258,7 @@ module MongoMapper
239
258
  def initialize_associations(attrs={})
240
259
  self.class.associations.each_pair do |name, association|
241
260
  if collection = attrs.delete(name)
242
- __send__("#{association.name}=", collection)
261
+ send("#{association.name}=", collection)
243
262
  end
244
263
  end
245
264
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{mongomapper}
5
- s.version = "0.3.5"
5
+ s.version = "0.4.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-21}
9
+ s.date = %q{2009-07-27}
10
10
  s.default_executable = %q{mmconsole}
11
11
  s.email = %q{nunemaker@gmail.com}
12
12
  s.executables = ["mmconsole"]
@@ -1,8 +1,7 @@
1
1
  require 'test_helper'
2
- require 'ruby-debug'
2
+
3
3
  class Address
4
4
  include MongoMapper::EmbeddedDocument
5
-
6
5
  key :address, String
7
6
  key :city, String
8
7
  key :state, String
@@ -11,19 +10,21 @@ end
11
10
 
12
11
  class Project
13
12
  include MongoMapper::Document
14
-
15
13
  key :name, String
16
-
17
14
  many :statuses
18
15
  many :addresses
19
16
  end
20
17
 
21
18
  class Status
22
19
  include MongoMapper::Document
23
-
24
20
  belongs_to :project
25
21
  belongs_to :target, :polymorphic => true
22
+ key :name, String
23
+ end
26
24
 
25
+ class RealPerson
26
+ include MongoMapper::Document
27
+ many :pets
27
28
  key :name, String
28
29
  end
29
30
 
@@ -31,13 +32,11 @@ class Person
31
32
  include MongoMapper::EmbeddedDocument
32
33
  key :name, String
33
34
  key :child, Person
34
-
35
35
  many :pets
36
36
  end
37
37
 
38
38
  class Pet
39
39
  include MongoMapper::EmbeddedDocument
40
-
41
40
  key :name, String
42
41
  key :species, String
43
42
  end
@@ -62,7 +61,6 @@ end
62
61
 
63
62
  class Catalog
64
63
  include MongoMapper::Document
65
-
66
64
  many :medias, :polymorphic => true
67
65
  end
68
66
 
@@ -91,7 +89,7 @@ module TrModels
91
89
  class Fleet
92
90
  include MongoMapper::Document
93
91
  many :transports, :polymorphic => true, :class_name => "TrModels::Transport"
94
- key :name, String
92
+ key :name, String
95
93
  end
96
94
  end
97
95
 
@@ -99,31 +97,70 @@ class AssociationsTest < Test::Unit::TestCase
99
97
  def setup
100
98
  Project.collection.clear
101
99
  Status.collection.clear
100
+ Catalog.collection.clear
101
+ TrModels::Fleet.collection.clear
102
102
  end
103
103
 
104
- context "Nested Polymorphic Many" do
104
+ context "Modularized Polymorphic Many Embedded" do
105
+ should "set associations correctly" do
106
+ fleet_attributes = {
107
+ "name" => "My Fleet",
108
+ "transports" => [
109
+ {"_type" => "TrModels::Ambulance", "license_plate" => "GGG123", "icu" => true},
110
+ {"_type" => "TrModels::Car", "license_plate" => "ABC123", "model" => "VW Golf", "year" => 2001},
111
+ {"_type" => "TrModels::Car", "license_plate" => "DEF123", "model" => "Honda Accord", "year" => 2008},
112
+ ]
113
+ }
114
+
115
+ fleet = TrModels::Fleet.new(fleet_attributes)
116
+ fleet.transports.size.should == 3
117
+ fleet.transports[0].class.should == TrModels::Ambulance
118
+ fleet.transports[0].license_plate.should == "GGG123"
119
+ fleet.transports[0].icu.should be_true
120
+ fleet.transports[1].class.should == TrModels::Car
121
+ fleet.transports[1].license_plate.should == "ABC123"
122
+ fleet.transports[1].model.should == "VW Golf"
123
+ fleet.transports[1].year.should == 2001
124
+ fleet.transports[2].class.should == TrModels::Car
125
+ fleet.transports[2].license_plate.should == "DEF123"
126
+ fleet.transports[2].model.should == "Honda Accord"
127
+ fleet.transports[2].year.should == 2008
128
+ fleet.save.should be_true
129
+
130
+ from_db = TrModels::Fleet.find(fleet.id)
131
+ from_db.transports.size.should == 3
132
+ from_db.transports[0].license_plate.should == "GGG123"
133
+ from_db.transports[0].icu.should be_true
134
+ from_db.transports[1].license_plate.should == "ABC123"
135
+ from_db.transports[1].model.should == "VW Golf"
136
+ from_db.transports[1].year.should == 2001
137
+ from_db.transports[2].license_plate.should == "DEF123"
138
+ from_db.transports[2].model.should == "Honda Accord"
139
+ from_db.transports[2].year.should == 2008
140
+ end
141
+
105
142
  should "default reader to empty array" do
106
143
  fleet = TrModels::Fleet.new
107
144
  fleet.transports.should == []
108
145
  end
109
-
146
+
110
147
  should "allow adding to association like it was an array" do
111
148
  fleet = TrModels::Fleet.new
112
149
  fleet.transports << TrModels::Car.new
113
150
  fleet.transports.push TrModels::Bus.new
114
151
  fleet.transports.size.should == 2
115
152
  end
116
-
153
+
117
154
  should "store the association" do
118
155
  fleet = TrModels::Fleet.new
119
156
  fleet.transports = [TrModels::Car.new("license_plate" => "DCU2013", "model" => "Honda Civic")]
120
157
  fleet.save.should be_true
121
-
158
+
122
159
  from_db = TrModels::Fleet.find(fleet.id)
123
160
  from_db.transports.size.should == 1
124
161
  from_db.transports[0].license_plate.should == "DCU2013"
125
162
  end
126
-
163
+
127
164
  should "store different associations" do
128
165
  fleet = TrModels::Fleet.new
129
166
  fleet.transports = [
@@ -132,7 +169,7 @@ class AssociationsTest < Test::Unit::TestCase
132
169
  TrModels::Ambulance.new("license_plate" => "HDD3030", "icu" => true)
133
170
  ]
134
171
  fleet.save.should be_true
135
-
172
+
136
173
  from_db = TrModels::Fleet.find(fleet.id)
137
174
  from_db.transports.size.should == 3
138
175
  from_db.transports[0].license_plate.should == "ABC1223"
@@ -145,30 +182,30 @@ class AssociationsTest < Test::Unit::TestCase
145
182
  end
146
183
  end
147
184
 
148
- context "Polymorphic Many" do
185
+ context "Polymorphic Many Embedded" do
149
186
  should "default reader to empty array" do
150
187
  catalog = Catalog.new
151
188
  catalog.medias.should == []
152
189
  end
153
-
190
+
154
191
  should "allow adding to association like it was an array" do
155
192
  catalog = Catalog.new
156
193
  catalog.medias << Video.new
157
194
  catalog.medias.push Video.new
158
195
  catalog.medias.size.should == 2
159
196
  end
160
-
197
+
161
198
  should "store the association" do
162
199
  catalog = Catalog.new
163
200
  catalog.medias = [Video.new("file" => "video.mpg", "length" => 3600)]
164
201
  catalog.save.should be_true
165
-
202
+
166
203
  from_db = Catalog.find(catalog.id)
167
204
  from_db.medias.size.should == 1
168
205
  from_db.medias[0].file.should == "video.mpg"
169
206
  end
170
-
171
- should "store different associations" do
207
+
208
+ should "store different associations" do
172
209
  catalog = Catalog.new
173
210
  catalog.medias = [
174
211
  Video.new("file" => "video.mpg", "length" => 3600),
@@ -176,7 +213,7 @@ class AssociationsTest < Test::Unit::TestCase
176
213
  Image.new("file" => "image.png", "width" => 800, "height" => 600)
177
214
  ]
178
215
  catalog.save.should be_true
179
-
216
+
180
217
  from_db = Catalog.find(catalog.id)
181
218
  from_db.medias.size.should == 3
182
219
  from_db.medias[0].file.should == "video.mpg"
@@ -188,32 +225,32 @@ class AssociationsTest < Test::Unit::TestCase
188
225
  from_db.medias[2].height.should == 600
189
226
  end
190
227
  end
191
-
228
+
192
229
  context "Polymorphic Belongs To" do
193
230
  should "default to nil" do
194
231
  status = Status.new
195
232
  status.target.should be_nil
196
233
  end
197
-
234
+
198
235
  should "store the association" do
199
236
  status = Status.new
200
237
  project = Project.new(:name => "mongomapper")
201
238
  status.target = project
202
239
  status.save.should be_true
203
-
240
+
204
241
  from_db = Status.find(status.id)
205
242
  from_db.target.should_not be_nil
206
243
  from_db.target_id.should == project.id
207
244
  from_db.target_type.should == "Project"
208
245
  from_db.target.name.should == "mongomapper"
209
246
  end
210
-
247
+
211
248
  should "unset the association" do
212
249
  status = Status.new
213
250
  project = Project.new(:name => "mongomapper")
214
251
  status.target = project
215
252
  status.save.should be_true
216
-
253
+
217
254
  from_db = Status.find(status.id)
218
255
  from_db.target = nil
219
256
  from_db.target_type.should be_nil
@@ -221,73 +258,68 @@ class AssociationsTest < Test::Unit::TestCase
221
258
  from_db.target.should be_nil
222
259
  end
223
260
  end
224
-
261
+
225
262
  context "Belongs To" do
226
263
  should "default to nil" do
227
264
  status = Status.new
228
265
  status.project.should be_nil
229
266
  end
230
-
267
+
231
268
  should "store the association" do
232
269
  status = Status.new
233
270
  project = Project.new(:name => "mongomapper")
234
271
  status.project = project
235
272
  status.save.should be_true
236
-
273
+
237
274
  from_db = Status.find(status.id)
238
275
  from_db.project.should_not be_nil
239
276
  from_db.project.name.should == "mongomapper"
240
277
  end
241
-
278
+
242
279
  should "unset the association" do
243
280
  status = Status.new
244
281
  project = Project.new(:name => "mongomapper")
245
282
  status.project = project
246
283
  status.save.should be_true
247
-
284
+
248
285
  from_db = Status.find(status.id)
249
286
  from_db.project = nil
250
287
  from_db.project.should be_nil
251
288
  end
252
289
  end
253
-
254
- context "Many documents" do
290
+
291
+ context "Many documents" do
255
292
  should "default reader to empty array" do
256
293
  project = Project.new
257
294
  project.statuses.should == []
258
295
  end
259
-
296
+
260
297
  should "allow adding to association like it was an array" do
261
298
  project = Project.new
262
299
  project.statuses << Status.new
263
300
  project.statuses.push Status.new
264
301
  project.statuses.size.should == 2
265
302
  end
266
-
303
+
267
304
  should "store the association" do
268
305
  project = Project.new
269
306
  project.statuses = [Status.new("name" => "ready")]
270
307
  project.save.should be_true
271
-
308
+
272
309
  from_db = Project.find(project.id)
273
310
  from_db.statuses.size.should == 1
274
311
  from_db.statuses[0].name.should == "ready"
275
312
  end
276
313
  end
277
-
314
+
278
315
  context "Many embedded documents" do
279
- should "default reader to empty array" do
280
- project = Project.new
281
- project.addresses.should == []
282
- end
283
-
284
316
  should "allow adding to association like it was an array" do
285
317
  project = Project.new
286
318
  project.addresses << Address.new
287
319
  project.addresses.push Address.new
288
320
  project.addresses.size.should == 2
289
321
  end
290
-
322
+
291
323
  should "be embedded in document on save" do
292
324
  sb = Address.new(:city => 'South Bend', :state => 'IN')
293
325
  chi = Address.new(:city => 'Chicago', :state => 'IL')
@@ -295,7 +327,7 @@ class AssociationsTest < Test::Unit::TestCase
295
327
  project.addresses << sb
296
328
  project.addresses << chi
297
329
  project.save
298
-
330
+
299
331
  from_db = Project.find(project.id)
300
332
  from_db.addresses.size.should == 2
301
333
  from_db.addresses[0].should == sb
@@ -321,10 +353,34 @@ class AssociationsTest < Test::Unit::TestCase
321
353
  from_db.person.child.child.name.should == 'Linda'
322
354
  end
323
355
 
356
+ should "allow assignment of 'many' embedded documents using a hash" do
357
+ person_attributes = {
358
+ "name" => "Mr. Pet Lover",
359
+ "pets" => [
360
+ {"name" => "Jimmy", "species" => "Cocker Spainel"},
361
+ {"name" => "Sasha", "species" => "Siberian Husky"},
362
+ ]
363
+ }
364
+
365
+ pet_lover = RealPerson.new(person_attributes)
366
+ pet_lover.name.should == "Mr. Pet Lover"
367
+ pet_lover.pets[0].name.should == "Jimmy"
368
+ pet_lover.pets[0].species.should == "Cocker Spainel"
369
+ pet_lover.pets[1].name.should == "Sasha"
370
+ pet_lover.pets[1].species.should == "Siberian Husky"
371
+ pet_lover.save.should be_true
372
+
373
+ from_db = RealPerson.find(pet_lover.id)
374
+ from_db.name.should == "Mr. Pet Lover"
375
+ from_db.pets[0].name.should == "Jimmy"
376
+ from_db.pets[0].species.should == "Cocker Spainel"
377
+ from_db.pets[1].name.should == "Sasha"
378
+ from_db.pets[1].species.should == "Siberian Husky"
379
+ end
380
+
324
381
  should "allow saving embedded documents in 'many' embedded documents" do
325
382
  @document = Class.new do
326
383
  include MongoMapper::Document
327
-
328
384
  many :people
329
385
  end
330
386
 
@@ -18,23 +18,6 @@ class DocumentTest < Test::Unit::TestCase
18
18
  MongoMapper::Document.descendants.should include(@document)
19
19
  end
20
20
 
21
- should "find its parent model" do
22
- class A < Address
23
- key :new_key, String
24
- end
25
-
26
- A.parent_model.should == Address
27
- end
28
-
29
- should "inherit keys" do
30
- class A < Address
31
- key :new_key, String
32
- end
33
-
34
- A.keys.should include("new_key")
35
- Address.keys.should_not include("new_key")
36
- end
37
-
38
21
  should "be able to define a key" do
39
22
  key = @document.key(:name, String)
40
23
  key.name.should == 'name'
@@ -55,6 +38,13 @@ class DocumentTest < Test::Unit::TestCase
55
38
  @document.keys['age'].name.should == 'age'
56
39
  @document.keys['age'].type.should == Integer
57
40
  end
41
+
42
+ should "allow redefining a key" do
43
+ @document.key(:foo, String)
44
+ @document.keys['foo'].type.should == String
45
+ @document.key(:foo, Integer)
46
+ @document.keys['foo'].type.should == Integer
47
+ end
58
48
 
59
49
  should "use default database by default" do
60
50
  @document.database.should == MongoMapper.database
@@ -1,5 +1,20 @@
1
1
  require 'test_helper'
2
2
 
3
+ class Grandparent
4
+ include MongoMapper::EmbeddedDocument
5
+ key :grandparent, String
6
+ end
7
+
8
+ class Parent < Grandparent
9
+ include MongoMapper::EmbeddedDocument
10
+ key :parent, String
11
+ end
12
+
13
+ class Child < Parent
14
+ include MongoMapper::EmbeddedDocument
15
+ key :child, String
16
+ end
17
+
3
18
  class EmbeddedDocumentTest < Test::Unit::TestCase
4
19
  context "Including MongoMapper::EmbeddedDocument" do
5
20
  setup do
@@ -12,6 +27,48 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
12
27
  @klass.keys.size.should == 0
13
28
  end
14
29
  end
30
+
31
+ context "parent_model" do
32
+ should "be nil if none of parents ancestors include EmbeddedDocument" do
33
+ parent = Class.new
34
+ document = Class.new(parent) do
35
+ include MongoMapper::EmbeddedDocument
36
+ end
37
+ document.parent_model.should be_nil
38
+ end
39
+
40
+ should "find parent" do
41
+ document = Class.new(Address)
42
+ document.parent_model.should == Address
43
+ end
44
+ end
45
+
46
+ context "keys" do
47
+ should "be inherited" do
48
+ Grandparent.keys.keys.should == ['grandparent']
49
+ Parent.keys.keys.sort.should == ['grandparent', 'parent']
50
+ Child.keys.keys.sort.should == ['child', 'grandparent', 'parent']
51
+ end
52
+
53
+ should "propogate to subclasses if key added after class definition" do
54
+ Grandparent.key :_type, String
55
+
56
+ Grandparent.keys.keys.sort.should == ['_type', 'grandparent']
57
+ Parent.keys.keys.sort.should == ['_type', 'grandparent', 'parent']
58
+ Child.keys.keys.sort.should == ['_type', 'child', 'grandparent', 'parent']
59
+ end
60
+ end
61
+
62
+ context "subclasses" do
63
+ should "default to array" do
64
+ Child.subclasses.sort.should == []
65
+ end
66
+
67
+ should "be recorded" do
68
+ Grandparent.subclasses.sort.should == [Parent]
69
+ Parent.subclasses.sort.should == [Child]
70
+ end
71
+ end
15
72
 
16
73
  context "An instance of an embedded document" do
17
74
  setup do
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require 'pp'
2
3
  require 'rubygems'
3
4
  require 'test/unit'
4
5
  require 'shoulda'
@@ -44,8 +44,9 @@ class TestRailsCompatibility < Test::Unit::TestCase
44
44
 
45
45
  should "have column names" do
46
46
  Order.column_names.sort.should == ['_id', 'created_at', 'order_only', 'updated_at']
47
- FirstItem.column_names.sort.should == ['first_only', 'for_all']
48
- SecondItem.column_names.sort.should == ['for_all', 'second_only']
47
+ Item.column_names.sort.should == ['_type', 'for_all']
48
+ FirstItem.column_names.sort.should == ['_type', 'first_only', 'for_all']
49
+ SecondItem.column_names.sort.should == ['_type', 'for_all', 'second_only']
49
50
  end
50
51
  end
51
52
 
@@ -169,6 +169,12 @@ class ValidationsTest < Test::Unit::TestCase
169
169
  should "allow to update an object" do
170
170
  doc = @document.new("name" => "joe")
171
171
  doc.save
172
+
173
+ @document \
174
+ .stubs(:find) \
175
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
176
+ .returns(doc)
177
+
172
178
  doc.name = "joe"
173
179
  doc.valid?.should be_true
174
180
  doc.should_not have_error_on(:name)
@@ -177,7 +183,12 @@ class ValidationsTest < Test::Unit::TestCase
177
183
  should "fail if object name is not unique" do
178
184
  doc = @document.new("name" => "joe")
179
185
  doc.save.should be_true
180
- sleep 0.2 # hack to avoid race condition
186
+
187
+ @document \
188
+ .stubs(:find) \
189
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
190
+ .returns(doc)
191
+
181
192
  doc2 = @document.new("name" => "joe")
182
193
  doc2.should have_error_on(:name)
183
194
  end
@@ -189,7 +200,12 @@ class ValidationsTest < Test::Unit::TestCase
189
200
 
190
201
  doc = @document.create(:name => 'John')
191
202
  doc.should_not have_error_on(:name)
192
- sleep 0.2 # hack to avoid race condition
203
+
204
+ @document \
205
+ .stubs(:find) \
206
+ .with(:first, :conditions => {:name => 'John'}, :limit => 1) \
207
+ .returns(doc)
208
+
193
209
  second_john = @document.create(:name => 'John')
194
210
  second_john.should have_error_on(:name, 'has already been taken')
195
211
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fcoury-mongomapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-21 00:00:00 -07:00
12
+ date: 2009-07-27 00:00:00 -07:00
13
13
  default_executable: mmconsole
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -119,6 +119,7 @@ files:
119
119
  - test/test_validations.rb
120
120
  has_rdoc: false
121
121
  homepage: http://github.com/jnunemaker/mongomapper
122
+ licenses:
122
123
  post_install_message:
123
124
  rdoc_options:
124
125
  - --charset=UTF-8
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  requirements: []
140
141
 
141
142
  rubyforge_project: mongomapper
142
- rubygems_version: 1.2.0
143
+ rubygems_version: 1.3.5
143
144
  signing_key:
144
145
  specification_version: 3
145
146
  summary: Awesome gem for modeling your domain and storing it in mongo