mongoose 0.2.0 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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