caruby-core 1.4.7 → 1.4.9

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.
Files changed (48) hide show
  1. data/History.txt +11 -0
  2. data/README.md +1 -1
  3. data/lib/caruby/cli/command.rb +27 -3
  4. data/lib/caruby/csv/csv_mapper.rb +2 -0
  5. data/lib/caruby/csv/csvio.rb +187 -169
  6. data/lib/caruby/database.rb +33 -16
  7. data/lib/caruby/database/lazy_loader.rb +23 -23
  8. data/lib/caruby/database/persistable.rb +32 -18
  9. data/lib/caruby/database/persistence_service.rb +20 -7
  10. data/lib/caruby/database/reader.rb +22 -21
  11. data/lib/caruby/database/search_template_builder.rb +7 -9
  12. data/lib/caruby/database/sql_executor.rb +52 -27
  13. data/lib/caruby/database/store_template_builder.rb +18 -13
  14. data/lib/caruby/database/writer.rb +107 -44
  15. data/lib/caruby/domain/attribute_metadata.rb +35 -25
  16. data/lib/caruby/domain/java_attribute_metadata.rb +43 -20
  17. data/lib/caruby/domain/merge.rb +9 -5
  18. data/lib/caruby/domain/reference_visitor.rb +4 -3
  19. data/lib/caruby/domain/resource_attributes.rb +52 -12
  20. data/lib/caruby/domain/resource_dependency.rb +129 -42
  21. data/lib/caruby/domain/resource_introspection.rb +1 -1
  22. data/lib/caruby/domain/resource_inverse.rb +20 -3
  23. data/lib/caruby/domain/resource_metadata.rb +20 -4
  24. data/lib/caruby/domain/resource_module.rb +190 -124
  25. data/lib/caruby/import/java.rb +39 -19
  26. data/lib/caruby/migration/migratable.rb +31 -6
  27. data/lib/caruby/migration/migrator.rb +126 -40
  28. data/lib/caruby/migration/uniquify.rb +0 -1
  29. data/lib/caruby/resource.rb +28 -5
  30. data/lib/caruby/util/attribute_path.rb +0 -2
  31. data/lib/caruby/util/class.rb +8 -5
  32. data/lib/caruby/util/collection.rb +5 -3
  33. data/lib/caruby/util/domain_extent.rb +0 -3
  34. data/lib/caruby/util/options.rb +10 -9
  35. data/lib/caruby/util/person.rb +41 -12
  36. data/lib/caruby/util/pretty_print.rb +1 -1
  37. data/lib/caruby/util/validation.rb +0 -28
  38. data/lib/caruby/version.rb +1 -1
  39. data/test/lib/caruby/import/java_test.rb +26 -9
  40. data/test/lib/caruby/migration/test_case.rb +103 -0
  41. data/test/lib/caruby/test_case.rb +231 -0
  42. data/test/lib/caruby/util/class_test.rb +2 -2
  43. data/test/lib/caruby/util/visitor_test.rb +3 -2
  44. data/test/lib/examples/galena/clinical_trials/migration/participant_test.rb +28 -0
  45. data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +40 -0
  46. metadata +195 -170
  47. data/lib/caruby/domain/attribute_initializer.rb +0 -16
  48. data/test/lib/caruby/util/validation_test.rb +0 -14
@@ -6,16 +6,15 @@ require 'caruby/util/options'
6
6
  require 'caruby/util/visitor'
7
7
  require 'caruby/util/inflector'
8
8
  require 'caruby/database/persistable'
9
- require 'caruby/database/persistifier'
9
+ require 'caruby/database/persistence_service'
10
10
  require 'caruby/database/reader'
11
11
  require 'caruby/database/writer'
12
- require 'caruby/database/persistence_service'
13
-
14
- # the caBIG client classes
15
- import 'gov.nih.nci.system.applicationservice.ApplicationServiceProvider'
16
- import 'gov.nih.nci.system.comm.client.ClientSession'
12
+ require 'caruby/database/persistifier'
17
13
 
18
14
  module CaRuby
15
+ # The caBIG client session class.
16
+ java_import Java::gov.nih.nci.system.comm.client.ClientSession
17
+
19
18
  # Database operation error.
20
19
  class DatabaseError < RuntimeError; end
21
20
 
@@ -43,7 +42,7 @@ module CaRuby
43
42
  # store method. CaRuby::Resource sets reasonable default values, recognizes application dependencies and steers
44
43
  # around caBIG idiosyncracies to the extent possible.
45
44
  class Database
46
- include Reader, Writer, Persistifier, Validation
45
+ include Reader, Writer, Persistifier
47
46
 
48
47
  # Database CRUD operation.
49
48
  class Operation
@@ -78,6 +77,24 @@ module CaRuby
78
77
 
79
78
  # Creates a new Database with the specified service name and options.
80
79
  #
80
+ # caCORE alert - obtaining a caCORE session instance mysteriously depends on referencing the
81
+ # application service first. Therefore, the default persistence service appService method must
82
+ # be called after it is instantiated and before the session is instantiated. However, when
83
+ # the appService method is called just before a session is acquired, then this call corrupts
84
+ # the object state of existing objects.
85
+ #
86
+ # Specifically, when a CaTissue::CollectionProtocol is created which references a
87
+ # CaTissue::CollectionProtocolRegistration which in turn references a CaTissue::Participant,
88
+ # then the call to PersistenceService.appService replaces the CaTissue::Participant
89
+ # reference with a difference CaTissue::Participant instance. The work-around for
90
+ # this extremely bizarre bug is to call appService immediately after instantiating
91
+ # the default persistence service.
92
+ #
93
+ # This bug might be a low-level JRuby-Java-caCORE-Hibernate confusion where something in
94
+ # caCORE stomps on an existing JRuby object graph. To reproduce, move the appService call
95
+ # to the start_session method and run PCBIN::MigrationTest#test_save with all but the
96
+ # verify_save(:biopsy, BIOPSY_OPTS) line commented out.
97
+ #
81
98
  # @param [String] service_name the name of the default {PersistenceService}
82
99
  # @param [{Symbol => String}] opts access options
83
100
  # @option opts [String] :host application service host name
@@ -96,6 +113,7 @@ module CaRuby
96
113
  port = Options.get(:port, opts)
97
114
  # class => service hash; default is the catissuecore app service
98
115
  @def_persist_svc = PersistenceService.new(service_name, :host => host, :port => port)
116
+ @def_persist_svc.app_service
99
117
  @persistence_services = [@def_persist_svc].to_set
100
118
  @cls_svc_hash = Hash.new(@def_persist_svc)
101
119
  # the create/update nested operations
@@ -149,11 +167,12 @@ module CaRuby
149
167
  # Subclasses can override for specialized services. A session is
150
168
  # started on demand if necessary.
151
169
  #
152
- # @param [Persistable] obj the domain object
170
+ # @param [Persistable, Class] obj the domain object or {Resource} class
153
171
  # @return [PersistanceService] the service for the domain object
154
- def persistence_service(obj)
155
- start_session if @session.nil?
156
- @def_persist_svc
172
+ def persistence_service(klass)
173
+ unless Class === klass then raise ArgumentError.new("#{self} persistence_service argument is not a Class: {#klass.qp}") end
174
+ start_session if @session.nil?
175
+ @def_persist_svc
157
176
  end
158
177
 
159
178
  # Adds the given service to this database.
@@ -244,11 +263,9 @@ module CaRuby
244
263
 
245
264
  # Initializes the default application service.
246
265
  def start_session
247
- raise DatabaseError.new('Application user option missing') if @user.nil?
248
- raise DatabaseError.new('Application password option missing') if @password.nil?
249
- # caCORE alert - obtaining a caCORE session instance mysteriously depends on referencing the application service first
250
- @def_persist_svc.app_service
251
- @session = ClientSession.instance()
266
+ if @user.nil? then raise DatabaseError.new('Application user option missing') end
267
+ if @password.nil? then raise DatabaseError.new('Application password option missing') end
268
+ @session = ClientSession.instance
252
269
  connect(@user, @password)
253
270
  end
254
271
 
@@ -3,19 +3,40 @@ require 'caruby/database/fetched_matcher'
3
3
  module CaRuby
4
4
  class Database
5
5
  # A LazyLoader fetches an association from the database on demand.
6
- class LazyLoader < Proc
6
+ class LazyLoader
7
7
  # Creates a new LazyLoader which calls the loader block on the subject.
8
8
  #
9
9
  # @yield [subject, attribute] fetches the given subject attribute value from the database
10
10
  # @yieldparam [Resource] subject the domain object whose attribute is to be loaded
11
11
  # @yieldparam [Symbol] attribute the domain attribute to load
12
12
  def initialize(&loader)
13
- super { |sbj, attr| load(sbj, attr, &loader) }
13
+ @loader = loader
14
14
  # the fetch result matcher
15
15
  @matcher = FetchedMatcher.new
16
16
  @enabled = true
17
17
  end
18
18
 
19
+ # @param [Resource] subject the domain object whose attribute is to be loaded
20
+ # @param [Symbol] the domain attribute to load
21
+ # @yield (see #initialize)
22
+ # @yieldparam (see #initialize)
23
+ # @return the attribute value loaded from the database
24
+ # @raise [RuntimeError] if this loader is disabled
25
+ def load(subject, attribute)
26
+ if disabled? then raise RuntimeError.new("#{subject.qp} lazy load called on disabled loader") end
27
+ logger.debug { "Lazy-loading #{subject.qp} #{attribute}..." }
28
+ # the current value
29
+ oldval = subject.send(attribute)
30
+ # load the fetched value
31
+ fetched = @loader.call(subject, attribute)
32
+ # nothing to merge if nothing fetched
33
+ return oldval if fetched.nil_or_empty?
34
+ # merge the fetched into the attribute
35
+ logger.debug { "Merging #{subject.qp} fetched #{attribute} value #{fetched.qp}#{' into ' + oldval.qp if oldval}..." }
36
+ matches = @matcher.match(fetched.to_enum, oldval.to_enum)
37
+ subject.merge_attribute(attribute, fetched, matches)
38
+ end
39
+
19
40
  # Disables this lazy loader. If the loader is already disabled, then this method is a no-op.
20
41
  # Otherwise, if a block is given, then the lazy loader is reenabled after the block is executed.
21
42
  #
@@ -74,27 +95,6 @@ module CaRuby
74
95
  # @return [Boolean] true if this loader was previously disabled, false otherwise
75
96
  def set_enabled
76
97
  disabled? and (@enabled = true)
77
- end
78
-
79
- # @param [Resource] subject the domain object whose attribute is to be loaded
80
- # @param [Symbol] the domain attribute to load
81
- # @yield (see #initialize)
82
- # @yieldparam (see #initialize)
83
- # @return the attribute value loaded from the database
84
- # @raise [RuntimeError] if this loader is disabled
85
- def load(subject, attribute)
86
- if disabled? then raise RuntimeError.new("#{subject.qp} lazy load called on disabled loader") end
87
- logger.debug { "Lazy-loading #{subject.qp} #{attribute}..." }
88
- # the current value
89
- oldval = subject.send(attribute)
90
- # load the fetched value
91
- fetched = yield(subject, attribute)
92
- # nothing to merge if nothing fetched
93
- return oldval if fetched.nil_or_empty?
94
- # merge the fetched into the attribute
95
- logger.debug { "Merging #{subject.qp} fetched #{attribute} value #{fetched.qp}#{' into ' + oldval.qp if oldval}..." }
96
- matches = @matcher.match(fetched.to_enum, oldval.to_enum)
97
- subject.merge_attribute(attribute, fetched, matches)
98
98
  end
99
99
  end
100
100
  end
@@ -8,10 +8,8 @@ module CaRuby
8
8
  # The Persistable mixin adds persistance capability. Every instance which includes Persistable
9
9
  # must respond to an overrided {#database} method.
10
10
  module Persistable
11
- include Validation
12
-
13
- # @return [{Symbol => Object}] the content value hash at the point of the last
14
- # take_snapshot call
11
+ # @return [{Symbol => Object}] the content value hash at the point of the last {#take_snapshot}
12
+ # call
15
13
  attr_reader :snapshot
16
14
 
17
15
  # @param [Resource, <Resource>, nil] obj the object(s) to check
@@ -30,10 +28,17 @@ module CaRuby
30
28
  not saved?(obj)
31
29
  end
32
30
 
33
- # @return [Database] the data access mediator for this Persistable
34
- # @raise [NotImplementedError] if the Persistable subclass does not define this method
31
+ # Returns the data access mediator for this domain object, if any. The default implementation
32
+ # returns nil. Application #{Resource} modules can override this method.
33
+ #
34
+ # @return [Database, nil] the data access mediator for this Persistable, if any
35
35
  def database
