lafcadio 0.9.0 → 0.9.1

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