mongoose 0.2.0 → 0.2.5

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.
@@ -0,0 +1,9 @@
1
+ module Mongoose
2
+
3
+ class MongooseError < StandardError
4
+ end
5
+
6
+ class RecordNotFound < MongooseError
7
+ end
8
+
9
+ end
@@ -0,0 +1,52 @@
1
+ module Mongoose
2
+
3
+ class Property
4
+ attr_reader :property_name, :subquery_no, :subquery_type, :comparison, :arg
5
+ def initialize(name, query, subquery_no, subquery_type)
6
+ @property_name = name
7
+ @query = query
8
+ @subquery_no = subquery_no
9
+ @subquery_type = subquery_type
10
+ end
11
+
12
+ [:>, :<=, :==, :<=, :<, :between, :one_of].each do |comparison|
13
+ define_method(comparison) do |*arg|
14
+ @comparison = comparison
15
+ @arg = arg
16
+ @query.add_predicate(self)
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+
23
+ class Query
24
+ attr_reader :predicates
25
+ def initialize
26
+ @predicates = []
27
+ @subquery_type = :and
28
+ @subquery_no = 0
29
+ end
30
+
31
+ def find(&block)
32
+ yield self
33
+ end
34
+
35
+ def add_predicate(pred)
36
+ @predicates << pred
37
+ end
38
+
39
+ def method_missing(name, *args)
40
+ @subquery_no += 1 if @subquery_type == :and
41
+ Property.new(name, self, @subquery_no, @subquery_type)
42
+ end
43
+
44
+ def any(&block)
45
+ @subquery_type = :or
46
+ @subquery_no += 1
47
+ yield
48
+ @subquery_type = :and
49
+ end
50
+ end
51
+
52
+ end
@@ -39,6 +39,14 @@ class Table
39
39
  self.db.tables[self][:columns]
40
40
  end
41
41
 
42
+ #-----------------------------------------------------------------------------
43
+ # Table.content_columns
44
+ #-----------------------------------------------------------------------------
45
+ def self.content_columns
46
+ self.db.tables[self][:columns] = self.columns.reject { |c|
47
+ c.name == :id || c.name =~ /(_id|_count)$/ }
48
+ end
49
+
42
50
  #-----------------------------------------------------------------------------
43
51
  # Table.column_names
44
52
  #-----------------------------------------------------------------------------
@@ -53,6 +61,14 @@ class Table
53
61
  self.db.path
54
62
  end
55
63
 
64
+ #-----------------------------------------------------------------------------
65
+ # Table.plural_form
66
+ #-----------------------------------------------------------------------------
67
+ def self.plural_form(pluralized)
68
+ Util::SINGULAR_TO_PLURAL[Util.class_case_to_us_case(self.to_s)] = pluralized
69
+ Util::PLURAL_TO_SINGULAR[pluralized] = Util.class_case_to_us_case(self.to_s)
70
+ end
71
+
56
72
  #-----------------------------------------------------------------------------
57
73
  # Table.validates_presence_of
58
74
  #-----------------------------------------------------------------------------
@@ -73,7 +89,7 @@ class Table
73
89
  define_method(kind.to_sym) do
74
90
  klass = Object.const_get(class_name)
75
91
  parent_id = @id
76
- Collection.new(self, klass.find { send(col) == parent_id })
92
+ Collection.new(self, klass.find { |r| r.send(col) == parent_id })
77
93
  end
78
94
  end
79
95
 
@@ -88,7 +104,7 @@ class Table
88
104
  define_method(kind.to_sym) do
89
105
  klass = Object.const_get(class_name)
90
106
  parent_id = @id
91
- klass.find(:first) { send(col) == parent_id }
107
+ klass.find(:first) { |r| r.send(col) == parent_id }
92
108
  end
93
109
  end
94
110
 
@@ -165,6 +181,18 @@ class Table
165
181
  define_method(col_name) do
166
182
  self.columns.detect { |c| c.name == col_name.to_sym }
167
183
  end