36
- raise NotImplementedError.new("Database operations are not available for #{self}")
36
+ nil
37
+ end
38
+
39
+ # @return [PersistenceService, nil] the database application service for this Persistable, if any
40
+ def persistence_service
41
+ database.persistence_service(self.class) if database
37
42
  end
38
43
 
39
44
  # Fetches the domain objects which match this template from the {#database}.
@@ -177,13 +182,12 @@ module CaRuby
177
182
  def add_lazy_loader(loader, attributes=nil)
178
183
  # guard against invalid call
179
184
  if identifier.nil? then raise ValidationError.new("Cannot add lazy loader to an unfetched domain object: #{self}") end
180
-
181
185
  # the attributes to lazy-load
182
186
  attributes ||= loadable_attributes
183
187
  return if attributes.empty?
184
188
  # define the reader and writer method overrides for the missing attributes
185
- loaded = attributes.select { |attr| inject_lazy_loader(attr) }
186
- logger.debug { "Lazy loader added to #{qp} attributes #{loaded.to_series}." } unless loaded.empty?
189
+ attrs = attributes.select { |attr| inject_lazy_loader(attr) }
190
+ logger.debug { "Lazy loader added to #{qp} attributes #{attrs.to_series}." } unless attrs.empty?
187
191
  end
188
192
 
189
193
  # Returns the attributes to load on demand. The base attribute list is given by the
@@ -361,7 +365,6 @@ module CaRuby
361
365
  vh = @snapshot
362
366
  ovh = value_hash(self.class.updatable_attributes)
363
367
 
364
-
365
368
  # KLUDGE TODO - confirm this is still a problem and fix
366
369
  # In Galena frozen migration example, SpecimenPosition snapshot doesn't include identifier; work around this here
367
370
  # This could be related to the problem of an abstract DomainObject not being added as a domain module class. See the
@@ -371,8 +374,6 @@ module CaRuby
371
374
  end
372
375
  # END OF KLUDGE
373
376
 
374
-
375
-
376
377
  if vh.size < ovh.size then
377
378
  attr, oval = ovh.detect { |a, v| not vh.has_key?(a) }
378
379
  logger.debug { "#{qp} is missing snapshot #{attr} compared to the current value #{oval.qp}." }
@@ -400,7 +401,7 @@ module CaRuby
400
401
  # @return [Boolean] whether a loader was added to the attribute
401
402
  def inject_lazy_loader(attribute)
402
403
  # bail if there is already a value
403
- send(attribute).enumerate { |ref| return false unless ref.identifier }
404
+ return false if attribute_loaded?(attribute)
404
405
  # the accessor methods to modify
405
406
  reader, writer = self.class.attribute_metadata(attribute).accessors
406
407
  # The singleton attribute reader method loads the reference once and thenceforth calls the
@@ -411,6 +412,15 @@ module CaRuby
411
412
  instance_eval "def #{writer}(value); remove_lazy_loader(:#{attribute}); super; end"
412
413
  true
413
414
  end
415
+
416
+ # @param (see #inject_lazy_loader)
417
+ # @return [Boolean] whether the attribute references one or more domain objects, and each
418
+ # referenced object has an identifier
419
+ def attribute_loaded?(attribute)
420
+ value = transient_value(attribute)
421
+ return false if value.nil_or_empty?
422
+ Enumerable === value ? value.all? { |ref| ref.identifier } : value.identifier
423
+ end
414
424
 
415
425
  # Loads the reference attribute database value into this Persistable.
416
426
  #
@@ -419,14 +429,12 @@ module CaRuby
419
429
  def load_reference(attribute)
420
430
  ldr = database.lazy_loader
421
431
  # bypass the singleton method and call the class instance method if the lazy loader is disabled
422
- unless ldr.enabled? then
423
- return self.class.instance_method(attribute).bind(self).call
424
- end
432
+ return transient_value(attribute) unless ldr.enabled?
425
433
 
426
434
  # Disable lazy loading first for the attribute, since the reader method is called by the loader.
427
435
  remove_lazy_loader(attribute)
428
436
  # load the fetched value
429
- merged = ldr.call(self, attribute)
437
+ merged = ldr.load(self, attribute)
430
438
 
431
439
  # update dependent snapshots if necessary
432
440
  attr_md = self.class.attribute_metadata(attribute)
@@ -447,6 +455,12 @@ module CaRuby
447
455
 
448
456
  merged
449
457
  end
