mongoose 0.2.0 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = Mongoose 0.2.0
1
+ = Mongoose 0.2.5
2
2
 
3
3
  A database management system written in Ruby. It has an ActiveRecord-like
4
4
  interface, uses Skiplists for its indexing, and Marshal for its data
@@ -15,6 +15,8 @@ provided much of the inspiration for the query language. Also, Ezra has
15
15
  graciously taken the time to give me pointers on how to make Mongoose's query
16
16
  language and api better.
17
17
 
18
+ Thanks to everyone who has given me feedback so far on Mongoose.
19
+
18
20
  Thanks to everyone who gave me feedback on KirbyBase. I have tried to put all
19
21
  the lessons learned from developing that library to good use here.
20
22
 
@@ -64,7 +66,7 @@ db.create_table(:plane) do |tbl|
64
66
  tbl.add_column(:range, :integer)
65
67
  end
66
68
 
67
- # Add a record.
69
+ # Add a record. You can also use #create.
68
70
  rec = Plane.new
69
71
  rec.name = 'P-51'
70
72
  rec.country = 'USA'
@@ -75,18 +77,18 @@ rec.save
75
77
  # Various ways to find a record; should be familiar to ActiveRecord users.
76
78
  Plane.find(1) # Find record with id equal 1.
77
79
 
78
- Plane.find { speed > 350 } # Find all planes with speed > 350.
80
+ Plane.find { |plane| plane.speed > 350 } # Find all planes with speed > 350.
79
81
 
80
82
  Plane.find # Find all records.
81
83
 
82
- Plane.find(:first) { country == 'USA' } # Find first plane from USA.
84
+ Plane.find(:first) { |plane| plane.country == 'USA' } # Find first plane from USA.
83
85
 
84
- Plane.find do # Find all planes from either USA or
85
- any do # Great Britain with speed > 400.
86
- country == 'USA'
87
- country == 'Great Britain'
86
+ Plane.find do |plane| # Find all planes from either USA or
87
+ plane.any do # Great Britain with speed > 400.
88
+ plane.country == 'USA'
89
+ plane.country == 'Great Britain'
88
90
  end
89
- speed > 400
91
+ plane.speed > 400
90
92
  end
91
93
 
92
94
  # Delete a record.
@@ -101,7 +103,8 @@ db.close
101
103
  * README - this file
102
104
  * install.rb - install script
103
105
  * changes.txt - history of changes.
104
- * lib directory - dbms module
106
+ * lib directory - dbms module
107
+ * bin directory - import, export scripts
105
108
  * test directory - unit tests
106
109
  * examples directory - many example scripts demonstrating features.
107
110
  * images directory - images used in manual.
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'rubygems'
3
+ require_gem 'Mongoose'
4
+ rescue LoadError
5
+ require 'mongoose'
6
+ end
7
+
8
+ raise "Must supply class name to export!" unless ARGV.size > 0
9
+
10
+ class_name = ARGV[0]
11
+ filename = 1
12
+ filename = ARGV[1] if ARGV.size > 1
13
+
14
+ Object.const_set(ARGV[0], Class.new(Mongoose::Table))
15
+
16
+ db = Mongoose::Database.new
17
+ Object.const_get(class_name).export(filename)
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'rubygems'
3
+ require_gem 'Mongoose'
4
+ rescue LoadError
5
+ require 'mongoose'
6
+ end
7
+
8
+ raise "Must supply class name to import!" unless ARGV.size > 0
9
+
10
+ class_name = ARGV[0]
11
+ filename = 0
12
+ filename = ARGV[1] if ARGV.size > 1
13
+
14
+ Object.const_set(ARGV[0], Class.new(Mongoose::Table))
15
+
16
+ db = Mongoose::Database.new
17
+ Object.const_get(class_name).import(filename)
@@ -23,3 +23,27 @@
23
23
  eliminated the string eval. Thanks Logan!
24
24
  * Included a new example from Daniel Sheppard that shows how to integrate
25
25
  ActiveRecord validations into Mongoose.
