caruby-core 1.4.9 → 1.5.1

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 (61) hide show
  1. data/History.md +48 -0
  2. data/lib/caruby/cli/command.rb +2 -1
  3. data/lib/caruby/csv/csv_mapper.rb +8 -8
  4. data/lib/caruby/database/persistable.rb +44 -65
  5. data/lib/caruby/database/persistence_service.rb +12 -9
  6. data/lib/caruby/database/persistifier.rb +14 -14
  7. data/lib/caruby/database/reader.rb +53 -51
  8. data/lib/caruby/database/search_template_builder.rb +9 -10
  9. data/lib/caruby/database/store_template_builder.rb +58 -58
  10. data/lib/caruby/database/writer.rb +96 -96
  11. data/lib/caruby/database.rb +19 -19
  12. data/lib/caruby/domain/attribute.rb +581 -0
  13. data/lib/caruby/domain/attributes.rb +615 -0
  14. data/lib/caruby/domain/dependency.rb +240 -0
  15. data/lib/caruby/domain/importer.rb +183 -0
  16. data/lib/caruby/domain/introspection.rb +176 -0
  17. data/lib/caruby/domain/inverse.rb +173 -0
  18. data/lib/caruby/domain/inversible.rb +1 -2
  19. data/lib/caruby/domain/java_attribute.rb +173 -0
  20. data/lib/caruby/domain/merge.rb +13 -10
  21. data/lib/caruby/domain/metadata.rb +141 -0
  22. data/lib/caruby/domain/mixin.rb +35 -0
  23. data/lib/caruby/domain/reference_visitor.rb +5 -3
  24. data/lib/caruby/domain.rb +340 -0
  25. data/lib/caruby/import/java.rb +29 -25
  26. data/lib/caruby/migration/migratable.rb +5 -5
  27. data/lib/caruby/migration/migrator.rb +19 -15
  28. data/lib/caruby/migration/resource_module.rb +1 -1
  29. data/lib/caruby/resource.rb +39 -30
  30. data/lib/caruby/util/collection.rb +94 -33
  31. data/lib/caruby/util/coordinate.rb +28 -2
  32. data/lib/caruby/util/log.rb +4 -4
  33. data/lib/caruby/util/module.rb +12 -28
  34. data/lib/caruby/util/partial_order.rb +9 -10
  35. data/lib/caruby/util/pretty_print.rb +46 -26
  36. data/lib/caruby/util/topological_sync_enumerator.rb +10 -4
  37. data/lib/caruby/util/transitive_closure.rb +2 -2
  38. data/lib/caruby/util/visitor.rb +1 -1
  39. data/lib/caruby/version.rb +1 -1
  40. data/test/lib/caruby/database/persistable_test.rb +1 -1
  41. data/test/lib/caruby/domain/domain_test.rb +14 -28
  42. data/test/lib/caruby/domain/inversible_test.rb +1 -1
  43. data/test/lib/caruby/import/java_test.rb +5 -0
  44. data/test/lib/caruby/migration/test_case.rb +0 -1
  45. data/test/lib/caruby/test_case.rb +9 -10
  46. data/test/lib/caruby/util/collection_test.rb +23 -5
  47. data/test/lib/caruby/util/module_test.rb +10 -14
  48. data/test/lib/caruby/util/partial_order_test.rb +16 -15
  49. data/test/lib/caruby/util/visitor_test.rb +1 -1
  50. data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +1 -1
  51. metadata +16 -15
  52. data/History.txt +0 -44
  53. data/lib/caruby/domain/attribute_metadata.rb +0 -551
  54. data/lib/caruby/domain/java_attribute_metadata.rb +0 -183
  55. data/lib/caruby/domain/resource_attributes.rb +0 -565
  56. data/lib/caruby/domain/resource_dependency.rb +0 -217
  57. data/lib/caruby/domain/resource_introspection.rb +0 -160
  58. data/lib/caruby/domain/resource_inverse.rb +0 -151
  59. data/lib/caruby/domain/resource_metadata.rb +0 -155
  60. data/lib/caruby/domain/resource_module.rb +0 -370
  61. data/lib/caruby/yard/resource_metadata_handler.rb +0 -8