458
+
459
+ # @param (see #load_reference)
460
+ # @return the in-memory attribute value, without invoking the lazy loader
461
+ def transient_value(attribute)
462
+ self.class.instance_method(attribute).bind(self).call
463
+ end
450
464
 
451
465
  # Disables the given singleton attribute accessor method.
452
466
  #
@@ -2,9 +2,16 @@ require 'caruby/util/version'
2
2
  require 'caruby/database'
3
3
  require 'caruby/util/stopwatch'
4
4
 
5
- import 'gov.nih.nci.common.util.HQLCriteria'
6
-
7
5
  module CaRuby
6
+ # HQLCriteria is required for the query_hql method.
7
+ java_import Java::gov.nih.nci.common.util.HQLCriteria
8
+
9
+ # The encapsulated caBIG service class.
10
+ java_import Java::gov.nih.nci.system.applicationservice.ApplicationServiceProvider
11
+
12
+ # This import is not strictly necessary, but works around Ticket #5.
13
+ java_import Java::gov.nih.nci.system.comm.client.ApplicationServiceClientImpl
14
+
8
15
  # A PersistenceService wraps a caCORE application service.
9
16
  class PersistenceService
10
17
  # The service name.
@@ -17,14 +24,15 @@ module CaRuby
17
24
  #
18
25
  # @param [String] the caBIG application service name
19
26
  # @param [{Symbol => Object}] opts the options
20
- # @option opts :host the service host (default +localhost+)
21
- # @option opts :version the caTissue version identifier
27
+ # @option opts [String] :host the service host (default +localhost+)
28
+ # @option opts [String] :version the caTissue version identifier
22
29
  def initialize(name, opts={})
23
30
  @name = name
24
31
  ver_opt = opts[:version]
25
32
  @version = ver_opt.to_s.to_version if ver_opt
26
33
  @host = opts[:host] || default_host
27
34
  @port = opts[:port] || 8080
35
+ @url = "http://#{@host}:#{@port}/#{@name}/http/remoteService"
28
36
  @timer = Stopwatch.new
29
37
  logger.debug { "Created persistence service #{name} at #{@host}:#{@port}." }
30
38
  end
@@ -81,11 +89,16 @@ module CaRuby
81
89
  end
82
90
  end
83
91
 
92
+ # Returns a freshly initialized ApplicationServiceProvider remote instance.
93
+ #
94
+ # caCORE alert - When more than one application service is used, each call to the service
95
+ # must reinitialize the remote instance. E.g. this is done in the caTissue DE examples,
96
+ # and is a good general practice.
97
+ #
84
98
  # @return [ApplicationServiceProvider] the CaCORE service provider wrapped by this PersistenceService
85
99
  def app_service
86
- url = "http://#{@host}:#{@port}/#{@name}/http/remoteService"
87
- logger.debug { "Connecting to service provider at #{url}..." }
88
- ApplicationServiceProvider.remote_instance(url)
100
+ logger.debug { "Connecting to service provider at #{@url}..." }
101
+ ApplicationServiceProvider.remote_instance(@url)
89
102
  end
90
103
 
91
104
  private
@@ -13,7 +13,7 @@ module CaRuby
13
13
  def initialize
14
14
  super
15
15
  # the query template builder
16
- @srch_tmpl_bldr = SearchTemplateBuilder.new(self)
16
+ @srch_tmpl_bldr = SearchTemplateBuilder.new
17
17
  # the fetch result matcher
18
18
  @matcher = FetchedMatcher.new
19
19
  # the fetched copier
@@ -85,16 +85,18 @@ module CaRuby
85
85
  end
86
86
  end
87
87
 
88
- # Returns whether domain object obj has a database identifier or exists in the database.
89
- # This method fetches obj from the database if necessary.
90
- # If obj is a domain object collection, then returns whether each item in the collection exists.
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.
90
+ #
91
+ # @param [Resource, <Resource>] obj the domain object(s) to find
92
+ # @return [Boolean] whether the domain object(s) exist in the database
91
93
  def exists?(obj)
92
94
  if obj.nil? then
93
95
  false
94
96
  elsif obj.collection? then
95
97
  obj.all? { |item| exists?(item) }
96
98
  else
97
- obj.identifier or (obj.searchable? and find(obj))
99
+ obj.identifier or find(obj)
98
100
  end
99
101
  end
100
102
 
@@ -196,9 +198,8 @@ module CaRuby
196
198
  def query_hql(hql)
197
199
  java_name = hql[/from\s+(\S+)/i, 1]