26
+
27
+ 2006-07-25:: Version 0.2.5
28
+ * Logan Capaldo submitted a patch to the Util module to make pluralization
29
+ a lot smarter. He also added method Table.plural_form. And he updated the
30
+ test cases to test these changes. Thanks Logan!
31
+ * Refactored the query engine code. Thanks to Logan Capaldo for code and ideas
32
+ to get me going.
33
+ * John Long brought up a great point about losing access to instance variables
34
+ from the calling object, using the present scheme of #instance_eval(&block)
35
+ in Table.find. Therefore, I am going back to requiring that the table class
36
+ be passed as a block parameter and that column names be qualified by the
37
+ table's class name.
38
+ * Added Table.import, Table.export methods.
39
+ * Added Table.destroy, Table.destroy_all, Table.content_columns, Table.exists?
40
+ methods.
41
+ * Major cleanup of Table.find. First, I have split the functionality up into
42
+ separate methods. Second, I believe I have got all of the basic options
43
+ working: :first, :all, one id, mulitple ids, :order, :limit, :offset.
44
+ * Added dynamic attribute-base finder methods, i.e. Plane.find_by_country and
45
+ Plane.find_all_by_country.
46
+ * Fixed Plane.initialize so that it does the right thing, i.e. if you pass it a
47
+ hash, it will create a new record initialized to those values.
48
+ * Added Table#update_attributes method.
49
+ * Added Table#delete , Table#delete_all methods.
@@ -12,6 +12,7 @@ unless ARGV[0] == 'keep-data'
12
12
  end
13
13
  end
14
14
 
15
+ # Create the table class.
15
16
  class Plane < Mongoose::Table
16
17
  validates_presence_of :name, :speed
17
18
  end
@@ -30,12 +31,13 @@ unless ARGV[0] == 'keep-data'
30
31
 
31
32
  # Add records.
32
33
  Plane.create :name => 'P-51', :country => 'USA', :speed => 402, :range => 1205
33
- Plane.create :name => 'Spitfire', :country => 'Great Britain', :speed => 333,
34
+ Plane.create :name => 'Spitfire', :country => 'Great Britain', :speed => 351,
34
35
  :range => 454
35
36
  Plane.create :name => 'ME-109', :country => 'Germany', :speed => 354,
36
37
  :range => 501
37
38
 
38
- # Forgot value for speed, which is a required field.
39
+ # Forgot value for speed, which is a required field. Notice
40
+ # validates_presence_of in the class definition above.
39
41
  begin
40
42
  Plane.create :name => 'P-39', :country => 'USA', :range => 701
41
43
  rescue RuntimeError => e
@@ -49,52 +51,87 @@ puts "\n\nFind P-51 record by ID"
49
51
  p_51 = Plane.find(1)
50
52
  p p_51
51
53
 
52
- if p_51
53
- # Change speed on P-51 record and save.
54
- p_51.speed = 405
55
- p_51.save
56
- end
54
+ # Change speed on P-51 record and save.
55
+ p_51.speed = 405
56
+ p_51.save
57
57
 
58
58
  puts "\n\nFind all records with speed greater than 350 mph"
59
- result = Plane.find { speed > 350 }
59
+ result = Plane.find { |plane| plane.speed > 350 }
60
60
  p result
61
61
 
62
- puts "\n\nFind all US planes with speed greater than 300 mph"
63
- result = Plane.find { country == 'USA' and speed > 300 }
62
+
63
+ puts "\n\nFind all US planes with speed greater than 350 mph"
64
+ result = Plane.find { |plane| plane.country == 'USA' and plane.speed > 350 }
64
65
  p result
65
66
 
66
67
  puts "\n\nFind all British planes with speed greater than 300 mph"
67
- result = Plane.find do
68
- country == 'Great Britain' and speed > 300
68
+ result = Plane.find do |plane|
69
+ plane.country == 'Great Britain' and plane.speed > 300
69
70
  end
70
71
  p result
71
72
 
72
73
  puts "\n\nFind all Allied planes"
73
- result = Plane.find do
74
- any do
75
- country == 'USA'
76
- country == 'Great Britain'
74
+ result = Plane.find do |plane|
75
+ plane.any do
76
+ plane.country == 'USA'
77
+ plane.country == 'Great Britain'
77
78
  end
