lib/activefacts/api/instance.rb in activefacts-api-1.9.5 vs lib/activefacts/api/instance.rb in activefacts-api-1.9.6

- old
+ new

@@ -13,14 +13,14 @@ def initialize(args = []) #:nodoc: unless (self.class.is_entity_type) begin super(*args) - rescue TypeError => e - if trace(:debug) - p e; puts e.backtrace*"\n\t"; debugger; true - end + rescue TypeError => e + if trace(:debug) + p e; puts e.backtrace*"\n\t"; debugger; true + end rescue ArgumentError => e e.message << " constructing a #{self.class}" raise end end @@ -30,124 +30,129 @@ super || self.class.supertypes_transitive.include?(klass) end # List entities which have an identifying role played by this object. def related_entities(indirectly = true, instances = []) - # Check all roles of this instance + # 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 - next unless c = role.counterpart and c.is_identifying + # If the counterpart role is not identifying for its object type, skip it + next unless c = role.counterpart and c.is_identifying - identified_instances = Array(self.send(role.getter)) - instances.concat(identified_instances) - identified_instances.each do |instance| - instance.related_entities(indirectly, instances) if indirectly - end + identified_instances = Array(self.send(role.getter)) + instances.concat(identified_instances) + identified_instances.each do |instance| + instance.related_entities(indirectly, instances) if indirectly + end end instances end def instance_index @constellation.send(self.class.basename.to_sym) end # De-assign all functional roles and remove from constellation, if any. def retract - return unless constellation = @constellation + return unless constellation = @constellation - unless constellation.loggers.empty? - # An object may have multiple identifiers, with potentially overlapping role sets - # Get one copy of each role to use in asserting the instance - if self.class.is_entity_type - identifying_role_values = {} - ([self.class]+self.class.supertypes_transitive).each do |klass| - klass.identifying_role_names.zip(identifying_role_values(klass)).each do |name, value| - identifying_role_values[name] = value - end - end - else - identifying_role_values = self - end - end + unless constellation.loggers.empty? + # An object may have multiple identifiers, with potentially overlapping role sets + # Get one copy of each role to use in asserting the instance + if self.class.is_entity_type + identifying_role_values = {} + ([self.class]+self.class.supertypes_transitive).each do |klass| + klass.identifying_role_names.zip(identifying_role_values(klass)).each do |name, value| + identifying_role_values[name] = value + end + end + else + identifying_role_values = self + end + end # Delete from the constellation first, while we remember our identifying role values constellation.deindex_instance(self) - instance_variable_set(@@constellation_variable_name ||= "@constellation", nil) + 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 + klasses = [self.class]+self.class.supertypes_transitive - irvrvs = {} # identifying_role_values by RoleValues - self.class.all_role_transitive.each do |_, role| - next unless role.counterpart and - role.unique and - !role.counterpart.unique and - counterpart = send(role.getter) - role_values = counterpart.send(role.counterpart.getter) - irvrvs[role_values] = role_values.index_values(self) - end + irvrvs = {} # identifying_role_values by RoleValues + self.class.all_role_transitive.each do |_, role| + next unless role.counterpart and + role.unique and + !role.counterpart.unique and + 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| + # 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) + 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 + 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| + # 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. + # 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 + next if role.fact_type.is_a?(TypeInheritanceFactType) + counterpart_instance = send(role.getter) + next unless counterpart_instance && counterpart_instance.constellation - 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]) + if (counterpart.unique) + # REVISIT: This will incorrectly fail to propagate a key change for a non-mandatory role + counterpart_instance.send(counterpart.setter, nil, false) + else + rv = counterpart_instance.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 + if (rv.empty? && !counterpart_instance.class.is_entity_type) + counterpart_instance.retract if counterpart_instance.plays_no_role + end - end - instance_variable_set(role.variable, nil) + 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.getter) - counterpart_instances.to_a.each do |counterpart_instance| - # This action deconstructs our RoleValues as we go: - counterpart_instance.send(counterpart.setter, nil, false) + next if role.fact_type.is_a?(TypeInheritanceFactType) + counterpart_instances = send(role.getter) + counterpart_instances.to_a.each do |counterpart_instance| + next unless counterpart_instance.constellation + # This action deconstructs our RoleValues as we go: + if counterpart.mandatory + counterpart_instance.retract + else + counterpart_instance.send(counterpart.setter, nil, false) + end end - instance_variable_set(role.variable, nil) + instance_variable_set(role.variable, nil) end end end - constellation.loggers.each{|l| l.call(:retract, self.class, identifying_role_values) } + constellation.loggers.each{|l| l.call(:retract, self.class, identifying_role_values) } end module ClassMethods #:nodoc: include ObjectType