caruby-core 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,7 +13,7 @@ module CaRuby
13
13
  def initialize
14
14
  super
15
15
  # the query template builder
16
- @srch_tmpl_bldr = TemplateBuilder.new
16
+ @rdr_tmpl_bldr = TemplateBuilder.new
17
17
  # the fetch result matcher
18
18
  @matcher = FetchedMatcher.new
19
19
  # the fetched copier
@@ -25,7 +25,7 @@ module CaRuby
25
25
  # visitor that merges the fetched object graph
26
26
  @ftchd_mrg_vstr = Jinx::MergeVisitor.new(:matcher => @matcher, :copier => copier) { |ref| ref.class.fetched_domain_attributes }
27
27
  end
28
-
28
+
29
29
  # Returns an array of objects matching the specified query template and attribute path.
30
30
  # The obj_or_hql argument is either a domain object template or a
31
31
  # Hibernate[http://www.hibernate.org/docs.html] HQL statement. If obj_or_hql
@@ -57,36 +57,46 @@ module CaRuby
57
57
  # enable change tracking and lazy-loading
58
58
  persistify(result)
59
59
  end
60
-
61
- # Fetches the given domain object from the database.
62
- # Only secondary key attributes are used in the match.
63
- # If no secondary key is defined for the object's class, then this method returns nil.
64
- # The {#query} method is used to fetch records on non-secondary key attributes.
60
+
61
+ # Fetches the given target domain object from the database. The target
62
+ # domain object class {Propertied#searchable_attributes} are used in the match.
63
+ # If this domain object does not have a set of searchable attributes with
64
+ # non-nil values, then this method returns nil.
65
65
  #
66
66
  # If the :create option is set, then this method creates an object if the
67
67
  # find is unsuccessful.
68
68
  #
69
- # @param [Jinx::Resource] obj the domain object to find
69
+ # If the fetched domain object is the same class as the target domain object,
70
+ # then the fetched content is merged into the target and this method returns
71
+ # the target. If the fetched domain object is a subclass of the target domain
72
+ # object, then the fetched domain object is returned.
73
+ #
74
+ # @param [Jinx::Resource] obj the target domain object to find
70
75
  # @param [Hash, Symbol] opts the find options
71
76
  # @option opts [Boolean] :create whether to create the object if it is not found
72
77
  # @return [Jinx::Resource, nil] the domain object if found, nil otherwise
73
78
  # @raise [DatabaseError] if obj is not a domain object or more than object
74
79
  # matches the obj attribute values
80
+ # @see #query
75
81
  def find(obj, opts=nil)
76
82
  return if obj.nil?
77
83
  perform(:find, obj) do
78
- if find_object(obj) then
84
+ fetched = find_object(obj)
85
+ if fetched.nil? then
86
+ logger.info { "#{obj.qp} not found." }
87
+ create(obj) if Options.get(:create, opts)
88
+ elsif fetched.equal?(obj) then
79
89
  logger.info { "Found #{obj}." }
80
90
  obj
81
91
  else
82
- logger.info { "#{obj.qp} not found." }
83
- if Options.get(:create, opts) then create(obj) end
92
+ logger.info { "Found #{fetched} matching the find template #{obj}." }
93
+ fetched
84
94
  end
85
95
  end
86
96
  end
87
-
88
- # Returns whether the given domain object has a database identifier or exists in the database.
89
- # This method fetches the object from the database if necessary.
97
+
98
+ # Returns whether the given domain object has a database identifier or exists
99
+ # in the database. This method fetches the object from the database if necessary.
90
100
  #
91
101
  # @param [Jinx::Resource, <Jinx::Resource>] obj the domain object(s) to find
92
102
  # @return [Boolean] whether the domain object(s) exist in the database
@@ -96,14 +106,14 @@ module CaRuby
96
106
  elsif obj.collection? then
97
107
  obj.all? { |item| exists?(item) }
98
108
  else
99
- obj.identifier or find(obj)
109
+ !!obj.identifier or find(obj).equal?(obj)
100
110
  end
101
111
  end
102
112
 
103
113
  private
104
-
114
+
105
115
  RESULT_PRINTER = PrintWrapper.new { |obj| obj.pp_s }