78
79
  end
79
80
  p result
80
81
 
81
- puts "\n\nFind all Allied planes with speed greater than 400 mph"
82
- result = Plane.find do
83
- any do
84
- country == 'USA'
85
- country == 'Great Britain'
82
+ puts "\n\nFind all Allied planes with speed greater than 350 mph"
83
+ result = Plane.find do |plane|
84
+ plane.any do
85
+ plane.country == 'USA'
86
+ plane.country == 'Great Britain'
86
87
  end
87
- speed > 400
88
+ plane.speed > 350
88
89
  end
89
90
  p result
90
91
 
91
- # Delete Spitfire record.
92
- spitfire = Plane.find(:first) { name == 'Spitfire' }
93
-
94
- spitfire.destroy if spitfire
92
+ puts "\n\nFind all Allied planes with speed between 300 and 350 mph"
93
+ result = Plane.find do |plane|
94
+ plane.any do
95
+ plane.country == 'USA'
96
+ plane.country == 'Great Britain'
97
+ end
98
+ plane.speed.between(300, 350)
99
+ end
100
+ p result
95
101
 
96
102
  puts "\n\nFind all records in table."
97
103
  result = Plane.find
98
104
  p result
99
105
 
106
+ puts "\n\nFind by dynamic finder method."
107
+ result = Plane.find_by_name('ME-109')
108
+ p result
109
+
110
+ puts "\n\nSort result set by name."
111
+ result = Plane.find(:all, :order => :name)
112
+ p result
113
+
114
+ puts "\n\nSort by country, then name."
115
+ result = Plane.find(:order => [:country, :name])
116
+ p result
117
+
118
+ puts "\n\nSort by speed descending."
119
+ result = Plane.find(:order => -:speed)
120
+ p result
121
+
122
+ puts "\n\nLimit number of records returned."
123
+ result = Plane.find(:order => :name, :limit => 2)
124
+ p result
125
+
126
+ puts "\n\nSpecify offset."
127
+ result = Plane.find(:order => :name, :offset => 3)
128
+ p result
129
+
130
+ # Delete Spitfire record.
131
+ spitfire = Plane.find(:first) { |plane| plane.name == 'Spitfire' }
132
+ spitfire.destroy if spitfire
133
+
134
+ puts "\n\nShow only content columns."
135
+ Plane.content_columns.each { |c| puts c.name }
136
+
100
137
  db.close
@@ -1,10 +1,23 @@
1
1
  require 'yaml'
2
+ require 'pp'
3
+ require 'forwardable'
4
+ require 'time'
5
+ require 'date'
6
+
7
+ begin
8
+ require 'faster_csv'
9
+ rescue LoadError
10
+ require 'csv'
11
+ end
12
+
2
13
  require 'mongoose/database'
3
14
  require 'mongoose/table'
4
15
  require 'mongoose/column'
5
16
  require 'mongoose/skiplist'
6
17
  require 'mongoose/linear_search'
18
+ require 'mongoose/query'
7
19
  require 'mongoose/util'
20
+ require 'mongoose/error'
8
21
 
9
22
  #
10
23
  # :main:Mongoose
@@ -22,7 +35,7 @@ require 'mongoose/util'
22
35
  #
23
36
  module Mongoose
24
37
 
25
- VERSION = '0.2.0'
38
+ VERSION = '0.2.5'
26
39
  DATA_TYPES = [:string, :integer, :float, :time, :date, :datetime, :boolean]
27
40
  TBL_EXT = '.mgt'
28
41
  TBL_HDR_EXT = '.mgh'
@@ -30,14 +43,41 @@ TBL_IDX_EXT = '.mgi'
30
43
 
31
44
  end
32
45
 
33
- #---------------------------------------------------------------------------
46
+
47
+ #-------------------------------------------------------------------------------
34
48
  # Object
35
- #---------------------------------------------------------------------------
49
+ #-------------------------------------------------------------------------------
36
50
  class Object
37
51
  def full_const_get(name)
