mongoose 0.1.0
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 +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
|