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