106
-
116
+
107
117
  # Queries the given obj_or_hql as described in {#query} and makes a detoxified copy of the
108
118
  # toxic caCORE search result.
109
119
  #
@@ -121,7 +131,7 @@ module CaRuby
121
131
  # detoxify the toxic caCORE result
122
132
  detoxify(toxic)
123
133
  end
124
-
134
+
125
135
  # Queries the given obj_or_hql as described in {#query} and returns the toxic caCORE search result.
126
136
  #
127
137
  # @param (see #query)
@@ -131,7 +141,7 @@ module CaRuby
131
141
  path_s = path.join('.') unless path.empty?
132
142
  # guard against recursive call back into the same operation
133
143
  if query_redundant?(obj_or_hql, path_s) then
134
- Jinx.fail(DatabaseError, "Query #{obj_or_hql.qp} #{path_s} recursively called in context #{print_operations}")
144
+ raise DatabaseError.new("Query #{obj_or_hql.qp} #{path_s} recursively called in context #{print_operations}")
135
145
  end
136
146
  # perform the query
137
147
  perform(:query, obj_or_hql, :attribute => path_s) { query_with_path(obj_or_hql, path) }
@@ -140,11 +150,11 @@ module CaRuby
140
150
  def query_redundant?(obj_or_hql, path)
141
151
  @operations.detect { |op| op.type == :query and query_subject_redundant?(op.subject, obj_or_hql) and op.attribute == path }
142
152
  end
143
-
153
+
144
154
  def query_subject_redundant?(s1, s2)
145
155
  s1 == s2 or (Jinx::Resource === s1 and Jinx::Resource === s2 and s1.identifier and s1.identifier == s2.identifier)
146
156
  end
147
-
157
+
148
158
  # @return an array of objects matching the given query template and path
149
159
  # @see #query
150
160
  def query_with_path(obj_or_hql, path)
@@ -154,14 +164,14 @@ module CaRuby
154
164
  # gather the results of querying on those penultimate result objects with the last
155
165
  # attribute as the path
156
166
  unless path.empty? then
157
- if attribute.nil? then Jinx.fail(DatabaseError, "Query path includes empty attribute: #{path.join('.')}.nil") end
167
+ if attribute.nil? then raise DatabaseError.new("Query path includes empty attribute: #{path.join('.')}.nil") end
158
168
  logger.debug { "Decomposing query on #{obj_or_hql} with path #{path.join('.')}.#{attribute} into query on #{path.join('.')} followed by #{attribute}..." }
159
169
  return query_safe(obj_or_hql, *path).map { |parent| query_toxic(parent, attribute) }.flatten
160
170
  end
161
171
  # perform the attribute query
162
172
  query_with_attribute(obj_or_hql, attribute)
163
173
  end
164
-
174
+
165
175
  # Returns an array of objects matching the given query template and optional attribute.
166
176
  # @see #query
167
177
  def query_with_attribute(obj_or_hql, attribute=nil)
@@ -195,20 +205,20 @@ module CaRuby
195
205
  def merge_fetched(source, target)
196
206
  @ftchd_mrg_vstr.visit(source, target) { |src, tgt| tgt.copy_volatile_attributes(src) }
197
207
  end
198
-
208
+
199
209
  def print_query_result(result)
200
210
  count_s = 'result object'.quantify(result.size)
201
211
  result_printer = result.wrap { |item| RESULT_PRINTER.wrap(item) }
202
212
  "Persistence service query returned #{count_s}: #{result_printer.pp_s(:single_line)}"
203
213
  end
204
-
214
+
205
215
  def query_hql(hql)
206
216
  java_name = hql[/from\s+(\S+)/i, 1]
207
- Jinx.fail(DatabaseError, "Could not determine target type from HQL: #{hql}") if java_name.nil?
217
+ raise DatabaseError.new("Could not determine target type from HQL: #{hql}") if java_name.nil?
208
218
  tgt = Class.to_ruby(java_name)
209
219
  persistence_service(tgt).query(hql)
210
220
  end
211
-
221
+
212
222
  # Returns an array of objects fetched from the database which matches
213
223
  # a template and follows the given optional domain attribute, if present.
214
224
  #
