caruby-core 1.4.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.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
|