184
+
185
+ define_method("find_by_#{col_name}".to_sym) do |other|
186
+ if col_name == :id
187
+ self.find(other)
188
+ else
189
+ self.find(:first) { |tbl| tbl.send(col_name) == other }
190
+ end
191
+ end
192
+
193
+ define_method("find_all_by_#{col_name}".to_sym) do |other, *args|
194
+ self.find(*args) { |tbl| tbl.send(col_name) == other }
195
+ end
168
196
  end
169
197
 
170
198
  self.class_eval do
@@ -235,109 +263,199 @@ class Table
235
263
  # Table.find
236
264
  #-----------------------------------------------------------------------------
237
265
  def self.find(*args, &block)
238
- # If searching for just one id or a group of ids...
239
- if args[0].is_a?(Integer)
240
- if args.size == 1
241
- self.get_rec(args[0])
242
- else
243
- args.collect { |a| self.get_rec(a) }
266
+ options = {}
267
+ if args.size == 0
268
+ args = [:all]
269
+ elsif args.first.is_a?(Hash)
270
+ options = args.first
271
+ args = [:all]
272
+ elsif args.first.is_a?(Integer)
273
+ if args.last.is_a?(Hash)
274
+ options = args.last
275
+ args = args[0...-1]
244
276
  end
277
+ elsif args.first == :first
278
+ options = args.last if args.last.is_a?(Hash)
279
+ options[:limit] = 1
280
+ args = [:all]
281
+ elsif args.first == :all
282
+ options = args.last if args.last.is_a?(Hash)
283
+ end
284
+
285
+ case args.first
286
+ when :all then self.find_every(options, &block)
287
+ else self.find_from_ids(args, options)
288
+ end
289
+ end
290
+
291
+ #-----------------------------------------------------------------------------
292
+ # Table.find_from_ids
293
+ #-----------------------------------------------------------------------------
294
+ def self.find_from_ids(args, options)
295
+ if args.size == 1
296
+ result = self.get_rec(args.first)
245
297
  else
246
- result = []
247
- # If passed a query block...
248
- if block
249
- self.query.clear
250
- query_start = true
251
- sub_q = false
252
- sub_q_start = false
253
- sub_q_result = []
254
-
255
- # Grab the query block
256
- instance_eval(&block)
257
-
258
- # Step through the query block...
259
- self.query.each_with_index do |q,i|
260
- # If this is the start of an #any sub-block within the main block,
261
- # mark it as so and start grabbing the sub-block query.
262
- if q == :any_begin
263
- sub_q = :true
264
- sub_q_start = true
265
- sub_q_result = []
266
- next
267
- end
268
- # If this is the end of an #any sub-block within the main block,
269
- # mark it as so, and add the sub-block's query results to the current
270
- # results of the main query.
271
- if q == :any_end
272
- sub_q = false
273
- if query_start
274
- query_start = false
275
- result = sub_q_result
276
- else
277
- result = result & sub_q_result
278
- end
279
- sub_q_result = nil
280
- next
281
- end
298
+ result = self.apply_options_to_result(args.collect { |a|
299
+ self.get_rec(a) }, options)
300
+ end
301
+ return result
302
+ end
282
303
 
283
- # If currently within a sub-block query, execute and add it's results
284
- # to the current sub-block query results.
285
- if sub_q
286
- if sub_q_start
287
- sub_q_start = false
288
- sub_q_result = q[0].send(q[1], *q[2])
289
- else
290
- sub_q_result = sub_q_result | q[0].send(q[1], *q[2])
291
- end
292
- next
293
- end
304
+ #-----------------------------------------------------------------------------
305
+ # Table.find_every
306
+ #-----------------------------------------------------------------------------
307
+ def self.find_every(options, &block)
308
+ # If no block was supplied, just grab all the keys from the id column's
309
+ # index.
310
+ if block
311
+ result = self.find_from_block(&block)
312
+ else
313
+ result = self.id.keys
314
+ end
315
+ return nil if result.nil?
294
316
 
