caruby-core 1.5.5 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/History.md +5 -1
- data/lib/caruby.rb +3 -5
- data/lib/caruby/caruby-src.tar.gz +0 -0
- data/lib/caruby/database.rb +53 -69
- data/lib/caruby/database/application_service.rb +25 -0
- data/lib/caruby/database/cache.rb +60 -0
- data/lib/caruby/database/fetched_matcher.rb +52 -38
- data/lib/caruby/database/lazy_loader.rb +4 -4
- data/lib/caruby/database/operation.rb +34 -0
- data/lib/caruby/database/persistable.rb +171 -86
- data/lib/caruby/database/persistence_service.rb +32 -34
- data/lib/caruby/database/persistifier.rb +100 -43
- data/lib/caruby/database/reader.rb +107 -85
- data/lib/caruby/database/reader_template_builder.rb +60 -0
- data/lib/caruby/database/saved_matcher.rb +3 -3
- data/lib/caruby/database/sql_executor.rb +88 -17
- data/lib/caruby/database/writer.rb +213 -177
- data/lib/caruby/database/writer_template_builder.rb +334 -0
- data/lib/caruby/{util → helpers}/controlled_value.rb +0 -0
- data/lib/caruby/{util → helpers}/coordinate.rb +4 -4
- data/lib/caruby/{util → helpers}/person.rb +3 -3
- data/lib/caruby/{util → helpers}/properties.rb +7 -9
- data/lib/caruby/{util → helpers}/roman.rb +2 -2
- data/lib/caruby/{util → helpers}/version.rb +1 -1
- data/lib/caruby/json/deserializer.rb +2 -2
- data/lib/caruby/json/serializer.rb +49 -7
- data/lib/caruby/metadata.rb +30 -0
- data/lib/caruby/metadata/java_property.rb +21 -0
- data/lib/caruby/metadata/propertied.rb +191 -0
- data/lib/caruby/metadata/property.rb +22 -0
- data/lib/caruby/metadata/property_characteristics.rb +201 -0
- data/lib/caruby/migration/migratable.rb +11 -182
- data/lib/caruby/rdbi/driver/jdbc.rb +446 -0
- data/lib/caruby/resource.rb +20 -823
- data/lib/caruby/version.rb +1 -1
- data/test/lib/caruby/database/cache_test.rb +54 -0
- data/test/lib/caruby/{util → helpers}/controlled_value_test.rb +3 -5
- data/test/lib/caruby/{util → helpers}/person_test.rb +4 -6
- data/test/lib/caruby/helpers/properties_test.rb +34 -0
- data/test/lib/caruby/{util → helpers}/roman_test.rb +2 -3
- data/test/lib/caruby/{util → helpers}/version_test.rb +2 -3
- data/test/lib/helper.rb +7 -0
- metadata +161 -214
- data/lib/caruby/cli/application.rb +0 -36
- data/lib/caruby/cli/command.rb +0 -202
- data/lib/caruby/csv/csv_mapper.rb +0 -159
- data/lib/caruby/csv/csvio.rb +0 -203
- data/lib/caruby/database/search_template_builder.rb +0 -56
- data/lib/caruby/database/store_template_builder.rb +0 -278
- data/lib/caruby/domain.rb +0 -193
- data/lib/caruby/domain/attribute.rb +0 -584
- data/lib/caruby/domain/attributes.rb +0 -628
- data/lib/caruby/domain/dependency.rb +0 -225
- data/lib/caruby/domain/id_alias.rb +0 -22
- data/lib/caruby/domain/importer.rb +0 -183
- data/lib/caruby/domain/introspection.rb +0 -176
- data/lib/caruby/domain/inverse.rb +0 -172
- data/lib/caruby/domain/inversible.rb +0 -90
- data/lib/caruby/domain/java_attribute.rb +0 -173
- data/lib/caruby/domain/merge.rb +0 -185
- data/lib/caruby/domain/metadata.rb +0 -142
- data/lib/caruby/domain/mixin.rb +0 -35
- data/lib/caruby/domain/properties.rb +0 -95
- data/lib/caruby/domain/reference_visitor.rb +0 -428
- data/lib/caruby/domain/uniquify.rb +0 -50
- data/lib/caruby/import/java.rb +0 -387
- data/lib/caruby/migration/migrator.rb +0 -918
- data/lib/caruby/migration/resource_module.rb +0 -9
- data/lib/caruby/migration/uniquify.rb +0 -17
- data/lib/caruby/util/attribute_path.rb +0 -44
- data/lib/caruby/util/cache.rb +0 -56
- data/lib/caruby/util/class.rb +0 -149
- data/lib/caruby/util/collection.rb +0 -1152
- data/lib/caruby/util/domain_extent.rb +0 -46
- data/lib/caruby/util/file_separator.rb +0 -65
- data/lib/caruby/util/inflector.rb +0 -27
- data/lib/caruby/util/log.rb +0 -95
- data/lib/caruby/util/math.rb +0 -12
- data/lib/caruby/util/merge.rb +0 -59
- data/lib/caruby/util/module.rb +0 -18
- data/lib/caruby/util/options.rb +0 -97
- data/lib/caruby/util/partial_order.rb +0 -35
- data/lib/caruby/util/pretty_print.rb +0 -204
- data/lib/caruby/util/stopwatch.rb +0 -74
- data/lib/caruby/util/topological_sync_enumerator.rb +0 -62
- data/lib/caruby/util/transitive_closure.rb +0 -55
- data/lib/caruby/util/tree.rb +0 -48
- data/lib/caruby/util/trie.rb +0 -37
- data/lib/caruby/util/uniquifier.rb +0 -30
- data/lib/caruby/util/validation.rb +0 -20
- data/lib/caruby/util/visitor.rb +0 -365
- data/lib/caruby/util/weak_hash.rb +0 -36
- data/test/lib/caruby/csv/csv_mapper_test.rb +0 -40
- data/test/lib/caruby/csv/csvio_test.rb +0 -69
- data/test/lib/caruby/database/persistable_test.rb +0 -92
- data/test/lib/caruby/domain/domain_test.rb +0 -112
- data/test/lib/caruby/domain/inversible_test.rb +0 -99
- data/test/lib/caruby/domain/reference_visitor_test.rb +0 -130
- data/test/lib/caruby/import/java_test.rb +0 -80
- data/test/lib/caruby/import/mixed_case_test.rb +0 -14
- data/test/lib/caruby/migration/test_case.rb +0 -102
- data/test/lib/caruby/test_case.rb +0 -230
- data/test/lib/caruby/util/cache_test.rb +0 -23
- data/test/lib/caruby/util/class_test.rb +0 -61
- data/test/lib/caruby/util/collection_test.rb +0 -398
- data/test/lib/caruby/util/command_test.rb +0 -55
- data/test/lib/caruby/util/domain_extent_test.rb +0 -60
- data/test/lib/caruby/util/file_separator_test.rb +0 -30
- data/test/lib/caruby/util/inflector_test.rb +0 -12
- data/test/lib/caruby/util/lazy_hash_test.rb +0 -34
- data/test/lib/caruby/util/merge_test.rb +0 -83
- data/test/lib/caruby/util/module_test.rb +0 -25
- data/test/lib/caruby/util/options_test.rb +0 -59
- data/test/lib/caruby/util/partial_order_test.rb +0 -42
- data/test/lib/caruby/util/pretty_print_test.rb +0 -85
- data/test/lib/caruby/util/properties_test.rb +0 -50
- data/test/lib/caruby/util/stopwatch_test.rb +0 -18
- data/test/lib/caruby/util/topological_sync_enumerator_test.rb +0 -69
- data/test/lib/caruby/util/transitive_closure_test.rb +0 -67
- data/test/lib/caruby/util/tree_test.rb +0 -23
- data/test/lib/caruby/util/trie_test.rb +0 -14
- data/test/lib/caruby/util/visitor_test.rb +0 -278
- data/test/lib/caruby/util/weak_hash_test.rb +0 -45
- data/test/lib/examples/clinical_trials/migration/migration_test.rb +0 -58
- data/test/lib/examples/clinical_trials/migration/test_case.rb +0 -38
@@ -1,584 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
require 'caruby/util/inflector'
|
3
|
-
require 'caruby/util/collection'
|
4
|
-
require 'caruby/util/validation'
|
5
|
-
require 'caruby/domain/java_attribute'
|
6
|
-
|
7
|
-
module CaRuby
|
8
|
-
module Domain
|
9
|
-
# An Attribute captures the following metadata about a domain class attribute:
|
10
|
-
# * attribute symbol
|
11
|
-
# * declarer type
|
12
|
-
# * return type
|
13
|
-
# * reader method symbol
|
14
|
-
# * writer method symbol
|
15
|
-
class Attribute
|
16
|
-
# The supported attribute qualifier flags. See the complementary methods for an explanation of
|
17
|
-
# the flag option, e.g. {#autogenerated?} for the +:autogenerated+ flag.
|
18
|
-
SUPPORTED_FLAGS = [
|
19
|
-
:autogenerated, :autogenerated_on_update, :collection, :dependent, :derived, :logical, :disjoint,
|
20
|
-
:owner, :cascaded, :no_cascade_update_to_create, :saved, :unsaved, :optional, :fetched, :unfetched,
|
21
|
-
:include_in_save_template, :saved_fetch, :create_only, :update_only, :unidirectional, :volatile].to_set
|
22
|
-
|
23
|
-
# @return [(Symbol, Symbol)] the standard attribute reader and writer methods
|
24
|
-
attr_reader :accessors
|
25
|
-
|
26
|
-
# @return [Class] the declaring class
|
27
|
-
attr_accessor :declarer
|
28
|
-
|
29
|
-
# @return [Class] the return type
|
30
|
-
attr_reader :type
|
31
|
-
|
32
|
-
# @return [<Symbol>] the qualifier flags
|
33
|
-
# @see SUPPORTED_FLAGS
|
34
|
-
attr_accessor :flags
|
35
|
-
|
36
|
-
# Creates a new Attribute from the given attribute.
|
37
|
-
#
|
38
|
-
# The return type is the referenced entity type. An attribute whose return type is a
|
39
|
-
# collection of domain objects is thus the domain object class rather than a collection class.
|
40
|
-
#
|
41
|
-
# @param [String,Symbol] attr the subject attribute
|
42
|
-
# @param [Class] declarer the declaring class
|
43
|
-
# @param [Class] type the return type
|
44
|
-
# @param [<Symbol>] flags the qualifying flags
|
45
|
-
# @option flags :dependent the attribute references a dependent
|
46
|
-
# @option flags :collection the attribute return type is a collection
|
47
|
-
# @option flags :owner the attribute references the owner of a dependent
|
48
|
-
# @option flags :cascaded database create/update/delete operation propagates to the attribute reference
|
49
|
-
def initialize(attribute, declarer, type=nil, *flags)
|
50
|
-
# the attribute symbol
|
51
|
-
@symbol = attribute.to_sym
|
52
|
-
# the declaring class
|
53
|
-
@declarer = declarer
|
54
|
-
# the Ruby class
|
55
|
-
@type = Class.to_ruby(type) if type
|
56
|
-
# the read and write methods
|
57
|
-
@accessors = [@symbol, "#{attribute}=".to_sym]
|
58
|
-
# the qualifier flags
|
59
|
-
@flags = Set.new
|
60
|
-
# identifier is always volatile
|
61
|
-
if @symbol == :identifier then flags << :volatile end
|
62
|
-
qualify(*flags)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @return [Symbol] the reader method
|
66
|
-
def reader
|
67
|
-
accessors.first
|
68
|
-
end
|
69
|
-
|
70
|
-
# @return [Symbol] the writer method
|
71
|
-
def writer
|
72
|
-
accessors.last
|
73
|
-
end
|
74
|
-
|
75
|
-
# @return [Symbol, nil] the inverse of this attribute, if any
|
76
|
-
def inverse
|
77
|
-
@inv_md.to_sym if @inv_md
|
78
|
-
end
|
79
|
-
|
80
|
-
# An attribute is unidirectional if both of the following is true:
|
81
|
-
# * there is no distinct {#inverse} attribute
|
82
|
-
# * the attribute is not a {#dependent?} with more than one owner
|
83
|
-
#
|
84
|
-
# @return [Boolean] whether this attribute does not have an inverse
|
85
|
-
def unidirectional?
|
86
|
-
inverse.nil? and not (dependent? and type.owner_attributes.size > 1)
|
87
|
-
end
|
88
|
-
|
89
|
-
# @param [Class] the attribute return type
|
90
|
-
def type=(klass)
|
91
|
-
return if klass == @type
|
92
|
-
@type = klass
|
93
|
-
if @inv_md then
|
94
|
-
self.inverse = @inv_md.to_sym
|
95
|
-
logger.debug { "Reset #{@declarer.qp}.#{self} inverse from #{@inv_md.type}.#{@inv_md} to #{klass}#{@inv_md}." }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Creates a new declarer attribute which restricts this attribute {#type} to the given type.
|
100
|
-
#
|
101
|
-
# @param declarer (see #restrict)
|
102
|
-
# @param [Class] type the restricted subclass of this attribute's return type
|
103
|
-
# @return (see #restrict)
|
104
|
-
def restrict_type(declarer, type)
|
105
|
-
if self.type and not type < self.type then
|
106
|
-
raise ArgumentError.new("Cannot restrict #{self.declarer.qp}.#{self} to incompatible attribute type #{type.qp}")
|
107
|
-
end
|
108
|
-
rst = restrict(declarer)
|
109
|
-
rst.type = type
|
110
|
-
# specialize the inverse to the restricted type attribute, if necessary
|
111
|
-
rst.restrict_inverse_type
|
112
|
-
rst
|
113
|
-
end
|
114
|
-
|
115
|
-
# Creates a new declarer attribute which qualifies this attribute for the given declarer.
|
116
|
-
#
|
117
|
-
# @param declarer (see #restrict)
|
118
|
-
# @param [<Symbol>] flags the additional flags for the restricted attribute
|
119
|
-
# @return (see #restrict)
|
120
|
-
def restrict_flags(declarer, *flags)
|
121
|
-
copy = restrict(declarer)
|
122
|
-
copy.qualify(*flags)
|
123
|
-
copy
|
124
|
-
end
|
125
|
-
|
126
|
-
# Sets the inverse of the subject attribute to the given attribute.
|
127
|
-
# The inverse relation is symmetric, i.e. the inverse of the referenced Attribute
|
128
|
-
# is set to this Attribute's subject attribute.
|
129
|
-
#
|
130
|
-
# @param [Symbol, nil] attribute the inverse attribute
|
131
|
-
def inverse=(attribute)
|
132
|
-
return if inverse == attribute
|
133
|
-
# if no attribute, then the clear the existing inverse, if any
|
134
|
-
return clear_inverse if attribute.nil?
|
135
|
-
|
136
|
-
# the inverse attribute meta-data
|
137
|
-
begin
|
138
|
-
@inv_md = type.attribute_metadata(attribute)
|
139
|
-
rescue NameError
|
140
|
-
raise MetadataError.new("#{@declarer.qp}.#{self} inverse attribute #{type.qp}.#{attribute} not found - #{$!}")
|
141
|
-
end
|
142
|
-
# the inverse of the inverse
|
143
|
-
inv_inv_md = @inv_md.inverse_metadata
|
144
|
-
# If the inverse of the inverse is already set to a different attribute, then raise an exception.
|
145
|
-
# Otherwise, it there is an inverse, then set the inverse of the inverse to this attribute.
|
146
|
-
return if inv_inv_md == self
|
147
|
-
if inv_inv_md and not inv_inv_md.restriction?(self) then
|
148
|
-
raise MetadataError.new("Cannot set #{type.qp}.#{@inv_md} inverse attribute to #{@declarer.qp}.#{self} since it conflicts with existing inverse #{inv_inv_md.declarer.qp}.#{inv_inv_md}")
|
149
|
-
end
|
150
|
-
# set the inverse of the inverse
|
151
|
-
@inv_md.inverse = @symbol
|
152
|
-
# If this attribute is disjoint, then so is the inverse.
|
153
|
-
@inv_md.qualify(:disjoint) if disjoint?
|
154
|
-
|
155
|
-
# propagate to restrictions
|
156
|
-
# if @restrictions then
|
157
|
-
# @restrictions.each { |attr_md| attr_md.restrict_inverse_type(@inv_md) }
|
158
|
-
# end
|
159
|
-
logger.debug { "Set #{@declarer.qp}.#{self} inverse to #{type.qp}.#{attribute}." }
|
160
|
-
end
|
161
|
-
|
162
|
-
# @return [Attribute, nil] the metadata for the {#inverse} attribute, if any
|
163
|
-
def inverse_metadata
|
164
|
-
@inv_md
|
165
|
-
end
|
166
|
-
|
167
|
-
# Qualifies this attribute with the given flags. Supported flags are listed in {SUPPORTED_FLAGS}.
|
168
|
-
#
|
169
|
-
# @param [<Symbol>] the flags to add
|
170
|
-
# @raise [ArgumentError] if the flag is not supported
|
171
|
-
def qualify(*flags)
|
172
|
-
flags.each { |flag| set_flag(flag) }
|
173
|
-
# propagate to restrictions
|
174
|
-
if @restrictions then @restrictions.each { |attr_md| attr_md.qualify(*flags) } end
|
175
|
-
end
|
176
|
-
|
177
|
-
# @return whether the subject attribute encapsulates a Java property
|
178
|
-
def java_property?
|
179
|
-
JavaAttribute === self
|
180
|
-
end
|
181
|
-
|
182
|
-
# @return whether the subject attribute returns a domain object or collection of domain objects
|
183
|
-
def domain?
|
184
|
-
# the type must be a Ruby class rather than a Java Class, and include the Domain mix-in
|
185
|
-
Class === type and type < Resource
|
186
|
-
end
|
187
|
-
|
188
|
-
# @return whether the subject attribute is not a domain object attribute
|
189
|
-
def nondomain?
|
190
|
-
not domain?
|
191
|
-
end
|
192
|
-
|
193
|
-
# Returns whether the subject attribute is fetched, determined as follows:
|
194
|
-
# * An attribute marked with the :fetched flag is fetched.
|
195
|
-
# * An attribute marked with the :unfetched flag is not fetched.
|
196
|
-
# Otherwise, a non-domain attribute is fetched, and a domain attribute is
|
197
|
-
# fetched if one of the following conditions hold:
|
198
|
-
# * A dependent domain attribute is fetched if it is not logical.
|
199
|
-
# * An owner domain attribute is fetched by default.
|
200
|
-
# * An independent domain attribute is fetched if it is abstract and not derived.
|
201
|
-
#
|
202
|
-
# @return [Boolean] whether the attribute is fetched
|
203
|
-
def fetched?
|
204
|
-
return true if @flags.include?(:fetched)
|
205
|
-
return false if @flags.include?(:unfetched)
|
206
|
-
nondomain? or dependent? ? fetched_dependent? : fetched_independent?
|
207
|
-
end
|
208
|
-
|
209
|
-
# @return whether the subject attribute return type is a collection
|
210
|
-
def collection?
|
211
|
-
@flags.include?(:collection)
|
212
|
-
end
|
213
|
-
|
214
|
-
# Returns whether the subject attribute is a dependent on a parent. See the caRuby configuration
|
215
|
-
# documentation for a dependency description.
|
216
|
-
#
|
217
|
-
# @return [Boolean] whether the attribute references a dependent
|
218
|
-
def dependent?
|
219
|
-
@flags.include?(:dependent)
|
220
|
-
end
|
221
|
-
|
222
|
-
# Returns whether the subject attribute is marked as optional in a create.
|
223
|
-
# This method returns true only if the :optional flag is explicitly set.
|
224
|
-
# Other attributes are optional by default.
|
225
|
-
#
|
226
|
-
# @return [Boolean] whether the attribute is optional
|
227
|
-
# @see Attributes#mandatory_attributes.
|
228
|
-
def optional?
|
229
|
-
@flags.include?(:optional)
|
230
|
-
end
|
231
|
-
|
232
|
-
# Returns whether the subject attribute is not saved.
|
233
|
-
#
|
234
|
-
# @return [Boolean] whether the attribute is unsaved
|
235
|
-
def unsaved?
|
236
|
-
@flags.include?(:unsaved)
|
237
|
-
end
|
238
|
-
|
239
|
-
# Returns whether the subject attribute is a dependent whose value is automatically generated
|
240
|
-
# with place-holder domain objects when the parent is created. An attribute is auto-generated
|
241
|
-
# if the +:autogenerate+ or the +:autogenerated_on_update+ flag is set.
|
242
|
-
#
|
243
|
-
# @return [Boolean] whether the attribute is auto-generated
|
244
|
-
def autogenerated?
|
245
|
-
@flags.include?(:autogenerated) or @flags.include?(:autogenerated_on_update)
|
246
|
-
end
|
247
|
-
|
248
|
-
# Returns whether the the subject attribute is {#autogenerated?} for create. An attribute is
|
249
|
-
# auto-generated for create if the +:autogenerate+ flag is set and the
|
250
|
-
# +:autogenerated_on_update+ flag is not set.
|
251
|
-
#
|
252
|
-
# @return [Boolean] whether the attribute is auto-generated on create
|
253
|
-
def autogenerated_on_create?
|
254
|
-
@flags.include?(:autogenerated) and not @flags.include?(:autogenerated_on_update)
|
255
|
-
end
|
256
|
-
|
257
|
-
# Returns whether this attribute must be fetched when a declarer instance is saved.
|
258
|
-
# An attribute is a saved fetch attribute if either of the following conditions hold:
|
259
|
-
# * it is {#autogenerated?}
|
260
|
-
# * it is {#cascaded?} and marked with the +:unfetched+ flag.
|
261
|
-
#
|
262
|
-
# @return [Boolean] whether the subject attribute must be refetched in order to reflect
|
263
|
-
# the database content
|
264
|
-
def saved_fetch?
|
265
|
-
@flags.include?(:saved_fetch) or autogenerated? or (cascaded? and @flags.include?(:unfetched))
|
266
|
-
end
|
267
|
-
|
268
|
-
# Returns whether the subject attribute is a dependent whose owner does not automatically
|
269
|
-
# cascade application service creation or update to the dependent. It is incumbent upon
|
270
|
-
# CaRuby::Database to cascade the changes.
|
271
|
-
#
|
272
|
-
# @return [Boolean] whether the attribute is an uncascaded dependent
|
273
|
-
def logical?
|
274
|
-
@flags.include?(:logical)
|
275
|
-
end
|
276
|
-
|
277
|
-
# An attribute is derived if the attribute value is set by setting another attribute, e.g. if this
|
278
|
-
# attribute is the inverse of a dependent owner attribute.
|
279
|
-
#
|
280
|
-
# @return [Boolean] whether this attribute is derived from another attribute
|
281
|
-
def derived?
|
282
|
-
@flags.include?(:derived) or (dependent? and not inverse.nil?)
|
283
|
-
end
|
284
|
-
|
285
|
-
# @return [Boolean] this attribute's inverse attribute if the inverse is a derived attribute, or nil otherwise
|
286
|
-
def derived_inverse
|
287
|
-
@inv_md.to_sym if @inv_md and @inv_md.derived?
|
288
|
-
end
|
289
|
-
|
290
|
-
# An independent attribute is a reference to one or more non-dependent Resource objects.
|
291
|
-
# An {#owner?} attribute is independent.
|
292
|
-
#
|
293
|
-
# @return [Boolean] whether the subject attribute is a non-dependent domain attribute
|
294
|
-
def independent?
|
295
|
-
domain? and not dependent?
|
296
|
-
end
|
297
|
-
|
298
|
-
# A Java attribute is creatable if all of the following conditions hold:
|
299
|
-
# * the attribute is {#saved?}
|
300
|
-
# * the attribute is not a {#proxied_save?}
|
301
|
-
# * the attribute :update_only flag is not set
|
302
|
-
#
|
303
|
-
# @return [Boolean] whether this attribute is saved in a create operation
|
304
|
-
def creatable?
|
305
|
-
saved? and not @flags.include?(:update_only)
|
306
|
-
end
|
307
|
-
|
308
|
-
# A Java attribute is an uncreated dependent if any of the following conditions hold:
|
309
|
-
# * the attribute is a {#logical?} dependent
|
310
|
-
# * the attribute is a {#dependent?} which is not {#creatable?}
|
311
|
-
#
|
312
|
-
# @return [Boolean] whether this attribute is saved in a create operation
|
313
|
-
def uncreated_dependent?
|
314
|
-
logical? or (dependent? and not creatable?)
|
315
|
-
end
|
316
|
-
|
317
|
-
# A Java attribute is updatable if all of the following conditions hold:
|
318
|
-
# * the attribute is {#saved?}
|
319
|
-
# * the attribute :create_only flag is not set
|
320
|
-
#
|
321
|
-
# @return [Boolean] whether this attribute is saved in a update operation
|
322
|
-
def updatable?
|
323
|
-
saved? and not @flags.include?(:create_only)
|
324
|
-
end
|
325
|
-
|
326
|
-
# @return [Boolean] whether the attribute is a physical dependent or the +:cascaded+ flag is set
|
327
|
-
def cascaded?
|
328
|
-
(dependent? and not logical?) or @flags.include?(:cascaded)
|
329
|
-
end
|
330
|
-
|
331
|
-
# @return whether this attribute is {#cascaded?} or marked with the +:include_in_save_template+ flag
|
332
|
-
def include_in_save_template?
|
333
|
-
cascaded? or @flags.include?(:include_in_save_template)
|
334
|
-
end
|
335
|
-
|
336
|
-
# Returns whether this attribute is #{#cascaded?} and cascades a parent update to a child
|
337
|
-
# create. This corresponds to the Hibernate +save-update+ cascade style but not the Hibernate
|
338
|
-
# +all+ cascade style.
|
339
|
-
#
|
340
|
-
# This method returns true if this attribute is cascaded and the +:no_cascade_update_to_create+
|
341
|
-
# flag is not set. Set this flag if the Hibernate mapping specifies the +all+ cascade style.
|
342
|
-
# Failure to set this flag will result in the caTissue Hibernate error:
|
343
|
-
# Exception: gov.nih.nci.system.applicationservice.ApplicationException:
|
344
|
-
# The given object has a null identifier:
|
345
|
-
# followed by the attribute type name.
|
346
|
-
#
|
347
|
-
# @return [Boolean] whether the attribute cascades to crate when the owner is updated
|
348
|
-
def cascade_update_to_create?
|
349
|
-
cascaded? and not @flags.include?(:no_cascade_update_to_create)
|
350
|
-
end
|
351
|
-
|
352
|
-
# A Java property attribute is saved if none of the following conditions hold:
|
353
|
-
# * the attribute :unsaved flag is set
|
354
|
-
# * the attribute is {#proxied_save?}
|
355
|
-
# and any of the following conditions hold:
|
356
|
-
# * the attibute is {#nondomain?}
|
357
|
-
# * the attribute is cascaded
|
358
|
-
# * the attribute value is not a collection
|
359
|
-
# * the attribute does not have an inverse
|
360
|
-
# * the attribute :saved flag is set
|
361
|
-
#
|
362
|
-
# @return [Boolean] whether this attribute is saved in a create or update operation
|
363
|
-
def saved?
|
364
|
-
@flags.include?(:saved) or
|
365
|
-
(java_property? and not @flags.include?(:unsaved) and not proxied_save? and
|
366
|
-
(nondomain? or cascaded? or not collection? or inverse.nil? or unidirectional_java_dependent?))
|
367
|
-
end
|
368
|
-
|
369
|
-
# @return [Boolean] whether this attribute is not {#saved?}
|
370
|
-
def unsaved?
|
371
|
-
not saved?
|
372
|
-
end
|
373
|
-
|
374
|
-
# @return [Boolean] whether the attribute return {#type} is a Resource class which
|
375
|
-
# implements the saver_proxy method
|
376
|
-
def proxied_save?
|
377
|
-
domain? and type.method_defined?(:saver_proxy)
|
378
|
-
end
|
379
|
-
|
380
|
-
# Returns whether this attribute's referents must exist before an instance of the
|
381
|
-
# declarer class can be created. An attribute is a storable prerequisite if it is
|
382
|
-
# either:
|
383
|
-
# * a {#cascaded?} dependent which does not #{#cascade_update_to_create?}, or
|
384
|
-
# * a {#saved?} {#independent?} 1:M or M:N association.
|
385
|
-
#
|
386
|
-
# @return [Boolean] whether this attribute is a create prerequisite
|
387
|
-
def storable_prerequisite?
|
388
|
-
return true if cascaded? and @flags.include?(:no_cascade_update_to_create)
|
389
|
-
return false unless independent? and saved?
|
390
|
-
return true unless collection?
|
391
|
-
inv_md = inverse_metadata
|
392
|
-
inv_md.nil? or inv_md.collection?
|
393
|
-
end
|
394
|
-
|
395
|
-
# @return [Boolean] whether this attribute is a collection with a collection inverse
|
396
|
-
def many_to_many?
|
397
|
-
return false unless collection?
|
398
|
-
inv_md = inverse_metadata
|
399
|
-
inv_md and inv_md.collection?
|
400
|
-
end
|
401
|
-
|
402
|
-
# @return [Boolean] whether the subject attribute is not saved
|
403
|
-
def transient?
|
404
|
-
not saved?
|
405
|
-
end
|
406
|
-
|
407
|
-
# Returns whether this attribute is set on the server as a side-effect
|
408
|
-
# of a change to the declarer object. The volatile attributes include
|
409
|
-
# those which are {#unsaved?} and those which are saved but marked
|
410
|
-
# with the +:volatile+ flag.
|
411
|
-
#
|
412
|
-
# @return [Boolean] whether this attribute's value is determined by the server
|
413
|
-
def volatile?
|
414
|
-
unsaved? or @flags.include?(:volatile)
|
415
|
-
end
|
416
|
-
|
417
|
-
# @return [Boolean] whether this is a non-collection Java attribute
|
418
|
-
def searchable?
|
419
|
-
java_property? and not collection?
|
420
|
-
end
|
421
|
-
|
422
|
-
# @return [Boolean] whether the subject attribute is a dependency owner
|
423
|
-
def owner?
|
424
|
-
@flags.include?(:owner)
|
425
|
-
end
|
426
|
-
|
427
|
-
# @return [Boolean] whether this is a dependent attribute which has exactly one owner value chosen from
|
428
|
-
# several owner attributes.
|
429
|
-
def disjoint?
|
430
|
-
@flags.include?(:disjoint)
|
431
|
-
end
|
432
|
-
|
433
|
-
# @return [Boolean] whether this attribute is a dependent which does not have a Java inverse owner attribute
|
434
|
-
def unidirectional_java_dependent?
|
435
|
-
dependent? and java_property? and not bidirectional_java_association?
|
436
|
-
end
|
437
|
-
|
438
|
-
# @return [Boolean] whether this is a Java attribute which has a Java inverse
|
439
|
-
def bidirectional_java_association?
|
440
|
-
inverse and java_property? and inverse_metadata.java_property?
|
441
|
-
end
|
442
|
-
|
443
|
-
def to_sym
|
444
|
-
@symbol
|
445
|
-
end
|
446
|
-
|
447
|
-
def to_s
|
448
|
-
@symbol.to_s
|
449
|
-
end
|
450
|
-
|
451
|
-
alias :inspect :to_s
|
452
|
-
|
453
|
-
alias :qp :to_s
|
454
|
-
|
455
|
-
protected
|
456
|
-
|
457
|
-
# Duplicates the mutable content as part of a {#deep_copy}.
|
458
|
-
def dup_content
|
459
|
-
# keep the copied flags but don't share them
|
460
|
-
@flags = @flags.dup
|
461
|
-
# restrictions are neither shared nor copied
|
462
|
-
@restrictions = nil
|
463
|
-
end
|
464
|
-
|
465
|
-
# Restricts this attribute's inverse to an attribute declared by this
|
466
|
-
# attribute's type. For example, if:
|
467
|
-
# * +AbstractProtocol.coordinator+ has inverse +Administrator.protocol+
|
468
|
-
# * +AbstractProtocol+ has subclass +StudyProtocol+
|
469
|
-
# * +StudyProtocol.coordinator+ returns a +StudyCoordinator+
|
470
|
-
# * +AbstractProtocol.coordinator+ is restricted to +StudyProtocol+
|
471
|
-
# then calling this method on the +StudyProtocol.coordinator+ restriction
|
472
|
-
# sets the +StudyProtocol.coordinator+ inverse to +StudyCoordinator.coordinator+.
|
473
|
-
#
|
474
|
-
# @param [Attribute, nil] inv_md the inverse attribute to restrict
|
475
|
-
# (default is the current inverse)
|
476
|
-
def restrict_inverse_type(inv_md=nil)
|
477
|
-
# default inverse is the current inverse
|
478
|
-
inv_md ||= @inv_md || return
|
479
|
-
# the current inverse
|
480
|
-
attr = inv_md.to_sym
|
481
|
-
# If the restricted type delegates to the current inverse metadata,
|
482
|
-
# then no change is needed.
|
483
|
-
return if @type.attribute_metadata(attr) == inv_md
|
484
|
-
# clear the current inverse
|
485
|
-
@inv_md = nil
|
486
|
-
# set the inverse to the restricted attribute
|
487
|
-
self.inverse = attr
|
488
|
-
end
|
489
|
-
|
490
|
-
# @param [Attribute] other the other attribute to check
|
491
|
-
# @return [Boolean] whether the other attribute restricts this attribute
|
492
|
-
def restriction?(other)
|
493
|
-
@restrictions.include?(other)
|
494
|
-
end
|
495
|
-
private
|
496
|
-
|
497
|
-
# Creates a copy of this metadata which does not share mutable content.
|
498
|
-
def deep_copy
|
499
|
-
other = dup
|
500
|
-
other.dup_content
|
501
|
-
other
|
502
|
-
end
|
503
|
-
|
504
|
-
# Creates a new declarer attribute which restricts this attribute type or flags.
|
505
|
-
#
|
506
|
-
# @param [Class] klass the declarer class for which the restriction holds
|
507
|
-
# @return [Attribute] the metadata for the new declarer attribute
|
508
|
-
def restrict(klass)
|
509
|
-
unless klass < @declarer then
|
510
|
-
raise ArgumentError.new("Cannot restrict #{@declarer.qp}.#{self} to incompatible declarer type #{klass.qp}")
|
511
|
-
end
|
512
|
-
rst = deep_copy
|
513
|
-
# specialize the copy declarer and type
|
514
|
-
rst.declarer = klass
|
515
|
-
# Capture the restriction to propagate modifications to this metadata, esp.
|
516
|
-
# adding an inverse.
|
517
|
-
@restrictions ||= []
|
518
|
-
@restrictions << rst
|
519
|
-
rst
|
520
|
-
end
|
521
|
-
|
522
|
-
def clear_inverse
|
523
|
-
return unless @inv_md
|
524
|
-
logger.debug { "Clearing #{@declarer.qp}.#{self} inverse #{type.qp}.#{inverse}..." }
|
525
|
-
# Capture the inverse before unsetting it.
|
526
|
-
inv_md = @inv_md
|
527
|
-
# Unset the inverse.
|
528
|
-
@inv_md = nil
|
529
|
-
# Clear the inverse of the inverse.
|
530
|
-
inv_md.inverse = nil
|
531
|
-
logger.debug { "Cleared #{@declarer.qp}.#{self} inverse." }
|
532
|
-
end
|
533
|
-
|
534
|
-
# @param [Symbol] the flag to set
|
535
|
-
# @raise [ArgumentError] if flag is not supported
|
536
|
-
def set_flag(flag)
|
537
|
-
return if @flags.include?(flag)
|
538
|
-
raise ArgumentError.new("Attribute flag not supported: #{flag}") unless SUPPORTED_FLAGS.include?(flag)
|
539
|
-
@flags << flag
|
540
|
-
case flag
|
541
|
-
when :owner then owner_flag_set
|
542
|
-
when :dependent then dependent_flag_set
|
543
|
-
end
|
544
|
-
end
|
545
|
-
|
546
|
-
# This method is called when the owner flag is set.
|
547
|
-
# The inverse is inferred as the referenced owner type's dependent attribute which references
|
548
|
-
# this attribute's type.
|
549
|
-
#
|
550
|
-
# @raise [MetadataError] if this attribute is dependent or an inverse could not be inferred
|
551
|
-
def owner_flag_set
|
552
|
-
if dependent? then
|
553
|
-
raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} owner since it is already defined as a #{type.qp} dependent")
|
554
|
-
end
|
555
|
-
inv_attr = type.dependent_attribute(@declarer)
|
556
|
-
if inv_attr.nil? then
|
557
|
-
raise MetadataError.new("#{@declarer.qp} owner attribute #{self} does not have a #{type.qp} dependent inverse")
|
558
|
-
end
|
559
|
-
self.inverse = type.dependent_attribute(@declarer)
|
560
|
-
if inverse_metadata.logical? then @flags << :logical end
|
561
|
-
end
|
562
|
-
|
563
|
-
# Validates that this is not an owner attribute.
|
564
|
-
#
|
565
|
-
# @raise [MetadataError] if this is an owner attribute
|
566
|
-
def dependent_flag_set
|
567
|
-
if owner? then
|
568
|
-
raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} dependent since it is already defined as a #{type.qp} owner")
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
# @return [Boolean] whether this dependent attribute is fetched. Only physical dependents are fetched by default.
|
573
|
-
def fetched_dependent?
|
574
|
-
not (logical? or @flags.include?(:unfetched))
|
575
|
-
end
|
576
|
-
|
577
|
-
# @return [Boolean] whether this independent attribute is fetched. Only abstract, non-derived independent
|
578
|
-
# references are fetched by default.
|
579
|
-
def fetched_independent?
|
580
|
-
type.abstract? and not (derived? or @flags.include?(:unfetched))
|
581
|
-
end
|
582
|
-
end
|
583
|
-
end
|
584
|
-
end
|