activefacts 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|