data/History.md ADDED
@@ -0,0 +1,48 @@
1
+ This history lists major release themes. See the GitHub Commits (https://github.com/caruby/core)
2
+ for change details.
3
+
4
+ 1.5.1 / 2011-06-28
5
+ ------------------
6
+ * Domain refactoring.
7
+
8
+ 1.4.9 / 2011-05-04
9
+ ------------------
10
+ * Support Oracle driver.
11
+
12
+ 1.4.8 / 2011-05-03
13
+ ------------------
14
+ * Fix annotation migration error.
15
+
16
+ * Refactor resource import.
17
+
18
+ * Add attribute filters.
19
+
20
+ 1.4.7 / 2011-03-04
21
+ ------------------
22
+ * Support annotation migration.
23
+
24
+ 1.4.6 / 2011-02-26
25
+ ------------------
26
+ * Upgrade to JRuby 1.5.
27
+
28
+ 1.4.5 / 2011-02-26
29
+ ------------------
30
+ * Fix default option.
31
+
32
+ 1.4.4 / 2011-02-25
33
+ ------------------
34
+ * Support default migration option.
35
+
36
+ * Merge nondomain collection value properly.
37
+
38
+ 1.4.3 / 2011-02-18
39
+ ------------------
40
+ * Refactor Persistifier
41
+
42
+ 1.4.2 / 2010-11-30
43
+ ------------------
44
+ * Minor Migrator fixes.
45
+
46
+ 1.4.1 / 2010-11-23
47
+ ------------------
48
+ * Initial public release.
@@ -79,7 +79,8 @@ module CaRuby
79
79
  [:file, "--file FILE", "Configuration file containing other options"],
80
80
  [:log, "--log FILE", "Log file"],
81
81
  [:debug, "--debug", "Display debug log messages"],
82
- [:quiet, "-q", "--quiet", "Suppress printing messages to stdout"]
82
+ [:quiet, "-q", "--quiet", "Suppress printing messages to stdout"],
83
+ [:verbose, "-v", "--verbose", "Print additional messages to stdout"]
83
84
  ]
84
85
 
85
86
  # @param [{Symbol => Object}] opts the option => value hash
@@ -55,18 +55,18 @@ module CaRuby
55
55
  @string_headers = Set.new
56
56
  @hdr_map.each do |path, cls_hdr_hash|
57
57
  last = path.last
58
- @string_headers.merge!(cls_hdr_hash.values) if AttributeMetadata === last and last.type == String
58
+ @string_headers.merge!(cls_hdr_hash.values) if Attribute === last and last.type == String
59
59
  end
60
60
  end
61
61
 
62
- # Returns the given klass's mapped AttributeMetadata paths.
62
+ # Returns the given klass's mapped Attribute paths.
63
63
  # The default klass is the target class.
64
64
  def paths(klass=nil)
65
65
  klass ||= @target
66
66
  @cls_paths_hash[klass]
67
67
  end
68
68
 
69
- # Returns the header mapped by the given AttributeMetadata path and starting klass.
69
+ # Returns the header mapped by the given Attribute path and starting klass.
70
70
  # The default klass is the target class.
71
71
  def header(path, klass=nil)
72
72
  klass ||= @target
@@ -90,8 +90,8 @@ module CaRuby
90
90
  end
91
91
  end
92
92
 
93
- # @param [{Symbol => <AttributeMetadata>}] config the field => path list configuration
94
- # @return [({Symbol => <AttributeMetadata>}, {Class => {<AttributeMetadata> => Symbol>}})]
93
+ # @param [{Symbol => <Attribute>}] config the field => path list configuration
94
+ # @return [({Symbol => <Attribute>}, {Class => {<Attribute> => Symbol>}})]
95
95
  # the class => paths hash and the path => class => header hash
96
96
  def map_headers(config)
97
97
  # the class => paths hash; populated in map_headers
@@ -112,7 +112,7 @@ module CaRuby
112
112
  [cls_paths_hash, hdr_map]
113
113
  end
114
114
 
115
- # Returns an array of AttributeMetadata or symbol objects for the period-delimited path string path_s in the
115
+ # Returns an array of Attribute or symbol objects for the period-delimited path string path_s in the
116
116
  # pattern (_class_|_attribute_)(+.+_attribute_)*, e.g.:
117
117
  # ClinicalStudy.status
118
118
  # study.status
@@ -127,7 +127,7 @@ module CaRuby
127
127
  if names.empty? then
128
128
  raise ConfigurationError.new("Attribute entry in CSV field mapping is not in <class>.<attribute> format: #{value}")
129
129
  end
130
- # build the AttributeMetadata path by traversing the names path
130
+ # build the Attribute path by traversing the names path
131
131
  # if the name corresponds to a parent attribute, then add the attribute metadata.
132
132
  # otherwise, if the name is a method, then add the method.
133
133
  path = []
@@ -150,7 +150,7 @@ module CaRuby
150
150
  tail = names[path.size..-1].map { |name| name.to_sym }
151
151
  path.concat(tail)
152
152
  # return the starting class and path
153
- # Note that the starting class is not necessarily the first path AttributeMetadata declarer, since the
153
+ # Note that the starting class is not necessarily the first path Attribute declarer, since the
154
154
  # starting class could be a concrete subclass of an abstract declarer. this is important, since the class