295
- # If this is the beginning of the query start a new result set,
296
- # otherwise, add the results of the current line of the query to
297
- # the existing result set.
298
- if query_start
299
- query_start = false
300
- result = q[0].send(q[1], *q[2])
301
- else
302
- result = result & q[0].send(q[1], *q[2])
317
+ return self.apply_options_to_result(
318
+ result.collect { |k| self.get_rec(k) }, options)
319
+ end
320
+
321
+ #-----------------------------------------------------------------------------
322
+ # Table.find_from_block
323
+ #-----------------------------------------------------------------------------
324
+ def self.find_from_block(&block)
325
+ result = []
326
+ or_result = []
327
+ query = Query.new
328
+ query.find(&block)
329
+
330
+ subquery_no = nil
331
+ subquery_type = nil
332
+
333
+ # Step through the query block...
334
+ query.predicates.each do |pred|
335
+ # Retain the previous subquery_no and subquery_type. This will help
336
+ # determine if I am still in an #any block or just finished an #any block.
337
+ previous_subquery_no = subquery_no
338
+ previous_subquery_type = subquery_type
339
+ subquery_no = pred.subquery_no
340
+ subquery_type = pred.subquery_type
341
+
342
+ # If subquery number has not changed, must be in the middle of an #any
343
+ # block. Therefore, we are going to do a union of the the comparison's
344
+ # results to the current or_result.
345
+ if previous_subquery_no == subquery_no
346
+ or_result = or_result | send(pred.property_name).send(pred.comparison,
347
+ *pred.arg)
348
+ # Otherwise, we are starting either a new :and predicate or a new #any
349
+ # block.
350
+ else
351
+ # Therefore, the first thing we want to check if the previous subquery
352
+ # was an #any block, and add it's result to the overall result array.
353
+ if previous_subquery_type == :or
354
+ # If the previous subquery was an #any block and it was the first
355
+ # subquery in the main query block, initialize the result array
356
+ # to the whole subqquery's result.
357
+ if previous_subquery_no == 1
358
+ result = or_result
359
+ # Otherwise, just do an intersection between the or_result and the
360
+ # overall result.
361
+ else
362
+ result = result & or_result
303
363
  end
304
364
  end
305
- # If did not pass a query block, just grab all of the ids in the table...
306
- else
307
- result = self.id.keys
308
- end
309
-
310
- # If no matching records found, return nil.
311
- if result.nil?
312
- nil
313
- # If user just wants first record, return first record.
314
- elsif args[0] == :first
315
- self.get_rec(result.first)
316
- # If user specified a paramaters hash, see if they specified a limit and
317
- # return that many records.
318
- elsif args[1].is_a?(Hash)
319
- if args[1].has_key?(:limit)
320
- if args[1][:limit] == 1
321
- self.get_rec(result.first)
365
+ # If the subquery type is :and, then we are just going to add it
366
+ # to the existing result.
367
+ if subquery_type == :and
368
+ # If this is the first subquery, then we just make the overall
369
+ # result equal to the comparison's result
370
+ if subquery_no == 1
371
+ result = send(pred.property_name).send(pred.comparison, *pred.arg)
372
+ # Otherwise, we are going to do an intersection on the
373
+ # comparison's result and the overall result.
322
374
  else
323
- result[0...args[1][:limit]].collect { |r| self.get_rec(r) }
375
+ result = result & send(pred.property_name).send(pred.comparison,
376
+ *pred.arg)
324
377
  end
378
+ # If the subquery type is :or, and it the subquery number is not
379
+ # equal to the previous subquery number, then we know we are
380
+ # at the first predicate of an #any block and we can initialize the
381
+ # the subquery's result array to whatever the subquery returns.
382
+ else
383
+ or_result = send(pred.property_name).send(pred.comparison, *pred.arg)
325
384
  end
326
- # Otherwise, just return the whole damn result set.
385
+ end
386
+ end
387
+ # Now that we are doing executing the whole query, we need to check if
388
+ # the last subquery was an #any block, so that we can make sure the
389
+ # results of this subquery get added into the overall query results.
390
+ if subquery_type == :or
391
+ if subquery_no == 1
392
+ result = or_result
327
393
  else
328
- result.collect { |r| self.get_rec(r) }
394
+ result = result & or_result
329
395
  end
330
396
  end
397
+
398
+ return result
331
399
  end
332
400
 
333
401
  #-----------------------------------------------------------------------------
