lafcadio 0.8.3 → 0.9.0

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.
@@ -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: