lib/activefacts/persistence/reference.rb in activefacts-0.8.9 vs lib/activefacts/persistence/reference.rb in activefacts-0.8.10
- old
+ new
@@ -1,32 +1,32 @@
#
# ActiveFacts Relational mapping and persistence.
-# Reference from one Concept to another, used to decide the relational mapping.
+# Reference from one ObjectType to another, used to decide the relational mapping.
#
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
#
-# A Reference from one Concept to another is created for each many-1 or 1-1 relationship
-# (including subtyping), and also for a unary role (implicitly to Boolean concept).
+# A Reference from one ObjectType to another is created for each many-1 or 1-1 relationship
+# (including subtyping), and also for a unary role (implicitly to Boolean object_type).
# A 1-1 or subtyping reference should be created in only one direction, and may be flipped
# if needed.
#
-# A reference to a concept that's a table or is fully absorbed into a table will
-# become a foreign key, otherwise it will absorb all that concept's references.
+# A reference to a object_type that's a table or is fully absorbed into a table will
+# become a foreign key, otherwise it will absorb all that object_type's references.
#
-# Reference objects update each concept's list of the references *to* and *from* that concept.
+# Reference objects update each object_type's list of the references *to* and *from* that object_type.
#
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
#
module ActiveFacts
module Persistence
# This class contains the core data structure used in composing a relational schema.
#
- # A Reference is *from* one Concept *to* another Concept, and relates to the *from_role* and the *to_role*.
- # When either Concept is an objectified fact type, the corresponding role is nil.
- # When the Reference from_role is of a unary fact type, there's no to_role or to Concept.
+ # A Reference is *from* one ObjectType *to* another ObjectType, and relates to the *from_role* and the *to_role*.
+ # When either ObjectType is an objectified fact type, the corresponding role is nil.
+ # When the Reference from_role is of a unary fact type, there's no to_role or to ObjectType.
# The final kind of Reference is a self-reference which is added to a ValueType that becomes a table.
#
# When the underlying fact type is a one-to-one (including an inheritance fact type), the Reference may be flipped.
#
# Each Reference has a name; an array of names in fact, in case of adjectives, etc.
@@ -38,11 +38,11 @@
class Reference
attr_reader :from, :to # A "from" instance is related to one "to" instance
attr_reader :from_role, :to_role # For objectified facts, one role will be nil (a phantom)
attr_reader :fact_type
- # A Reference is created from a concept in regard to a role it plays
+ # A Reference is created from a object_type in regard to a role it plays
def initialize(from, role)
@from = from
return unless role # All done if it's a self-value reference for a ValueType
@fact_type = role.fact_type
if @fact_type.all_role.size == 1
@@ -50,18 +50,18 @@
@to_role = role
@to = role.fact_type.entity_type # nil unless the unary is objectified
elsif (role.fact_type.entity_type == @from) # role is in "from", an objectified fact type
@from_role = nil # Phantom role
@to_role = role
- @to = @to_role.concept
+ @to = @to_role.object_type
else
@from_role = role
@to = role.fact_type.entity_type # If set, to_role is a phantom
unless @to
raise "Illegal reference through >binary fact type" if @fact_type.all_role.size >2
@to_role = (role.fact_type.all_role-[role])[0]
- @to = @to_role.concept
+ @to = @to_role.object_type
end
end
end
# What type of Role did this Reference arise from?
@@ -96,11 +96,11 @@
# Is this reference an injected role as a result a ValueType being a table?
def is_self_value
!@to && !@to_role
end
- # Is the *to* concept fully absorbed through this reference?
+ # Is the *to* object_type fully absorbed through this reference?
def is_absorbing
@to && @to.absorbed_via == self
end
# Is this a simple reference?
@@ -125,14 +125,35 @@
@from.name.camelwords + ["Value"]
when @to_role.role_name # Named role
@to_role.role_name.camelwords
else # Use the name from the preferred reading
role_ref = @to_role.preferred_reference
- [role_ref.leading_adjective, @to_role.concept.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
+ [role_ref.leading_adjective, @to_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
end
end
+ # Return the array of names for the (perhaps implicit) *from_role* of this Reference
+ def from_names
+ case
+ when is_unary
+ if @from && @from.fact_type
+ @from.name.camelwords
+ else
+ @from_role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.camelwords
+ end
+ when @from && !@from_role # @from is an objectified fact type so @from_role is a phantom
+ @from.name.camelwords
+ when !@from_role # Self-value role of an independent ValueType
+ @from.name.camelwords + ["Value"]
+ when @from_role.role_name # Named role
+ @from_role.role_name.camelwords
+ else # Use the name from the preferred reading
+ role_ref = @from_role.preferred_reference
+ [role_ref.leading_adjective, @from_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
+ end
+ end
+
# For a one-to-one (or a subtyping fact type), reverse the direction.
def flip #:nodoc:
raise "Illegal flip of #{self}" unless @to and [:one_one, :subtype, :supertype].include?(role_type)
detabulate
@@ -170,25 +191,25 @@
"reference from #{@from.name}#{@to ? " to #{@to.name}" : ""}" + (@fact_type ? " in '#{@fact_type.default_reading}'" : "")
end
# The reading for the fact type underlying this Reference
def reading
- is_self_value ? "#{from.name} has value" : @fact_type.default_reading([], true) # Include role name defn's
+ is_self_value ? "#{from.name} has value" : @fact_type.default_reading
end
def inspect #:nodoc:
to_s
end
end
end
module Metamodel #:nodoc:
- class Concept
+ class ObjectType
# Say whether the independence of this object is still under consideration
# This is used in detecting dependency cycles, such as occurs in the Metamodel
attr_accessor :tentative #:nodoc:
- attr_writer :is_table # The two Concept subclasses provide the attr_reader method
+ attr_writer :is_table # The two ObjectType subclasses provide the attr_reader method
def show_tabular #:nodoc:
(tentative ? "tentatively " : "") +
(is_table ? "" : "not ")+"a table"
end
@@ -211,21 +232,21 @@
def probably_not_table #:nodoc:
@is_table = false
@tentative = true
end
- # References from this Concept
+ # References from this ObjectType
def references_from
@references_from ||= []
end
- # References to this Concept
+ # References to this ObjectType
def references_to
@references_to ||= []
end
- # True if this Concept has any References (to or from)
+ # True if this ObjectType has any References (to or from)
def has_references #:nodoc:
@references_from || @references_to
end
def clear_references #:nodoc:
@@ -234,11 +255,15 @@
@references_from = nil
end
def populate_references #:nodoc:
all_role.each do |role|
- populate_reference role unless role.fact_type.is_a?(ImplicitFactType)
+ # It's possible that this role is in an implicit or derived fact type. Skip it if so.
+ next if role.fact_type.is_a?(ImplicitFactType) or
+ role.fact_type.preferred_reading.role_sequence.all_role_ref.to_a[0].join_role
+
+ populate_reference role
end
end
def populate_reference role #:nodoc:
role_type = role.role_type
@@ -319,16 +344,16 @@
unless r.from.name.downcase < r.to.name.downcase or
(r.from == r.to && references_to.detect{|ref| ref.to_role == role}) # one-to-one self reference, done already
r.tabulate
end
else
- raise "Illegal role type, #{role.fact_type.describe(role)} no uniqueness constraint"
+ raise "Role #{role.object_type.name} in '#{role.fact_type.default_reading}' lacks a uniqueness constraint"
end
end
end
- class EntityType < Concept
+ class EntityType < ObjectType
def populate_references #:nodoc:
if fact_type && fact_type.all_role.size > 1
# NOT: fact_type.all_role.each do |role| # Place roles in the preferred order instead:
fact_type.preferred_reading.role_sequence.all_role_ref.map(&:role).each do |role|
populate_reference role # Objectified fact role, handled specially
@@ -338,26 +363,26 @@
end
end
class Vocabulary
def populate_all_references #:nodoc:
- debug :references, "Populating all concept references" do
- all_concept.each do |concept|
- concept.clear_references
- concept.is_table = nil # Undecided; force an attempt to decide
- concept.tentative = true # Uncertain
+ debug :references, "Populating all object_type references" do
+ all_object_type.each do |object_type|
+ object_type.clear_references
+ object_type.is_table = nil # Undecided; force an attempt to decide
+ object_type.tentative = true # Uncertain
end
- all_concept.each do |concept|
- debug :references, "Populating references for #{concept.name}" do
- concept.populate_references
+ all_object_type.each do |object_type|
+ debug :references, "Populating references for #{object_type.name}" do
+ object_type.populate_references
end
end
end
- debug :references, "Finished concept references" do
- all_concept.each do |concept|
- next unless concept.references_from.size > 0
- debug :references, "#{concept.name}:" do
- concept.references_from.each do |ref|
+ debug :references, "Finished object_type references" do
+ all_object_type.each do |object_type|
+ next unless object_type.references_from.size > 0
+ debug :references, "#{object_type.name}:" do
+ object_type.references_from.each do |ref|
debug :references, "#{ref}"
end
end
end
end