334
- # Table.any
402
+ # Table.apply_options_to_result
335
403
  #-----------------------------------------------------------------------------
336
- def self.any
337
- self.query << :any_begin
338
- yield
339
- self.query << :any_end
340
- end
404
+ def self.apply_options_to_result(result, options)
405
+ return result if result.empty?
406
+
407
+ result = self.sort_result(result, *options[:order]) if options.has_key?(
408
+ :order)
409
+ result = result[options[:offset]-1..-1] if options.has_key?(:offset)
410
+
411
+ if options.has_key?(:limit)
412
+ if options[:limit] == 1
413
+ result = result.first
414
+ else
415
+ result = result[0...options[:limit]]
416
+ end
417
+ end
418
+ return result
419
+ end
420
+
421
+ #-----------------------------------------------------------------------------
422
+ # Table.sort_result
423
+ #-----------------------------------------------------------------------------
424
+ def self.sort_result(result, *order)
425
+ sort_cols_arrs = []
426
+ order.each do |sort_col|
427
+ if sort_col.to_s[0..0] == '-'
428
+ sort_cols_arrs << [sort_col.to_s[1..-1].to_sym, :desc]
429
+ elsif sort_col.to_s[0..0] == '+'
430
+ sort_cols_arrs << [sort_col.to_s[1..-1].to_sym, :asc]
431
+ else
432
+ sort_cols_arrs << [sort_col, :asc]
433
+ end
434
+ end
435
+
436
+ return result.sort do |a,b|
437
+ x = []
438
+ y = []
439
+ sort_cols_arrs.each do |s|
440
+ if [:integer, :float].include?(send(s.first).data_type)
441
+ a_value = a.send(s.first) || 0
442
+ b_value = b.send(s.first) || 0
443
+ else
444
+ a_value = a.send(s.first)
445
+ b_value = b.send(s.first)
446
+ end
447
+ if s.last == :desc
448
+ x << b_value
449
+ y << a_value
450
+ else
451
+ x << a_value
452
+ y << b_value
453
+ end
454
+ end
455
+
456
+ x <=> y
457
+ end
458
+ end
341
459
 
342
460
  #-----------------------------------------------------------------------------
343
461
  # Table.get_rec
@@ -359,7 +477,7 @@ class Table
359
477
  raise IndexCorruptError, "Index ID does not match table ID!", caller \
360
478
  unless rec_arr[1] == id
361
479
 
362
- rec = self.new(*rec_arr[1..-1])
480
+ rec = self.new(Hash[*self.column_names.zip(rec_arr[1..-1]).flatten])
363
481
  return rec
364
482
  end
365
483
 
@@ -375,11 +493,103 @@ class Table
375
493
  end
376
494
  end
377
495
 
496
+ #-----------------------------------------------------------------------------
497
+ # Table.export
498
+ #-----------------------------------------------------------------------------
499
+ def self.export(filename=1)
500
+ if filename.is_a?(Integer)
501
+ out_file = IO.open(1, 'w')
502
+ else
503
+ out_file = File.open(filename, 'w')
504
+ end
505
+ CSV::Writer.generate(out_file) do |out|
506
+ self.find.each do |rec|
507
+ out << self.column_names.collect {|n| rec.send(n)}
508
+ end
509
+ end
510
+
511
+ out_file.close
512
+ end
513
+
514
+ #-----------------------------------------------------------------------------
515
+ # Table.import
516
+ #-----------------------------------------------------------------------------
517
+ def self.import(filename=0)
518
+ if filename.is_a?(Integer)
519
+ in_file = IO.open(1, 'r')
520
+ else
521
+ in_file = File.open(filename, 'r')
522
+ end
523
+
524
+ CSV::Reader.parse(in_file) do |row|
525
+ rec = new
526
+ self.columns.zip(row) do |col, value|
527
+ rec.send("#{col.name}=", col.convert_to_native(value)) unless \
528
+ value.nil?
529
+ end
530
+ rec.save
531
+ end
532
+ in_file.close
533
+ end
534
+
535
+ #-----------------------------------------------------------------------------
536
+ # Table.exists?
537
+ #-----------------------------------------------------------------------------
538
+ def self.exists?(id)
539
+ if self.id[id]
540
+ true
541
+ else
542
+ false
543
+ end
544
+ end
545
+
546
+ #-----------------------------------------------------------------------------
547
+ # Table.destroy_all
548
+ #-----------------------------------------------------------------------------
549
+ def self.destroy_all(&block)
550
+ self.find(:all, &block).each { |r| p r.destroy }
551
+ end
552
+
553
+ #-----------------------------------------------------------------------------
554
+ # Table.destroy
555
+ #-----------------------------------------------------------------------------
556
+ def self.destroy(id)
557
+ self.find(id).destroy
558
+ end
559
+
560
+ #-----------------------------------------------------------------------------
561
+ # Table.delete_all
562
+ #-----------------------------------------------------------------------------
563
+ def self.delete_all(&block)
564
+ self.find(:all, &block).each { |r| p r.delete }
565
+ end
566
+
567
+ #-----------------------------------------------------------------------------
568
+ # Table.delete
569
+ #-----------------------------------------------------------------------------
570
+ def self.delete(id)
571
+ self.find(id).delete
572
+ end
573
+
378
574
  #-----------------------------------------------------------------------------
