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.
Files changed (86) hide show
  1. data/History.txt +4 -0
  2. data/LEGAL +5 -0
  3. data/LICENSE +22 -0
  4. data/README.md +51 -0
  5. data/doc/website/css/site.css +1 -5
  6. data/doc/website/images/avatar.png +0 -0
  7. data/doc/website/images/favicon.ico +0 -0
  8. data/doc/website/images/logo.png +0 -0
  9. data/doc/website/index.html +82 -0
  10. data/doc/website/install.html +87 -0
  11. data/doc/website/quick_start.html +87 -0
  12. data/doc/website/tissue.html +85 -0
  13. data/doc/website/uom.html +10 -0
  14. data/lib/caruby.rb +3 -0
  15. data/lib/caruby/active_support/README.txt +2 -0
  16. data/lib/caruby/active_support/core_ext/string.rb +7 -0
  17. data/lib/caruby/active_support/core_ext/string/inflections.rb +167 -0
  18. data/lib/caruby/active_support/inflections.rb +55 -0
  19. data/lib/caruby/active_support/inflector.rb +398 -0
  20. data/lib/caruby/cli/application.rb +36 -0
  21. data/lib/caruby/cli/command.rb +169 -0
  22. data/lib/caruby/csv/csv_mapper.rb +157 -0
  23. data/lib/caruby/csv/csvio.rb +185 -0
  24. data/lib/caruby/database.rb +252 -0
  25. data/lib/caruby/database/fetched_matcher.rb +66 -0
  26. data/lib/caruby/database/persistable.rb +432 -0
  27. data/lib/caruby/database/persistence_service.rb +162 -0
  28. data/lib/caruby/database/reader.rb +599 -0
  29. data/lib/caruby/database/saved_merger.rb +131 -0
  30. data/lib/caruby/database/search_template_builder.rb +59 -0
  31. data/lib/caruby/database/sql_executor.rb +75 -0
  32. data/lib/caruby/database/store_template_builder.rb +200 -0
  33. data/lib/caruby/database/writer.rb +469 -0
  34. data/lib/caruby/domain/annotatable.rb +25 -0
  35. data/lib/caruby/domain/annotation.rb +23 -0
  36. data/lib/caruby/domain/attribute_metadata.rb +447 -0
  37. data/lib/caruby/domain/java_attribute_metadata.rb +160 -0
  38. data/lib/caruby/domain/merge.rb +91 -0
  39. data/lib/caruby/domain/properties.rb +95 -0
  40. data/lib/caruby/domain/reference_visitor.rb +289 -0
  41. data/lib/caruby/domain/resource_attributes.rb +528 -0
  42. data/lib/caruby/domain/resource_dependency.rb +205 -0
  43. data/lib/caruby/domain/resource_introspection.rb +159 -0
  44. data/lib/caruby/domain/resource_metadata.rb +117 -0
  45. data/lib/caruby/domain/resource_module.rb +285 -0
  46. data/lib/caruby/domain/uniquify.rb +38 -0
  47. data/lib/caruby/import/annotatable_class.rb +28 -0
  48. data/lib/caruby/import/annotation_class.rb +27 -0
  49. data/lib/caruby/import/annotation_module.rb +67 -0
  50. data/lib/caruby/import/java.rb +338 -0
  51. data/lib/caruby/migration/migratable.rb +167 -0
  52. data/lib/caruby/migration/migrator.rb +533 -0
  53. data/lib/caruby/migration/resource.rb +8 -0
  54. data/lib/caruby/migration/resource_module.rb +11 -0
  55. data/lib/caruby/migration/uniquify.rb +20 -0
  56. data/lib/caruby/resource.rb +969 -0
  57. data/lib/caruby/util/attribute_path.rb +46 -0
  58. data/lib/caruby/util/cache.rb +53 -0
  59. data/lib/caruby/util/class.rb +99 -0
  60. data/lib/caruby/util/collection.rb +1053 -0
  61. data/lib/caruby/util/controlled_value.rb +35 -0
  62. data/lib/caruby/util/coordinate.rb +75 -0
  63. data/lib/caruby/util/domain_extent.rb +49 -0
  64. data/lib/caruby/util/file_separator.rb +65 -0
  65. data/lib/caruby/util/inflector.rb +20 -0
  66. data/lib/caruby/util/log.rb +95 -0
  67. data/lib/caruby/util/math.rb +12 -0
  68. data/lib/caruby/util/merge.rb +59 -0
  69. data/lib/caruby/util/module.rb +34 -0
  70. data/lib/caruby/util/options.rb +92 -0
  71. data/lib/caruby/util/partial_order.rb +36 -0
  72. data/lib/caruby/util/person.rb +119 -0
  73. data/lib/caruby/util/pretty_print.rb +184 -0
  74. data/lib/caruby/util/properties.rb +112 -0
  75. data/lib/caruby/util/stopwatch.rb +66 -0
  76. data/lib/caruby/util/topological_sync_enumerator.rb +53 -0
  77. data/lib/caruby/util/transitive_closure.rb +45 -0
  78. data/lib/caruby/util/tree.rb +48 -0
  79. data/lib/caruby/util/trie.rb +37 -0
  80. data/lib/caruby/util/uniquifier.rb +30 -0
  81. data/lib/caruby/util/validation.rb +48 -0
  82. data/lib/caruby/util/version.rb +56 -0
  83. data/lib/caruby/util/visitor.rb +351 -0
  84. data/lib/caruby/util/weak_hash.rb +36 -0
  85. data/lib/caruby/version.rb +3 -0
  86. 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