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
- #new_pi ⇒ Object
- #old_preferred_identifier ⇒ 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
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 |
# File 'lib/activefacts/metamodel/extensions.rb', line 879 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
850 851 852 |
# File 'lib/activefacts/metamodel/extensions.rb', line 850 def all_subtype all_type_inheritance_as_supertype.map(&:subtype) end |
#all_supertype ⇒ Object
845 846 847 |
# File 'lib/activefacts/metamodel/extensions.rb', line 845 def all_supertype supertypes end |
#all_supertype_inheritance ⇒ Object
832 833 834 835 836 |
# File 'lib/activefacts/metamodel/extensions.rb', line 832 def all_supertype_inheritance all_type_inheritance_as_subtype.sort_by{|ti| [ti.provides_identification ? 0 : 1, ti.supertype.name] } end |
#assimilation ⇒ Object
545 546 547 |
# File 'lib/activefacts/metamodel/extensions.rb', line 545 def assimilation ti = all_type_inheritance_as_subtype.map(&:assimilation).compact[0] end |
#common_supertype(other) ⇒ Object
872 873 874 875 876 877 |
# File 'lib/activefacts/metamodel/extensions.rb', line 872 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
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 |
# File 'lib/activefacts/metamodel/extensions.rb', line 933 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
540 541 542 543 |
# File 'lib/activefacts/metamodel/extensions.rb', line 540 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
868 869 870 |
# File 'lib/activefacts/metamodel/extensions.rb', line 868 def ti = and ti.supertype end |
#identifying_type_inheritance ⇒ Object
861 862 863 864 865 |
# File 'lib/activefacts/metamodel/extensions.rb', line 861 def all_type_inheritance_as_subtype.detect do |ti| ti.provides_identification end end |
#is_partitioned ⇒ Object
554 555 556 |
# File 'lib/activefacts/metamodel/extensions.rb', line 554 def is_partitioned assimilation == 'partitioned' end |
#is_separate ⇒ Object
549 550 551 552 |
# File 'lib/activefacts/metamodel/extensions.rb', line 549 def is_separate # Independent Object Types, Entity Types marked separate and TypeInheritance marked not absorbed super || !['absorbed', nil].include?(assimilation) end |
#new_pi ⇒ Object
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 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 |
# File 'lib/activefacts/metamodel/extensions.rb', line 558 def new_pi trace :newpi, "Looking for New PI for #{name}" do # An objectified fact type is identified by the object which plays it. if fact_type && fact_type.all_role.size == 1 # REVISIT: If the OFT is separate (is this even possible?), this probably won't work: return fact_type.all_role.single.object_type.preferred_identifier end # All roles in a preferred identifier must be a counterpart # to a role played by this type or any of its supertypes. # Here, a unary role is deemed to be its own counterpart. # If this is an objectified fact type, its roles are deemed # to be counterparts per virtue of the mirror role. # if name == 'User' # pp all_role_transitive.map{|a| a.fact_type.describe(a)} # debugger # end art = all_role_transitive.map do |role| # No TypeInheritance roles unless we play the subtype if (ti = role.fact_type).is_a?(TypeInheritance) && role != ti.subtype_role nil elsif role.is_a?(MirrorRole) nil elsif role.fact_type.all_role.size == 1 role.base_role else x = (c = role.counterpart and c.base_role) # debugger if x && x.object_type == self x end end.compact all_role.each do |role| # Is this the subtype role in a supertype that identifies us? if (ti = role.base_role.fact_type).is_a?(TypeInheritance) if role == ti.subtype_role and ti.provides_identification trace :newpi, "#{name} is identified by supertype #{ti.supertype.name}" pcs = ti.supertype_role.all_role_ref.to_a.flat_map(&:role_sequence).flat_map(&:all_presence_constraint).to_a debugger if pcs.size > 1 return pcs[0] return ti.supertype_role.object_type.preferred_identifier end next # It can't be this TypeInheritance end # The fact type must be binary or unary next if role.fact_type.all_role.size > 2 base_role = role.counterpart.base_role # Go to the OFT role that implied the link fact type base_role.all_role_ref.each do |rr| if rr.role_sequence.all_presence_constraint.size > 0 trace :newpi, "Looking for New PI that includes '#{base_role.fact_type.describe(base_role)}'" end rr.role_sequence.all_presence_constraint.each do |pc| next unless pc.max_frequency == 1 and pc.is_preferred_identifier and !pc.enforcement # Alethic uniqueness constraint # We have a uniqueness constraint that is alethic and preferred. # If all its role are valid counterparts, this is our preferred identifier. next if pc.role_sequence.all_role_ref.to_a.detect do |nrr| if !art.include?(nrr.role) trace :newpi, "Rejecting New PI with excluded role '#{nrr.role.fact_type.describe(nrr.role)}'" end !art.include?(nrr.role) # Nope, not included in the valid counterparts end trace :newpi, "Accepting New PI #{pc.describe}" return pc end end end debugger trace :newpi, "No New PI found for #{name}" nil end end |
#old_preferred_identifier ⇒ Object
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 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
# File 'lib/activefacts/metamodel/extensions.rb', line 652 def old_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 ⇒ Object
640 641 642 643 644 645 646 647 648 649 650 |
# File 'lib/activefacts/metamodel/extensions.rb', line 640 def preferred_identifier return @preferred_identifier if @preferred_identifier @old_preferred_identifier = old_preferred_identifier # @new_preferred_identifier = new_pi # if @old_preferred_identifier != @new_preferred_identifier ## debugger # new_pi # end return @preferred_identifier = @old_preferred_identifier end |
#preferred_identifier_roles ⇒ Object
814 815 816 |
# File 'lib/activefacts/metamodel/extensions.rb', line 814 def preferred_identifier_roles preferred_identifier.role_sequence.all_role_ref_in_order.map(&:role) end |
#rank_in_preferred_identifier(role) ⇒ Object
818 819 820 |
# File 'lib/activefacts/metamodel/extensions.rb', line 818 def rank_in_preferred_identifier(role) preferred_identifier_roles.index(role) end |
#subtypes ⇒ Object
An array of all direct subtypes:
823 824 825 826 |
# File 'lib/activefacts/metamodel/extensions.rb', line 823 def subtypes # REVISIT: There's no sorting here. Should there be? all_type_inheritance_as_supertype.map{|ti| ti.subtype } end |
#subtypes_transitive ⇒ Object
828 829 830 |
# File 'lib/activefacts/metamodel/extensions.rb', line 828 def subtypes_transitive [self] + subtypes.map{|st| st.subtypes_transitive}.flatten.uniq end |
#supertypes ⇒ Object
An array of all direct supertypes
839 840 841 842 843 |
# File 'lib/activefacts/metamodel/extensions.rb', line 839 def supertypes all_supertype_inheritance.map{|ti| ti.supertype } end |
#supertypes_transitive ⇒ Object
An array of self followed by all supertypes in order:
855 856 857 858 859 |
# File 'lib/activefacts/metamodel/extensions.rb', line 855 def supertypes_transitive ([self] + all_type_inheritance_as_subtype.map{|ti| ti.supertype.supertypes_transitive }).flatten.uniq end |