lafcadio 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -76,18 +76,18 @@ module Lafcadio
76
76
  end
77
77
 
78
78
  class DbBridge #:nodoc:
79
- @@dbh = nil
80
79
  @@last_pk_id_inserted = nil
81
80
 
82
81
  def self._load(aString)
83
- aString =~ /dbh:/
84
- dbString = $'
82
+ aString =~ /db_conn:/
83
+ db_conn_str = $'
85
84
  begin
86
- dbh = Marshal.load(dbString)
85
+ db_conn = Marshal.load db_conn_str
87
86
  rescue TypeError
88
- dbh = nil
87
+ db_conn = nil
89
88
  end
90
- new dbh
89
+ DbConnection.set_db_connection db_conn
90
+ new
91
91
  end
92
92
 
93
93
  def initialize
@@ -131,10 +131,7 @@ module Lafcadio
131
131
  end
132
132
 
133
133
  def group_query( query )
134
- execute_select( query.to_sql )[0].collect { |val|
135
- a_field = query.domain_class.get_field( query.field_name )
136
- a_field.value_from_sql( val )
137
- }
134
+ execute_select( query.to_sql ).map { |row| query.result_row( row ) }
138
135
  end
139
136
 
140
137
  def last_pk_id_inserted; @@last_pk_id_inserted; end
@@ -150,6 +147,34 @@ module Lafcadio
150
147
  sqllog.info sql
151
148
  end
152
149
  end
150
+
151
+ def transaction( action )
152
+ tr = Transaction.new @db_conn
153
+ tr.commit
154
+ begin
155
+ action.call tr
156
+ tr.commit
157
+ rescue RollbackError
158
+ # rollback handled by Transaction
159
+ rescue
160
+ err_to_raise = $!
161
+ tr.rollback false
162
+ raise err_to_raise
163
+ end
164
+ end
165
+
166
+ class Transaction
167
+ def initialize( db_conn ); @db_conn = db_conn; end
168
+
169
+ def commit; @db_conn.commit; end
170
+
171
+ def rollback( raise_error = true )
172
+ @db_conn.rollback
173
+ raise RollbackError if raise_error
174
+ end
175
+ end
176
+
177
+ class RollbackError < StandardError; end
153
178
  end
154
179
 
155
180
  class DbConnection < ContextualService::Service
@@ -186,6 +211,8 @@ module Lafcadio
186
211
  end
187
212
  @@dbh = @@connectionClass.connect( dbAndHost, config['dbuser'],
188
213
  config['dbpassword'] )
214
+ @@dbh['AutoCommit'] = false
215
+ @@dbh
189
216
  end
190
217
 
191
218
  def method_missing( symbol, *args )
@@ -280,7 +307,7 @@ module Lafcadio
280
307
  value = @obj.send(field.name)
281
308
  unless field.db_will_automatically_write
282
309
  nameValues << field.name_for_sql
283
- nameValues << field.value_for_sql(value)
310
+ nameValues <<(field.value_for_sql(value))
284
311
  end
285
312
  if field.bind_write?
286
313
  @bind_values << value
@@ -486,7 +513,8 @@ module Lafcadio
486
513
  # ObjectStore#get_max( Invoice, "rate" )
487
514
  # will return the highest rate for all invoices.
488
515
  def get_max( domain_class, field_name = 'pk_id' )
489
- @dbBridge.group_query( Query::Max.new( domain_class, field_name ) ).only
516
+ qry = Query::Max.new( domain_class, field_name )
517
+ @dbBridge.group_query( qry ).only[:max]
490
518
  end
491
519
 
492
520
  # Retrieves a collection of domain objects by +pk_id+.
@@ -527,6 +555,8 @@ module Lafcadio
527
555
  def mock? #:nodoc:
528
556
  false
529
557
  end
558
+
559
+ def query( query ); @dbBridge.group_query( query ); end
530
560
 
531
561
  def respond_to?( symbol, include_private = false )
532
562
  begin
@@ -536,6 +566,8 @@ module Lafcadio
536
566
  end
537
567
  end
538
568
 
569
+ def transaction( &action ); @dbBridge.transaction( action ); end
570
+
539
571
  class Cache #:nodoc:
540
572
  def initialize( dbBridge )
541
573
  @dbBridge = dbBridge
@@ -582,21 +614,11 @@ module Lafcadio
582
614
  query.implies?( other_query )
583
615
  }
584
616
  if pk_ids
