lafcadio 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.9.4"
19
+ Version = "0.9.5"
20
20
 
21
21
  require 'lafcadio/depend'
22
22
  require 'lafcadio/domain'
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.9.3"
19
+ Version = "0.9.4"
20
20
 
21
21
  require 'lafcadio/depend'
22
22
  require 'lafcadio/domain'
@@ -2,115 +2,127 @@ require 'lafcadio/objectStore'
2
2
  require 'lafcadio/util'
3
3
 
4
4
  module Lafcadio
5
- class MockDbBridge #:nodoc:
6
- attr_reader :last_pk_id_inserted
7
-
8
- def initialize
9
- @objects = {}
10
- @next_pk_ids = {}
11
- @queries = []
12
- @transaction = nil
13
- end
14
-
15
- def all( domain_class )
16
- @objects[domain_class] ? @objects[domain_class].values : []
17
- end
18
-
19
- def commit(db_object)
20
- if db_object.pk_id and !db_object.pk_id.is_a?( Integer )
21
- raise ArgumentError
5
+ class ObjectStore
6
+ class MockDbBridge #:nodoc:
7
+ attr_reader :last_pk_id_inserted
8
+ attr_accessor :objects, :next_pk_ids, :queries
9
+
10
+ def initialize
11
+ @objects = {}
12
+ @next_pk_ids = {}
13
+ @queries = []
14
+ @transaction = nil
22
15
  end
23
- if @transaction
24
- @transaction << db_object
25
- else
26
- objects_by_domain_class = objects_by_domain_class db_object.domain_class
27
- if db_object.delete
28
- objects_by_domain_class.delete( db_object.pk_id )
16
+
17
+ def all( domain_class )
18
+ @objects[domain_class] ? @objects[domain_class].values : []
19
+ end
20
+
21
+ def commit(db_object)
22
+ if db_object.pk_id and !db_object.pk_id.is_a?( Integer )
23
+ raise ArgumentError
24
+ end
25
+ if @transaction
26
+ @transaction << db_object
29
27
  else
30
- object_pk_id = pre_commit_pk_id( db_object )
31
- objects_by_domain_class[object_pk_id] = db_object
28
+ objects_by_domain_class = objects_by_domain_class(
29
+ db_object.domain_class
30
+ )
31
+ if db_object.delete
32
+ objects_by_domain_class.delete( db_object.pk_id )
33
+ else
34
+ object_pk_id = pre_commit_pk_id( db_object )
35
+ objects_by_domain_class[object_pk_id] = db_object
36
+ end
32
37
  end
33
38
  end
34
- end
35
-
36
- def group_query( query )
37
- query.collect objects_by_domain_class( query.domain_class ).values
38
- end
39
-
40
- def next_pk_id( domain_class )
41
- dobjs = objects_by_domain_class( domain_class ).values
42
- dobjs.inject( 0 ) { |memo, obj| memo > obj.pk_id ? memo : obj.pk_id } + 1
43
- end
44
-
45
- def objects_by_domain_class( domain_class )
46
- @objects[domain_class] = {} unless @objects[domain_class]
47
- @objects[domain_class]
48
- end
49
-
50
- def pre_commit_pk_id( domain_object )
51
- @next_pk_ids = {} unless @next_pk_ids
52
- if (next_pk_id = @next_pk_ids[domain_object.domain_class])
53
- @next_pk_ids[domain_object.domain_class] = nil
54
- @last_pk_id_inserted = next_pk_id
55
- elsif domain_object.pk_id
56
- domain_object.pk_id
57
- elsif ( next_pk_id = @next_pk_ids[domain_object.domain_class] )
58
- @last_pk_id_inserted = next_pk_id
59
- @next_pk_ids[domain_object.domain_class] = nil
60
- next_pk_id
61
- else
62
- pk_ids = objects_by_domain_class( domain_object.domain_class ).keys
63
- @last_pk_id_inserted = pk_ids.max ? pk_ids.max + 1 : 1
39
+
40
+ def group_query( query )
41
+ query.collect objects_by_domain_class( query.domain_class ).values
64
42
  end
