lib/activefacts/compositions/constraints.rb in activefacts-compositions-1.9.6 vs lib/activefacts/compositions/constraints.rb in activefacts-compositions-1.9.8

- old
+ new

@@ -7,78 +7,98 @@ module ActiveFacts module Metamodel class Composition def retract_constraint_classifications - all_composite.each(&:retract_constraint_classifications) + all_composite.each(&:retract_constraint_classifications) end def classify_constraints - retract_constraint_classifications - all_composite.each(&:classify_constraints) + retract_constraint_classifications + all_composite.each(&:classify_constraints) end end + class Component + def gather_constraints all_composite_roles = [], all_composite_constraints = [], constraints_by_leaf = {} + all_role.each do |role| + all_composite_roles << role + role.all_constraint.each do |constraint| + # Exclude single-role mandatory constraints and all uniqueness constraints: + next if constraint.is_a?(PresenceConstraint) and + constraint.max_frequency == 1 || + (constraint.role_sequence.all_role_ref.size == 1 && constraint.min_frequency == 1 && constraint.is_mandatory) + all_composite_constraints << constraint + (constraints_by_leaf[self] ||= []) << constraint + end + end + + gather_nested_constraints all_composite_roles, all_composite_constraints, constraints_by_leaf + end + + def gather_nested_constraints all_composite_roles, all_composite_constraints, constraints_by_leaf + all_member.each do |member| + member.gather_constraints all_composite_roles, all_composite_constraints, constraints_by_leaf + end + end + end + + class Absorption + def gather_nested_constraints all_composite_roles, all_composite_constraints, constraints_by_leaf + return if foreign_key # This has gone far enough! + super + end + end + class Composite def retract_constraint_classifications - all_spanning_constraint.to_a.each(&:retract) - all_local_constraint.to_a.each(&:retract) - mapping.leaves.each do |component| - component.all_leaf_constraint.to_a.each(&:retract) - end + all_spanning_constraint.to_a.each(&:retract) + all_local_constraint.to_a.each(&:retract) + mapping.all_leaf.each do |component| + component.all_leaf_constraint.to_a.each(&:retract) + end end def classify_constraints - leaves = mapping.leaves + leaves = mapping.all_leaf - # Categorise and index all constraints not already baked-in to the composition - all_composite_roles = [] - all_composite_constraints = [] - constraints_by_leaf = {} - leaves.each do |leaf| - all_composite_roles += leaf.path.flat_map(&:all_role) # May be non-unique, fix later - leaf.all_role.each do |role| - role.all_constraint.each do |constraint| - if constraint.is_a?(PresenceConstraint) - # Exclude single-role mandatory constraints and all uniqueness constraints: - if constraint.role_sequence.all_role_ref.size == 1 && constraint.min_frequency == 1 && constraint.is_mandatory or - constraint.max_frequency == 1 - next - end - end - all_composite_constraints << constraint - (constraints_by_leaf[leaf] ||= []) << constraint - end - end - end + # Categorise and index all constraints not already baked-in to the composition + # We recurse down the hierarchy, stopping at any foreign keys + all_composite_roles = [] + all_composite_constraints = [] + constraints_by_leaf = {} + mapping.gather_constraints all_composite_roles, all_composite_constraints, constraints_by_leaf + all_composite_roles.uniq! + all_composite_constraints.uniq! - all_composite_roles.uniq! - all_composite_constraints.uniq! - spanning_constraints = - all_composite_constraints.reject do |constraint| - (constraint.all_constrained_role-all_composite_roles).size == 0 - end - local_constraints = all_composite_constraints - spanning_constraints + # Spanning constraints constrain some role outside this composite: + spanning_constraints = + all_composite_constraints.select do |constraint| + (constraint.all_constrained_role-all_composite_roles).size > 0 + end + spanning_constraints.each do |spanning_constraint| + constellation.SpanningConstraint(composite: self, spanning_constraint: spanning_constraint) + end - spanning_constraints.each do |spanning_constraint| - constellation.SpanningConstraint(composite: self, spanning_constraint: spanning_constraint) - end + # Local and leaf constraints are what remains. Extract the leaf constraints: + local_constraints = all_composite_constraints - spanning_constraints + leaves.each do |leaf| + # Find any constraints that affect just this leaf: + leaf_constraints = + leaf.path.flat_map{|component| Array(constraints_by_leaf[component]) }. + select do |constraint| + # Does this constraint constrain only this leaf? + (constraint.all_constrained_role - leaf.path.flat_map(&:all_role)).size == 0 + end + leaf_constraints.each do |leaf_constraint| + constellation.LeafConstraint(component: leaf, leaf_constraint: leaf_constraint) + end + local_constraints -= leaf_constraints + end - leaves.each do |leaf| - # Find any constraints that affect just this leaf: - leaf_constraints = (constraints_by_leaf[leaf]||[]). - reject do |constraint| - (constraint.all_constrained_role - leaf.all_role).size > 0 - end - local_constraints -= leaf_constraints - leaf_constraints.each do |leaf_constraint| - constellation.LeafConstraint(component: leaf, leaf_constraint: leaf_constraint) - end - end - - local_constraints.each do |local_constraint| - constellation.LocalConstraint(composite: self, local_constraint: local_constraint) - end + local_constraints.each do |local_constraint| + constellation.LocalConstraint(composite: self, local_constraint: local_constraint) + end end end end