lib/activefacts/api/instance.rb in activefacts-api-1.0.0 vs lib/activefacts/api/instance.rb in activefacts-api-1.1.0

- old
+ new

@@ -28,34 +28,10 @@ def is_a? klass super || self.class.supertypes_transitive.include?(klass) end - # If this instance's role is updated to the new value, does that cause a collision? - # We need to check each superclass that has a different identification pattern - def check_identification_change_legality(role, value) - return unless @constellation && role.is_identifying - return if @constellation.send(:instance_variable_get, :@suspend_duplicate_key_check) - - klasses = [self.class] + self.class.supertypes_transitive - last_identity = nil - last_irns = nil - counterpart_class = role.counterpart ? role.counterpart.object_type : value.class - duplicate = klasses.detect do |klass| - next false unless klass.identifying_roles.include?(role) - irns = klass.identifying_role_names - if last_irns != irns - last_identity = identifying_role_values(klass) - role_position = irns.index(role.name) - last_identity[role_position] = value.identifying_role_values(counterpart_class) - end - @constellation.instances[klass][last_identity] - end - - raise DuplicateIdentifyingValueException.new(self.class, role.name, value) if duplicate - end - # List entities which have an identifying role played by this object. def related_entities(indirectly = true, instances = []) # Check all roles of this instance self.class.all_role.each do |role_name, role| # If the counterpart role is not identifying for its object type, skip it @@ -76,10 +52,11 @@ # De-assign all functional roles and remove from constellation, if any. def retract # Delete from the constellation first, while we remember our identifying role values @constellation.deindex_instance(self) if @constellation + instance_variable_set(@@constellation_variable_name ||= "@constellation", nil) # Now, for all roles (from this class and all supertypes), assign nil to all functional roles # The counterpart roles get cleared automatically. klasses = [self.class]+self.class.supertypes_transitive @@ -91,45 +68,62 @@ counterpart = send(role.getter) role_values = counterpart.send(role.counterpart.getter) irvrvs[role_values] = role_values.index_values(self) end + # Nullify the counterpart role of objects we identify first, before damaging our identifying_role_values: klasses.each do |klass| klass.all_role.each do |role_name, role| + next if role.unary? + next if !(counterpart = role.counterpart).is_identifying + next if role.fact_type.is_a?(TypeInheritanceFactType) + + counterpart_instances = send(role.getter) + counterpart_instances.to_a.each do |counterpart_instance| + # Allow nullifying non-mandatory roles, as long as they're not identifying. + if counterpart.mandatory + counterpart_instance.retract + else + counterpart_instance.send(counterpart.setter, nil, false) + end + end + end + end + + # Now deal with other roles: + klasses.each do |klass| + klass.all_role.each do |role_name, role| next if role.unary? counterpart = role.counterpart # Objects being created do not have to have non-identifying mandatory roles, # so we allow retracting to the same state. if role.unique next if role.fact_type.is_a?(TypeInheritanceFactType) i = send(role.getter) next unless i - if counterpart.is_identifying && counterpart.mandatory - # We play a mandatory identifying role in i; so retract that (it'll clear our instance variable) - i.retract + + if (counterpart.unique) + # REVISIT: This will incorrectly fail to propagate a key change for a non-mandatory role + i.send(counterpart.setter, nil, false) else - if (counterpart.unique) - # REVISIT: This will incorrectly fail to propagate a key change for a non-mandatory role - i.send(counterpart.setter, nil, false) - else - rv = i.send(role.counterpart.getter) - rv.delete_instance(self, irvrvs[rv]) + rv = i.send(role.counterpart.getter) + rv.delete_instance(self, irvrvs[rv]) + + if (rv.empty? && !i.class.is_entity_type) + i.retract if i.plays_no_role end + end instance_variable_set(role.variable, nil) else # puts "Not removing role #{role_name} from counterpart RoleValues #{counterpart.name}" # Duplicate the array using to_a, as the RoleValues here will be modified as we traverse it: next if role.fact_type.is_a?(TypeInheritanceFactType) - counterpart_instances = send(role.name) + counterpart_instances = send(role.getter) counterpart_instances.to_a.each do |counterpart_instance| - # These actions deconstruct the RoleValues as we go: - if counterpart.is_identifying && counterpart.mandatory - counterpart_instance.retract - else - counterpart_instance.send(counterpart.setter, nil, false) - end + # This action deconstructs our RoleValues as we go: + counterpart_instance.send(counterpart.setter, nil, false) end instance_variable_set(role.variable, nil) end end end