activefacts 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -59,7 +59,7 @@ module ActiveFacts
|
|
59
59
|
# A Column name is a sequence of names (derived from the to_roles of the References)
|
60
60
|
# joined by a joiner string (pass nil to get the original array of names)
|
61
61
|
def name(joiner = "")
|
62
|
-
|
62
|
+
last_names = []
|
63
63
|
names = @references.
|
64
64
|
reject do |ref|
|
65
65
|
# Skip any object after the first which is identified by this reference
|
@@ -67,37 +67,41 @@ module ActiveFacts
|
|
67
67
|
!ref.fact_type.is_a?(TypeInheritance) and
|
68
68
|
ref.to and
|
69
69
|
ref.to.is_a?(EntityType) and
|
70
|
-
(
|
71
|
-
|
70
|
+
(role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
|
71
|
+
role_ref.role == ref.from_role
|
72
72
|
end.
|
73
73
|
inject([]) do |a, ref|
|
74
74
|
names = ref.to_names
|
75
75
|
|
76
76
|
# When traversing type inheritances, keep the subtype name, not the supertype names as well:
|
77
77
|
if a.size > 0 && ref.fact_type.is_a?(TypeInheritance)
|
78
|
-
a
|
79
|
-
|
78
|
+
next a if ref.to != ref.fact_type.subtype # Did we already have the subtype?
|
79
|
+
last_names.size.times { a.pop } # Remove the last names added
|
80
|
+
elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
|
81
|
+
# When Xyz is followed by XyzID, truncate that to just ID
|
82
|
+
names[0] = names[0][last_names.last.size..-1]
|
83
|
+
elsif last_names.last == names[0]
|
84
|
+
# Same, but where an underscore split up the words
|
85
|
+
names.shift
|
80
86
|
end
|
81
87
|
|
82
|
-
#
|
83
|
-
|
84
|
-
|
88
|
+
# Where the last name is like a reference mode but the preceeding name isn't the identified concept,
|
89
|
+
# strip it down (so turn Driver.PartyID into Driver.ID for example):
|
90
|
+
if a.size > 0 and
|
91
|
+
(et = ref.from).is_a?(EntityType) and
|
92
|
+
(role_ref = et.preferred_identifier.role_sequence.all_role_ref.single) and
|
93
|
+
role_ref.role == ref.to_role and
|
94
|
+
names[0][0...et.name.size].downcase == et.name.downcase
|
95
|
+
names[0] = names[0][et.name.size..-1]
|
96
|
+
names.shift if names[0] == ""
|
97
|
+
end
|
98
|
+
|
99
|
+
last_names = names
|
85
100
|
|
86
101
|
a += names
|
87
102
|
a
|
88
103
|
end
|
89
104
|
|
90
|
-
# Where the last name is like a reference mode but the preceeding name isn't the identified concept,
|
91
|
-
# strip it down (so turn Driver.PartyID into Driver.ID for example):
|
92
|
-
if names.size > 1 and
|
93
|
-
(et = @references.last.from).is_a?(EntityType) and
|
94
|
-
(role_refs = et.preferred_identifier.role_sequence.all_role_ref).size == 1 and
|
95
|
-
role_refs.only.role == @references.last.to_role and
|
96
|
-
names.last[0...et.name.size].downcase == et.name.downcase
|
97
|
-
names[-1] = names.last[et.name.size..-1]
|
98
|
-
names.pop if names.last == ''
|
99
|
-
end
|
100
|
-
|
101
105
|
name_array = names.map{|n| n.sub(/^[a-z]/){|s| s.upcase}}
|
102
106
|
joiner ? name_array * joiner : name_array
|
103
107
|
end
|
@@ -185,10 +189,13 @@ module ActiveFacts
|
|
185
189
|
# This section shows the features relevant to relational Persistence.
|
186
190
|
class Concept
|
187
191
|
# The array of columns for this Concept's table
|
188
|
-
def columns
|
192
|
+
def columns
|
193
|
+
@columns
|
194
|
+
end
|
189
195
|
|
190
196
|
def populate_columns #:nodoc:
|
191
|
-
@columns =
|
197
|
+
@columns =
|
198
|
+
all_columns({})
|
192
199
|
end
|
193
200
|
end
|
194
201
|
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ActiveFacts
|
2
|
+
module API
|
3
|
+
module Concept
|
4
|
+
def table
|
5
|
+
@is_table = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def is_table
|
9
|
+
@is_table
|
10
|
+
end
|
11
|
+
|
12
|
+
def columns
|
13
|
+
#puts "Calculating columns for #{basename}"
|
14
|
+
return @columns if @columns
|
15
|
+
@columns = (
|
16
|
+
roles.
|
17
|
+
values.
|
18
|
+
select{|role| role.unique}.
|
19
|
+
inject([]) do |columns, role|
|
20
|
+
rn = role.name.to_s.split(/_/)
|
21
|
+
columns += role.counterpart_concept.__absorb(rn, role.counterpart)
|
22
|
+
end +
|
23
|
+
# REVISIT: Need to use subtypes_transitive here:
|
24
|
+
subtypes.
|
25
|
+
select{|subtype| !subtype.is_table}. # Don't absorb separate subtypes
|
26
|
+
inject([]) { |columns, subtype|
|
27
|
+
sn = [subtype.basename]
|
28
|
+
columns += subtype.__absorb(sn)
|
29
|
+
# puts "subtype #{subtype.name} contributed #{columns.inspect}"
|
30
|
+
columns
|
31
|
+
}
|
32
|
+
).map{|col_names| col_names.uniq.map{|name| name.sub(/^[a-z]/){|c| c.upcase}}*"."}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return an array of the absorbed columns, using prefix for name truncation
|
36
|
+
def __absorb(prefix, except_role = nil)
|
37
|
+
is_entity = respond_to?(:identifying_role_names)
|
38
|
+
absorbed_into = nil
|
39
|
+
if absorbed_into
|
40
|
+
# REVISIT: if this concept is fully absorbed through one of its roles into another table, we absorb that tables identifying_roles
|
41
|
+
absorbed_into.__absorb(prefix)
|
42
|
+
elsif !@is_table
|
43
|
+
# Not a table -> all roles are absorbed
|
44
|
+
if is_entity
|
45
|
+
roles.
|
46
|
+
values.
|
47
|
+
select{|role| role.unique && role.counterpart_concept != except_role }.
|
48
|
+
inject([]) do |columns, role|
|
49
|
+
columns += role.counterpart_concept.__absorb(prefix + role.name.to_s.split(/_/), self)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
[prefix]
|
53
|
+
end
|
54
|
+
else
|
55
|
+
#puts "#{@is_table ? "referencing" : "absorbing"} #{is_entity ? "entity" : "value"} #{basename} using #{prefix.inspect}"
|
56
|
+
if is_entity
|
57
|
+
identifying_role_names.map{|role_name| prefix+role_name.to_s.split(/_/)}
|
58
|
+
else
|
59
|
+
# Reference to value type which is a table
|
60
|
+
[prefix + ["Value"]]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class TrueClass
|
70
|
+
def self.__absorb(prefix, except_role = nil)
|
71
|
+
[prefix]
|
72
|
+
end
|
73
|
+
end
|
@@ -35,7 +35,11 @@ module ActiveFacts
|
|
35
35
|
# Return an Array of Reference paths for such absorbed FKs
|
36
36
|
def all_absorbed_foreign_key_reference_path
|
37
37
|
references_from.inject([]) do |array, ref|
|
38
|
+
|
38
39
|
if ref.is_simple_reference
|
40
|
+
# This catches references that would be created to secondary supertypes, when absorption is through primary.
|
41
|
+
# There might be other cases where an exclusion like this is needed, but I can't reason it out.
|
42
|
+
next array if TypeInheritance === ref.fact_type && absorbed_via && TypeInheritance === absorbed_via.fact_type
|
39
43
|
array << [ref]
|
40
44
|
elsif ref.is_absorbing
|
41
45
|
ref.to.all_absorbed_foreign_key_reference_path.each{|aref|
|
@@ -102,8 +106,20 @@ module ActiveFacts
|
|
102
106
|
end
|
103
107
|
debug :fk, "to_columns in #{to.name}: #{to_columns.map { |column| column ? column.name : "OOPS!" }*", "}"
|
104
108
|
|
105
|
-
|
109
|
+
# Put the column pairs in a defined order, sorting key pairs by to-name:
|
110
|
+
froms, tos = from_columns.zip(to_columns).sort_by { |pair|
|
111
|
+
pair[1].name(nil)
|
112
|
+
}.transpose
|
113
|
+
|
114
|
+
ActiveFacts::Persistence::ForeignKey.new(self, to, fk_ref_path[-1], froms, tos)
|
106
115
|
end
|
116
|
+
end.
|
117
|
+
sort_by do |fk|
|
118
|
+
# Put the foreign keys in a defined order:
|
119
|
+
[ fk.to.name,
|
120
|
+
fk.to_columns.map{|col| col.name(nil).sort},
|
121
|
+
fk.from_columns.map{|col| col.name(nil).sort}
|
122
|
+
]
|
107
123
|
end
|
108
124
|
end
|
109
125
|
end
|
@@ -94,10 +94,14 @@ module ActiveFacts
|
|
94
94
|
# The reference path is the set of absorption references and one past it.
|
95
95
|
# Stopping here means we don't dig into the definitions of FK column counterparts.
|
96
96
|
# Note that many columns of an object may have the same ref_path.
|
97
|
+
#
|
98
|
+
# REVISIT:
|
99
|
+
# Note also that this produces columns ordered for each refpath the same as the
|
100
|
+
# order of the columns, not the same as the columns in the PK for which they might be an FK.
|
97
101
|
all_column_by_ref_path =
|
98
102
|
debug :index2, "Indexing columns by ref_path" do
|
99
|
-
columns.inject({}) do |hash, column|
|
100
|
-
debug :index2, "References in column #{name}
|
103
|
+
@columns.inject({}) do |hash, column|
|
104
|
+
debug :index2, "References in column #{name}.#{column.name}" do
|
101
105
|
ref_path = column.absorption_references
|
102
106
|
raise "No absorption_references for #{column.name} from #{column.references.map(&:to_s)*" and "}" if !ref_path || ref_path.empty?
|
103
107
|
(hash[ref_path] ||= []) << column
|
@@ -114,15 +118,17 @@ module ActiveFacts
|
|
114
118
|
inject({}) do |hash, ref_path|
|
115
119
|
ref_path.each do |ref|
|
116
120
|
next unless ref.to_role
|
121
|
+
#debug :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name(".")}*", "}"
|
122
|
+
#debugger if name == 'VehicleIncident' && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
|
117
123
|
ref.to_role.all_role_ref.each do |role_ref|
|
118
|
-
|
124
|
+
all_pcs = role_ref.role_sequence.all_presence_constraint
|
125
|
+
#puts "pcs over #{ref_path.map{|r| r.to_names}.flatten*"."}: #{role_ref.role_sequence.all_presence_constraint.map(&:describe)*"; "}" if all_pcs.size > 0
|
126
|
+
pcs = all_pcs.
|
119
127
|
reject do |pc|
|
120
128
|
!pc.max_frequency or # No maximum freq; cannot be a uniqueness constraint
|
121
129
|
pc.max_frequency != 1 or # maximum is not 1
|
122
|
-
|
123
|
-
|
124
|
-
fact_type.all_role.size == 1) # Unary
|
125
|
-
# The preceeeding two restrictions exclude the internal UCs created within NORMA.
|
130
|
+
# Constraint is not over a unary fact type role (NORMA does this)
|
131
|
+
pc.role_sequence.all_role_ref.size == 1 && ref_path[-1].to_role.fact_type.all_role.size == 1
|
126
132
|
end
|
127
133
|
next unless pcs.size > 0
|
128
134
|
# The columns for this ref_path support the UCs in "pcs".
|
@@ -151,8 +157,8 @@ module ActiveFacts
|
|
151
157
|
# Absorption through a one-to-one forms a UC that we don't need to enforce using an index:
|
152
158
|
next nil if over != self and
|
153
159
|
over.absorbed_via == columns[0].references[absorption_level-1] and
|
154
|
-
(
|
155
|
-
over.absorbed_via.fact_type.all_role.include?(
|
160
|
+
(rr = uc.role_sequence.all_role_ref.single) and
|
161
|
+
over.absorbed_via.fact_type.all_role.include?(rr.role)
|
156
162
|
|
157
163
|
index = ActiveFacts::Persistence::Index.new(
|
158
164
|
uc,
|
@@ -163,7 +169,12 @@ module ActiveFacts
|
|
163
169
|
)
|
164
170
|
debug :index, index
|
165
171
|
index
|
166
|
-
end.
|
172
|
+
end.
|
173
|
+
compact.
|
174
|
+
sort_by do |index|
|
175
|
+
# Put the indices in a defined order:
|
176
|
+
index.columns.map(&:name)
|
177
|
+
end
|
167
178
|
end
|
168
179
|
end
|
169
180
|
|
@@ -114,16 +114,16 @@ module ActiveFacts
|
|
114
114
|
def to_names
|
115
115
|
case
|
116
116
|
when is_unary
|
117
|
-
@to_role.fact_type.preferred_reading.reading_text.gsub(/\{[0-9]\}/,'').strip.split(
|
117
|
+
@to_role.fact_type.preferred_reading.reading_text.gsub(/\{[0-9]\}/,'').strip.split(/[_\s]/)
|
118
118
|
when @to && !@to_role # @to is an objectified fact type so @to_role is a phantom
|
119
|
-
|
119
|
+
@to.name.split(/[_\s]/)
|
120
120
|
when !@to_role # Self-value role of an independent ValueType
|
121
|
-
["#{@from.name}Value"]
|
121
|
+
["#{@from.name}", "Value"]
|
122
122
|
when @to_role.role_name # Named role
|
123
|
-
|
123
|
+
@to_role.role_name.split(/[_\s]/)
|
124
124
|
else # Use the name from the preferred reading
|
125
125
|
role_ref = @to_role.preferred_reference
|
126
|
-
[role_ref.leading_adjective, @to_role.concept.name, role_ref.trailing_adjective].compact.map{|w| w.split(
|
126
|
+
[role_ref.leading_adjective, @to_role.concept.name, role_ref.trailing_adjective].compact.map{|w| w.split(/[_\s]/)}.flatten.reject{|s| s == ''}
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -315,7 +315,7 @@ module ActiveFacts
|
|
315
315
|
end
|
316
316
|
end
|
317
317
|
|
318
|
-
class EntityType
|
318
|
+
class EntityType < Concept
|
319
319
|
def populate_references #:nodoc:
|
320
320
|
if fact_type && fact_type.all_role.size > 1
|
321
321
|
# NOT: fact_type.all_role.each do |role| # Place roles in the preferred order instead:
|
@@ -17,7 +17,7 @@ require 'activefacts/persistence/reference'
|
|
17
17
|
module ActiveFacts
|
18
18
|
module Metamodel
|
19
19
|
|
20
|
-
class ValueType
|
20
|
+
class ValueType < Concept
|
21
21
|
def absorbed_via #:nodoc:
|
22
22
|
# ValueTypes aren't absorbed in the way EntityTypes are
|
23
23
|
nil
|
@@ -55,7 +55,7 @@ module ActiveFacts
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
class EntityType
|
58
|
+
class EntityType < Concept
|
59
59
|
# A Reference from an entity type that fully absorbs this one
|
60
60
|
def absorbed_via; @absorbed_via; end
|
61
61
|
def absorbed_via=(r) #:nodoc:
|
@@ -133,8 +133,8 @@ module ActiveFacts
|
|
133
133
|
to_1 =
|
134
134
|
all_uniqueness_constraints.
|
135
135
|
detect do |c|
|
136
|
-
c.role_sequence.all_role_ref.
|
137
|
-
|
136
|
+
(rr = c.role_sequence.all_role_ref.single) and
|
137
|
+
rr.role == self
|
138
138
|
end
|
139
139
|
|
140
140
|
if fact_type.entity_type
|
@@ -310,7 +310,10 @@ module ActiveFacts
|
|
310
310
|
populate_all_columns
|
311
311
|
populate_all_indices
|
312
312
|
|
313
|
-
@tables =
|
313
|
+
@tables =
|
314
|
+
all_feature.
|
315
|
+
select { |f| f.is_table }.
|
316
|
+
sort_by { |table| table.name }
|
314
317
|
end
|
315
318
|
end
|
316
319
|
|
data/lib/activefacts/version.rb
CHANGED
@@ -58,7 +58,7 @@ module ActiveFacts
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
class
|
61
|
+
class Join
|
62
62
|
def column_name(joiner = '-')
|
63
63
|
concept == input_role.concept ? input_role.preferred_reference.role_name(joiner) : Array(concept.name)
|
64
64
|
end
|
@@ -72,8 +72,8 @@ module ActiveFacts
|
|
72
72
|
|
73
73
|
class RoleRef
|
74
74
|
def describe
|
75
|
-
# The reference traverses the
|
76
|
-
|
75
|
+
# The reference traverses the Joins in sequence to the final role:
|
76
|
+
all_join.sort_by{|jp| jp.join_step}.map{ |jp| jp.describe + "." }*"" + role_name
|
77
77
|
end
|
78
78
|
|
79
79
|
def role_name(joiner = "-")
|
@@ -86,13 +86,13 @@ module ActiveFacts
|
|
86
86
|
return joiner ? Array(name_array)*joiner : Array(name_array)
|
87
87
|
end
|
88
88
|
|
89
|
-
# Two RoleRefs are equal if they have the same role and
|
89
|
+
# Two RoleRefs are equal if they have the same role and Joins with matching roles
|
90
90
|
def ==(role_ref)
|
91
91
|
RoleRef === role_ref &&
|
92
92
|
role_ref.role == role &&
|
93
|
-
|
94
|
-
!
|
95
|
-
zip(role_ref.
|
93
|
+
all_join.size == role_ref.all_join.size &&
|
94
|
+
!all_join.sort_by{|j|j.join_step}.
|
95
|
+
zip(role_ref.all_join.sort_by{|j|j.join_step}).
|
96
96
|
detect{|j1,j2|
|
97
97
|
j1.input_role != j2.input_role ||
|
98
98
|
j1.output_role != j2.output_role
|
@@ -376,6 +376,16 @@ module ActiveFacts
|
|
376
376
|
((max && min == max) ? "#{max == 1 ? "one" : max.to_s}" : nil)
|
377
377
|
].compact * " and"
|
378
378
|
end
|
379
|
+
|
380
|
+
def describe
|
381
|
+
role_sequence.describe + " occurs " + frequency + " time"
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
class TypeInheritance
|
386
|
+
def describe(role = nil)
|
387
|
+
"#{subtype.name} is a kind of #{supertype.name}"
|
388
|
+
end
|
379
389
|
end
|
380
390
|
|
381
391
|
end
|
@@ -55,6 +55,11 @@ module ActiveFacts
|
|
55
55
|
value_type :length => 32
|
56
56
|
end
|
57
57
|
|
58
|
+
class Pronoun < String
|
59
|
+
value_type
|
60
|
+
# REVISIT: Pronoun has restricted values
|
61
|
+
end
|
62
|
+
|
58
63
|
class ReadingText < String
|
59
64
|
value_type :length => 256
|
60
65
|
end
|
@@ -90,7 +95,7 @@ module ActiveFacts
|
|
90
95
|
end
|
91
96
|
|
92
97
|
class Coefficient
|
93
|
-
identified_by :numerator, :denominator
|
98
|
+
identified_by :numerator, :denominator, :is_precise
|
94
99
|
has_one :denominator # See Denominator.all_coefficient
|
95
100
|
maybe :is_precise
|
96
101
|
has_one :numerator # See Numerator.all_coefficient
|
@@ -175,11 +180,11 @@ module ActiveFacts
|
|
175
180
|
one_to_one :unit_id # See UnitId.unit
|
176
181
|
end
|
177
182
|
|
178
|
-
class
|
179
|
-
identified_by :
|
180
|
-
has_one :base_unit, Unit # See Unit.
|
181
|
-
has_one :derived_unit, Unit # See Unit.
|
182
|
-
has_one :exponent # See Exponent.
|
183
|
+
class Derivation
|
184
|
+
identified_by :derived_unit, :base_unit
|
185
|
+
has_one :base_unit, Unit # See Unit.all_derivation_as_base_unit
|
186
|
+
has_one :derived_unit, Unit # See Unit.all_derivation_as_derived_unit
|
187
|
+
has_one :exponent # See Exponent.all_derivation
|
183
188
|
end
|
184
189
|
|
185
190
|
class ValueRange
|
@@ -194,7 +199,7 @@ module ActiveFacts
|
|
194
199
|
end
|
195
200
|
|
196
201
|
class AllowedRange
|
197
|
-
identified_by :
|
202
|
+
identified_by :value_restriction, :value_range
|
198
203
|
has_one :value_range # See ValueRange.all_allowed_range
|
199
204
|
has_one :value_restriction # See ValueRestriction.all_allowed_range
|
200
205
|
end
|
@@ -205,19 +210,19 @@ module ActiveFacts
|
|
205
210
|
end
|
206
211
|
|
207
212
|
class Import
|
208
|
-
identified_by :
|
213
|
+
identified_by :vocabulary, :imported_vocabulary
|
209
214
|
has_one :imported_vocabulary, Vocabulary # See Vocabulary.all_import_as_imported_vocabulary
|
210
215
|
has_one :vocabulary # See Vocabulary.all_import
|
211
216
|
end
|
212
217
|
|
213
218
|
class Feature
|
214
|
-
identified_by :
|
219
|
+
identified_by :vocabulary, :name
|
215
220
|
has_one :name # See Name.all_feature
|
216
221
|
has_one :vocabulary # See Vocabulary.all_feature
|
217
222
|
end
|
218
223
|
|
219
224
|
class Correspondence
|
220
|
-
identified_by :
|
225
|
+
identified_by :import, :imported_feature
|
221
226
|
has_one :import # See Import.all_correspondence
|
222
227
|
has_one :imported_feature, Feature # See Feature.all_correspondence_as_imported_feature
|
223
228
|
has_one :local_feature, Feature # See Feature.all_correspondence_as_local_feature
|
@@ -251,7 +256,7 @@ module ActiveFacts
|
|
251
256
|
|
252
257
|
class Concept < Feature
|
253
258
|
maybe :is_independent
|
254
|
-
|
259
|
+
has_one :pronoun # See Pronoun.all_concept
|
255
260
|
end
|
256
261
|
|
257
262
|
class Role
|
@@ -272,13 +277,13 @@ module ActiveFacts
|
|
272
277
|
has_one :trailing_adjective, Adjective # See Adjective.all_role_ref_as_trailing_adjective
|
273
278
|
end
|
274
279
|
|
275
|
-
class
|
280
|
+
class Join
|
276
281
|
identified_by :role_ref, :join_step
|
277
|
-
has_one :join_step, Ordinal # See Ordinal.
|
278
|
-
has_one :role_ref # See RoleRef.
|
279
|
-
has_one :concept # See Concept.
|
280
|
-
has_one :input_role, Role # See Role.
|
281
|
-
has_one :output_role, Role # See Role.
|
282
|
+
has_one :join_step, Ordinal # See Ordinal.all_join_as_join_step
|
283
|
+
has_one :role_ref # See RoleRef.all_join
|
284
|
+
has_one :concept # See Concept.all_join
|
285
|
+
has_one :input_role, Role # See Role.all_join_as_input_role
|
286
|
+
has_one :output_role, Role # See Role.all_join_as_output_role
|
282
287
|
end
|
283
288
|
|
284
289
|
class EntityType < Concept
|