155
155
  # must be instantiated.
156
156
  [klass, path]
@@ -28,17 +28,18 @@ module CaRuby
28
28
  not saved?(obj)
29
29
  end
30
30
 
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.
31
+ # Returns the data access mediator for this domain object.
32
+ # Application #{Resource} modules are required to override this method.
33
33
  #
34
- # @return [Database, nil] the data access mediator for this Persistable, if any
34
+ # @return [Database] the data access mediator for this Persistable, if any
35
+ # @raise [DatabaseError] if the subclass does not override this method
35
36
  def database
36
- nil
37
+ raise ValidationError.new("#{self} database is missing")
37
38
  end
38
39
 
39
- # @return [PersistenceService, nil] the database application service for this Persistable, if any
40
+ # @return [PersistenceService] the database application service for this Persistable
40
41
  def persistence_service
41
- database.persistence_service(self.class) if database
42
+ database.persistence_service(self.class)
42
43
  end
43
44
 
44
45
  # Fetches the domain objects which match this template from the {#database}.
@@ -74,6 +75,13 @@ module CaRuby
74
75
  database.create(self)
75
76
  end
76
77
 
78
+ # Creates this domain object, if necessary.
79
+ #
80
+ # @raise (see Database#ensure_exists)
81
+ def ensure_exists
82
+ database.ensure_exists(self)
83
+ end
84
+
77
85
  # Saves this domain object in the {#database}.
78
86
  #
79
87
  # @return (see Writer#save)
@@ -109,7 +117,7 @@ module CaRuby
109
117
  alias :== :equal?
110
118
 
111
119
  alias :eql? :==
112
-
120
+
113
121
  # Captures the Persistable's updatable attribute base values.
114
122
  # The snapshot is subsequently accessible using the {#snapshot} method.
115
123
  #
@@ -156,8 +164,7 @@ module CaRuby
156
164
  def changed_attributes
157
165
  if @snapshot then
158
166
  ovh = value_hash(self.class.updatable_attributes)
159
- diff = @snapshot.diff(ovh) { |attr, v, ov|
160
- Resource.value_equal?(v, ov) }
167
+ diff = @snapshot.diff(ovh) { |attr, v, ov| Resource.value_equal?(v, ov) }
161
168
  diff.keys
162
169
  else
163
170
  self.class.updatable_attributes
@@ -191,8 +198,8 @@ module CaRuby
191
198
  end
192
199
 
193
200
  # Returns the attributes to load on demand. The base attribute list is given by the
194
- # {ResourceAttributes#loadable_attributes} whose value is nil or empty.
195
- # In addition, if this Persistable has more than one {ResourceDependency#owner_attributes}
201
+ # {Attributes#loadable_attributes} whose value is nil or empty.
202
+ # In addition, if this Persistable has more than one {Dependency#owner_attributes}
196
203
  # and one is non-nil, then none of the owner attributes are loaded on demand,
197
204
  # since there can be at most one owner and ownership cannot change.
198
205
  #
@@ -225,52 +232,23 @@ module CaRuby
225
232
  disable_singleton_method(writer)
226
233
  end
227
234
 
228
- # Returns whether this domain object must be fetched to reflect the database state.
229
- # This default implementation returns whether this domain object was created and
230
- # there are any autogenerated attributes. Subclasses can override to relax or restrict
231
- # the condition.
232
- #
233
- # caCORE alert - the auto-generated criterion is a necessary but not sufficient condition
234
- # to determine whether a save caCORE result reflects the database state. Example:
235
- # * caTissue SCG event parameters are not auto-generated on SCG create if the SCG collection
236
- # status is Pending, but are auto-generated on SCG update if the SCG status is changed
237
- # to Complete. By contrast, the SCG specimens are auto-generated on SCG create, even if
238
- # the status is +Pending+.
239
- # The caBIG application can override this method in a Database subclass to fine-tune the
240
- # fetch criteria. Adding a more restrictive {#fetch_saved?} condition will will improve
241
- # performance but not change functionality.
242
- #
243
- # caCORE alert - a saved attribute which is cascaded but not fetched must be fetched in
244
- # order to reflect the database identifier in the saved object.
245
- #
246
- # @return [Boolean] whether this domain object must be fetched to reflect the database state
247
- def fetch_saved?
248
- # only fetch a create, not an update (note that subclasses can override this condition)
249
- return false if identifier
250
- # Check for an attribute with a value that might need to be changed in order to
251
- # reflect the auto-generated database content.
252
- ag_attrs = self.class.autogenerated_attributes
253
- return false if ag_attrs.empty?
254
- ag_attrs.any? { |attr| not send(attr).nil_or_empty? }
255
- end
256
-
257
235
  # Returns this domain object's attributes which must be fetched to reflect the database state.