65
- end
66
-
67
- def queries( domain_class = nil )
68
- @queries.select { |qry|
69
- domain_class ? qry.domain_class == domain_class : true
70
- }
71
- end
72
-
73
- def query_count( sql )
74
- @queries.select { |qry| qry.to_sql == sql }.size
75
- end
76
-
77
- def select_dobjs(query)
78
- @queries << query
79
- domain_class = query.domain_class
80
- objects = []
81
- all( domain_class ).each { |dbObj|
82
- objects << dbObj if query.dobj_satisfies?( dbObj )
83
- }
84
- query.order_and_limit_collection objects
85
- end
86
-
87
- def set_next_pk_id( domain_class, npi )
88
- @next_pk_ids = {} unless @next_pk_ids
89
- @next_pk_ids[ domain_class ] = npi
90
- end
91
-
92
- def transaction( action )
93
- tr = MockDbBridge::Transaction.new
94
- @transaction = tr
95
- begin
43
+
44
+ def next_pk_id( domain_class )
45
+ dobjs = objects_by_domain_class( domain_class ).values
46
+ dobjs.inject( 0 ) { |memo, obj|
47
+ memo > obj.pk_id ? memo : obj.pk_id
48
+ } + 1
49
+ end
50
+
51
+ def objects_by_domain_class( domain_class )
52
+ @objects[domain_class] = {} unless @objects[domain_class]
53
+ @objects[domain_class]
54
+ end
55
+
56
+ def pre_commit_pk_id( domain_object )
57
+ @next_pk_ids = {} unless @next_pk_ids
58
+ if (next_pk_id = @next_pk_ids[domain_object.domain_class])
59
+ @next_pk_ids[domain_object.domain_class] = nil
60
+ @last_pk_id_inserted = next_pk_id
61
+ elsif domain_object.pk_id
62
+ domain_object.pk_id
63
+ else
64
+ pk_ids = objects_by_domain_class( domain_object.domain_class ).keys
65
+ @last_pk_id_inserted = pk_ids.max ? pk_ids.max + 1 : 1
66
+ end
67
+ end
68
+
69
+ def queries( domain_class = nil )
70
+ @queries.select { |qry|
71
+ domain_class ? qry.domain_class == domain_class : true
72
+ }
73
+ end
74
+
75
+ def query_count( sql )
76
+ @queries.select { |qry| qry.to_sql == sql }.size
77
+ end
78
+
79
+ def rollback; end
80
+
81
+ def select_dobjs( query )
82
+ @queries << query
83
+ domain_class = query.domain_class
84
+ all = all domain_class
85
+ if @transaction
86
+ from_tr = @transaction.select { |dobj|
87
+ dobj.domain_class == query.domain_class
88
+ }
89
+ all = all.concat from_tr
90
+ end
91
+ objects = all.select { |dobj| query.dobj_satisfies?( dobj ) }
92
+ query.order_and_limit_collection objects
93
+ end
94
+
95
+ def set_next_pk_id( domain_class, npi )
96
+ @next_pk_ids = {} unless @next_pk_ids
97
+ @next_pk_ids[ domain_class ] = npi
98
+ end
99
+
100
+ def transaction( action )
101
+ tr = MockDbBridge::Transaction.new
102
+ @transaction = tr
96
103
  action.call tr
97
104
  @transaction = nil
105
+ tr.uniq!
98
106
  tr.each do |dobj_to_commit| commit( dobj_to_commit ); end
99
- rescue RollbackError; end
100
- end
101
-
102
- class Transaction < Array
103
- def rollback; raise RollbackError; end
104
- end
105
-
106
- class RollbackError < StandardError #:nodoc:
107
+ end
108
+
109
+ def transactional_clone
110
+ tc = clone
111
+ tc.objects = objects.clone
112
+ tc.next_pk_ids = next_pk_ids.clone
113
+ tc.queries = queries.clone
114
+ tc
115
+ end
116
+
117
+ class Transaction < Array
118
+ def rollback; raise RollbackError; end
119
+ end
107
120
  end
108
121
  end
109
122
 
110
123
  # Externally, the MockObjectStore looks and acts exactly like the ObjectStore,
111
124
  # but stores all its data in memory. This makes it very useful for unit
112
- # testing, and in fact LafcadioTestCase#setup creates a new instance of
113
- # MockObjectStore for each test case. For example:
125
+ # testing. For example:
114
126
  #
115
127
  # class SomeTestCase < Test::Unit::TestCase
116
128
  # def setup
@@ -2,115 +2,130 @@ require 'lafcadio/objectStore'
2
2
  require 'lafcadio/util'
3
3
 
4
4
  module Lafcadio