198
200
  raise DatabaseError.new("Could not determine target type from HQL: #{hql}") if java_name.nil?
199
- target = Class.to_ruby(java_name)
200
- service = persistence_service(target)
201
- service.query(hql)
201
+ tgt = Class.to_ruby(java_name)
202
+ persistence_service(tgt).query(hql)
202
203
  end
203
204
 
204
205
  # Returns an array of objects fetched from the database which matches
@@ -229,9 +230,9 @@ module CaRuby
229
230
  # Returns an array of objects fetched from the database which matches
230
231
  # the given template and follows the given optional domain attribute.
231
232
  def query_on_template(template, attribute=nil)
232
- target = attribute ? template.class.domain_type(attribute) : template.class
233
- service = persistence_service(target)
234
- attribute ? service.query(template, attribute) : service.query(template)
233
+ tgt = attribute ? template.class.domain_type(attribute) : template.class
234
+ svc = persistence_service(tgt)
235
+ attribute ? svc.query(template, attribute) : svc.query(template)
235
236
  end
236
237
 
237
238
  # Queries on the given template and attribute by issuing a HQL query with an identifier condition.
@@ -272,6 +273,10 @@ module CaRuby
272
273
 
273
274
  # Queries the given query object attribute by querying an attribute type template which references obj.
274
275
  #
276
+ # caCORE alert - caCORE caCORE search enters an infinite loop when the search argument has an object
277
+ # reference graph cycle. Work-around is to ensure that reference integrity is broken in the search
278
+ # argument by not setting inverse attributes.
279
+ #
275
280
  # @param (see #query_object)
276
281
  def query_with_inverted_reference(obj, attribute=nil)
277
282
  attr_md = obj.class.attribute_metadata(attribute)
@@ -282,17 +287,15 @@ module CaRuby
282
287
  tmpl = attr_md.type.new
283
288
  # the inverse attribute
284
289
  inv_md = tmpl.class.attribute_metadata(attr_md.inverse)
285
- # the Java property writer to set the tmpl inverse to ref.
286
- # use the property writer rather than the attribute writer in order to curtail automatically
290
+ # The Java property writer to set the tmpl inverse to ref.
291
+ # Use the property writer rather than the attribute writer in order to curtail automatically
287
292
  # adding tmpl to the ref attribute value when the inv_md attribute is set to ref.
288
- # caCORE alert - caCORE query relies on a lack of inverse integrity, since caCORE search
289
- # enters an infinite loop upon encountering an object graph cycle.
290
- writer = inv_md.property_accessors.last
293
+ wtr = inv_md.property_writer
291
294
  # parameterize tmpl with inverse ref
292
- tmpl.send(writer, ref)
295
+ tmpl.send(wtr, ref)
293
296
  # submit the query
294
297
  logger.debug { "Submitting #{obj.qp} #{attribute} inverted query template #{tmpl.qp} ..." }
295
- persistence_service(tmpl).query(tmpl)
298
+ persistence_service(tmpl.class).query(tmpl)
296
299
  end
297
300
 
298
301
  # Finds the database content matching the given search object and merges the matching
@@ -310,7 +313,7 @@ module CaRuby
310
313
  # @raise [DatabaseError] if more than object matches the obj attribute values or if
311
314
  # the search object is a dependent entity that does not reference an owner
312
315
  def find_object(obj)
313
- if @transients.include?(obj) then
316
+ if @transients.include?(obj) then
314
317
  logger.debug { "Find #{obj.qp} obviated since the search was previously unsuccessful in the current database operation context." }
315
318
  return
316
319
  end
@@ -327,7 +330,6 @@ module CaRuby
327
330
  # so it is done manually here.
328
331
  # recursively copy the nondomain attributes, esp. the identifer, of the fetched domain object references
329
332
  merge_fetched(fetched, obj)
330
-
331
333
  # caCORE alert - see query method alerts.
332
334
  # Inject the lazy loader for loadable domain reference attributes.
333
335
  persistify(obj, fetched)
@@ -354,7 +356,6 @@ module CaRuby
354
356
  # submit the query on the template
355
357
  logger.debug { "Query template for finding #{obj.qp}: #{template}." }
356
358
  result = query_on_template(template)
357
-
358
359
  # a fetch query which returns more than one result is an error.
359
360
  # possible cause is an incorrect secondary key.
360
361
  if result.size > 1 then