258
- # This default implementation returns the {ResourceAttributes#autogenerated_logical_dependent_attributes}
236
+ # This default implementation returns the {Attributes#autogenerated_logical_dependent_attributes}
259
237
  # if this domain object does not have an identifier, or an empty array otherwise.
260
238
  # Subclasses can override to relax or restrict the condition.
261
239
  #
262
- # caCORE alert - the auto-generated criterion is a necessary but not sufficient condition
263
- # to determine whether a save caCORE result reflects the database state. Example:
264
- # * caTissue SCG event parameters are not auto-generated on SCG create if the SCG collection
265
- # status is Pending, but are auto-generated on SCG update if the SCG status is changed
266
- # to Complete. By contrast, the SCG specimens are auto-generated on SCG create, even if
267
- # the status is +Pending+.
268
- # The caBIG application can override this method in a Database subclass to fine-tune the
269
- # fetch criteria. Adding a more restrictive {#fetch_saved?} condition will will improve
270
- # performance but not change functionality.
240
+ # @quirk caCORE the auto-generated criterion is a necessary but not sufficient condition
241
+ # to determine whether a save caCORE result reflects the database state. Example:
242
+ # * caTissue SCG event parameters are not auto-generated on SCG create if the SCG collection
243
+ # status is Pending, but are auto-generated on SCG update if the SCG status is changed
244
+ # to Complete. By contrast, the SCG specimens are auto-generated on SCG create, even if
245
+ # the status is +Pending+.
246
+ # The caBIG application can override this method in a Database subclass to fine-tune the
247
+ # fetch criteria. Adding a more restrictive {#fetch_saved?} condition will will improve
248
+ # performance but not change functionality.
271
249
  #
272
- # caCORE alert - a saved attribute which is cascaded but not fetched must be fetched in
273
- # order to reflect the database identifier in the saved object.
250
+ # @quirk caCORE a saved attribute which is cascaded but not fetched must be fetched in
251
+ # order to reflect the database identifier in the saved object.
274
252
  #
275
253
  # @param [Database::Operation] the save operation
276
254
  # @return [<Symbol>] whether this domain object must be fetched to reflect the database state
@@ -306,18 +284,19 @@ module CaRuby
306
284
  # there are any autogenerated attributes. Subclasses can override to relax or restrict
307
285
  # the condition.
308
286
  #
309
- # caCORE alert - the auto-generated criterion is a necessary but not sufficient condition
310
- # to determine whether a save caCORE result reflects the database state. Example:
311
- # * caTissue SCG event parameters are not auto-generated on SCG create if the SCG collection
312
- # status is Pending, but are auto-generated on SCG update if the SCG status is changed
313
- # to Complete. By contrast, the SCG specimens are auto-generated on SCG create, even if
314
- # the status is +Pending+.
315
- # The caBIG application can override this method in a Database subclass to fine-tune the
316
- # fetch criteria. Adding a more restrictive {#fetch_saved?} condition will will improve
317
- # performance but not change functionality.
287
+ # @quirk caCORE The auto-generated criterion is a necessary but not sufficient condition
288
+ # to determine whether a save caCORE result reflects the database state. Example:
289
+ # * caTissue SCG event parameters are not auto-generated on SCG create if the SCG collection
290
+ # status is Pending, but are auto-generated on SCG update if the SCG status is changed
291
+ # to Complete. By contrast, the SCG specimens are auto-generated on SCG create, even if
292
+ # the status is +Pending+.
293
+ #
294
+ # The caBIG application can override this method in a Database subclass to fine-tune the
295
+ # fetch criteria. Adding a more restrictive {#fetch_saved?} condition will will improve
296
+ # performance but not change functionality.
318
297
  #
319
- # caCORE alert - a saved attribute which is cascaded but not fetched must be fetched in
320
- # order to reflect the database identifier in the saved object.
298
+ # @quirk caCORE A saved attribute which is cascaded but not fetched must be fetched in
299
+ # order to reflect the database identifier in the saved object.
321
300
  #
322
301
  # @return [Boolean] whether this domain object must be fetched to reflect the database state
323
302
  def fetch_saved?
@@ -330,7 +309,7 @@ module CaRuby
330
309
  ag_attrs.any? { |attr| not send(attr).nil_or_empty? }
331
310
  end
332
311
 
333
- # Sets the {ResourceAttributes#volatile_nondomain_attributes} to the other fetched value,
312
+ # Sets the {Attributes#volatile_nondomain_attributes} to the other fetched value,
334
313
  # if different.
335
314
  #
336
315
  # @param [Resource] other the fetched domain object reflecting the database state