@@ -228,12 +238,12 @@ module CaRuby
228
238
  elsif invertible_query?(obj, attribute) then
229
239
  query_with_inverted_reference(obj, attribute)
230
240
  else
231
- tmpl = @srch_tmpl_bldr.build_template(obj)
241
+ tmpl = @rdr_tmpl_bldr.build_template(obj)
232
242
  return Array::EMPTY_ARRAY if tmpl.nil?
233
243
  query_on_template(tmpl, attribute)
234
244
  end
235
245
  end
236
-
246
+
237
247
  # Returns an array of objects fetched from the database which matches
238
248
  # the given template and follows the given optional domain attribute.
239
249
  def query_on_template(template, attribute=nil)
@@ -241,7 +251,7 @@ module CaRuby
241
251
  svc = persistence_service(tgt)
242
252
  attribute ? svc.query(template, attribute) : svc.query(template)
243
253
  end
244
-
254
+
245
255
  # Queries on the given template and attribute by issuing a HQL query with an identifier condition.
246
256
  #
247
257
  # @param (see #query_object)
@@ -252,17 +262,17 @@ module CaRuby
252
262
  sa = source[/([[:alnum:]])[[:alnum:]]*$/, 1].downcase
253
263
  # the HQL condition
254
264
  hql = "from #{source} #{sa} where #{sa}.id = #{obj.identifier}"
255
-
265
+
256
266
  # the join attribute property
257
267
  if attribute then
258
268
  pd = obj.class.property(attribute).property_descriptor
259
269
  hql.insert(0, "select #{sa}.#{pd.name} ")
260
270
  end
261
271
  logger.debug { "Querying on #{obj} #{attribute} using HQL identifier criterion..." }
262
-
272
+
263
273
  query_hql(hql)
264
274
  end
265
-
275
+
266
276
  # Returns whether the query specified by the given search object and attribute can be
267
277
  # inverted as a query on a template of type attribute which references the object.
268
278
  # This condition holds if the search object has a key and attribute is a non-abstract
@@ -277,10 +287,11 @@ module CaRuby
277
287
  inv_prop = pa.inverse_property
278
288
  inv_prop and inv_prop.searchable? and finder_parameters(obj)
279
289
  end
280
-
281
- # Queries the given query object attribute by querying an attribute type template which references obj.
290
+
291
+ # Queries the given query object attribute by querying an attribute type template which references
292
+ # the target object.
282
293
  #
283
- # @quirk caCORE caCORE caCORE search enters an infinite loop when the search argument has an object
294
+ # @quirk caCORE caCORE search enters an infinite loop when the search argument has an object
284
295
  # reference graph cycle. Work-around is to ensure that reference integrity is broken in the search
285
296
  # argument by not setting inverse attributes.
286
297
  #
@@ -297,14 +308,14 @@ module CaRuby
297
308
  # The Java property writer to set the tmpl inverse to ref.
298
309
  # Use the property writer rather than the attribute writer in order to curtail automatically
299
310
  # adding tmpl to the ref attribute value when the inv_prop attribute is set to ref.
300
- wtr = inv_prop.property_writer
311
+ wtr = inv_prop.java_writer
301
312
  # parameterize tmpl with inverse ref
302
313
  tmpl.send(wtr, ref)
303
314
  # submit the query
304
315
  logger.debug { "Submitting #{obj.qp} #{attribute} inverted query template #{tmpl.qp} ..." }
305
316
  persistence_service(tmpl.class).query(tmpl)
306
317
  end
307
-
318
+
308
319
  # Finds the database content matching the given search object and merges the matching
309
320
  # database values into the object. The find uses the search object secondary or alternate
310
321
  # key for the search.
@@ -315,14 +326,16 @@ module CaRuby
315
326
  # If a match is found, then each missing search object non-domain-valued attribute is set to
316
327
  # the fetched attribute value and this method returns the search object.
317
328
  #
318
- # @quirk caCORE there is no caCORE find utility method to update a search target with persistent content,
319
- # so it is done manually here.
329
+ # @quirk caCORE there is no caCORE find utility method to update a search target with persistent
330
+ # content, so it is done manually here.
320
331
  #
