Class: ActiveFacts::Metamodel::EntityType
- Inherits:
-
DomainObjectType
- Object
- ObjectType
- DomainObjectType
- ActiveFacts::Metamodel::EntityType
- Defined in:
- lib/activefacts/metamodel/metamodel.rb,
lib/activefacts/metamodel/extensions.rb
Instance Attribute Summary
Attributes inherited from ObjectType
Instance Method Summary collapse
- #add_supertype(supertype, is_identifying_supertype, assimilation) ⇒ Object
-
#all_subtype ⇒ Object
An array of all direct subtypes.
- #all_supertype ⇒ Object
- #all_supertype_inheritance ⇒ Object
- #assimilation ⇒ Object
- #common_supertype(other) ⇒ Object
-
#create_link_fact_types ⇒ Object
This entity type has just objectified a fact type.
- #identification_is_inherited ⇒ Object
-
#identifying_supertype ⇒ Object
A subtype does not have a identifying_supertype if it defines its own identifier.
- #identifying_type_inheritance ⇒ Object
- #is_partitioned ⇒ Object
- #is_separate ⇒ Object
- #preferred_identifier ⇒ Object
- #preferred_identifier_roles ⇒ Object
- #rank_in_preferred_identifier(role) ⇒ Object
-
#subtypes ⇒ Object
An array of all direct subtypes:.
- #subtypes_transitive ⇒ Object
-
#supertypes ⇒ Object
An array of all direct supertypes.
-
#supertypes_transitive ⇒ Object
An array of self followed by all supertypes in order:.
Methods inherited from ObjectType
#all_role_transitive, #is_static
Instance Method Details
#add_supertype(supertype, is_identifying_supertype, assimilation) ⇒ Object
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
# File 'lib/activefacts/metamodel/extensions.rb', line 812 def add_supertype(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 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 ⇒ Object
An array of all direct subtypes
783 784 785 |
# File 'lib/activefacts/metamodel/extensions.rb', line 783 def all_subtype all_type_inheritance_as_supertype.map(&:subtype) end |
#all_supertype ⇒ Object
778 779 780 |
# File 'lib/activefacts/metamodel/extensions.rb', line 778 def all_supertype supertypes end |
#all_supertype_inheritance ⇒ Object
765 766 767 768 769 |
# 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 ⇒ Object
571 572 573 |
# File 'lib/activefacts/metamodel/extensions.rb', line 571 def assimilation ti = all_type_inheritance_as_subtype.map(&:assimilation).compact[0] end |
#common_supertype(other) ⇒ Object
805 806 807 808 809 810 |
# 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 ⇒ Object
This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with objectification and mirror roles
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 |
# 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 ⇒ Object
566 567 568 569 |
# 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 ⇒ Object
A subtype does not have a identifying_supertype if it defines its own identifier
801 802 803 |
# File 'lib/activefacts/metamodel/extensions.rb', line 801 def ti = and ti.supertype end |
#identifying_type_inheritance ⇒ Object
794 795 796 797 798 |
# File 'lib/activefacts/metamodel/extensions.rb', line 794 def all_type_inheritance_as_subtype.detect do |ti| ti.provides_identification end end |
#is_partitioned ⇒ Object
580 581 582 |
# File 'lib/activefacts/metamodel/extensions.rb', line 580 def is_partitioned assimilation == 'partitioned' end |
#is_separate ⇒ Object
575 576 577 578 |
# 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 ⇒ Object
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 |
# 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 = ) # 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 ⇒ Object
747 748 749 |
# 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) ⇒ Object
751 752 753 |
# File 'lib/activefacts/metamodel/extensions.rb', line 751 def rank_in_preferred_identifier(role) preferred_identifier_roles.index(role) end |
#subtypes ⇒ Object
An array of all direct subtypes:
756 757 758 759 |
# 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 ⇒ Object
761 762 763 |
# File 'lib/activefacts/metamodel/extensions.rb', line 761 def subtypes_transitive [self] + subtypes.map{|st| st.subtypes_transitive}.flatten.uniq end |
#supertypes ⇒ Object
An array of all direct supertypes
772 773 774 775 776 |
# File 'lib/activefacts/metamodel/extensions.rb', line 772 def supertypes all_supertype_inheritance.map{|ti| ti.supertype } end |
#supertypes_transitive ⇒ Object
An array of self followed by all supertypes in order:
788 789 790 791 792 |
# 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 |