@@ -470,7 +449,7 @@ module CaRuby
470
449
  # dissociate the method from this instance
471
450
  method = self.method(name_or_sym.to_sym)
472
451
  method.unbind
473
- # JRuby alert - Unbind doesn't work in JRuby 1.1.6. In that case, redefine the singleton method to delegate
452
+ # JRuby unbind doesn't work in JRuby 1.1.6. In that case, redefine the singleton method to delegate
474
453
  # to the class instance method.
475
454
  if singleton_methods.include?(name_or_sym.to_s) then
476
455
  args = (1..method.arity).map { |argnum| "arg#{argnum}" }.join(', ')
@@ -47,16 +47,16 @@ module CaRuby
47
47
  # object following the given attribute path. The query condition is determined by the values set in the
48
48
  # template. Every non-nil attribute in the template is used as a select condition.
49
49
  #
50
- # caCORE alert - this method returns the direct result of calling the +caCORE+ application service
51
- # search method. Calling reference attributes of this result is broken by +caCORE+ design.
50
+ # @quirk caCORE this method returns the direct result of calling the +caCORE+ application service
51
+ # search method. Calling reference attributes of this result is broken by +caCORE+ design.
52
52
  def query(template_or_hql, *path)
53
53
  String === template_or_hql ? query_hql(template_or_hql) : query_template(template_or_hql, path)
54
54
  end
55
55
 
56
56
  # Submits the create to the application service and returns the created object.
57
57
  #
58
- # caCORE alert - this method returns the direct result of calling the +caCORE+ application service
59
- # create method. Calling reference attributes of this result is broken by +caCORE+ design.
58
+ # @quirk caCORE this method returns the direct result of calling the +caCORE+ application service
59
+ # create method. Calling reference attributes of this result is broken by +caCORE+ design.
60
60
  def create(obj)
61
61
  logger.debug { "Submitting create #{obj.pp_s(:single_line)} to application service #{name}..." }
62
62
  begin
@@ -91,9 +91,9 @@ module CaRuby
91
91
 
92
92
  # Returns a freshly initialized ApplicationServiceProvider remote instance.
93
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.
94
+ # @quirk caCORE 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
97
  #
98
98
  # @return [ApplicationServiceProvider] the CaCORE service provider wrapped by this PersistenceService
99
99
  def app_service
@@ -135,11 +135,14 @@ module CaRuby
135
135
  'localhost'
136
136
  end
137
137
 
138
+ # Dispatches the given HQL to the application service.
139
+ #
140
+ # @quirk caCORE query target parameter is necessary for caCORE 3.x but deprecated in caCORE 4+.
141
+ #
142
+ # @param [String] hql the HQL to submit
138
143
  def query_hql(hql)
139
144
  logger.debug { "Building HQLCriteria..." }
140
145
  criteria = HQLCriteria.new(hql)
141
- # caCORE alert - query target parameter is necessary for caCORE 3.x but deprecated in caCORE 4+
142
- # TODO caCORE 4 - remove target parameter
143
146
  target = hql[/from\s+(\S+)/i, 1]
144
147
  raise DatabaseError.new("HQL does not contain a FROM clause: #{hql}") unless target
145
148
  logger.debug { "Submitting search on target class #{target} with the following HQL:\n #{hql}" }
@@ -61,22 +61,22 @@ module CaRuby
61
61
  cached
62
62
  end
63
63
 
64
- # caCORE alert - Dereferencing a caCORE search result uncascaded collection attribute
65
- # raises a Hibernate missing session error.
66
- # This problem is addressed by post-processing the +caCORE+ search result to set the
67
- # toxic attributes to an empty value.
68
- #
69
- # caCORE alert - The caCORE search result does not set the obvious inverse attributes,
70
- # e.g. children fetched with a parent do not have the children inverse parent attribute
71
- # set to the parent. Rather, it is a toxic caCORE reference which must be purged. This
72
- # leaves an empty reference which must be lazy-loaded, which is inefficient and inconsistent.
73
- # This situation is rectified in this detoxify method by setting the dependent owner
74
- # attribute to the fetched owner in the detoxification {ReferenceVisitor} copy-match-merge.
75
- #
76
64
  # This method copies each result domain object into a new object of the same type.
77
65
  # The copy nondomain attribute values are set to the fetched object values.
78
66
  # The copy fetched reference attribute values are set to a copy of the result references.
79
67
  #