321
- # @param obj (see #find)
322
- # @return [Jinx::Resource, nil] obj if there is a matching database record, nil otherwise
323
- # @raise [DatabaseError] if more than object matches the obj attribute values or if
332
+ # @param (see #find)
333
+ # @return (see #find)
334
+ # @raise [DatabaseError] if more than object matches the target object attribute values or if
324
335
  # the search object is a dependent entity that does not reference an owner
325
336
  def find_object(obj)
337
+ # The transient set includes every object for which a find failed. This set is used to preclude
338
+ # a recursive unsuccessful search.
326
339
  if @transients.include?(obj) then
327
340
  logger.debug { "Find #{obj.qp} obviated since the search was previously unsuccessful in the current database operation context." }
328
341
  return
@@ -334,23 +347,30 @@ module CaRuby
334
347
  return obj if obj.equal?(fetched)
335
348
 
336
349
  logger.debug { "Fetch #{obj.qp} matched database object #{fetched}." }
350
+ # The target is no longer a transient since the find succeeded (see above).
337
351
  @transients.delete(obj)
338
- # recursively copy the nondomain attributes, esp. the identifer, of the fetched domain object references
352
+ # If the fetched object is a subclass of the target, then return the fetched object.
353
+ if fetched.class < obj.class then
354
+ # Clean up the fetched object before it is returned.
355
+ detoxify(fetched)
356
+ return persistify(fetched)
357
+ end
358
+ # Recursively copy the nondomain attributes of the fetched domain object references.
359
+ # This merge sets the target identifier and fills in missing values of other nondomain
360
+ # attributes.
339
361
  merge_fetched(fetched, obj)
340
362
  # Inject the lazy loader for loadable domain reference attributes.
341
363
  persistify(obj, fetched)
342
364
  obj
343
365
  end
344
-
345
- # Fetches the object matching the specified object obj from the database.
366
+
367
+ # Fetches the object matching the specified domain object from the database.
346
368
  #
347
369
  # @see #find_object
348
370
  def fetch_object(obj)
349
371
  # If there is an identifier, then work around the caCORE identifier query bug by delegating
350
372
  # to the HQL identifier query.
351
- if obj.identifier then
352
- return query_on_identifier(obj).first
353
- end
373
+ return query_on_identifier(obj).first if obj.identifier
354
374
  # Make the finder template with key attributes.
355
375
  tmpl = finder_template(obj)
356
376
  # If a template could be made, then fetch on the template.
@@ -367,18 +387,14 @@ module CaRuby
367
387
  # submit the query on the template
368
388
  logger.debug { "Query template for finding #{obj.qp}: #{template}." }
369
389
  result = query_on_template(template)
370
- # a fetch query which returns more than one result is an error.
371
- # possible cause is an incorrect secondary key.
390
+ # It is an error to have an ambiguous result.
391
+ # A possible cause is an unenforced secondary or alternate key.
372
392
  if result.size > 1 then
373
- msg = "More than one match for #{obj.class.qp} find with template #{template}."
374
- # it is an error to have an ambiguous result
375
- logger.error("Fetch error - #{msg}:\n#{obj}")
376
- Jinx.fail(DatabaseError, msg)
393
+ raise DatabaseError.new("More than one match for #{obj.class.qp} find with template #{template.qp}:\n#{template.dump}")
377
394
  end
378
-
379
395
  result.first
380
396
  end
381
-
397
+
382
398
  # If the given domain object is a dependent with an unfetched owner, then this method fetches
383
399
  # the owner and attempts to match the owner dependent to this object.
384
400
  #
@@ -388,11 +404,11 @@ module CaRuby
388
404
  owner = nil
389
405
  oattr = obj.class.owner_attributes.detect { |pa| owner = obj.send(pa) }
390
406
  return if owner.nil? or owner.fetched?
391
-
407
+
392
408
  logger.debug { "Querying #{obj.qp} by matching on the #{oattr} owner #{owner.qp} dependents..." }
393
409
  inv_prop = obj.class.property(oattr)
394
410
  if inv_prop.nil? then