5
- class MockDbBridge #:nodoc:
6
- attr_reader :last_pk_id_inserted
7
-
8
- def initialize
9
- @objects = {}
10
- @next_pk_ids = {}
11
- @queries = []
12
- @transaction = nil
13
- end
14
-
15
- def all( domain_class )
16
- @objects[domain_class] ? @objects[domain_class].values : []
17
- end
18
-
19
- def select_dobjs(query)
20
- @queries << query
21
- domain_class = query.domain_class
22
- objects = []
23
- all( domain_class ).each { |dbObj|
24
- objects << dbObj if query.dobj_satisfies?( dbObj )
25
- }
26
- query.order_and_limit_collection objects
27
- end
28
-
29
- def commit(db_object)
30
- if db_object.pk_id and !db_object.pk_id.is_a?( Integer )
31
- raise ArgumentError
5
+ class ObjectStore
6
+ class MockDbBridge #:nodoc:
7
+ attr_reader :last_pk_id_inserted
8
+ attr_accessor :objects, :next_pk_ids, :queries
9
+
10
+ def initialize
11
+ @objects = {}
12
+ @next_pk_ids = {}
13
+ @queries = []
14
+ @transaction = nil
15
+ end
16
+
17
+ def all( domain_class )
18
+ @objects[domain_class] ? @objects[domain_class].values : []
32
19
  end
33
- if @transaction
34
- @transaction << db_object
35
- else
36
- objects_by_domain_class = objects_by_domain_class db_object.domain_class
37
- if db_object.delete
38
- objects_by_domain_class.delete( db_object.pk_id )
20
+
21
+ def commit(db_object)
22
+ if db_object.pk_id and !db_object.pk_id.is_a?( Integer )
23
+ raise ArgumentError
24
+ end
25
+ if @transaction
26
+ @transaction << db_object
39
27
  else
40
- object_pk_id = pre_commit_pk_id( db_object )
41
- objects_by_domain_class[object_pk_id] = db_object
28
+ objects_by_domain_class = objects_by_domain_class(
29
+ db_object.domain_class
30
+ )
31
+ if db_object.delete
32
+ objects_by_domain_class.delete( db_object.pk_id )
33
+ else
34
+ object_pk_id = pre_commit_pk_id( db_object )
35
+ objects_by_domain_class[object_pk_id] = db_object
36
+ end
42
37
  end
43
38
  end
44
- end
45
-
46
- def group_query( query )
47
- query.collect objects_by_domain_class( query.domain_class ).values
48
- end
49
-
50
- def next_pk_id( domain_class )
51
- dobjs = objects_by_domain_class( domain_class ).values
52
- dobjs.inject( 0 ) { |memo, obj| memo > obj.pk_id ? memo : obj.pk_id } + 1
53
- end
54
-
55
- def objects_by_domain_class( domain_class )
56
- @objects[domain_class] = {} unless @objects[domain_class]
57
- @objects[domain_class]
58
- end
59
-
60
- def pre_commit_pk_id( domain_object )
61
- @next_pk_ids = {} unless @next_pk_ids
62
- if (next_pk_id = @next_pk_ids[domain_object.domain_class])
63
- @next_pk_ids[domain_object.domain_class] = nil
64
- @last_pk_id_inserted = next_pk_id
65
- elsif domain_object.pk_id
66
- domain_object.pk_id
67
- elsif ( next_pk_id = @next_pk_ids[domain_object.domain_class] )
68
- @last_pk_id_inserted = next_pk_id
69
- @next_pk_ids[domain_object.domain_class] = nil
70
- next_pk_id
71
- else
72
- pk_ids = objects_by_domain_class( domain_object.domain_class ).keys
73
- @last_pk_id_inserted = pk_ids.max ? pk_ids.max + 1 : 1
39
+
40
+ def group_query( query )
41
+ query.collect objects_by_domain_class( query.domain_class ).values
74
42
  end
75
- end
76
-
77
- def queries( domain_class = nil )
78
- @queries.select { |qry|
79
- domain_class ? qry.domain_class == domain_class : true
80
- }
81
- end
82
-
83
- def query_count( sql )
84
- @queries.select { |qry| qry.to_sql == sql }.size
85
- end
86
-
87
- def set_next_pk_id( domain_class, npi )
88
- @next_pk_ids = {} unless @next_pk_ids
89
- @next_pk_ids[ domain_class ] = npi
90
- end
91
-
92
- def transaction( action )
93
- tr = MockDbBridge::Transaction.new
94
- @transaction = tr
95
- begin
43
+
44
+ def next_pk_id( domain_class )
45
+ dobjs = objects_by_domain_class( domain_class ).values
46
+ dobjs.inject( 0 ) { |memo, obj|
47
+ memo > obj.pk_id ? memo : obj.pk_id
48
+ } + 1
49
+ end
50
+
51
+ def objects_by_domain_class( domain_class )
52
+ @objects[domain_class] = {} unless @objects[domain_class]
53
+ @objects[domain_class]
54
+ end
55
+
56
+ def pre_commit_pk_id( domain_object )
57
+ @next_pk_ids = {} unless @next_pk_ids
58
+ if (next_pk_id = @next_pk_ids[domain_object.domain_class])
59
+ @next_pk_ids[domain_object.domain_class] = nil
60
+ @last_pk_id_inserted = next_pk_id
61
+ elsif domain_object.pk_id
62
+ domain_object.pk_id
63
+ elsif ( next_pk_id = @next_pk_ids[domain_object.domain_class] )
64
+ @last_pk_id_inserted = next_pk_id
65
+ @next_pk_ids[domain_object.domain_class] = nil
66
+ next_pk_id
67
+ else
68
+ pk_ids = objects_by_domain_class( domain_object.domain_class ).keys
69
+ @last_pk_id_inserted = pk_ids.max ? pk_ids.max + 1 : 1
70
+ end
71
+ end
72
+
73
+ def queries( domain_class = nil )
74
+ @queries.select { |qry|
75
+ domain_class ? qry.domain_class == domain_class : true
76
+ }
77
+ end
78
+
79
+ def query_count( sql )
80
+ @queries.select { |qry| qry.to_sql == sql }.size
81
+ end
82
+
83
+ def rollback; end
84
+
85
+ def select_dobjs( query )
86
+ @queries << query
87
+ domain_class = query.domain_class
88
+ all = all domain_class
89
+ if @transaction
90
+ from_tr = @transaction.select { |dobj|
91
+ dobj.domain_class == query.domain_class
92
+ }
93
+ all = all.concat from_tr
94
+ end
95
+ objects = all.select { |dobj| query.dobj_satisfies?( dobj ) }
96
+ query.order_and_limit_collection objects
97
+ end
98
+
99
+ def set_next_pk_id( domain_class, npi )
100
+ @next_pk_ids = {} unless @next_pk_ids
101
+ @next_pk_ids[ domain_class ] = npi
102
+ end
103
+
104
+ def transaction( action )
105
+ tr = MockDbBridge::Transaction.new
106
+ @transaction = tr
96
107
  action.call tr
97
108
  @transaction = nil
98
109
  tr.each do |dobj_to_commit| commit( dobj_to_commit ); end
99
- rescue RollbackError; end
100
- end
101
-
102
- class Transaction < Array
103
- def rollback; raise RollbackError; end
104
- end
105
-
106
- class RollbackError < StandardError #:nodoc:
110
+ end
111
+
112
+ def transactional_clone
113
+ tc = clone
114
+ tc.objects = objects.clone
115
+ tc.next_pk_ids = next_pk_ids.clone
116
+ tc.queries = queries.clone
117
+ tc
118
+ end
119
+
120
+ class Transaction < Array
121
+ def rollback; raise RollbackError; end
122
+ end
107
123
  end
108
124
  end
109
125
 
110
126
  # Externally, the MockObjectStore looks and acts exactly like the ObjectStore,
111
127
  # but stores all its data in memory. This makes it very useful for unit
112
- # testing, and in fact LafcadioTestCase#setup creates a new instance of
113
- # MockObjectStore for each test case. For example:
128
+ # testing. For example:
114
129
  #
115
130
  # class SomeTestCase < Test::Unit::TestCase
116
131
  # def setup
@@ -265,12 +265,24 @@ module Lafcadio
265
265
  # Client.new( 'name' => 'Big Co.' ).commit
266
266
  # tr.rollback
267
267
  # end # the client will not be saved to the DB
268
- def transaction( &action ); @cache.transaction( action ); end
268
+ def transaction( &action )
269
+ old_cache = @cache
270
+ @cache = @cache.transactional_clone
271
+ begin
272
+ @cache.transaction action
273
+ rescue
274
+ err_to_raise = $!
275
+ @cache.rollback unless $!.is_a? RollbackError
276
+ @cache = old_cache
277
+ raise err_to_raise unless $!.is_a? RollbackError
278
+ end
279
+ end
269
280
 
270
281
  class Cache #:nodoc:
271
282
  include MonitorMixin
272
283
 
273
- attr_reader :db_bridge
284
+ attr_reader :db_bridge
285
+ attr_accessor :domain_class_caches
274
286
 
275
287
  def initialize( db_bridge = DbBridge.new )
276
288
  super()
@@ -315,7 +327,9 @@ module Lafcadio
315
327
  if !collected and main_cache.queries.values
316
328
  newObjects = @db_bridge.select_dobjs query
317
329
  newObjects.each { |dbObj| main_cache.save dbObj }
318
- main_cache.queries[query] = newObjects.collect { |dobj| dobj.pk_id }
330
+ main_cache.queries[query] = newObjects.collect { |dobj|
331
+ dobj.pk_id
332
+ }
319
333
  end
320
334
  end
321
335
  main_cache.queries[query].map { |pk_id| main_cache[pk_id] }.compact
@@ -333,17 +347,29 @@ module Lafcadio
333
347
 
334
348
  def method_missing( meth, *args )
335
349
  simple_dispatch = [
336
- :queries, :save, :set_commit_time, :update_after_commit
350
+ :flush, :queries, :save, :set_commit_time, :update_after_commit
337
351
  ]
338
352
  if simple_dispatch.include?( meth )
339
353
  cache( args.first.domain_class ).send( meth, *args )
340
354
  elsif [ :[], :last_commit_time ].include?( meth )
341
355
  cache( args.first ).send( meth, *args[1..-1] )
342
- elsif [ :group_query, :transaction ].include?( meth )
356
+ elsif [ :group_query, :rollback, :transaction ].include?( meth )
343
357
  @db_bridge.send( meth, *args )
358
+ else
359
+ super
344
360
  end
345
361
  end
346
362
 
363
+ def transactional_clone
364
+ tc = Cache.new @db_bridge.transactional_clone
365
+ dcc_clones = {}
366
+ @domain_class_caches.each do |domain_class, dcc|
367
+ dcc_clones[domain_class] = dcc.transactional_clone
368
+ end
369
+ tc.domain_class_caches = dcc_clones
370
+ tc
371
+ end
372
+
347
373
  def update_dependent_domain_class( db_object, aClass, field )
348
374
  object_store = ObjectStore.get_object_store
349
375
  collection = aClass.get( db_object, field.name )
@@ -367,7 +393,8 @@ module Lafcadio
367
393
  end
368
394
 
369
395
  class DomainClassCache < Hash #:nodoc:
370
- attr_reader :commit_times, :domain_class, :queries
396
+ attr_reader :domain_class
397
+ attr_accessor :commit_times, :queries
371
398
 
372
399
  def initialize( domain_class, db_bridge )
373
400
  super()
@@ -423,6 +450,13 @@ module Lafcadio
423
450
  end
424
451
 
425
452
  def set_commit_time( d_obj ); commit_times[d_obj.pk_id] = Time.now; end
453
+
454
+ def transactional_clone
455
+ tc = clone
456
+ tc.commit_times = commit_times.clone
457
+ tc.queries = queries.clone
458
+ tc
459
+ end
426
460
 
427
461
  def update_after_commit( db_object ) #:nodoc:
428
462
  if [ :update, :insert ].include?(
@@ -547,9 +581,6 @@ module Lafcadio
547
581
  def initialize
548
582
  @db_conn = DbConnection.get_db_connection
549
583
  @transaction = nil
550
- ObjectSpace.define_finalizer( self, proc { |id|
551
- DbConnection.get_db_connection.disconnect
552
- } )
553
584
  end
554
585
 
555
586
  def _dump(aDepth)
@@ -610,6 +641,8 @@ module Lafcadio
610
641
  end
611
642
  end
612
643
 
644
+ def rollback; @transaction.rollback( false ) if @transaction; end
645
+
613
646
  def select_all(sql)
614
647
  maybe_log sql
615
648
  begin
@@ -646,8 +679,6 @@ module Lafcadio
646
679
  begin
647
680
  action.call @transaction
648
681
  @transaction.commit
649
- rescue RollbackError
650
- # rollback handled by Transaction
651
682
  rescue
652
683
  err_to_raise = $!
653
684
  @transaction.rollback false
@@ -656,6 +687,8 @@ module Lafcadio
656
687
  @transaction = nil
657
688
  end
658
689
 
690
+ def transactional_clone; clone; end
691
+
659
692
  class Transaction #:nodoc:
660
693
  def initialize( db_conn ); @db_conn = db_conn; end
661
694
 
@@ -666,9 +699,6 @@ module Lafcadio
666
699
  raise RollbackError if raise_error
667
700
  end
668
701
  end
669
-
670
- class RollbackError < StandardError #:nodoc:
671
- end
672
702
  end
673
703
 
674
704
  class DbConnection < ContextualService::Service #:nodoc:
@@ -679,7 +709,10 @@ module Lafcadio
679
709
 
680
710
  def self.db_name=( db_name ); @@db_name = db_name; end
681
711
 
682
- def initialize; @dbh = load_new_dbh; end
712
+ def initialize
713
+ @dbh = load_new_dbh
714
+ ObjectSpace.define_finalizer( self, proc { |id| disconnect } )
715
+ end
683
716
 
684
717
  def disconnect; @dbh.disconnect; end
685
718
 
@@ -812,6 +845,9 @@ module Lafcadio
812
845
  )
813
846
  end
814
847
  end
848
+
849
+ class RollbackError < StandardError #:nodoc:
850
+ end
815
851
 
816
852
  class SqlToRubyValues #:nodoc:
817
853
  attr_reader :domain_class, :row_hash
@@ -265,12 +265,20 @@ module Lafcadio
265
265
  # Client.new( 'name' => 'Big Co.' ).commit
266
266
  # tr.rollback
267
267
  # end # the client will not be saved to the DB
268
- def transaction( &action ); @cache.transaction( action ); end
268
+ def transaction( &action )
269
+ transactional_cache = @cache.transactional_clone
270
+ begin
271
+ transactional_cache.transaction action
272
+ @cache = transactional_cache
273
+ rescue RollbackError
274
+ end
275
+ end
269
276
 
270
277
  class Cache #:nodoc:
271
278
  include MonitorMixin
272
279
 
273
- attr_reader :db_bridge
280
+ attr_reader :db_bridge
281
+ attr_accessor :domain_class_caches
274
282
 
275
283
  def initialize( db_bridge = DbBridge.new )
276
284
  super()
@@ -305,17 +313,27 @@ module Lafcadio
305
313
  end
306
314
 
307
315
  def get_by_query( query )
316
+ puts "get_by_query #{ query.to_sql }"
317
+ puts 1
308
318
  main_cache = cache query.domain_class
309
319
  unless main_cache.queries[query]
320
+ puts 2
310
321
  if query.one_pk_id?
311
322
  collected = false
323
+ puts 3
312
324
  else
313
325
  collected = main_cache.collect_from_superset query
326
+ puts 4
314
327
  end
315
328
  if !collected and main_cache.queries.values
329
+ puts 5
316
330
  newObjects = @db_bridge.select_dobjs query
317
331
  newObjects.each { |dbObj| main_cache.save dbObj }
318
- main_cache.queries[query] = newObjects.collect { |dobj| dobj.pk_id }
332
+ puts 6
333
+ main_cache.queries[query] = newObjects.collect { |dobj|
334
+ dobj.pk_id
335
+ }
336
+ puts 8
319
337
  end
320
338
  end
321
339
  main_cache.queries[query].map { |pk_id| main_cache[pk_id] }.compact
@@ -341,8 +359,20 @@ module Lafcadio
341
359
  cache( args.first ).send( meth, *args[1..-1] )
342
360
  elsif [ :group_query, :transaction ].include?( meth )
343
361
  @db_bridge.send( meth, *args )
362
+ else
363
+ super
344
364
  end
345
365
  end
366
+
367
+ def transactional_clone
368
+ tc = Cache.new @db_bridge.transactional_clone
369
+ dcc_clones = {}
370
+ @domain_class_caches.each do |dcc|
371
+ dcc_clones[dcc.domain_class] = dcc.transactional_clone
372
+ end
373
+ tc.domain_class_caches = dcc_clones
374
+ tc
375
+ end
346
376
 
347
377
  def update_dependent_domain_class( db_object, aClass, field )
348
378
  object_store = ObjectStore.get_object_store
@@ -367,7 +397,8 @@ module Lafcadio
367
397
  end
368
398
 
