activefacts 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/Manifest.txt +83 -0
- data/README.rdoc +81 -0
- data/Rakefile +41 -0
- data/bin/afgen +46 -0
- data/bin/cql +52 -0
- data/examples/CQL/Address.cql +46 -0
- data/examples/CQL/Blog.cql +54 -0
- data/examples/CQL/CompanyDirectorEmployee.cql +51 -0
- data/examples/CQL/Death.cql +16 -0
- data/examples/CQL/Genealogy.cql +95 -0
- data/examples/CQL/Marriage.cql +18 -0
- data/examples/CQL/Metamodel.cql +238 -0
- data/examples/CQL/MultiInheritance.cql +19 -0
- data/examples/CQL/OilSupply.cql +47 -0
- data/examples/CQL/Orienteering.cql +108 -0
- data/examples/CQL/PersonPlaysGame.cql +17 -0
- data/examples/CQL/SchoolActivities.cql +31 -0
- data/examples/CQL/SimplestUnary.cql +12 -0
- data/examples/CQL/SubtypePI.cql +32 -0
- data/examples/CQL/Warehousing.cql +99 -0
- data/examples/CQL/WindowInRoomInBldg.cql +22 -0
- data/lib/activefacts.rb +10 -0
- data/lib/activefacts/api.rb +25 -0
- data/lib/activefacts/api/concept.rb +384 -0
- data/lib/activefacts/api/constellation.rb +106 -0
- data/lib/activefacts/api/entity.rb +239 -0
- data/lib/activefacts/api/instance.rb +54 -0
- data/lib/activefacts/api/numeric.rb +158 -0
- data/lib/activefacts/api/role.rb +94 -0
- data/lib/activefacts/api/standard_types.rb +67 -0
- data/lib/activefacts/api/support.rb +59 -0
- data/lib/activefacts/api/value.rb +122 -0
- data/lib/activefacts/api/vocabulary.rb +120 -0
- data/lib/activefacts/cql.rb +31 -0
- data/lib/activefacts/cql/CQLParser.treetop +104 -0
- data/lib/activefacts/cql/Concepts.treetop +112 -0
- data/lib/activefacts/cql/DataTypes.treetop +66 -0
- data/lib/activefacts/cql/Expressions.treetop +113 -0
- data/lib/activefacts/cql/FactTypes.treetop +185 -0
- data/lib/activefacts/cql/Language/English.treetop +92 -0
- data/lib/activefacts/cql/LexicalRules.treetop +169 -0
- data/lib/activefacts/cql/Rakefile +6 -0
- data/lib/activefacts/cql/parser.rb +88 -0
- data/lib/activefacts/generate/absorption.rb +87 -0
- data/lib/activefacts/generate/cql.rb +441 -0
- data/lib/activefacts/generate/cql/html.rb +397 -0
- data/lib/activefacts/generate/null.rb +19 -0
- data/lib/activefacts/generate/ordered.rb +557 -0
- data/lib/activefacts/generate/ruby.rb +326 -0
- data/lib/activefacts/generate/sql/server.rb +164 -0
- data/lib/activefacts/generate/text.rb +21 -0
- data/lib/activefacts/input/cql.rb +1268 -0
- data/lib/activefacts/input/orm.rb +926 -0
- data/lib/activefacts/persistence.rb +1 -0
- data/lib/activefacts/persistence/composition.rb +653 -0
- data/lib/activefacts/support.rb +51 -0
- data/lib/activefacts/version.rb +3 -0
- data/lib/activefacts/vocabulary.rb +6 -0
- data/lib/activefacts/vocabulary/extensions.rb +343 -0
- data/lib/activefacts/vocabulary/metamodel.rb +303 -0
- data/script/txt2html +71 -0
- data/spec/absorption_spec.rb +95 -0
- data/spec/api/autocounter.rb +82 -0
- data/spec/api/constellation.rb +130 -0
- data/spec/api/entity_type.rb +101 -0
- data/spec/api/instance.rb +428 -0
- data/spec/api/roles.rb +122 -0
- data/spec/api/value_type.rb +112 -0
- data/spec/api_spec.rb +14 -0
- data/spec/cql_cql_spec.rb +58 -0
- data/spec/cql_parse_spec.rb +31 -0
- data/spec/cql_ruby_spec.rb +60 -0
- data/spec/cql_sql_spec.rb +54 -0
- data/spec/cql_symbol_tables_spec.rb +259 -0
- data/spec/cql_unit_spec.rb +336 -0
- data/spec/cqldump_spec.rb +169 -0
- data/spec/norma_cql_spec.rb +48 -0
- data/spec/norma_ruby_spec.rb +50 -0
- data/spec/norma_sql_spec.rb +45 -0
- data/spec/norma_tables_spec.rb +94 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +173 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
## The ActiveFacts Runtime API Concept class
|
2
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
3
|
+
#
|
4
|
+
module ActiveFacts
|
5
|
+
module API
|
6
|
+
|
7
|
+
# A Role represents the relationship of one object to another (or to a boolean condition).
|
8
|
+
# Relationships (or binary fact types) have a Role at each end; one is declared using _has_one_
|
9
|
+
# or _one_to_one_, and the other is created on the counterpart class. Each Concept class maintains
|
10
|
+
# an array of the roles it plays.
|
11
|
+
class Role
|
12
|
+
attr_accessor :name
|
13
|
+
attr_accessor :counterpart # All roles except unaries have a binary counterpart
|
14
|
+
attr_accessor :player # May be a Symbol, which will be converted to a Class/Concept
|
15
|
+
attr_accessor :unique
|
16
|
+
attr_accessor :mandatory
|
17
|
+
attr_accessor :value_restriction
|
18
|
+
|
19
|
+
def initialize(player, counterpart, name, mandatory = false, unique = true)
|
20
|
+
@player = player
|
21
|
+
@counterpart = counterpart
|
22
|
+
@name = name
|
23
|
+
@mandatory = mandatory
|
24
|
+
@unique = unique
|
25
|
+
end
|
26
|
+
|
27
|
+
def unary?
|
28
|
+
# N.B. A role with a forward reference looks unary until it is resolved.
|
29
|
+
counterpart == nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def resolve_player(vocabulary) #:nodoc:
|
33
|
+
return @player if Class === @player # Done already
|
34
|
+
klass = vocabulary.concept(@player) # Trigger the binding
|
35
|
+
raise "Cannot resolve role player #{@player.inspect} for role #{name} in vocabulary #{vocabulary.basename}; still forward-declared?" unless klass
|
36
|
+
@player = klass # Memoize a successful result
|
37
|
+
end
|
38
|
+
|
39
|
+
def adapt(constellation, value) #:nodoc:
|
40
|
+
# If the value is a compatible class, use it (if in another constellation, clone it),
|
41
|
+
# else create a compatible object using the value as constructor parameters.
|
42
|
+
if @player === value # REVISIT: may be a non-primary subtype of player
|
43
|
+
# Check that the value is in a compatible constellation, clone if not:
|
44
|
+
if constellation && (vc = value.constellation) && vc != constellation
|
45
|
+
value = value.clone # REVISIT: There's sure to be things we should reset/clone here, like non-identifying roles
|
46
|
+
end
|
47
|
+
value.constellation = constellation if constellation
|
48
|
+
else
|
49
|
+
value = [value] unless Array === value
|
50
|
+
raise "No parameters were provided to identify an #{@player.basename} instance" if value == []
|
51
|
+
if constellation
|
52
|
+
value = constellation.send(@player.basename.to_sym, *value)
|
53
|
+
else
|
54
|
+
value = @player.new(*value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Every Concept has a Role collection
|
62
|
+
# REVISIT: You can enumerate the concept's own roles, or inherited roles as well.
|
63
|
+
class RoleCollection < Hash #:nodoc:
|
64
|
+
def verbalise
|
65
|
+
keys.sort_by(&:to_s).inspect
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# A RoleValueArray is an array with all mutating methods hidden.
|
70
|
+
# We use these for the "many" side of a 1:many relationship.
|
71
|
+
# Only "replace" and "delete" are actually used (so far!).
|
72
|
+
#
|
73
|
+
# Don't rely on this implementation, as it must change to support
|
74
|
+
# persistence.
|
75
|
+
#
|
76
|
+
class RoleValueArray < Array #:nodoc:
|
77
|
+
[ :"<<", :"[]=", :clear, :collect!, :compact!, :concat, :delete,
|
78
|
+
:delete_at, :delete_if, :fill, :flatten!, :insert, :map!, :pop,
|
79
|
+
:push, :reject!, :replace, :reverse!, :shift, :shuffle!, :slice!,
|
80
|
+
:sort!, :uniq!, :unshift
|
81
|
+
].each{|s|
|
82
|
+
begin
|
83
|
+
alias_method("__#{s}", s)
|
84
|
+
rescue NameError # shuffle! is in 1.9 only
|
85
|
+
end
|
86
|
+
}
|
87
|
+
|
88
|
+
def verbalise
|
89
|
+
"["+map{|e| e.verbalise}*", "+"]"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# The ActiveFacts Runtime API Standard types extensions.
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
# These extensions add ActiveFacts Concept and Instance behaviour into base Ruby classes.
|
6
|
+
#
|
7
|
+
require 'date'
|
8
|
+
|
9
|
+
module ActiveFacts
|
10
|
+
module API
|
11
|
+
# Adapter module to add value_type to all potential value classes
|
12
|
+
module ValueClass #:nodoc:
|
13
|
+
def value_type *args, &block #:nodoc:
|
14
|
+
include ActiveFacts::API::Value
|
15
|
+
# the included method adds the Value::ClassMethods
|
16
|
+
initialise_value_type(*args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'activefacts/api/numeric'
|
23
|
+
|
24
|
+
# Add the methods that convert our classes into Concept types:
|
25
|
+
|
26
|
+
ValueClasses = [String, Date, DateTime, Time, Int, Real, AutoCounter]
|
27
|
+
ValueClasses.each{|c|
|
28
|
+
c.send :extend, ActiveFacts::API::ValueClass
|
29
|
+
}
|
30
|
+
|
31
|
+
class TrueClass #:nodoc:
|
32
|
+
def verbalise(role_name = nil); role_name ? "#{role_name}: true" : "true"; end
|
33
|
+
end
|
34
|
+
|
35
|
+
class NilClass #:nodoc:
|
36
|
+
def verbalise; "nil"; end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Class
|
40
|
+
def identified_by *args
|
41
|
+
raise "not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
|
42
|
+
include ActiveFacts::API::Entity
|
43
|
+
initialise_entity_type(*args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# REVISIT: Fix these NORMA types
|
48
|
+
class Decimal < Int #:nodoc:
|
49
|
+
end
|
50
|
+
class SignedInteger < Int #:nodoc:
|
51
|
+
end
|
52
|
+
class SignedSmallInteger < Int #:nodoc:
|
53
|
+
end
|
54
|
+
class UnsignedInteger < Int #:nodoc:
|
55
|
+
end
|
56
|
+
class UnsignedSmallInteger < Int #:nodoc:
|
57
|
+
end
|
58
|
+
class LargeLengthText < String #:nodoc:
|
59
|
+
end
|
60
|
+
class FixedLengthText < String #:nodoc:
|
61
|
+
end
|
62
|
+
class PictureRawData < String #:nodoc:
|
63
|
+
end
|
64
|
+
class DateAndTime < DateTime #:nodoc:
|
65
|
+
end
|
66
|
+
class Money < Decimal #:nodoc:
|
67
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts runtime API.
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
# Note that we still require facets/basicobject, see numeric.rb
|
6
|
+
#
|
7
|
+
|
8
|
+
class Symbol #:nodoc:
|
9
|
+
def to_proc
|
10
|
+
Proc.new{|*args| args.shift.__send__(self, *args)}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class String #:nodoc:
|
15
|
+
def camelcase(first=false, on='_\s')
|
16
|
+
if first
|
17
|
+
gsub(/(^|[#{on}]+)([A-Za-z])/){ $2.upcase }
|
18
|
+
else
|
19
|
+
gsub(/([#{on}]+)([A-Za-z])/){ $2.upcase }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def snakecase
|
24
|
+
gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Module #:nodoc:
|
29
|
+
def modspace
|
30
|
+
space = name[ 0...(name.rindex( '::' ) || 0)]
|
31
|
+
space == '' ? Object : eval(space)
|
32
|
+
end
|
33
|
+
|
34
|
+
def basename
|
35
|
+
name.gsub(/.*::/, '')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ActiveFacts #:nodoc:
|
40
|
+
# If the args array ends with a hash, remove it.
|
41
|
+
# If the remaining args are fewer than the arg_names,
|
42
|
+
# extract values from the hash and append them to args.
|
43
|
+
# Return the new args array and the hash.
|
44
|
+
# In any case leave the original args unmodified.
|
45
|
+
def self.extract_hash_args(arg_names, args)
|
46
|
+
if Hash === args[-1]
|
47
|
+
arg_hash = args[-1] # Don't pop args, leave it unmodified
|
48
|
+
args = args[0..-2]
|
49
|
+
arg_hash = arg_hash.clone if (args.size < arg_names.size)
|
50
|
+
while args.size < arg_names.size
|
51
|
+
args << arg_hash[n = arg_names[args.size]]
|
52
|
+
arg_hash.delete(n)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
arg_hash = {}
|
56
|
+
end
|
57
|
+
return args, arg_hash
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#
|
2
|
+
# The ActiveFacts Runtime API Value extension module
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
# The methods of this module are added to Value type classes.
|
6
|
+
#
|
7
|
+
module ActiveFacts
|
8
|
+
module API
|
9
|
+
|
10
|
+
# All Value instances include the methods defined here
|
11
|
+
module Value
|
12
|
+
include Instance
|
13
|
+
|
14
|
+
# Value instance methods:
|
15
|
+
def initialize(*args) #:nodoc:
|
16
|
+
super(args)
|
17
|
+
end
|
18
|
+
|
19
|
+
# verbalise this Value
|
20
|
+
def verbalise(role_name = nil)
|
21
|
+
"#{role_name || self.class.basename} '#{to_s}'"
|
22
|
+
end
|
23
|
+
|
24
|
+
# A value is its own key
|
25
|
+
def identifying_role_values #:nodoc:
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# All ValueType classes include the methods defined here
|
30
|
+
module ClassMethods
|
31
|
+
include Instance::ClassMethods
|
32
|
+
|
33
|
+
def initialise_value_type *args, &block #:nodoc:
|
34
|
+
# REVISIT: args could be a hash, with keys :length, :scale, :unit, :allow
|
35
|
+
#raise "value_type args unexpected" if args.size > 0
|
36
|
+
end
|
37
|
+
|
38
|
+
class_eval do
|
39
|
+
define_method :length do |*args|
|
40
|
+
@length = args[0] if args.length > 0
|
41
|
+
@length
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class_eval do
|
46
|
+
define_method :scale do |*args|
|
47
|
+
@scale = args[0] if args.length > 0
|
48
|
+
@scale
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# verbalise this ValueType
|
53
|
+
def verbalise
|
54
|
+
# REVISIT: Add length and scale here, if set
|
55
|
+
# REVISIT: Set vocabulary name of superclass if not same as this
|
56
|
+
"#{basename} = #{superclass.basename}();"
|
57
|
+
end
|
58
|
+
|
59
|
+
def identifying_role_values(*args) #:nodoc:
|
60
|
+
# If the single arg is the correct class or a subclass, use it directly
|
61
|
+
#puts "#{basename}.identifying_role_values#{args.inspect}"
|
62
|
+
return args[0] if (args.size == 1 and self.class === args[0]) # No secondary supertypes allowed for value types
|
63
|
+
new(*args)
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_instance(constellation, args) #:nodoc:
|
67
|
+
# Build the key for this instance from the args
|
68
|
+
# The key of an instance is the value or array of keys of the identifying values.
|
69
|
+
# The key values aren't necessarily present in the constellation, even after this.
|
70
|
+
key = identifying_role_values(*args)
|
71
|
+
#puts "#{klass} key is #{key.inspect}"
|
72
|
+
|
73
|
+
# Find and return an existing instance matching this key
|
74
|
+
instances = constellation.instances[self] # All instances of this class in this constellation
|
75
|
+
instance = instances[key]
|
76
|
+
# DEBUG: puts "assert #{self.basename} #{key.inspect} #{instance ? "exists" : "new"}"
|
77
|
+
return instance, key if instance # A matching instance of this class
|
78
|
+
|
79
|
+
instance = new(*args)
|
80
|
+
|
81
|
+
instance.constellation = constellation
|
82
|
+
return *index_instance(instance)
|
83
|
+
end
|
84
|
+
|
85
|
+
def index_instance(instance, key = nil) #:nodoc:
|
86
|
+
instances = instance.constellation.instances[self]
|
87
|
+
key = instance.identifying_role_values
|
88
|
+
instances[key] = instance
|
89
|
+
# DEBUG: puts "indexing value #{basename} using #{key.inspect} in #{constellation.object_id}"
|
90
|
+
|
91
|
+
# Index the instance for each supertype:
|
92
|
+
supertypes.each do |supertype|
|
93
|
+
supertype.index_instance(instance, key)
|
94
|
+
end
|
95
|
+
|
96
|
+
return instance, key
|
97
|
+
end
|
98
|
+
|
99
|
+
def inherited(other) #:nodoc:
|
100
|
+
#puts "REVISIT: ValueType #{self} < #{self.superclass} was inherited by #{other}; not implemented" #+"from #{caller*"\n\t"}"
|
101
|
+
# Copy the type parameters here, etc?
|
102
|
+
other.send :realise_supertypes, self
|
103
|
+
vocabulary.add_concept(other)
|
104
|
+
super
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.included other #:nodoc:
|
109
|
+
other.send :extend, ClassMethods
|
110
|
+
|
111
|
+
#puts "ValueType included in #{other.basename} from #{caller*"\n\t"}"
|
112
|
+
|
113
|
+
# Register ourselves with the parent module, which has become a Vocabulary:
|
114
|
+
vocabulary = other.modspace
|
115
|
+
unless vocabulary.respond_to? :concept # Extend module with Vocabulary if necessary
|
116
|
+
vocabulary.send :extend, Vocabulary
|
117
|
+
end
|
118
|
+
vocabulary.add_concept(other)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#
|
2
|
+
# The ActiveFacts Runtime API Vocabulary extension module.
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
# The methods of this module are extended into any module that contains
|
6
|
+
# a Concept class (Entity type or Value type).
|
7
|
+
#
|
8
|
+
module ActiveFacts
|
9
|
+
module API
|
10
|
+
module Vocabulary
|
11
|
+
# With a parameter, look up a concept class by name.
|
12
|
+
# Without, return the hash of all concepts in this vocabulary
|
13
|
+
def concept(name = nil)
|
14
|
+
@concept ||= {}
|
15
|
+
return @concept unless name
|
16
|
+
|
17
|
+
return name if name.is_a? Class
|
18
|
+
|
19
|
+
# puts "Looking up concept #{name} in #{self.name}"
|
20
|
+
camel = name.to_s.camelcase(true)
|
21
|
+
if (c = @concept[camel])
|
22
|
+
__bind(camel)
|
23
|
+
return c
|
24
|
+
end
|
25
|
+
return (const_get(camel) rescue nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_concept(klass) #:nodoc:
|
29
|
+
name = klass.basename
|
30
|
+
__bind(name)
|
31
|
+
# puts "Adding concept #{name} to #{self.name}"
|
32
|
+
@concept ||= {}
|
33
|
+
@concept[klass.basename] = klass
|
34
|
+
end
|
35
|
+
|
36
|
+
def __delay(concept_name, args, &block) #:nodoc:
|
37
|
+
# puts "Arranging for delayed binding on #{concept_name.inspect}"
|
38
|
+
@delayed ||= Hash.new { |h,k| h[k] = [] }
|
39
|
+
@delayed[concept_name] << [args, block]
|
40
|
+
end
|
41
|
+
|
42
|
+
# __bind raises an error if the named class doesn't exist yet.
|
43
|
+
def __bind(concept_name) #:nodoc:
|
44
|
+
concept = const_get(concept_name)
|
45
|
+
if (@delayed && @delayed.include?(concept_name))
|
46
|
+
# $stderr.puts "#{concept_name} was delayed, binding now"
|
47
|
+
d = @delayed[concept_name]
|
48
|
+
d.each{|(a,b)|
|
49
|
+
b.call(concept, *a)
|
50
|
+
}
|
51
|
+
@delayed.delete(concept_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def verbalise
|
56
|
+
"Vocabulary #{name}:\n\t" +
|
57
|
+
@concept.keys.sort.map{|concept|
|
58
|
+
c = @concept[concept]
|
59
|
+
__bind(c.basename)
|
60
|
+
c.verbalise + "\n\t\t// Roles played: " + c.roles.verbalise
|
61
|
+
}*"\n\t"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create or find an instance of klass in constellation using value to identify it
|
65
|
+
def adopt(klass, constellation, value) #:nodoc:
|
66
|
+
puts "Adopting #{ value.verbalise rescue value.class.to_s+' '+value.inspect} as #{klass} into constellation #{constellation.object_id}"
|
67
|
+
|
68
|
+
path = "unknown"
|
69
|
+
# Create a value instance we can hack if the value isn't already in this constellation
|
70
|
+
if (c = constellation)
|
71
|
+
if klass === value # Right class?
|
72
|
+
vc = value.constellation rescue nil
|
73
|
+
if (c == vc) # Right constellation?
|
74
|
+
# Already right class, in the right constellation
|
75
|
+
path = "right constellation, right class, just use it"
|
76
|
+
else
|
77
|
+
# We need a new object from our constellation, so copy the value.
|
78
|
+
if klass.respond_to?(:identifying_roles)
|
79
|
+
# Make a new entity having only the identifying roles set.
|
80
|
+
# Someone will complain that this is wrong, and all functional role values should also
|
81
|
+
# be cloned, and I'm listening... but not there yet. Why just those?
|
82
|
+
cloned = c.send(
|
83
|
+
:"#{klass.basename}",
|
84
|
+
*klass.identifying_roles.map{|role| value.send(role) }
|
85
|
+
)
|
86
|
+
path = "wrong constellation, right class, cloned entity"
|
87
|
+
else
|
88
|
+
# Just copy a value:
|
89
|
+
cloned = c.send(:"#{klass.basename}", *value)
|
90
|
+
path = "wrong constellation, right class, copied value"
|
91
|
+
end
|
92
|
+
value.constellation = c
|
93
|
+
end
|
94
|
+
else
|
95
|
+
# Wrong class, assume it's a valid constructor arg. Get our constellation to find/make it:
|
96
|
+
value = [ value ] unless Array === value
|
97
|
+
value = c.send(:"#{klass.basename}", *value)
|
98
|
+
path = "right constellation but wrong class, constructed from args"
|
99
|
+
end
|
100
|
+
else
|
101
|
+
# This object's not in a constellation
|
102
|
+
if klass === value # Right class?
|
103
|
+
if vc = value.constellation rescue nil
|
104
|
+
raise "REVISIT: Assigning to #{self.class.basename}.#{role_name} with constellation=#{c.inspect}: Can't dis-associate object from its constellation #{vc.object_id} yet"
|
105
|
+
end
|
106
|
+
# Right class, no constellation, just use it
|
107
|
+
path = "no constellation, correct class"
|
108
|
+
else
|
109
|
+
# Wrong class, construct one
|
110
|
+
value = klass.send(:new, *value)
|
111
|
+
path = "no constellation, constructed from wrong class"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
# print "#{path}"; puts ", adopted as #{value.verbalise rescue value.inspect}"
|
115
|
+
value
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|