38
52
  list = name.split("::")
39
53
  obj = Object
40
54
  list.each {|x| obj = obj.const_get(x) }
41
55
  obj
56
+ end
57
+ end
58
+
59
+
60
+ #-------------------------------------------------------------------------------
61
+ # Symbol
62
+ #-------------------------------------------------------------------------------
63
+ class Symbol
64
+ #-----------------------------------------------------------------------------
65
+ # -@
66
+ #-----------------------------------------------------------------------------
67
+ #
68
+ # This allows you to put a minus sign in front of a field name in order
69
+ # to specify descending sort order.
70
+ def -@
71
+ ("-"+self.to_s).to_sym
72
+ end
73
+
74
+ #-----------------------------------------------------------------------------
75
+ # +@
76
+ #-----------------------------------------------------------------------------
77
+ #
78
+ # This allows you to put a plus sign in front of a field name in order
79
+ # to specify ascending sort order.
80
+ def +@
81
+ ("+"+self.to_s).to_sym
42
82
  end
43
83
  end
@@ -1,5 +1,3 @@
1
- require 'forwardable'
2
-
3
1
  module Mongoose
4
2
 
5
3
  #-------------------------------------------------------------------------------
@@ -11,14 +9,32 @@ class BaseColumn
11
9
 
12
10
  private_class_method :new
13
11
 
12
+ extend Forwardable
13
+ def_delegator(:@idx, :>, :>)
14
+ def_delegator(:@idx, :>=, :>=)
15
+ def_delegator(:@idx, :==, :==)
16
+ def_delegator(:@idx, :<, :<)
17
+ def_delegator(:@idx, :<=, :<=)
18
+ def_delegator(:@idx, :between, :between)
19
+ def_delegator(:@idx, :one_of, :one_of)
20
+
21
+ #-----------------------------------------------------------------------
22
+ # BaseColumn.valid_data_type?
23
+ #-----------------------------------------------------------------------
14
24
  def self.valid_data_type?(data_type)
15
25
  DATA_TYPES.include?(data_type)
16
26
  end
17
27
 
28
+ #-----------------------------------------------------------------------
29
+ # BaseColumn.create_table
30
+ #-----------------------------------------------------------------------
18
31
  def self.create(tbl_class, name, col_def)
19
32
  return new(tbl_class, name, col_def)
20
33
  end
21
34
 
35
+ #-----------------------------------------------------------------------
36
+ # initialize
37
+ #-----------------------------------------------------------------------
22
38
  def initialize(tbl_class, name, col_def)
23
39
  @tbl_class = tbl_class
24
40
  @name = name
@@ -27,17 +43,47 @@ class BaseColumn
27
43
  @required = false
28
44
  end
29
45
 
46
+ #-----------------------------------------------------------------------
47
+ # indexed?
48
+ #-----------------------------------------------------------------------
30
49
  def indexed?
31
50
  @indexed
32
51
  end
33
52
 
53
+ #-----------------------------------------------------------------------
54
+ # required?
55
+ #-----------------------------------------------------------------------
34
56
  def required?
35
57
  @required
36
58
  end
37
59
 
60
+ #-----------------------------------------------------------------------
61
+ # close
62
+ #-----------------------------------------------------------------------
38
63
  def close
39
64
  end
40
65
 
66
+ #-----------------------------------------------------------------------
67
+ # convert_to_native
68
+ #-----------------------------------------------------------------------
69
+ def convert_to_native(value)
70
+ case @data_type
71
+ when :string
72
+ value.to_s
73
+ when :integer
74
+ value.to_i
75
+ when :float
76
+ value.to_f
77
+ when :time
78
+ Time.parse(value)
79
+ when :date
80
+ Date.parse(value)
81
+ when :datetime
82
+ DateTime.parse(value)
83
+ when :boolean
84
+ true if [true, 'true', 1].include?(value)
85
+ end
86
+ end
41
87
  end
42
88
 
43
89
 
@@ -45,38 +91,13 @@ end
45
91
  # Column class
46
92
  #-------------------------------------------------------------------------------
47
93
  class Column < BaseColumn
