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.
Files changed (126) hide show
  1. data/Gemfile +9 -0
  2. data/History.md +5 -1
  3. data/lib/caruby.rb +3 -5
  4. data/lib/caruby/caruby-src.tar.gz +0 -0
  5. data/lib/caruby/database.rb +53 -69
  6. data/lib/caruby/database/application_service.rb +25 -0
  7. data/lib/caruby/database/cache.rb +60 -0
  8. data/lib/caruby/database/fetched_matcher.rb +52 -38
  9. data/lib/caruby/database/lazy_loader.rb +4 -4
  10. data/lib/caruby/database/operation.rb +34 -0
  11. data/lib/caruby/database/persistable.rb +171 -86
  12. data/lib/caruby/database/persistence_service.rb +32 -34
  13. data/lib/caruby/database/persistifier.rb +100 -43
  14. data/lib/caruby/database/reader.rb +107 -85
  15. data/lib/caruby/database/reader_template_builder.rb +60 -0
  16. data/lib/caruby/database/saved_matcher.rb +3 -3
  17. data/lib/caruby/database/sql_executor.rb +88 -17
  18. data/lib/caruby/database/writer.rb +213 -177
  19. data/lib/caruby/database/writer_template_builder.rb +334 -0
  20. data/lib/caruby/{util → helpers}/controlled_value.rb +0 -0
  21. data/lib/caruby/{util → helpers}/coordinate.rb +4 -4
  22. data/lib/caruby/{util → helpers}/person.rb +3 -3
  23. data/lib/caruby/{util → helpers}/properties.rb +7 -9
  24. data/lib/caruby/{util → helpers}/roman.rb +2 -2
  25. data/lib/caruby/{util → helpers}/version.rb +1 -1
  26. data/lib/caruby/json/deserializer.rb +2 -2
  27. data/lib/caruby/json/serializer.rb +49 -7
  28. data/lib/caruby/metadata.rb +30 -0
  29. data/lib/caruby/metadata/java_property.rb +21 -0
  30. data/lib/caruby/metadata/propertied.rb +191 -0
  31. data/lib/caruby/metadata/property.rb +22 -0
  32. data/lib/caruby/metadata/property_characteristics.rb +201 -0
  33. data/lib/caruby/migration/migratable.rb +11 -182
  34. data/lib/caruby/rdbi/driver/jdbc.rb +446 -0
  35. data/lib/caruby/resource.rb +20 -823
  36. data/lib/caruby/version.rb +1 -1
  37. data/test/lib/caruby/database/cache_test.rb +54 -0
  38. data/test/lib/caruby/{util → helpers}/controlled_value_test.rb +3 -5
  39. data/test/lib/caruby/{util → helpers}/person_test.rb +4 -6
  40. data/test/lib/caruby/helpers/properties_test.rb +34 -0
  41. data/test/lib/caruby/{util → helpers}/roman_test.rb +2 -3
  42. data/test/lib/caruby/{util → helpers}/version_test.rb +2 -3
  43. data/test/lib/helper.rb +7 -0
  44. metadata +161 -214
  45. data/lib/caruby/cli/application.rb +0 -36
  46. data/lib/caruby/cli/command.rb +0 -202
  47. data/lib/caruby/csv/csv_mapper.rb +0 -159
  48. data/lib/caruby/csv/csvio.rb +0 -203
  49. data/lib/caruby/database/search_template_builder.rb +0 -56
  50. data/lib/caruby/database/store_template_builder.rb +0 -278
  51. data/lib/caruby/domain.rb +0 -193
  52. data/lib/caruby/domain/attribute.rb +0 -584
  53. data/lib/caruby/domain/attributes.rb +0 -628
  54. data/lib/caruby/domain/dependency.rb +0 -225
  55. data/lib/caruby/domain/id_alias.rb +0 -22
  56. data/lib/caruby/domain/importer.rb +0 -183
  57. data/lib/caruby/domain/introspection.rb +0 -176
  58. data/lib/caruby/domain/inverse.rb +0 -172
  59. data/lib/caruby/domain/inversible.rb +0 -90
  60. data/lib/caruby/domain/java_attribute.rb +0 -173
  61. data/lib/caruby/domain/merge.rb +0 -185
  62. data/lib/caruby/domain/metadata.rb +0 -142
  63. data/lib/caruby/domain/mixin.rb +0 -35
  64. data/lib/caruby/domain/properties.rb +0 -95
  65. data/lib/caruby/domain/reference_visitor.rb +0 -428
  66. data/lib/caruby/domain/uniquify.rb +0 -50
  67. data/lib/caruby/import/java.rb +0 -387
  68. data/lib/caruby/migration/migrator.rb +0 -918
  69. data/lib/caruby/migration/resource_module.rb +0 -9
  70. data/lib/caruby/migration/uniquify.rb +0 -17
  71. data/lib/caruby/util/attribute_path.rb +0 -44
  72. data/lib/caruby/util/cache.rb +0 -56
  73. data/lib/caruby/util/class.rb +0 -149
  74. data/lib/caruby/util/collection.rb +0 -1152
  75. data/lib/caruby/util/domain_extent.rb +0 -46
  76. data/lib/caruby/util/file_separator.rb +0 -65
  77. data/lib/caruby/util/inflector.rb +0 -27
  78. data/lib/caruby/util/log.rb +0 -95
  79. data/lib/caruby/util/math.rb +0 -12
  80. data/lib/caruby/util/merge.rb +0 -59
  81. data/lib/caruby/util/module.rb +0 -18
  82. data/lib/caruby/util/options.rb +0 -97
  83. data/lib/caruby/util/partial_order.rb +0 -35
  84. data/lib/caruby/util/pretty_print.rb +0 -204
  85. data/lib/caruby/util/stopwatch.rb +0 -74
  86. data/lib/caruby/util/topological_sync_enumerator.rb +0 -62
  87. data/lib/caruby/util/transitive_closure.rb +0 -55
  88. data/lib/caruby/util/tree.rb +0 -48
  89. data/lib/caruby/util/trie.rb +0 -37
  90. data/lib/caruby/util/uniquifier.rb +0 -30
  91. data/lib/caruby/util/validation.rb +0 -20
  92. data/lib/caruby/util/visitor.rb +0 -365
  93. data/lib/caruby/util/weak_hash.rb +0 -36
  94. data/test/lib/caruby/csv/csv_mapper_test.rb +0 -40
  95. data/test/lib/caruby/csv/csvio_test.rb +0 -69
  96. data/test/lib/caruby/database/persistable_test.rb +0 -92
  97. data/test/lib/caruby/domain/domain_test.rb +0 -112
  98. data/test/lib/caruby/domain/inversible_test.rb +0 -99
  99. data/test/lib/caruby/domain/reference_visitor_test.rb +0 -130
  100. data/test/lib/caruby/import/java_test.rb +0 -80
  101. data/test/lib/caruby/import/mixed_case_test.rb +0 -14
  102. data/test/lib/caruby/migration/test_case.rb +0 -102
  103. data/test/lib/caruby/test_case.rb +0 -230
  104. data/test/lib/caruby/util/cache_test.rb +0 -23
  105. data/test/lib/caruby/util/class_test.rb +0 -61
  106. data/test/lib/caruby/util/collection_test.rb +0 -398
  107. data/test/lib/caruby/util/command_test.rb +0 -55
  108. data/test/lib/caruby/util/domain_extent_test.rb +0 -60
  109. data/test/lib/caruby/util/file_separator_test.rb +0 -30
  110. data/test/lib/caruby/util/inflector_test.rb +0 -12
  111. data/test/lib/caruby/util/lazy_hash_test.rb +0 -34
  112. data/test/lib/caruby/util/merge_test.rb +0 -83
  113. data/test/lib/caruby/util/module_test.rb +0 -25
  114. data/test/lib/caruby/util/options_test.rb +0 -59
  115. data/test/lib/caruby/util/partial_order_test.rb +0 -42
  116. data/test/lib/caruby/util/pretty_print_test.rb +0 -85
  117. data/test/lib/caruby/util/properties_test.rb +0 -50
  118. data/test/lib/caruby/util/stopwatch_test.rb +0 -18
  119. data/test/lib/caruby/util/topological_sync_enumerator_test.rb +0 -69
  120. data/test/lib/caruby/util/transitive_closure_test.rb +0 -67
  121. data/test/lib/caruby/util/tree_test.rb +0 -23
  122. data/test/lib/caruby/util/trie_test.rb +0 -14
  123. data/test/lib/caruby/util/visitor_test.rb +0 -278
  124. data/test/lib/caruby/util/weak_hash_test.rb +0 -45
  125. data/test/lib/examples/clinical_trials/migration/migration_test.rb +0 -58
  126. 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