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 +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
|