mongo_mapper-unstable 2010.1.27 → 2010.1.28
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/mongo_mapper/document.rb +17 -12
- data/lib/mongo_mapper/embedded_document.rb +12 -5
- data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +1 -0
- data/lib/mongo_mapper/plugins/keys.rb +9 -1
- data/lib/mongo_mapper/plugins/protected.rb +41 -0
- data/lib/mongo_mapper/support.rb +2 -1
- data/lib/mongo_mapper.rb +2 -1
- data/test/functional/associations/test_many_embedded_proxy.rb +38 -0
- data/test/functional/test_document.rb +91 -73
- data/test/functional/test_protected.rb +139 -0
- data/test/test_helper.rb +11 -17
- data/test/unit/test_embedded_document.rb +544 -533
- data/test/unit/test_support.rb +5 -1
- data/test/unit/test_validations.rb +55 -3
- metadata +6 -3
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ Jeweler::Tasks.new do |gem|
|
|
12
12
|
gem.authors = ["John Nunemaker"]
|
13
13
|
|
14
14
|
gem.add_dependency('activesupport', '>= 2.3')
|
15
|
-
gem.add_dependency('mongo', '0.18.
|
15
|
+
gem.add_dependency('mongo', '0.18.3')
|
16
16
|
gem.add_dependency('jnunemaker-validatable', '1.8.1')
|
17
17
|
|
18
18
|
gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2010.01.
|
1
|
+
2010.01.28
|
@@ -7,8 +7,8 @@ module MongoMapper
|
|
7
7
|
include InstanceMethods
|
8
8
|
extend ClassMethods
|
9
9
|
extend Finders
|
10
|
-
|
11
10
|
extend Plugins
|
11
|
+
|
12
12
|
plugin Plugins::Associations
|
13
13
|
plugin Plugins::Clone
|
14
14
|
plugin Plugins::Descendants
|
@@ -18,6 +18,7 @@ module MongoMapper
|
|
18
18
|
plugin Plugins::Dirty # for now dirty needs to be after keys
|
19
19
|
plugin Plugins::Logger
|
20
20
|
plugin Plugins::Pagination
|
21
|
+
plugin Plugins::Protected
|
21
22
|
plugin Plugins::Rails
|
22
23
|
plugin Plugins::Serialization
|
23
24
|
plugin Plugins::Validations
|
@@ -69,6 +70,21 @@ module MongoMapper
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
73
|
+
def find_or_create(arg)
|
74
|
+
first(arg) || create(arg)
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_each(options={})
|
78
|
+
criteria, options = to_finder_options(options)
|
79
|
+
collection.find(criteria, options).each do |doc|
|
80
|
+
yield load(doc)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_by_id(id)
|
85
|
+
find(id)
|
86
|
+
end
|
87
|
+
|
72
88
|
def first(options={})
|
73
89
|
find_one(options)
|
74
90
|
end
|
@@ -82,21 +98,10 @@ module MongoMapper
|
|
82
98
|
find_many(options)
|
83
99
|
end
|
84
100
|
|
85
|
-
def find_by_id(id)
|
86
|
-
find(id)
|
87
|
-
end
|
88
|
-
|
89
101
|
def count(options={})
|
90
102
|
collection.find(to_criteria(options)).count
|
91
103
|
end
|
92
104
|
|
93
|
-
def find_each(options={})
|
94
|
-
criteria, options = to_finder_options(options)
|
95
|
-
collection.find(criteria, options).each do |doc|
|
96
|
-
yield load(doc)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
105
|
def exists?(options={})
|
101
106
|
!count(options).zero?
|
102
107
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module EmbeddedDocument
|
3
3
|
extend DescendantAppends
|
4
|
-
|
4
|
+
|
5
5
|
def self.included(model)
|
6
6
|
model.class_eval do
|
7
7
|
include InstanceMethods
|
8
8
|
extend ClassMethods
|
9
|
-
|
10
9
|
extend Plugins
|
10
|
+
|
11
11
|
plugin Plugins::Associations
|
12
12
|
plugin Plugins::Clone
|
13
13
|
plugin Plugins::Descendants
|
@@ -15,13 +15,14 @@ module MongoMapper
|
|
15
15
|
plugin Plugins::Inspect
|
16
16
|
plugin Plugins::Keys
|
17
17
|
plugin Plugins::Logger
|
18
|
+
plugin Plugins::Protected
|
18
19
|
plugin Plugins::Rails
|
19
20
|
plugin Plugins::Serialization
|
20
21
|
plugin Plugins::Validations
|
21
22
|
|
22
|
-
attr_accessor :_root_document
|
23
|
+
attr_accessor :_root_document, :_parent_document
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
super
|
26
27
|
end
|
27
28
|
|
@@ -29,6 +30,12 @@ module MongoMapper
|
|
29
30
|
def embeddable?
|
30
31
|
true
|
31
32
|
end
|
33
|
+
|
34
|
+
def embedded_in(owner_name)
|
35
|
+
define_method(owner_name) do
|
36
|
+
self._parent_document
|
37
|
+
end
|
38
|
+
end
|
32
39
|
end
|
33
40
|
|
34
41
|
module InstanceMethods
|
@@ -38,7 +45,7 @@ module MongoMapper
|
|
38
45
|
end
|
39
46
|
result
|
40
47
|
end
|
41
|
-
|
48
|
+
|
42
49
|
def save!(options={})
|
43
50
|
if result = _root_document.try(:save!, options)
|
44
51
|
@new = false
|
@@ -122,6 +122,14 @@ module MongoMapper
|
|
122
122
|
validates_format_of(attribute, :with => key.options[:format])
|
123
123
|
end
|
124
124
|
|
125
|
+
if key.options[:in]
|
126
|
+
validates_inclusion_of(attribute, :within => key.options[:in])
|
127
|
+
end
|
128
|
+
|
129
|
+
if key.options[:not_in]
|
130
|
+
validates_exclusion_of(attribute, :within => key.options[:not_in])
|
131
|
+
end
|
132
|
+
|
125
133
|
if key.options[:length]
|
126
134
|
length_options = case key.options[:length]
|
127
135
|
when Integer
|
@@ -289,4 +297,4 @@ module MongoMapper
|
|
289
297
|
end
|
290
298
|
end
|
291
299
|
end
|
292
|
-
end
|
300
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module Protected
|
4
|
+
module ClassMethods
|
5
|
+
def attr_protected(*attrs)
|
6
|
+
self.write_inheritable_attribute(:attr_protected, Set.new(attrs) + (protected_attributes || []))
|
7
|
+
end
|
8
|
+
|
9
|
+
def protected_attributes
|
10
|
+
self.read_inheritable_attribute(:attr_protected)
|
11
|
+
end
|
12
|
+
|
13
|
+
def key(*args)
|
14
|
+
key = super
|
15
|
+
attr_protected key.name.to_sym if key.options[:protected]
|
16
|
+
key
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module InstanceMethods
|
21
|
+
def update_attributes(attrs={})
|
22
|
+
super(filter_protected_attrs(attrs))
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_attributes!(attrs={})
|
26
|
+
super(filter_protected_attrs(attrs))
|
27
|
+
end
|
28
|
+
|
29
|
+
def protected_attributes
|
30
|
+
self.class.protected_attributes
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
def filter_protected_attrs(attrs)
|
35
|
+
return attrs if protected_attributes.blank?
|
36
|
+
attrs.dup.delete_if { |key, val| protected_attributes.include?(key) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/mongo_mapper/support.rb
CHANGED
data/lib/mongo_mapper.rb
CHANGED
@@ -6,7 +6,7 @@ require 'set'
|
|
6
6
|
# if there is a better way to do this, please enlighten me!
|
7
7
|
if self.class.const_defined?(:Gem)
|
8
8
|
gem 'activesupport', '>= 2.3'
|
9
|
-
gem 'mongo', '0.18.
|
9
|
+
gem 'mongo', '0.18.3'
|
10
10
|
gem 'jnunemaker-validatable', '1.8.1'
|
11
11
|
end
|
12
12
|
|
@@ -117,6 +117,7 @@ require 'mongo_mapper/plugins/inspect'
|
|
117
117
|
require 'mongo_mapper/plugins/keys'
|
118
118
|
require 'mongo_mapper/plugins/logger'
|
119
119
|
require 'mongo_mapper/plugins/pagination'
|
120
|
+
require 'mongo_mapper/plugins/protected'
|
120
121
|
require 'mongo_mapper/plugins/rails'
|
121
122
|
require 'mongo_mapper/plugins/serialization'
|
122
123
|
require 'mongo_mapper/plugins/validations'
|
@@ -15,6 +15,7 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
|
|
15
15
|
@pet_class = EDoc do
|
16
16
|
key :name, String
|
17
17
|
end
|
18
|
+
@pet_class.embedded_in :person
|
18
19
|
@person_class = EDoc do
|
19
20
|
key :name, String
|
20
21
|
end
|
@@ -133,6 +134,17 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
|
|
133
134
|
doc.people.first._root_document.should == doc
|
134
135
|
doc.people.first.pets.first._root_document.should == doc
|
135
136
|
end
|
137
|
+
should "create a reference to the owning document for all embedded documents before save" do
|
138
|
+
doc = @klass.new
|
139
|
+
meg = @person_class.new(:name => 'Meg')
|
140
|
+
pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
|
141
|
+
|
142
|
+
doc.people << meg
|
143
|
+
meg.pets << pet
|
144
|
+
|
145
|
+
doc.people.first._parent_document.should == doc
|
146
|
+
doc.people.first.pets.first._parent_document.should == doc.people.first
|
147
|
+
end
|
136
148
|
|
137
149
|
should "create a reference to the root document for all embedded documents" do
|
138
150
|
sparky = @pet_class.new(:name => 'Sparky', :species => 'Dog')
|
@@ -145,6 +157,32 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
|
|
145
157
|
doc.people.first._root_document.should == doc
|
146
158
|
doc.people.first.pets.first._root_document.should == doc
|
147
159
|
end
|
160
|
+
should "create a reference to the owning document for all embedded documents" do
|
161
|
+
doc = @klass.new
|
162
|
+
meg = @person_class.new(:name => 'Meg')
|
163
|
+
pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
|
164
|
+
|
165
|
+
doc.people << meg
|
166
|
+
meg.pets << pet
|
167
|
+
doc.save
|
168
|
+
|
169
|
+
doc.reload
|
170
|
+
doc.people.first._parent_document.should == doc
|
171
|
+
doc.people.first.pets.first._parent_document.should == doc.people.first
|
172
|
+
end
|
173
|
+
|
174
|
+
should "create embedded_in relationship for embedded docs" do
|
175
|
+
doc = @klass.new
|
176
|
+
meg = @person_class.new(:name => 'Meg')
|
177
|
+
pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
|
178
|
+
|
179
|
+
doc.people << meg
|
180
|
+
meg.pets << pet
|
181
|
+
doc.save
|
182
|
+
|
183
|
+
doc.reload
|
184
|
+
doc.people.first.pets.first.person.should == doc.people.first
|
185
|
+
end
|
148
186
|
end
|
149
187
|
|
150
188
|
should "allow finding by id" do
|
@@ -13,7 +13,7 @@ class DocumentTest < Test::Unit::TestCase
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
context "
|
16
|
+
context "array key" do
|
17
17
|
setup do
|
18
18
|
@document.key :tags, Array
|
19
19
|
end
|
@@ -60,7 +60,7 @@ class DocumentTest < Test::Unit::TestCase
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
context "
|
63
|
+
context "hash key" do
|
64
64
|
setup do
|
65
65
|
@document.key :foo, Hash
|
66
66
|
end
|
@@ -95,7 +95,7 @@ class DocumentTest < Test::Unit::TestCase
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
context "
|
98
|
+
context "custom type key with default" do
|
99
99
|
setup do
|
100
100
|
@document.key :window, WindowSize, :default => WindowSize.new(600, 480)
|
101
101
|
end
|
@@ -407,6 +407,24 @@ class DocumentTest < Test::Unit::TestCase
|
|
407
407
|
end
|
408
408
|
end
|
409
409
|
|
410
|
+
context "find_or_create" do
|
411
|
+
should "find if exists" do
|
412
|
+
created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
|
413
|
+
lambda {
|
414
|
+
found = @document.find_or_create(:first_name => 'John', :last_name => 'Nunemaker')
|
415
|
+
found.should == created
|
416
|
+
}.should_not change { @document.count }
|
417
|
+
end
|
418
|
+
|
419
|
+
should "create if not found" do
|
420
|
+
lambda {
|
421
|
+
created = @document.find_or_create(:first_name => 'John', :last_name => 'Nunemaker')
|
422
|
+
created.first_name.should == 'John'
|
423
|
+
created.last_name.should == 'Nunemaker'
|
424
|
+
}.should change { @document.count }.by(1)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
410
428
|
context "ClassMethods#delete (single document)" do
|
411
429
|
setup do
|
412
430
|
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
@@ -568,10 +586,10 @@ class DocumentTest < Test::Unit::TestCase
|
|
568
586
|
@document.new.database.should == @document.database
|
569
587
|
end
|
570
588
|
|
571
|
-
context "#
|
589
|
+
context "#update_attributes (new document)" do
|
572
590
|
setup do
|
573
591
|
@doc = @document.new(:first_name => 'John', :age => '27')
|
574
|
-
@doc.
|
592
|
+
@doc.update_attributes(:first_name => 'Johnny', :age => 30)
|
575
593
|
end
|
576
594
|
|
577
595
|
should "insert document into the collection" do
|
@@ -583,54 +601,27 @@ class DocumentTest < Test::Unit::TestCase
|
|
583
601
|
end
|
584
602
|
|
585
603
|
should "save attributes" do
|
586
|
-
@doc.first_name.should == '
|
587
|
-
@doc.age.should ==
|
604
|
+
@doc.first_name.should == 'Johnny'
|
605
|
+
@doc.age.should == 30
|
588
606
|
end
|
589
607
|
|
590
608
|
should "update attributes in the database" do
|
591
609
|
doc = @doc.reload
|
592
610
|
doc.should == @doc
|
593
|
-
doc.first_name.should == '
|
594
|
-
doc.age.should ==
|
595
|
-
end
|
596
|
-
|
597
|
-
should "allow to add custom attributes to the document" do
|
598
|
-
@doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
|
599
|
-
@doc.save
|
600
|
-
doc = @doc.reload
|
601
|
-
doc.gender.should == 'male'
|
602
|
-
doc.tags.should == [1, "2"]
|
603
|
-
end
|
604
|
-
|
605
|
-
should "allow to use custom methods to assign properties" do
|
606
|
-
klass = Doc do
|
607
|
-
key :name, String
|
608
|
-
|
609
|
-
def realname=(value)
|
610
|
-
self.name = value
|
611
|
-
end
|
612
|
-
end
|
613
|
-
|
614
|
-
person = klass.new(:realname => 'David')
|
615
|
-
person.save
|
616
|
-
person.reload.name.should == 'David'
|
611
|
+
doc.first_name.should == 'Johnny'
|
612
|
+
doc.age.should == 30
|
617
613
|
end
|
618
614
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
doc.save
|
623
|
-
doc.date.should == Date.new(2009, 12, 1)
|
624
|
-
end
|
615
|
+
should "allow updating custom attributes" do
|
616
|
+
@doc.update_attributes(:gender => 'mALe')
|
617
|
+
@doc.reload.gender.should == 'mALe'
|
625
618
|
end
|
626
619
|
end
|
627
620
|
|
628
|
-
context "#
|
621
|
+
context "#update_attributes (existing document)" do
|
629
622
|
setup do
|
630
623
|
@doc = @document.create(:first_name => 'John', :age => '27')
|
631
|
-
@doc.first_name
|
632
|
-
@doc.age = 30
|
633
|
-
@doc.save
|
624
|
+
@doc.update_attributes(:first_name => 'Johnny', :age => 30)
|
634
625
|
end
|
635
626
|
|
636
627
|
should "not insert document into collection" do
|
@@ -647,19 +638,26 @@ class DocumentTest < Test::Unit::TestCase
|
|
647
638
|
doc.first_name.should == 'Johnny'
|
648
639
|
doc.age.should == 30
|
649
640
|
end
|
641
|
+
end
|
650
642
|
|
651
|
-
|
652
|
-
|
653
|
-
@
|
654
|
-
@doc.save
|
655
|
-
@doc.reload.gender.should == 'Male'
|
643
|
+
context "#update_attributes (return value)" do
|
644
|
+
setup do
|
645
|
+
@document.key :foo, String, :required => true
|
656
646
|
end
|
657
|
-
end
|
658
647
|
|
659
|
-
|
648
|
+
should "be true if document valid" do
|
649
|
+
@document.new.update_attributes(:foo => 'bar').should be_true
|
650
|
+
end
|
651
|
+
|
652
|
+
should "be false if document not valid" do
|
653
|
+
@document.new.update_attributes({}).should be_false
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
context "#save (new document)" do
|
660
658
|
setup do
|
661
659
|
@doc = @document.new(:first_name => 'John', :age => '27')
|
662
|
-
@doc.
|
660
|
+
@doc.save
|
663
661
|
end
|
664
662
|
|
665
663
|
should "insert document into the collection" do
|
@@ -671,27 +669,54 @@ class DocumentTest < Test::Unit::TestCase
|
|
671
669
|
end
|
672
670
|
|
673
671
|
should "save attributes" do
|
674
|
-
@doc.first_name.should == '
|
675
|
-
@doc.age.should ==
|
672
|
+
@doc.first_name.should == 'John'
|
673
|
+
@doc.age.should == 27
|
676
674
|
end
|
677
675
|
|
678
676
|
should "update attributes in the database" do
|
679
677
|
doc = @doc.reload
|
680
678
|
doc.should == @doc
|
681
|
-
doc.first_name.should == '
|
682
|
-
doc.age.should ==
|
679
|
+
doc.first_name.should == 'John'
|
680
|
+
doc.age.should == 27
|
683
681
|
end
|
684
682
|
|
685
|
-
should "allow
|
686
|
-
@doc.
|
687
|
-
@doc.
|
683
|
+
should "allow to add custom attributes to the document" do
|
684
|
+
@doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
|
685
|
+
@doc.save
|
686
|
+
doc = @doc.reload
|
687
|
+
doc.gender.should == 'male'
|
688
|
+
doc.tags.should == [1, "2"]
|
689
|
+
end
|
690
|
+
|
691
|
+
should "allow to use custom methods to assign properties" do
|
692
|
+
klass = Doc do
|
693
|
+
key :name, String
|
694
|
+
|
695
|
+
def realname=(value)
|
696
|
+
self.name = value
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
person = klass.new(:realname => 'David')
|
701
|
+
person.save
|
702
|
+
person.reload.name.should == 'David'
|
703
|
+
end
|
704
|
+
|
705
|
+
context "with key of type date" do
|
706
|
+
should "save the date value as a Time object" do
|
707
|
+
doc = @document.new(:first_name => 'John', :age => '27', :date => "12/01/2009")
|
708
|
+
doc.save
|
709
|
+
doc.date.should == Date.new(2009, 12, 1)
|
710
|
+
end
|
688
711
|
end
|
689
712
|
end
|
690
713
|
|
691
|
-
context "#
|
714
|
+
context "#save (existing document)" do
|
692
715
|
setup do
|
693
716
|
@doc = @document.create(:first_name => 'John', :age => '27')
|
694
|
-
@doc.
|
717
|
+
@doc.first_name = 'Johnny'
|
718
|
+
@doc.age = 30
|
719
|
+
@doc.save
|
695
720
|
end
|
696
721
|
|
697
722
|
should "not insert document into collection" do
|
@@ -708,22 +733,15 @@ class DocumentTest < Test::Unit::TestCase
|
|
708
733
|
doc.first_name.should == 'Johnny'
|
709
734
|
doc.age.should == 30
|
710
735
|
end
|
711
|
-
end
|
712
|
-
|
713
|
-
context "#update_attributes" do
|
714
|
-
setup do
|
715
|
-
@document.key :foo, String, :required => true
|
716
|
-
end
|
717
|
-
|
718
|
-
should "return true if document valid" do
|
719
|
-
@document.new.update_attributes(:foo => 'bar').should be_true
|
720
|
-
end
|
721
736
|
|
722
|
-
should "
|
723
|
-
@document.new
|
737
|
+
should "allow updating custom attributes" do
|
738
|
+
@doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
|
739
|
+
@doc.gender = 'Male'
|
740
|
+
@doc.save
|
741
|
+
@doc.reload.gender.should == 'Male'
|
724
742
|
end
|
725
743
|
end
|
726
|
-
|
744
|
+
|
727
745
|
context "#save (with validations off)" do
|
728
746
|
setup do
|
729
747
|
@document = Doc do
|
@@ -1091,7 +1109,7 @@ class DocumentTest < Test::Unit::TestCase
|
|
1091
1109
|
end
|
1092
1110
|
end
|
1093
1111
|
|
1094
|
-
context "#
|
1112
|
+
context "#exists?" do
|
1095
1113
|
setup do
|
1096
1114
|
@doc = @document.create(:first_name => "James", :age => 27)
|
1097
1115
|
end
|
@@ -1162,7 +1180,7 @@ class DocumentTest < Test::Unit::TestCase
|
|
1162
1180
|
end
|
1163
1181
|
end
|
1164
1182
|
|
1165
|
-
context "
|
1183
|
+
context "database has keys not defined in model" do
|
1166
1184
|
setup do
|
1167
1185
|
@id = Mongo::ObjectID.new
|
1168
1186
|
@document.collection.insert({
|