585
- collection = ( pk_ids.collect { |pk_id|
617
+ @collections_by_query[query] = ( pk_ids.collect { |pk_id|
586
618
  get( query.domain_class, pk_id )
587
619
  } ).select { |dobj| query.object_meets( dobj ) }.collect { |dobj|
588
620
  dobj.pk_id
589
621
  }
590
- collection = collection[query.limit] if query.limit
591
- if ( order_by = query.order_by )
592
- collection = collection.sort_by { |pk_id|
593
- get( query.domain_class, pk_id ).send( order_by )
594
- }
595
- collection.reverse! if query.order_by_order == Query::DESC
596
- else
597
- collection = collection.sort
598
- end
599
- @collections_by_query[query] = collection
600
622
  elsif @collections_by_query.values
601
623
  newObjects = @dbBridge.get_collection_by_query(query)
602
624
  newObjects.each { |dbObj| save dbObj }
@@ -747,18 +769,23 @@ module Lafcadio
747
769
  end
748
770
  end
749
771
 
750
- class SqlValueConverter #:nodoc:
772
+ class SqlValueConverter < Hash #:nodoc:
751
773
  attr_reader :domain_class, :row_hash
752
774
 
753
775
  def initialize( domain_class, row_hash )
776
+ super()
754
777
  @domain_class = domain_class
755
- @row_hash = row_hash
778
+ row_hash.each do |key, value| self[key] = value; end
756
779
  end
757
780
 
758
781
  def []( key )
782
+ puts key.inspect
759
783
  begin
760
784
  field = @domain_class.get_field( key )
761
- val = field.value_from_sql( @row_hash[ field.db_field_name ] )
785
+ sql_value = super field.db_field_name
786
+ puts sql_value.inspect
787
+ val = field.value_from_sql sql_value
788
+ puts val.inspect
762
789
  if field.instance_of?( PrimaryKeyField ) && val.nil?
763
790
  raise FieldMatchError, error_msg, caller
764
791
  else
@@ -768,6 +795,10 @@ module Lafcadio
768
795
  nil
769
796
  end
770
797
  end
798
+
799
+ def each( &action )
800
+ super do |k, v| puts "#{ k } #{ v }"; action.call( k, self[k] ); end
801
+ end
771
802
 
772
803
  def error_msg
773
804
  "The field \"" + @domain_class.sql_primary_key_name +
@@ -90,10 +90,8 @@ module Lafcadio
90
90
  class Query
91
91
  def self.And( *conditions ); CompoundCondition.new( *conditions ); end
92
92
 
93
- def self.infer( domain_class, &action )
94
- inferrer = Query::Inferrer.new( domain_class ) { |obj|
95
- action.call( obj )
96
- }
93
+ def self.infer( *args, &action )
94
+ inferrer = Query::Inferrer.new( *args ) { |obj| action.call( obj ) }
97
95
  inferrer.execute
98
96
  end
99
97
 
@@ -108,15 +106,16 @@ module Lafcadio
108
106
  attr_reader :domain_class, :condition
109
107
  attr_accessor :order_by, :order_by_order, :limit
110
108
 
111
- def initialize(domain_class, pk_idOrCondition = nil)
112
- @domain_class = domain_class
109
+ def initialize(domain_class, pk_id_or_condition = nil, opts = {} )
110
+ @domain_class, @opts = domain_class, opts
113
111
  ( @condition, @order_by, @limit ) = [ nil, nil, nil ]
114
- if pk_idOrCondition
115
- if pk_idOrCondition.class <= Condition
116
- @condition = pk_idOrCondition
112
+ if pk_id_or_condition
113
+ if pk_id_or_condition.class <= Condition
114
+ @condition = pk_id_or_condition
117
115
  else
118
- @condition = Query::Equals.new( 'pk_id', pk_idOrCondition,
119
- domain_class )
116
+ @condition = Query::Equals.new(
117
+ 'pk_id', pk_id_or_condition, domain_class
118
+ )
120
119
  end
121
120
  end
122
121
  @order_by_order = ASC
@@ -124,20 +123,24 @@ module Lafcadio
124
123
 
125
124
  def and( &action ); compound( CompoundCondition::AND, action ); end
126
125
 
126
+ def collect( coll )
127
+ if @opts[:group_functions] == [:count]
128
+ [ result_row( [coll.size] ) ]
129
+ else
130
+ raise
131
+ end
132
+ end
133
+
127
134
  def compound( comp_type, action )
128
135
  rquery = Query.infer( @domain_class ) { |dobj| action.call( dobj ) }
129
136
  comp_cond = Query::CompoundCondition.new( @condition, rquery.condition,
130
137
  comp_type )
131
- q = comp_cond.query
132
- [ :order_by, :order_by_order, :limit ].each do |attr|
133
- q.send( attr.to_s + '=', self.send( attr ) )
134
- end
135
- q
138
+ comp_cond.query
136
139
  end
137
140
 
138
141
  def eql?( other ); other.class <= Query && other.to_sql == to_sql; end
139
142
 
140
- def fields; '*'; end
143
+ def fields; @opts[:group_functions] == [:count] ? 'count(*)' : '*'; end
141
144
 
142
145
  def hash; to_sql.hash; end
143
146
 
@@ -151,8 +154,14 @@ module Lafcadio
151
154
 
152
155
  def order_clause
153
156
  if @order_by
154
- order_by_field = @domain_class.get_field( @order_by )
155
- clause = "order by #{ order_by_field.db_field_name } "
157
+ if @order_by.is_a? Array
158
+ field_str = @order_by.map { |f_name|
159
+ @domain_class.get_field( f_name.to_s ).db_field_name
160
+ }.join( ', ' )
161
+ else
162
+ field_str = @domain_class.get_field( @order_by ).db_field_name
163
+ end
164
+ clause = "order by #{ field_str } "
156
165
  clause += @order_by_order == ASC ? 'asc' : 'desc'
157
166
  clause
158
167
  end
@@ -169,6 +178,14 @@ module Lafcadio
169
178
  end
170
179
  end
171
180
  end
181
+
182
+ def result_row( row )
183
+ if @opts[:group_functions] == [:count]
184
+ { :count => row.first }
185
+ else
186
+ raise
187
+ end
188
+ end
172
189
 
173
190
  def sql_primary_key_field(domain_class)
174
191
  "#{ domain_class.table_name }.#{ domain_class.sql_primary_key_name }"
@@ -226,6 +243,10 @@ module Lafcadio
226
243
  end
227
244
  end
228
245
 
246
+ def |( other_cond ); Query.Or( self, other_cond ); end
247
+
248
+ def &( other_cond ); Query.And( self, other_cond ); end
249
+
229
250
  def implies?( other_condition )
230
251
  self.eql?( other_condition ) or (
231
252
  other_condition.respond_to?( :implied_by? ) and
@@ -462,14 +483,22 @@ module Lafcadio
462
483
  end
463
484
 
464
485
  class Inferrer #:nodoc:
465
- def initialize( domain_class, &action )
466
- @domain_class = domain_class; @action = action
486
+ def initialize( *args, &action )
487
+ @domain_class = args.first; @action = action
488
+ unless args.size == 1
489
+ h = args.last
490
+ @order_by = h[:order_by]
491
+ @order_by_order = h[:order_by_order]
492
+ end
467
493
  end
468
494
 
469
495
  def execute
470
496
  impostor = DomainObjectImpostor.impostor( @domain_class )
471
497
  condition = @action.call( impostor ).to_condition
472
498
  query = Query.new( @domain_class, condition )
499
+ query.order_by = @order_by
500
+ query.order_by_order = @order_by_order
501
+ query
473
502
  end
474
503
  end
475
504
 
@@ -532,12 +561,14 @@ module Lafcadio
532
561
  a_value = d_obj.send( @field_name )
533
562
  ( max.nil? || a_value > max ) ? a_value : max
534
563
  }
535
- [ max ]
564
+ [ result_row( [max] ) ]
536
565
  end
537
566
 
538
567
  def fields
539
568
  "max(#{ @domain_class.get_field( @field_name ).db_field_name })"
540
569
  end
570
+
571
+ def result_row( row ); { :max => row.first }; end
541
572
  end
542
573
 
543
574
  class Not < Condition #:nodoc:
@@ -586,6 +617,10 @@ module Lafcadio
586
617
  end
587
618
  end
588
619
 
620
+ def |( condition ); Query.Or( to_condition, condition ); end
621
+
622
+ def &( condition ); Query.And( to_condition, condition ); end
623
+
589
624
  def register_compare_condition( compareStr, searchTerm)
590
625
  compareVal = ObjectFieldImpostor.comparators[compareStr]
591
626
  Compare.new( @field_name, searchTerm,
@@ -90,10 +90,8 @@ module Lafcadio
90
90
  class Query
91
91
  def self.And( *conditions ); CompoundCondition.new( *conditions ); end
92
92
 
93
- def self.infer( domain_class, &action )
94
- inferrer = Query::Inferrer.new( domain_class ) { |obj|
95
- action.call( obj )
96
- }
93
+ def self.infer( *args, &action )
94
+ inferrer = Query::Inferrer.new( *args ) { |obj| action.call( obj ) }
97
95
  inferrer.execute
98
96
  end
99
97
 
@@ -108,15 +106,16 @@ module Lafcadio
108
106
  attr_reader :domain_class, :condition
109
107
  attr_accessor :order_by, :order_by_order, :limit
110
108
 
111
- def initialize(domain_class, pk_idOrCondition = nil)
112
- @domain_class = domain_class
109
+ def initialize(domain_class, pk_id_or_condition = nil, opts = {} )
110
+ @domain_class, @opts = domain_class, opts
113
111
  ( @condition, @order_by, @limit ) = [ nil, nil, nil ]
114
- if pk_idOrCondition
115
- if pk_idOrCondition.class <= Condition
116
- @condition = pk_idOrCondition
112
+ if pk_id_or_condition
113
+ if pk_id_or_condition.class <= Condition
114
+ @condition = pk_id_or_condition
117
115
  else
118
- @condition = Query::Equals.new( 'pk_id', pk_idOrCondition,
119
- domain_class )
116
+ @condition = Query::Equals.new(
117
+ 'pk_id', pk_id_or_condition, domain_class
118
+ )
120
119
  end
121
120
  end
122
121
  @order_by_order = ASC
@@ -124,6 +123,14 @@ module Lafcadio
124
123
 
125
124
  def and( &action ); compound( CompoundCondition::AND, action ); end
126
125
 
126
+ def collect( coll )
127
+ if @opts[:group_functions] == [:count]
128
+ [ result_row( [coll.size] ) ]
129
+ else
130
+ raise
131
+ end
132
+ end
133
+
127
134
  def compound( comp_type, action )
128
135
  rquery = Query.infer( @domain_class ) { |dobj| action.call( dobj ) }
129
136
  comp_cond = Query::CompoundCondition.new( @condition, rquery.condition,
@@ -133,7 +140,7 @@ module Lafcadio
133
140
 
134
141
  def eql?( other ); other.class <= Query && other.to_sql == to_sql; end
135
142
 
136
- def fields; '*'; end
143
+ def fields; @opts[:group_functions] == [:count] ? 'count(*)' : '*'; end
137
144
 
138
145
  def hash; to_sql.hash; end
139
146
 
@@ -147,8 +154,14 @@ module Lafcadio
147
154
 
148
155
  def order_clause
149
156
  if @order_by
150
- order_by_field = @domain_class.get_field( @order_by )
151
- clause = "order by #{ order_by_field.db_field_name } "
157
+ if @order_by.is_a? Array
158
+ field_str = @order_by.map { |f_name|
159
+ @domain_class.get_field( f_name.to_s ).db_field_name
160
+ }.join( ', ' )
161
+ else
162
+ field_str = @domain_class.get_field( @order_by ).db_field_name
163
+ end
164
+ clause = "order by #{ field_str } "
152
165
  clause += @order_by_order == ASC ? 'asc' : 'desc'
153
166
  clause
154
167
  end
@@ -165,6 +178,14 @@ module Lafcadio
165
178
  end
166
179
  end
167
180
  end
181
+
182
+ def result_row( row )
183
+ if @opts[:group_functions] == [:count]
184
+ { :count => row.first }
185
+ else
186
+ raise
187
+ end
188
+ end
168
189
 
169
190
  def sql_primary_key_field(domain_class)
170
191
  "#{ domain_class.table_name }.#{ domain_class.sql_primary_key_name }"
@@ -458,14 +479,22 @@ module Lafcadio
458
479
  end
459
480
 
460
481
  class Inferrer #:nodoc:
461
- def initialize( domain_class, &action )
462
- @domain_class = domain_class; @action = action
482
+ def initialize( *args, &action )
483
+ @domain_class = args.first; @action = action
484
+ unless args.size == 1
485
+ h = args.last
486
+ @order_by = h[:order_by]
487
+ @order_by_order = h[:order_by_order]
488
+ end
463
489
  end
464
490
 
465
491
  def execute
466
492
  impostor = DomainObjectImpostor.impostor( @domain_class )
467
493
  condition = @action.call( impostor ).to_condition
468
494
  query = Query.new( @domain_class, condition )
495
+ query.order_by = @order_by
496
+ query.order_by_order = @order_by_order
497
+ query
469
498
  end
470
499
  end
471
500
 
@@ -528,12 +557,14 @@ module Lafcadio
528
557
  a_value = d_obj.send( @field_name )
529
558
  ( max.nil? || a_value > max ) ? a_value : max
530
559
  }
531
- [ max ]
560
+ [ result_row( [max] ) ]
532
561
  end
533
562
 
534
563
  def fields
535
564
  "max(#{ @domain_class.get_field( @field_name ).db_field_name })"
536
565
  end
566
+
567
+ def result_row( row ); { :max => row.first }; end
537
568
  end
538
569
 
539
570
  class Not < Condition #:nodoc: