lafcadio 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -443,7 +443,7 @@ module Lafcadio
443
443
  def get_db_bridge; @dbBridge; end
444
444
 
445
445
  def get_field_name( domain_object )
446
- domain_object.domain_class.basename.decapitalize
446
+ domain_object.domain_class.basename.camel_case_to_underscore
447
447
  end
448
448
 
449
449
  def get_filtered(domain_class_name, searchTerm, fieldName = nil) #:nodoc:
@@ -0,0 +1,754 @@
1
+ require 'dbi'
2
+ require 'lafcadio/domain'
3
+ require 'lafcadio/query'
4
+ require 'lafcadio/util'
5
+
6
+ module Lafcadio
7
+ class Committer #:nodoc:
8
+ INSERT = 1
9
+ UPDATE = 2
10
+ DELETE = 3
11
+
12
+ attr_reader :commit_type, :db_object
13
+
14
+ def initialize(db_object, dbBridge)
15
+ @db_object = db_object
16
+ @dbBridge = dbBridge
17
+ @objectStore = ObjectStore.get_object_store
18
+ @commit_type = nil
19
+ end
20
+
21
+ def execute
22
+ @db_object.verify if LafcadioConfig.new()['checkFields'] == 'onCommit'
23
+ set_commit_type
24
+ @db_object.last_commit = get_last_commit
25
+ @db_object.pre_commit_trigger
26
+ update_dependent_domain_objects if @db_object.delete
27
+ @dbBridge.commit @db_object
28
+ unless @db_object.pk_id
29
+ @db_object.pk_id = @dbBridge.last_pk_id_inserted
30
+ end
31
+ @db_object.post_commit_trigger
32
+ end
33
+
34
+ def get_last_commit
35
+ if @db_object.delete
36
+ DomainObject::COMMIT_DELETE
37
+ elsif @db_object.pk_id
38
+ DomainObject::COMMIT_EDIT
39
+ else
40
+ DomainObject::COMMIT_ADD
41
+ end
42
+ end
43
+
44
+ def set_commit_type
45
+ if @db_object.delete
46
+ @commit_type = DELETE
47
+ elsif @db_object.pk_id
48
+ @commit_type = UPDATE
49
+ else
50
+ @commit_type = INSERT
51
+ end
52
+ end
53
+
54
+ def update_dependent_domain_objects
55
+ dependent_classes = @db_object.domain_class.dependent_classes
56
+ dependent_classes.keys.each { |aClass|
57
+ field = dependent_classes[aClass]
58
+ collection = @objectStore.get_filtered( aClass.name, @db_object,
59
+ field.name )
60
+ collection.each { |dependentObject|
61
+ if field.delete_cascade
62
+ dependentObject.delete = true
63
+ else
64
+ dependentObject.send( field.name + '=', nil )
65
+ end
66
+ @objectStore.commit(dependentObject)
67
+ }
68
+ }
69
+ end
70
+ end
71
+
72
+ class CouldntMatchDomainClassError < RuntimeError #:nodoc:
73
+ end
74
+
75
+ class DbBridge #:nodoc:
76
+ @@dbh = nil
77
+ @@last_pk_id_inserted = nil
78
+
79
+ def self._load(aString)
80
+ aString =~ /dbh:/
81
+ dbString = $'
82
+ begin
83
+ dbh = Marshal.load(dbString)
84
+ rescue TypeError
85
+ dbh = nil
86
+ end
87
+ new dbh
88
+ end
89
+
90
+ def initialize
91
+ @db_conn = DbConnection.get_db_connection
92
+ ObjectSpace.define_finalizer( self, proc { |id|
93
+ DbConnection.get_db_connection.disconnect
94
+ } )
95
+ end
96
+
97
+ def _dump(aDepth)
98
+ dbDump = @dbh.respond_to?( '_dump' ) ? @dbh._dump : @dbh.class.to_s
99
+ "dbh:#{dbDump}"
100
+ end
101
+
102
+ def commit(db_object)
103
+ sqlMaker = DomainObjectSqlMaker.new(db_object)
104
+ sqlMaker.sql_statements.each { |sql, binds| execute_commit( sql, binds ) }
105
+ if sqlMaker.sql_statements[0].first =~ /insert/
106
+ sql = 'select last_insert_id()'
107
+ result = execute_select( sql )
108
+ @@last_pk_id_inserted = result[0]['last_insert_id()'].to_i
109
+ end
110
+ end
111
+
112
+ def execute_commit( sql, binds ); @db_conn.do( sql, *binds ); end
113
+
114
+ def execute_select(sql)
115
+ maybe_log sql
116
+ begin
117
+ @db_conn.select_all( sql )
118
+ rescue DBI::DatabaseError => e
119
+ raise $!.to_s + ": #{ e.errstr }"
120
+ end
121
+ end
122
+
123
+ def get_collection_by_query(query)
124
+ domain_class = query.domain_class
125
+ execute_select( query.to_sql ).collect { |row_hash|
126
+ domain_class.new( SqlValueConverter.new( domain_class, row_hash ) )
127
+ }
128
+ end
129
+
130
+ def group_query( query )
131
+ execute_select( query.to_sql )[0].collect { |val|
132
+ if query.field_name != query.domain_class.sql_primary_key_name
133
+ a_field = query.domain_class.get_field( query.field_name )
134
+ a_field.value_from_sql( val )
135
+ else
136
+ val.to_i
137
+ end
138
+ }
139
+ end
140
+
141
+ def last_pk_id_inserted; @@last_pk_id_inserted; end
142
+
143
+ def maybe_log(sql)
144
+ config = LafcadioConfig.new
145
+ if config['logSql'] == 'y'
146
+ sqllog = Log4r::Logger['sql'] || Log4r::Logger.new( 'sql' )
147
+ filename = File.join( config['logdir'], config['sqlLogFile'] || 'sql' )
148
+ outputter = Log4r::FileOutputter.new( 'outputter',
149
+ { :filename => filename } )
150
+ sqllog.outputters = outputter
151
+ sqllog.info sql
152
+ end
153
+ end
154
+ end
155
+
156
+ class DbConnection < ContextualService
157
+ @@connectionClass = DBI
158
+ @@db_name = nil
159
+ @@dbh = nil
160
+
161
+ def self.flush
162
+ DbConnection.set_db_connection( nil )
163
+ @@dbh = nil
164
+ end
165
+
166
+ def self.set_connection_class( aClass ); @@connectionClass = aClass; end
167
+
168
+ def self.set_db_name( db_name ); @@db_name = db_name; end
169
+
170
+ def self.set_dbh( dbh ); @@dbh = dbh; end
171
+
172
+ def initialize
173
+ @@dbh = load_new_dbh if @@dbh.nil?
174
+ @dbh = @@dbh
175
+ end
176
+
177
+ def disconnect; @dbh.disconnect if @dbh; end
178
+
179
+ def load_new_dbh
180
+ config = LafcadioConfig.new
181
+ dbName = @@db_name || config['dbname']
182
+ dbAndHost = nil
183
+ if dbName && config['dbhost']
184
+ dbAndHost = "dbi:Mysql:#{ dbName }:#{ config['dbhost'] }"
185
+ else
186
+ dbAndHost = "dbi:#{config['dbconn']}"
187
+ end
188
+ @@dbh = @@connectionClass.connect( dbAndHost, config['dbuser'],
189
+ config['dbpassword'] )
190
+ end
191
+
192
+ def method_missing( symbol, *args )
193
+ @dbh.send( symbol, *args )
194
+ end
195
+ end
196
+
197
+ class DomainObjectInitError < RuntimeError #:nodoc:
198
+ attr_reader :messages
199
+
200
+ def initialize(messages)
201
+ @messages = messages
202
+ end
203
+ end
204
+
205
+ class DomainObjectNotFoundError < RuntimeError #:nodoc:
206
+ end
207
+
208
+ # The DomainObjectProxy is used when retrieving domain objects that are
209
+ # linked to other domain objects with LinkFields. In terms of +domain_class+
210
+ # and
211
+ # +pk_id+, a DomainObjectProxy instance looks to the outside world like the
212
+ # domain object it's supposed to represent. It only retrieves its domain
213
+ # object from the database when member data is requested.
214
+ #
215
+ # In normal usage you will probably never manipulate a DomainObjectProxy
216
+ # directly, but you may discover it by accident by calling
217
+ # DomainObjectProxy#class (or DomainObject#class) instead of
218
+ # DomainObjectProxy#domain_class (or DomainObjectProxy#domain_class).
219
+ class DomainObjectProxy
220
+ include DomainComparable
221
+
222
+ attr_accessor :domain_class, :pk_id
223
+
224
+ def initialize(domain_classOrDbObject, pk_id = nil)
225
+ if pk_id
226
+ @domain_class = domain_classOrDbObject
227
+ @pk_id = pk_id
228
+ elsif domain_classOrDbObject.class < DomainObject
229
+ @db_object = domain_classOrDbObject
230
+ @d_obj_retrieve_time = Time.now
231
+ @domain_class = @db_object.class
232
+ @pk_id = @db_object.pk_id
233
+ else
234
+ raise ArgumentError
235
+ end
236
+ @db_object = nil
237
+ end
238
+
239
+ def get_db_object
240
+ object_store = ObjectStore.get_object_store
241
+ if @db_object.nil? || needs_refresh?
242
+ @db_object = object_store.get( @domain_class, @pk_id )
243
+ @d_obj_retrieve_time = Time.now
244
+ end
245
+ @db_object
246
+ end
247
+
248
+ def hash
249
+ get_db_object.hash
250
+ end
251
+
252
+ def method_missing(methodId, *args)
253
+ get_db_object.send(methodId.id2name, *args)
254
+ end
255
+
256
+ def needs_refresh?
257
+ object_store = ObjectStore.get_object_store
258
+ last_commit_time = object_store.last_commit_time( @domain_class, @pk_id )
259
+ !last_commit_time.nil? && last_commit_time > @d_obj_retrieve_time
260
+ end
261
+
262
+ def to_s
263
+ get_db_object.to_s
264
+ end
265
+ end
266
+
267
+ class DomainObjectSqlMaker #:nodoc:
268
+ attr_reader :bind_values
269
+
270
+ def initialize(obj); @obj = obj; end
271
+
272
+ def delete_sql( domain_class )
273
+ "delete from #{ domain_class.table_name} " +
274
+ "where #{ domain_class.sql_primary_key_name }=#{ @obj.pk_id }"
275
+ end
276
+
277
+ def get_name_value_pairs( domain_class )
278
+ nameValues = []
279
+ domain_class.class_fields.each { |field|
280
+ unless field.instance_of?( PrimaryKeyField )
281
+ value = @obj.send(field.name)
282
+ unless field.db_will_automatically_write
283
+ nameValues << field.name_for_sql
284
+ nameValues <<(field.value_for_sql(value))
285
+ end
286
+ if field.bind_write?
287
+ @bind_values << value
288
+ end
289
+ end
290
+ }
291
+ QueueHash.new( *nameValues )
292
+ end
293
+
294
+ def insert_sql( domain_class )
295
+ fields = domain_class.class_fields
296
+ nameValuePairs = get_name_value_pairs( domain_class )
297
+ if domain_class.is_based_on?
298
+ nameValuePairs[domain_class.sql_primary_key_name] = 'LAST_INSERT_ID()'
299
+ end
300
+ fieldNameStr = nameValuePairs.keys.join ", "
301
+ fieldValueStr = nameValuePairs.values.join ", "
302
+ "insert into #{ domain_class.table_name}(#{fieldNameStr}) " +
303
+ "values(#{fieldValueStr})"
304
+ end
305
+
306
+ def sql_statements
307
+ statements = []
308
+ if @obj.error_messages.size > 0
309
+ raise DomainObjectInitError, @obj.error_messages, caller
310
+ end
311
+ @obj.class.self_and_concrete_superclasses.each { |domain_class|
312
+ statements << statement_bind_value_pair( domain_class )
313
+ }
314
+ statements.reverse
315
+ end
316
+
317
+ def statement_bind_value_pair( domain_class )
318
+ @bind_values = []
319
+ if @obj.pk_id == nil
320
+ statement = insert_sql( domain_class )
321
+ else
322
+ if @obj.delete
323
+ statement = delete_sql( domain_class )
324
+ else
325
+ statement = update_sql( domain_class)
326
+ end
327
+ end
328
+ [statement, @bind_values]
329
+ end
330
+
331
+ def update_sql( domain_class )
332
+ nameValueStrings = []
333
+ nameValuePairs = get_name_value_pairs( domain_class )
334
+ nameValuePairs.each { |key, value|
335
+ nameValueStrings << "#{key}=#{ value }"
336
+ }
337
+ allNameValues = nameValueStrings.join ', '
338
+ "update #{ domain_class.table_name} set #{allNameValues} " +
339
+ "where #{ domain_class.sql_primary_key_name}=#{@obj.pk_id}"
340
+ end
341
+ end
342
+
343
+ class FieldMatchError < StandardError; end
344
+
345
+ # The ObjectStore represents the database in a Lafcadio application.
346
+ #
347
+ # = Configuring the ObjectStore
348
+ # The ObjectStore depends on a few values being set correctly in the
349
+ # LafcadioConfig file:
350
+ # [dbuser] The database username.
351
+ # [dbpassword] The database password.
352
+ # [dbname] The database name.
353
+ # [dbhost] The database host.
354
+ #
355
+ # = Instantiating ObjectStore
356
+ # The ObjectStore is a ContextualService, meaning you can't get an instance by
357
+ # calling ObjectStore.new. Instead, you should call
358
+ # ObjectStore.get_object_store. (Using a ContextualService makes it easier to
359
+ # make out the ObjectStore for unit tests: See ContextualService for more.)
360
+ #
361
+ # = Dynamic method calls
362
+ # ObjectStore uses reflection to provide a lot of convenience methods for
363
+ # querying domain objects in a number of ways.
364
+ # [ObjectStore#get< domain class > (pk_id)]
365
+ # Retrieves one domain object by pk_id. For example,
366
+ # ObjectStore#getUser( 100 )
367
+ # will return User 100.
368
+ # [ObjectStore#get< domain class >s (searchTerm, fieldName = nil)]
369
+ # Returns a collection of all instances of that domain class matching that
370
+ # search term. For example,
371
+ # ObjectStore#getProducts( aProductCategory )
372
+ # queries MySQL for all products that belong to that product category. You
373
+ # can omit +fieldName+ if +searchTerm+ is a non-nil domain object, and the
374
+ # field connecting the first domain class to the second is named after the
375
+ # domain class. (For example, the above line assumes that Product has a
376
+ # field named "productCategory".) Otherwise, it's best to include
377
+ # +fieldName+:
378
+ # ObjectStore#getUsers( "Jones", "lastName" )
379
+ #
380
+ # = Querying
381
+ # ObjectStore can also be used to generate complex, ad-hoc queries which
382
+ # emulate much of the functionality you'd get from writing the SQL yourself.
383
+ # Furthermore, these queries can be run against in-memory data stores, which
384
+ # is particularly useful for tests.
385
+ # date = Date.new( 2003, 1, 1 )
386
+ # ObjectStore#getInvoices { |invoice|
387
+ # Query.And( invoice.date.gte( date ), invoice.rate.equals( 10 ),
388
+ # invoice.hours.equals( 10 ) )
389
+ # }
390
+ # is the same as
391
+ # select * from invoices
392
+ # where (date >= '2003-01-01' and rate = 10 and hours = 10)
393
+ # See lafcadio/query.rb for more.
394
+ #
395
+ # = SQL Logging
396
+ # Lafcadio uses log4r to log all of its SQL statements. The simplest way to
397
+ # turn on logging is to set the following values in the LafcadioConfig file:
398
+ # [logSql] Should be set to "y" to turn on logging.
399
+ # [logdir] The directory where log files should be written. Required if
400
+ # +logSql+ is "y"
401
+ # [sqlLogFile] The name of the file (not including its directory) where SQL
402
+ # should be logged. Default is "sql".
403
+ #
404
+ # = Triggers
405
+ # Domain classes can be set to fire triggers either before or after commits.
406
+ # Since these triggers are executed in Ruby, they're easy to test. See
407
+ # DomainObject#pre_commit_trigger and DomainObject#post_commit_trigger for more.
408
+ class ObjectStore < ContextualService
409
+ def self.set_db_name(dbName) #:nodoc:
410
+ DbConnection.set_db_name dbName
411
+ end
412
+
413
+ def initialize( dbBridge = nil ) #:nodoc:
414
+ @dbBridge = dbBridge == nil ? DbBridge.new : dbBridge
415
+ @cache = ObjectStore::Cache.new( @dbBridge )
416
+ end
417
+
418
+ # Commits a domain object to the database. You can also simply call
419
+ # myDomainObject.commit
420
+ def commit(db_object)
421
+ @cache.commit( db_object )
422
+ db_object
423
+ end
424
+
425
+ # Flushes one domain object from its cache.
426
+ def flush(db_object)
427
+ @cache.flush db_object
428
+ end
429
+
430
+ # Returns the domain object corresponding to the domain class and pk_id.
431
+ def get( domain_class, pk_id )
432
+ query = Query.new domain_class, pk_id
433
+ @cache.get_by_query( query )[0] ||
434
+ ( raise( DomainObjectNotFoundError,
435
+ "Can't find #{domain_class} #{pk_id}", caller ) )
436
+ end
437
+
438
+ # Returns all domain objects for the given domain class.
439
+ def get_all(domain_class); @cache.get_by_query( Query.new( domain_class ) ); end
440
+
441
+ # Returns the DbBridge; this is useful in case you need to use raw SQL for a
442
+ # specific query.
443
+ def get_db_bridge; @dbBridge; end
444
+
445
+ def get_field_name( domain_object )
446
+ domain_object.domain_class.basename.decapitalize
447
+ end
448
+
449
+ def get_filtered(domain_class_name, searchTerm, fieldName = nil) #:nodoc:
450
+ domain_class = DomainObject.get_domain_class_from_string(
451
+ domain_class_name
452
+ )
453
+ fieldName = get_field_name( searchTerm ) unless fieldName
454
+ get_subset( Query::Equals.new( fieldName, searchTerm, domain_class ) )
455
+ end
456
+
457
+ def get_map_match( domain_class, mapped ) #:nodoc:
458
+ Query::Equals.new( get_field_name( mapped ), mapped, domain_class )
459
+ end
460
+
461
+ def get_map_object( domain_class, map1, map2 ) #:nodoc:
462
+ unless map1 && map2
463
+ raise ArgumentError,
464
+ "ObjectStore#get_map_object needs two non-nil keys", caller
465
+ end
466
+ mapMatch1 = get_map_match domain_class, map1
467
+ mapMatch2 = get_map_match domain_class, map2
468
+ condition = Query::CompoundCondition.new mapMatch1, mapMatch2
469
+ get_subset(condition)[0]
470
+ end
471
+
472
+ def get_mapped(searchTerm, resultTypeName) #:nodoc:
473
+ resultType = DomainObject.get_domain_class_from_string resultTypeName
474
+ firstTypeName = searchTerm.class.basename
475
+ secondTypeName = resultType.basename
476
+ mapTypeName = firstTypeName + secondTypeName
477
+ get_filtered( mapTypeName, searchTerm ).collect { |mapObj|
478
+ mapObj.send( resultType.name.decapitalize )
479
+ }
480
+ end
481
+
482
+ # Retrieves the maximum value across all instances of one domain class.
483
+ # ObjectStore#get_max( Client )
484
+ # returns the highest +pk_id+ in the +clients+ table.
485
+ # ObjectStore#get_max( Invoice, "rate" )
486
+ # will return the highest rate for all invoices.
487
+ def get_max( domain_class, field_name = 'pk_id' )
488
+ @dbBridge.group_query( Query::Max.new( domain_class, field_name ) ).only
489
+ end
490
+
491
+ # Retrieves a collection of domain objects by +pk_id+.
492
+ # ObjectStore#get_objects( Clients, [ 1, 2, 3 ] )
493
+ def get_objects( domain_class, pk_ids )
494
+ if pk_ids.is_a?( Array ) && pk_ids.all? { |elt| elt.is_a?( Integer ) }
495
+ get_subset Query::In.new( 'pk_id', pk_ids, domain_class )
496
+ else
497
+ raise(
498
+ ArgumentError,
499
+ "ObjectStore#get_objects( domain_class, pk_ids ): pk_ids needs to " +
500
+ "be an array of integers",
501
+ caller
502
+ )
503
+ end
504
+ end
505
+
506
+ def get_subset(conditionOrQuery) #:nodoc:
507
+ if conditionOrQuery.class <= Query::Condition
508
+ condition = conditionOrQuery
509
+ query = Query.new condition.domain_class, condition
510
+ else
511
+ query = conditionOrQuery
512
+ end
513
+ @cache.get_by_query( query )
514
+ end
515
+
516
+ def last_commit_time( domain_class, pk_id ) #:nodoc:
517
+ @cache.last_commit_time( domain_class, pk_id )
518
+ end
519
+
520
+ def method_missing(methodId, *args) #:nodoc:
521
+ proc = block_given? ? ( proc { |obj| yield( obj ) } ) : nil
522
+ dispatch = MethodDispatch.new( methodId, proc, *args )
523
+ self.send( dispatch.symbol, *dispatch.args )
524
+ end
525
+
526
+ def respond_to?( symbol, include_private = false )
527
+ begin
528
+ dispatch = MethodDispatch.new( symbol )
529
+ rescue NoMethodError
530
+ super
531
+ end
532
+ end
533
+
534
+ class Cache #:nodoc:
535
+ def initialize( dbBridge )
536
+ @dbBridge = dbBridge
537
+ @objects = {}
538
+ @collections_by_query = {}
539
+ @commit_times = {}
540
+ end
541
+
542
+ def commit( db_object )
543
+ committer = Committer.new db_object, @dbBridge
544
+ committer.execute
545
+ update_after_commit( committer )
546
+ end
547
+
548
+ # Flushes a domain object.
549
+ def flush(db_object)
550
+ hash_by_domain_class( db_object.domain_class ).delete db_object.pk_id
551
+ flush_collection_cache( db_object.domain_class )
552
+ end
553
+
554
+ def flush_collection_cache( domain_class )
555
+ @collections_by_query.keys.each { |query|
556
+ if query.domain_class == domain_class
557
+ @collections_by_query.delete( query )
558
+ end
559
+ }
560
+ end
561
+
562
+ # Returns a cached domain object, or nil if none is found.
563
+ def get( domain_class, pk_id )
564
+ hash_by_domain_class( domain_class )[pk_id].clone
565
+ end
566
+
567
+ # Returns an array of all domain objects of a given type.
568
+ def get_all( domain_class )
569
+ hash_by_domain_class( domain_class ).values.collect { |d_obj|
570
+ d_obj.clone
571
+ }
572
+ end
573
+
574
+ def get_by_query( query )
575
+ unless @collections_by_query[query]
576
+ newObjects = @dbBridge.get_collection_by_query(query)
577
+ newObjects.each { |dbObj| save dbObj }
578
+ @collections_by_query[query] = newObjects.collect { |dobj|
579
+ dobj.pk_id
580
+ }
581
+ end
582
+ collection = []
583
+ @collections_by_query[query].each { |pk_id|
584
+ dobj = get( query.domain_class, pk_id )
585
+ collection << dobj if dobj
586
+ }
587
+ collection
588
+ end
589
+
590
+ def hash_by_domain_class( domain_class )
591
+ unless @objects[domain_class]
592
+ @objects[domain_class] = {}
593
+ end
594
+ @objects[domain_class]
595
+ end
596
+
597
+ def last_commit_time( domain_class, pk_id )
598
+ by_domain_class = @commit_times[domain_class]
599
+ by_domain_class ? by_domain_class[pk_id] : nil
600
+ end
601
+
602
+ def set_commit_time( d_obj )
603
+ by_domain_class = @commit_times[d_obj.domain_class]
604
+ if by_domain_class.nil?
605
+ by_domain_class = {}
606
+ @commit_times[d_obj.domain_class] = by_domain_class
607
+ end
608
+ by_domain_class[d_obj.pk_id] = Time.now
609
+ end
610
+
611
+ # Saves a domain object.
612
+ def save(db_object)
613
+ hash = hash_by_domain_class( db_object.domain_class )
614
+ hash[db_object.pk_id] = db_object
615
+ flush_collection_cache( db_object.domain_class )
616
+ end
617
+
618
+ def update_after_commit( committer ) #:nodoc:
619
+ if committer.commit_type == Committer::UPDATE ||
620
+ committer.commit_type == Committer::INSERT
621
+ save( committer.db_object )
622
+ elsif committer.commit_type == Committer::DELETE
623
+ flush( committer.db_object )
624
+ end
625
+ set_commit_time( committer.db_object )
626
+ end
627
+ end
628
+
629
+ class MethodDispatch #:nodoc:
630
+ attr_reader :symbol, :args
631
+
632
+ def initialize( orig_method, *other_args )
633
+ @orig_method = orig_method
634
+ @orig_args = other_args
635
+ if @orig_args.size > 0
636
+ @maybe_proc = @orig_args.shift
637
+ end
638
+ @methodName = orig_method.id2name
639
+ if @methodName =~ /^get(.*)$/
640
+ dispatch_get_method
641
+ else
642
+ raise_no_method_error
643
+ end
644
+ end
645
+
646
+ def dispatch_get_plural
647
+ if @orig_args.size == 0 && @maybe_proc.nil?
648
+ @symbol = :get_all
649
+ @args = [ @domain_class ]
650
+ else
651
+ searchTerm = @orig_args[0]
652
+ fieldName = @orig_args[1]
653
+ if searchTerm.nil? && @maybe_proc.nil? && fieldName.nil?
654
+ msg = "ObjectStore\##{ @orig_method } needs a field name as its " +
655
+ "second argument if its first argument is nil"
656
+ raise( ArgumentError, msg, caller )
657
+ end
658
+ dispatch_get_plural_by_query_block_or_search_term( searchTerm,
659
+ fieldName )
660
+ end
661
+ end
662
+
663
+ def dispatch_get_plural_by_query_block
664
+ inferrer = Query::Inferrer.new( @domain_class ) { |obj|
665
+ @maybe_proc.call( obj )
666
+ }
667
+ @symbol = :get_subset
668
+ @args = [ inferrer.execute ]
669
+ end
670
+
671
+ def dispatch_get_plural_by_query_block_or_search_term( searchTerm,
672
+ fieldName )
673
+ if !@maybe_proc.nil? && searchTerm.nil?
674
+ dispatch_get_plural_by_query_block
675
+ elsif @maybe_proc.nil? && ( !( searchTerm.nil? && fieldName.nil? ) )
676
+ @symbol = :get_filtered
677
+ @args = [ @domain_class.name, searchTerm, fieldName ]
678
+ else
679
+ raise( ArgumentError,
680
+ "Shouldn't send both a query block and a search term",
681
+ caller )
682
+ end
683
+ end
684
+
685
+ def dispatch_get_method
686
+ begin
687
+ dispatch_get_singular
688
+ rescue CouldntMatchDomainClassError
689
+ domain_class_name = English.singular(
690
+ camel_case_method_name_after_get
691
+ )
692
+ begin
693
+ @domain_class =
694
+ DomainObject.get_domain_class_from_string( domain_class_name )
695
+ dispatch_get_plural
696
+ rescue CouldntMatchDomainClassError
697
+ raise_no_method_error
698
+ end
699
+ end
700
+ end
701
+
702
+ def dispatch_get_singular
703
+ domain_class = DomainObject.get_domain_class_from_string(
704
+ camel_case_method_name_after_get
705
+ )
706
+ if @orig_args[0].class <= Integer
707
+ @symbol = :get
708
+ @args = [ domain_class, @orig_args[0] ]
709
+ elsif @orig_args[0].class <= DomainObject
710
+ @symbol = :get_map_object
711
+ @args = [ domain_class, @orig_args[0], @orig_args[1] ]
712
+ end
713
+ end
714
+
715
+ def camel_case_method_name_after_get
716
+ @orig_method.id2name =~ /^get(.*)$/
717
+ $1.underscore_to_camel_case
718
+ end
719
+
720
+ def raise_no_method_error
721
+ raise( NoMethodError, "undefined method '#{ @methodName }'", caller )
722
+ end
723
+ end
724
+ end
725
+
726
+ class SqlValueConverter #:nodoc:
727
+ attr_reader :domain_class, :row_hash
728
+
729
+ def initialize( domain_class, row_hash )
730
+ @domain_class = domain_class
731
+ @row_hash = row_hash
732
+ end
733
+
734
+ def []( key )
735
+ begin
736
+ field = @domain_class.get_field( key )
737
+ val = field.value_from_sql( @row_hash[ field.db_field_name ] )
738
+ if field.instance_of?( PrimaryKeyField ) && val.nil?
739
+ raise FieldMatchError, error_msg, caller
740
+ else
741
+ val
742
+ end
743
+ rescue MissingError
744
+ nil
745
+ end
746
+ end
747
+
748
+ def error_msg
749
+ "The field \"" + @domain_class.sql_primary_key_name +
750
+ "\" can\'t be found in the table \"" +
751
+ @domain_class.table_name + "\"."
752
+ end
753
+ end
754
+ end