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 +1 -1
- data/lib/mongomapper/associations.rb +33 -38
- data/lib/mongomapper/associations/base.rb +24 -22
- data/lib/mongomapper/associations/has_many_embedded_proxy.rb +4 -4
- data/lib/mongomapper/associations/has_many_proxy.rb +7 -6
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +6 -6
- data/lib/mongomapper/associations/polymorphic_has_many_embedded_proxy.rb +28 -21
- data/lib/mongomapper/associations/proxy.rb +21 -14
- data/lib/mongomapper/document.rb +3 -3
- data/lib/mongomapper/embedded_document.rb +22 -3
- data/mongomapper.gemspec +2 -2
- data/test/test_associations.rb +102 -46
- data/test/test_document.rb +7 -17
- data/test/test_embedded_document.rb +57 -0
- data/test/test_helper.rb +1 -0
- data/test/test_rails_compatibility.rb +3 -2
- data/test/test_validations.rb +18 -2
- metadata +4 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
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
|
-
|
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
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
@@ -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
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def find_target
|
21
|
+
@association.klass.find(:all, {:conditions => {self.foreign_key => @owner.id}})
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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 <
|
3
|
+
class PolymorphicHasManyEmbeddedProxy < ArrayProxy
|
4
4
|
def replace(v)
|
5
|
-
@_values = v.map do |
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
50
|
+
def load_target
|
51
|
+
@target ||= find_target
|
52
|
+
end
|
54
53
|
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
data/lib/mongomapper/document.rb
CHANGED
@@ -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
|
-
|
261
|
+
send("#{association.name}=", collection)
|
243
262
|
end
|
244
263
|
end
|
245
264
|
end
|
data/mongomapper.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{mongomapper}
|
5
|
-
s.version = "0.
|
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-
|
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"]
|
data/test/test_associations.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
|
-
|
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 "
|
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
|
|
data/test/test_document.rb
CHANGED
@@ -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
|
data/test/test_helper.rb
CHANGED
@@ -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
|
-
|
48
|
-
|
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
|
|
data/test/test_validations.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
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-
|
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.
|
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
|