activefacts 0.8.9 → 0.8.10
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/.gemtest +0 -0
- data/Manifest.txt +28 -33
- data/Rakefile +11 -12
- data/bin/cql +90 -46
- data/examples/CQL/Blog.cql +2 -1
- data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Diplomacy.cql +9 -9
- data/examples/CQL/Genealogy.cql +3 -2
- data/examples/CQL/Insurance.cql +10 -7
- data/examples/CQL/JoinEquality.cql +2 -2
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +73 -53
- data/examples/CQL/MetamodelNext.cql +89 -67
- data/examples/CQL/OneToOnes.cql +2 -2
- data/examples/CQL/ServiceDirector.cql +10 -5
- data/examples/CQL/Supervision.cql +3 -3
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/Warehousing.cql +4 -2
- data/lib/activefacts/cql/CQLParser.treetop +26 -60
- data/lib/activefacts/cql/Context.treetop +12 -2
- data/lib/activefacts/cql/Expressions.treetop +14 -30
- data/lib/activefacts/cql/FactTypes.treetop +165 -110
- data/lib/activefacts/cql/Language/English.treetop +167 -54
- data/lib/activefacts/cql/LexicalRules.treetop +16 -2
- data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
- data/lib/activefacts/cql/Terms.treetop +57 -27
- data/lib/activefacts/cql/ValueTypes.treetop +39 -13
- data/lib/activefacts/cql/compiler.rb +5 -3
- data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
- data/lib/activefacts/cql/compiler/constraint.rb +178 -275
- data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
- data/lib/activefacts/cql/compiler/expression.rb +418 -0
- data/lib/activefacts/cql/compiler/fact.rb +146 -145
- data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
- data/lib/activefacts/cql/compiler/join.rb +159 -0
- data/lib/activefacts/cql/compiler/shared.rb +51 -23
- data/lib/activefacts/cql/compiler/value_type.rb +56 -2
- data/lib/activefacts/cql/parser.rb +15 -4
- data/lib/activefacts/generate/absorption.rb +7 -7
- data/lib/activefacts/generate/cql.rb +100 -37
- data/lib/activefacts/generate/oo.rb +28 -51
- data/lib/activefacts/generate/ordered.rb +60 -36
- data/lib/activefacts/generate/ruby.rb +6 -6
- data/lib/activefacts/generate/sql/server.rb +4 -4
- data/lib/activefacts/input/orm.rb +71 -53
- data/lib/activefacts/persistence.rb +1 -1
- data/lib/activefacts/persistence/columns.rb +27 -23
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +17 -17
- data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
- data/lib/activefacts/persistence/reference.rb +61 -36
- data/lib/activefacts/persistence/tables.rb +61 -59
- data/lib/activefacts/support.rb +54 -29
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +99 -54
- data/lib/activefacts/vocabulary/metamodel.rb +43 -37
- data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
- data/spec/absorption_spec.rb +8 -8
- data/spec/cql/comparison_spec.rb +91 -0
- data/spec/cql/contractions_spec.rb +251 -0
- data/spec/cql/entity_type_spec.rb +319 -0
- data/spec/cql/expressions_spec.rb +63 -0
- data/spec/cql/fact_type_matching_spec.rb +283 -0
- data/spec/cql/french_spec.rb +21 -0
- data/spec/cql/parser/bad_literals_spec.rb +86 -0
- data/spec/cql/parser/constraints_spec.rb +19 -0
- data/spec/cql/parser/entity_types_spec.rb +106 -0
- data/spec/cql/parser/expressions_spec.rb +179 -0
- data/spec/cql/parser/fact_types_spec.rb +41 -0
- data/spec/cql/parser/literals_spec.rb +312 -0
- data/spec/cql/parser/pragmas_spec.rb +89 -0
- data/spec/cql/parser/value_types_spec.rb +42 -0
- data/spec/cql/role_matching_spec.rb +147 -0
- data/spec/cql/samples_spec.rb +9 -9
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/cql_dm_spec.rb +116 -0
- data/spec/cql_mysql_spec.rb +1 -1
- data/spec/cql_ruby_spec.rb +1 -1
- data/spec/cql_sql_spec.rb +3 -3
- data/spec/cql_symbol_tables_spec.rb +30 -30
- data/spec/cqldump_spec.rb +4 -4
- data/spec/helpers/array_matcher.rb +32 -27
- data/spec/helpers/diff_matcher.rb +6 -26
- data/spec/helpers/file_matcher.rb +41 -32
- data/spec/helpers/parse_to_ast_matcher.rb +76 -0
- data/spec/helpers/string_matcher.rb +32 -31
- data/spec/norma_cql_spec.rb +1 -1
- data/spec/norma_ruby_spec.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +1 -1
- data/spec/norma_sql_spec.rb +3 -1
- data/spec/norma_tables_spec.rb +1 -1
- data/spec/ruby_api_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -4
- metadata +66 -66
- data/examples/CQL/OrienteeringER.cql +0 -58
- data/lib/activefacts/api.rb +0 -44
- data/lib/activefacts/api/concept.rb +0 -410
- data/lib/activefacts/api/constellation.rb +0 -128
- data/lib/activefacts/api/entity.rb +0 -256
- data/lib/activefacts/api/instance.rb +0 -60
- data/lib/activefacts/api/instance_index.rb +0 -80
- data/lib/activefacts/api/numeric.rb +0 -167
- data/lib/activefacts/api/role.rb +0 -80
- data/lib/activefacts/api/role_proxy.rb +0 -70
- data/lib/activefacts/api/role_values.rb +0 -117
- data/lib/activefacts/api/standard_types.rb +0 -87
- data/lib/activefacts/api/support.rb +0 -65
- data/lib/activefacts/api/value.rb +0 -135
- data/lib/activefacts/api/vocabulary.rb +0 -82
- data/spec/api/autocounter.rb +0 -82
- data/spec/api/constellation.rb +0 -130
- data/spec/api/entity_type.rb +0 -103
- data/spec/api/instance.rb +0 -461
- data/spec/api/roles.rb +0 -124
- data/spec/api/value_type.rb +0 -112
- data/spec/api_spec.rb +0 -13
- data/spec/cql/matching_spec.rb +0 -517
- data/spec/cql/unit_spec.rb +0 -394
- data/spec/spec.opts +0 -1
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Runtime API
|
|
3
|
-
# Constellation class
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
module ActiveFacts
|
|
9
|
-
module API #:nodoc:
|
|
10
|
-
# A Constellation is a population of instances of the Concept classes of a Vocabulary.
|
|
11
|
-
# Every concept class is either a Value type or an Entity type.
|
|
12
|
-
#
|
|
13
|
-
# Value types are uniquely identified by their value, and a constellation will only
|
|
14
|
-
# ever have a single instance of a given value of that class.
|
|
15
|
-
#
|
|
16
|
-
# Entity instances are uniquely identified by their identifying roles, and again, a
|
|
17
|
-
# constellation will only ever have a single entity instance for the values of those
|
|
18
|
-
# identifying roles.
|
|
19
|
-
#
|
|
20
|
-
# As a result, you cannot "create" an object in a constellation - you merely _assert_
|
|
21
|
-
# its existence. This is done using method_missing; @constellation.Thing(3) creates
|
|
22
|
-
# an instance (or returns the existing instance) of Thing identified by the value 3.
|
|
23
|
-
# You can also use the populate() method to apply a block of assertions.
|
|
24
|
-
#
|
|
25
|
-
# You can instance##retract any instance, and that removes it from the constellation (will
|
|
26
|
-
# delete it from the database when the constellation is saved), and nullifies any
|
|
27
|
-
# references to it.
|
|
28
|
-
#
|
|
29
|
-
# A Constellation may or not be valid according to the vocabulary's constraints,
|
|
30
|
-
# but it may also represent a portion of a larger population (a database) with
|
|
31
|
-
# which it may be merged to form a valid population. In other words, an invalid
|
|
32
|
-
# Constellation may be invalid only because it lacks some of the facts.
|
|
33
|
-
#
|
|
34
|
-
class Constellation
|
|
35
|
-
attr_reader :vocabulary
|
|
36
|
-
# All instances are indexed in this hash, keyed by the class object. Each instance is indexed for every supertype it has (including multiply-inherited ones). It's a bad idea to try to modify these indexes!
|
|
37
|
-
attr_reader :instances # Can say c.instances[MyClass].each{|k, v| ... }
|
|
38
|
-
# Can also say c.MyClass.each{|k, v| ... }
|
|
39
|
-
|
|
40
|
-
# Create a new empty Constellation over the given Vocabulary
|
|
41
|
-
def initialize(vocabulary)
|
|
42
|
-
@vocabulary = vocabulary
|
|
43
|
-
@instances = Hash.new do |h,k|
|
|
44
|
-
raise "A constellation over #{@vocabulary.name} can only index instances of concepts in that vocabulary, not #{k.inspect}" unless k.is_a?(Class) and k.modspace == vocabulary
|
|
45
|
-
h[k] = InstanceIndex.new
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def inspect #:nodoc:
|
|
50
|
-
"Constellation:#{object_id}"
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Evaluate assertions against the population of this Constellation
|
|
54
|
-
def populate *args, &block
|
|
55
|
-
# REVISIT: Use args for something? Like options to enable/disable validation?
|
|
56
|
-
instance_eval(&block)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Delete instances from the constellation, nullifying (or cascading) the roles each plays
|
|
60
|
-
def retract(*instances)
|
|
61
|
-
Array(instances).each do |i|
|
|
62
|
-
i.retract
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Constellations verbalise all members of all classes in alphabetical order, showing
|
|
67
|
-
# non-identifying role values as well
|
|
68
|
-
def verbalise
|
|
69
|
-
"Constellation over #{vocabulary.name}:\n" +
|
|
70
|
-
vocabulary.concept.keys.sort.map{|concept|
|
|
71
|
-
klass = vocabulary.const_get(concept)
|
|
72
|
-
|
|
73
|
-
# REVISIT: It would be better not to rely on the role name pattern here:
|
|
74
|
-
single_roles, multiple_roles = klass.roles.keys.sort_by(&:to_s).partition{|r| r.to_s !~ /\Aall_/ }
|
|
75
|
-
single_roles -= klass.identifying_role_names if (klass.is_entity_type)
|
|
76
|
-
# REVISIT: Need to include superclass roles also.
|
|
77
|
-
|
|
78
|
-
instances = send(concept.to_sym)
|
|
79
|
-
next nil unless instances.size > 0
|
|
80
|
-
"\tEvery #{concept}:\n" +
|
|
81
|
-
instances.map{|key, instance|
|
|
82
|
-
s = "\t\t" + instance.verbalise
|
|
83
|
-
if (single_roles.size > 0)
|
|
84
|
-
role_values =
|
|
85
|
-
single_roles.map{|role|
|
|
86
|
-
[ role_name = role.to_s.camelcase(true),
|
|
87
|
-
value = instance.send(role)]
|
|
88
|
-
}.select{|role_name, value|
|
|
89
|
-
value
|
|
90
|
-
}.map{|role_name, value|
|
|
91
|
-
"#{role_name} = #{value ? value.verbalise : "nil"}"
|
|
92
|
-
}
|
|
93
|
-
s += " where " + role_values*", " if role_values.size > 0
|
|
94
|
-
end
|
|
95
|
-
s
|
|
96
|
-
} * "\n"
|
|
97
|
-
}.compact*"\n"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# This method removes the given instance from this constellation's indexes
|
|
101
|
-
# It must be called before the identifying roles get deleted or nullified.
|
|
102
|
-
def __retract(instance) #:nodoc:
|
|
103
|
-
# REVISIT: Need to search, as key values are gone already. Is there a faster way?
|
|
104
|
-
([instance.class]+instance.class.supertypes_transitive).each do |klass|
|
|
105
|
-
@instances[klass].delete_if{|k,v| v == instance }
|
|
106
|
-
end
|
|
107
|
-
# REVISIT: Need to nullify all the roles this object plays.
|
|
108
|
-
# If mandatory on the counterpart side, this may/must propagate the delete (without mutual recursion!)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# With parameters, assert an instance of the concept whose name is the missing method, identified by the values passed as *args*.
|
|
112
|
-
# With no parameters, return the collection of all instances of that concept.
|
|
113
|
-
def method_missing(m, *args)
|
|
114
|
-
if klass = @vocabulary.const_get(m)
|
|
115
|
-
if args.size == 0
|
|
116
|
-
# Return the collection of all instances of this class in the constellation:
|
|
117
|
-
@instances[klass]
|
|
118
|
-
else
|
|
119
|
-
# Assert a new ground fact (concept instance) of the specified class, identified by args:
|
|
120
|
-
# REVISIT: create a constructor method here instead?
|
|
121
|
-
instance, key = klass.assert_instance(self, args)
|
|
122
|
-
instance
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
end
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Runtime API
|
|
3
|
-
# Entity class (a mixin module for the class Class)
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
module ActiveFacts
|
|
8
|
-
module API
|
|
9
|
-
# An Entity type is any Concept that isn't a value type.
|
|
10
|
-
# All Entity types must have an identifier made up of one or more roles.
|
|
11
|
-
module Entity
|
|
12
|
-
include Instance
|
|
13
|
-
|
|
14
|
-
# Assign the identifying roles to initialise a new Entity instance.
|
|
15
|
-
# The role values are asserted in the constellation first, so you
|
|
16
|
-
# can pass bare values (array, string, integer, etc) for any role
|
|
17
|
-
# whose instances can be constructed using those values.
|
|
18
|
-
#
|
|
19
|
-
# A value must be provided for every identifying role, but if the
|
|
20
|
-
# last argument is a hash, they may come from there.
|
|
21
|
-
#
|
|
22
|
-
# Any additional (non-identifying) roles may also be passed in the final hash.
|
|
23
|
-
def initialize(*args)
|
|
24
|
-
super(args)
|
|
25
|
-
klass = self.class
|
|
26
|
-
hash = {}
|
|
27
|
-
hash = args.pop.clone if Hash === args[-1]
|
|
28
|
-
|
|
29
|
-
# Pick any missing identifying roles out of the hash if possible:
|
|
30
|
-
while args.size < (ir = klass.identifying_role_names).size
|
|
31
|
-
value = hash[role = ir[args.size]]
|
|
32
|
-
hash.delete(role)
|
|
33
|
-
args.push value
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# If one arg is expected but more are passed, they might be the args for the object that plays the identifying role:
|
|
37
|
-
args = [args] if klass.identifying_role_names.size == 1 && args.size > 1
|
|
38
|
-
|
|
39
|
-
# This should now only occur when there are too many args passed:
|
|
40
|
-
raise "Wrong number of parameters to #{klass}.new, " +
|
|
41
|
-
"expect (#{klass.identifying_role_names*","}) " +
|
|
42
|
-
"got (#{args.map{|a| a.to_s.inspect}*", "})" if args.size != klass.identifying_role_names.size
|
|
43
|
-
|
|
44
|
-
# Assign the identifying roles in order, then the other roles passed as a hash:
|
|
45
|
-
(klass.identifying_role_names.zip(args) + hash.entries).each do |role_name, value|
|
|
46
|
-
role = klass.roles(role_name)
|
|
47
|
-
send("#{role_name}=", value)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def inspect #:nodoc:
|
|
52
|
-
"\#<#{
|
|
53
|
-
self.class.basename
|
|
54
|
-
}:#{
|
|
55
|
-
object_id
|
|
56
|
-
}#{
|
|
57
|
-
constellation ? " in #{constellation.inspect}" : ""
|
|
58
|
-
} #{
|
|
59
|
-
# REVISIT: Where there are one-to-one roles, this cycles
|
|
60
|
-
self.class.identifying_role_names.map{|role| "@#{role}="+send(role).inspect }*" "
|
|
61
|
-
}>"
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# When used as a hash key, the hash key of this entity instance is calculated
|
|
65
|
-
# by hashing the values of its identifying roles
|
|
66
|
-
def hash
|
|
67
|
-
self.class.identifying_role_names.map{|role|
|
|
68
|
-
instance_variable_get("@#{role}")
|
|
69
|
-
}.inject(0) { |h,v|
|
|
70
|
-
h ^= v.hash
|
|
71
|
-
h
|
|
72
|
-
}
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# When used as a hash key, this entity instance is compared with another by
|
|
76
|
-
# comparing the values of its identifying roles
|
|
77
|
-
def eql?(other)
|
|
78
|
-
return false unless self.class == other.class
|
|
79
|
-
self.class.identifying_role_names.each{|role|
|
|
80
|
-
return false unless send(role).eql?(other.send(role))
|
|
81
|
-
}
|
|
82
|
-
return true
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Verbalise this entity instance
|
|
86
|
-
def verbalise(role_name = nil)
|
|
87
|
-
"#{role_name || self.class.basename}(#{
|
|
88
|
-
self.class.identifying_role_names.map{|role_sym|
|
|
89
|
-
value = send(role_sym)
|
|
90
|
-
role_name = self.class.roles(role_sym).name.to_s.camelcase(true)
|
|
91
|
-
value ? value.verbalise(role_name) : "nil"
|
|
92
|
-
}*", "
|
|
93
|
-
})"
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Return the array of the values of this entity instance's identifying roles
|
|
97
|
-
def identifying_role_values
|
|
98
|
-
self.class.identifying_role_names.map{|role|
|
|
99
|
-
send(role)
|
|
100
|
-
}
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# All classes that become Entity types receive the methods of this class as class methods:
|
|
104
|
-
module ClassMethods
|
|
105
|
-
include Instance::ClassMethods
|
|
106
|
-
|
|
107
|
-
# Return the array of Role objects that define the identifying relationships of this Entity type:
|
|
108
|
-
def identifying_role_names
|
|
109
|
-
@identifying_role_names ||= []
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def identifying_roles
|
|
113
|
-
debug :persistence, "Identifying roles for #{basename}" do
|
|
114
|
-
@identifying_role_names.map{|name|
|
|
115
|
-
role = roles[name] || (!superclass.is_entity_type || superclass.roles[name])
|
|
116
|
-
debug :persistence, "#{name} -> #{role ? "found" : "NOT FOUND"}"
|
|
117
|
-
role
|
|
118
|
-
}
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Convert the passed arguments into an array of Instance objects that can identify an instance of this Entity type:
|
|
123
|
-
def identifying_role_values(*args)
|
|
124
|
-
#puts "Getting identifying role values #{identifying_role_names.inspect} of #{basename} using #{args.inspect}"
|
|
125
|
-
|
|
126
|
-
# If the single arg is an instance of the correct class or a subclass,
|
|
127
|
-
# use the instance's identifying_role_values
|
|
128
|
-
if (args.size == 1 and
|
|
129
|
-
(arg = args[0]).is_a?(self)) # REVISIT: or a secondary supertype
|
|
130
|
-
arg = arg.__getobj__ if RoleProxy === arg
|
|
131
|
-
return arg.identifying_role_values
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
ir = identifying_role_names
|
|
135
|
-
args, arg_hash = ActiveFacts::extract_hash_args(ir, args)
|
|
136
|
-
if args.size < ir.size
|
|
137
|
-
raise "#{basename} requires all identifying values, you're missing #{ir[args.size..-1].map(&:to_sym)*', '}"
|
|
138
|
-
elsif args.size > ir.size
|
|
139
|
-
raise "#{basename} requires all identifying values, you have #{args.size-ir.size} extras #{args[ir.size..-1].map(&:inspect)*', '}"
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
role_args = ir.map{|role_sym| roles(role_sym)}.zip(args)
|
|
143
|
-
role_args.map do |role, arg|
|
|
144
|
-
#puts "Getting identifying_role_value for #{role.counterpart_concept.basename} using #{arg.inspect}"
|
|
145
|
-
next nil unless arg
|
|
146
|
-
next !!arg unless role.counterpart # Unary
|
|
147
|
-
arg = arg.__getobj__ if RoleProxy === arg
|
|
148
|
-
if arg.is_a?(role.counterpart_concept) # REVISIT: or a secondary supertype
|
|
149
|
-
# Note that with a secondary supertype, it must still return the values of these identifying_role_names
|
|
150
|
-
next arg.identifying_role_values
|
|
151
|
-
end
|
|
152
|
-
role.counterpart_concept.identifying_role_values(*arg)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def assert_instance(constellation, args) #:nodoc:
|
|
157
|
-
# Build the key for this instance from the args
|
|
158
|
-
# The key of an instance is the value or array of keys of the identifying values.
|
|
159
|
-
# The key values aren't necessarily present in the constellation, even after this.
|
|
160
|
-
key = identifying_role_values(*args)
|
|
161
|
-
|
|
162
|
-
# Find and return an existing instance matching this key
|
|
163
|
-
instances = constellation.instances[self] # All instances of this class in this constellation
|
|
164
|
-
instance = instances[key]
|
|
165
|
-
# DEBUG: puts "assert #{self.basename} #{key.inspect} #{instance ? "exists" : "new"}"
|
|
166
|
-
return instance, key if instance # A matching instance of this class
|
|
167
|
-
|
|
168
|
-
# Now construct each of this object's identifying roles
|
|
169
|
-
ir = identifying_role_names
|
|
170
|
-
args, arg_hash = ActiveFacts::extract_hash_args(ir, args)
|
|
171
|
-
role_values = ir.map{|role_sym| roles(role_sym)}.zip(args)
|
|
172
|
-
key = [] # Gather the actual key (AutoCounters are special)
|
|
173
|
-
values = role_values.map do |role, arg|
|
|
174
|
-
if !arg
|
|
175
|
-
value = role_key = nil # No value
|
|
176
|
-
elsif !role.counterpart
|
|
177
|
-
value = role_key = !!arg # Unary
|
|
178
|
-
elsif arg.is_a?(role.counterpart_concept) # REVISIT: or a secondary supertype
|
|
179
|
-
arg = arg.__getobj__ if RoleProxy === arg
|
|
180
|
-
raise "Connecting values across constellations" unless arg.constellation == constellation
|
|
181
|
-
value, role_key = arg, arg.identifying_role_values
|
|
182
|
-
else
|
|
183
|
-
value, role_key = role.counterpart_concept.assert_instance(constellation, Array(arg))
|
|
184
|
-
end
|
|
185
|
-
key << role_key
|
|
186
|
-
value
|
|
187
|
-
end
|
|
188
|
-
values << arg_hash if arg_hash and !arg_hash.empty?
|
|
189
|
-
|
|
190
|
-
#puts "Creating new #{basename} using #{values.inspect}"
|
|
191
|
-
instance = new(*values)
|
|
192
|
-
|
|
193
|
-
# Make the new entity instance a member of this constellation:
|
|
194
|
-
instance.constellation = constellation
|
|
195
|
-
return *index_instance(instance, key, ir)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def index_instance(instance, key = nil, key_roles = nil) #:nodoc:
|
|
199
|
-
# Derive a new key if we didn't receive one or if the roles are different:
|
|
200
|
-
unless key && key_roles && key_roles == identifying_role_names
|
|
201
|
-
key = (key_roles = identifying_role_names).map do |role_name|
|
|
202
|
-
instance.send role_name
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
# Index the instance for this class in the constellation
|
|
207
|
-
instances = instance.constellation.instances[self]
|
|
208
|
-
instances[key] = instance
|
|
209
|
-
# DEBUG: puts "indexing entity #{basename} using #{key.inspect} in #{constellation.object_id}"
|
|
210
|
-
|
|
211
|
-
# Index the instance for each supertype:
|
|
212
|
-
supertypes.each do |supertype|
|
|
213
|
-
supertype.index_instance(instance, key, key_roles)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
return instance, key
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
# A concept that isn't a ValueType must have an identification scheme,
|
|
220
|
-
# which is a list of roles it plays. The identification scheme may be
|
|
221
|
-
# inherited from a superclass.
|
|
222
|
-
def initialise_entity_type(*args) #:nodoc:
|
|
223
|
-
#puts "Initialising entity type #{self} using #{args.inspect}"
|
|
224
|
-
@identifying_role_names = superclass.identifying_role_names if superclass.is_entity_type
|
|
225
|
-
# REVISIT: @identifying_role_names here are the symbols passed in, not the Role objects we should use.
|
|
226
|
-
# We'd need late binding to use Role objects...
|
|
227
|
-
@identifying_role_names = args if args.size > 0 || !@identifying_role_names
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def inherited(other) #:nodoc:
|
|
231
|
-
other.identified_by *identifying_role_names
|
|
232
|
-
subtypes << other unless subtypes.include? other
|
|
233
|
-
#puts "#{self.name} inherited by #{other.name}"
|
|
234
|
-
vocabulary.__add_concept(other)
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
# verbalise this concept
|
|
238
|
-
def verbalise
|
|
239
|
-
"#{basename} is identified by #{identifying_role_names.map{|role_sym| role_sym.to_s.camelcase(true)}*" and "};"
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def Entity.included other #:nodoc:
|
|
244
|
-
other.send :extend, ClassMethods
|
|
245
|
-
|
|
246
|
-
# Register ourselves with the parent module, which has become a Vocabulary:
|
|
247
|
-
vocabulary = other.modspace
|
|
248
|
-
# puts "Entity.included(#{other.inspect})"
|
|
249
|
-
unless vocabulary.respond_to? :concept # Extend module with Vocabulary if necessary
|
|
250
|
-
vocabulary.send :extend, Vocabulary
|
|
251
|
-
end
|
|
252
|
-
vocabulary.__add_concept(other)
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
end
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Runtime API
|
|
3
|
-
# Instance (mixin module for instances of a Concept - a class with Concept mixed in)
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
# Instance methods are extended into all instances, whether of value or entity types.
|
|
8
|
-
#
|
|
9
|
-
module ActiveFacts
|
|
10
|
-
module API
|
|
11
|
-
# Every Instance of a Concept (A Value type or an Entity type) includes the methods of this module:
|
|
12
|
-
module Instance
|
|
13
|
-
# What constellation does this Instance belong to (if any):
|
|
14
|
-
attr_accessor :constellation
|
|
15
|
-
|
|
16
|
-
def initialize(args = []) #:nodoc:
|
|
17
|
-
unless (self.class.is_entity_type)
|
|
18
|
-
#if (self.class.superclass != Object)
|
|
19
|
-
# puts "constructing #{self.class.superclass} with #{args.inspect}"
|
|
20
|
-
super(*args)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Verbalise this instance
|
|
25
|
-
def verbalise
|
|
26
|
-
# This method should always be overridden in subclasses
|
|
27
|
-
raise "#{self.class} Instance verbalisation needed"
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# De-assign all functional roles and remove from constellation, if any.
|
|
31
|
-
def retract
|
|
32
|
-
# Delete from the constellation first, so it can remember our identifying role values
|
|
33
|
-
@constellation.__retract(self) if @constellation
|
|
34
|
-
|
|
35
|
-
# Now, for all roles (from this class and all supertypes), assign nil to all functional roles
|
|
36
|
-
# The counterpart roles get cleared automatically.
|
|
37
|
-
([self.class]+self.class.supertypes_transitive).each do |klass|
|
|
38
|
-
klass.roles.each do |role_name, role|
|
|
39
|
-
next if role.unary?
|
|
40
|
-
next if !role.unique
|
|
41
|
-
|
|
42
|
-
counterpart = role.counterpart
|
|
43
|
-
puts "Nullifying mandatory role #{role.name} of #{role.owner.name}" if counterpart.mandatory
|
|
44
|
-
|
|
45
|
-
send "#{role.name}=", nil
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
module ClassMethods #:nodoc:
|
|
51
|
-
include Concept
|
|
52
|
-
# Add Instance class methods here
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def Instance.included other #:nodoc:
|
|
56
|
-
other.send :extend, ClassMethods
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# ActiveFacts Runtime API
|
|
3
|
-
# InstanceIndex class
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
|
6
|
-
#
|
|
7
|
-
module ActiveFacts
|
|
8
|
-
module API
|
|
9
|
-
#
|
|
10
|
-
# Each Constellation maintains an InstanceIndex for each Concept in its Vocabulary.
|
|
11
|
-
# The InstanceIndex object is returned when you call @constellation.Concept with no
|
|
12
|
-
# arguments (where Concept is the concept name you're interested in)
|
|
13
|
-
#
|
|
14
|
-
class InstanceIndex
|
|
15
|
-
def []=(key, value) #:nodoc:
|
|
16
|
-
raise "Adding RoleProxy to InstanceIndex" if value && RoleProxy === value
|
|
17
|
-
h[key] = value
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def [](*args)
|
|
21
|
-
a = args
|
|
22
|
-
#a = naked(args)
|
|
23
|
-
# p "vvvv",
|
|
24
|
-
# args,
|
|
25
|
-
# a,
|
|
26
|
-
# keys.map{|k| v=super(k); (RoleProxy === k ? "*" : "")+k.to_s+"=>"+(RoleProxy === v ? "*" : "")+v.to_s}*",",
|
|
27
|
-
# "^^^^"
|
|
28
|
-
h[*a]
|
|
29
|
-
#super(*a)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def size
|
|
33
|
-
h.size
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def each &b
|
|
37
|
-
h.each &b
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def map &b
|
|
41
|
-
h.map &b
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def detect &b
|
|
45
|
-
r = h.detect &b
|
|
46
|
-
r ? r[1] : nil
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Return an array of all the instances of this concept
|
|
50
|
-
def values
|
|
51
|
-
h.values
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Return an array of the identifying role values arrays for all the instances of this concept
|
|
55
|
-
def keys
|
|
56
|
-
h.keys
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def delete_if(&b) #:nodoc:
|
|
60
|
-
h.delete_if &b
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
def h
|
|
65
|
-
@hash ||= {}
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def naked(o)
|
|
69
|
-
case o
|
|
70
|
-
when Array
|
|
71
|
-
o.map{|e| naked(e) }
|
|
72
|
-
when RoleProxy
|
|
73
|
-
o.__getobj__
|
|
74
|
-
else
|
|
75
|
-
o
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|