mongoose 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +136 -0
- data/changes.txt +2 -0
- data/example/relation_examples.rb +117 -0
- data/example/simple_examples.rb +117 -0
- data/lib/mongoose.rb +45 -0
- data/lib/mongoose/column.rb +286 -0
- data/lib/mongoose/database.rb +113 -0
- data/lib/mongoose/linear_search.rb +114 -0
- data/lib/mongoose/skiplist.rb +412 -0
- data/lib/mongoose/table.rb +568 -0
- data/lib/mongoose/util.rb +43 -0
- data/test/tc_database.rb +27 -0
- data/test/tc_table.rb +167 -0
- data/test/ts_mongoose.rb +3 -0
- metadata +59 -0
data/README
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
= Mongoose 0.1.0
|
2
|
+
|
3
|
+
A database management system written in Ruby. It has an ActiveRecord-like
|
4
|
+
interface, uses Skiplists for its indexing, and Marshal for its data
|
5
|
+
serialization. I named it Mongoose, because, like Rudyard Kipling's
|
6
|
+
Rikki-Tikki-Tavi, my aim is for it to be small, quick, and friendly.
|
7
|
+
|
8
|
+
|
9
|
+
== Credits
|
10
|
+
|
11
|
+
Thanks to Logan Capaldo for letting me steal a lot of the code from KirbyRecord.
|
12
|
+
|
13
|
+
Thanks to Ezra Zygmuntowicz and Fabien Franzen, whose ez_where Rails plugin,
|
14
|
+
provided much of the inspiration for the query language.
|
15
|
+
|
16
|
+
Thanks to everyone who gave me feedback on KirbyBase. I have tried to put all
|
17
|
+
the lessons learned from developing that library to good use here.
|
18
|
+
|
19
|
+
|
20
|
+
== Installation
|
21
|
+
|
22
|
+
Unpack the file you downloaded. Execute "ruby setup.rb".
|
23
|
+
|
24
|
+
|
25
|
+
== Features
|
26
|
+
|
27
|
+
* Pure Ruby, with no external dependencies.
|
28
|
+
* ActiveRecord-like interface.
|
29
|
+
* Fast queries on indexed fields (Up to 10x faster than KirbyBase). Indexes
|
30
|
+
are Skiplists, which are just plain fun to play around with.
|
31
|
+
* Not an in-memory database. Data is only read in from disk when needed and
|
32
|
+
changes are immediately written out to disk.
|
33
|
+
* In-memory indexes are initialized from dedicated index files, rather than
|
34
|
+
rebuilt from scratch upon database initialization (like KirbyBase does). This
|
35
|
+
can greatly reduce startup times.
|
36
|
+
* Supports any datatype that Marshal supports.
|
37
|
+
* Table relations supported via has_one, has_many.
|
38
|
+
|
39
|
+
|
40
|
+
== Documentation
|
41
|
+
|
42
|
+
Right now, documentation is a little light. It will get better. See the
|
43
|
+
examples directory for examples of how to use Mongoose. Here's a quick
|
44
|
+
tutorial:
|
45
|
+
|
46
|
+
require 'mongoose'
|
47
|
+
|
48
|
+
# Create a class for your table.
|
49
|
+
class Plane < Mongoose::Table
|
50
|
+
validates_presence_of :name, :speed
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create a database instance.
|
54
|
+
db = Mongoose::Database.new
|
55
|
+
|
56
|
+
# Create new table. Notice how you specify whether a column is indexed or not.
|
57
|
+
db.create_table(:plane) do |tbl|
|
58
|
+
tbl.add_indexed_column(:plane_name, :string)
|
59
|
+
tbl.add_column(:country, :string)
|
60
|
+
tbl.add_indexed_column(:speed, :integer)
|
61
|
+
tbl.add_column(:range, :integer)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add a record.
|
65
|
+
rec = Plane.new
|
66
|
+
rec.plane_name = 'P-51'
|
67
|
+
rec.country = 'USA'
|
68
|
+
rec.speed = 402
|
69
|
+
rec.range = 1205
|
70
|
+
rec.save
|
71
|
+
|
72
|
+
# Various ways to find a record; should be familiar to ActiveRecord users.
|
73
|
+
Plane.find(1) # Find record with id equal 1.
|
74
|
+
|
75
|
+
Plane.find { |plane| plane.speed > 350 } # Find all planes with speed > 350.
|
76
|
+
|
77
|
+
Plane.find # Find all records.
|
78
|
+
|
79
|
+
Plane.find(:first) { |plane| plane.country == 'USA' }
|
80
|
+
|
81
|
+
Plane.find do |plane| # Find all planes from either USA or
|
82
|
+
plane.any do # Great Britain with speed > 400.
|
83
|
+
plane.country == 'USA'
|
84
|
+
plane.country == 'Great Britain'
|
85
|
+
end
|
86
|
+
plane.speed > 400
|
87
|
+
end
|
88
|
+
|
89
|
+
# Delete a record.
|
90
|
+
Plane.find(1).destroy
|
91
|
+
|
92
|
+
== Manifest
|
93
|
+
|
94
|
+
* README - this file
|
95
|
+
* install.rb - install script
|
96
|
+
* changes.txt - history of changes.
|
97
|
+
* lib directory - dbms module
|
98
|
+
* test directory - unit tests
|
99
|
+
* examples directory - many example scripts demonstrating features.
|
100
|
+
* images directory - images used in manual.
|
101
|
+
|
102
|
+
|
103
|
+
== Author
|
104
|
+
|
105
|
+
Written in 2006 by Jamey Cribbs <mailto:jcribbs@netpromi.com>
|
106
|
+
|
107
|
+
|
108
|
+
== License
|
109
|
+
|
110
|
+
Mongoose is distributed under the same license as Ruby.
|
111
|
+
|
112
|
+
Copyright (c) 2006 Jamey Cribbs
|
113
|
+
|
114
|
+
|
115
|
+
== Warranty
|
116
|
+
|
117
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
118
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
119
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
120
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
121
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
122
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
123
|
+
IN THE SOFTWARE.
|
124
|
+
|
125
|
+
|
126
|
+
== Feedback
|
127
|
+
|
128
|
+
Please send any bug reports, suggestions, ideas,
|
129
|
+
improvements, to:
|
130
|
+
|
131
|
+
jcribbs@netpromi.com
|
132
|
+
|
133
|
+
|
134
|
+
== Home Page
|
135
|
+
|
136
|
+
http://rubyforge.org/projects/mongoose/
|
data/changes.txt
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
require_gem 'Mongoose'
|
4
|
+
rescue LoadError
|
5
|
+
require 'mongoose'
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
Dir.glob('[plane|pilot|flight]*.mg?').each do |filename|
|
10
|
+
File.delete(filename)
|
11
|
+
end
|
12
|
+
|
13
|
+
class Pilot < Mongoose::Table
|
14
|
+
has_one :plane
|
15
|
+
has_many :flights
|
16
|
+
end
|
17
|
+
|
18
|
+
class Plane < Mongoose::Table
|
19
|
+
belongs_to :pilot
|
20
|
+
end
|
21
|
+
|
22
|
+
class Flight < Mongoose::Table
|
23
|
+
belongs_to :pilot
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a database instance.
|
27
|
+
db = Mongoose::Database.new
|
28
|
+
|
29
|
+
# Create tables.
|
30
|
+
db.create_table(:pilot) do |tbl|
|
31
|
+
tbl.add_indexed_column(:pilot_name, :string)
|
32
|
+
tbl.add_column(:years_flying, :integer)
|
33
|
+
end
|
34
|
+
db.create_table(:plane) do |tbl|
|
35
|
+
tbl.add_indexed_column(:plane_name, :string)
|
36
|
+
tbl.add_column(:country, :string)
|
37
|
+
tbl.add_indexed_column(:speed, :integer)
|
38
|
+
tbl.add_column(:range, :integer)
|
39
|
+
tbl.add_column(:pilot_id, :integer)
|
40
|
+
end
|
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
|
+
# Create Pilot records.
|
48
|
+
doolittle = Pilot.new
|
49
|
+
doolittle.pilot_name = 'Doolitle, James'
|
50
|
+
doolittle.years_flying = 15
|
51
|
+
doolittle.save
|
52
|
+
|
53
|
+
amelia = Pilot.new
|
54
|
+
amelia.pilot_name = 'Earhart, Amelia'
|
55
|
+
amelia.years_flying = 10
|
56
|
+
amelia.save
|
57
|
+
|
58
|
+
# Create Plane records.
|
59
|
+
rec = Plane.new
|
60
|
+
rec.plane_name = 'P-51'
|
61
|
+
rec.country = 'USA'
|
62
|
+
rec.speed = 402
|
63
|
+
rec.range = 1205
|
64
|
+
rec.pilot_id = doolittle.id
|
65
|
+
rec.save
|
66
|
+
|
67
|
+
rec = Plane.new
|
68
|
+
rec.plane_name = 'Spitfire'
|
69
|
+
rec.country = 'Great Britain'
|
70
|
+
rec.speed = 333
|
71
|
+
rec.range = 454
|
72
|
+
rec.pilot_id = amelia.id
|
73
|
+
rec.save
|
74
|
+
|
75
|
+
# Create Flight records.
|
76
|
+
rec = Flight.new
|
77
|
+
rec.pilot_id = doolittle.id
|
78
|
+
rec.origin = 'Army'
|
79
|
+
rec.destination = 'Nowhere'
|
80
|
+
rec.save
|
81
|
+
|
82
|
+
rec = Flight.new
|
83
|
+
rec.pilot_id = amelia.id
|
84
|
+
rec.origin = 'USA'
|
85
|
+
rec.destination = 'France'
|
86
|
+
|
87
|
+
rec.save
|
88
|
+
rec = Flight.new
|
89
|
+
rec.pilot_id = amelia.id
|
90
|
+
rec.origin = 'USA'
|
91
|
+
rec.destination = 'Phillipines'
|
92
|
+
rec.save
|
93
|
+
|
94
|
+
rec = Flight.new
|
95
|
+
rec.pilot_id = amelia.id
|
96
|
+
rec.origin = 'China'
|
97
|
+
rec.destination = 'Unknown'
|
98
|
+
rec.save
|
99
|
+
|
100
|
+
puts "\n\nFind Jimmy Doolittle"
|
101
|
+
rec = Pilot.find(1)
|
102
|
+
p rec
|
103
|
+
puts "\nShow his plane"
|
104
|
+
p rec.plane
|
105
|
+
|
106
|
+
puts "\n\nShow flights that belong to Amelia"
|
107
|
+
p amelia.flights
|
108
|
+
|
109
|
+
puts "\n\nAdd another flight to Amelia's collection and redisplay"
|
110
|
+
rec = Flight.new
|
111
|
+
rec.pilot_id = amelia.id
|
112
|
+
rec.origin = 'Tuscon'
|
113
|
+
rec.destination = 'Phoenix'
|
114
|
+
amelia.flights << rec
|
115
|
+
p amelia.flights
|
116
|
+
|
117
|
+
db.close
|
@@ -0,0 +1,117 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
require_gem 'Mongoose'
|
4
|
+
rescue LoadError
|
5
|
+
require 'mongoose'
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
Dir.glob('[plane|pilot|flight]*.mg?').each do |filename|
|
10
|
+
File.delete(filename)
|
11
|
+
end
|
12
|
+
|
13
|
+
class Plane < Mongoose::Table
|
14
|
+
validates_presence_of :name, :speed
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a database instance.
|
18
|
+
db = Mongoose::Database.new
|
19
|
+
|
20
|
+
# Create new table. Notice how you specify whether a column is indexed or not.
|
21
|
+
db.create_table(:plane) do |tbl|
|
22
|
+
tbl.add_indexed_column(:plane_name, :string)
|
23
|
+
tbl.add_column(:country, :string)
|
24
|
+
tbl.add_indexed_column(:speed, :integer)
|
25
|
+
tbl.add_column(:range, :integer)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Add records.
|
29
|
+
rec = Plane.new
|
30
|
+
rec.plane_name = 'P-51'
|
31
|
+
rec.country = 'USA'
|
32
|
+
rec.speed = 402
|
33
|
+
rec.range = 1205
|
34
|
+
rec.save
|
35
|
+
|
36
|
+
rec2 = Plane.new
|
37
|
+
rec2.plane_name = 'Spitfire'
|
38
|
+
rec2.country = 'Great Britain'
|
39
|
+
rec2.speed = 333
|
40
|
+
rec2.range = 454
|
41
|
+
rec2.save
|
42
|
+
|
43
|
+
rec3 = Plane.new
|
44
|
+
rec3.plane_name = 'ME-109'
|
45
|
+
rec3.country = 'Germany'
|
46
|
+
rec3.speed = 354
|
47
|
+
rec3.range = 501
|
48
|
+
rec3.save
|
49
|
+
|
50
|
+
rec4 = Plane.new
|
51
|
+
rec4.plane_name = 'P-39'
|
52
|
+
rec4.country = 'USA'
|
53
|
+
rec4.range = 701
|
54
|
+
|
55
|
+
# Forgot value for speed, which is a required field.
|
56
|
+
begin
|
57
|
+
rec4.save
|
58
|
+
rescue RuntimeError => e
|
59
|
+
puts e
|
60
|
+
end
|
61
|
+
|
62
|
+
rec4.speed = 339
|
63
|
+
rec4.save
|
64
|
+
|
65
|
+
puts "\n\nFind P-51 record by ID"
|
66
|
+
p_51 = Plane.find(1)
|
67
|
+
p p_51
|
68
|
+
|
69
|
+
if p_51
|
70
|
+
# Change speed on P-51 record and save.
|
71
|
+
p_51.speed = 405
|
72
|
+
p_51.save
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "\n\nFind all records with speed greater than 350 mph"
|
76
|
+
result = Plane.find { |plane| plane.speed > 350 }
|
77
|
+
p result
|
78
|
+
|
79
|
+
puts "\n\nFind all US planes with speed greater than 300 mph"
|
80
|
+
result = Plane.find { |plane| plane.country == 'USA' and plane.speed > 300 }
|
81
|
+
p result
|
82
|
+
|
83
|
+
puts "\n\nFind all British planes with speed greater than 300 mph"
|
84
|
+
result = Plane.find do |plane|
|
85
|
+
plane.country == 'Great Britain' and plane.speed > 300
|
86
|
+
end
|
87
|
+
p result
|
88
|
+
|
89
|
+
puts "\n\nFind all Allied planes"
|
90
|
+
result = Plane.find do |plane|
|
91
|
+
plane.any do
|
92
|
+
plane.country == 'USA'
|
93
|
+
plane.country == 'Great Britain'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
p result
|
97
|
+
|
98
|
+
puts "\n\nFind all Allied planes with speed greater than 400 mph"
|
99
|
+
result = Plane.find do |plane|
|
100
|
+
plane.any do
|
101
|
+
plane.country == 'USA'
|
102
|
+
plane.country == 'Great Britain'
|
103
|
+
end
|
104
|
+
plane.speed > 400
|
105
|
+
end
|
106
|
+
p result
|
107
|
+
|
108
|
+
# Delete Spitfire record.
|
109
|
+
spitfire = Plane.find(:first) { |plane| plane.plane_name == 'Spitfire' }
|
110
|
+
|
111
|
+
spitfire.destroy if spitfire
|
112
|
+
|
113
|
+
puts "\n\nFind all records in table."
|
114
|
+
result = Plane.find
|
115
|
+
p result
|
116
|
+
|
117
|
+
db.close
|
data/lib/mongoose.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'mongoose/database'
|
3
|
+
require 'mongoose/table'
|
4
|
+
require 'mongoose/column'
|
5
|
+
require 'mongoose/skiplist'
|
6
|
+
require 'mongoose/linear_search'
|
7
|
+
require 'mongoose/util'
|
8
|
+
|
9
|
+
#
|
10
|
+
# :main:Mongoose
|
11
|
+
# :title:Mongoose Module Documentation
|
12
|
+
# Mongoose is a library that implements a database management system. It is
|
13
|
+
# written in Ruby so it runs anywhere Ruby runs. It uses Skiplists for its
|
14
|
+
# indexes, so queries are fast. It uses Marshal to store its data so data
|
15
|
+
# retrieval is fast.
|
16
|
+
#
|
17
|
+
# Author:: Jamey Cribbs (mailto:jcribbs@netpromi.com)
|
18
|
+
# Homepage:: http://rubyforge.org/projects/mongoose/
|
19
|
+
# Copyright:: Copyright (c) 2006 NetPro Technologies, LLC
|
20
|
+
# License:: Distributed under the same terms as Ruby
|
21
|
+
# History:
|
22
|
+
# 2006-07-19:: Version 0.1.0
|
23
|
+
# * Initial release.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
module Mongoose
|
27
|
+
|
28
|
+
DATA_TYPES = [:string, :integer, :float, :time, :date, :datetime, :boolean]
|
29
|
+
TBL_EXT = '.mgt'
|
30
|
+
TBL_HDR_EXT = '.mgh'
|
31
|
+
TBL_IDX_EXT = '.mgi'
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
#---------------------------------------------------------------------------
|
36
|
+
# Object
|
37
|
+
#---------------------------------------------------------------------------
|
38
|
+
class Object
|
39
|
+
def full_const_get(name)
|
40
|
+
list = name.split("::")
|
41
|
+
obj = Object
|
42
|
+
list.each {|x| obj = obj.const_get(x) }
|
43
|
+
obj
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Mongoose
|
4
|
+
|
5
|
+
#-------------------------------------------------------------------------------
|
6
|
+
# BaseColumn class
|
7
|
+
#-------------------------------------------------------------------------------
|
8
|
+
class BaseColumn
|
9
|
+
attr_reader :name, :data_type, :tbl_class
|
10
|
+
attr_writer :required
|
11
|
+
|
12
|
+
private_class_method :new
|
13
|
+
|
14
|
+
def self.valid_data_type?(data_type)
|
15
|
+
DATA_TYPES.include?(data_type)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.create(tbl_class, name, col_def)
|
19
|
+
return new(tbl_class, name, col_def)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(tbl_class, name, col_def)
|
23
|
+
@tbl_class = tbl_class
|
24
|
+
@name = name
|
25
|
+
@data_type = col_def
|
26
|
+
@indexed = false
|
27
|
+
@required = false
|
28
|
+
end
|
29
|
+
|
30
|
+
def indexed?
|
31
|
+
@indexed
|
32
|
+
end
|
33
|
+
|
34
|
+
def required?
|
35
|
+
@required
|
36
|
+
end
|
37
|
+
|
38
|
+
def close
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
#-------------------------------------------------------------------------------
|
45
|
+
# Column class
|
46
|
+
#-------------------------------------------------------------------------------
|
47
|
+
class Column < BaseColumn
|
48
|
+
def initialize(tbl_class, name, col_def)
|
49
|
+
super
|
50
|
+
@idx = LinearSearch.new(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
def store(key, value)
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove(key, value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def >(other)
|
60
|
+
@tbl_class.query << [@idx, :>, other]
|
61
|
+
end
|
62
|
+
|
63
|
+
def >=(other)
|
64
|
+
@tbl_class.query << [@idx, :>=, other]
|
65
|
+
end
|
66
|
+
|
67
|
+
def <(other)
|
68
|
+
@tbl_class.query << [@idx, :<, other]
|
69
|
+
end
|
70
|
+
|
71
|
+
def <=(other)
|
72
|
+
@tbl_class.query << [@idx, :<=, other]
|
73
|
+
end
|
74
|
+
|
75
|
+
def ==(other)
|
76
|
+
@tbl_class.query << [@idx, :==, other]
|
77
|
+
end
|
78
|
+
|
79
|
+
def between(*other)
|
80
|
+
@tbl_class.query << [@idx, :between, other]
|
81
|
+
end
|
82
|
+
|
83
|
+
def one_of(*other)
|
84
|
+
@tbl_class.query << [@idx, :one_of, other]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
#-------------------------------------------------------------------------------
|
90
|
+
# IndexedColumn class
|
91
|
+
#-------------------------------------------------------------------------------
|
92
|
+
class IndexedColumn < BaseColumn
|
93
|
+
attr_reader :idx, :index_file_name
|
94
|
+
|
95
|
+
#-----------------------------------------------------------------------------
|
96
|
+
# initialize
|
97
|
+
#-----------------------------------------------------------------------------
|
98
|
+
def initialize(tbl_class, name, col_def)
|
99
|
+
super
|
100
|
+
@indexed = true
|
101
|
+
|
102
|
+
@index_file_name = File.join(@tbl_class.db.path,
|
103
|
+
@tbl_class.table_name.to_s + '_' + @name.to_s + TBL_IDX_EXT)
|
104
|
+
end
|
105
|
+
|
106
|
+
#-----------------------------------------------------------------------------
|
107
|
+
# close
|
108
|
+
#-----------------------------------------------------------------------------
|
109
|
+
def close
|
110
|
+
rebuild_index_file if index_file_out_of_date?
|
111
|
+
end
|
112
|
+
|
113
|
+
def init_index
|
114
|
+
if File.exists?(@index_file_name) and not index_file_out_of_date?
|
115
|
+
rebuild_index_from_index_file
|
116
|
+
else
|
117
|
+
rebuild_index_from_table
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
#-----------------------------------------------------------------------------
|
122
|
+
# with_index_file
|
123
|
+
#-----------------------------------------------------------------------------
|
124
|
+
def with_index_file(access='r')
|
125
|
+
begin
|
126
|
+
yield fptr = open(@index_file_name, access)
|
127
|
+
ensure
|
128
|
+
fptr.close
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
#-----------------------------------------------------------------------------
|
133
|
+
# index_file_out_of_date?
|
134
|
+
#-----------------------------------------------------------------------------
|
135
|
+
def index_file_out_of_date?
|
136
|
+
if not File.exists?(@index_file_name) or (File.mtime(File.join(
|
137
|
+
@tbl_class.db.path, @tbl_class.table_name.to_s + TBL_EXT)) >
|
138
|
+
File.mtime(@index_file_name))
|
139
|
+
true
|
140
|
+
else
|
141
|
+
false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
#-------------------------------------------------------------------------------
|
148
|
+
# SkipListIndexColumn class
|
149
|
+
#-------------------------------------------------------------------------------
|
150
|
+
class SkipListIndexColumn < IndexedColumn
|
151
|
+
def initialize(tbl_class, name, col_def)
|
152
|
+
@idx = SkipList.new
|
153
|
+
super
|
154
|
+
end
|
155
|
+
|
156
|
+
def clear_index
|
157
|
+
@idx = SkipList.new
|
158
|
+
end
|
159
|
+
|
160
|
+
def >(other)
|
161
|
+
@tbl_class.query << [@idx, :>, other]
|
162
|
+
end
|
163
|
+
|
164
|
+
def >=(other)
|
165
|
+
@tbl_class.query << [@idx, :>=, other]
|
166
|
+
end
|
167
|
+
|
168
|
+
def <(other)
|
169
|
+
@tbl_class.query << [@idx, :<, other]
|
170
|
+
end
|
171
|
+
|
172
|
+
def <=(other)
|
173
|
+
@tbl_class.query << [@idx, :<=, other]
|
174
|
+
end
|
175
|
+
|
176
|
+
def ==(other)
|
177
|
+
@tbl_class.query << [@idx, :==, other]
|
178
|
+
end
|
179
|
+
|
180
|
+
def search(other)
|
181
|
+
@tbl_class.query << [@idx, :search, other]
|
182
|
+
end
|
183
|
+
|
184
|
+
def between(*other)
|
185
|
+
@tbl_class.query << [@idx, :between, other]
|
186
|
+
end
|
187
|
+
|
188
|
+
def one_of(*other)
|
189
|
+
@tbl_class.query << [@idx, :one_of, other]
|
190
|
+
end
|
191
|
+
|
192
|
+
def rebuild_index_file
|
193
|
+
with_index_file('w') do |fptr|
|
194
|
+
@idx.each { |k,v| fptr.write(Marshal.dump([k, v])) }
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def rebuild_index_from_table
|
199
|
+
clear_index
|
200
|
+
i = @tbl_class.columns.index(self)
|
201
|
+
|
202
|
+
@tbl_class.get_all_recs do |rec, fpos|
|
203
|
+
add_index_rec(rec[i], rec[0]) unless rec[i].nil?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def rebuild_index_from_index_file
|
208
|
+
clear_index
|
209
|
+
|
210
|
+
with_index_file do |fptr|
|
211
|
+
begin
|
212
|
+
while true
|
213
|
+
@idx.load(*Marshal.load(fptr))
|
214
|
+
end
|
215
|
+
rescue EOFError
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def add_index_rec(key, value)
|
221
|
+
@idx.store(key, value)
|
222
|
+
end
|
223
|
+
|
224
|
+
def remove_index_rec(key, value)
|
225
|
+
@idx.remove(key, value)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
#-------------------------------------------------------------------------------
|
231
|
+
# IDColumn class
|
232
|
+
#-------------------------------------------------------------------------------
|
233
|
+
class IDColumn < IndexedColumn
|
234
|
+
extend Forwardable
|
235
|
+
|
236
|
+
def_delegator(:@idx, :[], :[])
|
237
|
+
def_delegator(:@idx, :keys, :keys)
|
238
|
+
# def_delegator(:@idx, :delete, :remove)
|
239
|
+
|
240
|
+
def initialize(tbl_class, name, col_def)
|
241
|
+
@idx = {}
|
242
|
+
super
|
243
|
+
end
|
244
|
+
|
245
|
+
def clear_index
|
246
|
+
@idx = {}
|
247
|
+
end
|
248
|
+
|
249
|
+
def rebuild_index_file
|
250
|
+
with_index_file('w') do |fptr|
|
251
|
+
# @idx.each_pair { |k,v| fptr.write(Marshal.dump([k, v])) }
|
252
|
+
fptr.write(Marshal.dump(@idx))
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def rebuild_index_from_table
|
257
|
+
clear_index
|
258
|
+
@tbl_class.get_all_recs do |rec, fpos|
|
259
|
+
add_index_rec(rec[0], fpos)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def rebuild_index_from_index_file
|
264
|
+
clear_index
|
265
|
+
|
266
|
+
with_index_file do |fptr|
|
267
|
+
# begin
|
268
|
+
# while true
|
269
|
+
# add_index_rec(*Marshal.load(fptr))
|
270
|
+
# end
|
271
|
+
# rescue EOFError
|
272
|
+
# end
|
273
|
+
@idx = Marshal.load(fptr)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def add_index_rec(id, fpos)
|
278
|
+
@idx[id] = fpos
|
279
|
+
end
|
280
|
+
|
281
|
+
def remove_index_rec(id)
|
282
|
+
@idx.delete(id)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|