mongodb-mongo-activerecord-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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