Class: ActiveFacts::RMap::Reference
- Inherits:
-
Object
- Object
- ActiveFacts::RMap::Reference
- Defined in:
- lib/activefacts/rmap/reference.rb,
lib/activefacts/rmap/columns.rb
Overview
This class contains the core data structure used in composing a relational schema.
A Reference is from one ObjectType to another ObjectType, and relates to the from_role and the to_role. When either ObjectType is an objectified fact type, the corresponding role is nil. When the Reference from_role is of a unary fact type, there’s no to_role or to ObjectType. The final kind of Reference is a self-reference which is added to a ValueType that becomes a table.
When the underlying fact type is a one-to-one (including an inheritance fact type), the Reference may be flipped.
Each Reference has a name; an array of names in fact, in case of adjectives, etc. Each Refererence can produce the reading of the underlying fact type.
A Reference is indexed in the player’s references_from and references_to, and flipping updates those. Finally, a Reference may be marked as absorbing the whole referenced object, and that can flip too.
Instance Attribute Summary collapse
-
#fact_type ⇒ Object
readonly
Returns the value of attribute fact_type.
-
#fk_jump ⇒ Object
True if this reference links a table to another in an FK (between absorbed references).
-
#from ⇒ Object
readonly
A “from” instance is related to one “to” instance.
-
#from_role ⇒ Object
readonly
For objectified facts, one role will be nil (a phantom).
-
#to ⇒ Object
readonly
A “from” instance is related to one “to” instance.
-
#to_role ⇒ Object
readonly
For objectified facts, one role will be nil (a phantom).
Instance Method Summary collapse
-
#columns(excluded_supertypes) ⇒ Object
:nodoc:.
-
#detabulate ⇒ Object
:nodoc:.
-
#flip ⇒ Object
For a one-to-one (or a subtyping fact type), reverse the direction.
-
#from_names ⇒ Object
Return the array of names for the (perhaps implicit) from_role of this Reference.
-
#initialize(from, role) ⇒ Reference
constructor
A Reference is created from a object_type in regard to a role it plays.
-
#inspect ⇒ Object
:nodoc:.
-
#is_absorbing ⇒ Object
Is the to object_type fully absorbed through this reference?.
-
#is_from_objectified_fact ⇒ Object
If this Reference is from an objectified FactType, there is no from_role.
-
#is_mandatory ⇒ Object
Is this Reference covered by a mandatory constraint (implicitly or explicitly).
- #is_one_to_one ⇒ Object
-
#is_self_value ⇒ Object
Is this reference an injected role as a result a ValueType being a table?.
-
#is_simple_reference ⇒ Object
Is this a simple reference?.
-
#is_to_objectified_fact ⇒ Object
If this Reference is to an objectified FactType, there is no to_role.
-
#is_unary ⇒ Object
Is this Reference from a unary Role?.
-
#mirror ⇒ Object
Create a (non-tabulated) flipped version of this Reference.
-
#reading ⇒ Object
The reading for the fact type underlying this Reference.
- #reversed ⇒ Object
-
#role_type ⇒ Object
What type of Role did this Reference arise from?.
-
#tabulate ⇒ Object
:nodoc:.
-
#to_names(is_prefix = true) ⇒ Object
Return the array of names for the (perhaps implicit) to_role of this Reference.
-
#to_s ⇒ Object
:nodoc:.
- #verbalised_path(reverse = false) ⇒ Object
Constructor Details
#initialize(from, role) ⇒ Reference
A Reference is created from a object_type in regard to a role it plays
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/activefacts/rmap/reference.rb', line 45 def initialize(from, role) @fk_jump = false @from = from return unless role # All done if it's a self-value reference for a ValueType @fact_type = role.fact_type if @fact_type.all_role.size == 1 # @from_role is nil for a unary @to_role = role @to = role.fact_type.entity_type # nil unless the unary is objectified elsif (role.fact_type.entity_type == @from) # role is in "from", an objectified fact type @from_role = nil # Phantom role @to_role = role @to = @to_role.object_type else @from_role = role @to = role.fact_type.entity_type # If set, to_role is a phantom unless @to raise "Illegal reference through >binary fact type" if @fact_type.all_role.size >2 @to_role = (role.fact_type.all_role-[role])[0] @to = @to_role.object_type end end end |
Instance Attribute Details
#fact_type ⇒ Object (readonly)
Returns the value of attribute fact_type.
41 42 43 |
# File 'lib/activefacts/rmap/reference.rb', line 41 def fact_type @fact_type end |
#fk_jump ⇒ Object
True if this reference links a table to another in an FK (between absorbed references)
42 43 44 |
# File 'lib/activefacts/rmap/reference.rb', line 42 def fk_jump @fk_jump end |
#from ⇒ Object (readonly)
A “from” instance is related to one “to” instance
39 40 41 |
# File 'lib/activefacts/rmap/reference.rb', line 39 def from @from end |
#from_role ⇒ Object (readonly)
For objectified facts, one role will be nil (a phantom)
40 41 42 |
# File 'lib/activefacts/rmap/reference.rb', line 40 def from_role @from_role end |
#to ⇒ Object (readonly)
A “from” instance is related to one “to” instance
39 40 41 |
# File 'lib/activefacts/rmap/reference.rb', line 39 def to @to end |
#to_role ⇒ Object (readonly)
For objectified facts, one role will be nil (a phantom)
40 41 42 |
# File 'lib/activefacts/rmap/reference.rb', line 40 def to_role @to_role end |
Instance Method Details
#columns(excluded_supertypes) ⇒ Object
:nodoc:
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/activefacts/rmap/columns.rb', line 194 def columns(excluded_supertypes) #:nodoc: kind = "" cols = if is_unary kind = "unary " objectified_unary_columns = ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : []) =begin # This code omits the unary if it's objectified and that plays a mandatory role first_mandatory_column = nil if (@to && @to.fact_type) trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do first_mandatory_column = objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary trace :unary_col, "checking column #{col.name}" do !col.references.detect do |ref| trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}" !ref.is_mandatory end end end if is_from_objectified_fact && first_mandatory_column trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory" end end end (is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified =end [Column.new()] + # The unary itself objectified_unary_columns elsif is_self_value kind = "self-role " [Column.new()] elsif is_simple_reference @to.reference_columns(excluded_supertypes) else kind = "absorbing " @to.all_columns(excluded_supertypes) end cols.each do |c| c.prepend self end trace :columns, "Columns from #{kind}#{self}" do cols.each {|c| trace :columns, "#{c}" } end end |
#detabulate ⇒ Object
:nodoc:
197 198 199 200 201 202 203 |
# File 'lib/activefacts/rmap/reference.rb', line 197 def detabulate #:nodoc: # Remove from @to and @from's reference lists if present return unless @from.references_from.delete(self) @to.references_to.delete self if @to # Guard against self-values trace :references, "Dropping #{to_s}" self end |
#flip ⇒ Object
For a one-to-one (or a subtyping fact type), reverse the direction.
162 163 164 165 166 167 168 |
# File 'lib/activefacts/rmap/reference.rb', line 162 def flip #:nodoc: raise "Illegal flip of #{self}" unless @to and is_one_to_one detabulate mirror tabulate end |
#from_names ⇒ Object
Return the array of names for the (perhaps implicit) from_role of this Reference
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/activefacts/rmap/reference.rb', line 137 def from_names case when @from && !@from_role # @from is an objectified fact type so @from_role is a phantom @from.name.camelwords when is_unary if @from && @from.fact_type @from.name.camelwords else @from_role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.camelwords end when !@from_role # Self-value role of an independent ValueType @from.name.camelwords + ["Value"] when @from_role.role_name # Named role @from_role.role_name.camelwords else # Use the name from the preferred reading role_ref = @from_role.preferred_reference [role_ref.leading_adjective, @from_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''} end end |
#inspect ⇒ Object
:nodoc:
235 236 237 |
# File 'lib/activefacts/rmap/reference.rb', line 235 def inspect #:nodoc: to_s end |
#is_absorbing ⇒ Object
Is the to object_type fully absorbed through this reference?
104 105 106 |
# File 'lib/activefacts/rmap/reference.rb', line 104 def is_absorbing @to && @to.absorbed_via == self end |
#is_from_objectified_fact ⇒ Object
If this Reference is from an objectified FactType, there is no from_role
94 95 96 |
# File 'lib/activefacts/rmap/reference.rb', line 94 def is_from_objectified_fact @to && !@from_role && @to_role end |
#is_mandatory ⇒ Object
Is this Reference covered by a mandatory constraint (implicitly or explicitly)
76 77 78 79 80 |
# File 'lib/activefacts/rmap/reference.rb', line 76 def is_mandatory !is_unary && (!@from_role || # All phantom roles of fact types are mandatory @from_role.is_mandatory) end |
#is_one_to_one ⇒ Object
157 158 159 |
# File 'lib/activefacts/rmap/reference.rb', line 157 def is_one_to_one [:one_one, :subtype, :supertype].include?(role_type) end |
#is_self_value ⇒ Object
Is this reference an injected role as a result a ValueType being a table?
99 100 101 |
# File 'lib/activefacts/rmap/reference.rb', line 99 def is_self_value !@to && !@to_role end |
#is_simple_reference ⇒ Object
Is this a simple reference?
109 110 111 112 113 |
# File 'lib/activefacts/rmap/reference.rb', line 109 def is_simple_reference # It's a simple reference to a thing if that thing is a table, # or is fully absorbed into another table but not via this reference. @to && (@to.is_table or @to.absorbed_via && !is_absorbing) end |
#is_to_objectified_fact ⇒ Object
If this Reference is to an objectified FactType, there is no to_role
88 89 90 91 |
# File 'lib/activefacts/rmap/reference.rb', line 88 def is_to_objectified_fact # This case is the only one that cannot be used in the preferred identifier of @from @to && !@to_role && @from_role end |
#is_unary ⇒ Object
Is this Reference from a unary Role?
83 84 85 |
# File 'lib/activefacts/rmap/reference.rb', line 83 def is_unary @to_role && @to_role.fact_type.all_role.size == 1 end |
#mirror ⇒ Object
Create a (non-tabulated) flipped version of this Reference. Careful not to tabulate it!
171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/activefacts/rmap/reference.rb', line 171 def mirror if @to.absorbed_via == self @to.absorbed_via = nil @from.absorbed_via = self end # Flip the reference @to, @from = @from, @to @to_role, @from_role = @from_role, @to_role trace :references, "Mirror #{self.inspect} absorbs #{@to.name}" if @to.absorbed_via == self self end |
#reading ⇒ Object
The reading for the fact type underlying this Reference
211 212 213 |
# File 'lib/activefacts/rmap/reference.rb', line 211 def reading is_self_value ? "#{from.name} has value" : @fact_type.reading_preferably_starting_with_role(@from_role). end |
#reversed ⇒ Object
184 185 186 |
# File 'lib/activefacts/rmap/reference.rb', line 184 def reversed clone.mirror end |
#role_type ⇒ Object
What type of Role did this Reference arise from?
70 71 72 73 |
# File 'lib/activefacts/rmap/reference.rb', line 70 def role_type role = @from_role||@to_role role && role.role_type end |
#tabulate ⇒ Object
:nodoc:
188 189 190 191 192 193 194 195 |
# File 'lib/activefacts/rmap/reference.rb', line 188 def tabulate #:nodoc: # Add to @to and @from's reference lists @from.references_from << self @to.references_to << self if @to # Guard against self-values trace :references, "Adding #{to_s}" self end |
#to_names(is_prefix = true) ⇒ Object
Return the array of names for the (perhaps implicit) to_role of this Reference
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/activefacts/rmap/reference.rb', line 116 def to_names(is_prefix = true) case when is_unary if @to && @to.fact_type && is_prefix @to.name.camelwords else @to_role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.camelwords end when @to && !@to_role # @to is an objectified fact type so @to_role is a phantom @to.name.camelwords when !@to_role # Self-value role of an independent ValueType @from.name.camelwords + ["Value"] when @to_role.role_name # Named role @to_role.role_name.camelwords else # Use the name from the preferred reading role_ref = @to_role.preferred_reference [role_ref.leading_adjective, @to_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''} end end |
#to_s ⇒ Object
:nodoc:
205 206 207 208 |
# File 'lib/activefacts/rmap/reference.rb', line 205 def to_s #:nodoc: ref_type = fk_jump ? "jumping to" : (is_absorbing ? "absorbing" : "to") "reference from #{@from.name}#{@to ? " #{ref_type} #{@to.name}" : ""}" + (@fact_type ? " in '#{@fact_type.default_reading}'" : "") end |
#verbalised_path(reverse = false) ⇒ Object
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/activefacts/rmap/reference.rb', line 215 def verbalised_path reverse = false return "#{from.name} Value" if is_self_value objectified = fact_type.entity_type f = # Switch to the Link Fact Type if we're traversing an objectification (to_role && to_role.link_fact_type) || (from_role && from_role.link_fact_type) || fact_type start_role = if objectified target = reverse ? to : from [to_role, from_role, f.all_role_in_order[0]].compact.detect{|role| role.object_type == target} else reverse ? to_role : from_role end reading = f.reading_preferably_starting_with_role(start_role) (is_mandatory || is_unary ? '' : 'maybe ') + reading. end |