activefacts 0.7.1 → 0.7.2
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/Manifest.txt +4 -0
- data/examples/CQL/Metamodel.cql +11 -7
- data/lib/activefacts/api/concept.rb +10 -0
- data/lib/activefacts/api/entity.rb +2 -0
- data/lib/activefacts/api/instance_index.rb +71 -0
- data/lib/activefacts/api/role_proxy.rb +70 -0
- data/lib/activefacts/api/role_values.rb +117 -0
- data/lib/activefacts/generate/absorption.rb +20 -30
- data/lib/activefacts/generate/null.rb +1 -0
- data/lib/activefacts/generate/ruby.rb +12 -28
- data/lib/activefacts/generate/sql/server.rb +8 -22
- data/lib/activefacts/generate/text.rb +1 -0
- data/lib/activefacts/input/cql.rb +30 -32
- data/lib/activefacts/input/orm.rb +11 -12
- data/lib/activefacts/persistence.rb +5 -0
- data/lib/activefacts/persistence/columns.rb +28 -21
- data/lib/activefacts/persistence/concept.rb +73 -0
- data/lib/activefacts/persistence/foreignkey.rb +17 -1
- data/lib/activefacts/persistence/index.rb +21 -10
- data/lib/activefacts/persistence/reference.rb +6 -6
- data/lib/activefacts/persistence/tables.rb +8 -5
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +17 -7
- data/lib/activefacts/vocabulary/metamodel.rb +22 -17
- data/spec/absorption_spec.rb +1 -1
- data/spec/cql_symbol_tables_spec.rb +1 -1
- data/spec/cqldump_spec.rb +8 -8
- data/spec/norma_tables_spec.rb +1 -1
- metadata +7 -3
data/Manifest.txt
CHANGED
@@ -26,8 +26,11 @@ lib/activefacts/api/concept.rb
|
|
26
26
|
lib/activefacts/api/constellation.rb
|
27
27
|
lib/activefacts/api/entity.rb
|
28
28
|
lib/activefacts/api/instance.rb
|
29
|
+
lib/activefacts/api/instance_index.rb
|
29
30
|
lib/activefacts/api/numeric.rb
|
30
31
|
lib/activefacts/api/role.rb
|
32
|
+
lib/activefacts/api/role_proxy.rb
|
33
|
+
lib/activefacts/api/role_values.rb
|
31
34
|
lib/activefacts/api/standard_types.rb
|
32
35
|
lib/activefacts/api/support.rb
|
33
36
|
lib/activefacts/api/value.rb
|
@@ -55,6 +58,7 @@ lib/activefacts/input/cql.rb
|
|
55
58
|
lib/activefacts/input/orm.rb
|
56
59
|
lib/activefacts/persistence.rb
|
57
60
|
lib/activefacts/persistence/columns.rb
|
61
|
+
lib/activefacts/persistence/concept.rb
|
58
62
|
lib/activefacts/persistence/foreignkey.rb
|
59
63
|
lib/activefacts/persistence/index.rb
|
60
64
|
lib/activefacts/persistence/reference.rb
|
data/examples/CQL/Metamodel.cql
CHANGED
@@ -16,6 +16,7 @@ Length is defined as UnsignedInteger(32);
|
|
16
16
|
Name is defined as VariableLengthText(64);
|
17
17
|
Numerator is defined as Decimal();
|
18
18
|
Ordinal is defined as UnsignedSmallInteger(32);
|
19
|
+
Pronoun is defined as VariableLengthText() restricted to {'feminine', 'masculine', 'personal'};
|
19
20
|
RingType is defined as VariableLengthText();
|
20
21
|
RoleSequenceId is defined as AutoCounter();
|
21
22
|
Scale is defined as UnsignedInteger(32);
|
@@ -142,8 +143,8 @@ SetExclusionConstraint is mandatory;
|
|
142
143
|
Alias is a kind of Feature;
|
143
144
|
|
144
145
|
Concept is a kind of Feature;
|
146
|
+
Concept uses at most one Pronoun;
|
145
147
|
Concept is independent;
|
146
|
-
Concept is personal;
|
147
148
|
Instance is of one Concept;
|
148
149
|
Role is where
|
149
150
|
FactType has Ordinal role played by Concept,
|
@@ -165,12 +166,12 @@ RoleRef is where
|
|
165
166
|
Role has Ordinal place in RoleSequence;
|
166
167
|
RoleRef has at most one leading-Adjective;
|
167
168
|
RoleRef has at most one trailing-Adjective;
|
168
|
-
|
169
|
-
RoleRef has Ordinal (as JoinStep)
|
170
|
-
|
171
|
-
Concept is traversed by
|
172
|
-
|
173
|
-
|
169
|
+
Join is where
|
170
|
+
RoleRef has Ordinal (as JoinStep) join;
|
171
|
+
Join traverses at most one Concept,
|
172
|
+
Concept is traversed by Join;
|
173
|
+
Join has at most one input-Role;
|
174
|
+
Join has at most one output-Role;
|
174
175
|
|
175
176
|
EntityType is a kind of Concept;
|
176
177
|
EntityType nests at most one FactType,
|
@@ -227,6 +228,9 @@ each combination EntityType, TypeInheritance occurs at most one time in
|
|
227
228
|
TypeInheritance provides identification;
|
228
229
|
each FactType occurs at least one time in
|
229
230
|
FactType has Ordinal role played by Concept;
|
231
|
+
each Join occurs at least one time in
|
232
|
+
Join has input-Role,
|
233
|
+
Join has output-Role;
|
230
234
|
each PresenceConstraint occurs at least one time in
|
231
235
|
PresenceConstraint has min-Frequency,
|
232
236
|
PresenceConstraint has max-Frequency,
|
@@ -121,6 +121,10 @@ module ActiveFacts
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
+
def subtypes
|
125
|
+
@subtypes ||= []
|
126
|
+
end
|
127
|
+
|
124
128
|
# Every new role added or inherited comes through here:
|
125
129
|
def realise_role(role) #:nodoc:
|
126
130
|
#puts "Realising role #{role.counterpart_concept.basename rescue role.counterpart_concept}.#{role.name} in #{basename}"
|
@@ -137,6 +141,10 @@ module ActiveFacts
|
|
137
141
|
|
138
142
|
# REVISIT: Use method_missing to catch all_some_role_as_other_role_and_third_role, to sort_by those roles?
|
139
143
|
|
144
|
+
def is_a? klass
|
145
|
+
super || supertypes_transitive.include?(klass)
|
146
|
+
end
|
147
|
+
|
140
148
|
private
|
141
149
|
|
142
150
|
def realise_supertypes(concept, all_supertypes = nil)
|
@@ -146,6 +154,7 @@ module ActiveFacts
|
|
146
154
|
s.each {|t|
|
147
155
|
next if all_supertypes.include? t
|
148
156
|
realise_supertypes(t, all_supertypes)
|
157
|
+
t.subtypes << self
|
149
158
|
all_supertypes << t
|
150
159
|
}
|
151
160
|
#puts "Realising roles of #{concept.basename} in #{basename}"
|
@@ -174,6 +183,7 @@ module ActiveFacts
|
|
174
183
|
else
|
175
184
|
target.roles[related_role_name] = role.counterpart = Role.new(target, definer, role, related_role_name, false, false)
|
176
185
|
end
|
186
|
+
role.counterpart_concept = target
|
177
187
|
#puts "Realising role pair #{definer.basename}.#{role_name} <-> #{target.basename}.#{related_role_name}"
|
178
188
|
realise_role(role)
|
179
189
|
target.realise_role(role.counterpart)
|
@@ -219,6 +219,8 @@ module ActiveFacts
|
|
219
219
|
|
220
220
|
def inherited(other) #:nodoc:
|
221
221
|
other.identified_by *identifying_role_names
|
222
|
+
subtypes << other unless subtypes.include? other
|
223
|
+
#puts "#{self.name} inherited by #{other.name}"
|
222
224
|
vocabulary.add_concept(other)
|
223
225
|
end
|
224
226
|
|
@@ -0,0 +1,71 @@
|
|
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 map &b
|
37
|
+
h.map &b
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return an array of all the instances of this concept
|
41
|
+
def values
|
42
|
+
h.values
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return an array of the identifying role values arrays for all the instances of this concept
|
46
|
+
def keys
|
47
|
+
h.keys
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete_if(&b) #:nodoc:
|
51
|
+
h.delete_if &b
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def h
|
56
|
+
@hash ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def naked(o)
|
60
|
+
case o
|
61
|
+
when Array
|
62
|
+
o.map{|e| naked(e) }
|
63
|
+
when RoleProxy
|
64
|
+
o.__getobj__
|
65
|
+
else
|
66
|
+
o
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# RoleProxy class, still somewhat experimental
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
require 'delegate'
|
8
|
+
|
9
|
+
module ActiveFacts
|
10
|
+
module API
|
11
|
+
#
|
12
|
+
# When you use the accessor method created by has_one, one_to_one, or maybe, you get a RoleProxy for the actual value.
|
13
|
+
# This behaves almost exactly as the value, but it knows through which role you fetched it.
|
14
|
+
# That will allow it to verbalise itself using the correct reading for that role.
|
15
|
+
#
|
16
|
+
# Don't use "SomeClass === role_value" to test the type, use "role_value.is_a?(SomeClass)" instead.
|
17
|
+
#
|
18
|
+
# In future, retrieving a value by indexing into a RoleValues array will do the same thing.
|
19
|
+
#
|
20
|
+
class RoleProxy < SimpleDelegator
|
21
|
+
def initialize(role, o = nil) #:nodoc:
|
22
|
+
@role = role # REVISIT: Use this to implement verbalise()
|
23
|
+
__setobj__(o)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(m, *a, &b) #:nodoc:
|
27
|
+
begin
|
28
|
+
super # Delegate first
|
29
|
+
rescue NoMethodError => e
|
30
|
+
__getobj__.method_missing(m, *a, &b)
|
31
|
+
rescue => e
|
32
|
+
raise
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def class #:nodoc:
|
37
|
+
__getobj__.class
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_a? klass #:nodoc:
|
41
|
+
__getobj__.is_a? klass
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s #:nodoc:
|
45
|
+
__getobj__.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def object_id #:nodoc:
|
49
|
+
__getobj__.object_id
|
50
|
+
end
|
51
|
+
|
52
|
+
# REVISIT: Should Proxies hash and eql? the same as their wards?
|
53
|
+
def hash #:nodoc:
|
54
|
+
__getobj__.hash ^ self.class.hash
|
55
|
+
end
|
56
|
+
|
57
|
+
def eql?(o) #:nodoc:
|
58
|
+
self.class == o.class and __getobj__.eql?(o)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ==(o) #:nodoc:
|
62
|
+
__getobj__.==(o)
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect #:nodoc:
|
66
|
+
"Proxy:#{__getobj__.inspect}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Runtime API
|
3
|
+
# RoleValues, manages the set of instances involved in a many_to_one relationship.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
# There are two implementations here, one using an array and one using a hash.
|
8
|
+
# The hash one has problems with keys being changed during object deletion, so
|
9
|
+
# cannot be used yet; a fix is upcoming and will improve performance of large sets.
|
10
|
+
#
|
11
|
+
module ActiveFacts
|
12
|
+
module API
|
13
|
+
|
14
|
+
class RoleValues #:nodoc:
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
#=begin
|
18
|
+
def initialize
|
19
|
+
@a = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def each &b
|
23
|
+
# REVISIT: Provide a configuration variable to enable this heckling during testing:
|
24
|
+
#@a.sort_by{rand}.each &b
|
25
|
+
@a.each &b
|
26
|
+
end
|
27
|
+
|
28
|
+
def size
|
29
|
+
@a.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def empty?
|
33
|
+
@a.size == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def +(a)
|
37
|
+
@a.+(a.is_a?(RoleValues) ? Array(a) : a)
|
38
|
+
end
|
39
|
+
|
40
|
+
def -(a)
|
41
|
+
@a - a
|
42
|
+
end
|
43
|
+
|
44
|
+
def single
|
45
|
+
@a.size > 1 ? nil : @a[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(old, value)
|
49
|
+
@a.delete(old) if old
|
50
|
+
@a << value if value
|
51
|
+
raise "Adding RoleProxy to RoleValues collection" if value && RoleProxy === value
|
52
|
+
end
|
53
|
+
|
54
|
+
def verbalise
|
55
|
+
"["+@a.to_a.map{|e| e.verbalise}*", "+"]"
|
56
|
+
end
|
57
|
+
#=end
|
58
|
+
|
59
|
+
=begin
|
60
|
+
def initialize
|
61
|
+
@h = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def each &b
|
65
|
+
@h.keys.each &b
|
66
|
+
end
|
67
|
+
|
68
|
+
def size
|
69
|
+
@h.size
|
70
|
+
end
|
71
|
+
|
72
|
+
def empty?
|
73
|
+
@h.size == 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def +(a)
|
77
|
+
@h.keys.+(a.is_a?(RoleValues) ? Array(a) : a)
|
78
|
+
end
|
79
|
+
|
80
|
+
def -(a)
|
81
|
+
@h.keys - a
|
82
|
+
end
|
83
|
+
|
84
|
+
def single
|
85
|
+
@h.size > 1 ? nil : @h.keys[0]
|
86
|
+
end
|
87
|
+
|
88
|
+
def update(old, value)
|
89
|
+
if old
|
90
|
+
unless @h.delete(old)
|
91
|
+
@h.each { |k, v|
|
92
|
+
next if k != old
|
93
|
+
puts "#{@h.object_id}: Didn't delete #{k.verbalise} (hash=#{k.hash}) matching #{old.verbalise} (hash=#{old.hash})"
|
94
|
+
puts "They are #{k.eql?(old) ? "" : "not "}eql?"
|
95
|
+
found = @h[k]
|
96
|
+
puts "found #{found.inspect}" if found
|
97
|
+
debugger
|
98
|
+
x = k.eql?(old)
|
99
|
+
y = old.eql?(k)
|
100
|
+
p y
|
101
|
+
}
|
102
|
+
raise "failed to delete #{old.verbalise}, have #{map{|e| e.verbalise}.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
puts "#{@h.object_id}: Adding #{value.inspect}" if value && (value.name == 'Meetingisboardmeeting' rescue false)
|
106
|
+
@h[value] = true if value
|
107
|
+
end
|
108
|
+
|
109
|
+
def verbalise
|
110
|
+
"["+@h.keys.map{|e| e.verbalise}*", "+"]"
|
111
|
+
end
|
112
|
+
=end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -14,7 +14,7 @@ module ActiveFacts
|
|
14
14
|
# afgen --absorption[=options] <file>.cql"
|
15
15
|
# Options are comma or space separated:
|
16
16
|
# * no_columns Don't emit the columns
|
17
|
-
# *
|
17
|
+
# * all Show Concepts that are not tables as well
|
18
18
|
# * paths Show the references paths through which each column was defined
|
19
19
|
# * no_identifier Don't show the identified_by columns for an EntityType
|
20
20
|
|
@@ -25,7 +25,6 @@ module ActiveFacts
|
|
25
25
|
@vocabulary = vocabulary
|
26
26
|
@vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
|
27
27
|
@no_columns = options.include? "no_columns"
|
28
|
-
@dependent = options.include? "dependent"
|
29
28
|
@paths = options.include? "paths"
|
30
29
|
@no_identifier = options.include? "no_identifier"
|
31
30
|
end
|
@@ -38,39 +37,30 @@ module ActiveFacts
|
|
38
37
|
multi_absorption_ets = 0
|
39
38
|
@vocabulary.tables
|
40
39
|
@vocabulary.all_feature.sort_by{|c| c.name}.each do |o|
|
41
|
-
|
42
|
-
next if ValueType === o && !o.supertype
|
40
|
+
next if !o.is_table
|
43
41
|
show(o)
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
45
|
def show concept #:nodoc:
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if (@paths)
|
67
|
-
ap = concept.absorption_paths
|
68
|
-
puts "#{ ap.map {|role|
|
69
|
-
prr = role.preferred_reference.describe
|
70
|
-
player = role.fact_type.entity_type == concept ? role.concept : (role.fact_type.all_role-[role])[0].concept
|
71
|
-
"\tcan absorb #{prr != role.concept.name ? "(via #{prr}) " : "" }into #{player.name}\n"
|
72
|
-
}*"" }"
|
73
|
-
end
|
46
|
+
indices = concept.indices
|
47
|
+
pk = indices.select(&:is_primary)[0]
|
48
|
+
indices = indices.clone
|
49
|
+
indices.delete pk
|
50
|
+
puts "#{concept.name}: #{
|
51
|
+
# "[#{concept.indices.size} indices] "
|
52
|
+
# } #{
|
53
|
+
concept.columns.sort_by do |column|
|
54
|
+
column.name(nil)
|
55
|
+
end.map do |column|
|
56
|
+
index_nrs =
|
57
|
+
[pk && pk.columns.include?(column) ? "*" : nil] +
|
58
|
+
(0...indices.size).select{|i| indices[i].columns.include?(column)}.map{|i| (i+1).to_i }
|
59
|
+
index_nrs.compact!
|
60
|
+
(@paths ? column.references.map{|r| r.to_names}.flatten : column.name(nil)) * "." +
|
61
|
+
(index_nrs.empty? ? "" : "["+index_nrs*""+"]")
|
62
|
+
end*", "
|
63
|
+
}"
|
74
64
|
|
75
65
|
end
|
76
66
|
end
|