68
+ # @quirk caCORE Dereferencing a caCORE search result uncascaded collection attribute
69
+ # raises a Hibernate missing session error.
70
+ # This problem is addressed by post-processing the +caCORE+ search result to set the
71
+ # toxic attributes to an empty value.
72
+ #
73
+ # @quirk caCORE The caCORE search result does not set the obvious inverse attributes,
74
+ # e.g. children fetched with a parent do not have the children inverse parent attribute
75
+ # set to the parent. Rather, it is a toxic caCORE reference which must be purged. This
76
+ # leaves an empty reference which must be lazy-loaded, which is inefficient and inconsistent.
77
+ # This situation is rectified in this detoxify method by setting the dependent owner
78
+ # attribute to the fetched owner in the detoxification {ReferenceVisitor} copy-match-merge.
79
+ #
80
80
  # @return [Resource, <Resource>] the detoxified object(s)
81
81
  def detoxify(toxic)
82
82
  return if toxic.nil?
@@ -90,7 +90,7 @@ module CaRuby
90
90
  end
91
91
 
92
92
  # Sets each of the toxic attributes in the given domain object to the corresponding
93
- # {ResourceMetadata#empty_value}.
93
+ # {Metadata#empty_value}.
94
94
  #
95
95
  # @param [Resource] toxic the toxic domain object
96
96
  def clear_toxic_attributes(toxic)
@@ -156,7 +156,7 @@ module CaRuby
156
156
  # @param obj (see #persistify_object)
157
157
  def set_inverses(obj)
158
158
  obj.class.domain_attributes.each_pair do |attr, attr_md|
159
- inv_md = attr_md.inverse_attribute_metadata || next
159
+ inv_md = attr_md.inverse_metadata || next
160
160
  if inv_md.collection? then
161
161
  obj.send(attr).enumerate { |ref| ref.send(inv_md.to_sym) << obj }
162
162
  else
@@ -32,7 +32,7 @@ module CaRuby
32
32
  # is a String, then the HQL statement String is executed.
33
33
  #
34
34
  # Otherwise, the query condition is determined by the values set in the template.
35
- # The non-nil {ResourceAttributes#searchable_attributes} are used in the query.
35
+ # The non-nil {Attributes#searchable_attributes} are used in the query.
36
36
  #
37
37
  # The optional path arguments are attribute symbols from the template to the
38
38
  # destination class, e.g.:
@@ -107,11 +107,11 @@ module CaRuby
107
107
  # Queries the given obj_or_hql as described in {#query} and makes a detoxified copy of the
108
108
  # toxic caCORE search result.
109
109
  #
110
- # caCORE alert - The query result consists of new domain objects whose content is copied
111
- # from the caBIG application search result. The caBIG result is Hibernate-enhanced but
112
- # sessionless. This result contains toxic broken objects whose access methods fail.
113
- # Therefore, this method sanitizes the toxic caBIG result to reflect the persistent state
114
- # of the domain objects.
110
+ # @quirk caCORE The query result consists of new domain objects whose content is copied
111
+ # from the caBIG application search result. The caBIG result is Hibernate-enhanced but
112
+ # sessionless. This result contains toxic broken objects whose access methods fail.
113
+ # Therefore, this method sanitizes the toxic caBIG result to reflect the persistent state
114
+ # of the domain objects.
115
115
  #
116
116
  # @param (see #query)
117
117
  # @return (see #query)
@@ -209,8 +209,8 @@ module CaRuby
209
209
  # If a template could not be built and obj is dependent, then this method
210
210
  # queries the obj owner with a dependent filter.
211
211
  #
212
- # caCORE alert - Bug #79 - API search with only id returns entire table.
213
- # Work around this bug by issuing a HQL query instead.
212
+ # @quirk caCORE Bug #79 - API search with only id returns entire table.
213
+ # Work around this bug by issuing a HQL query instead.
214
214
  #
215
215
  # @param [Resource] obj the query template object
216
216
  # @param [Symbol, nil] attribute the optional attribute to fetch
@@ -267,15 +267,15 @@ module CaRuby
267
267
  return false if attribute.nil?
268
268
  attr_md = obj.class.attribute_metadata(attribute)
269
269
  return false if attr_md.type.abstract?
270
- inv_md = attr_md.inverse_attribute_metadata
270
+ inv_md = attr_md.inverse_metadata
271
271
  inv_md and inv_md.searchable? and finder_parameters(obj)
272
272
  end
273
273
 
274
274
  # Queries the given query object attribute by querying an attribute type template which references obj.
275
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.
276
+ # @quirk caCORE 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
279
  #
280
280
  # @param (see #query_object)
281
281
  def query_with_inverted_reference(obj, attribute=nil)
@@ -308,6 +308,9 @@ module CaRuby
308
308
  # If a match is found, then each missing search object non-domain-valued attribute is set to
309
309
  # the fetched attribute value and this method returns the search object.
310
310
  #
311
+ # @quirk caCORE there is no caCORE find utility method to update a search target with persistent content,
312
+ # so it is done manually here.
313
+ #
311
314
  # @param obj (see #find)
