Class: Jinx::Property
- Includes:
- PropertyCharacteristics
- Defined in:
- lib/jinx/metadata/property.rb
Overview
A Property captures the following metadata about a domain class attribute:
-
attribute symbol
-
declarer type
-
return type
-
reader method symbol
-
writer method symbol
Direct Known Subclasses
Constant Summary collapse
- SUPPORTED_FLAGS =
The supported property qualifier flags. See the complementary methods for an explanation of the flag option, e.g. #dependent? for the
:dependent
flag.Included persistence adapters should add specialized flags to this set. An unsupported flag is allowed and can be used by adapters, but a warning log message is issued in that case.
[ :collection, :dependent, :disjoint, :owner, :mandatory, :optional].to_set
Instance Attribute Summary collapse
-
#accessors ⇒ (Symbol, Symbol)
readonly
The standard attribute reader and writer methods.
-
#attribute ⇒ Symbol
(also: #to_sym)
readonly
The standard attribute symbol for this property.
-
#declarer ⇒ Class
readonly
The declaring class.
-
#flags ⇒ <Symbol>
readonly
The qualifier flags.
-
#type ⇒ Class
The return type.
Instance Method Summary collapse
-
#bidirectional? ⇒ Boolean
Whether this property has an inverse.
-
#bidirectional_java_association? ⇒ Boolean
Whether this is a Java attribute which has a Java inverse.
- #clear_inverse ⇒ Object private
-
#collection? ⇒ Boolean
Whether the subject attribute return type is a collection.
-
#deep_copy ⇒ Property
private
Creates a copy of this metadata which does not share mutable content.
-
#dependent? ⇒ Boolean
Returns whether the subject attribute is a dependent on a parent.
-
#dependent_flag_set ⇒ Object
private
Validates that this is not an owner attribute.
-
#derived? ⇒ Boolean
An attribute is derived if the attribute value is set by setting another attribute, e.g.
-
#derived_inverse ⇒ Boolean
This attribute’s inverse attribute if the inverse is a derived attribute, or nil otherwise.
-
#disjoint? ⇒ Boolean
Whether this is a dependent attribute which has exactly one owner value chosen from several owner attributes.
-
#domain? ⇒ Boolean
Whether the subject attribute returns a domain object or collection of domain objects.
-
#dup_content ⇒ Object
protected
Duplicates the mutable content as part of a #deep_copy.
-
#flag_supported?(flag) ⇒ Boolean
private
Whether the flag is supported.
-
#independent? ⇒ Boolean
An independent attribute is a reference to one or more non-dependent Resource objects.
-
#initialize(attribute, declarer, type = nil, *flags) ⇒ Property
constructor
Creates a new Property from the given attribute.
-
#inverse ⇒ Symbol?
The inverse of this attribute, if any.
-
#inverse=(attribute) ⇒ Object
Sets the inverse of the subject attribute to the given attribute.
-
#inverse_property ⇒ Property?
The property for the #inverse attribute, if any.
-
#java_property? ⇒ Boolean
Whether the subject attribute encapsulates a Java attribute.
-
#mandatory? ⇒ Boolean
Returns whether the subject attribute must have a value when it is saved.
-
#many_to_many? ⇒ Boolean
Whether this attribute is a collection with a collection inverse.
-
#nondomain? ⇒ Boolean
Whether the subject attribute is not a domain object attribute.
-
#owner? ⇒ Boolean
Whether the subject attribute is a dependency owner.
-
#owner_flag_set ⇒ Object
private
This method is called when the owner flag is set.
-
#qualify(*flags) ⇒ Object
Qualifies this attribute with the given flags.
-
#reader ⇒ Symbol
The reader method.
-
#restrict(declarer, opts = {}) ⇒ Property
Creates a new declarer attribute which restricts this attribute.
-
#restrict_flags(declarer, *flags) ⇒ Property
Creates a new declarer attribute which qualifies this attribute for the given declarer.
-
#restriction?(other) ⇒ Boolean
protected
Whether the other attribute restricts this attribute.
- #set_flag(flag) ⇒ Object private
- #set_restricted_declarer(klass) ⇒ Object protected
- #to_s ⇒ Object (also: #inspect, #qp)
-
#unidirectional? ⇒ Boolean
An attribute is unidirectional if both of the following is true: * there is no distinct #inverse attribute * the attribute is not a #dependent? with more than one owner.
-
#unidirectional_java_dependent? ⇒ Boolean
Whether this attribute is a dependent which does not have a Java inverse owner attribute.
-
#writer ⇒ Symbol
The writer method.
Constructor Details
#initialize(attribute, declarer, type = nil, *flags) ⇒ Property
Creates a new Property from the given attribute.
The return type is the referenced entity type. An attribute whose return type is a collection of domain objects is thus the domain object class rather than a collection class.
51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/jinx/metadata/property.rb', line 51 def initialize(attribute, declarer, type=nil, *flags) # the attribute symbol @attribute = attribute.to_sym # the declaring class @declarer = declarer # the Ruby class @type = Class.to_ruby(type) if type # the read and write methods @accessors = [@attribute, "#{attribute}=".to_sym] # the qualifier flags @flags = Set.new qualify(*flags) end |
Instance Attribute Details
#accessors ⇒ (Symbol, Symbol) (readonly)
Returns the standard attribute reader and writer methods.
29 30 31 |
# File 'lib/jinx/metadata/property.rb', line 29 def accessors @accessors end |
#attribute ⇒ Symbol (readonly) Also known as: to_sym
Returns the standard attribute symbol for this property.
26 27 28 |
# File 'lib/jinx/metadata/property.rb', line 26 def attribute @attribute end |
#declarer ⇒ Class (readonly)
Returns the declaring class.
32 33 34 |
# File 'lib/jinx/metadata/property.rb', line 32 def declarer @declarer end |
#flags ⇒ <Symbol> (readonly)
Returns the qualifier flags.
39 40 41 |
# File 'lib/jinx/metadata/property.rb', line 39 def flags @flags end |
#type ⇒ Class
Returns the return type.
35 36 37 |
# File 'lib/jinx/metadata/property.rb', line 35 def type @type end |
Instance Method Details
#bidirectional? ⇒ Boolean
Returns whether this property has an inverse.
140 141 142 |
# File 'lib/jinx/metadata/property.rb', line 140 def bidirectional? !!@inv_prop end |
#bidirectional_java_association? ⇒ Boolean
Returns whether this is a Java attribute which has a Java inverse.
241 242 243 |
# File 'lib/jinx/metadata/property.rb', line 241 def bidirectional_java_association? inverse and java_property? and inverse_property.java_property? end |
#clear_inverse ⇒ Object (private)
349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/jinx/metadata/property.rb', line 349 def clear_inverse return unless @inv_prop logger.debug { "Clearing #{@declarer.qp}.#{self} inverse #{type.qp}.#{inverse}..." } # Capture the inverse before unsetting it. ip = @inv_prop # Unset the inverse. @inv_prop = nil # Clear the inverse of the inverse. ip.inverse = nil logger.debug { "Cleared #{@declarer.qp}.#{self} inverse." } end |
#collection? ⇒ Boolean
Returns whether the subject attribute return type is a collection.
176 177 178 |
# File 'lib/jinx/metadata/property.rb', line 176 def collection? @flags.include?(:collection) end |
#deep_copy ⇒ Property (private)
Creates a copy of this metadata which does not share mutable content.
The copy instance variables are as follows:
-
the copy inverse and restrictions are empty
-
the copy flags is a deep copy of this attribute’s flags
-
other instance variable references are shared between the copy and this attribute
343 344 345 346 347 |
# File 'lib/jinx/metadata/property.rb', line 343 def deep_copy other = dup other.dup_content other end |
#dependent? ⇒ Boolean
Returns whether the subject attribute is a dependent on a parent. See the Jinx configuration documentation for a dependency description.
184 185 186 |
# File 'lib/jinx/metadata/property.rb', line 184 def dependent? @flags.include?(:dependent) end |
#dependent_flag_set ⇒ Object (private)
Validates that this is not an owner attribute.
395 396 397 398 399 |
# File 'lib/jinx/metadata/property.rb', line 395 def dependent_flag_set if owner? then raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} dependent since it is already defined as a #{type.qp} owner") end end |
#derived? ⇒ Boolean
An attribute is derived if the attribute value is set by setting another attribute, e.g. if this attribute is the inverse of a dependent owner attribute.
199 200 201 |
# File 'lib/jinx/metadata/property.rb', line 199 def derived? dependent? and !!inverse end |
#derived_inverse ⇒ Boolean
Returns this attribute’s inverse attribute if the inverse is a derived attribute, or nil otherwise.
204 205 206 |
# File 'lib/jinx/metadata/property.rb', line 204 def derived_inverse @inv_prop.attribute if @inv_prop and @inv_prop.derived? end |
#disjoint? ⇒ Boolean
Returns whether this is a dependent attribute which has exactly one owner value chosen from several owner attributes.
230 231 232 |
# File 'lib/jinx/metadata/property.rb', line 230 def disjoint? @flags.include?(:disjoint) end |
#domain? ⇒ Boolean
Returns whether the subject attribute returns a domain object or collection of domain objects.
165 166 167 168 |
# File 'lib/jinx/metadata/property.rb', line 165 def domain? # the type must be a Ruby class rather than a Java Class, and include the Domain mix-in Class === type and type < Resource end |
#dup_content ⇒ Object (protected)
Duplicates the mutable content as part of a #deep_copy.
305 306 307 308 309 310 |
# File 'lib/jinx/metadata/property.rb', line 305 def dup_content # keep the copied flags but don't share them @flags = @flags.dup # restrictions and inverse are neither shared nor copied @inv_prop = @restrictions = nil end |
#flag_supported?(flag) ⇒ Boolean (private)
Returns whether the flag is supported.
331 332 333 |
# File 'lib/jinx/metadata/property.rb', line 331 def flag_supported?(flag) SUPPORTED_FLAGS.include?(flag) end |
#independent? ⇒ Boolean
An independent attribute is a reference to one or more non-dependent Resource objects. An #owner? attribute is independent.
212 213 214 |
# File 'lib/jinx/metadata/property.rb', line 212 def independent? domain? and not dependent? end |
#inverse ⇒ Symbol?
Returns the inverse of this attribute, if any.
76 77 78 |
# File 'lib/jinx/metadata/property.rb', line 76 def inverse @inv_prop.attribute if @inv_prop end |
#inverse=(attribute) ⇒ Object
Sets the inverse of the subject attribute to the given attribute. The inverse relation is symmetric, i.e. the inverse of the referenced Property is set to this Property’s subject attribute.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/jinx/metadata/property.rb', line 116 def inverse=(attribute) return if inverse == attribute # if no attribute, then the clear the existing inverse, if any return clear_inverse if attribute.nil? # the inverse attribute meta-data begin @inv_prop = type.property(attribute) rescue NameError => e raise MetadataError.new("#{@declarer.qp}.#{self} inverse attribute #{type.qp}.#{attribute} not found") end # the inverse of the inverse inv_inv_prop = @inv_prop.inverse_property # If the inverse of the inverse is already set to a different attribute, then raise an exception. if inv_inv_prop and not (inv_inv_prop == self or inv_inv_prop.restriction?(self)) raise MetadataError.new("Cannot set #{type.qp}.#{attribute} inverse attribute to #{@declarer.qp}.#{self}@#{object_id} since it conflicts with existing inverse #{inv_inv_prop.declarer.qp}.#{inv_inv_prop}@#{inv_inv_prop.object_id}") end # Set the inverse of the inverse to this attribute. @inv_prop.inverse = @attribute # If this attribute is disjoint, then so is the inverse. @inv_prop.qualify(:disjoint) if disjoint? logger.debug { "Assigned #{@declarer.qp}.#{self} attribute inverse to #{type.qp}.#{attribute}." } end |
#inverse_property ⇒ Property?
Returns the property for the #inverse attribute, if any.
145 146 147 |
# File 'lib/jinx/metadata/property.rb', line 145 def inverse_property @inv_prop end |
#java_property? ⇒ Boolean
Returns whether the subject attribute encapsulates a Java attribute.
160 161 162 |
# File 'lib/jinx/metadata/property.rb', line 160 def java_property? JavaProperty === self end |
#mandatory? ⇒ Boolean
Returns whether the subject attribute must have a value when it is saved
191 192 193 |
# File 'lib/jinx/metadata/property.rb', line 191 def mandatory? @declarer.mandatory_attributes.include?(attribute) end |
#many_to_many? ⇒ Boolean
Returns whether this attribute is a collection with a collection inverse.
217 218 219 220 221 |
# File 'lib/jinx/metadata/property.rb', line 217 def many_to_many? return false unless collection? inv_prop = inverse_property inv_prop and inv_prop.collection? end |
#nondomain? ⇒ Boolean
Returns whether the subject attribute is not a domain object attribute.
171 172 173 |
# File 'lib/jinx/metadata/property.rb', line 171 def nondomain? not domain? end |
#owner? ⇒ Boolean
Returns whether the subject attribute is a dependency owner.
224 225 226 |
# File 'lib/jinx/metadata/property.rb', line 224 def owner? @flags.include?(:owner) end |
#owner_flag_set ⇒ Object (private)
This method is called when the owner flag is set. The inverse is inferred as the referenced owner type’s dependent attribute which references this attribute’s type.
380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/jinx/metadata/property.rb', line 380 def owner_flag_set if dependent? then raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} owner since it is already defined as a #{type.qp} dependent") end inv_attr = type.dependent_attribute(@declarer) if inv_attr.nil? then raise MetadataError.new("#{@declarer.qp} owner attribute #{self} does not have a #{type.qp} dependent inverse") end logger.debug { "#{declarer.qp}.#{self} inverse is the #{type.qp} dependent attribute #{inv_attr}." } self.inverse = inv_attr end |
#qualify(*flags) ⇒ Object
Qualifies this attribute with the given flags. Supported flags are listed in SUPPORTED_FLAGS.
153 154 155 156 157 |
# File 'lib/jinx/metadata/property.rb', line 153 def qualify(*flags) flags.each { |flag| set_flag(flag) } # propagate to restrictions if @restrictions then @restrictions.each { |prop| prop.qualify(*flags) } end end |
#reader ⇒ Symbol
Returns the reader method.
66 67 68 |
# File 'lib/jinx/metadata/property.rb', line 66 def reader accessors.first end |
#restrict(declarer, opts = {}) ⇒ Property
Creates a new declarer attribute which restricts this attribute. This method should only be called by a Resource class, since the class is responsible for resetting the attribute symbol => meta-data association to point to the new restricted attribute.
If this attribute has an inverse, then the restriction inverse is set to the attribute declared by the restriction declarer’. For example, if:
-
AbstractProtocol.coordinator
has inverseAdministrator.protocol
-
AbstractProtocol
has subclassStudyProtocol
-
StudyProtocol.coordinator
returns aStudyCoordinator
-
AbstractProtocol.coordinator
is restricted toStudyProtocol
then calling this method on the StudyProtocol.coordinator
restriction sets the StudyProtocol.coordinator
inverse to StudyCoordinator.coordinator
.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/jinx/metadata/property.rb', line 269 def restrict(declarer, opts={}) rtype = opts[:type] || @type rinv = opts[:inverse] || inverse unless declarer < @declarer then raise ArgumentError.new("Cannot restrict #{@declarer.qp}.#{self} to an incompatible declarer type #{declarer.qp}") end unless rtype <= @type then raise ArgumentError.new("Cannot restrict #{@declarer.qp}.#{self}({@type.qp}) to an incompatible return type #{rtype.qp}") end # Copy this attribute and its instance variables minus the restrictions and make a deep copy of the flags. rst = deep_copy # specialize the copy declarer rst.set_restricted_declarer(declarer) # Capture the restriction to propagate modifications to this metadata, esp. adding an inverse. @restrictions ||= [] @restrictions << rst # Set the restriction type rst.type = rtype # Specialize the inverse to the restricted type attribute, if necessary. rst.inverse = rinv rst end |
#restrict_flags(declarer, *flags) ⇒ Property
Creates a new declarer attribute which qualifies this attribute for the given declarer.
104 105 106 107 108 |
# File 'lib/jinx/metadata/property.rb', line 104 def restrict_flags(declarer, *flags) copy = restrict(declarer) copy.qualify(*flags) copy end |
#restriction?(other) ⇒ Boolean (protected)
Returns whether the other attribute restricts this attribute.
314 315 316 |
# File 'lib/jinx/metadata/property.rb', line 314 def restriction?(other) @restrictions and @restrictions.include?(other) end |
#set_flag(flag) ⇒ Object (private)
363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/jinx/metadata/property.rb', line 363 def set_flag(flag) return if @flags.include?(flag) unless flag_supported?(flag) then raise ArgumentError.new("Property #{declarer.name}.#{self} flag not supported: #{flag.qp}") end @flags << flag case flag when :owner then owner_flag_set when :dependent then dependent_flag_set end end |
#set_restricted_declarer(klass) ⇒ Object (protected)
319 320 321 322 323 324 325 |
# File 'lib/jinx/metadata/property.rb', line 319 def set_restricted_declarer(klass) if @declarer and not klass < @declarer then raise MetadataError.new("Cannot reset #{declarer.qp}.#{self} declarer to #{type.qp}") end @declarer = klass @declarer.add_restriction(self) end |
#to_s ⇒ Object Also known as: inspect, qp
294 295 296 |
# File 'lib/jinx/metadata/property.rb', line 294 def to_s attribute.to_s end |
#unidirectional? ⇒ Boolean
An attribute is unidirectional if both of the following is true:
-
there is no distinct #inverse attribute
-
the attribute is not a #dependent? with more than one owner
85 86 87 |
# File 'lib/jinx/metadata/property.rb', line 85 def unidirectional? inverse.nil? and not (dependent? and type.owner_attributes.size > 1) end |
#unidirectional_java_dependent? ⇒ Boolean
Returns whether this attribute is a dependent which does not have a Java inverse owner attribute.
236 237 238 |
# File 'lib/jinx/metadata/property.rb', line 236 def unidirectional_java_dependent? dependent? and java_property? and not bidirectional_java_association? end |
#writer ⇒ Symbol
Returns the writer method.
71 72 73 |
# File 'lib/jinx/metadata/property.rb', line 71 def writer accessors.last end |