395
- Jinx.fail(DatabaseError, "#{dep.class.qp} owner attribute #{oattr} does not have a #{owner.class.qp} inverse dependent attribute.")
411
+ raise DatabaseError.new("#{dep.class.qp} owner attribute #{oattr} does not have a #{owner.class.qp} inverse dependent attribute.")
396
412
  end
397
413
  inv = inv_prop.inverse
398
414
  # fetch the owner if necessary
@@ -402,19 +418,20 @@ module CaRuby
402
418
  logger.debug { "Found #{obj.qp} by fetching the owner #{owner}." }
403
419
  return obj
404
420
  end
405
-
421
+
406
422
  # try to match a fetched owner dependent
407
423
  deps = lazy_loader.enable { owner.send(inv) }
408
424
  if obj.identifier then
409
425
  logger.debug { "Found #{obj.qp} by fetching the owner #{owner} #{inv} dependents." }
410
426
  return obj
411
427
  else
412
- logger.debug { "#{obj.qp} does not match one of the fetched owner #{owner} #{inv} dependents #{deps}." }
428
+ logger.debug { "#{obj.qp} does not match one of the fetched owner #{owner} #{inv} dependents #{deps.qp}." }
413
429
  nil
414
430
  end
415
431
  end
416
-
417
- # Returns a copy of obj containing only those key attributes used in a find operation.
432
+
433
+ # Returns a copy of the given domain object containing only those key attributes used
434
+ # in a find operation.
418
435
  #
419
436
  # @quirk caCORE Bug #79: caCORE search fetches on all non-nil attributes, except
420
437
  # occasionally the identifier. There is no indication of how to identify uniquely
@@ -422,9 +439,9 @@ module CaRuby
422
439
  # application configuration.
423
440
  def finder_template(obj)
424
441
  hash = finder_parameters(obj) || return
425
- @srch_tmpl_bldr.build_template(obj, hash)
442
+ @rdr_tmpl_bldr.build_template(obj, hash)
426
443
  end
427
-
444
+
428
445
  # Fetches the given object attribute value from the database.
429
446
  #
430
447
  # @quirk caCORE there is no association fetch for caCORE 3.1 and earlier;
@@ -460,7 +477,7 @@ module CaRuby
460
477
  logger.debug { "Fetching association #{attribute} for #{obj}..." }
461
478
  # load the object if necessary
462
479
  unless exists?(obj) then
463
- Jinx.fail(DatabaseError, "Can't fetch an association since the referencing object is not found in the database: #{obj}")
480
+ raise DatabaseError.new("Can't fetch an association since the referencing object is not found in the database: #{obj}")
464
481
  end
465
482
  # fetch the reference
466
483
  result = query_safe(obj, attribute)
@@ -476,7 +493,7 @@ module CaRuby
476
493
  # Unbracket the result if the search propery is not a collection.
477
494
  prop.collection? ? result : result.first
478
495
  end
479
-
496
+
480
497
  # Fetches the given object attribute reference from the database and sets the property value.
481
498
  #
482
499
  # @param (see #fetch_association)
@@ -484,7 +501,7 @@ module CaRuby
484
501
  def load_association(obj, attribute)
485
502
  obj.set_property_value(attribute, fetch_association(obj, attribute))
486
503
  end
487
-
504
+
488
505
  # @return [{Symbol => Object}, nil] the find operation key attributes, or nil if there is no complete key
489
506
  #
490
507
  # @quirk caCORE caCORE search fetches on all non-nil attributes, except occasionally the identifier
@@ -496,7 +513,7 @@ module CaRuby
496
513
  key_value_hash(obj, obj.class.secondary_key_attributes) or
497
514
  key_value_hash(obj, obj.class.alternate_key_attributes)
498
515
  end
499
-
516
+
500
517
  # @return [{Symbol => Object}, nil] the attribute => value hash suitable for a finder template
501
518
  # if obj has searchable values for all of the given key attributes, nil otherwise
502
519
  def key_value_hash(obj, attributes)
@@ -525,7 +542,7 @@ module CaRuby
525
542
  value
526
543
  end
527
544
  end
528
-
545
+
529
546
  # Returns whether the obj attribute value is either not a domain object reference or exists
530
547
  # in the database.
531
548
  #
@@ -535,23 +552,6 @@ module CaRuby
535
552
  return false if value.nil?
