lib/activefacts/api/role.rb in activefacts-0.7.0 vs lib/activefacts/api/role.rb in activefacts-0.7.1
- old
+ new
@@ -1,59 +1,70 @@
-## The ActiveFacts Runtime API Concept class
-# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
#
+# ActiveFacts API
+# Role class.
+# Each accessor method created on an instance corresponds to a Role object in the instance's class.
+# Binary fact types construct a Role at each end.
+#
+# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
+#
module ActiveFacts
module API
# A Role represents the relationship of one object to another (or to a boolean condition).
# Relationships (or binary fact types) have a Role at each end; one is declared using _has_one_
# or _one_to_one_, and the other is created on the counterpart class. Each Concept class maintains
# an array of the roles it plays.
class Role
- attr_accessor :name
+ attr_accessor :owner # The Concept to which this role belongs
+ attr_accessor :name # The name of the role (a Symbol)
+ attr_accessor :counterpart_concept # A Concept Class (may be temporarily a Symbol before the class is defined)
attr_accessor :counterpart # All roles except unaries have a binary counterpart
- attr_accessor :player # May be a Symbol, which will be converted to a Class/Concept
- attr_accessor :unique
- attr_accessor :mandatory
- attr_accessor :value_restriction
+ attr_accessor :unique # Is this role played by at most one instance, or more?
+ attr_accessor :mandatory # In a valid fact population, is this role required to be played?
+ attr_accessor :value_restriction # Counterpart Instances playing this role must meet these restrictions
+ attr_reader :is_identifying # Is this an identifying role for owner?
- def initialize(player, counterpart, name, mandatory = false, unique = true)
- @player = player
+ def initialize(owner, counterpart_concept, counterpart, name, mandatory = false, unique = true)
+ @owner = owner
+ @counterpart_concept = counterpart_concept
@counterpart = counterpart
@name = name
@mandatory = mandatory
@unique = unique
+ @is_identifying = @owner.respond_to?(:identifying_role_names) && @owner.identifying_role_names.include?(@name)
end
+ # Is this role a unary (created by maybe)? If so, it has no counterpart
def unary?
# N.B. A role with a forward reference looks unary until it is resolved.
counterpart == nil
end
- def resolve_player(vocabulary) #:nodoc:
- return @player if Class === @player # Done already
- klass = vocabulary.concept(@player) # Trigger the binding
- raise "Cannot resolve role player #{@player.inspect} for role #{name} in vocabulary #{vocabulary.basename}; still forward-declared?" unless klass
- @player = klass # Memoize a successful result
+ def resolve_counterpart(vocabulary) #:nodoc:
+ return @counterpart_concept if @counterpart_concept.is_a?(Class) # Done already
+ klass = vocabulary.concept(@counterpart_concept) # Trigger the binding
+ raise "Cannot resolve role counterpart_concept #{@counterpart_concept.inspect} for role #{name} in vocabulary #{vocabulary.basename}; still forward-declared?" unless klass
+ @counterpart_concept = klass # Memoize a successful result
end
def adapt(constellation, value) #:nodoc:
# If the value is a compatible class, use it (if in another constellation, clone it),
# else create a compatible object using the value as constructor parameters.
- if @player === value # REVISIT: may be a non-primary subtype of player
+ if value.is_a?(@counterpart_concept) # REVISIT: may be a non-primary subtype of counterpart_concept
+ value = value.__getobj__ if RoleProxy === value
# Check that the value is in a compatible constellation, clone if not:
if constellation && (vc = value.constellation) && vc != constellation
value = value.clone # REVISIT: There's sure to be things we should reset/clone here, like non-identifying roles
end
value.constellation = constellation if constellation
else
value = [value] unless Array === value
- raise "No parameters were provided to identify an #{@player.basename} instance" if value == []
+ raise "No parameters were provided to identify an #{@counterpart_concept.basename} instance" if value == []
if constellation
- value = constellation.send(@player.basename.to_sym, *value)
+ value = constellation.send(@counterpart_concept.basename.to_sym, *value)
else
- value = @player.new(*value)
+ value = @counterpart_concept.new(*value)
end
end
value
end
end
@@ -63,32 +74,7 @@
class RoleCollection < Hash #:nodoc:
def verbalise
keys.sort_by(&:to_s).inspect
end
end
-
- # A RoleValueArray is an array with all mutating methods hidden.
- # We use these for the "many" side of a 1:many relationship.
- # Only "replace" and "delete" are actually used (so far!).
- #
- # Don't rely on this implementation, as it must change to support
- # persistence.
- #
- class RoleValueArray < Array #:nodoc:
- [ :"<<", :"[]=", :clear, :collect!, :compact!, :concat, :delete,
- :delete_at, :delete_if, :fill, :flatten!, :insert, :map!, :pop,
- :push, :reject!, :replace, :reverse!, :shift, :shuffle!, :slice!,
- :sort!, :uniq!, :unshift
- ].each{|s|
- begin
- alias_method("__#{s}", s)
- rescue NameError # shuffle! is in 1.9 only
- end
- }
-
- def verbalise
- "["+map{|e| e.verbalise}*", "+"]"
- end
- end
-
end
end