mongodb-mongo-activerecord-ruby 0.0.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.
@@ -0,0 +1,35 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mongo-activerecord-ruby'
3
+ s.version = '0.0.1'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.summary = 'ActiveRecord-like models for the 10gen Mongo DB'
6
+ s.description = 'MongoRecord is an ActiveRecord-like framework for the 10gen Monog database. For more information about Mongo, see http://www.mongodb.org.'
7
+
8
+ s.add_dependency('mongodb-mongo-ruby-driver', ['>= 0.1.1'])
9
+
10
+ s.require_paths = ['lib']
11
+
12
+ s.files = ['examples/tracks.rb', 'lib/mongo_record.rb',
13
+ 'lib/mongo_record/base.rb',
14
+ 'lib/mongo_record/convert.rb',
15
+ 'lib/mongo_record/log_device.rb',
16
+ 'lib/mongo_record/sql.rb',
17
+ 'lib/mongo_record/subobject.rb',
18
+ 'README.rdoc', 'Rakefile', 'mongo-activerecord-ruby.gemspec']
19
+ s.test_files = ['tests/address.rb',
20
+ 'tests/course.rb',
21
+ 'tests/student.rb',
22
+ 'tests/test_log_device.rb',
23
+ 'tests/test_mongo.rb',
24
+ 'tests/test_sql.rb',
25
+ 'tests/track2.rb',
26
+ 'tests/track3.rb']
27
+
28
+ s.has_rdoc = true
29
+ s.rdoc_options = ['--main', 'README.rdoc', '--inline-source']
30
+ s.extra_rdoc_files = ['README.rdoc']
31
+
32
+ s.author = 'Jim Menard'
33
+ s.email = 'jim@10gen.com'
34
+ s.homepage = 'http://www.mongodb.org'
35
+ end
@@ -0,0 +1,12 @@
1
+ require 'mongo_record'
2
+
3
+ class Address < MongoRecord::Subobject
4
+
5
+ fields :street, :city, :state, :postal_code
6
+ belongs_to :student
7
+
8
+ def to_s
9
+ "#{street}\n#{city}, #{state} #{postal_code}"
10
+ end
11
+
12
+ end
@@ -0,0 +1,10 @@
1
+ class Course < MongoRecord::Base
2
+
3
+ # Declare Mongo collection name and ivars to be saved
4
+ collection_name :courses
5
+ field :name
6
+
7
+ def to_s
8
+ "Course #{name}"
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ require 'mongo_record'
2
+ require File.join(File.dirname(__FILE__), 'address')
3
+
4
+ class Score < MongoRecord::Base
5
+
6
+ field :grade
7
+ has_one :for_course, :class_name => 'Course' # Mongo will store course db reference, not duplicate object
8
+
9
+ def passed?
10
+ @grade >= 2.0
11
+ end
12
+
13
+ def to_s
14
+ "#@for_course: #@grade"
15
+ end
16
+
17
+ end
18
+
19
+ class Student < MongoRecord::Base
20
+
21
+ collection_name :students
22
+ fields :name, :email, :num_array, :created_at, :created_on, :updated_on
23
+ has_one :address
24
+ has_many :scores, :class_name => "Score"
25
+
26
+ def initialize(row=nil)
27
+ super
28
+ @address ||= Address.new
29
+ end
30
+
31
+ def add_score(course_id, grade)
32
+ @scores << Score.new(:for_course => Course.find(course_id), :grade => grade)
33
+ end
34
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright (C) 2008 10gen Inc.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify it
4
+ # under the terms of the GNU Affero General Public License, version 3, as
5
+ # published by the Free Software Foundation.
6
+ #
7
+ # This program is distributed in the hope that it will be useful, but WITHOUT
8
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
10
+ # for more details.
11
+ #
12
+ # You should have received a copy of the GNU Affero General Public License
13
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
+
15
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '../lib')
16
+ require 'rubygems'
17
+ require 'test/unit'
18
+ require 'logger'
19
+ require 'mongo'
20
+ require 'mongo_record/log_device.rb'
21
+
22
+ class LoggerTest < Test::Unit::TestCase
23
+
24
+ MAX_RECS = 3
25
+
26
+ def setup
27
+ @db = XGen::Mongo::Driver::Mongo.new.db('mongorecord-test')
28
+ @db.drop_collection('testlogger') # can't remove recs from capped colls
29
+ MongoRecord::LogDevice.connection = @db
30
+ # Create a log device with a max of MAX_RECS records
31
+ @logger = Logger.new(MongoRecord::LogDevice.new('testlogger', :size => 1_000_000, :max => MAX_RECS))
32
+ end
33
+
34
+ def teardown
35
+ @db.drop_collection('testlogger') # can't remove recs from capped colls
36
+ end
37
+
38
+ # We really don't have to test much more than this. We can trust that Mongo
39
+ # works properly.
40
+ def test_max
41
+ assert_not_nil @db
42
+ assert_equal @db.name, MongoRecord::LogDevice.connection.name
43
+ collection = MongoRecord::LogDevice.connection.collection('testlogger')
44
+ MAX_RECS.times { |i|
45
+ @logger.debug("test message #{i+1}")
46
+ assert_equal i+1, collection.count()
47
+ }
48
+
49
+ MAX_RECS.times { |i|
50
+ @logger.debug("test message #{i+MAX_RECS+1}")
51
+ assert_equal MAX_RECS, collection.count()
52
+ }
53
+ end
54
+
55
+ def test_alternate_connection
56
+ old_db = @db
57
+ alt_db = XGen::Mongo::Driver::Mongo.new.db('mongorecord-test-log-device')
58
+ begin
59
+ @db = nil
60
+ MongoRecord::LogDevice.connection = alt_db
61
+
62
+ logger = Logger.new(MongoRecord::LogDevice.new('testlogger', :size => 1_000_000, :max => MAX_RECS))
63
+ logger.debug('test message')
64
+
65
+ coll = alt_db.collection('testlogger')
66
+ assert_equal 1, coll.count()
67
+ rec = coll.find({}, :limit => 1).next_object
68
+ assert_not_nil rec
69
+ assert_match /test message/, rec['msg']
70
+ rescue => ex
71
+ fail ex.to_s
72
+ ensure
73
+ @db = old_db
74
+ MongoRecord::LogDevice.connection = @db
75
+ alt_db.drop_collection('testlogger')
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,605 @@
1
+ # Copyright (C) 2008 10gen Inc.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify it
4
+ # under the terms of the GNU Affero General Public License, version 3, as
5
+ # published by the Free Software Foundation.
6
+ #
7
+ # This program is distributed in the hope that it will be useful, but WITHOUT
8
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
10
+ # for more details.
11
+ #
12
+ # You should have received a copy of the GNU Affero General Public License
13
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
+
15
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '../lib')
16
+ require 'rubygems'
17
+ require 'test/unit'
18
+ require 'mongo_record'
19
+ require File.join(File.dirname(__FILE__), 'course')
20
+ require File.join(File.dirname(__FILE__), 'address')
21
+ require File.join(File.dirname(__FILE__), 'student')
22
+
23
+ class Track < MongoRecord::Base
24
+ collection_name :tracks
25
+ fields :artist, :album, :song, :track
26
+ def to_s
27
+ # Uses both accessor methods and ivars themselves
28
+ "artist: #{artist}, album: #{album}, song: #@song, track: #{@track ? @track.to_i : nil}"
29
+ end
30
+ end
31
+
32
+ # Same class, but this time class.name.downcase == collection name so we don't
33
+ # have to call collection_name.
34
+ class Rubytest < MongoRecord::Base
35
+ fields :artist, :album, :song, :track
36
+ def to_s
37
+ "artist: #{artist}, album: #{album}, song: #{song}, track: #{track ? track.to_i : nil}"
38
+ end
39
+ end
40
+
41
+ class MongoTest < Test::Unit::TestCase
42
+
43
+ def setup
44
+ super
45
+ @db = XGen::Mongo::Driver::Mongo.new.db('mongorecord-test')
46
+ @students = @db.collection('students')
47
+ @courses = @db.collection('courses')
48
+ @tracks = @db.collection('tracks')
49
+
50
+ @students.clear
51
+ @courses.clear
52
+ @tracks.clear
53
+
54
+ # Manually insert data without using MongoRecord::Base
55
+ @tracks.insert({:_id => XGen::Mongo::Driver::ObjectID.new, :artist => 'Thomas Dolby', :album => 'Aliens Ate My Buick', :song => 'The Ability to Swing'})
56
+ @tracks.insert({:_id => XGen::Mongo::Driver::ObjectID.new, :artist => 'Thomas Dolby', :album => 'Aliens Ate My Buick', :song => 'Budapest by Blimp'})
57
+ @tracks.insert({:_id => XGen::Mongo::Driver::ObjectID.new, :artist => 'Thomas Dolby', :album => 'The Golden Age of Wireless', :song => 'Europa and the Pirate Twins'})
58
+ @tracks.insert({:_id => XGen::Mongo::Driver::ObjectID.new, :artist => 'XTC', :album => 'Oranges & Lemons', :song => 'Garden Of Earthly Delights', :track => 1})
59
+ @mayor_id = XGen::Mongo::Driver::ObjectID.new
60
+ @tracks.insert({:_id => @mayor_id, :artist => 'XTC', :album => 'Oranges & Lemons', :song => 'The Mayor Of Simpleton', :track => 2})
61
+ @tracks.insert({:_id => XGen::Mongo::Driver::ObjectID.new, :artist => 'XTC', :album => 'Oranges & Lemons', :song => 'King For A Day', :track => 3})
62
+
63
+ @mayor_str = "artist: XTC, album: Oranges & Lemons, song: The Mayor Of Simpleton, track: 2"
64
+ @mayor_song = 'The Mayor Of Simpleton'
65
+
66
+ MongoRecord::Base.connection = @db
67
+
68
+ @spongebob_addr = Address.new(:street => "3 Pineapple Lane", :city => "Bikini Bottom", :state => "HI", :postal_code => "12345")
69
+ @bender_addr = Address.new(:street => "Planet Express", :city => "New New York", :state => "NY", :postal_code => "10001")
70
+ @course1 = Course.new(:name => 'Introductory Testing')
71
+ @course2 = Course.new(:name => 'Advanced Phlogiston Combuston Theory')
72
+ @score1 = Score.new(:for_course => @course1, :grade => 4.0)
73
+ @score2 = Score.new(:for_course => @course2, :grade => 3.5)
74
+ end
75
+
76
+ def teardown
77
+ @students.clear
78
+ @courses.clear
79
+ @tracks.clear
80
+ super
81
+ end
82
+
83
+ def test_ivars_created
84
+ t = Track.new
85
+ %w(_id artist album song track).each { |iv|
86
+ assert t.instance_variable_defined?("@#{iv}")
87
+ }
88
+ end
89
+
90
+ def test_method_generation
91
+ x = Track.new({:artist => 1, :album => 2})
92
+
93
+ assert x.respond_to?(:_id)
94
+ assert x.respond_to?(:artist)
95
+ assert x.respond_to?(:album)
96
+ assert x.respond_to?(:song)
97
+ assert x.respond_to?(:track)
98
+ assert x.respond_to?(:_id=)
99
+ assert x.respond_to?(:artist=)
100
+ assert x.respond_to?(:album=)
101
+ assert x.respond_to?(:song=)
102
+ assert x.respond_to?(:track=)
103
+ assert x.respond_to?(:_id?)
104
+ assert x.respond_to?(:artist?)
105
+ assert x.respond_to?(:album?)
106
+ assert x.respond_to?(:song?)
107
+ assert x.respond_to?(:track?)
108
+
109
+ assert_equal(1, x.artist)
110
+ assert_equal(2, x.album)
111
+ assert_nil(x.song)
112
+ assert_nil(x.track)
113
+ end
114
+
115
+ def test_initialize_block
116
+ track = Track.new { |t|
117
+ t.artist = "Me'Shell Ndegeocello"
118
+ t.album = "Peace Beyond Passion"
119
+ t.song = "Bittersweet"
120
+ }
121
+ assert_equal "Me'Shell Ndegeocello", track.artist
122
+ assert_equal "Peace Beyond Passion", track.album
123
+ assert_equal "Bittersweet", track.song
124
+ assert !track.track?
125
+ end
126
+
127
+ def test_find_by_id
128
+ assert_equal(@mayor_str, Track.find_by_id(@mayor_id).to_s)
129
+ end
130
+
131
+ def test_find_by_song
132
+ assert_equal("artist: Thomas Dolby, album: Aliens Ate My Buick, song: Budapest by Blimp, track: ", Track.find_by_song('Budapest by Blimp').to_s)
133
+ end
134
+
135
+ def test_update
136
+ t = Track.find_by_track(2)
137
+ t.track = 99
138
+ t.save
139
+ str = @mayor_str.sub(/2/, '99')
140
+ assert_equal(str, t.to_s)
141
+ assert_equal(str, Track.find_by_track(99).to_s)
142
+ end
143
+
144
+ def test_find_all
145
+ assert_all_songs Track.find(:all).inject('') { |str, t| str + t.to_s }
146
+ end
147
+
148
+ def test_find_using_hash
149
+ str = Track.find(:all, :conditions => {:album => 'Aliens Ate My Buick'}).inject('') { |str, t| str + t.to_s }
150
+ assert_match(/song: The Ability to Swing/, str)
151
+ assert_match(/song: Budapest by Blimp/, str)
152
+ end
153
+
154
+ def test_find_first
155
+ t = Track.find(:first)
156
+ assert t.kind_of?(Track)
157
+ str = t.to_s
158
+ assert_match(/artist: [^,]+,/, str, "did not find non-empty artist name")
159
+ end
160
+
161
+ def test_find_first_with_search
162
+ t = Track.find(:first, :conditions => {:track => 3})
163
+ assert_not_nil t, "oops: nil track returned"
164
+ assert_equal "artist: XTC, album: Oranges & Lemons, song: King For A Day, track: 3", t.to_s
165
+ end
166
+
167
+ def test_find_first_returns_nil_if_not_found
168
+ assert_nil Track.find(:first, :conditions => {:track => 666})
169
+ end
170
+
171
+ def test_find_all_by
172
+ str = Track.find_all_by_album('Oranges & Lemons').inject('') { |str, t| str + t.to_s }
173
+ assert_match(/song: Garden Of Earthly Delights/, str)
174
+ assert_match(/song: The Mayor Of Simpleton/, str)
175
+ assert_match(/song: King For A Day/, str)
176
+ end
177
+
178
+ def test_find_using_hash_with_array_and_range
179
+ sorted_track_titles = ['Garden Of Earthly Delights', 'King For A Day', @mayor_song]
180
+
181
+ # Array
182
+ list = Track.find(:all, :conditions => {:track => [1,2,3]}).to_a
183
+ assert_equal 3, list.length
184
+ assert_equal sorted_track_titles, list.collect{|t| t.song}.sort
185
+
186
+ # Range
187
+ list = Track.find(:all, :conditions => {:track => 1..3}).to_a
188
+ assert_equal 3, list.length
189
+ assert_equal sorted_track_titles, list.collect{|t| t.song}.sort
190
+ end
191
+
192
+ def test_new_no_arg
193
+ assert_equal "artist: , album: , song: , track: ", Track.new.to_s
194
+ end
195
+
196
+ def test_new_by_hash
197
+ assert_equal("artist: Level 42, album: Standing In The Light, song: Micro-Kid, track: 1",
198
+ Track.new(:song => 'Micro-Kid', :album => 'Standing In The Light', :artist => 'Level 42', :track => 1).to_s)
199
+ end
200
+
201
+ def test_new_and_save
202
+ x = Track.new(:artist => 'Level 42', :album => 'Standing In The Light', :song => 'Micro-Kid', :track => 1)
203
+ assert_nil(x.id)
204
+ assert x.save, "x.save returned false; expected true"
205
+ assert_not_nil(x.id)
206
+ z = Track.find(x.id)
207
+ assert_equal(x.to_s, z.to_s)
208
+ assert_equal(x._id, z._id)
209
+ end
210
+
211
+ def test_find_or_create_but_already_exists
212
+ assert_equal("artist: Thomas Dolby, album: Aliens Ate My Buick, song: The Ability to Swing, track: ",
213
+ Track.find_or_create_by_song('The Ability to Swing', :artist => 'ignored because song found').to_s)
214
+ end
215
+
216
+ def test_find_or_create_new_created
217
+ assert_equal("artist: New Artist, album: New Album, song: New Song, track: ",
218
+ Track.find_or_create_by_song('New Song', :artist => 'New Artist', :album => 'New Album').to_s)
219
+ end
220
+
221
+ def test_cursor_methods
222
+ assert_equal 2, Track.find(:all, :limit => 2).to_a.length
223
+ end
224
+
225
+ def test_return_nil_if_no_match
226
+ assert_nil Track.find(:first, :conditions => {:song => 'Does Not Compute'})
227
+ end
228
+
229
+ def test_raise_error_if_bogus_id
230
+ Track.find("bogus_id")
231
+ fail 'expected "invalid ObjectID" exception'
232
+ rescue => ex
233
+ assert_match /illegal ObjectID format/, ex.to_s
234
+ end
235
+
236
+ def test_raise_error_if_first_and_bogus_id_in_hash
237
+ Track.find(:first, :conditions => {:_id => "bogus_id"})
238
+ fail 'expected "invalid ObjectID" exception'
239
+ rescue => ex
240
+ assert_match /invalid ObjectID/, ex.to_s
241
+ end
242
+
243
+ def test_find_options
244
+ assert_equal 2, Track.find(:all, :limit => 2).to_a.length
245
+ end
246
+
247
+ def test_order_options
248
+ tracks = Track.find(:all, :order => "song asc")
249
+ assert_not_nil tracks
250
+ assert_equal "Budapest by Blimp:Europa and the Pirate Twins:Garden Of Earthly Delights:King For A Day:The Ability to Swing:The Mayor Of Simpleton",
251
+ tracks.collect {|t| t.song }.join(':')
252
+
253
+ # TODO this should work, but the database does not yet sort this properly
254
+ # tracks = Track.find(:all, :order => "artist desc, song")
255
+ # assert_not_nil tracks
256
+ # assert_equal "Garden Of Earthly Delights:King For A Day:The Mayor Of Simpleton:Budapest by Blimp:Europa and the Pirate Twins:The Ability to Swing",
257
+ # tracks.collect {|t| t.song }.join(':')
258
+ end
259
+
260
+ def test_delete
261
+ Track.find(:first, :conditions => {:song => 'King For A Day'}).delete
262
+ str = Track.find(:all).inject('') { |str, t| str + t.to_s }
263
+ assert_match(/song: The Ability to Swing/, str)
264
+ assert_match(/song: Budapest by Blimp/, str)
265
+ assert_match(/song: Europa and the Pirate Twins/, str)
266
+ assert_match(/song: Garden Of Earthly Delights/, str)
267
+ assert_match(/song: The Mayor Of Simpleton/, str)
268
+ assert_no_match(/song: King For A Day/, str)
269
+ end
270
+
271
+ def test_class_delete
272
+ Track.delete(@mayor_id)
273
+ assert_no_match(/song: The Mayor Of Simpleton/, Track.find(:all).inject('') { |str, t| str + t.to_s })
274
+ end
275
+
276
+ def test_delete_all
277
+ Track.delete_all({:artist => 'XTC'})
278
+ assert_no_match(/artist: XTC/, Track.find(:all).inject('') { |str, t| str + t.to_s })
279
+
280
+ Track.delete_all(["song = ?", 'The Mayor Of Simpleton'])
281
+ assert_no_match(/song: The Mayor Of Simpleton/, Track.find(:all).inject('') { |str, t| str + t.to_s })
282
+
283
+ Track.delete_all("song = 'King For A Day'")
284
+ assert_no_match(/song: King For A Day/, Track.find(:all).inject('') { |str, t| str + t.to_s })
285
+
286
+ Track.delete_all()
287
+ assert_equal 0, Track.count
288
+ end
289
+
290
+ def test_find_by_mql_not_implemented
291
+ Track.find_by_mql("")
292
+ fail "should have raised a 'not implemented' exception"
293
+ rescue => ex
294
+ assert_equal("not implemented", ex.to_s)
295
+ end
296
+
297
+ def test_count
298
+ assert_equal 6, Track.count
299
+ assert_equal 3, Track.count(:conditions => {:artist => 'XTC'})
300
+ end
301
+
302
+ def test_select
303
+ str = Track.find(:all, :select => :album).inject('') { |str, t| str + t.to_s }
304
+ assert str.include?("artist: , album: Oranges & Lemons, song: , track:")
305
+ end
306
+
307
+ def test_find_using_id
308
+ t = Track.find_by_song('King For A Day')
309
+ tid = t._id
310
+ # first is string id, second is ObjectID
311
+ str = Track.find([@mayor_id, tid]).inject('') { |str, t| str + t.to_s }
312
+ assert str.include?(@mayor_str)
313
+ assert str.include?('King For A Day')
314
+ end
315
+
316
+ def test_find_one_using_id
317
+ t = Track.find(@mayor_id)
318
+ assert_not_nil t
319
+ assert_match /song: The Mayor Of Simpleton/, t.to_s
320
+ end
321
+
322
+ def test_select_find_by_id
323
+ t = Track.find(@mayor_id, :select => :album)
324
+ assert t.album?
325
+ assert !t.artist?
326
+ assert !t.song?
327
+ assert !t.track?
328
+ assert_equal "artist: , album: Oranges & Lemons, song: , track: ", t.to_s
329
+ end
330
+
331
+ def test_has_one_initialize
332
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :address => @spongebob_addr)
333
+
334
+ assert_not_nil s.address, "Address not set correctly in Student#initialize"
335
+ assert_equal '3 Pineapple Lane', s.address.street
336
+ end
337
+
338
+ def test_has_one_save_and_find
339
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :address => @spongebob_addr)
340
+ s.save
341
+
342
+ s2 = Student.find(:first)
343
+ assert_equal 'Spongebob Squarepants', s2.name
344
+ assert_equal 'spongebob@example.com', s2.email
345
+ a2 = s2.address
346
+ assert_not_nil a2
347
+ assert_kind_of Address, a2
348
+ assert_equal @spongebob_addr.street, a2.street
349
+ assert_equal @spongebob_addr.city, a2.city
350
+ assert_equal @spongebob_addr.state, a2.state
351
+ assert_equal @spongebob_addr.postal_code, a2.postal_code
352
+ end
353
+
354
+ def test_student_array_field
355
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :num_array => [100, 90, 80])
356
+ s.save
357
+
358
+ s2 = Student.find(:first)
359
+ assert_equal [100, 90, 80], s2.num_array
360
+ end
361
+
362
+ def test_has_many_initialize
363
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :scores => [@score1, @score2])
364
+ assert_not_nil s.scores
365
+ assert_equal 2, s.scores.length
366
+ assert_equal @score1, s.scores[0]
367
+ assert_equal @score2, s.scores[1]
368
+ end
369
+
370
+ def test_has_many_initialize_one_value
371
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :scores => @score1)
372
+ assert_not_nil s.scores
373
+ assert_equal 1, s.scores.length
374
+ assert_equal @score1, s.scores[0]
375
+ end
376
+
377
+ def test_has_many_save_and_find
378
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :scores => [@score1, @score2])
379
+ s.save
380
+
381
+ s2 = Student.find(:first)
382
+ assert_equal 'Spongebob Squarepants', s2.name
383
+ assert_equal 'spongebob@example.com', s2.email
384
+ list = s2.scores
385
+ assert_not_nil list
386
+ assert_equal 2, list.length
387
+ score = list.first
388
+ assert_not_nil score
389
+ assert_kind_of Score, score
390
+ assert (score.for_course.name == @score1.for_course.name && score.grade == @score1.grade), "oops: first score is wrong: #{score}"
391
+ end
392
+
393
+ def test_field_query_methods
394
+ s = Student.new(:name => 'Spongebob Squarepants', :email => 'spongebob@example.com', :scores => [@score1, @score2])
395
+ assert s.name?
396
+ assert s.email?
397
+ assert s.scores
398
+
399
+ s = Student.new(:name => 'Spongebob Squarepants')
400
+ assert s.name?
401
+ assert !s.email?
402
+ assert !s.scores?
403
+
404
+ s.email = ''
405
+ assert !s.email?
406
+ end
407
+
408
+ def test_new_record
409
+ t = Track.new
410
+ assert t.new_record?
411
+ t.save
412
+ assert !t.new_record?
413
+
414
+ t = Track.create(:artist => 'Level 42', :album => 'Standing In The Light', :song => 'Micro-Kid', :track => 1)
415
+ assert !t.new_record?
416
+
417
+ t = Track.find(:first)
418
+ assert !t.new_record?
419
+
420
+ t = Track.find_or_create_by_song('New Song', :artist => 'New Artist', :album => 'New Album')
421
+ assert !t.new_record?
422
+
423
+ t = Track.find_or_initialize_by_song('Newer Song', :artist => 'Newer Artist', :album => 'Newer Album')
424
+ assert t.new_record?
425
+ end
426
+
427
+ def test_sql_parsing
428
+ t = Track.find(:first, :conditions => "song = '#{@mayor_song}'")
429
+ assert_equal @mayor_str, t.to_s
430
+ end
431
+
432
+ def test_sql_substitution
433
+ s = @mayor_song
434
+ t = Track.find(:first, :conditions => ["song = ?", s])
435
+ assert_equal @mayor_str, t.to_s
436
+ end
437
+
438
+ def test_sql_named_substitution
439
+ t = Track.find(:first, :conditions => ["song = :song", {:song => @mayor_song}])
440
+ assert_equal @mayor_str, t.to_s
441
+ end
442
+
443
+ def test_sql_like
444
+ t = Track.find(:first, :conditions => "song like '%Simp%'")
445
+ assert_equal @mayor_str, t.to_s
446
+ end
447
+
448
+ def test_sql_in
449
+ str = Track.find(:all, :conditions => "song in ('#{@mayor_song}', 'King For A Day')").inject('') { |str, t| str + t.to_s }
450
+ assert str.include?(@mayor_song)
451
+ assert str.include?('King For A Day')
452
+
453
+ list = Track.find(:all, :conditions => "track in (1,2,3)").to_a
454
+ assert_equal 3, list.length
455
+ assert_equal ['Garden Of Earthly Delights', 'King For A Day', @mayor_song], list.collect{|t| t.song}.sort
456
+ end
457
+
458
+ def test_in_array
459
+ str = Track.find(:all, :conditions => ["song in (?)", [@mayor_song, 'King For A Day']]).inject('') { |str, t| str + t.to_s }
460
+ assert str.include?(@mayor_song)
461
+ assert str.include?('King For A Day')
462
+ end
463
+
464
+ def test_in_array_rails_syntax
465
+ str = Track.find(:all, :conditions => {:song => [@mayor_song, 'King For A Day']}).inject('') { |str, t| str + t.to_s }
466
+ assert str.include?(@mayor_song)
467
+ assert str.include?('King For A Day')
468
+ end
469
+
470
+ def test_in_named_array
471
+ str = Track.find(:all, :conditions => ["song in (:songs)", {:songs => [@mayor_song, 'King For A Day']}]).inject('') { |str, t| str + t.to_s }
472
+ assert str.include?(@mayor_song)
473
+ assert str.include?('King For A Day')
474
+ end
475
+
476
+ def test_where
477
+ # function
478
+ str = Track.find(:all, :where => "function() { return obj.song == '#{@mayor_song}'; }").inject('') { |str, t| str + t.to_s }
479
+ assert_equal @mayor_str, str
480
+
481
+ # expression
482
+ str = Track.find(:all, :where => "obj.song == '#{@mayor_song}'").inject('') { |str, t| str + t.to_s }
483
+ assert_equal @mayor_str, str
484
+ end
485
+
486
+ def test_destroy
487
+ Track.destroy(@mayor_id)
488
+ begin
489
+ Track.find(@mayor_id)
490
+ fail "expected exception about missing ID"
491
+ rescue => ex
492
+ assert_match /Couldn't find Track with ID=#@mayor_id/, ex.to_s # ' <= for Emacs font lock mode
493
+ end
494
+ end
495
+
496
+ # Potential bug: if this test runs at midnight, a create runs before midnight
497
+ # and the update runs after, then this test will fail.
498
+ def test_time_updates
499
+ s = Student.new(:name => 'Spongebob Squarepants')
500
+ assert s.instance_variable_defined?(:@created_at)
501
+
502
+ assert !s.created_at?
503
+ assert !s.created_on?
504
+ assert !s.updated_on?
505
+
506
+ s.save
507
+ assert s.created_at?
508
+ assert_kind_of Time, s.created_at
509
+ assert s.created_on?
510
+ assert_kind_of Time, s.created_on
511
+ assert !s.updated_on?
512
+ t = Time.now
513
+ assert_equal Time.local(t.year, t.month, t.day), s.created_on
514
+
515
+ s.save
516
+ assert s.created_at?
517
+ assert s.created_on?
518
+ assert s.updated_on?
519
+ assert_kind_of Time, s.created_at
520
+ assert_equal s.created_on, s.updated_on
521
+ end
522
+
523
+ # # TODO dbrefs are not yet implemented
524
+ # # This reproduces a bug where DBRefs weren't being created properly because
525
+ # # the MongoRecord::Base objects weren't storing the magic _ns, _update, and
526
+ # # other values set by the database.
527
+ # def test_db_ref
528
+ # s = Student.new(:name => 'Spongebob Squarepants', :address => @spongebob_addr)
529
+ # s.save
530
+
531
+ # @course1.save
532
+ # assert_not_nil @course1.id
533
+
534
+ # s.add_score(@course1.id, 3.5)
535
+ # s.save # This used to blow up
536
+
537
+ # score = s.scores.first
538
+ # assert_not_nil score
539
+ # assert_equal @course1.name, score.for_course.name
540
+
541
+ # # Now change the name of @course1 and see the student's score's course
542
+ # # name change.
543
+ # @course1.name = 'changed'
544
+ # @course1.save
545
+
546
+ # s = Student.find(:first, :conditions => "name = 'Spongebob Squarepants'")
547
+ # assert_not_nil s
548
+ # assert_equal 1, s.scores.length
549
+ # assert_equal 'changed', s.scores.first.for_course.name
550
+ # end
551
+
552
+ def test_subobjects_have_no_ids
553
+ @spongebob_addr.id
554
+ rescue => ex
555
+ assert_match /Subobjects don't have ids/, ex.to_s # ' <= for Emacs font-lock mode
556
+ end
557
+
558
+ def test_can_not_save_subobject
559
+ @spongebob_addr.save
560
+ fail "expected failed save of address"
561
+ rescue => ex
562
+ assert_match /Subobjects/, ex.to_s
563
+ end
564
+
565
+ def test_alternate_connection
566
+ old_db = MongoRecord::Base.connection
567
+ assert_equal @db, old_db
568
+ alt_db = XGen::Mongo::Driver::Mongo.new.db('mongorecord-test-alt-conn')
569
+ assert_not_equal old_db, alt_db
570
+ alt_db.drop_collection('students')
571
+ begin
572
+ @db = nil
573
+ MongoRecord::Base.connection = alt_db
574
+ assert_equal alt_db, MongoRecord::Base.connection
575
+
576
+ assert_equal 0, alt_db.collection('students').count()
577
+ s = Student.new(:name => 'Spongebob Squarepants', :address => @spongebob_addr)
578
+ assert s.save, "save failed"
579
+ assert_equal 1, alt_db.collection('students').count()
580
+ ensure
581
+ @db = old_db
582
+ MongoRecord::Base.connection = @db
583
+ alt_db.drop_collection('students')
584
+ end
585
+ end
586
+
587
+ def test_method_missing
588
+ begin
589
+ Track.foobar
590
+ fail "expected 'undefined method' exception"
591
+ rescue => ex
592
+ assert_match /undefined method \`foobar\' for Track:Class/, ex.to_s
593
+ end
594
+ end
595
+
596
+ def assert_all_songs(str)
597
+ assert_match(/song: The Ability to Swing/, str)
598
+ assert_match(/song: Budapest by Blimp/, str)
599
+ assert_match(/song: Europa and the Pirate Twins/, str)
600
+ assert_match(/song: Garden Of Earthly Delights/, str)
601
+ assert_match(/song: The Mayor Of Simpleton/, str)
602
+ assert_match(/song: King For A Day/, str)
603
+ end
604
+
605
+ end