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.
@@ -32,7 +32,7 @@
32
32
  # qry = Query.infer(
33
33
  # SKU,
34
34
  # :order_by => [ :standardPrice, :salePrice ],
35
- # :order_by_order => Query::DESC
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 ^ and $ work for this.
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 Query::ASC or Query::DESC. Defaults
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 => Query::DESC
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 << CompoundCondition::OR
167
+ conditions << :or
167
168
  CompoundCondition.new( *conditions)
168
169
  end
169
170
 
170
- ASC = 1
171
- DESC = 2
171
+ attr_reader :domain_class, :condition, :include, :limit, :order_by
172
+ attr_accessor :order_by_order
172
173
 
173
- attr_reader :domain_class, :condition, :limit
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 pk_id_or_condition
180
- if pk_id_or_condition.is_a?( Condition )
181
- @condition = pk_id_or_condition
182
- else
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
- @order_by_order = ASC
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( CompoundCondition::AND, action ); end
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
- "limit #{ @limit.begin }, #{ @limit.end - @limit.begin + 1 }" if @limit
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( CompoundCondition::OR, action ); end
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 == ASC ? 'asc' : 'desc'
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 == Query::DESC
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
- table_names = concrete_classes.collect { |domain_class|
305
- domain_class.table_name
306
- }
307
- table_names.join( ', ' )
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
- concrete_classes.each_with_index { |domain_class, i|
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
- LESS_THAN = 1
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
- LESS_THAN => Proc.new { |d1, d2| d1 < d2 },
402
- LESS_THAN_OR_EQUAL => Proc.new { |d1, d2| d1 <= d2 },
403
- GREATER_THAN_OR_EQUAL => Proc.new { |d1, d2| d1 >= d2 },
404
- GREATER_THAN => Proc.new { |d1, d2| d1 > d2 }
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( [ AND, OR ].include?( args.last ) )
445
+ if( [ :and, :or ].include?( args.last ) )
439
446
  @compound_type = args.last
440
447
  args.pop
441
448
  else
442
- @compound_type = AND
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 == AND
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 == OR && @conditions.any? { |cond|
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 == AND and @conditions.any? { |cond|
477
+ @compound_type == :and and @conditions.any? { |cond|
471
478
  cond.implies? other_condition
472
479
  }
473
480
  ) or (
474
- @compound_type == OR and @conditions.all? { |cond|
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 == AND ? 'and' : 'or'
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
- super( methId, *args )
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, Like::POST_ONLY
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, Like::PRE_ONLY
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, OR )
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 ASC )
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 = PRE_AND_POST
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 = Query::Like::POST_ONLY
646
+ @matchType = :post_only
641
647
  $1
642
648
  elsif searchTerm.source =~ /(.*)\$$/
643
- @matchType = Query::Like::PRE_ONLY
649
+ @matchType = :pre_only
644
650
  $1
645
651
  else
646
- @matchType = Query::Like::PRE_AND_POST
652
+ @matchType = :pre_and_post
647
653
  searchTerm.source
648
654
  end
649
655
  end
650
656
 
651
657
  def regexp
652
- if @matchType == PRE_AND_POST
658
+ if @matchType == :pre_and_post
653
659
  Regexp.new( @searchTerm, Regexp::IGNORECASE )
654
- elsif @matchType == PRE_ONLY
660
+ elsif @matchType == :pre_only
655
661
  Regexp.new( @searchTerm.to_s + "$", Regexp::IGNORECASE )
656
- elsif @matchType == POST_ONLY
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 == PRE_AND_POST
668
+ withWildcards = @searchTerm.clone
669
+ if @matchType == :pre_and_post
664
670
  withWildcards = "%" + withWildcards + "%"
665
- elsif @matchType == PRE_ONLY
671
+ elsif @matchType == :pre_only
666
672
  withWildcards = "%" + withWildcards
667
- elsif @matchType == POST_ONLY
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
@@ -32,7 +32,7 @@
32
32
  # qry = Query.infer(
33
33
  # SKU,
34
34
  # :order_by => [ :standardPrice, :salePrice ],
35
- # :order_by_order => Query::DESC
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 = object_store.getInvoices { |inv|
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 Query::ASC or Query::DESC. Defaults
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 => Query::DESC
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 << CompoundCondition::OR
165
+ conditions << :or
167
166
  CompoundCondition.new( *conditions)
168
167
  end
169
168
 
170
- ASC = 1
171
- DESC = 2
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, pk_id_or_condition = nil, opts = {} ) #:nodoc:
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 pk_id_or_condition
180
- if pk_id_or_condition.is_a?( Condition )
181
- @condition = pk_id_or_condition
182
- else
183
- @condition = Query::Equals.new(
184
- :pk_id, pk_id_or_condition, domain_class
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 = ASC
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( CompoundCondition::AND, action ); end
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
- "limit #{ @limit.begin }, #{ @limit.end - @limit.begin + 1 }" if @limit
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( CompoundCondition::OR, action ); end
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 == ASC ? 'asc' : 'desc'
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 == Query::DESC
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
- table_names = concrete_classes.collect { |domain_class|
305
- domain_class.table_name
306
- }
307
- table_names.join( ', ' )
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
- concrete_classes.each_with_index { |domain_class, i|
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
- LESS_THAN = 1
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
- LESS_THAN => Proc.new { |d1, d2| d1 < d2 },
402
- LESS_THAN_OR_EQUAL => Proc.new { |d1, d2| d1 <= d2 },
403
- GREATER_THAN_OR_EQUAL => Proc.new { |d1, d2| d1 >= d2 },
404
- GREATER_THAN => Proc.new { |d1, d2| d1 > d2 }
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( [ AND, OR ].include?( args.last ) )
443
+ if( [ :and, :or ].include?( args.last ) )
439
444
  @compound_type = args.last
440
445
  args.pop
441
446
  else
442
- @compound_type = AND
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 == AND
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 == OR && @conditions.any? { |cond|
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 == AND and @conditions.any? { |cond|
475
+ @compound_type == :and and @conditions.any? { |cond|
471
476
  cond.implies? other_condition
472
477
  }
473
478
  ) or (
474
- @compound_type == OR and @conditions.all? { |cond|
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 == AND ? 'and' : 'or'
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
- super( methId, *args )
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, Like::POST_ONLY
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, Like::PRE_ONLY
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, OR )
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 ASC )
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 = PRE_AND_POST
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 = Query::Like::POST_ONLY
644
+ @matchType = :post_only
641
645
  $1
642
646
  elsif searchTerm.source =~ /(.*)\$$/
643
- @matchType = Query::Like::PRE_ONLY
647
+ @matchType = :pre_only
644
648
  $1
645
649
  else
646
- @matchType = Query::Like::PRE_AND_POST
650
+ @matchType = :pre_and_post
647
651
  searchTerm.source
648
652
  end
649
653
  end
650
654
 
651
655
  def regexp
652
- if @matchType == PRE_AND_POST
656
+ if @matchType == :pre_and_post
653
657
  Regexp.new( @searchTerm, Regexp::IGNORECASE )
654
- elsif @matchType == PRE_ONLY
658
+ elsif @matchType == :pre_only
655
659
  Regexp.new( @searchTerm.to_s + "$", Regexp::IGNORECASE )
656
- elsif @matchType == POST_ONLY
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 == PRE_AND_POST
666
+ withWildcards = @searchTerm.clone
667
+ if @matchType == :pre_and_post
664
668
  withWildcards = "%" + withWildcards + "%"
665
- elsif @matchType == PRE_ONLY
669
+ elsif @matchType == :pre_only
666
670
  withWildcards = "%" + withWildcards
667
- elsif @matchType == POST_ONLY
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