312
315
  # @return [Resource, nil] obj if there is a matching database record, nil otherwise
313
316
  # @raise [DatabaseError] if more than object matches the obj attribute values or if
@@ -323,14 +326,11 @@ module CaRuby
323
326
  fetched = fetch_object(obj) || return
324
327
  # fetch_object can return obj; if so, then done
325
328
  return obj if obj.equal?(fetched)
329
+
326
330
  logger.debug { "Fetch #{obj.qp} matched database object #{fetched}." }
327
331
  @transients.delete(obj)
328
-
329
- # caCORE alert - there is no caCORE find utility method to update a search target with persistent content,
330
- # so it is done manually here.
331
- # recursively copy the nondomain attributes, esp. the identifer, of the fetched domain object references
332
+ # recursively copy the nondomain attributes, esp. the identifer, of the fetched domain object references
332
333
  merge_fetched(fetched, obj)
333
- # caCORE alert - see query method alerts.
334
334
  # Inject the lazy loader for loadable domain reference attributes.
335
335
  persistify(obj, fetched)
336
336
  obj
@@ -407,38 +407,38 @@ module CaRuby
407
407
 
408
408
  # Returns a copy of obj containing only those key attributes used in a find operation.
409
409
  #
410
- # caCORE alert - Bug #79: caCORE search fetches on all non-nil attributes, except
411
- # occasionally the identifier. There is no indication of how to identify uniquely
412
- # searchable attributes, so the secondary and alternate key is added manually in the
413
- # application configuration.
410
+ # @quirk caCORE Bug #79: caCORE search fetches on all non-nil attributes, except
411
+ # occasionally the identifier. There is no indication of how to identify uniquely
412
+ # searchable attributes, so the secondary and alternate key is added manually in the
413
+ # application configuration.
414
414
  def finder_template(obj)
415
415
  hash = finder_parameters(obj) || return
416
416
  @srch_tmpl_bldr.build_template(obj, hash)
417
417
  end
418
418
 
419
419
  # Fetches the given obj attribute from the database.
420
- # caCORE alert - there is no association fetch for caCORE 3.1 and earlier;
421
- # caCORE 4 association search is not yet adequately proven in caRuby testing.
422
- # Fall back on a general query instead (the devil we know). See also the
423
- # following alert.
424
- #
425
- # caCORE alert - caCORE search on a non-collection attribute returns a collection result,
426
- # even with the caCORE 4 association search. caRuby rectifies this by returning
427
- # an association fetch result consistent with the association attribute return type.
428
- #
429
- # caCORE alert - Preliminary indication is that caCORE 4 does not validate that
430
- # a non-collection association search returns at most one item.
431
- #
432
- # caCORE alert - Since the caCORE search result has toxic references which must be purged,
433
- # the detoxified copy loses reference integrity. E.g. a query on the children attribute of
434
- # a parent object forces lazy load of each child => parent reference separately resolving
435
- # in separate parent copies. There is no recognition that the children reference the parent
436
- # which generated the query. This anomaly is partially rectified in this fetch_association
437
- # method by setting the fetched objects inverse to the given search target object. The
438
- # inconsistent and inefficient caCORE behavior is further corrected by setting inverse
439
- # owners when the fetch result is persistified, as described in {Persistifier#persistify}.
440
- # Callers who do not persistify the result should call {Persistifier#set_inverses} on the
441
- # result.
420
+ # @quirk caCORE there is no association fetch for caCORE 3.1 and earlier;
421
+ # caCORE 4 association search is not yet adequately proven in caRuby testing.
422
+ # Fall back on a general query instead (the devil we know). See also the
423
+ # following alert.
424
+ #
425
+ # @quirk caCORE caCORE search on a non-collection attribute returns a collection result,
426
+ # even with the caCORE 4 association search. caRuby rectifies this by returning
427
+ # an association fetch result consistent with the association attribute return type.
428
+ #
429
+ # @quirk caCORE Preliminary indication is that caCORE 4 does not validate that
430
+ # a non-collection association search returns at most one item.
431
+ #
432
+ # @quirk caCORE Since the caCORE search result has toxic references which must be purged,
433
+ # the detoxified copy loses reference integrity. E.g. a query on the children attribute of
434
+ # a parent object forces lazy load of each child => parent reference separately resolving
435
+ # in separate parent copies. There is no recognition that the children reference the parent
436
+ # which generated the query. This anomaly is partially rectified in this fetch_association
437
+ # method by setting the fetched objects inverse to the given search target object. The
438
+ # inconsistent and inefficient caCORE behavior is further corrected by setting inverse
439
+ # owners when the fetch result is persistified, as described in {Persistifier#persistify}.
440
+ # Callers who do not persistify the result should call {Persistifier#set_inverses} on the
441
+ # result.
442
442
  #
