lafcadio 0.9.2 → 0.9.3
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/bin/lafcadio_schema +28 -26
- data/lib/lafcadio.rb +3 -3
- data/lib/lafcadio.rb~ +1 -1
- data/lib/lafcadio/domain.rb +29 -35
- data/lib/lafcadio/domain.rb~ +35 -42
- data/lib/lafcadio/mock.rb +37 -15
- data/lib/lafcadio/mock.rb~ +59 -36
- data/lib/lafcadio/objectField.rb +31 -20
- data/lib/lafcadio/objectField.rb~ +163 -142
- data/lib/lafcadio/objectStore.rb +125 -58
- data/lib/lafcadio/objectStore.rb~ +242 -177
- data/lib/lafcadio/query.rb +98 -95
- data/lib/lafcadio/query.rb~ +96 -95
- data/lib/lafcadio/schema.rb +4 -4
- data/lib/lafcadio/schema.rb~ +13 -17
- data/lib/lafcadio/test.rb +33 -7
- data/lib/lafcadio/test.rb~ +146 -20
- data/lib/lafcadio/util.rb +27 -11
- data/lib/lafcadio/util.rb~ +27 -43
- metadata +3 -7
- data/lib/lafcadio/dateTime.rb~ +0 -93
- data/lib/lafcadio/depend.rb~ +0 -8
- data/lib/lafcadio/objectStore.rb.~1.64.~ +0 -766
- data/lib/lafcadio/test/testconfig.dat~ +0 -13
data/lib/lafcadio/query.rb
CHANGED
@@ -32,7 +32,7 @@
|
|
32
32
|
# qry = Query.infer(
|
33
33
|
# SKU,
|
34
34
|
# :order_by => [ :standardPrice, :salePrice ],
|
35
|
-
# :order_by_order =>
|
35
|
+
# :order_by_order => :desc
|
36
36
|
# ) { |s| s.sku.nil? }
|
37
37
|
# qry.to_sql # => "select * from skus where skus.sku is null order by
|
38
38
|
# standardPrice, salePrice desc"
|
@@ -93,8 +93,10 @@
|
|
93
93
|
# # => "select * from users where fname like '%a'"
|
94
94
|
# fname_contains_a = User.get { |user| user.fname.like( /a/ ) }
|
95
95
|
# # => "select * from users where fname like '%a%'"
|
96
|
+
# james_or_jones = User.get { |user| user.lname.like( /J..es/ ) }
|
97
|
+
# # => "select * from users where lname like 'J__es'"
|
96
98
|
# Please note that although we're using the Regexp operators here, these aren't
|
97
|
-
# full-fledged regexps. Only
|
99
|
+
# full-fledged regexps. Only ^, $, and . work for this.
|
98
100
|
#
|
99
101
|
# == Compound conditions: <tt>&</tt> and <tt>|</tt>
|
100
102
|
# invoices = Invoice.get { |inv|
|
@@ -146,14 +148,13 @@ module Lafcadio
|
|
146
148
|
# Infers a query from a block. The first required argument is the domain
|
147
149
|
# class. Other optional arguments should be passed in hash form:
|
148
150
|
# [:order_by] An array of fields to order the results by.
|
149
|
-
# [:order_by_order] Possible values are
|
150
|
-
# to Query::DESC.
|
151
|
+
# [:order_by_order] Possible values are :asc or :desc. Defaults to :desc.
|
151
152
|
# qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
|
152
153
|
# qry.to_sql # => "select * from users where users.lname = 'Hwang'"
|
153
154
|
# qry = Query.infer(
|
154
155
|
# SKU,
|
155
156
|
# :order_by => [ :standardPrice, :salePrice ],
|
156
|
-
# :order_by_order =>
|
157
|
+
# :order_by_order => :desc
|
157
158
|
# ) { |s| s.sku.nil? }
|
158
159
|
# qry.to_sql # => "select * from skus where skus.sku is null order by
|
159
160
|
# standardPrice, salePrice desc"
|
@@ -163,29 +164,25 @@ module Lafcadio
|
|
163
164
|
end
|
164
165
|
|
165
166
|
def self.Or( *conditions ) #:nodoc:
|
166
|
-
conditions <<
|
167
|
+
conditions << :or
|
167
168
|
CompoundCondition.new( *conditions)
|
168
169
|
end
|
169
170
|
|
170
|
-
|
171
|
-
|
171
|
+
attr_reader :domain_class, :condition, :include, :limit, :order_by
|
172
|
+
attr_accessor :order_by_order
|
172
173
|
|
173
|
-
|
174
|
-
attr_accessor :order_by, :order_by_order
|
175
|
-
|
176
|
-
def initialize(domain_class, pk_id_or_condition = nil, opts = {} ) #:nodoc:
|
174
|
+
def initialize(domain_class, opts = {} ) #:nodoc:
|
177
175
|
@domain_class, @opts = domain_class, opts
|
178
176
|
( @condition, @order_by, @limit ) = [ nil, nil, nil ]
|
179
|
-
if
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
@condition = Query::Equals.new(
|
184
|
-
:pk_id, pk_id_or_condition, domain_class
|
185
|
-
)
|
186
|
-
end
|
177
|
+
if ( cond = opts[:condition] )
|
178
|
+
@condition = cond
|
179
|
+
elsif ( pk_id = opts[:pk_id] )
|
180
|
+
@condition = Query::Equals.new( :pk_id, pk_id, domain_class )
|
187
181
|
end
|
188
|
-
@
|
182
|
+
if ( @include = opts[:include] )
|
183
|
+
@include = [ @include ] unless @include.is_a?( Array )
|
184
|
+
end
|
185
|
+
@order_by_order = :asc
|
189
186
|
end
|
190
187
|
|
191
188
|
# Returns a new query representing the condition of the current query and
|
@@ -195,7 +192,7 @@ module Lafcadio
|
|
195
192
|
# qry = qry.and { |u| u.fname.equals( 'Francis' ) }
|
196
193
|
# qry.to_sql # => "select * from users where (users.lname = 'Hwang' and
|
197
194
|
# users.fname = 'Francis')"
|
198
|
-
def and( &action ); compound(
|
195
|
+
def and( &action ); compound( :and, action ); end
|
199
196
|
|
200
197
|
def collect( coll ) #:nodoc:
|
201
198
|
if @opts[:group_functions] == [:count]
|
@@ -248,10 +245,21 @@ module Lafcadio
|
|
248
245
|
@limit = limit.is_a?( Fixnum ) ? 0..limit-1 : limit
|
249
246
|
end
|
250
247
|
|
251
|
-
def limit_clause #:nodoc:
|
252
|
-
|
248
|
+
def limit_clause( db ) #:nodoc:
|
249
|
+
if @limit
|
250
|
+
case db
|
251
|
+
when 'Mysql'
|
252
|
+
"limit #{ @limit.begin }, #{ @limit.end - @limit.begin + 1 }"
|
253
|
+
when 'Pg'
|
254
|
+
limit_clause = "limit #{ @limit.end - @limit.begin + 1 }"
|
255
|
+
limit_clause += " offset #{ @limit.begin }" if @limit.begin > 0
|
256
|
+
limit_clause
|
257
|
+
end
|
258
|
+
end
|
253
259
|
end
|
254
260
|
|
261
|
+
def one_pk_id?; @condition and @condition.one_pk_id?; end
|
262
|
+
|
255
263
|
# Returns a new query representing the condition of the current query and
|
256
264
|
# the new inferred query.
|
257
265
|
# qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
|
@@ -259,7 +267,11 @@ module Lafcadio
|
|
259
267
|
# qry = qry.or { |u| u.fname.equals( 'Francis' ) }
|
260
268
|
# qry.to_sql # => "select * from users where (users.lname = 'Hwang' or
|
261
269
|
# users.fname = 'Francis')"
|
262
|
-
def or( &action ); compound(
|
270
|
+
def or( &action ); compound( :or, action ); end
|
271
|
+
|
272
|
+
def order_by=( ob )
|
273
|
+
@order_by = ( ob.is_a?( Array ) ? ob.map { |f| f.to_s } : ob.to_s ) if ob
|
274
|
+
end
|
263
275
|
|
264
276
|
def order_clause #:nodoc:
|
265
277
|
if @order_by
|
@@ -267,7 +279,7 @@ module Lafcadio
|
|
267
279
|
@domain_class.field( f_name.to_s ).db_field_name
|
268
280
|
}.join( ', ' )
|
269
281
|
clause = "order by #{ field_str } "
|
270
|
-
clause += @order_by_order ==
|
282
|
+
clause += @order_by_order == :asc ? 'asc' : 'desc'
|
271
283
|
clause
|
272
284
|
end
|
273
285
|
end
|
@@ -282,7 +294,7 @@ module Lafcadio
|
|
282
294
|
dobj.send order_by
|
283
295
|
end
|
284
296
|
}
|
285
|
-
objects.reverse! if order_by_order ==
|
297
|
+
objects.reverse! if order_by_order == :desc
|
286
298
|
objects = objects[limit] if limit
|
287
299
|
objects
|
288
300
|
end
|
@@ -301,32 +313,38 @@ module Lafcadio
|
|
301
313
|
|
302
314
|
def tables #:nodoc:
|
303
315
|
concrete_classes = domain_class.self_and_concrete_superclasses.reverse
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
316
|
+
sql = ''
|
317
|
+
dclass = nil
|
318
|
+
until concrete_classes.empty?
|
319
|
+
prev_dclass = dclass
|
320
|
+
dclass = concrete_classes.shift
|
321
|
+
if sql == ''
|
322
|
+
sql = dclass.table_name
|
323
|
+
else
|
324
|
+
sql += " inner join #{ dclass.table_name } on #{ sql_primary_key_field( prev_dclass ) } = #{ sql_primary_key_field( dclass ) }"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
if @include
|
328
|
+
@include.each do |include_sym|
|
329
|
+
field = dclass.field include_sym
|
330
|
+
included_dclass = field.linked_type
|
331
|
+
sql += " left outer join #{ included_dclass.table_name } on #{ dclass.table_name }.#{ field.db_field_name } = #{ sql_primary_key_field( included_dclass ) }"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
sql
|
308
335
|
end
|
309
336
|
|
310
|
-
def to_sql
|
337
|
+
def to_sql( db = 'Mysql' )
|
311
338
|
clauses = [ "select #{ fields }", "from #{ tables }" ]
|
312
339
|
clauses << where_clause if where_clause
|
313
340
|
clauses << order_clause if order_clause
|
314
|
-
clauses << limit_clause if limit_clause
|
341
|
+
clauses << limit_clause( db ) if limit_clause( db )
|
315
342
|
clauses.join ' '
|
316
343
|
end
|
317
344
|
|
318
345
|
def where_clause #:nodoc:
|
319
|
-
concrete_classes = domain_class.self_and_concrete_superclasses.reverse
|
320
346
|
where_clauses = []
|
321
|
-
|
322
|
-
if i < concrete_classes.size - 1
|
323
|
-
join_clause = sql_primary_key_field( domain_class ) + ' = ' +
|
324
|
-
sql_primary_key_field( concrete_classes[i+1] )
|
325
|
-
where_clauses << join_clause
|
326
|
-
else
|
327
|
-
where_clauses << @condition.to_sql if @condition
|
328
|
-
end
|
329
|
-
}
|
347
|
+
where_clauses << @condition.to_sql if @condition
|
330
348
|
!where_clauses.empty? ? 'where ' + where_clauses.join( ' and ' ) : nil
|
331
349
|
end
|
332
350
|
|
@@ -376,32 +394,24 @@ module Lafcadio
|
|
376
394
|
end
|
377
395
|
|
378
396
|
def not; Query::Not.new( self ); end
|
397
|
+
|
398
|
+
def one_pk_id?; self.is_a?( Equals ) and primary_key_field?; end
|
379
399
|
|
380
400
|
def primary_key_field?; 'pk_id' == @fieldName; end
|
381
401
|
|
382
|
-
def query; Query.new( @domain_class, self ); end
|
402
|
+
def query; Query.new( @domain_class, :condition => self ); end
|
383
403
|
|
384
404
|
def to_condition; self; end
|
385
405
|
end
|
386
406
|
|
387
407
|
class Compare < Condition #:nodoc:
|
388
|
-
|
389
|
-
LESS_THAN_OR_EQUAL = 2
|
390
|
-
GREATER_THAN_OR_EQUAL = 3
|
391
|
-
GREATER_THAN = 4
|
392
|
-
|
393
|
-
@@comparators = {
|
394
|
-
LESS_THAN => '<',
|
395
|
-
LESS_THAN_OR_EQUAL => '<=',
|
396
|
-
GREATER_THAN_OR_EQUAL => '>=',
|
397
|
-
GREATER_THAN => '>'
|
398
|
-
}
|
408
|
+
@@comparators = { :lt => '<', :lte => '<=', :gte => '>=', :gt => '>' }
|
399
409
|
|
400
410
|
@@mockComparators = {
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
411
|
+
:lt => Proc.new { |d1, d2| d1 < d2 },
|
412
|
+
:lte => Proc.new { |d1, d2| d1 <= d2 },
|
413
|
+
:gte => Proc.new { |d1, d2| d1 >= d2 },
|
414
|
+
:gt => Proc.new { |d1, d2| d1 > d2 }
|
405
415
|
}
|
406
416
|
|
407
417
|
def initialize(fieldName, searchTerm, domain_class, compareType)
|
@@ -431,15 +441,12 @@ module Lafcadio
|
|
431
441
|
end
|
432
442
|
|
433
443
|
class CompoundCondition < Condition #:nodoc:
|
434
|
-
AND = 1
|
435
|
-
OR = 2
|
436
|
-
|
437
444
|
def initialize( *args )
|
438
|
-
if( [
|
445
|
+
if( [ :and, :or ].include?( args.last ) )
|
439
446
|
@compound_type = args.last
|
440
447
|
args.pop
|
441
448
|
else
|
442
|
-
@compound_type =
|
449
|
+
@compound_type = :and
|
443
450
|
end
|
444
451
|
@conditions = args.map { |arg|
|
445
452
|
arg.respond_to?( :to_condition ) ? arg.to_condition : arg
|
@@ -448,7 +455,7 @@ module Lafcadio
|
|
448
455
|
end
|
449
456
|
|
450
457
|
def dobj_satisfies?(anObj)
|
451
|
-
if @compound_type ==
|
458
|
+
if @compound_type == :and
|
452
459
|
@conditions.inject( true ) { |result, cond|
|
453
460
|
result && cond.dobj_satisfies?( anObj )
|
454
461
|
}
|
@@ -460,25 +467,25 @@ module Lafcadio
|
|
460
467
|
end
|
461
468
|
|
462
469
|
def implied_by?( other_condition )
|
463
|
-
@compound_type ==
|
470
|
+
@compound_type == :or && @conditions.any? { |cond|
|
464
471
|
cond.implies?( other_condition )
|
465
472
|
}
|
466
473
|
end
|
467
474
|
|
468
475
|
def implies?( other_condition )
|
469
476
|
super( other_condition ) or (
|
470
|
-
@compound_type ==
|
477
|
+
@compound_type == :and and @conditions.any? { |cond|
|
471
478
|
cond.implies? other_condition
|
472
479
|
}
|
473
480
|
) or (
|
474
|
-
@compound_type ==
|
481
|
+
@compound_type == :or and @conditions.all? { |cond|
|
475
482
|
cond.implies? other_condition
|
476
483
|
}
|
477
484
|
)
|
478
485
|
end
|
479
486
|
|
480
487
|
def to_sql
|
481
|
-
booleanString = @compound_type ==
|
488
|
+
booleanString = @compound_type == :and ? 'and' : 'or'
|
482
489
|
subSqlStrings = @conditions.collect { |cond| cond.to_sql }
|
483
490
|
"(#{ subSqlStrings.join(" #{ booleanString } ") })"
|
484
491
|
end
|
@@ -500,7 +507,10 @@ module Lafcadio
|
|
500
507
|
if ( classField = self.domain_class.field( fieldName ) )
|
501
508
|
ObjectFieldImpostor.new( self, classField )
|
502
509
|
else
|
503
|
-
|
510
|
+
msg = "undefined method `" + fieldName +
|
511
|
+
"' for #<DomainObjectImpostor::" +
|
512
|
+
'#{ domain_class.name }' + ">"
|
513
|
+
raise( NoMethodError, msg )
|
504
514
|
end
|
505
515
|
end
|
506
516
|
|
@@ -574,16 +584,16 @@ module Lafcadio
|
|
574
584
|
class Include < CompoundCondition #:nodoc:
|
575
585
|
def initialize( field_name, search_term, domain_class )
|
576
586
|
begin_cond = Like.new(
|
577
|
-
field_name, search_term + ',', domain_class,
|
587
|
+
field_name, search_term + ',', domain_class, :post_only
|
578
588
|
)
|
579
589
|
mid_cond = Like.new(
|
580
590
|
field_name, ',' + search_term + ',', domain_class
|
581
591
|
)
|
582
592
|
end_cond = Like.new(
|
583
|
-
field_name, ',' + search_term, domain_class,
|
593
|
+
field_name, ',' + search_term, domain_class, :pre_only
|
584
594
|
)
|
585
595
|
only_cond = Equals.new( field_name, search_term, domain_class )
|
586
|
-
super( begin_cond, mid_cond, end_cond, only_cond,
|
596
|
+
super( begin_cond, mid_cond, end_cond, only_cond, :or )
|
587
597
|
end
|
588
598
|
end
|
589
599
|
|
@@ -593,7 +603,7 @@ module Lafcadio
|
|
593
603
|
unless args.size == 1
|
594
604
|
h = args.last
|
595
605
|
@order_by = h[:order_by]
|
596
|
-
@order_by_order = ( h[:order_by_order] or
|
606
|
+
@order_by_order = ( h[:order_by_order] or :asc )
|
597
607
|
@limit = h[:limit]
|
598
608
|
end
|
599
609
|
end
|
@@ -601,7 +611,7 @@ module Lafcadio
|
|
601
611
|
def execute
|
602
612
|
impostor = DomainObjectImpostor.impostor @domain_class
|
603
613
|
condition = @action.call( impostor ).to_condition
|
604
|
-
query = Query.new( @domain_class, condition )
|
614
|
+
query = Query.new( @domain_class, :condition => condition )
|
605
615
|
query.order_by = @order_by
|
606
616
|
query.order_by_order = @order_by_order
|
607
617
|
query.limit = @limit
|
@@ -610,12 +620,8 @@ module Lafcadio
|
|
610
620
|
end
|
611
621
|
|
612
622
|
class Like < Condition #:nodoc:
|
613
|
-
PRE_AND_POST = 1
|
614
|
-
PRE_ONLY = 2
|
615
|
-
POST_ONLY = 3
|
616
|
-
|
617
623
|
def initialize(
|
618
|
-
fieldName, searchTerm, domain_class, matchType =
|
624
|
+
fieldName, searchTerm, domain_class, matchType = :pre_and_post
|
619
625
|
)
|
620
626
|
if searchTerm.is_a? Regexp
|
621
627
|
searchTerm = process_regexp searchTerm
|
@@ -637,36 +643,37 @@ module Lafcadio
|
|
637
643
|
|
638
644
|
def process_regexp( searchTerm )
|
639
645
|
if searchTerm.source =~ /^\^(.*)/
|
640
|
-
@matchType =
|
646
|
+
@matchType = :post_only
|
641
647
|
$1
|
642
648
|
elsif searchTerm.source =~ /(.*)\$$/
|
643
|
-
@matchType =
|
649
|
+
@matchType = :pre_only
|
644
650
|
$1
|
645
651
|
else
|
646
|
-
@matchType =
|
652
|
+
@matchType = :pre_and_post
|
647
653
|
searchTerm.source
|
648
654
|
end
|
649
655
|
end
|
650
656
|
|
651
657
|
def regexp
|
652
|
-
if @matchType ==
|
658
|
+
if @matchType == :pre_and_post
|
653
659
|
Regexp.new( @searchTerm, Regexp::IGNORECASE )
|
654
|
-
elsif @matchType ==
|
660
|
+
elsif @matchType == :pre_only
|
655
661
|
Regexp.new( @searchTerm.to_s + "$", Regexp::IGNORECASE )
|
656
|
-
elsif @matchType ==
|
662
|
+
elsif @matchType == :post_only
|
657
663
|
Regexp.new( "^" + @searchTerm, Regexp::IGNORECASE )
|
658
664
|
end
|
659
665
|
end
|
660
666
|
|
661
667
|
def to_sql
|
662
|
-
withWildcards = @searchTerm
|
663
|
-
if @matchType ==
|
668
|
+
withWildcards = @searchTerm.clone
|
669
|
+
if @matchType == :pre_and_post
|
664
670
|
withWildcards = "%" + withWildcards + "%"
|
665
|
-
elsif @matchType ==
|
671
|
+
elsif @matchType == :pre_only
|
666
672
|
withWildcards = "%" + withWildcards
|
667
|
-
elsif @matchType ==
|
673
|
+
elsif @matchType == :post_only
|
668
674
|
withWildcards += "%"
|
669
675
|
end
|
676
|
+
withWildcards.gsub!( /(\\?\.)/ ) { |m| m.size == 1 ? "_" : "." }
|
670
677
|
"#{ db_field_name } like '#{ withWildcards }'"
|
671
678
|
end
|
672
679
|
end
|
@@ -712,11 +719,7 @@ module Lafcadio
|
|
712
719
|
|
713
720
|
class ObjectFieldImpostor #:nodoc:
|
714
721
|
def self.comparators
|
715
|
-
{
|
716
|
-
'lt' => Compare::LESS_THAN, 'lte' => Compare::LESS_THAN_OR_EQUAL,
|
717
|
-
'gte' => Compare::GREATER_THAN_OR_EQUAL,
|
718
|
-
'gt' => Compare::GREATER_THAN
|
719
|
-
}
|
722
|
+
{ 'lt' => :lt, 'lte' => :lte, 'gte' => :gte, 'gt' => :gt }
|
720
723
|
end
|
721
724
|
|
722
725
|
attr_reader :class_field
|
data/lib/lafcadio/query.rb~
CHANGED
@@ -32,7 +32,7 @@
|
|
32
32
|
# qry = Query.infer(
|
33
33
|
# SKU,
|
34
34
|
# :order_by => [ :standardPrice, :salePrice ],
|
35
|
-
# :order_by_order =>
|
35
|
+
# :order_by_order => :desc
|
36
36
|
# ) { |s| s.sku.nil? }
|
37
37
|
# qry.to_sql # => "select * from skus where skus.sku is null order by
|
38
38
|
# standardPrice, salePrice desc"
|
@@ -108,7 +108,7 @@
|
|
108
108
|
# }
|
109
109
|
# # => "select * from invoices where (hours = 40 or rate = 50 or client = 99)"
|
110
110
|
# Note that both compound operators can be nested:
|
111
|
-
# invoices =
|
111
|
+
# invoices = Invoice.get { |inv|
|
112
112
|
# inv.hours.equals( 40 ) &
|
113
113
|
# ( inv.rate.equals( 50 ) | inv.client.equals( client99 ) )
|
114
114
|
# }
|
@@ -146,14 +146,13 @@ module Lafcadio
|
|
146
146
|
# Infers a query from a block. The first required argument is the domain
|
147
147
|
# class. Other optional arguments should be passed in hash form:
|
148
148
|
# [:order_by] An array of fields to order the results by.
|
149
|
-
# [:order_by_order] Possible values are
|
150
|
-
# to Query::DESC.
|
149
|
+
# [:order_by_order] Possible values are :asc or :desc. Defaults to :desc.
|
151
150
|
# qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
|
152
151
|
# qry.to_sql # => "select * from users where users.lname = 'Hwang'"
|
153
152
|
# qry = Query.infer(
|
154
153
|
# SKU,
|
155
154
|
# :order_by => [ :standardPrice, :salePrice ],
|
156
|
-
# :order_by_order =>
|
155
|
+
# :order_by_order => :desc
|
157
156
|
# ) { |s| s.sku.nil? }
|
158
157
|
# qry.to_sql # => "select * from skus where skus.sku is null order by
|
159
158
|
# standardPrice, salePrice desc"
|
@@ -163,29 +162,25 @@ module Lafcadio
|
|
163
162
|
end
|
164
163
|
|
165
164
|
def self.Or( *conditions ) #:nodoc:
|
166
|
-
conditions <<
|
165
|
+
conditions << :or
|
167
166
|
CompoundCondition.new( *conditions)
|
168
167
|
end
|
169
168
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
attr_reader :domain_class, :condition, :limit
|
174
|
-
attr_accessor :order_by, :order_by_order
|
169
|
+
attr_reader :domain_class, :condition, :include, :limit, :order_by
|
170
|
+
attr_accessor :order_by_order
|
175
171
|
|
176
|
-
def initialize(domain_class,
|
172
|
+
def initialize(domain_class, opts = {} ) #:nodoc:
|
177
173
|
@domain_class, @opts = domain_class, opts
|
178
174
|
( @condition, @order_by, @limit ) = [ nil, nil, nil ]
|
179
|
-
if
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
end
|
175
|
+
if ( cond = opts[:condition] )
|
176
|
+
@condition = cond
|
177
|
+
elsif ( pk_id = opts[:pk_id] )
|
178
|
+
@condition = Query::Equals.new( :pk_id, pk_id, domain_class )
|
179
|
+
end
|
180
|
+
if ( @include = opts[:include] )
|
181
|
+
@include = [ @include ] unless @include.is_a?( Array )
|
187
182
|
end
|
188
|
-
@order_by_order =
|
183
|
+
@order_by_order = :asc
|
189
184
|
end
|
190
185
|
|
191
186
|
# Returns a new query representing the condition of the current query and
|
@@ -195,7 +190,7 @@ module Lafcadio
|
|
195
190
|
# qry = qry.and { |u| u.fname.equals( 'Francis' ) }
|
196
191
|
# qry.to_sql # => "select * from users where (users.lname = 'Hwang' and
|
197
192
|
# users.fname = 'Francis')"
|
198
|
-
def and( &action ); compound(
|
193
|
+
def and( &action ); compound( :and, action ); end
|
199
194
|
|
200
195
|
def collect( coll ) #:nodoc:
|
201
196
|
if @opts[:group_functions] == [:count]
|
@@ -248,10 +243,21 @@ module Lafcadio
|
|
248
243
|
@limit = limit.is_a?( Fixnum ) ? 0..limit-1 : limit
|
249
244
|
end
|
250
245
|
|
251
|
-
def limit_clause #:nodoc:
|
252
|
-
|
246
|
+
def limit_clause( db ) #:nodoc:
|
247
|
+
if @limit
|
248
|
+
case db
|
249
|
+
when 'Mysql'
|
250
|
+
"limit #{ @limit.begin }, #{ @limit.end - @limit.begin + 1 }"
|
251
|
+
when 'Pg'
|
252
|
+
limit_clause = "limit #{ @limit.end - @limit.begin + 1 }"
|
253
|
+
limit_clause += " offset #{ @limit.begin }" if @limit.begin > 0
|
254
|
+
limit_clause
|
255
|
+
end
|
256
|
+
end
|
253
257
|
end
|
254
258
|
|
259
|
+
def one_pk_id?; @condition and @condition.one_pk_id?; end
|
260
|
+
|
255
261
|
# Returns a new query representing the condition of the current query and
|
256
262
|
# the new inferred query.
|
257
263
|
# qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
|
@@ -259,7 +265,11 @@ module Lafcadio
|
|
259
265
|
# qry = qry.or { |u| u.fname.equals( 'Francis' ) }
|
260
266
|
# qry.to_sql # => "select * from users where (users.lname = 'Hwang' or
|
261
267
|
# users.fname = 'Francis')"
|
262
|
-
def or( &action ); compound(
|
268
|
+
def or( &action ); compound( :or, action ); end
|
269
|
+
|
270
|
+
def order_by=( ob )
|
271
|
+
@order_by = ( ob.is_a?( Array ) ? ob.map { |f| f.to_s } : ob.to_s ) if ob
|
272
|
+
end
|
263
273
|
|
264
274
|
def order_clause #:nodoc:
|
265
275
|
if @order_by
|
@@ -267,7 +277,7 @@ module Lafcadio
|
|
267
277
|
@domain_class.field( f_name.to_s ).db_field_name
|
268
278
|
}.join( ', ' )
|
269
279
|
clause = "order by #{ field_str } "
|
270
|
-
clause += @order_by_order ==
|
280
|
+
clause += @order_by_order == :asc ? 'asc' : 'desc'
|
271
281
|
clause
|
272
282
|
end
|
273
283
|
end
|
@@ -282,7 +292,7 @@ module Lafcadio
|
|
282
292
|
dobj.send order_by
|
283
293
|
end
|
284
294
|
}
|
285
|
-
objects.reverse! if order_by_order ==
|
295
|
+
objects.reverse! if order_by_order == :desc
|
286
296
|
objects = objects[limit] if limit
|
287
297
|
objects
|
288
298
|
end
|
@@ -301,32 +311,38 @@ module Lafcadio
|
|
301
311
|
|
302
312
|
def tables #:nodoc:
|
303
313
|
concrete_classes = domain_class.self_and_concrete_superclasses.reverse
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
314
|
+
sql = ''
|
315
|
+
dclass = nil
|
316
|
+
until concrete_classes.empty?
|
317
|
+
prev_dclass = dclass
|
318
|
+
dclass = concrete_classes.shift
|
319
|
+
if sql == ''
|
320
|
+
sql = dclass.table_name
|
321
|
+
else
|
322
|
+
sql += " inner join #{ dclass.table_name } on #{ sql_primary_key_field( prev_dclass ) } = #{ sql_primary_key_field( dclass ) }"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
if @include
|
326
|
+
@include.each do |include_sym|
|
327
|
+
field = dclass.field include_sym
|
328
|
+
included_dclass = field.linked_type
|
329
|
+
sql += " left outer join #{ included_dclass.table_name } on #{ dclass.table_name }.#{ field.db_field_name } = #{ sql_primary_key_field( included_dclass ) }"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
sql
|
308
333
|
end
|
309
334
|
|
310
|
-
def to_sql
|
335
|
+
def to_sql( db = 'Mysql' )
|
311
336
|
clauses = [ "select #{ fields }", "from #{ tables }" ]
|
312
337
|
clauses << where_clause if where_clause
|
313
338
|
clauses << order_clause if order_clause
|
314
|
-
clauses << limit_clause if limit_clause
|
339
|
+
clauses << limit_clause( db ) if limit_clause( db )
|
315
340
|
clauses.join ' '
|
316
341
|
end
|
317
342
|
|
318
343
|
def where_clause #:nodoc:
|
319
|
-
concrete_classes = domain_class.self_and_concrete_superclasses.reverse
|
320
344
|
where_clauses = []
|
321
|
-
|
322
|
-
if i < concrete_classes.size - 1
|
323
|
-
join_clause = sql_primary_key_field( domain_class ) + ' = ' +
|
324
|
-
sql_primary_key_field( concrete_classes[i+1] )
|
325
|
-
where_clauses << join_clause
|
326
|
-
else
|
327
|
-
where_clauses << @condition.to_sql if @condition
|
328
|
-
end
|
329
|
-
}
|
345
|
+
where_clauses << @condition.to_sql if @condition
|
330
346
|
!where_clauses.empty? ? 'where ' + where_clauses.join( ' and ' ) : nil
|
331
347
|
end
|
332
348
|
|
@@ -376,32 +392,24 @@ module Lafcadio
|
|
376
392
|
end
|
377
393
|
|
378
394
|
def not; Query::Not.new( self ); end
|
395
|
+
|
396
|
+
def one_pk_id?; self.is_a?( Equals ) and primary_key_field?; end
|
379
397
|
|
380
398
|
def primary_key_field?; 'pk_id' == @fieldName; end
|
381
399
|
|
382
|
-
def query; Query.new( @domain_class, self ); end
|
400
|
+
def query; Query.new( @domain_class, :condition => self ); end
|
383
401
|
|
384
402
|
def to_condition; self; end
|
385
403
|
end
|
386
404
|
|
387
405
|
class Compare < Condition #:nodoc:
|
388
|
-
|
389
|
-
LESS_THAN_OR_EQUAL = 2
|
390
|
-
GREATER_THAN_OR_EQUAL = 3
|
391
|
-
GREATER_THAN = 4
|
392
|
-
|
393
|
-
@@comparators = {
|
394
|
-
LESS_THAN => '<',
|
395
|
-
LESS_THAN_OR_EQUAL => '<=',
|
396
|
-
GREATER_THAN_OR_EQUAL => '>=',
|
397
|
-
GREATER_THAN => '>'
|
398
|
-
}
|
406
|
+
@@comparators = { :lt => '<', :lte => '<=', :gte => '>=', :gt => '>' }
|
399
407
|
|
400
408
|
@@mockComparators = {
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
409
|
+
:lt => Proc.new { |d1, d2| d1 < d2 },
|
410
|
+
:lte => Proc.new { |d1, d2| d1 <= d2 },
|
411
|
+
:gte => Proc.new { |d1, d2| d1 >= d2 },
|
412
|
+
:gt => Proc.new { |d1, d2| d1 > d2 }
|
405
413
|
}
|
406
414
|
|
407
415
|
def initialize(fieldName, searchTerm, domain_class, compareType)
|
@@ -431,15 +439,12 @@ module Lafcadio
|
|
431
439
|
end
|
432
440
|
|
433
441
|
class CompoundCondition < Condition #:nodoc:
|
434
|
-
AND = 1
|
435
|
-
OR = 2
|
436
|
-
|
437
442
|
def initialize( *args )
|
438
|
-
if( [
|
443
|
+
if( [ :and, :or ].include?( args.last ) )
|
439
444
|
@compound_type = args.last
|
440
445
|
args.pop
|
441
446
|
else
|
442
|
-
@compound_type =
|
447
|
+
@compound_type = :and
|
443
448
|
end
|
444
449
|
@conditions = args.map { |arg|
|
445
450
|
arg.respond_to?( :to_condition ) ? arg.to_condition : arg
|
@@ -448,7 +453,7 @@ module Lafcadio
|
|
448
453
|
end
|
449
454
|
|
450
455
|
def dobj_satisfies?(anObj)
|
451
|
-
if @compound_type ==
|
456
|
+
if @compound_type == :and
|
452
457
|
@conditions.inject( true ) { |result, cond|
|
453
458
|
result && cond.dobj_satisfies?( anObj )
|
454
459
|
}
|
@@ -460,25 +465,25 @@ module Lafcadio
|
|
460
465
|
end
|
461
466
|
|
462
467
|
def implied_by?( other_condition )
|
463
|
-
@compound_type ==
|
468
|
+
@compound_type == :or && @conditions.any? { |cond|
|
464
469
|
cond.implies?( other_condition )
|
465
470
|
}
|
466
471
|
end
|
467
472
|
|
468
473
|
def implies?( other_condition )
|
469
474
|
super( other_condition ) or (
|
470
|
-
@compound_type ==
|
475
|
+
@compound_type == :and and @conditions.any? { |cond|
|
471
476
|
cond.implies? other_condition
|
472
477
|
}
|
473
478
|
) or (
|
474
|
-
@compound_type ==
|
479
|
+
@compound_type == :or and @conditions.all? { |cond|
|
475
480
|
cond.implies? other_condition
|
476
481
|
}
|
477
482
|
)
|
478
483
|
end
|
479
484
|
|
480
485
|
def to_sql
|
481
|
-
booleanString = @compound_type ==
|
486
|
+
booleanString = @compound_type == :and ? 'and' : 'or'
|
482
487
|
subSqlStrings = @conditions.collect { |cond| cond.to_sql }
|
483
488
|
"(#{ subSqlStrings.join(" #{ booleanString } ") })"
|
484
489
|
end
|
@@ -500,7 +505,10 @@ module Lafcadio
|
|
500
505
|
if ( classField = self.domain_class.field( fieldName ) )
|
501
506
|
ObjectFieldImpostor.new( self, classField )
|
502
507
|
else
|
503
|
-
|
508
|
+
msg = "undefined method `" + fieldName +
|
509
|
+
"' for #<DomainObjectImpostor::" +
|
510
|
+
'#{ domain_class.name }' + ">"
|
511
|
+
raise( NoMethodError, msg )
|
504
512
|
end
|
505
513
|
end
|
506
514
|
|
@@ -574,16 +582,16 @@ module Lafcadio
|
|
574
582
|
class Include < CompoundCondition #:nodoc:
|
575
583
|
def initialize( field_name, search_term, domain_class )
|
576
584
|
begin_cond = Like.new(
|
577
|
-
field_name, search_term + ',', domain_class,
|
585
|
+
field_name, search_term + ',', domain_class, :post_only
|
578
586
|
)
|
579
587
|
mid_cond = Like.new(
|
580
588
|
field_name, ',' + search_term + ',', domain_class
|
581
589
|
)
|
582
590
|
end_cond = Like.new(
|
583
|
-
field_name, ',' + search_term, domain_class,
|
591
|
+
field_name, ',' + search_term, domain_class, :pre_only
|
584
592
|
)
|
585
593
|
only_cond = Equals.new( field_name, search_term, domain_class )
|
586
|
-
super( begin_cond, mid_cond, end_cond, only_cond,
|
594
|
+
super( begin_cond, mid_cond, end_cond, only_cond, :or )
|
587
595
|
end
|
588
596
|
end
|
589
597
|
|
@@ -593,7 +601,7 @@ module Lafcadio
|
|
593
601
|
unless args.size == 1
|
594
602
|
h = args.last
|
595
603
|
@order_by = h[:order_by]
|
596
|
-
@order_by_order = ( h[:order_by_order] or
|
604
|
+
@order_by_order = ( h[:order_by_order] or :asc )
|
597
605
|
@limit = h[:limit]
|
598
606
|
end
|
599
607
|
end
|
@@ -601,7 +609,7 @@ module Lafcadio
|
|
601
609
|
def execute
|
602
610
|
impostor = DomainObjectImpostor.impostor @domain_class
|
603
611
|
condition = @action.call( impostor ).to_condition
|
604
|
-
query = Query.new( @domain_class, condition )
|
612
|
+
query = Query.new( @domain_class, :condition => condition )
|
605
613
|
query.order_by = @order_by
|
606
614
|
query.order_by_order = @order_by_order
|
607
615
|
query.limit = @limit
|
@@ -610,12 +618,8 @@ module Lafcadio
|
|
610
618
|
end
|
611
619
|
|
612
620
|
class Like < Condition #:nodoc:
|
613
|
-
PRE_AND_POST = 1
|
614
|
-
PRE_ONLY = 2
|
615
|
-
POST_ONLY = 3
|
616
|
-
|
617
621
|
def initialize(
|
618
|
-
fieldName, searchTerm, domain_class, matchType =
|
622
|
+
fieldName, searchTerm, domain_class, matchType = :pre_and_post
|
619
623
|
)
|
620
624
|
if searchTerm.is_a? Regexp
|
621
625
|
searchTerm = process_regexp searchTerm
|
@@ -637,36 +641,37 @@ module Lafcadio
|
|
637
641
|
|
638
642
|
def process_regexp( searchTerm )
|
639
643
|
if searchTerm.source =~ /^\^(.*)/
|
640
|
-
@matchType =
|
644
|
+
@matchType = :post_only
|
641
645
|
$1
|
642
646
|
elsif searchTerm.source =~ /(.*)\$$/
|
643
|
-
@matchType =
|
647
|
+
@matchType = :pre_only
|
644
648
|
$1
|
645
649
|
else
|
646
|
-
@matchType =
|
650
|
+
@matchType = :pre_and_post
|
647
651
|
searchTerm.source
|
648
652
|
end
|
649
653
|
end
|
650
654
|
|
651
655
|
def regexp
|
652
|
-
if @matchType ==
|
656
|
+
if @matchType == :pre_and_post
|
653
657
|
Regexp.new( @searchTerm, Regexp::IGNORECASE )
|
654
|
-
elsif @matchType ==
|
658
|
+
elsif @matchType == :pre_only
|
655
659
|
Regexp.new( @searchTerm.to_s + "$", Regexp::IGNORECASE )
|
656
|
-
elsif @matchType ==
|
660
|
+
elsif @matchType == :post_only
|
657
661
|
Regexp.new( "^" + @searchTerm, Regexp::IGNORECASE )
|
658
662
|
end
|
659
663
|
end
|
660
664
|
|
661
665
|
def to_sql
|
662
|
-
withWildcards = @searchTerm
|
663
|
-
if @matchType ==
|
666
|
+
withWildcards = @searchTerm.clone
|
667
|
+
if @matchType == :pre_and_post
|
664
668
|
withWildcards = "%" + withWildcards + "%"
|
665
|
-
elsif @matchType ==
|
669
|
+
elsif @matchType == :pre_only
|
666
670
|
withWildcards = "%" + withWildcards
|
667
|
-
elsif @matchType ==
|
671
|
+
elsif @matchType == :post_only
|
668
672
|
withWildcards += "%"
|
669
673
|
end
|
674
|
+
withWildcards.gsub!( /(\\?\.)/ ) { |m| m.size == 1 ? "_" : "." }
|
670
675
|
"#{ db_field_name } like '#{ withWildcards }'"
|
671
676
|
end
|
672
677
|
end
|
@@ -712,11 +717,7 @@ module Lafcadio
|
|
712
717
|
|
713
718
|
class ObjectFieldImpostor #:nodoc:
|
714
719
|
def self.comparators
|
715
|
-
{
|
716
|
-
'lt' => Compare::LESS_THAN, 'lte' => Compare::LESS_THAN_OR_EQUAL,
|
717
|
-
'gte' => Compare::GREATER_THAN_OR_EQUAL,
|
718
|
-
'gt' => Compare::GREATER_THAN
|
719
|
-
}
|
720
|
+
{ 'lt' => :lt, 'lte' => :lte, 'gte' => :gte, 'gt' => :gt }
|
720
721
|
end
|
721
722
|
|
722
723
|
attr_reader :class_field
|