caruby-core 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +51 -0
- data/doc/website/css/site.css +1 -5
- data/doc/website/images/avatar.png +0 -0
- data/doc/website/images/favicon.ico +0 -0
- data/doc/website/images/logo.png +0 -0
- data/doc/website/index.html +82 -0
- data/doc/website/install.html +87 -0
- data/doc/website/quick_start.html +87 -0
- data/doc/website/tissue.html +85 -0
- data/doc/website/uom.html +10 -0
- data/lib/caruby.rb +3 -0
- data/lib/caruby/active_support/README.txt +2 -0
- data/lib/caruby/active_support/core_ext/string.rb +7 -0
- data/lib/caruby/active_support/core_ext/string/inflections.rb +167 -0
- data/lib/caruby/active_support/inflections.rb +55 -0
- data/lib/caruby/active_support/inflector.rb +398 -0
- data/lib/caruby/cli/application.rb +36 -0
- data/lib/caruby/cli/command.rb +169 -0
- data/lib/caruby/csv/csv_mapper.rb +157 -0
- data/lib/caruby/csv/csvio.rb +185 -0
- data/lib/caruby/database.rb +252 -0
- data/lib/caruby/database/fetched_matcher.rb +66 -0
- data/lib/caruby/database/persistable.rb +432 -0
- data/lib/caruby/database/persistence_service.rb +162 -0
- data/lib/caruby/database/reader.rb +599 -0
- data/lib/caruby/database/saved_merger.rb +131 -0
- data/lib/caruby/database/search_template_builder.rb +59 -0
- data/lib/caruby/database/sql_executor.rb +75 -0
- data/lib/caruby/database/store_template_builder.rb +200 -0
- data/lib/caruby/database/writer.rb +469 -0
- data/lib/caruby/domain/annotatable.rb +25 -0
- data/lib/caruby/domain/annotation.rb +23 -0
- data/lib/caruby/domain/attribute_metadata.rb +447 -0
- data/lib/caruby/domain/java_attribute_metadata.rb +160 -0
- data/lib/caruby/domain/merge.rb +91 -0
- data/lib/caruby/domain/properties.rb +95 -0
- data/lib/caruby/domain/reference_visitor.rb +289 -0
- data/lib/caruby/domain/resource_attributes.rb +528 -0
- data/lib/caruby/domain/resource_dependency.rb +205 -0
- data/lib/caruby/domain/resource_introspection.rb +159 -0
- data/lib/caruby/domain/resource_metadata.rb +117 -0
- data/lib/caruby/domain/resource_module.rb +285 -0
- data/lib/caruby/domain/uniquify.rb +38 -0
- data/lib/caruby/import/annotatable_class.rb +28 -0
- data/lib/caruby/import/annotation_class.rb +27 -0
- data/lib/caruby/import/annotation_module.rb +67 -0
- data/lib/caruby/import/java.rb +338 -0
- data/lib/caruby/migration/migratable.rb +167 -0
- data/lib/caruby/migration/migrator.rb +533 -0
- data/lib/caruby/migration/resource.rb +8 -0
- data/lib/caruby/migration/resource_module.rb +11 -0
- data/lib/caruby/migration/uniquify.rb +20 -0
- data/lib/caruby/resource.rb +969 -0
- data/lib/caruby/util/attribute_path.rb +46 -0
- data/lib/caruby/util/cache.rb +53 -0
- data/lib/caruby/util/class.rb +99 -0
- data/lib/caruby/util/collection.rb +1053 -0
- data/lib/caruby/util/controlled_value.rb +35 -0
- data/lib/caruby/util/coordinate.rb +75 -0
- data/lib/caruby/util/domain_extent.rb +49 -0
- data/lib/caruby/util/file_separator.rb +65 -0
- data/lib/caruby/util/inflector.rb +20 -0
- data/lib/caruby/util/log.rb +95 -0
- data/lib/caruby/util/math.rb +12 -0
- data/lib/caruby/util/merge.rb +59 -0
- data/lib/caruby/util/module.rb +34 -0
- data/lib/caruby/util/options.rb +92 -0
- data/lib/caruby/util/partial_order.rb +36 -0
- data/lib/caruby/util/person.rb +119 -0
- data/lib/caruby/util/pretty_print.rb +184 -0
- data/lib/caruby/util/properties.rb +112 -0
- data/lib/caruby/util/stopwatch.rb +66 -0
- data/lib/caruby/util/topological_sync_enumerator.rb +53 -0
- data/lib/caruby/util/transitive_closure.rb +45 -0
- data/lib/caruby/util/tree.rb +48 -0
- data/lib/caruby/util/trie.rb +37 -0
- data/lib/caruby/util/uniquifier.rb +30 -0
- data/lib/caruby/util/validation.rb +48 -0
- data/lib/caruby/util/version.rb +56 -0
- data/lib/caruby/util/visitor.rb +351 -0
- data/lib/caruby/util/weak_hash.rb +36 -0
- data/lib/caruby/version.rb +3 -0
- metadata +186 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'caruby/resource'
|
2
|
+
|
3
|
+
module CaRuby
|
4
|
+
# The Annotatable module marks a domain class as an anchor which holds at least one annotation attribute.
|
5
|
+
module Annotatable
|
6
|
+
include Resource
|
7
|
+
|
8
|
+
# Dynamically creates a new annotation reference method with the given symbol if symbol is the camelized form of a
|
9
|
+
# class in one of the Annotatable class's annotation modules.
|
10
|
+
def method_missing(symbol, *args)
|
11
|
+
name = symbol.to_s
|
12
|
+
# remove trailing assignment = if present
|
13
|
+
name.chop! if name =~ /=$/
|
14
|
+
# the class with the camelized form of the name
|
15
|
+
klass = self.class.annotation_class(name.camelize)
|
16
|
+
# delegate to super to print an error if no class
|
17
|
+
super if klass.nil?
|
18
|
+
# add the annotation attribute
|
19
|
+
klass.add_annotation(self.class)
|
20
|
+
raise NotImplementedError.new("#{self.class.qp} annotation method not created: #{symbol}") unless respond_to?(symbol)
|
21
|
+
#call the annotation attribute method
|
22
|
+
send(symbol, *args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'caruby/resource'
|
2
|
+
|
3
|
+
module CaRuby
|
4
|
+
# The Annotation module marks a domain object as an Annotation.
|
5
|
+
module Annotation
|
6
|
+
include Resource
|
7
|
+
|
8
|
+
# Dynamically creates a new annotable owner attribute with the given symbol if symbol is an annotatable owner attribute
|
9
|
+
# accessor method.
|
10
|
+
#
|
11
|
+
# @see #attribute_missing
|
12
|
+
def method_missing(symbol, *args)
|
13
|
+
name = symbol.to_s
|
14
|
+
# remove trailing assignment = if present
|
15
|
+
name.chop! if name =~ /=$/
|
16
|
+
# try to make the owner attribute
|
17
|
+
self.class.attribute_metadata(name.to_sym)
|
18
|
+
# if we reached here, then the owner was created so verify and call the new method
|
19
|
+
raise NoMethodError.new("#{name.demodulize} owner attribute #{name} created but accessor method not found: #{symbol}") unless method_defined?(symbol)
|
20
|
+
send(symbol, *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,447 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'caruby/util/inflector'
|
3
|
+
require 'caruby/util/collection'
|
4
|
+
require 'caruby/util/validation'
|
5
|
+
|
6
|
+
module CaRuby
|
7
|
+
# An Attribute captures the following metadata about a domain class attribute:
|
8
|
+
# * attribute symbol
|
9
|
+
# * declarer type
|
10
|
+
# * return type
|
11
|
+
# * reader method symbol
|
12
|
+
# * writer method symbol
|
13
|
+
class AttributeMetadata
|
14
|
+
# The supported attribute qualifier flags.
|
15
|
+
SUPPORTED_FLAGS = [
|
16
|
+
:autogenerated, :collection, :dependent, :derived, :logical, :disjoint, :owner, :cascaded,
|
17
|
+
:no_cascade_update_to_create, :saved, :unsaved, :saved_unmergeable, :optional, :fetched, :unfetched,
|
18
|
+
:create_only, :update_only, :unidirectional, :volatile].to_set
|
19
|
+
|
20
|
+
# The standard attribute reader and writer methods.
|
21
|
+
attr_reader :accessors
|
22
|
+
|
23
|
+
# The declaring class.
|
24
|
+
attr_accessor :declarer
|
25
|
+
|
26
|
+
# The return class.
|
27
|
+
attr_accessor :type
|
28
|
+
|
29
|
+
# The qualifier flags.
|
30
|
+
# @see SUPPORTED_FLAGS
|
31
|
+
attr_accessor :flags
|
32
|
+
|
33
|
+
protected :declarer=
|
34
|
+
|
35
|
+
# Creates a new AttributeMetadata from the given attribute.
|
36
|
+
#
|
37
|
+
# The type is the referenced entity type; an attribute whose return type is a collection
|
38
|
+
# of domain objects is thus the domain object class rather than a collection class.
|
39
|
+
#
|
40
|
+
# If the type is given, then the following flags are recognized:
|
41
|
+
# * +:dependent+ - the attribute references a dependent
|
42
|
+
# * +:collection+ - the attribute return type is a collection
|
43
|
+
# * +:owner+ - the attribute references the owner of a dependent
|
44
|
+
# * +:cascaded+ - a database create/update/delete operation propagates to the attribute reference
|
45
|
+
#
|
46
|
+
# @param [String,Symbol] attr the subject attribute
|
47
|
+
# @param [Class] declarer the declaring class
|
48
|
+
# @param [Class] declarer the return type
|
49
|
+
# @param [<Symbol>] flags the qualifying flags
|
50
|
+
def initialize(attribute, declarer, type=nil, *flags)
|
51
|
+
# the attribute symbol
|
52
|
+
@symbol = attribute.to_sym
|
53
|
+
# the declaring class
|
54
|
+
@declarer = declarer
|
55
|
+
# the Ruby class
|
56
|
+
@type = Class.to_ruby(type) if type
|
57
|
+
# the read and write methods
|
58
|
+
@accessors = [@symbol, "#{attribute}=".to_sym]
|
59
|
+
# the qualifier flags
|
60
|
+
@flags = Set.new
|
61
|
+
# identifier is always volatile
|
62
|
+
if @symbol == :identifier then flags << :volatile end
|
63
|
+
qualify(*flags)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Symbol] the reader method
|
67
|
+
def reader
|
68
|
+
accessors.first
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Symbol] the writer method
|
72
|
+
def writer
|
73
|
+
accessors.last
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Symbol, nil] the inverse of this attribute, if any
|
77
|
+
def inverse
|
78
|
+
@inv_md.to_sym if @inv_md
|
79
|
+
end
|
80
|
+
|
81
|
+
# Creates a new declarer attribute which restricts this attribute {#type} to the given type.
|
82
|
+
#
|
83
|
+
#@param [Class] declarer the class for which the restriction holds
|
84
|
+
# @return [AttributeMetadata] the metadata for the new declarer attribute
|
85
|
+
def restrict(declarer, type)
|
86
|
+
unless declarer < self.type then
|
87
|
+
raise ArgumentError.new("Cannot restrict #{self.declarer}.#{self} to incompatible declarer type #{declarer}")
|
88
|
+
end
|
89
|
+
if self.type and not type < self.type then
|
90
|
+
raise ArgumentError.new("Cannot restrict #{self.declarer}.#{self} to incompatible type #{type}")
|
91
|
+
end
|
92
|
+
# set aside the restrictions prior to the copy
|
93
|
+
rstr = @restrictions
|
94
|
+
@restrictions = nil
|
95
|
+
copy = dup
|
96
|
+
# Restore the restrictions. Initialize the array if necessary, since the copy
|
97
|
+
# will be added to the list.
|
98
|
+
@restrictions = rstr || []
|
99
|
+
# specialize the copy declarer and type
|
100
|
+
copy.declarer = declarer
|
101
|
+
copy.type = type
|
102
|
+
# specialize the inverse to the restricted type attribute, if necessary
|
103
|
+
if inverse then copy.inverse = inverse end
|
104
|
+
# Capture the restriction to propagate modifications to this metadata, esp.
|
105
|
+
# adding an inverse.
|
106
|
+
@restrictions << copy
|
107
|
+
copy
|
108
|
+
end
|
109
|
+
|
110
|
+
# Sets the inverse of the subject attribute to the given attribute.
|
111
|
+
# The inverse relation is symmetric, i.e. the inverse of the referenced AttributeMetadata
|
112
|
+
# is set to this AttributeMetadata's subject attribute.
|
113
|
+
#
|
114
|
+
# @param attribute the inverse attribute
|
115
|
+
def inverse=(attribute)
|
116
|
+
logger.debug { "Set #{@declarer.qp}.#{self} inverse to #{type.qp}.#{attribute}." }
|
117
|
+
@inv_md = type.attribute_metadata(attribute)
|
118
|
+
unless @inv_md then
|
119
|
+
raise MetadataError.new("#{@declarer.qp}.#{self} inverse attribute #{type.qp}.#{attribute} not found")
|
120
|
+
end
|
121
|
+
inv_inv_md = @inv_md.inverse_attribute_metadata
|
122
|
+
if inv_inv_md then
|
123
|
+
unless inv_inv_md == self then
|
124
|
+
if @inv_md.inverse == @symbol then
|
125
|
+
@inv_md.inverse = @symbol
|
126
|
+
else
|
127
|
+
raise MetadataError.new("Cannot set #{type.qp}.#{@inv_md} inverse attribute to #{@declarer.qp}.#{self} since it conflicts with existing inverse #{@inv_md.inverse}")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
else
|
131
|
+
@inv_md.inverse = @symbol
|
132
|
+
@inv_md.set_flag(:disjoint) if disjoint?
|
133
|
+
end
|
134
|
+
# propagate to restrictions
|
135
|
+
if @restrictions then @restrictions.each { |attr_md| attr_md.inverse = attribute } end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [AttributeMetadata, nil] the metadata for the {#inverse} attribute, if any
|
139
|
+
def inverse_attribute_metadata
|
140
|
+
@inv_md
|
141
|
+
end
|
142
|
+
|
143
|
+
# Qualifies this attribute with the given flags. Supported flags are listed in {SUPPORTED_FLAGS}.
|
144
|
+
#
|
145
|
+
# @param [<Symbol>] the flags to add
|
146
|
+
# @raise [ArgumentError] if the flag is not supported
|
147
|
+
def qualify(*flags)
|
148
|
+
flags.each { |flag| set_flag(flag) }
|
149
|
+
# propagate to restrictions
|
150
|
+
if @restrictions then @restrictions.each { |attr_md| attr_md.qualify(*flags) } end
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return whether the subject attribute encapsulates a Java property
|
154
|
+
def java_property?
|
155
|
+
CaRuby::JavaAttributeMetadata === self
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return whether the subject attribute returns a domain object or collection of domain objects
|
159
|
+
def domain?
|
160
|
+
# the type must be a Ruby class rather than a Java Class, and include the Domain mix-in
|
161
|
+
Class === type and type < Resource
|
162
|
+
end
|
163
|
+
|
164
|
+
# @return whether the subject attribute is not a domain object attribute
|
165
|
+
def nondomain?
|
166
|
+
not domain?
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns whether the subject attribute is fetched, determined as follows:
|
170
|
+
# * An attribute marked with the :fetched flag is fetched.
|
171
|
+
# * An attribute marked with the :unfetched flag is not fetched.
|
172
|
+
# Otherwise, a non-domain attribute is fetched, and a domain attribute is
|
173
|
+
# fetched if one of the following conditions hold:
|
174
|
+
# * A dependent domain attribute is fetched if it is not logical.
|
175
|
+
# * An owner domain attribute is fetched by default.
|
176
|
+
# * An independent domain attribute is fetched if it is abstract and not derived.
|
177
|
+
#
|
178
|
+
# @return [Boolean] whether the attribute is fetched
|
179
|
+
def fetched?
|
180
|
+
return true if @flags.include?(:fetched)
|
181
|
+
return false if @flags.include?(:unfetched)
|
182
|
+
nondomain? or dependent? ? fetched_dependent? : fetched_independent?
|
183
|
+
end
|
184
|
+
|
185
|
+
# @return whether the subject attribute return type is a collection
|
186
|
+
def collection?
|
187
|
+
@flags.include?(:collection)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns whether the subject attribute is a dependent on a parent. See the caRuby configuration
|
191
|
+
# documentation for a dependency description.
|
192
|
+
#
|
193
|
+
# @return [Boolean] whether the attribute references a dependent
|
194
|
+
def dependent?
|
195
|
+
annotation? or @flags.include?(:dependent)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Returns whether the subject attribute is marked as optional in a create.
|
199
|
+
# This method returns true only if the :optional flag is explicitly set.
|
200
|
+
# Other attributes are optional by default.
|
201
|
+
#
|
202
|
+
# @return [Boolean] whether the attribute is optional
|
203
|
+
# @see ResourceAttributes#mandatory_attributes.
|
204
|
+
def optional?
|
205
|
+
@flags.include?(:optional)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Returns whether the subject attribute is not saved.
|
209
|
+
#
|
210
|
+
# @return [Boolean] whether the attribute is unsaved
|
211
|
+
def unsaved?
|
212
|
+
@flags.include?(:unsaved)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns whether the subject attribute is a dependent whose value is automatically generated
|
216
|
+
# with place-holder domain objects when the parent is created.
|
217
|
+
#
|
218
|
+
# @return [Boolean] whether the attribute is auto-generated
|
219
|
+
def autogenerated?
|
220
|
+
@flags.include?(:autogenerated)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Returns whether the subject attribute is either an #autogenerated? {#dependent?}
|
224
|
+
# or {#cascaded?} and marked with the :unfetched flag.
|
225
|
+
def unfetched_created?
|
226
|
+
(dependent? and autogenerated?) or (cascaded? and @flags.include?(:unfetched))
|
227
|
+
end
|
228
|
+
|
229
|
+
# Returns whether the subject attribute is a dependent whose owner does not automatically
|
230
|
+
# cascade application service creation or update to the dependent. It is incumbent upon
|
231
|
+
# CaRuby::Database to cascade the changes.
|
232
|
+
def logical?
|
233
|
+
annotation? or @flags.include?(:logical)
|
234
|
+
end
|
235
|
+
|
236
|
+
# @return whether the subject attribute is an annotation
|
237
|
+
def annotation?
|
238
|
+
false
|
239
|
+
# TODO - enable when annotation enabled
|
240
|
+
end
|
241
|
+
|
242
|
+
# @return whether this attribute is derived from another attribute
|
243
|
+
# This occurs when the attribute value is set by setting another attribute, e.g. if this
|
244
|
+
# attribute is the inverse of a dependent owner attribute.
|
245
|
+
def derived?
|
246
|
+
@flags.include?(:derived) or (dependent? and not inverse.nil?)
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return this attribute's inverse attribute if the inverse is a derived attribute, or nil otherwise
|
250
|
+
def derived_inverse
|
251
|
+
@inv_md.to_sym if @inv_md and @inv_md.derived?
|
252
|
+
end
|
253
|
+
|
254
|
+
# @return whether the subject attribute is a non-dependent domain attribute
|
255
|
+
def independent?
|
256
|
+
domain? and not dependent?
|
257
|
+
end
|
258
|
+
|
259
|
+
# A Java attribute is creatable if all of the following conditions hold:
|
260
|
+
# * the attribute is {#saved?}
|
261
|
+
# * the attribute is not a {#proxied_save?}
|
262
|
+
# * the attribute :update_only flag is not set
|
263
|
+
#
|
264
|
+
# @return [Boolean] whether this attribute is saved in a create operation
|
265
|
+
def creatable?
|
266
|
+
saved? and not @flags.include?(:update_only)
|
267
|
+
end
|
268
|
+
|
269
|
+
# A Java attribute is an uncreated dependent if any of the following conditions hold:
|
270
|
+
# * the attribute is a {#logical?} dependent
|
271
|
+
# * the attribute is a #dependent? which is not {#creatable?}
|
272
|
+
#
|
273
|
+
# @return [Boolean] whether this attribute is saved in a create operation
|
274
|
+
def uncreated_dependent?
|
275
|
+
logical? or (dependent? and not creatable?)
|
276
|
+
end
|
277
|
+
|
278
|
+
# @return whether this attribute is saved in a update operation
|
279
|
+
#
|
280
|
+
# A Java attribute is updatable if all of the following conditions hold:
|
281
|
+
# * the attribute is {#saved?}
|
282
|
+
# * the attribute :create_only flag is not set
|
283
|
+
def updatable?
|
284
|
+
saved? and not @flags.include?(:create_only)
|
285
|
+
end
|
286
|
+
|
287
|
+
# @return whether this attribute is navigated to build a template used in a create or update operation
|
288
|
+
# A cascaded attribute determines where to prune a database create or update object graph.
|
289
|
+
#
|
290
|
+
# An attribute is cascaded if it is a physical dependent or the :cascaded flag is set.
|
291
|
+
def cascaded?
|
292
|
+
(dependent? and not logical?) or @flags.include?(:cascaded)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Returns whether this attribute is #{#cascaded} and cascades a parent update to a child
|
296
|
+
# create. This corresponds to the Hibernate +save-update+ cascade style but not the Hibernate
|
297
|
+
# +all+ cascade style.
|
298
|
+
#
|
299
|
+
# This method returns true if this attribute is cascaded and the +:no_cascade_update_to_create+
|
300
|
+
# flag is not set. Set this flag if the Hibernate mapping specifies the +all+ cascade style.
|
301
|
+
# Failure to set this flag will result in the caTissue Hibernate error:
|
302
|
+
# Exception: gov.nih.nci.system.applicationservice.ApplicationException:
|
303
|
+
# The given object has a null identifier:
|
304
|
+
# followed by the attribute type name.
|
305
|
+
def cascade_update_to_create?
|
306
|
+
cascaded? and not @flags.include?(:no_cascade_update_to_create)
|
307
|
+
end
|
308
|
+
|
309
|
+
# A Java property attribute is saved if none of the following conditions hold:
|
310
|
+
# * the attribute :unsaved flag is set
|
311
|
+
# * the attribute is {#proxied_save?}
|
312
|
+
# and any of the following conditions hold:
|
313
|
+
# * the attibute is {#nondomain?}
|
314
|
+
# * the attribute is cascaded
|
315
|
+
# * the attribute value is not a collection
|
316
|
+
# * the attribute does not have an inverse
|
317
|
+
# * the attribute :saved flag is set
|
318
|
+
#
|
319
|
+
# @return [Boolean] whether this attribute is saved in a create or update operation
|
320
|
+
def saved?
|
321
|
+
java_property? and not @flags.include?(:unsaved) and not proxied_save? and
|
322
|
+
(nondomain? or cascaded? or not collection? or inverse.nil? or unidirectional_java_dependent? or @flags.include?(:saved))
|
323
|
+
end
|
324
|
+
|
325
|
+
# @return [Boolean] whether this attribute is not {#saved?}
|
326
|
+
def unsaved?
|
327
|
+
not saved?
|
328
|
+
end
|
329
|
+
|
330
|
+
# @return [Boolean] whether the attribute return {#type} is a Resource class which
|
331
|
+
# implements the saver_proxy method
|
332
|
+
def proxied_save?
|
333
|
+
domain? and type.method_defined?(:saver_proxy)
|
334
|
+
end
|
335
|
+
|
336
|
+
# Each saved attribute is a saved mergeable attribute unless the :saved_unmergeable flag is set.
|
337
|
+
# @return [Boolean] whether this attribute can be merged from a save result
|
338
|
+
def saved_mergeable?
|
339
|
+
not @flags.include?(:saved_unmergeable)
|
340
|
+
end
|
341
|
+
|
342
|
+
# Returns whether this attribute's referents must exist before an instance of the
|
343
|
+
# declarer class can be created. An attribute is a storable prerequisite if it is
|
344
|
+
# either:
|
345
|
+
# * a {#cascaded?} dependent which does not #{#cascade_update_to_create?}, or
|
346
|
+
# * a {#saved?} {#independent?} 1:M or M:N association.
|
347
|
+
#
|
348
|
+
# @return [Boolean] whether this attribute is a create prerequisite
|
349
|
+
def storable_prerequisite?
|
350
|
+
return true if cascaded? and @flags.include?(:no_cascade_update_to_create)
|
351
|
+
return false unless independent? and saved?
|
352
|
+
return true unless collection?
|
353
|
+
inv_md = inverse_attribute_metadata
|
354
|
+
inv_md.nil? or inv_md.collection?
|
355
|
+
end
|
356
|
+
|
357
|
+
# @return [Boolean] whether this attribute is a collection with a collection inverse
|
358
|
+
def many_to_many?
|
359
|
+
return false unless collection?
|
360
|
+
inv_md = inverse_attribute_metadata
|
361
|
+
inv_md and inv_md.collection?
|
362
|
+
end
|
363
|
+
|
364
|
+
# @return [Boolean] whether the subject attribute is not saved
|
365
|
+
def transient?
|
366
|
+
not saved?
|
367
|
+
end
|
368
|
+
|
369
|
+
# Returns whether this attribute is set on the server as a side-effect
|
370
|
+
# of a change to the declarer object. The volatile attributes include
|
371
|
+
# those which are {#unsaved?} and those which are saved but marked
|
372
|
+
# with the +:volatile+ flag.
|
373
|
+
#
|
374
|
+
# @return [Boolean] whether this attribute's value is determined by the server
|
375
|
+
def volatile?
|
376
|
+
unsaved? or @flags.include?(:volatile)
|
377
|
+
end
|
378
|
+
|
379
|
+
# @return [Boolean] whether this is a non-collection Java attribute
|
380
|
+
def searchable?
|
381
|
+
java_property? and not collection?
|
382
|
+
end
|
383
|
+
|
384
|
+
# @return [Boolean] whether the subject attribute is a dependency owner
|
385
|
+
def owner?
|
386
|
+
@flags.include?(:owner)
|
387
|
+
end
|
388
|
+
|
389
|
+
# @return [Boolean] whether this is a dependent attribute which has exactly one owner value chosen from
|
390
|
+
# several owner attributes.
|
391
|
+
def disjoint?
|
392
|
+
@flags.include?(:disjoint)
|
393
|
+
end
|
394
|
+
|
395
|
+
# @return [Boolean] whether this attribute is a dependent which does not have a Java inverse owner attribute
|
396
|
+
def unidirectional_java_dependent?
|
397
|
+
# TODO - can this be relaxed to java_unidirectional? i.e. eliminate dependent filter
|
398
|
+
dependent? and not bidirectional_java_association?
|
399
|
+
end
|
400
|
+
|
401
|
+
# @return [Boolean] whether this is a Java attribute which has a Java inverse
|
402
|
+
def bidirectional_java_association?
|
403
|
+
inverse and java_property? and inverse_attribute_metadata.java_property?
|
404
|
+
end
|
405
|
+
|
406
|
+
def to_sym
|
407
|
+
@symbol
|
408
|
+
end
|
409
|
+
|
410
|
+
def to_s
|
411
|
+
@symbol.to_s
|
412
|
+
end
|
413
|
+
|
414
|
+
alias :inspect :to_s
|
415
|
+
|
416
|
+
alias :qp :to_s
|
417
|
+
|
418
|
+
private
|
419
|
+
|
420
|
+
# @param [Symbol] the flag to set
|
421
|
+
# @raise [ArgumentError] if flag is not supported
|
422
|
+
def set_flag(flag)
|
423
|
+
return if @flags.include?(flag)
|
424
|
+
raise ArgumentError.new("Attribute flag not supported: #{flag}") unless SUPPORTED_FLAGS.include?(flag)
|
425
|
+
@flags << flag
|
426
|
+
if flag == :owner then
|
427
|
+
inv_attr = type.dependent_attribute(@declarer)
|
428
|
+
if inv_attr.nil? then
|
429
|
+
raise MetadataError.new("#{@declarer.qp} owner attribute #{self} does not have a #{type.qp} dependent inverse")
|
430
|
+
end
|
431
|
+
self.inverse = type.dependent_attribute(@declarer)
|
432
|
+
@flags << :logical if inverse_attribute_metadata.logical?
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# @return [Boolean] whether this dependent attribute is fetched. Only physical dependents are fetched by default.
|
437
|
+
def fetched_dependent?
|
438
|
+
not (logical? or @flags.include?(:unfetched))
|
439
|
+
end
|
440
|
+
|
441
|
+
# @return [Boolean] whether this independent attribute is fetched. Only abstract, non-derived independent
|
442
|
+
# references are fetched by default.
|
443
|
+
def fetched_independent?
|
444
|
+
type.abstract? and not (derived? or @flags.include?(:unfetched))
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|