caruby-core 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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