caruby-core 1.4.7 → 1.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +11 -0
- data/README.md +1 -1
- data/lib/caruby/cli/command.rb +27 -3
- data/lib/caruby/csv/csv_mapper.rb +2 -0
- data/lib/caruby/csv/csvio.rb +187 -169
- data/lib/caruby/database.rb +33 -16
- data/lib/caruby/database/lazy_loader.rb +23 -23
- data/lib/caruby/database/persistable.rb +32 -18
- data/lib/caruby/database/persistence_service.rb +20 -7
- data/lib/caruby/database/reader.rb +22 -21
- data/lib/caruby/database/search_template_builder.rb +7 -9
- data/lib/caruby/database/sql_executor.rb +52 -27
- data/lib/caruby/database/store_template_builder.rb +18 -13
- data/lib/caruby/database/writer.rb +107 -44
- data/lib/caruby/domain/attribute_metadata.rb +35 -25
- data/lib/caruby/domain/java_attribute_metadata.rb +43 -20
- data/lib/caruby/domain/merge.rb +9 -5
- data/lib/caruby/domain/reference_visitor.rb +4 -3
- data/lib/caruby/domain/resource_attributes.rb +52 -12
- data/lib/caruby/domain/resource_dependency.rb +129 -42
- data/lib/caruby/domain/resource_introspection.rb +1 -1
- data/lib/caruby/domain/resource_inverse.rb +20 -3
- data/lib/caruby/domain/resource_metadata.rb +20 -4
- data/lib/caruby/domain/resource_module.rb +190 -124
- data/lib/caruby/import/java.rb +39 -19
- data/lib/caruby/migration/migratable.rb +31 -6
- data/lib/caruby/migration/migrator.rb +126 -40
- data/lib/caruby/migration/uniquify.rb +0 -1
- data/lib/caruby/resource.rb +28 -5
- data/lib/caruby/util/attribute_path.rb +0 -2
- data/lib/caruby/util/class.rb +8 -5
- data/lib/caruby/util/collection.rb +5 -3
- data/lib/caruby/util/domain_extent.rb +0 -3
- data/lib/caruby/util/options.rb +10 -9
- data/lib/caruby/util/person.rb +41 -12
- data/lib/caruby/util/pretty_print.rb +1 -1
- data/lib/caruby/util/validation.rb +0 -28
- data/lib/caruby/version.rb +1 -1
- data/test/lib/caruby/import/java_test.rb +26 -9
- data/test/lib/caruby/migration/test_case.rb +103 -0
- data/test/lib/caruby/test_case.rb +231 -0
- data/test/lib/caruby/util/class_test.rb +2 -2
- data/test/lib/caruby/util/visitor_test.rb +3 -2
- data/test/lib/examples/galena/clinical_trials/migration/participant_test.rb +28 -0
- data/test/lib/examples/galena/clinical_trials/migration/test_case.rb +40 -0
- metadata +195 -170
- data/lib/caruby/domain/attribute_initializer.rb +0 -16
- data/test/lib/caruby/util/validation_test.rb +0 -14
@@ -11,18 +11,18 @@ module CaRuby
|
|
11
11
|
# * reader method symbol
|
12
12
|
# * writer method symbol
|
13
13
|
class AttributeMetadata
|
14
|
-
# The supported attribute qualifier flags.
|
14
|
+
# The supported attribute qualifier flags. See the complementary methods for an explanation of
|
15
|
+
# the flag option, e.g. {#autogenerated?} for the +:autogenerated+ flag.
|
15
16
|
SUPPORTED_FLAGS = [
|
16
|
-
:autogenerated, :collection, :dependent, :derived, :logical, :disjoint,
|
17
|
-
:no_cascade_update_to_create, :saved, :unsaved, :optional, :fetched, :unfetched,
|
18
|
-
:create_only, :update_only, :unidirectional, :volatile].to_set
|
17
|
+
:autogenerated, :autogenerated_on_update, :collection, :dependent, :derived, :logical, :disjoint,
|
18
|
+
:owner, :cascaded, :no_cascade_update_to_create, :saved, :unsaved, :optional, :fetched, :unfetched,
|
19
|
+
:include_in_save_template, :saved_fetch, :create_only, :update_only, :unidirectional, :volatile].to_set
|
19
20
|
|
20
21
|
# @return [(Symbol, Symbol)] the standard attribute reader and writer methods
|
21
22
|
attr_reader :accessors
|
22
23
|
|
23
24
|
# @return [Class] the declaring class
|
24
25
|
attr_accessor :declarer
|
25
|
-
protected :declarer=
|
26
26
|
|
27
27
|
# @return [Class] the return type
|
28
28
|
attr_reader :type
|
@@ -88,7 +88,7 @@ module CaRuby
|
|
88
88
|
@type = klass
|
89
89
|
if @inv_md then
|
90
90
|
self.inverse = @inv_md.to_sym
|
91
|
-
logger.debug { "Reset #{@declarer.qp}.#{self} inverse from #{@inv_md.type}.#{@inv_md} to #{klass}#{inv_md}." }
|
91
|
+
logger.debug { "Reset #{@declarer.qp}.#{self} inverse from #{@inv_md.type}.#{@inv_md} to #{klass}#{@inv_md}." }
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
@@ -150,9 +150,8 @@ module CaRuby
|
|
150
150
|
# if this attribute is disjoint, then so is the inverse.
|
151
151
|
@inv_md.qualify(:disjoint) if disjoint?
|
152
152
|
end
|
153
|
-
|
154
153
|
# propagate to restrictions
|
155
|
-
if @restrictions then @restrictions.each { |attr_md| attr_md.restrict_inverse_type } end
|
154
|
+
if @restrictions then @restrictions.each { |attr_md| attr_md.restrict_inverse_type(@inv_md) } end
|
156
155
|
end
|
157
156
|
|
158
157
|
# @return [AttributeMetadata, nil] the metadata for the {#inverse} attribute, if any
|
@@ -233,11 +232,21 @@ module CaRuby
|
|
233
232
|
end
|
234
233
|
|
235
234
|
# Returns whether the subject attribute is a dependent whose value is automatically generated
|
236
|
-
# with place-holder domain objects when the parent is created.
|
235
|
+
# with place-holder domain objects when the parent is created. An attribute is auto-generated
|
236
|
+
# if the +:autogenerate+ or the +:autogenerated_on_update+ flag is set.
|
237
237
|
#
|
238
238
|
# @return [Boolean] whether the attribute is auto-generated
|
239
239
|
def autogenerated?
|
240
|
-
@flags.include?(:autogenerated)
|
240
|
+
@flags.include?(:autogenerated) or @flags.include?(:autogenerated_on_update)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Returns whether the the subject attribute is #{autogenerated?} for create. An attribute is
|
244
|
+
# auto-generated for create if the +:autogenerate+ flag is set and the
|
245
|
+
# +:autogenerated_on_update+ flag is not set.
|
246
|
+
#
|
247
|
+
# @return [Boolean] whether the attribute is auto-generated on create
|
248
|
+
def autogenerated_on_create?
|
249
|
+
@flags.include?(:autogenerated) and not @flags.include?(:autogenerated_on_update)
|
241
250
|
end
|
242
251
|
|
243
252
|
# Returns whether this attribute must be fetched when a declarer instance is saved.
|
@@ -248,7 +257,7 @@ module CaRuby
|
|
248
257
|
# @return [Boolean] whether the subject attribute must be refetched in order to reflect
|
249
258
|
# the database content
|
250
259
|
def saved_fetch?
|
251
|
-
autogenerated? or (cascaded? and @flags.include?(:unfetched))
|
260
|
+
@flags.include?(:saved_fetch) or autogenerated? or (cascaded? and @flags.include?(:unfetched))
|
252
261
|
end
|
253
262
|
|
254
263
|
# Returns whether the subject attribute is a dependent whose owner does not automatically
|
@@ -300,23 +309,25 @@ module CaRuby
|
|
300
309
|
logical? or (dependent? and not creatable?)
|
301
310
|
end
|
302
311
|
|
303
|
-
# @return whether this attribute is saved in a update operation
|
304
|
-
#
|
305
312
|
# A Java attribute is updatable if all of the following conditions hold:
|
306
313
|
# * the attribute is {#saved?}
|
307
314
|
# * the attribute :create_only flag is not set
|
315
|
+
#
|
316
|
+
# @return [Boolean] whether this attribute is saved in a update operation
|
308
317
|
def updatable?
|
309
318
|
saved? and not @flags.include?(:create_only)
|
310
319
|
end
|
311
320
|
|
312
|
-
# @return whether
|
313
|
-
# A cascaded attribute determines where to prune a database create or update object graph.
|
314
|
-
#
|
315
|
-
# An attribute is cascaded if it is a physical dependent or the :cascaded flag is set.
|
321
|
+
# @return [Boolean] whether the attribute is a physical dependent or the +:cascaded+ flag is set
|
316
322
|
def cascaded?
|
317
323
|
(dependent? and not logical?) or @flags.include?(:cascaded)
|
318
324
|
end
|
319
325
|
|
326
|
+
# @return whether this attribute is {#cascaded?} or marked with the +:include_in_save_template+ flag
|
327
|
+
def include_in_save_template?
|
328
|
+
cascaded? or @flags.include?(:include_in_save_template)
|
329
|
+
end
|
330
|
+
|
320
331
|
# Returns whether this attribute is #{#cascaded} and cascades a parent update to a child
|
321
332
|
# create. This corresponds to the Hibernate +save-update+ cascade style but not the Hibernate
|
322
333
|
# +all+ cascade style.
|
@@ -327,6 +338,8 @@ module CaRuby
|
|
327
338
|
# Exception: gov.nih.nci.system.applicationservice.ApplicationException:
|
328
339
|
# The given object has a null identifier:
|
329
340
|
# followed by the attribute type name.
|
341
|
+
#
|
342
|
+
# @return [Boolean] whether the attribute cascades to crate when the owner is updated
|
330
343
|
def cascade_update_to_create?
|
331
344
|
cascaded? and not @flags.include?(:no_cascade_update_to_create)
|
332
345
|
end
|
@@ -412,12 +425,6 @@ module CaRuby
|
|
412
425
|
@flags.include?(:disjoint)
|
413
426
|
end
|
414
427
|
|
415
|
-
# @param [Symbol] attribute the attribute to check
|
416
|
-
# @return [Boolean] whether the attribute is part of a 1:1 non-dependency association
|
417
|
-
def one_to_one_bidirectional_independent?
|
418
|
-
independent? and not owner? and not collection? and @inv_md and not @inv_md.collection?
|
419
|
-
end
|
420
|
-
|
421
428
|
# @return [Boolean] whether this attribute is a dependent which does not have a Java inverse owner attribute
|
422
429
|
def unidirectional_java_dependent?
|
423
430
|
# TODO - can this be relaxed to java_unidirectional? i.e. eliminate dependent filter
|
@@ -453,8 +460,11 @@ module CaRuby
|
|
453
460
|
|
454
461
|
# If there is a current inverse which can be restricted to an attribute in the scope of
|
455
462
|
# this metadata's restricted type, then reset the inverse to that attribute.
|
456
|
-
|
457
|
-
|
463
|
+
#
|
464
|
+
# @param [AttributeMetadata, nil] inv_md the inverse attribute to restrict
|
465
|
+
def restrict_inverse_type(inv_md=nil)
|
466
|
+
# set the inverse, if necessary
|
467
|
+
@inv_md ||= inv_md || return
|
458
468
|
# the current inverse
|
459
469
|
attr = inverse
|
460
470
|
# the restricted type's metadata for the current inverse
|
@@ -55,6 +55,16 @@ module CaRuby
|
|
55
55
|
qualify(:collection) if collection_java_class?
|
56
56
|
end
|
57
57
|
|
58
|
+
# @return [Symbol] the JRuby wrapper method for the Java property reader
|
59
|
+
def property_reader
|
60
|
+
property_accessors.first
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Symbol] the JRuby wrapper method for the Java property writer
|
64
|
+
def property_writer
|
65
|
+
property_accessors.last
|
66
|
+
end
|
67
|
+
|
58
68
|
def type
|
59
69
|
@type ||= infer_type
|
60
70
|
end
|
@@ -90,12 +100,15 @@ module CaRuby
|
|
90
100
|
end
|
91
101
|
end
|
92
102
|
|
93
|
-
#
|
103
|
+
# @return [Boolean] whether this property's Java type is +Iterable+
|
94
104
|
def collection_java_class?
|
95
|
-
|
105
|
+
# the Java property type
|
106
|
+
ptype = @property_descriptor.property_type
|
107
|
+
# Test whether the corresponding JRuby wrapper class or module is an Iterable.
|
108
|
+
Class.to_ruby(ptype) < Java::JavaLang::Iterable
|
96
109
|
end
|
97
110
|
|
98
|
-
#
|
111
|
+
# @return [Class] the type for the specified klass property descriptor pd as described in {#initialize}
|
99
112
|
def infer_type
|
100
113
|
collection_java_class? ? infer_collection_type : infer_non_collection_type
|
101
114
|
end
|
@@ -103,40 +116,47 @@ module CaRuby
|
|
103
116
|
# Returns the domain type for this attribute's Java Collection property descriptor.
|
104
117
|
# If the property type is parameterized by a single domain class, then that generic type argument is the domain type.
|
105
118
|
# Otherwise, the type is inferred from the property name as described in {#infer_collection_type_from_name}.
|
119
|
+
#
|
120
|
+
# @return [Class] this property's Ruby type
|
106
121
|
def infer_collection_type
|
107
122
|
generic_parameter_type or infer_collection_type_from_name or Java::JavaLang::Object
|
108
123
|
end
|
109
124
|
|
125
|
+
# @return [Class] this property's Ruby type
|
110
126
|
def infer_non_collection_type
|
111
|
-
|
112
|
-
if
|
113
|
-
Class.to_ruby(
|
127
|
+
jtype = @property_descriptor.property_type
|
128
|
+
if jtype.primitive then
|
129
|
+
Class.to_ruby(jtype)
|
114
130
|
else
|
115
|
-
@declarer.domain_module.domain_type_with_name(
|
131
|
+
@declarer.domain_module.domain_type_with_name(jtype.name) or Class.to_ruby(jtype)
|
116
132
|
end
|
117
133
|
end
|
118
134
|
|
135
|
+
# @return [Class, nil] the Ruby type as determined by the configuration, if any
|
119
136
|
def configured_type
|
120
137
|
name = @declarer.class.configuration.domain_type_name(to_sym) || return
|
121
138
|
@declarer.domain_module.domain_type_with_name(name) or java_to_ruby_class(name)
|
122
139
|
end
|
123
140
|
|
124
|
-
#
|
141
|
+
# @return [Class, nil] the domain type of this attribute's property descriptor Collection generic
|
142
|
+
# type argument, or nil if none
|
125
143
|
def generic_parameter_type
|
126
144
|
method = @property_descriptor.readMethod || return
|
127
|
-
|
128
|
-
return unless Java::JavaLangReflect::ParameterizedType ===
|
129
|
-
|
130
|
-
return unless
|
131
|
-
|
132
|
-
klass = java_to_ruby_class(
|
133
|
-
logger.debug { "Inferred #{declarer.qp} #{self} domain type #{klass.qp} from generic parameter #{
|
145
|
+
gtype = method.genericReturnType
|
146
|
+
return unless Java::JavaLangReflect::ParameterizedType === gtype
|
147
|
+
atypes = gtype.actualTypeArguments
|
148
|
+
return unless atypes.size == 1
|
149
|
+
atype = atypes[0]
|
150
|
+
klass = java_to_ruby_class(atype)
|
151
|
+
logger.debug { "Inferred #{declarer.qp} #{self} domain type #{klass.qp} from generic parameter #{atype.name}." } if klass
|
134
152
|
klass
|
135
153
|
end
|
136
154
|
|
137
|
-
|
138
|
-
|
139
|
-
|
155
|
+
# @param [Class, String] jtype the Java class or class name
|
156
|
+
# @return [Class] the corresponding Ruby type
|
157
|
+
def java_to_ruby_class(jtype)
|
158
|
+
name = String === jtype ? jtype : jtype.name
|
159
|
+
@declarer.domain_module.domain_type_with_name(name) or Class.to_ruby(name)
|
140
160
|
end
|
141
161
|
|
142
162
|
# Returns the domain type for this attribute's collection Java property descriptor name.
|
@@ -148,13 +168,16 @@ module CaRuby
|
|
148
168
|
# is inferred as +DistributionProtocol+ by stripping the +Collection+ suffix,
|
149
169
|
# capitalizing the prefix and looking for a class of that name in this classifier's
|
150
170
|
# domain_module.
|
171
|
+
#
|
172
|
+
# @return [Class] the collection item type
|
151
173
|
def infer_collection_type_from_name
|
152
174
|
prop_name = @property_descriptor.name
|
153
175
|
index = prop_name =~ /Collection$/
|
154
176
|
index ||= prop_name.length
|
155
177
|
prefix = prop_name[0...1].upcase + prop_name[1...index]
|
156
|
-
|
157
|
-
|
178
|
+
klass = @declarer.domain_module.domain_type_with_name(prefix)
|
179
|
+
if klass then logger.debug { "Inferred #{declarer.qp} #{self} collection domain type #{klass.qp} from the attribute name." } end
|
180
|
+
klass
|
158
181
|
end
|
159
182
|
end
|
160
183
|
end
|
data/lib/caruby/domain/merge.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'caruby/util/validation'
|
2
|
+
|
1
3
|
module CaRuby
|
2
4
|
# A Mergeable supports merging {Resource} attribute values.
|
3
5
|
module Mergeable
|
@@ -74,7 +76,7 @@ module CaRuby
|
|
74
76
|
# @return the merged attribute value
|
75
77
|
def merge_attribute(attribute, newval, matches=nil)
|
76
78
|
# the previous value
|
77
|
-
oldval = send(attribute)
|
79
|
+
oldval = send(attribute)
|
78
80
|
# If nothing to merge or a block can take over, then bail.
|
79
81
|
if newval.nil? or mergeable__equal?(oldval, newval) then
|
80
82
|
return oldval
|
@@ -132,10 +134,12 @@ module CaRuby
|
|
132
134
|
if matches && matches.has_key?(src) then
|
133
135
|
# the source match
|
134
136
|
tgt = matches[src]
|
135
|
-
if
|
136
|
-
|
137
|
-
|
138
|
-
|
137
|
+
if tgt then
|
138
|
+
if oldval.include?(tgt) then
|
139
|
+
tgt.merge_attributes(src)
|
140
|
+
else
|
141
|
+
adds << tgt
|
142
|
+
end
|
139
143
|
end
|
140
144
|
else
|
141
145
|
adds << src
|
@@ -2,6 +2,7 @@ require 'enumerator'
|
|
2
2
|
require 'generator'
|
3
3
|
require 'caruby/util/options'
|
4
4
|
require 'caruby/util/collection'
|
5
|
+
require 'caruby/util/validation'
|
5
6
|
require 'caruby/util/visitor'
|
6
7
|
require 'caruby/util/math'
|
7
8
|
|
@@ -184,13 +185,13 @@ module CaRuby
|
|
184
185
|
# @yieldparam [Resource] source the visited source domain object
|
185
186
|
# @yieldparam [Resource] target the domain object which matches the visited source
|
186
187
|
def visit_matched(source)
|
187
|
-
|
188
|
+
tgt = match_for_visited(source)
|
188
189
|
# match the matchable references, if any
|
189
190
|
if @matchable then
|
190
191
|
attrs = @matchable.call(source) - attributes_to_visit(source)
|
191
|
-
attrs.each { |attr| match_reference(source,
|
192
|
+
attrs.each { |attr| match_reference(source, tgt, attr) }
|
192
193
|
end
|
193
|
-
block_given? ? yield(source,
|
194
|
+
block_given? ? yield(source, tgt) : tgt
|
194
195
|
end
|
195
196
|
|
196
197
|
# @param source (see #match_visited)
|
@@ -129,6 +129,8 @@ module CaRuby
|
|
129
129
|
@log_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.logical? }
|
130
130
|
end
|
131
131
|
|
132
|
+
# @return [<Symbol>] the unidirectional dependent attributes
|
133
|
+
# @see AttributeMetadata#unidirectional?
|
132
134
|
def unidirectional_dependent_attributes
|
133
135
|
@uni_dep_attrs ||= dependent_attributes.compose { |attr_md| attr_md.unidirectional? }
|
134
136
|
end
|
@@ -165,14 +167,19 @@ module CaRuby
|
|
165
167
|
|
166
168
|
# @return [<Symbol>] the {#cascaded_attributes} which are saved with a proxy
|
167
169
|
# using the dependent saver_proxy method
|
168
|
-
def
|
169
|
-
@px_cscd_attrs ||=
|
170
|
+
def proxied_save_template_attributes
|
171
|
+
@px_cscd_attrs ||= save_template_attributes.compose { |attr_md| attr_md.proxied_save? }
|
170
172
|
end
|
171
173
|
|
172
174
|
# @return [<Symbol>] the {#cascaded_attributes} which do not have a
|
173
175
|
# #{AttributeMetadata#proxied_save?}
|
174
|
-
def
|
175
|
-
@
|
176
|
+
def unproxied_save_template_attributes
|
177
|
+
@unpx_sv_tmpl_attrs ||= save_template_attributes.compose { |attr_md| not attr_md.proxied_save? }
|
178
|
+
end
|
179
|
+
|
180
|
+
# @return [<Symbol>] the {#domain_attributes} to {AttributeMetadata#include_in_save_template?}
|
181
|
+
def save_template_attributes
|
182
|
+
@sv_tmpl_attrs ||= domain_attributes.compose { |attr_md| attr_md.include_in_save_template? }
|
176
183
|
end
|
177
184
|
|
178
185
|
# Returns the physical or auto-generated logical dependent attributes that can
|
@@ -317,9 +324,9 @@ module CaRuby
|
|
317
324
|
# @param [{Symbol => AttributeMetadata}] hash the attribute symbol => metadata hash
|
318
325
|
# @yield [attr_md] condition which determines whether the attribute is selected
|
319
326
|
# @yieldparam [AttributeMetadata] the metadata for the standard attribute
|
320
|
-
def initialize(hash, &filter)
|
321
|
-
raise ArgumentError.new("
|
322
|
-
raise ArgumentError.new("
|
327
|
+
def initialize(klass, hash, &filter)
|
328
|
+
raise ArgumentError.new("#{klass.qp} attribute filter missing hash argument") if hash.nil?
|
329
|
+
raise ArgumentError.new("#{klass.qp} attribute filter missing filter block") unless block_given?
|
323
330
|
@hash = hash
|
324
331
|
@filter = filter
|
325
332
|
end
|
@@ -345,6 +352,14 @@ module CaRuby
|
|
345
352
|
each_pair { |attr, attr_md| yield(attr_md) }
|
346
353
|
end
|
347
354
|
|
355
|
+
# @yield [attribute] the block to apply to the attribute
|
356
|
+
# @yieldparam [Symbol] attribute the attribute
|
357
|
+
# @return [AttributeMetadata] the first attribute metadata satisfies the block
|
358
|
+
def detect_metadata
|
359
|
+
each_pair { |attr, attr_md| return attr_md if yield(attr) }
|
360
|
+
nil
|
361
|
+
end
|
362
|
+
|
348
363
|
# @yield [attr_md] the block to apply to the attribute metadata
|
349
364
|
# @yieldparam [AttributeMetadata] attr_md the attribute metadata
|
350
365
|
# @return [Symbol] the first attribute whose metadata satisfies the block
|
@@ -358,7 +373,7 @@ module CaRuby
|
|
358
373
|
# @return [Filter] a new Filter which applies the filter block given to this
|
359
374
|
# method with the AttributeMetadata enumerated by this filter
|
360
375
|
def compose
|
361
|
-
Filter.new(@hash) { |attr_md| @filter.call(attr_md) and yield(attr_md) }
|
376
|
+
Filter.new(self, @hash) { |attr_md| @filter.call(attr_md) and yield(attr_md) }
|
362
377
|
end
|
363
378
|
end
|
364
379
|
|
@@ -368,7 +383,7 @@ module CaRuby
|
|
368
383
|
# @yield [attr_md] the attribute selector
|
369
384
|
# @yieldparam [AttributeMetadata] attr_md the candidate attribute
|
370
385
|
def attribute_filter(&filter)
|
371
|
-
Filter.new(@attr_md_hash, &filter)
|
386
|
+
Filter.new(self, @attr_md_hash, &filter)
|
372
387
|
end
|
373
388
|
|
374
389
|
# Initializes the attribute meta-data structures.
|
@@ -382,10 +397,23 @@ module CaRuby
|
|
382
397
|
@local_defaults = {}
|
383
398
|
@defaults = append_ancestor_enum(@local_defaults) { |par| par.defaults }
|
384
399
|
end
|
400
|
+
|
401
|
+
|
402
|
+
# Creates the given attribute alias. Not that unlike {Class#alias_attribute}, this method creates a new
|
403
|
+
# alias reader (writer) method which delegates to the attribute reader (writer, resp.) rather than aliasing
|
404
|
+
# the existing reader or writer method. This allows the alias to pick up run-time redefinitions of the
|
405
|
+
# aliased reader and writer.
|
406
|
+
#
|
407
|
+
# @param [Symbol] aliaz the attribute alias
|
408
|
+
# @param [Symbol] attribute the attribute to alias
|
409
|
+
def alias_attribute(aliaz, attribute)
|
410
|
+
add_attribute_aliases(aliaz => attribute)
|
411
|
+
end
|
385
412
|
|
386
413
|
# Creates the given aliases to attributes.
|
387
414
|
#
|
388
415
|
# @param [{Symbol => Symbol}] hash the alias => attribute hash
|
416
|
+
# @see #attribute_alias
|
389
417
|
def add_attribute_aliases(hash)
|
390
418
|
hash.each { |aliaz, attr| delegate_to_attribute(aliaz, attr) }
|
391
419
|
end
|
@@ -414,10 +442,11 @@ module CaRuby
|
|
414
442
|
# Otherwise, if the attribute type is unspecified or is a superclass of the given class,
|
415
443
|
# then make a new attribute metadata for this class.
|
416
444
|
if attr_md.declarer == self then
|
445
|
+
logger.debug { "Set #{qp}.#{attribute} type to #{klass.qp}." }
|
417
446
|
attr_md.type = klass
|
418
447
|
elsif attr_md.type.nil? or klass < attr_md.type then
|
419
|
-
logger.debug { "Restricting #{attr_md.declarer.qp}.#{attribute}(#{attr_md.type.qp}) to #{qp} with return type #{klass.qp}..." }
|
420
448
|
new_attr_md = attr_md.restrict_type(self, klass)
|
449
|
+
logger.debug { "Restricted #{attr_md.declarer.qp}.#{attribute}(#{attr_md.type.qp}) to #{qp} with return type #{klass.qp}." }
|
421
450
|
add_attribute_metadata(new_attr_md)
|
422
451
|
elsif klass != attr_md.type then
|
423
452
|
raise ArgumentError.new("Cannot reset #{qp}.#{attribute} type #{attr_md.type} to incompatible #{klass.qp}")
|
@@ -481,7 +510,10 @@ module CaRuby
|
|
481
510
|
# @return [Enumerable] the {Enumerable#union} of the base collection with the superclass
|
482
511
|
# collection, if applicable
|
483
512
|
def append_ancestor_enum(enum)
|
484
|
-
|
513
|
+
return enum unless superclass < Resource
|
514
|
+
anc_enum = yield superclass
|
515
|
+
if anc_enum.nil? then raise MetadataError.new("#{qp} superclass #{superclass.qp} does not have required metadata") end
|
516
|
+
enum.union(anc_enum)
|
485
517
|
end
|
486
518
|
|
487
519
|
def each_attribute_metadata(&block)
|
@@ -508,12 +540,20 @@ module CaRuby
|
|
508
540
|
# add the secondary key
|
509
541
|
mandatory.merge(secondary_key_attributes)
|
510
542
|
# add the owner attribute, if any
|
511
|
-
|
543
|
+
oattr = mandatory_owner_attribute
|
544
|
+
mandatory << oattr if oattr
|
512
545
|
# remove autogenerated or optional attributes
|
513
546
|
mandatory.delete_if { |attr| attribute_metadata(attr).autogenerated? or attribute_metadata(attr).optional? }
|
514
547
|
@local_mndty_attrs.merge!(mandatory)
|
515
548
|
append_ancestor_enum(@local_mndty_attrs) { |par| par.mandatory_attributes }
|
516
549
|
end
|
550
|
+
|
551
|
+
# @return [Symbol, nil] the unique non-self-referential owner attribute, if one exists
|
552
|
+
def mandatory_owner_attribute
|
553
|
+
attr = owner_attribute || return
|
554
|
+
attr_md = attribute_metadata(attr)
|
555
|
+
attr if attr_md.java_property? and attr_md.type != self
|
556
|
+
end
|
517
557
|
|
518
558
|
# Raises a NameError. Domain classes can override this method to dynamically create a new reference attribute.
|
519
559
|
#
|