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