443
443
  # @param [Resource] obj the search target object
444
444
  # @param [Symbol] attribute the association to fetch
@@ -452,7 +452,7 @@ module CaRuby
452
452
  # fetch the reference
453
453
  result = query_safe(obj, attribute)
454
454
  # set the result inverse references
455
- inv_md = obj.class.attribute_metadata(attribute).inverse_attribute_metadata
455
+ inv_md = obj.class.attribute_metadata(attribute).inverse_metadata
456
456
  if inv_md and not inv_md.collection? then
457
457
  inv_obj = obj.copy(:identifier)
458
458
  result.each do |ref|
@@ -466,10 +466,10 @@ module CaRuby
466
466
 
467
467
  # Returns a copy of obj containing only those key attributes used in a find operation.
468
468
  #
469
- # caCORE alert - caCORE search fetches on all non-nil attributes, except occasionally the identifier
470
- # (cf. https://cabig-kc.nci.nih.gov/Bugzilla/show_bug.cgi?id=79).
471
- # there is no indication of how to identify uniquely searchable attributes, so the secondary key
472
- # is added manually in the application configuration.
469
+ # @quirk caCORE caCORE search fetches on all non-nil attributes, except occasionally the identifier
470
+ # (cf. https://cabig-kc.nci.nih.gov/Bugzilla/show_bug.cgi?id=79).
471
+ # there is no indication of how to identify uniquely searchable attributes, so the secondary key
472
+ # is added manually in the application configuration.
473
473
  def finder_parameters(obj)
474
474
  key_value_hash(obj, obj.class.primary_key_attributes) or
475
475
  key_value_hash(obj, obj.class.secondary_key_attributes) or
@@ -513,13 +513,15 @@ module CaRuby
513
513
 
514
514
  # Sets the template attribute to a new search reference object created from source.
515
515
  # The reference contains only the source identifier.
516
- # Returns the search reference, or nil if source does not exist in the database.
516
+ #
517
+ # @quirk caCORE The search template must break inverse integrity by clearing an owner inverse reference,
518
+ # since a dependent => onwer => dependent cycle causes a caCORE search infinite loop.
519
+ #
520
+ # @return [Resource, nil] the search reference, or nil if source does not exist in the database
517
521
  def add_search_template_reference(template, source, attribute)
518
522
  return if not exists?(source)
519
523
  ref = source.copy(:identifier)
520
524
  template.set_attribute(attribute, ref)
521
- # caCORE alert - clear an owner inverse reference, since the template attr assignment might have added a reference
522
- # from ref to template, which introduces a template => ref => template cycle that causes a caCORE search infinite loop.
523
525
  inverse = template.class.attribute_metadata(attribute).derived_inverse
524
526
  ref.clear_attribute(inverse) if inverse
525
527
  logger.debug { "Search reference parameter #{attribute} for #{template.qp} set to #{ref} copied from #{source.qp}" }
@@ -5,19 +5,18 @@ module CaRuby
5
5
  # SearchTemplateBuilder builds a template suitable for a caCORE saarch database operation.
6
6
  class SearchTemplateBuilder
7
7
  # Returns a template for matching the domain object obj and the optional hash values.
8
- # The default hash attributes are the {ResourceAttributes#searchable_attributes}.
8
+ # The default hash attributes are the {Attributes#searchable_attributes}.
9
9
  # The template includes only the non-domain attributes of the hash references.
10
10
  #
11
- # caCORE alert - Because of caCORE API limitations, the obj searchable attribute
12
- # values are limited to the following:
13
- # * non-domain attribute values
14
- # * non-collection domain attribute references which contain a key
11
+ # @quirk caCORE Because of caCORE API limitations, the obj searchable attribute
12
+ # values are limited to the following:
13
+ # * non-domain attribute values
14
+ # * non-collection domain attribute references which contain a key
15
15
  #
16
- # caCORE alert - the caCORE query builder breaks on reference cycles and
17
- # is easily confused by extraneous references, so it is necessary to search
18
- # with a template instead that contains only references essential to the
19
- # search. Each reference is confirmed to exist and the reference content in
20
- # the template consists entirely of the fetched identifier attribute.
16
+ # @quirk caCORE the caCORE query builder breaks on reference cycles and is easily confused
17
+ # by extraneous references, so it is necessary to search with a template instead that contains
18
+ # only references essential to the search. Each reference is confirmed to exist and the
19
+ # reference content in the template consists entirely of the fetched identifier attribute.
21
20
  def build_template(obj, hash=nil)
22
21
  # split the attributes into reference and non-reference attributes.
23
22
  # the new search template object is built from the non-reference attributes.