94
+ #-----------------------------------------------------------------------
95
+ # initialize
96
+ #-----------------------------------------------------------------------
48
97
  def initialize(tbl_class, name, col_def)
49
98
  super
50
99
  @idx = LinearSearch.new(self)
51
100
  end
52
-
53
- def >(other)
54
- @tbl_class.query << [@idx, :>, other]
55
- end
56
-
57
- def >=(other)
58
- @tbl_class.query << [@idx, :>=, other]
59
- end
60
-
61
- def <(other)
62
- @tbl_class.query << [@idx, :<, other]
63
- end
64
-
65
- def <=(other)
66
- @tbl_class.query << [@idx, :<=, other]
67
- end
68
-
69
- def ==(other)
70
- @tbl_class.query << [@idx, :==, other]
71
- end
72
-
73
- def between(*other)
74
- @tbl_class.query << [@idx, :between, other]
75
- end
76
-
77
- def one_of(*other)
78
- @tbl_class.query << [@idx, :one_of, other]
79
- end
80
101
  end
81
102
 
82
103
 
@@ -145,51 +166,31 @@ end
145
166
  # SkipListIndexColumn class
146
167
  #-------------------------------------------------------------------------------
147
168
  class SkipListIndexColumn < IndexedColumn
169
+ #-----------------------------------------------------------------------
170
+ # initialize
171
+ #-----------------------------------------------------------------------
148
172
  def initialize(tbl_class, name, col_def)
149
173
  @idx = SkipList.new(self)
150
174
  super
151
175
  end
152
176
 
177
+ #-----------------------------------------------------------------------
178
+ # clear_index
179
+ #-----------------------------------------------------------------------
153
180
  def clear_index
154
181
  @idx = SkipList.new(self)
155
182
  end
156
183
 
157
- def >(other)
158
- @tbl_class.query << [@idx, :>, other]
159
- end
160
-
161
- def >=(other)
162
- @tbl_class.query << [@idx, :>=, other]
163
- end
164
-
165
- def <(other)
166
- @tbl_class.query << [@idx, :<, other]
167
- end
168
-
169
- def <=(other)
170
- @tbl_class.query << [@idx, :<=, other]
171
- end
172
-
173
- def ==(other)
174
- @tbl_class.query << [@idx, :==, other]
175
- end
176
-
177
- def search(other)
178
- @tbl_class.query << [@idx, :search, other]
179
- end
180
-
181
- def between(*other)
182
- @tbl_class.query << [@idx, :between, other]
183
- end
184
-
185
- def one_of(*other)
186
- @tbl_class.query << [@idx, :one_of, other]
187
- end
188
-
184
+ #-----------------------------------------------------------------------
185
+ # rebuild_index_file
186
+ #-----------------------------------------------------------------------
189
187
  def rebuild_index_file
190
188
  with_index_file('w') { |fptr| fptr.write(Marshal.dump(@idx.dump_to_hash)) }
191
189
  end
192
190
 
191
+ #-----------------------------------------------------------------------
192
+ # rebuild_index_from_table
193
+ #-----------------------------------------------------------------------
193
194
  def rebuild_index_from_table
194
195
  clear_index
195
196
  i = @tbl_class.columns.index(self)
@@ -199,15 +200,24 @@ class SkipListIndexColumn < IndexedColumn
199
200
  end
200
201
  end
201
202
 
203
+ #-----------------------------------------------------------------------
204
+ # rebuild_index_from_index_file
205
+ #-----------------------------------------------------------------------
202
206
  def rebuild_index_from_index_file
203
207
  clear_index
204
208
  with_index_file { |fptr| @idx.load_from_hash(Marshal.load(fptr)) }
205
209
  end
206
210
 
211
+ #-----------------------------------------------------------------------
212
+ # add_index_rec
213
+ #-----------------------------------------------------------------------
207
214
  def add_index_rec(key, value)
208
215
  @idx.store(key, value)
209
216
  end
210
217
 
218
+ #-----------------------------------------------------------------------
219
+ # remove_index_rec
220
+ #-----------------------------------------------------------------------
211
221
  def remove_index_rec(key, value)
