fcoury-mongomapper 0.3.5 → 0.4.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.
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