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. Jinx::PropertyCharacteristics#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
- #clear_inverse ⇒ Object private
-
#deep_copy ⇒ Property
private
Creates a copy of this metadata which does not share mutable content.
-
#dependent_flag_set ⇒ Object
private
Validates that this is not an owner attribute.
-
#derived_inverse ⇒ Boolean
This attribute’s inverse attribute if the inverse is a derived attribute, or nil otherwise.
-
#dup_content ⇒ Object
protected
Duplicates the mutable content as part of a #deep_copy.
-
#flag_supported?(flag) ⇒ Boolean
private
Whether the flag is supported.
-
#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.
-
#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)
-
#writer ⇒ Symbol
The writer method.
Methods included from PropertyCharacteristics
#bidirectional?, #bidirectional_java_association?, #collection?, #dependent?, #derived?, #disjoint?, #domain?, #independent?, #java_property?, #mandatory?, #many_to_many?, #nondomain?, #owner?, #unidirectional?, #unidirectional_java_dependent?
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
#clear_inverse ⇒ Object (private)
254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/jinx/metadata/property.rb', line 254 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 |
#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
248 249 250 251 252 |
# File 'lib/jinx/metadata/property.rb', line 248 def deep_copy other = dup other.dup_content other end |
#dependent_flag_set ⇒ Object (private)
Validates that this is not an owner attribute.
305 306 307 308 309 |
# File 'lib/jinx/metadata/property.rb', line 305 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_inverse ⇒ Boolean
Returns this attribute’s inverse attribute if the inverse is a derived attribute, or nil otherwise.
146 147 148 |
# File 'lib/jinx/metadata/property.rb', line 146 def derived_inverse @inv_prop.attribute if @inv_prop and @inv_prop.derived? end |
#dup_content ⇒ Object (protected)
Duplicates the mutable content as part of a #deep_copy.
210 211 212 213 214 215 |
# File 'lib/jinx/metadata/property.rb', line 210 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.
236 237 238 |
# File 'lib/jinx/metadata/property.rb', line 236 def flag_supported?(flag) SUPPORTED_FLAGS.include?(flag) 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.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/jinx/metadata/property.rb', line 107 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.
131 132 133 |
# File 'lib/jinx/metadata/property.rb', line 131 def inverse_property @inv_prop 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.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/jinx/metadata/property.rb', line 285 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_prop = inverse_property if inv_prop then inv_prop.qualify(:dependent) unless inv_prop.dependent? else inv_attr = type.dependent_attribute(@declarer) if inv_attr.nil? then raise MetadataError.new("The #{@declarer.qp} owner attribute #{self} of type #{type.qp} does not have an inverse") end logger.debug { "#{declarer.qp}.#{self} inverse is the #{type.qp} dependent attribute #{inv_attr}." } self.inverse = inv_attr end end |
#qualify(*flags) ⇒ Object
Qualifies this attribute with the given flags. Supported flags are listed in SUPPORTED_FLAGS.
139 140 141 142 143 |
# File 'lib/jinx/metadata/property.rb', line 139 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
.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/jinx/metadata/property.rb', line 174 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.
95 96 97 98 99 |
# File 'lib/jinx/metadata/property.rb', line 95 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.
219 220 221 |
# File 'lib/jinx/metadata/property.rb', line 219 def restriction?(other) @restrictions and @restrictions.include?(other) end |
#set_flag(flag) ⇒ Object (private)
268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/jinx/metadata/property.rb', line 268 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)
224 225 226 227 228 229 230 |
# File 'lib/jinx/metadata/property.rb', line 224 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
199 200 201 |
# File 'lib/jinx/metadata/property.rb', line 199 def to_s attribute.to_s end |
#writer ⇒ Symbol
Returns the writer method.
71 72 73 |
# File 'lib/jinx/metadata/property.rb', line 71 def writer accessors.last end |