536
553
  obj.class.nondomain_attribute?(pa) or value.identifier
537
554
  end
538
-
539
- # Sets the template attribute to a new search reference object created from source.
540
- # The reference contains only the source identifier.
541
- #
542
- # @quirk caCORE The search template must break inverse integrity by clearing an owner inverse reference,
543
- # since a dependent => onwer => dependent cycle causes a caCORE search infinite loop.
544
- #
545
- # @return [Jinx::Resource, nil] the search reference, or nil if source does not exist in the database
546
- def add_search_template_reference(template, source, attribute)
547
- return if not exists?(source)
548
- ref = source.copy(:identifier)
549
- template.set_property_value(attribute, ref)
550
- inverse = template.class.property(attribute).derived_inverse
551
- ref.clear_attribute(inverse) if inverse
552
- logger.debug { "Search reference parameter #{attribute} for #{template.qp} set to #{ref} copied from #{source.qp}" }
553
- ref
554
- end
555
555
  end
556
556
  end
557
557
  end
@@ -43,13 +43,16 @@ module CaRuby
43
43
  # source domain object. The reference contains only the source identifier, if it exists,
44
44
  # or the source non-domain attributes otherwise.
45
45
  #
46
+ # @quirk caCORE The search template must break inverse integrity by clearing an owner inverse reference,
47
+ # since a dependent => owner => dependent cycle causes a caCORE search infinite loop.
48
+ #
46
49
  # @return [Jinx::Resource] the search reference
47
50
  def add_search_template_reference(template, source, attribute)
48
51
  ref = source.identifier ? source.copy(:identifier) : source.copy
49
- # Disable inverse integrity, since the template attribute assignment might have added a reference
50
- # from ref to template, which introduces a template => ref => template cycle that causes a caCORE
51
- # search infinite loop. Use the Java property writer instead.
52
- wtr = template.class.property(attribute).property_writer
52
+ # Disable inverse integrity by using the Java property writer instead of the attribute writer.
53
+ # The attribute writer will add a reference from ref to template, which introduces a
54
+ # template => ref => template cycle that causes a caCORE search infinite loop.
55
+ wtr = template.class.property(attribute).java_writer
53
56
  template.send(wtr, ref)
54
57
  logger.debug { "Search reference parameter #{attribute} for #{template.qp} set to #{ref} copied from #{source.qp}" }
55
58
  ref
@@ -24,26 +24,34 @@ module CaRuby
24
24
  # @option opts [String] :database the mandatory database name
25
25
  # @option opts [String] :database_user the mandatory database username (not the application login name)
26
26
  # @option opts [String] :database_password the optional database password (not the application login password)
27
+ # @option opts [String] :database_type the optional database type (default +mysql+)
27
28
  # @option opts [String] :database_host the optional database host
28
29
  # @option opts [Integer] :database_port the optional database port number
29
- # @option opts [Integer] :database_port the optional database port number
30
30
  # @option opts [String] :database_driver the optional DBI connect driver string, e.g. +jdbc:mysql+
31
31
  # @option opts [String] :database_url the optional database connection URL
32
32
  # @option opts [String] :database_driver_class the optional DBI connect driver class name
33
33
  # @raise [CaRuby::ConfigurationError] if an option is invalid
34
34
  def initialize(opts)
35
35
  if opts.empty? then
36
- Jinx.fail(CaRuby::ConfigurationError, "The caRuby database connection properties were not found.")
36
+ raise CaRuby::ConfigurationError.new("The caRuby database connection properties were not found.")
37
37
  end
38
38
  app_host = Options.get(:host, opts, 'localhost')
39
39
  db_host = Options.get(:database_host, opts, app_host)
40
40
  db_type = Options.get(:database_type, opts, 'mysql')
41
41
  db_driver = Options.get(:database_driver, opts) { default_driver_string(db_type) }
42
42
  db_port = Options.get(:database_port, opts) { default_port(db_type) }
