caruby-core 1.4.9 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.md +48 -0
- data/lib/caruby/cli/command.rb +2 -1
- data/lib/caruby/csv/csv_mapper.rb +8 -8
- data/lib/caruby/database/persistable.rb +44 -65
- data/lib/caruby/database/persistence_service.rb +12 -9
- data/lib/caruby/database/persistifier.rb +14 -14
- data/lib/caruby/database/reader.rb +53 -51
- data/lib/caruby/database/search_template_builder.rb +9 -10
- data/lib/caruby/database/store_template_builder.rb +58 -58
- data/lib/caruby/database/writer.rb +96 -96
- data/lib/caruby/database.rb +19 -19
- data/lib/caruby/domain/attribute.rb +581 -0
- data/lib/caruby/domain/attributes.rb +615 -0
- data/lib/caruby/domain/dependency.rb +240 -0
- data/lib/caruby/domain/importer.rb +183 -0
- data/lib/caruby/domain/introspection.rb +176 -0
- data/lib/caruby/domain/inverse.rb +173 -0
- data/lib/caruby/domain/inversible.rb +1 -2
- data/lib/caruby/domain/java_attribute.rb +173 -0
- data/lib/caruby/domain/merge.rb +13 -10
- data/lib/caruby/domain/metadata.rb +141 -0
- data/lib/caruby/domain/mixin.rb +35 -0
- data/lib/caruby/domain/reference_visitor.rb +5 -3
- data/lib/caruby/domain.rb +340 -0
- data/lib/caruby/import/java.rb +29 -25
- data/lib/caruby/migration/migratable.rb +5 -5
- data/lib/caruby/migration/migrator.rb +19 -15
- data/lib/caruby/migration/resource_module.rb +1 -1
- data/lib/caruby/resource.rb +39 -30
- data/lib/caruby/util/collection.rb +94 -33
- data/lib/caruby/util/coordinate.rb +28 -2
- data/lib/caruby/util/log.rb +4 -4
- data/lib/caruby/util/module.rb +12 -28
- data/lib/caruby/util/partial_order.rb +9 -10
- data/lib/caruby/util/pretty_print.rb +46 -26
- data/lib/caruby/util/topological_sync_enumerator.rb +10 -4
- data/lib/caruby/util/transitive_closure.rb +2 -2
- data/lib/caruby/util/visitor.rb +1 -1
- data/lib/caruby/version.rb +1 -1
- data/test/lib/caruby/database/persistable_test.rb +1 -1
- data/test/lib/caruby/domain/domain_test.rb +14 -28
- data/test/lib/caruby/domain/inversible_test.rb +1 -1
- data/test/lib/caruby/import/java_test.rb +5 -0
- data/test/lib/caruby/migration/test_case.rb +0 -1
- data/test/lib/caruby/test_case.rb +9 -10
- data/test/lib/caruby/util/collection_test.rb +23 -5
- data/test/lib/caruby/util/module_test.rb +10 -14
- data/test/lib/caruby/util/partial_order_test.rb +16 -15
- data/test/lib/caruby/util/visitor_test.rb +1 -1
- data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +1 -1
- metadata +16 -15
- data/History.txt +0 -44
- data/lib/caruby/domain/attribute_metadata.rb +0 -551
- data/lib/caruby/domain/java_attribute_metadata.rb +0 -183
- data/lib/caruby/domain/resource_attributes.rb +0 -565
- data/lib/caruby/domain/resource_dependency.rb +0 -217
- data/lib/caruby/domain/resource_introspection.rb +0 -160
- data/lib/caruby/domain/resource_inverse.rb +0 -151
- data/lib/caruby/domain/resource_metadata.rb +0 -155
- data/lib/caruby/domain/resource_module.rb +0 -370
- 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.
|
data/lib/caruby/cli/command.rb
CHANGED
@@ -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
|
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
|
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
|
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 => <
|
94
|
-
# @return [({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
|
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
|
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
|
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
|
32
|
-
#
|
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
|
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
|
-
|
37
|
+
raise ValidationError.new("#{self} database is missing")
|
37
38
|
end
|
38
39
|
|
39
|
-
# @return [PersistenceService
|
40
|
+
# @return [PersistenceService] the database application service for this Persistable
|
40
41
|
def persistence_service
|
41
|
-
database.persistence_service(self.class)
|
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
|
-
# {
|
195
|
-
# In addition, if this Persistable has more than one {
|
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 {
|
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
|
263
|
-
#
|
264
|
-
#
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
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
|
273
|
-
#
|
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
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
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
|
320
|
-
#
|
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 {
|
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
|
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
|
51
|
-
#
|
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
|
59
|
-
#
|
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
|
95
|
-
#
|
96
|
-
#
|
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
|
-
# {
|
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.
|
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 {
|
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
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
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
|
213
|
-
#
|
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.
|
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
|
277
|
-
#
|
278
|
-
#
|
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
|
411
|
-
#
|
412
|
-
#
|
413
|
-
#
|
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
|
421
|
-
#
|
422
|
-
#
|
423
|
-
#
|
424
|
-
#
|
425
|
-
# caCORE
|
426
|
-
#
|
427
|
-
#
|
428
|
-
#
|
429
|
-
# caCORE
|
430
|
-
#
|
431
|
-
#
|
432
|
-
# caCORE
|
433
|
-
#
|
434
|
-
#
|
435
|
-
#
|
436
|
-
#
|
437
|
-
#
|
438
|
-
#
|
439
|
-
#
|
440
|
-
#
|
441
|
-
#
|
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).
|
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
|
470
|
-
#
|
471
|
-
#
|
472
|
-
#
|
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
|
-
#
|
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 {
|
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
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
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
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
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.
|