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
@@ -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
|