Permalink
Browse files

Fix tests for multipart_identification_spec.

Adds information into related_entities. It should contain a RoleValue
object when the role is pointing to many entities (see unique attribute
for roles).

It also adds a way to query entities in a two-way fashion. Which means
you can now search for entities which references the current one, but
also entities which are referenced by the current one.
  • Loading branch information...
1 parent d751d26 commit d5c29e24bc050a8ac273b758197fdaab8f60d946 @kouno committed Sep 14, 2012
@@ -80,4 +80,4 @@ def flatten_key(key)
end
end
end
-end
+end
@@ -66,6 +66,11 @@ def retract(*instances)
self
end
+ # Get instance from InstanceIndex.
+ def get_instance(entity_class, args)
+ instances[entity_class][args]
+ end
+
# Copies object type across constellations.
def copy(value)
send(value.class.basename, value.clone_identity)
@@ -110,7 +115,7 @@ def verbalise
instances.map do |key, instance|
s = "\t\t" + instance.verbalise
if (single_roles.size > 0)
- role_values =
+ role_values =
single_roles.map{|role|
[ role_name = role.to_s.camelcase,
value = instance.send(role)]
@@ -216,23 +216,26 @@ def identifying_role_values(*args)
end
end
+ def has_unused_params?(assert_args)
+ args, arg_hash = ActiveFacts.extract_hash_args(identifying_role_names, assert_args)
+ !arg_hash.empty?
+ end
+
# REVISIT: This method should verify that all identifying roles (including
# those required to identify any superclass) are present (if mandatory)
# and are unique... BEFORE it creates any new object(s)
# This is a hard problem because it's recursive.
def assert_instance(constellation, args) #:nodoc:
- # Build the key for this instance from the args
- # The key of an instance is the value or array of keys of the identifying values.
- # The key values aren't necessarily present in the constellation, even after this.
+ # Hijack assert_instance if an instance is already present.
key = identifying_role_values(*args)
- # Find and return an existing instance matching this key
- instances = constellation.instances[self] # All instances of this class in this constellation
- instance = instances[key]
- # REVISIT: This ignores any additional attribute assignments
+ instance = constellation.get_instance(self, key)
if instance
- raise "Additional role values are ignored when asserting an existing instance" if args[-1].is_a? Hash and !args[-1].empty?
- return instance, key # A matching instance of this class
+ if has_unused_params?(args)
+ raise "Additional role values are ignored when asserting an existing instance"
+ else
+ return instance, key
+ end
end
# Now construct each of this object's identifying roles
@@ -244,7 +247,7 @@ def assert_instance(constellation, args) #:nodoc:
# We received a single argument of a compatible type
# With a secondary supertype or a type having separate identification,
# we would get the wrong identifier from arg.identifying_role_values:
- key =
+ key =
values = identifying_role_values(args[0])
values = values + [arg_hash = args.pop] if has_hash
else
@@ -8,9 +8,12 @@ module ActiveFacts
module API
class DuplicateIdentifyingValueException < StandardError
def initialize(desc)
+ verbalised_entities = desc[:value].related_entities.map do |entity, role_obj, role_value|
+ entity.verbalise
+ end
super("Illegal attempt to assert #{desc[:class].basename} having identifying value" +
" (#{desc[:role].name} is #{desc[:value].verbalise})," +
- " when #{desc[:value].related_entities.map(&:verbalise).join(", ")} already exists")
+ " when #{verbalised_entities} already exists")
end
end
end
@@ -38,6 +38,11 @@ def each(&block)
@hash.each(&block)
end
end
+
+ def refresh_key(key)
+ value = self.delete(key)
+ self.[]=(value, value) if value
+ end
end
end
end
@@ -81,10 +81,13 @@ def is_unique?(args)
# related entities of this instance.
def related_entities(instances = [])
self.class.roles.each do |role_name, role|
- instance_index_counterpart(role).each do |irv, instance|
+ instance_index_counterpart(role).each do |instance|
if instance.class.is_entity_type && instance.is_identified_by?(self)
- if !instances.include?(instance)
- instances << instance
+ if !instances.any? { |i, r, iic| i == instance }
+ counterpart_role = instance.is_identified_by?(self)
+ role_value = instance_index_counterpart(counterpart_role)
+ role_value = role_value.is_a?(RoleValues) ? role_value : nil
+ instances << [instance, role, role_value]
instance.related_entities(instances)
end
end
@@ -93,6 +96,33 @@ def related_entities(instances = [])
instances
end
+ def reverse_related_entities(instances = [])
+ self.class.roles.each do |role_name, role|
+ instance_index_counterpart(role).each do |instance|
+ if self.class.is_entity_type && self.is_identified_by?(instance)
+ if !instances.any? { |i, r, iic| i == instance }
+ counterpart_role = self.is_identified_by?(instance)
+ role_value = instance.send(counterpart_role.counterpart.name)
+ role_value = role_value.is_a?(RoleValues) ? role_value : nil
+ instances << [self, role, role_value]
+ instance.reverse_related_entities(instances)
+ end
+ end
+ end
+ end
+ instances
+ end
+
+ def two_way_related_entities(old)
+ entities = self.reverse_related_entities
+ if old && old.constellation
+ entities = entities + old.related_entities
+ end
+ entities.map do |entity, role_obj, role_value|
+ [entity.identifying_role_values, entity, role_obj, role_value]
+ end
+ end
+
# Determine if entity is an identifying value
# of the current instance.
def is_identified_by?(entity)
@@ -58,11 +58,6 @@ def detect &b
def delete(key)
@hash.delete(ComparableHashKey.new(key))
end
-
- def refresh_key(key)
- value = self.delete(key)
- self.[]=(value, value) if value
- end
end
end
end
@@ -255,11 +255,7 @@ def define_one_to_one_accessor(role)
detect_inconsistencies(role, value)
- if @constellation && old
- keys = old.related_entities.map do |entity|
- [entity.identifying_role_values, entity]
- end
- end
+ keys = two_way_related_entities(old)
instance_variable_set(role.variable, value)
@@ -269,9 +265,10 @@ def define_one_to_one_accessor(role)
# Assign self to the new counterpart
value.send(role.counterpart.setter, self) if value
- if keys
- keys.each do |key, entity|
+ unless keys.empty?
+ keys.each do |key, entity, role_obj, role_value|
entity.instance_index.refresh_key(key)
+ role_value.refresh_key(entity) unless role_value.nil?
end
end
@@ -296,11 +293,7 @@ def define_one_to_many_accessor(role)
detect_inconsistencies(role, value) if value
- if old && old.constellation
- keys = old.related_entities.map do |entity|
- [entity.identifying_role_values, entity]
- end
- end
+ keys = two_way_related_entities(old)
instance_variable_set(role_var, value)
@@ -310,9 +303,10 @@ def define_one_to_many_accessor(role)
# Add "self" into the counterpart
value.send(getter ||= role.counterpart.getter).update(old, self) if value
- if keys
- keys.each do |key, entity|
+ unless keys.empty?
+ keys.each do |key, entity, role_obj, role_value|
entity.instance_index.refresh_key(key)
+ role_value.refresh_key(entity) unless role_value.nil?
end
end
@@ -426,7 +420,7 @@ def extract_binary_params(one_to_one, role_name, options)
[ role_name,
related,
mandatory,
- other_role_method.to_sym
+ other_role_method.to_sym
]
end
@@ -98,9 +98,7 @@ def assert_instance(constellation, args) #:nodoc:
instance = instances[key]
return instance, key if instance # A matching instance of this class
- #trace :assert, "Constructing new #{self} with #{args.inspect}" do
- instance = new(*args)
- #end
+ instance = new(*args)
instance.constellation = constellation
return *index_instance(instance)
@@ -1,5 +1,3 @@
-require 'activefacts/api'
-
module TestMultiPartIdentifierModule
class ParentId < AutoCounter
value_type
@@ -22,13 +20,12 @@ class Child
end
describe "Multi-part identifiers" do
- include ActiveFacts::API
before :each do
- @c = Constellation.new(TestMultiPartIdentifierModule)
+ @c = ActiveFacts::API::Constellation.new(TestMultiPartIdentifierModule)
@p = @c.Parent(:new)
@c0 = @c.Child(@p, 0)
- @c2 = @c.Child(@p, 2)
@c1 = @c.Child(@p, 1)
+ @c2 = @c.Child(@p, 2)
end
it "should allow children to be found in the instance index" do

0 comments on commit d5c29e2

Please sign in to comment.