369
399
  class DomainClassCache < Hash #:nodoc:
370
- attr_reader :commit_times, :domain_class, :queries
400
+ attr_reader :domain_class
401
+ attr_accessor :commit_times, :queries
371
402
 
372
403
  def initialize( domain_class, db_bridge )
373
404
  super()
@@ -423,6 +454,13 @@ module Lafcadio
423
454
  end
424
455
 
425
456
  def set_commit_time( d_obj ); commit_times[d_obj.pk_id] = Time.now; end
457
+
458
+ def transactional_clone
459
+ tc = clone
460
+ tc.commit_times = commit_times.clone
461
+ tc.queries = queries.clone
462
+ tc
463
+ end
426
464
 
427
465
  def update_after_commit( db_object ) #:nodoc:
428
466
  if [ :update, :insert ].include?(
@@ -562,6 +600,7 @@ module Lafcadio
562
600
  db_object
563
601
  )
564
602
  statements_and_binds.each do |sql, binds|
603
+ maybe_log sql
565
604
  @db_conn.do( sql, *binds )
566
605
  end
567
606
  if statements_and_binds[0].first =~ /insert/
@@ -596,13 +635,15 @@ module Lafcadio
596
635
  config = LafcadioConfig.new
597
636
  if config['logSql'] == 'y'
598
637
  sqllog = Log4r::Logger['sql'] || Log4r::Logger.new( 'sql' )
599
- filename = File.join(
600
- config['logdir'], config['sqlLogFile'] || 'sql'
601
- )
602
- outputter = Log4r::FileOutputter.new(
603
- 'outputter', { :filename => filename }
604
- )
605
- sqllog.outputters = outputter
638
+ if sqllog.outputters.empty?
639
+ filename = File.join(
640
+ config['logdir'], config['sqlLogFile'] || 'sql'
641
+ )
642
+ outputter = Log4r::FileOutputter.new(
643
+ 'outputter', { :filename => filename }
644
+ )
645
+ sqllog.outputters = outputter
646
+ end
606
647
  sqllog.info sql
607
648
  end
608
649
  end
@@ -643,8 +684,6 @@ module Lafcadio
643
684
  begin
644
685
  action.call @transaction
645
686
  @transaction.commit
646
- rescue RollbackError
647
- # rollback handled by Transaction
648
687
  rescue
649
688
  err_to_raise = $!
650
689
  @transaction.rollback false
@@ -653,6 +692,8 @@ module Lafcadio
653
692
  @transaction = nil
654
693
  end
655
694
 
695
+ def transactional_clone; clone; end
696
+
656
697
  class Transaction #:nodoc:
657
698
  def initialize( db_conn ); @db_conn = db_conn; end
658
699
 
@@ -663,9 +704,6 @@ module Lafcadio
663
704
  raise RollbackError if raise_error
664
705
  end
665
706
  end
666
-
667
- class RollbackError < StandardError #:nodoc:
668
- end
669
707
  end
670
708
 
671
709
  class DbConnection < ContextualService::Service #:nodoc:
@@ -809,6 +847,9 @@ module Lafcadio
809
847
  )
810
848
  end
811
849
  end
850
+
851
+ class RollbackError < StandardError #:nodoc:
852
+ end
812
853
 
813
854
  class SqlToRubyValues #:nodoc:
814
855
  attr_reader :domain_class, :row_hash
@@ -1,39 +1,6 @@
1
1
  require 'lafcadio/depend'
2
2
  require 'lafcadio/mock'
3
3
  require 'lafcadio/util'
4
- require 'test/unit'
5
-
6
- # A test case that sets up a number of mock services. In writing an application
7
- # that uses Lafcadio you may find it convenient to inherit from this class.
8
- class LafcadioTestCase < Test::Unit::TestCase
9
- include Lafcadio
10
-
11
- def setup
12
- context = ContextualService::Context.instance
13
- context.flush
14
- @mockObjectStore = MockObjectStore.new
15
- ObjectStore.set_object_store @mockObjectStore
16
- LafcadioConfig.set_values(
17
- 'classDefinitionDir' => '../test/testData', 'dbhost' => 'localhost',
18
- 'dbname' => 'test', 'dbpassword' => 'password', 'dbuser' => 'test',
19
- 'domainFiles' => %w( ../test/mock/domain ),
20
- 'logdir' => '../test/testOutput/', 'logSql' => 'n'
21
- )
22
- end
23
-
24
- # Asserts that for each key-value pair in +att_values+, sending the key to
25
- # +object+ will return the value.
26
- # u = User.new( 'fname' => 'Francis', 'lname' => 'Hwang' )
27
- # assert_attributes( u, { 'fname' => 'Francis', 'lname' => 'Hwang' } )
28
- def assert_attributes( object, att_values )
29
- att_values.each { |method, expected|
30
- assert_equal( expected, object.send( method ), method.to_s )
31
- }
32
- end
33
-
34
- def default_test #:nodoc:
35
- end
36
- end
37
4
 
