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.
- data/README.rdoc +49 -0
- data/Rakefile +39 -0
- data/examples/tracks.rb +107 -0
- data/lib/mongo_record.rb +23 -0
- data/lib/mongo_record/base.rb +827 -0
- data/lib/mongo_record/convert.rb +45 -0
- data/lib/mongo_record/log_device.rb +113 -0
- data/lib/mongo_record/sql.rb +237 -0
- data/lib/mongo_record/subobject.rb +111 -0
- data/mongo-activerecord-ruby.gemspec +35 -0
- data/tests/address.rb +12 -0
- data/tests/course.rb +10 -0
- data/tests/student.rb +34 -0
- data/tests/test_log_device.rb +79 -0
- data/tests/test_mongo.rb +605 -0
- data/tests/test_sql.rb +176 -0
- data/tests/track2.rb +9 -0
- data/tests/track3.rb +9 -0
- metadata +79 -0
@@ -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
|
data/tests/address.rb
ADDED
data/tests/course.rb
ADDED
data/tests/student.rb
ADDED
@@ -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
|
data/tests/test_mongo.rb
ADDED
@@ -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
|