djsun-mongomapper 0.3.1

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.
Files changed (61) hide show
  1. data/.gitignore +7 -0
  2. data/History +51 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/bin/mmconsole +56 -0
  8. data/lib/mongomapper.rb +96 -0
  9. data/lib/mongomapper/associations.rb +61 -0
  10. data/lib/mongomapper/associations/base.rb +71 -0
  11. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +32 -0
  12. data/lib/mongomapper/associations/belongs_to_proxy.rb +22 -0
  13. data/lib/mongomapper/associations/many_documents_proxy.rb +85 -0
  14. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongomapper/associations/many_embedded_proxy.rb +17 -0
  16. data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongomapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongomapper/associations/proxy.rb +67 -0
  19. data/lib/mongomapper/callbacks.rb +106 -0
  20. data/lib/mongomapper/document.rb +278 -0
  21. data/lib/mongomapper/embedded_document.rb +237 -0
  22. data/lib/mongomapper/finder_options.rb +96 -0
  23. data/lib/mongomapper/key.rb +80 -0
  24. data/lib/mongomapper/observing.rb +50 -0
  25. data/lib/mongomapper/pagination.rb +52 -0
  26. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  27. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  28. data/lib/mongomapper/save_with_validation.rb +19 -0
  29. data/lib/mongomapper/serialization.rb +55 -0
  30. data/lib/mongomapper/serializers/json_serializer.rb +79 -0
  31. data/lib/mongomapper/validations.rb +47 -0
  32. data/mongomapper.gemspec +139 -0
  33. data/test/NOTE_ON_TESTING +1 -0
  34. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +39 -0
  35. data/test/functional/associations/test_belongs_to_proxy.rb +35 -0
  36. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
  37. data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
  38. data/test/functional/associations/test_many_polymorphic_proxy.rb +267 -0
  39. data/test/functional/associations/test_many_proxy.rb +236 -0
  40. data/test/functional/test_associations.rb +40 -0
  41. data/test/functional/test_callbacks.rb +85 -0
  42. data/test/functional/test_document.rb +691 -0
  43. data/test/functional/test_pagination.rb +81 -0
  44. data/test/functional/test_rails_compatibility.rb +31 -0
  45. data/test/functional/test_validations.rb +172 -0
  46. data/test/models.rb +108 -0
  47. data/test/test_helper.rb +67 -0
  48. data/test/unit/serializers/test_json_serializer.rb +103 -0
  49. data/test/unit/test_association_base.rb +136 -0
  50. data/test/unit/test_document.rb +125 -0
  51. data/test/unit/test_embedded_document.rb +370 -0
  52. data/test/unit/test_finder_options.rb +214 -0
  53. data/test/unit/test_key.rb +217 -0
  54. data/test/unit/test_mongo_id.rb +35 -0
  55. data/test/unit/test_mongomapper.rb +28 -0
  56. data/test/unit/test_observing.rb +101 -0
  57. data/test/unit/test_pagination.rb +113 -0
  58. data/test/unit/test_rails_compatibility.rb +34 -0
  59. data/test/unit/test_serializations.rb +52 -0
  60. data/test/unit/test_validations.rb +259 -0
  61. metadata +189 -0
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class Address; end
4
+
5
+ class MongoMapperTest < Test::Unit::TestCase
6
+ should "be able to write and read connection" do
7
+ conn = XGen::Mongo::Driver::Mongo.new
8
+ MongoMapper.connection = conn
9
+ MongoMapper.connection.should == conn
10
+ end
11
+
12
+ should "default connection to new mongo ruby driver" do
13
+ MongoMapper.connection = nil
14
+ MongoMapper.connection.should be_instance_of(XGen::Mongo::Driver::Mongo)
15
+ end
16
+
17
+ should "be able to write and read default database" do
18
+ MongoMapper.database = DefaultDatabase
19
+ MongoMapper.database.should be_instance_of(XGen::Mongo::Driver::DB)
20
+ MongoMapper.database.name.should == DefaultDatabase
21
+ end
22
+
23
+ should "have document not found error" do
24
+ lambda {
25
+ MongoMapper::DocumentNotFound
26
+ }.should_not raise_error
27
+ end
28
+ end
@@ -0,0 +1,101 @@
1
+ require 'test_helper'
2
+
3
+ class Comment
4
+ include MongoMapper::Document
5
+
6
+ key :name, String
7
+ key :body, String
8
+
9
+ attr_accessor :callers
10
+ before_validation :record_callers
11
+
12
+ def after_validation
13
+ record_callers
14
+ end
15
+
16
+ def record_callers
17
+ callers << self.class if callers
18
+ end
19
+ end
20
+
21
+ class Article
22
+ include MongoMapper::Document
23
+
24
+ key :title, String
25
+ key :body, String
26
+ end
27
+
28
+ class CommentObserver < MongoMapper::Observer
29
+ attr_accessor :callers
30
+
31
+ def after_validation(model)
32
+ callers << self.class if callers
33
+ end
34
+ end
35
+
36
+ class AuditObserver < MongoMapper::Observer
37
+ observe Article, Comment
38
+ attr_reader :document
39
+
40
+ def after_validation(document)
41
+ @document = document
42
+ end
43
+ end
44
+
45
+ class GlobalObserver < MongoMapper::Observer
46
+ observe Article, Comment
47
+ attr_reader :document
48
+
49
+ def before_save(document)
50
+ @document = document
51
+ end
52
+ end
53
+
54
+ class NonAutomaticObserver < MongoMapper::Observer
55
+ observe Comment
56
+ attr_reader :comment
57
+
58
+ def after_validation(comment)
59
+ @comment = comment
60
+ end
61
+ end
62
+
63
+ class ObserverTest < Test::Unit::TestCase
64
+ should "fire model callbacks before observer" do
65
+ callers = []
66
+ comment = Comment.new
67
+ comment.callers = callers
68
+
69
+ CommentObserver.instance.callers = callers
70
+
71
+ comment.valid?
72
+ callers.should == [Comment, Comment, CommentObserver]
73
+ end
74
+
75
+ should "automatically observe model based on name when possible" do
76
+ CommentObserver.observed_class.should == Comment
77
+ end
78
+
79
+ should "be able to observe other models using observe" do
80
+ obs = NonAutomaticObserver.instance
81
+ comment = Comment.new(:name => 'John Nunemaker', :body => 'is awesome')
82
+ comment.valid?
83
+ obs.comment.name.should == 'John Nunemaker'
84
+ obs.comment.body.should == 'is awesome'
85
+ end
86
+
87
+ should "be able to observe multiple models" do
88
+ obs = AuditObserver.instance
89
+ comment = Comment.new(:name => 'Steve Smith', :body => 'is awesome')
90
+ comment.valid?
91
+
92
+ obs.document.name.should == 'Steve Smith'
93
+ obs.document.body.should == 'is awesome'
94
+
95
+ article = Article.new(:title => 'Ordered List Is Awesome', :body => 'Learn to accept it!')
96
+ article.valid?
97
+
98
+ obs.document.title.should == 'Ordered List Is Awesome'
99
+ obs.document.body.should == 'Learn to accept it!'
100
+ end
101
+ end
@@ -0,0 +1,113 @@
1
+ require 'test_helper'
2
+
3
+ class PaginationTest < Test::Unit::TestCase
4
+ context "Pagination proxy" do
5
+ include MongoMapper::Pagination
6
+
7
+ should "should have accessors for subject" do
8
+ subject = [1,2,3,4,5]
9
+ collection = PaginationProxy.new(25, 2)
10
+ collection.subject = subject
11
+ collection.subject.should == subject
12
+ end
13
+
14
+ should "delegate any methods not defined to the subject" do
15
+ subject = [1,2,3,4,5]
16
+ collection = PaginationProxy.new(25, 2, 10)
17
+ collection.subject = subject
18
+ collection.size.should == 5
19
+ collection.each_with_index do |value, i|
20
+ value.should == subject[i]
21
+ end
22
+ collection[0..2].should == [1,2,3]
23
+ collection.class.should == Array
24
+ end
25
+
26
+ should "return correct value for total_entries" do
27
+ PaginationProxy.new(25, 2, 10).total_entries.should == 25
28
+ PaginationProxy.new('25', 2, 10).total_entries.should == 25
29
+ end
30
+
31
+ should "return correct value for per_page" do
32
+ PaginationProxy.new(25, 2, 10).per_page.should == 10
33
+ PaginationProxy.new(25, 2, '10').per_page.should == 10
34
+ end
35
+
36
+ should "alias limit to per_page" do
37
+ PaginationProxy.new(100, 1, 300).limit.should == 300
38
+ end
39
+
40
+ should "set per_page to 25 if nil or blank" do
41
+ PaginationProxy.new(25, 2).per_page.should == 25
42
+ PaginationProxy.new(25, 2, '').per_page.should == 25
43
+ end
44
+
45
+ should "return correct value for current_page" do
46
+ PaginationProxy.new(25, 2, 10).current_page.should == 2
47
+ PaginationProxy.new(25, '2', 10).current_page.should == 2
48
+ end
49
+
50
+ should "not allow value less than 1 for current page" do
51
+ PaginationProxy.new(25, -1).current_page.should == 1
52
+ end
53
+
54
+ should "automatically calculate total_pages from total_entries and per page" do
55
+ PaginationProxy.new(25, 2, 10).total_pages.should == 3
56
+ end
57
+
58
+ should "know how many records to skip" do
59
+ PaginationProxy.new(25, 2, 10).skip.should == 10
60
+ end
61
+
62
+ should "alias offset to skip" do
63
+ PaginationProxy.new(25, 2, 10).offset.should == 10
64
+ end
65
+
66
+ context "previous_page" do
67
+ should "be nil if current page 1" do
68
+ PaginationProxy.new(25, 1, 10).previous_page.should be_nil
69
+ end
70
+
71
+ should "be one less than current page if current is > 1" do
72
+ PaginationProxy.new(25, 2, 10).previous_page.should == 1
73
+ end
74
+ end
75
+
76
+ context "next_page" do
77
+ should "be nil if current page is last page" do
78
+ PaginationProxy.new(25, 3, 10).next_page.should be_nil
79
+ end
80
+
81
+ should "work for any page that is not last" do
82
+ PaginationProxy.new(25, 1, 10).next_page.should == 2
83
+ PaginationProxy.new(25, 2, 10).next_page.should == 3
84
+ end
85
+ end
86
+
87
+ context "previous_page" do
88
+ should "be nil if current page is first page" do
89
+ PaginationProxy.new(25, 1, 10).previous_page.should be_nil
90
+ end
91
+
92
+ should "work for any page other than first" do
93
+ PaginationProxy.new(25, 2, 10).previous_page.should == 1
94
+ PaginationProxy.new(25, 3, 10).previous_page.should == 2
95
+ end
96
+ end
97
+
98
+ context "out_of_bounds?" do
99
+ should "be true if current_page is greater than total_pages" do
100
+ PaginationProxy.new(25, 10, 4).out_of_bounds?.should be_true
101
+ end
102
+
103
+ should "be false if current_page is less than total_pages" do
104
+ PaginationProxy.new(25, 10, 1).out_of_bounds?.should be_false
105
+ PaginationProxy.new(25, 2, 10).out_of_bounds?.should be_false
106
+ end
107
+
108
+ should "be false if current_page is equal to total_pages" do
109
+ PaginationProxy.new(25, 3, 10).out_of_bounds?.should be_false
110
+ end
111
+ end
112
+ end # end of pagination proxy
113
+ end # end of test case
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+
3
+ class TestRailsCompatibility < Test::Unit::TestCase
4
+ class Item
5
+ include MongoMapper::EmbeddedDocument
6
+ key :for_all, String
7
+ end
8
+
9
+ class FirstItem < Item
10
+ key :first_only, String
11
+ many :second_items
12
+ end
13
+
14
+ class SecondItem < Item
15
+ key :second_only, String
16
+ end
17
+
18
+ context "EmbeddedDocument" do
19
+ should "raise error for to_param as embedded do not have id's" do
20
+ lambda { Item.new.to_param }.should raise_error
21
+ end
22
+
23
+ should "alias many to has_many" do
24
+ FirstItem.should respond_to(:has_many)
25
+ FirstItem.method(:has_many).should == FirstItem.method(:many)
26
+ end
27
+
28
+ should "have column names" do
29
+ Item.column_names.sort.should == ['_id', 'for_all']
30
+ FirstItem.column_names.sort.should == ['_id', 'first_only', 'for_all']
31
+ SecondItem.column_names.sort.should == ['_id', 'for_all', 'second_only']
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ class SerializationTest < Test::Unit::TestCase
4
+ def setup
5
+ @document = Class.new do
6
+ include MongoMapper::EmbeddedDocument
7
+ key :name, String
8
+ key :age, Integer
9
+ key :awesome, Boolean
10
+ key :preferences, Hash
11
+ key :created_at, Time
12
+ end
13
+
14
+ @instance = @document.new(
15
+ :name => 'John Doe',
16
+ :age => 25,
17
+ :awesome => true,
18
+ :preferences => {:language => 'Ruby'},
19
+ :created_at => Time.now.change(:usec => 0)
20
+ )
21
+ end
22
+
23
+ [:json].each do |format|
24
+ context format do
25
+ should "be reversable" do
26
+ serialized = @instance.send("to_#{format}")
27
+ unserialized = @document.new.send("from_#{format}", serialized)
28
+
29
+ assert_equal @instance, unserialized
30
+ end
31
+
32
+ should "allow attribute only filtering" do
33
+ serialized = @instance.send("to_#{format}", :only => [ :age, :name ])
34
+ unserialized = @document.new.send("from_#{format}", serialized)
35
+
36
+ assert_equal @instance.name, unserialized.name
37
+ assert_equal @instance.age, unserialized.age
38
+ assert_nil unserialized.awesome
39
+ assert_nil unserialized.created_at
40
+ end
41
+
42
+ should "allow attribute except filtering" do
43
+ serialized = @instance.send("to_#{format}", :except => [ :age, :name ])
44
+ unserialized = @document.new.send("from_#{format}", serialized)
45
+
46
+ assert_nil unserialized.name
47
+ assert_nil unserialized.age
48
+ assert_equal @instance.awesome, unserialized.awesome
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,259 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context "Validations" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ end
9
+ end
10
+
11
+ context "Validating acceptance of" do
12
+ should "work with validates_acceptance_of macro" do
13
+ @document.key :terms, String
14
+ @document.validates_acceptance_of :terms
15
+ doc = @document.new(:terms => '')
16
+ doc.should have_error_on(:terms)
17
+ doc.terms = '1'
18
+ doc.should_not have_error_on(:terms)
19
+ end
20
+ end
21
+
22
+ context "validating confirmation of" do
23
+ should "work with validates_confirmation_of macro" do
24
+ @document.key :password, String
25
+ @document.validates_confirmation_of :password
26
+ doc = @document.new
27
+ doc.password = 'foobar'
28
+ doc.should have_error_on(:password)
29
+ doc.password_confirmation = 'foobar'
30
+ doc.should_not have_error_on(:password)
31
+ end
32
+ end
33
+
34
+ context "validating format of" do
35
+ should "work with validates_format_of macro" do
36
+ @document.key :name, String
37
+ @document.validates_format_of :name, :with => /.+/
38
+ doc = @document.new
39
+ doc.should have_error_on(:name)
40
+ doc.name = 'John'
41
+ doc.should_not have_error_on(:name)
42
+ end
43
+
44
+ should "work with :format shorcut key" do
45
+ @document.key :name, String, :format => /.+/
46
+ doc = @document.new
47
+ doc.should have_error_on(:name)
48
+ doc.name = 'John'
49
+ doc.should_not have_error_on(:name)
50
+ end
51
+ end
52
+
53
+ context "validating length of" do
54
+ should "work with validates_length_of macro" do
55
+ @document.key :name, String
56
+ @document.validates_length_of :name, :minimum => 5
57
+ doc = @document.new
58
+ doc.should have_error_on(:name)
59
+ end
60
+
61
+ context "with :length => integer shortcut" do
62
+ should "set maximum of integer provided" do
63
+ @document.key :name, String, :length => 5
64
+ doc = @document.new
65
+ doc.name = '123456'
66
+ doc.should have_error_on(:name)
67
+ doc.name = '12345'
68
+ doc.should_not have_error_on(:name)
69
+ end
70
+ end
71
+
72
+ context "with :length => range shortcut" do
73
+ setup do
74
+ @document.key :name, String, :length => 5..7
75
+ end
76
+
77
+ should "set minimum of range min" do
78
+ doc = @document.new
79
+ doc.should have_error_on(:name)
80
+ doc.name = '123456'
81
+ doc.should_not have_error_on(:name)
82
+ end
83
+
84
+ should "set maximum of range max" do
85
+ doc = @document.new
86
+ doc.should have_error_on(:name)
87
+ doc.name = '12345678'
88
+ doc.should have_error_on(:name)
89
+ doc.name = '123456'
90
+ doc.should_not have_error_on(:name)
91
+ end
92
+ end
93
+
94
+ context "with :length => hash shortcut" do
95
+ should "pass options through" do
96
+ @document.key :name, String, :length => {:minimum => 2}
97
+ doc = @document.new
98
+ doc.should have_error_on(:name)
99
+ doc.name = '12'
100
+ doc.should_not have_error_on(:name)
101
+ end
102
+ end
103
+ end # validates_length_of
104
+
105
+ context "Validating numericality of" do
106
+ should "work with validates_numericality_of macro" do
107
+ @document.key :age, Integer
108
+ @document.validates_numericality_of :age
109
+ doc = @document.new
110
+ doc.age = 'String'
111
+ doc.should have_error_on(:age)
112
+ doc.age = 23
113
+ doc.should_not have_error_on(:age)
114
+ end
115
+
116
+ context "with :numeric shortcut" do
117
+ should "work with integer or float" do
118
+ @document.key :weight, Float, :numeric => true
119
+ doc = @document.new
120
+ doc.weight = 'String'
121
+ doc.should have_error_on(:weight)
122
+ doc.weight = 23.0
123
+ doc.should_not have_error_on(:weight)
124
+ doc.weight = 23
125
+ doc.should_not have_error_on(:weight)
126
+ end
127
+ end
128
+
129
+ context "with :numeric shortcut on Integer key" do
130
+ should "only work with integers" do
131
+ @document.key :age, Integer, :numeric => true
132
+ doc = @document.new
133
+ doc.age = 'String'
134
+ doc.should have_error_on(:age)
135
+ doc.age = 23.1
136
+ doc.should have_error_on(:age)
137
+ doc.age = 23
138
+ doc.should_not have_error_on(:age)
139
+ end
140
+ end
141
+ end # numericality of
142
+
143
+ context "validating presence of" do
144
+ should "work with validates_presence_of macro" do
145
+ @document.key :name, String
146
+ @document.validates_presence_of :name
147
+ doc = @document.new
148
+ doc.should have_error_on(:name)
149
+ end
150
+
151
+ should "work with :required shortcut on key definition" do
152
+ @document.key :name, String, :required => true
153
+ doc = @document.new
154
+ doc.should have_error_on(:name)
155
+ end
156
+ end
157
+
158
+ context "validating exclusion of" do
159
+ should "throw error if enumerator not provided" do
160
+ @document.key :action, String
161
+ lambda {
162
+ @document.validates_exclusion_of :action
163
+ }.should raise_error(ArgumentError)
164
+ end
165
+
166
+ should "work with validates_exclusion_of macro" do
167
+ @document.key :action, String
168
+ @document.validates_exclusion_of :action, :within => %w(kick run)
169
+
170
+ doc = @document.new
171
+ doc.should_not have_error_on(:action)
172
+
173
+ doc.action = 'fart'
174
+ doc.should_not have_error_on(:action)
175
+
176
+ doc.action = 'kick'
177
+ doc.should have_error_on(:action, 'is reserved')
178
+ end
179
+
180
+ should "not have error if allow nil is true and value is nil" do
181
+ @document.key :action, String
182
+ @document.validates_exclusion_of :action, :within => %w(kick run), :allow_nil => true
183
+
184
+ doc = @document.new
185
+ doc.should_not have_error_on(:action)
186
+ end
187
+
188
+ should "not have error if allow blank is true and value is blank" do
189
+ @document.key :action, String
190
+ @document.validates_exclusion_of :action, :within => %w(kick run), :allow_nil => true
191
+
192
+ doc = @document.new(:action => '')
193
+ doc.should_not have_error_on(:action)
194
+ end
195
+ end
196
+
197
+ context "validating inclusion of" do
198
+ should "throw error if enumerator not provided" do
199
+ @document.key :action, String
200
+ lambda {
201
+ @document.validates_inclusion_of :action
202
+ }.should raise_error(ArgumentError)
203
+ end
204
+
205
+ should "work with validates_inclusion_of macro" do
206
+ @document.key :action, String
207
+ @document.validates_inclusion_of :action, :within => %w(kick run)
208
+
209
+ doc = @document.new
210
+ doc.should have_error_on(:action, 'is not in the list')
211
+
212
+ doc.action = 'fart'
213
+ doc.should have_error_on(:action, 'is not in the list')
214
+
215
+ doc.action = 'kick'
216
+ doc.should_not have_error_on(:action)
217
+ end
218
+
219
+ should "not have error if allow nil is true and value is nil" do
220
+ @document.key :action, String
221
+ @document.validates_inclusion_of :action, :within => %w(kick run), :allow_nil => true
222
+
223
+ doc = @document.new
224
+ doc.should_not have_error_on(:action)
225
+ end
226
+
227
+ should "not have error if allow blank is true and value is blank" do
228
+ @document.key :action, String
229
+ @document.validates_inclusion_of :action, :within => %w(kick run), :allow_blank => true
230
+
231
+ doc = @document.new(:action => '')
232
+ doc.should_not have_error_on(:action)
233
+ end
234
+ end
235
+ end # Validations
236
+
237
+ context "Adding validation errors" do
238
+ setup do
239
+ @document = Class.new do
240
+ include MongoMapper::Document
241
+ key :action, String
242
+ def action_present
243
+ errors.add(:action, 'is invalid') if action.blank?
244
+ end
245
+ end
246
+ end
247
+
248
+ should "work with validate callback" do
249
+ @document.validate :action_present
250
+
251
+ doc = @document.new
252
+ doc.action = nil
253
+ doc.should have_error_on(:action)
254
+
255
+ doc.action = 'kick'
256
+ doc.should_not have_error_on(:action)
257
+ end
258
+ end
259
+ end