43
- db_name = Options.get(:database, opts) { raise_missing_option_exception(:database) }
44
- @db_url = Options.get(:database_url, opts) { "#{db_driver}://#{db_host}:#{db_port}/#{db_name}" }
43
+ db_name = Options.get(:database, opts)
44
+ @db_url = Options.get(:database_url, opts) do
45
+ # If there is a db name, then make the default db url.
46
+ # Otherwise, raise an error.
47
+ if db_name then
48
+ "#{db_driver}://#{db_host}:#{db_port}/#{db_name}"
49
+ else
50
+ raise_missing_option_error(:database)
51
+ end
52
+ end
45
53
  @dbi_url = 'dbi:' + @db_url
46
- @username = Options.get(:database_user, opts) { raise_missing_option_exception(:database_user) }
54
+ @username = Options.get(:database_user, opts) { raise_missing_option_error(:database_user) }
47
55
  @password = Options.get(:database_password, opts)
48
56
  @driver_class = Options.get(:database_driver_class, opts, default_driver_class(db_type))
49
57
  # The effective connection options.
@@ -65,7 +73,7 @@ module CaRuby
65
73
  # @yield [dbh] the transaction statements
66
74
  # @yieldparam [RDBI::Database] dbh the database handle
67
75
  def execute
68
- RDBI.connect(:JDBC, :database => @db_url, :user => @username, :password => @password, :driver_class=> @driver_class) do |dbh|
76
+ RDBI.connect(:JDBC, :database => @db_url, :user => @username, :password => @password, :driver_class => @driver_class) do |dbh|
69
77
  yield dbh
70
78
  end
71
79
  end
@@ -74,13 +82,19 @@ module CaRuby
74
82
  #
75
83
  # @param [String] sql the SQL to execute
76
84
  # @param [Array] args the SQL bindings
85
+ # @yield [row] operate on the result
86
+ # @yield [Array] the result row
77
87
  # @return [Array] the query result
78
- def query(sql, *args)
88
+ def query(sql, *args, &block)
79
89
  fetched = nil
80
90
  execute do |dbh|
81
- res = dbh.execute(sql, *args)
82
- fetched = res.fetch(:all)
83
- res.finish
91
+ result = dbh.execute(sql, *args)
92
+ if block_given? then
93
+ result.each(&block)
94
+ else
95
+ fetched = result.fetch(:all)
96
+ end
97
+ result.finish
84
98
  end
85
99
  fetched
86
100
  end
@@ -149,7 +163,8 @@ module CaRuby
149
163
  case db_type.downcase
150
164
  when 'mysql' then 'Jdbc:mysql'
151
165
  when 'oracle' then 'Oracle'
152
- else Jinx.fail(CaRuby::ConfigurationError, "Default database connection driver string could not be determined for database type #{db_type}")
166
+ when 'jdbc' then 'Jdbc'
167
+ else raise CaRuby::ConfigurationError.new("Default database connection driver string could not be determined for database type #{db_type}")
153
168
  end
154
169
  end
155
170
 
@@ -157,7 +172,8 @@ module CaRuby
157
172
  case db_type.downcase
158
173
  when 'mysql' then MYSQL_DRIVER_CLASS_NAME
159
174
  when 'oracle' then ORACLE_DRIVER_CLASS_NAME
160
- else Jinx.fail(CaRuby::ConfigurationError, "Default database connection driver class could not be determined for database type #{db_type}")
175
+ when 'jdbc' then ''
176
+ else raise CaRuby::ConfigurationError.new("Default database connection driver class could not be determined for database type #{db_type}")
161
177
  end
162
178
  end
163
179
 
@@ -165,12 +181,13 @@ module CaRuby
165
181
  case db_type.downcase
166
182
  when 'mysql' then 3306
167
183
  when 'oracle' then 1521
168
- else Jinx.fail(CaRuby::ConfigurationError, "Default database connection port could not be determined for database type #{db_type}")
184
+ when 'jdbc' then -1
185
+ else raise CaRuby::ConfigurationError.new("Default database connection port could not be determined for database type #{db_type}")
169
186
  end
170
187
  end
171
188
 
172
- def raise_missing_option_exception(option)
173
- Jinx.fail(CaRuby::ConfigurationError, "Database connection property not found: #{option}")
189
+ def raise_missing_option_error(option)
190
+ raise CaRuby::ConfigurationError.new("Database connection property not found: #{option}")
174
191
  end
175
192
  end
176
193
  end