38
5
  module Lafcadio
39
6
  def BooleanField.mock_value #:nodoc:
@@ -48,9 +48,35 @@ module Lafcadio
48
48
  Time.now
49
49
  end
50
50
 
51
- module DomainMock #:nodoc:
52
- Version = '0.1.0'
53
-
51
+ # A convenience module for test-cases of Lafcadio-dependent applications.
52
+ # Include this module in a test-case, and you automatically get the
53
+ # class-level method <tt>setup_mock_dobjs</tt>. This calls
54
+ # DomainObject.default_mock, and assigns the result to an instance variable
55
+ # named after the domain class. Note that if your test case also defines a
56
+ # <tt>setup</tt>, you should make sure to call <tt>super</tt> in that setup
57
+ # method to make <tt>setup_mock_dobjs</tt> work.
58
+ #
59
+ # class User < Lafcadio::DomainObject
60
+ # strings :fname, :lname, :email
61
+ # end
62
+ #
63
+ # class TestSendMessage < Test::Unit::TestCase
64
+ # include Lafcadio::DomainMock
65
+ # setup_mock_dobjs User
66
+ # def test_send_to_self
67
+ # SendMessage.new( 'sender' => @user, 'recipient' => @user )
68
+ # assert_equal( 1, Message.all.size )
69
+ # end
70
+ # end
71
+ #
72
+ # <tt>setup_mock_dobjs</tt> can handle plural domain classes:
73
+ #
74
+ # setup_mock_dobjs User, Message
75
+ #
76
+ # It can also handle assignments to different instance variables:
77
+ #
78
+ # setup_mock_dobjs User, '@sender'
79
+ module DomainMock
54
80
  def self.included( includer )
55
81
  def includer.setup_mock_dobjs( *domain_classes_or_symbol_names )
56
82
  domain_classes = DomainClassSymbolMapper.new
@@ -158,7 +184,7 @@ module Lafcadio
158
184
  # <tt>lafcadio/test.rb</tt>.
159
185
  #
160
186
  # class User < Lafcadio::DomainObject
161
- # string :fname, :lname, :email
187
+ # strings :fname, :lname, :email
162
188
  # end
163
189
  # u1 = User.custom_mock
164
190
  # u1.fname # => 'test text'
@@ -225,7 +251,7 @@ module Lafcadio
225
251
  # <tt>lafcadio/test.rb</tt>.
226
252
  #
227
253
  # class User < Lafcadio::DomainObject
228
- # string :fname, :lname, :email
254
+ # strings :fname, :lname, :email
229
255
  # end
230
256
  # u1 = User.default_mock
231
257
  # u1.fname # => 'test text'
@@ -269,7 +295,7 @@ module Lafcadio
269
295
  # <tt>lafcadio/test.rb</tt>.
270
296
  #
271
297
  # class User < Lafcadio::DomainObject
272
- # string :fname, :lname, :email
298
+ # strings :fname, :lname, :email
273
299
  # end
274
300
  # User.mock_value :fname, 'Bill'
275
301
  # User.mock_value :lname, 'Smith'
@@ -289,7 +315,7 @@ module Lafcadio
289
315
  # <tt>lafcadio/test.rb</tt>.
290
316
  #
291
317
  # class User < Lafcadio::DomainObject
292
- # string :fname, :lname, :email
318
+ # strings :fname, :lname, :email
293
319
  # end
294
320
  # User.mock_values { :fname => 'Bill', :lname => 'Smith' }
295
321
  # u1 = User.default_mock
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: lafcadio
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.9.4
7
- date: 2006-05-01 00:00:00 -04:00
6
+ version: 0.9.5
7
+ date: 2006-07-03 00:00:00 -04:00
8
8
  summary: Lafcadio is an object-relational mapping layer
9
9
  require_paths:
10
10
  - lib