379
575
  # initialize
380
576
  #-----------------------------------------------------------------------------
381
- def initialize(*values)
382
- self.class.columns.zip(values).each { |c,v| send("#{c.name}=", v) }
577
+ def initialize(values=nil)
578
+ unless values.nil?
579
+ values.each do |k,v|
580
+ send("#{k}=", v) if self.class.column_names.include? k
581
+ end
582
+ end
583
+ end
584
+
585
+ #-----------------------------------------------------------------------------
586
+ # update_attributes
587
+ #-----------------------------------------------------------------------------
588
+ def update_attributes(values)
589
+ values.each do |k,v|
590
+ send("#{k}=", v) if self.class.column_names.include? k
591
+ end
592
+ save
383
593
  end
384
594
 
385
595
  #-----------------------------------------------------------------------------
@@ -396,10 +606,8 @@ class Table
396
606
 
397
607
  # Add new record.
398
608
  if @id.nil?
399
- id = increment_last_id_used
400
- append_record(id, self.class.column_names[1..-1].collect { |col_name|
401
- send(col_name) })
402
- @id = id
609
+ @id = append_record(self.class.column_names[1..-1].collect { |c_name|
610
+ send(c_name) })
403
611
  # Update existing record.
404
612
  else
405
613
  update_record(@id, self.class.columns[1..-1].collect { |c| send(c.name) })
@@ -407,6 +615,13 @@ class Table
407
615
  return true
408
616
  end
409
617
 
618
+ #-----------------------------------------------------------------------------
619
+ # delete
620
+ #-----------------------------------------------------------------------------
621
+ def delete
622
+ destroy
623
+ end
624
+
410
625
  #-----------------------------------------------------------------------------
411
626
  # destroy
412
627
  #-----------------------------------------------------------------------------
@@ -451,7 +666,8 @@ class Table
451
666
  #-----------------------------------------------------------------------------
452
667
  # append_record
453
668
  #-----------------------------------------------------------------------------
454
- def append_record(id, values)
669
+ def append_record(values)
670
+ id = increment_last_id_used
455
671
  fpos = nil
456
672
 
457
673
  self.class.with_table(File::RDWR) do |fptr|
@@ -471,6 +687,7 @@ class Table
471
687
  c.add_index_rec(values[i-1], id) unless values[i-1].nil?
472
688
  end
473
689
  end
690
+ return id
474
691
  end
475
692
 
476
693
  #-----------------------------------------------------------------------------
@@ -481,6 +698,10 @@ class Table
481
698
 
482
699
  fpos_rec_start = self.class.id[id]
483
700
 
701
+ raise(RecordNotFound,
702
+ "No #{self.class.table_name} record found with id: #{id}") if \
703
+ fpos_rec_start.nil?
704
+
484
705
  self.class.with_table(File::RDWR) do |fptr|
485
706
  fptr.seek(fpos_rec_start)
486
707