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.
- 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.
|