class ActiveFacts::Metamodel::EntityType
Public Instance Methods
add_supertype(supertype, is_identifying_supertype, assimilation)
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 812 def add_supertype(supertype, is_identifying_supertype, assimilation) inheritance_fact = constellation.TypeInheritance(self, supertype, :concept => :new) inheritance_fact.assimilation = assimilation # Create a reading: sub_role = constellation.Role(inheritance_fact, 0, :object_type => self, :concept => :new) super_role = constellation.Role(inheritance_fact, 1, :object_type => supertype, :concept => :new) rs = constellation.RoleSequence(:new) constellation.RoleRef(rs, 0, :role => sub_role) constellation.RoleRef(rs, 1, :role => super_role) constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}", :is_negative => false) rs2 = constellation.RoleSequence(:new) constellation.RoleRef(rs2, 0, :role => super_role) constellation.RoleRef(rs2, 1, :role => sub_role) # Decide in which order to include is a/is an. Provide both, but in order. n = 'aeioh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : '' constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false) if is_identifying_supertype inheritance_fact.provides_identification = true end # Create uniqueness constraints over the subtyping fact type. p1rs = constellation.RoleSequence(:new) constellation.RoleRef(p1rs, 0).role = sub_role pc1 = constellation.PresenceConstraint(:new, :vocabulary => vocabulary) pc1.name = "#{name}MustHaveSupertype#{supertype.name}" pc1.role_sequence = p1rs pc1.is_mandatory = true # A subtype instance must have a supertype instance pc1.min_frequency = 1 pc1.max_frequency = 1 pc1.is_preferred_identifier = false trace :constraint, "Made new subtype PC GUID=#{pc1.concept.guid} min=1 max=1 over #{p1rs.describe}" p2rs = constellation.RoleSequence(:new) constellation.RoleRef(p2rs, 0).role = super_role pc2 = constellation.PresenceConstraint(:new, :vocabulary => vocabulary) pc2.name = "#{supertype.name}MayBeA#{name}" pc2.role_sequence = p2rs pc2.is_mandatory = false pc2.min_frequency = 0 pc2.max_frequency = 1 # The supertype role often identifies the subtype: pc2.is_preferred_identifier = inheritance_fact.provides_identification trace :supertype, "identification of #{name} via supertype #{supertype.name} was #{inheritance_fact.provides_identification ? '' : 'not '}added" trace :constraint, "Made new supertype PC GUID=#{pc2.concept.guid} min=1 max=1 over #{p2rs.describe}" inheritance_fact end
all_subtype()
click to toggle source
An array of all direct subtypes
# File lib/activefacts/metamodel/extensions.rb, line 783 def all_subtype all_type_inheritance_as_supertype.map(&:subtype) end
all_supertype()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 778 def all_supertype supertypes end
all_supertype_inheritance()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 765 def all_supertype_inheritance all_type_inheritance_as_subtype.sort_by{|ti| [ti.provides_identification ? 0 : 1, ti.supertype.name] } end
assimilation()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 571 def assimilation ti = all_type_inheritance_as_subtype.map(&:assimilation).compact[0] end
common_supertype(other)
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 805 def common_supertype(other) return nil unless other.is_a?(ActiveFacts::Metamodel::EntityType) candidates = supertypes_transitive & other.supertypes_transitive return candidates[0] if candidates.size <= 1 candidates[0] # REVISIT: This might not be the closest supertype end
create_link_fact_types()
click to toggle source
This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with objectification and mirror roles
# File lib/activefacts/metamodel/extensions.rb, line 866 def create_link_fact_types fact_type.all_role.map do |role| next if role.mirror_role_as_base_role # Already exists link_fact_type = @constellation.LinkFactType(:new, :implying_role => role) objectification_role = @constellation.Role(link_fact_type, 0, :object_type => self, :concept => :new) mirror_role = @constellation.MirrorRole( link_fact_type, 1, :concept => :new, :object_type => role.object_type, :base_role => role, :role_name => role.role_name ) link_fact_type.concept.implication_rule = objectification_role.concept.implication_rule = mirror_role.concept.implication_rule = 'objectification' link_fact_type end end
identification_is_inherited()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 566 def identification_is_inherited preferred_identifier and preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) } end
identifying_supertype()
click to toggle source
A subtype does not have a identifying_supertype
if it defines its own identifier
# File lib/activefacts/metamodel/extensions.rb, line 801 def identifying_supertype ti = identifying_type_inheritance and ti.supertype end
identifying_type_inheritance()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 794 def identifying_type_inheritance all_type_inheritance_as_subtype.detect do |ti| ti.provides_identification end end
is_partitioned()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 580 def is_partitioned assimilation == 'partitioned' end
is_separate()
click to toggle source
Calls superclass method
ActiveFacts::Metamodel::ObjectType#is_separate
# File lib/activefacts/metamodel/extensions.rb, line 575 def is_separate # Independent Object Types, Entity Types marked separate and TypeInheritance marked not absorbed super || !['absorbed', nil].include?(assimilation) end
preferred_identifier()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 584 def preferred_identifier return @preferred_identifier if @preferred_identifier if fact_type # Objectified unaries are identified by the ID of the object that plays the role: if fact_type.all_role.size == 1 entity_type = fact_type.all_role.single.object_type return @preferred_identifier = entity_type.preferred_identifier end # When compiling a fact instance, the delayed creation of a preferred identifier might be necessary if c = fact_type.check_and_add_spanning_uniqueness_constraint fact_type.check_and_add_spanning_uniqueness_constraint = nil c.call end # For a nested fact type, the PI is a unique constraint over N or N-1 roles fact_roles = Array(fact_type.all_role) trace :pi, "Looking for PI on nested fact type #{name}" do pi = catch :pi do fact_roles[0,2].each{|r| # Try the first two roles of the fact type, that's enough r.all_role_ref.map{|rr| # All role sequences that reference this role role_sequence = rr.role_sequence # The role sequence is only interesting if it cover only this fact's roles # or roles of the objectification next if role_sequence.all_role_ref.size < fact_roles.size-1 # Not enough roles next if role_sequence.all_role_ref.size > fact_roles.size # Too many roles next if role_sequence.all_role_ref.detect do |rsr| if (of = rsr.role.fact_type) != fact_type case of.all_role.size when 1 # A unary FT must be played by the objectification of this fact type next rsr.role.object_type != fact_type.entity_type when 2 # A binary FT must have the objectification of this FT as the other player other_role = (of.all_role-[rsr.role])[0] next other_role.object_type != fact_type.entity_type else next true # A role in a ternary (or higher) cannot be usd in our identifier end end rsr.role.fact_type != fact_type end # This role sequence is a candidate pc = role_sequence.all_presence_constraint.detect{|c| c.max_frequency == 1 && c.is_preferred_identifier } throw :pi, pc if pc } } throw :pi, nil end trace :pi, "Got PI #{pi.name||pi.object_id} for nested #{name}" if pi trace :pi, "Looking for PI on entity that nests this fact" unless pi raise "Oops, pi for nested fact is #{pi.class}" unless !pi || pi.is_a?(ActiveFacts::Metamodel::PresenceConstraint) return @preferred_identifier = pi if pi end end trace :pi, "Looking for PI for ordinary entity #{name} with #{all_role.size} roles:" do trace :pi, "Roles are in fact types #{all_role.map{|r| r.fact_type.describe(r)}*", "}" pi = catch :pi do all_supertypes = supertypes_transitive trace :pi, "PI roles must be played by one of #{all_supertypes.map(&:name)*", "}" if all_supertypes.size > 1 all_role.each{|role| next unless role.is_unique || fact_type next if role.fact_type.is_a?(TypeInheritance) && !role.fact_type.provides_identification ftroles = Array(role.fact_type.all_role) # Skip roles in ternary and higher fact types, they're objectified # REVISIT: This next line prevents a unary being used as a preferred_identifier: next if ftroles.size != 2 trace :pi, "Considering role in #{role.fact_type.describe(role)}" # Find the related role which must be included in any PI: # Note this works with unary fact types: pi_role = ftroles[ftroles[0] != role ? 0 : -1] next if ftroles.size == 2 && pi_role.object_type == self trace :pi, " Considering #{pi_role.object_type.name} as a PI role" # If this is an identifying role, the PI is a PC whose role_sequence spans the role. # Walk through all role_sequences that span this role, and test each: pi_role.all_role_ref.each{|rr| role_sequence = rr.role_sequence # A role sequence that includes a possible role trace :pi, " Considering role sequence #{role_sequence.describe}" # All roles in this role_sequence must be in fact types which # (apart from that role) only have roles played by the original # entity type or a supertype. #trace :pi, " All supertypes #{all_supertypes.map{|st| "#{st.object_id}=>#{st.name}"}*", "}" if role_sequence.all_role_ref.detect{|rsr| fact_type = rsr.role.fact_type trace :pi, " Role Sequence touches #{fact_type.describe(pi_role)}" fact_type_roles = fact_type.all_role trace :pi, " residual is #{fact_type_roles.map{|r| r.object_type.name}.inspect} minus #{rsr.role.object_type.name}" residual_roles = fact_type_roles-[rsr.role] residual_roles.detect{|rfr| trace :pi, " Checking residual role #{rfr.object_type.object_id}=>#{rfr.object_type.name}" # This next line looks right, but breaks things. Find out what and why: # !rfr.unique or !all_supertypes.include?(rfr.object_type) } } trace :pi, " Discounting this role_sequence because it includes alien roles" next end # Any presence constraint over this role sequence is a candidate rr.role_sequence.all_presence_constraint.detect{|pc| # Found it! if pc.is_preferred_identifier trace :pi, "found PI #{pc.name||pc.object_id}, is_preferred_identifier=#{pc.is_preferred_identifier.inspect} over #{pc.role_sequence.describe}" throw :pi, pc end } } } throw :pi, nil end raise "Oops, pi for entity is #{pi.class}" if pi && !pi.is_a?(ActiveFacts::Metamodel::PresenceConstraint) trace :pi, "Got PI #{pi.name||pi.object_id} for #{name}" if pi if !pi if (supertype = identifying_supertype) # This shouldn't happen now, as an identifying supertype is connected by a fact type # that has a uniqueness constraint marked as the preferred identifier. #trace :pi, "PI not found for #{name}, looking in supertype #{supertype.name}" #pi = supertype.preferred_identifier #return nil elsif fact_type possible_pi = nil fact_type.all_role.each{|role| role.all_role_ref.each{|role_ref| # Discount role sequences that contain roles not in this fact type: next if role_ref.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type != fact_type } role_ref.role_sequence.all_presence_constraint.each{|pc| next unless pc.max_frequency == 1 possible_pi = pc next unless pc.is_preferred_identifier pi = pc break } break if pi } break if pi } if !pi && possible_pi trace :pi, "Using existing PC as PI for #{name}" pi = possible_pi end else trace :pi, "No PI found for #{name}" debugger if respond_to?(:debugger) end end raise "No PI found for #{name}" unless pi @preferred_identifier = pi end end
preferred_identifier_roles()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 747 def preferred_identifier_roles preferred_identifier.role_sequence.all_role_ref_in_order.map(&:role) end
rank_in_preferred_identifier(role)
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 751 def rank_in_preferred_identifier(role) preferred_identifier_roles.index(role) end
subtypes()
click to toggle source
An array of all direct subtypes:
# File lib/activefacts/metamodel/extensions.rb, line 756 def subtypes # REVISIT: There's no sorting here. Should there be? all_type_inheritance_as_supertype.map{|ti| ti.subtype } end
subtypes_transitive()
click to toggle source
# File lib/activefacts/metamodel/extensions.rb, line 761 def subtypes_transitive [self] + subtypes.map{|st| st.subtypes_transitive}.flatten.uniq end
supertypes()
click to toggle source
An array of all direct supertypes
# File lib/activefacts/metamodel/extensions.rb, line 772 def supertypes all_supertype_inheritance.map{|ti| ti.supertype } end
supertypes_transitive()
click to toggle source
An array of self followed by all supertypes in order:
# File lib/activefacts/metamodel/extensions.rb, line 788 def supertypes_transitive ([self] + all_type_inheritance_as_subtype.map{|ti| ti.supertype.supertypes_transitive }).flatten.uniq end