212
222
  @idx.remove(key, value)
213
223
  end
@@ -218,38 +228,111 @@ end
218
228
  # IDColumn class
219
229
  #-------------------------------------------------------------------------------
220
230
  class IDColumn < IndexedColumn
221
- extend Forwardable
222
-
231
+ def_delegator(:@idx, :==, :[])
223
232
  def_delegator(:@idx, :[], :[])
224
233
  def_delegator(:@idx, :keys, :keys)
225
234
 
235
+ #-----------------------------------------------------------------------
236
+ # initialize
237
+ #-----------------------------------------------------------------------
226
238
  def initialize(tbl_class, name, col_def)
227
239
  @idx = {}
228
240
  super
229
241
  end
230
242
 
243
+ #-----------------------------------------------------------------------
244
+ # clear_index
245
+ #-----------------------------------------------------------------------
231
246
  def clear_index
232
247
  @idx = {}
233
248
  end
234
249
 
250
+ #-----------------------------------------------------------------------
251
+ # >
252
+ #-----------------------------------------------------------------------
253
+ def >(other)
254
+ return @idx.keys.select { |k| k > other }
255
+ end
256
+
257
+ #-----------------------------------------------------------------------
258
+ # >=
259
+ #-----------------------------------------------------------------------
260
+ def >=(other)
261
+ return @idx.keys.select { |k| k >= other }
262
+ end
263
+
264
+ #-----------------------------------------------------------------------
265
+ # <
266
+ #-----------------------------------------------------------------------
267
+ def <(other)
268
+ return @idx.keys.select { |k| k < other }
269
+ end
270
+
271
+ #-----------------------------------------------------------------------
272
+ # <=
273
+ #-----------------------------------------------------------------------
274
+ def <=(other)
275
+ return @idx.keys.select { |k| k <= other }
276
+ end
277
+
278
+ #-----------------------------------------------------------------------
279
+ # between
280
+ #-----------------------------------------------------------------------
281
+ def between(search_start, search_end, start_inclusive=false,
282
+ end_inclusive=false)
283
+ return @idx.keys.select do |k|
284
+ if k == search_start and start_inclusive
285
+ true
286
+ elsif k > search_start and k < search_end
287
+ true
288
+ elsif k == search_end and end_inclusive
289
+ true
290
+ else
291
+ false
292
+ end
293
+ end
294
+ end
295
+
296
+ #-----------------------------------------------------------------------
297
+ # one_of
298
+ #-----------------------------------------------------------------------
299
+ def one_of(*other)
300
+ return @idx.keys.select { |k| other.include?(k) }
301
+ end
302
+
303
+ #-----------------------------------------------------------------------
304
+ # rebuild_index_file
305
+ #-----------------------------------------------------------------------
235
306
  def rebuild_index_file
236
307
  with_index_file('w') { |fptr| fptr.write(Marshal.dump(@idx)) }
237
308
  end
238
309
 
310
+ #-----------------------------------------------------------------------
311
+ # rebuild_index_from_table
312
+ #-----------------------------------------------------------------------
239
313
  def rebuild_index_from_table
240
314
  clear_index
241
315
  @tbl_class.get_all_recs { |rec, fpos| add_index_rec(rec[0], fpos) }
242
316
  end
243
317
 
318
+ #-----------------------------------------------------------------------
319
+ # rebuild_index_from_index_file
320
+ #-----------------------------------------------------------------------
244
321
  def rebuild_index_from_index_file
245
322
  clear_index
246
323
  with_index_file { |fptr| @idx = Marshal.load(fptr) }
247
324
  end
248
325
 
326
+ #-----------------------------------------------------------------------
327
+ # add_index_rec
328
+ #-----------------------------------------------------------------------
249
329
  def add_index_rec(id, fpos)
250
330
  @idx[id] = fpos
251
331
  end
252
332
 
333
+ #-----------------------------------------------------------------------
334
+ # remove_index_rec
335
+ #-----------------------------------------------------------------------
253
336
  def remove_index_rec(id)
254
337
  @idx.delete(id)
255
338
  end