mongoose 0.1.0 → 0.1.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 +51 -1
- data/changes.txt +11 -0
- data/example/simple_examples.rb +47 -43
- data/lib/mongoose.rb +1 -3
- data/lib/mongoose/column.rb +10 -38
- data/lib/mongoose/database.rb +4 -13
- data/lib/mongoose/skiplist.rb +407 -336
- data/lib/mongoose/table.rb +53 -43
- data/lib/mongoose/util.rb +1 -1
- data/test/tc_relations.rb +139 -0
- data/test/tc_table.rb +51 -5
- data/test/ts_mongoose.rb +1 -0
- metadata +3 -2
data/lib/mongoose/table.rb
CHANGED
@@ -67,7 +67,7 @@ class Table
|
|
67
67
|
#-----------------------------------------------------------------------------
|
68
68
|
def self.has_many(kind)
|
69
69
|
table_name = Util.singularize(kind.to_s).to_sym
|
70
|
-
class_name = Util.us_case_to_class_case(table_name
|
70
|
+
class_name = Util.us_case_to_class_case(table_name)
|
71
71
|
col = Util.col_name_for_class(self.to_s)
|
72
72
|
|
73
73
|
define_method(kind.to_sym) do
|
@@ -82,7 +82,7 @@ class Table
|
|
82
82
|
#-----------------------------------------------------------------------------
|
83
83
|
def self.has_one(kind)
|
84
84
|
table_name = kind.to_sym
|
85
|
-
class_name = Util.us_case_to_class_case(table_name
|
85
|
+
class_name = Util.us_case_to_class_case(table_name)
|
86
86
|
col = Util.col_name_for_class(self.to_s)
|
87
87
|
|
88
88
|
define_method(kind.to_sym) do
|
@@ -95,7 +95,8 @@ class Table
|
|
95
95
|
# Table.belongs_to
|
96
96
|
#-----------------------------------------------------------------------------
|
97
97
|
def self.belongs_to(kind)
|
98
|
-
|
98
|
+
table_name = kind.to_sym
|
99
|
+
class_name = Util.us_case_to_class_case(table_name)
|
99
100
|
|
100
101
|
define_method(kind) do
|
101
102
|
klass = Object.const_get(class_name.to_s)
|
@@ -113,28 +114,14 @@ class Table
|
|
113
114
|
# Table.last_id_used
|
114
115
|
#-----------------------------------------------------------------------------
|
115
116
|
def self.last_id_used
|
116
|
-
self.
|
117
|
-
end
|
118
|
-
|
119
|
-
#-----------------------------------------------------------------------------
|
120
|
-
# Table.last_id_used=
|
121
|
-
#-----------------------------------------------------------------------------
|
122
|
-
def self.last_id_used=(value)
|
123
|
-
self.db.tables[self][:last_id_used] = value
|
117
|
+
self.class.read_header[:last_id_used]
|
124
118
|
end
|
125
119
|
|
126
120
|
#-----------------------------------------------------------------------------
|
127
121
|
# Table.deleted_recs_counter
|
128
122
|
#-----------------------------------------------------------------------------
|
129
123
|
def self.deleted_recs_counter
|
130
|
-
self.
|
131
|
-
end
|
132
|
-
|
133
|
-
#-----------------------------------------------------------------------------
|
134
|
-
# Table.deleted_recs_counter=
|
135
|
-
#-----------------------------------------------------------------------------
|
136
|
-
def self.deleted_recs_counter=(value)
|
137
|
-
self.db.tables[self][:deleted_recs_counter]=(value)
|
124
|
+
self.class.read_header[:deleted_recs_counter]
|
138
125
|
end
|
139
126
|
|
140
127
|
#-----------------------------------------------------------------------------
|
@@ -149,21 +136,16 @@ class Table
|
|
149
136
|
# Table.write_header
|
150
137
|
#-----------------------------------------------------------------------------
|
151
138
|
def self.write_header(header)
|
152
|
-
File.open(File.join(self.path, self.table_name.to_s + TBL_HDR_EXT), 'w'
|
153
|
-
|
154
|
-
end
|
139
|
+
File.open(File.join(self.path, self.table_name.to_s + TBL_HDR_EXT), 'w'
|
140
|
+
) { |f| YAML.dump(header, f) }
|
155
141
|
end
|
156
142
|
|
157
143
|
#-----------------------------------------------------------------------------
|
158
144
|
# Table.init_table
|
159
145
|
#-----------------------------------------------------------------------------
|
160
146
|
def self.init_table
|
161
|
-
|
162
147
|
tbl_header = self.read_header
|
163
148
|
|
164
|
-
self.last_id_used = tbl_header[:last_id_used]
|
165
|
-
self.deleted_recs_counter = tbl_header[:deleted_recs_counter]
|
166
|
-
|
167
149
|
self.read_header[:columns].each do |c|
|
168
150
|
self.init_column(c[:name], c[:data_type], Object.full_const_get(c[:class])
|
169
151
|
)
|
@@ -385,9 +367,7 @@ class Table
|
|
385
367
|
# initialize
|
386
368
|
#-----------------------------------------------------------------------------
|
387
369
|
def initialize(*values)
|
388
|
-
self.class.columns.zip(values).each
|
389
|
-
send("#{c.name}=", v)
|
390
|
-
end
|
370
|
+
self.class.columns.zip(values).each { |c,v| send("#{c.name}=", v) }
|
391
371
|
end
|
392
372
|
|
393
373
|
#-----------------------------------------------------------------------------
|
@@ -395,25 +375,23 @@ class Table
|
|
395
375
|
#-----------------------------------------------------------------------------
|
396
376
|
def save
|
397
377
|
self.class.columns.each do |c|
|
378
|
+
# First checks to see if validates_presence_of was set in class def.
|
398
379
|
raise "Value required for #{c.name}!" if respond_to?('required?') and \
|
399
380
|
required?(c.name) and send(c.name).nil?
|
381
|
+
# Next checks to see if validates_presence_of was set in #add_column.
|
400
382
|
raise "Value required for #{c.name}!" if c.required? and send(c.name).nil?
|
401
383
|
end
|
402
384
|
|
385
|
+
# Add new record.
|
403
386
|
if @id.nil?
|
404
|
-
|
405
|
-
self.class.
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
@id = self.class.last_id_used
|
411
|
-
tbl_header[:last_id_used] = self.class.last_id_used
|
412
|
-
self.class.write_header(tbl_header)
|
387
|
+
id = increment_last_id_used
|
388
|
+
append_record(id, self.class.column_names[1..-1].collect { |col_name|
|
389
|
+
send(col_name) })
|
390
|
+
@id = id
|
391
|
+
# Update existing record.
|
413
392
|
else
|
414
393
|
update_record(@id, self.class.columns[1..-1].collect { |c| send(c.name) })
|
415
394
|
end
|
416
|
-
|
417
395
|
return true
|
418
396
|
end
|
419
397
|
|
@@ -431,12 +409,16 @@ class Table
|
|
431
409
|
raise IndexCorruptError, "Index ID does not match table ID!", caller \
|
432
410
|
unless rec[1] == @id
|
433
411
|
|
412
|
+
# First array position of record is the deleted flag: true means deleted
|
434
413
|
rec[0] = true
|
435
414
|
|
415
|
+
# Record is not actually deleted; it just has its deleted flag set to
|
416
|
+
# true.
|
436
417
|
write_record(fptr, fpos_rec_start, Marshal.dump(rec))
|
437
418
|
increment_deleted_recs_counter
|
438
419
|
end
|
439
420
|
|
421
|
+
# Remove all index recs pointing to this record.
|
440
422
|
self.class.columns.each_with_index do |c,i|
|
441
423
|
if i == 0
|
442
424
|
c.remove_index_rec(@id)
|
@@ -445,6 +427,7 @@ class Table
|
|
445
427
|
end
|
446
428
|
end
|
447
429
|
|
430
|
+
# Don't allow any more changes to this record.
|
448
431
|
freeze
|
449
432
|
end
|
450
433
|
|
@@ -463,9 +446,12 @@ class Table
|
|
463
446
|
fptr.seek(0, IO::SEEK_END)
|
464
447
|
fpos = fptr.tell
|
465
448
|
|
449
|
+
# Append record to end of file, making sure to add deleted flag and record
|
450
|
+
# id to front of record.
|
466
451
|
fpos = write_record(fptr, 'end', Marshal.dump([false, id].concat(values)))
|
467
452
|
end
|
468
453
|
|
454
|
+
# Update indexes with new record.
|
469
455
|
self.class.columns.each_with_index do |c,i|
|
470
456
|
if i == 0
|
471
457
|
c.add_index_rec(id, fpos)
|
@@ -495,15 +481,25 @@ class Table
|
|
495
481
|
|
496
482
|
old_rec_length = fptr.tell - fpos_rec_start
|
497
483
|
|
484
|
+
# If updates did not change record length, we can write it back out to
|
485
|
+
# the same spot in the file...
|
498
486
|
if new_rec.length == old_rec_length
|
499
487
|
write_record(fptr, fpos_rec_start, new_rec)
|
500
488
|
else
|
501
|
-
|
489
|
+
# Set deleted flag to true and update old rec position. Increment
|
490
|
+
# deleted records counter.
|
491
|
+
old_rec[0] = true
|
492
|
+
write_record(fptr, fpos_rec_start, Marshal.dump(old_rec))
|
502
493
|
increment_deleted_recs_counter
|
494
|
+
|
495
|
+
# Append the updated record to the end of the file and update the
|
496
|
+
# record id index.
|
497
|
+
fpos = write_record(fptr, 'end', new_rec)
|
503
498
|
self.class.columns[0].add_index_rec(id, fpos)
|
504
499
|
end
|
505
500
|
end
|
506
501
|
|
502
|
+
# Update all of the indexed columns with the updated record data.
|
507
503
|
self.class.columns[1..-1].each do |c|
|
508
504
|
unless temp_instance.send(c.name) == send(c.name)
|
509
505
|
if c.indexed?
|
@@ -514,14 +510,24 @@ class Table
|
|
514
510
|
end
|
515
511
|
end
|
516
512
|
|
513
|
+
#-----------------------------------------------------------------------------
|
514
|
+
# increment_last_id_used
|
515
|
+
#-----------------------------------------------------------------------------
|
516
|
+
def increment_last_id_used
|
517
|
+
tbl_header = self.class.read_header
|
518
|
+
tbl_header[:last_id_used] += 1
|
519
|
+
self.class.write_header(tbl_header)
|
520
|
+
return tbl_header[:last_id_used]
|
521
|
+
end
|
522
|
+
|
517
523
|
#-----------------------------------------------------------------------------
|
518
524
|
# increment_deleted_recs_counter
|
519
525
|
#-----------------------------------------------------------------------------
|
520
526
|
def increment_deleted_recs_counter
|
521
527
|
tbl_header = self.class.read_header
|
522
|
-
|
523
|
-
tbl_header[:deleted_recs_counter] = self.class.deleted_recs_counter
|
528
|
+
tbl_header[:deleted_recs_counter] += 1
|
524
529
|
self.class.write_header(tbl_header)
|
530
|
+
return tbl_header[:deleted_recs_counter]
|
525
531
|
end
|
526
532
|
|
527
533
|
#-----------------------------------------------------------------------------
|
@@ -551,7 +557,7 @@ class Collection
|
|
551
557
|
end
|
552
558
|
|
553
559
|
def each
|
554
|
-
records.each { |rec| yield rec }
|
560
|
+
@records.each { |rec| yield rec }
|
555
561
|
end
|
556
562
|
|
557
563
|
def <<(rec)
|
@@ -563,6 +569,10 @@ class Collection
|
|
563
569
|
def append(rec)
|
564
570
|
self << rec
|
565
571
|
end
|
572
|
+
|
573
|
+
def size
|
574
|
+
@records.size
|
575
|
+
end
|
566
576
|
end
|
567
577
|
|
568
578
|
end
|
data/lib/mongoose/util.rb
CHANGED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubygems'
|
6
|
+
require_gem 'Mongoose'
|
7
|
+
rescue LoadError
|
8
|
+
require 'mongoose'
|
9
|
+
end
|
10
|
+
|
11
|
+
class Pilot < Mongoose::Table
|
12
|
+
has_one :plane
|
13
|
+
has_many :flights
|
14
|
+
end
|
15
|
+
|
16
|
+
class Plane < Mongoose::Table
|
17
|
+
belongs_to :pilot
|
18
|
+
end
|
19
|
+
|
20
|
+
class Flight < Mongoose::Table
|
21
|
+
belongs_to :pilot
|
22
|
+
end
|
23
|
+
|
24
|
+
class TestRelations < Test::Unit::TestCase
|
25
|
+
def setup
|
26
|
+
@db = Mongoose::Database.new(:path => Dir.tmpdir)
|
27
|
+
|
28
|
+
@db.create_table(:pilot) do |tbl|
|
29
|
+
tbl.add_indexed_column(:pilot_name, :string)
|
30
|
+
tbl.add_column(:years_flying, :integer)
|
31
|
+
end
|
32
|
+
|
33
|
+
@db.create_table(:plane) do |tbl|
|
34
|
+
tbl.add_indexed_column(:plane_name, :string)
|
35
|
+
tbl.add_column(:country, :string)
|
36
|
+
tbl.add_indexed_column(:speed, :integer)
|
37
|
+
tbl.add_column(:range, :integer)
|
38
|
+
tbl.add_column(:pilot_id, :integer)
|
39
|
+
end
|
40
|
+
|
41
|
+
@db.create_table(:flight) do |tbl|
|
42
|
+
tbl.add_column(:origin, :string)
|
43
|
+
tbl.add_column(:destination, :string)
|
44
|
+
tbl.add_column(:pilot_id, :integer)
|
45
|
+
end
|
46
|
+
|
47
|
+
@doolittle = Pilot.new
|
48
|
+
@doolittle.pilot_name = 'Doolittle, James'
|
49
|
+
@doolittle.years_flying = 15
|
50
|
+
@doolittle.save
|
51
|
+
|
52
|
+
@amelia = Pilot.new
|
53
|
+
@amelia.pilot_name = 'Earhart, Amelia'
|
54
|
+
@amelia.years_flying = 10
|
55
|
+
@amelia.save
|
56
|
+
|
57
|
+
rec = Plane.new
|
58
|
+
rec.plane_name = 'P-51'
|
59
|
+
rec.country = 'USA'
|
60
|
+
rec.speed = 402
|
61
|
+
rec.range = 1205
|
62
|
+
rec.pilot_id = @doolittle.id
|
63
|
+
rec.save
|
64
|
+
|
65
|
+
rec = Plane.new
|
66
|
+
rec.plane_name = 'Spitfire'
|
67
|
+
rec.country = 'Great Britain'
|
68
|
+
rec.speed = 333
|
69
|
+
rec.range = 454
|
70
|
+
rec.pilot_id = @amelia.id
|
71
|
+
rec.save
|
72
|
+
|
73
|
+
rec = Flight.new
|
74
|
+
rec.pilot_id = @doolittle.id
|
75
|
+
rec.origin = 'Army'
|
76
|
+
rec.destination = 'Nowhere'
|
77
|
+
rec.save
|
78
|
+
|
79
|
+
rec = Flight.new
|
80
|
+
rec.pilot_id = @amelia.id
|
81
|
+
rec.origin = 'USA'
|
82
|
+
rec.destination = 'France'
|
83
|
+
|
84
|
+
rec.save
|
85
|
+
rec = Flight.new
|
86
|
+
rec.pilot_id = @amelia.id
|
87
|
+
rec.origin = 'USA'
|
88
|
+
rec.destination = 'Phillipines'
|
89
|
+
rec.save
|
90
|
+
|
91
|
+
rec = Flight.new
|
92
|
+
rec.pilot_id = @amelia.id
|
93
|
+
rec.origin = 'China'
|
94
|
+
rec.destination = 'Unknown'
|
95
|
+
rec.save
|
96
|
+
end
|
97
|
+
|
98
|
+
def teardown
|
99
|
+
@db.drop_table(:pilot)
|
100
|
+
@db.drop_table(:plane)
|
101
|
+
@db.drop_table(:flight)
|
102
|
+
@db.close
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_has_one
|
106
|
+
assert_equal('P-51', @doolittle.plane.plane_name)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_has_many_001
|
110
|
+
assert_equal(3, @amelia.flights.size)
|
111
|
+
assert(@amelia.flights.any? {|f| f.destination == 'France'})
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_has_many_002
|
115
|
+
rec = Flight.new
|
116
|
+
rec.pilot_id = @amelia.id
|
117
|
+
rec.origin = 'Tuscon'
|
118
|
+
rec.destination = 'Phoenix'
|
119
|
+
@amelia.flights << rec
|
120
|
+
|
121
|
+
assert(@amelia.flights.any? {|f| f.origin == 'Tuscon'})
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_belongs_to_001
|
125
|
+
assert_equal('Earhart, Amelia', Plane.find(2).pilot.pilot_name)
|
126
|
+
assert_equal('Doolittle, James', Flight.find(1).pilot.pilot_name)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_belongs_to_002
|
130
|
+
red_baron = Pilot.new
|
131
|
+
red_baron.pilot_name = 'Baron, The Red'
|
132
|
+
red_baron.years_flying = 50
|
133
|
+
spitfire = Plane.find(:first) {|plane| plane.plane_name == 'Spitfire'}
|
134
|
+
spitfire.pilot = red_baron
|
135
|
+
|
136
|
+
assert('Baron, The Red', spitfire.pilot.pilot_name)
|
137
|
+
assert_equal('Spitfire', red_baron.plane.plane_name)
|
138
|
+
end
|
139
|
+
end
|
data/test/tc_table.rb
CHANGED
@@ -74,7 +74,17 @@ class TestTable < Test::Unit::TestCase
|
|
74
74
|
assert_equal(1205, rec.range)
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
77
|
+
def test_update_record_001
|
78
|
+
rec = Plane.find(2)
|
79
|
+
rec.country = 'England'
|
80
|
+
rec.save
|
81
|
+
|
82
|
+
rec = Plane.find(2)
|
83
|
+
|
84
|
+
assert_equal('England', rec.country)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_update_record_002
|
78
88
|
rec = Plane.find(1)
|
79
89
|
rec.speed = 405
|
80
90
|
rec.save
|
@@ -84,14 +94,23 @@ class TestTable < Test::Unit::TestCase
|
|
84
94
|
assert_equal(rec.speed, 405)
|
85
95
|
end
|
86
96
|
|
87
|
-
def
|
97
|
+
def find_001
|
88
98
|
rec = Plane.find(:first) { |plane| plane.name == 'P-51' }
|
89
99
|
|
90
100
|
assert(rec.is_a?(Plane))
|
91
101
|
assert_equal(rec.name, 'P-51')
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_find_001
|
105
|
+
find_001
|
106
|
+
|
107
|
+
@db.close
|
108
|
+
@db = Mongoose::Database.new(:path => Dir.tmpdir)
|
109
|
+
|
110
|
+
find_001
|
92
111
|
end
|
93
112
|
|
94
|
-
def
|
113
|
+
def find_002
|
95
114
|
recs = Plane.find { |plane| plane.name == 'P-51' }
|
96
115
|
|
97
116
|
assert_equal(recs.size, 1)
|
@@ -105,7 +124,16 @@ class TestTable < Test::Unit::TestCase
|
|
105
124
|
assert_equal(recs[1].name, 'Spitfire')
|
106
125
|
end
|
107
126
|
|
108
|
-
def
|
127
|
+
def test_find_002
|
128
|
+
find_002
|
129
|
+
|
130
|
+
@db.close
|
131
|
+
@db = Mongoose::Database.new(:path => Dir.tmpdir)
|
132
|
+
|
133
|
+
find_002
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_003
|
109
137
|
recs = Plane.find do |plane|
|
110
138
|
plane.any do
|
111
139
|
plane.country == 'USA'
|
@@ -135,7 +163,16 @@ class TestTable < Test::Unit::TestCase
|
|
135
163
|
assert_equal(recs.first.name, 'P-51')
|
136
164
|
end
|
137
165
|
|
138
|
-
def
|
166
|
+
def test_find_003
|
167
|
+
find_003
|
168
|
+
|
169
|
+
@db.close
|
170
|
+
@db = Mongoose::Database.new(:path => Dir.tmpdir)
|
171
|
+
|
172
|
+
find_003
|
173
|
+
end
|
174
|
+
|
175
|
+
def find_004
|
139
176
|
recs = Plane.find { |plane| plane.name.one_of('P-51', 'ME-109') }
|
140
177
|
assert_equal(recs.size, 2)
|
141
178
|
|
@@ -156,6 +193,15 @@ class TestTable < Test::Unit::TestCase
|
|
156
193
|
assert_equal(2, recs.size)
|
157
194
|
end
|
158
195
|
|
196
|
+
def test_find_004
|
197
|
+
find_004
|
198
|
+
|
199
|
+
@db.close
|
200
|
+
@db = Mongoose::Database.new(:path => Dir.tmpdir)
|
201
|
+
|
202
|
+
find_004
|
203
|
+
end
|
204
|
+
|
159
205
|
def test_destroy
|
160
206
|
rec = Plane.find(:first) { |plane| plane.name == 'P-51' }
|
161
207
|
rec.destroy
|