activefacts-rmap 1.8.1 → 1.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf33b3400b5c538ad43e3db107ca862a29f5b1f5
4
- data.tar.gz: 3eb0d4cfa5be71a688a8a41cb138601fd76f6e3d
3
+ metadata.gz: 41a6ef1c77d0e5d8085c6564a4cdf9e4715094ba
4
+ data.tar.gz: 59989842387ccc9f7bc701cd9be100279ef8af92
5
5
  SHA512:
6
- metadata.gz: d5358ddf562d53888ce4b9213a85e845d43b6b1947692bb8b2661b9ed30214b7c9fc2617ca589aaf674fde0715f584fdc8cba27ed4b1ece4ad68ddce60bd275a
7
- data.tar.gz: 1c6170b95730f6b6c0ad08249953ea9dd2c63ea78aedabbb240b6190289d7f27d5074d11280eba6418c248e20f1f90b088ba2f75722941e55d444027f2181208
6
+ metadata.gz: c14280f747f47d489475e8499c1b1fb40f6dabfd593c424e7494f62bb39677dc04c2f2022443c39595a51439be01c501634ad07312040b453f98f0fc29e6083a
7
+ data.tar.gz: b82c172e59dbad9b14078be5a2410d96ca8fac4e2e6777e01f63ce1e3ea592a71f9f6a901d7ae6ff7311f8ecf987c45de080834f928e47ea8b6ffed45173c39a
data/Gemfile CHANGED
@@ -2,7 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- if ENV['PWD'] =~ %r{\A#{ENV['HOME']}/work}
6
- $stderr.puts "Using work area gems for #{File.basename(File.dirname(__FILE__))} from activefacts-rmap"
7
- gem 'activefacts-metamodel', path: '/Users/cjh/work/activefacts/metamodel'
5
+ this_file = File.absolute_path(__FILE__)
6
+ if this_file =~ %r{\A#{ENV['HOME']}}i
7
+ dir = File.dirname(File.dirname(this_file))
8
+ $stderr.puts "Using work area gems in #{dir} from activefacts-rmap"
9
+ gem 'activefacts-metamodel', path: dir+'/metamodel'
8
10
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_development_dependency "bundler", ">= 1.10", "~> 1.10.6"
20
+ spec.add_development_dependency "bundler", ">= 1.10"
21
21
  spec.add_development_dependency "rake", "~> 10.0"
22
22
  spec.add_development_dependency "rspec", "~> 3.3"
23
23
 
@@ -68,69 +68,73 @@ module ActiveFacts
68
68
 
69
69
  def self.name(refs, separator = "")
70
70
  last_names = []
71
- names = refs.
72
- inject([]) do |a, ref|
73
-
74
- # Skip any object after the first which is identified by this reference
75
- if ref != refs[0] and
76
- !ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) and
77
- ref.to and
78
- ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
79
- (role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
80
- role_ref.role == ref.from_role
81
- trace :columns, "Skipping #{ref}, identifies non-initial object"
82
- next a
83
- end
71
+ name_array = nil
72
+ trace :columns, "Building column name from #{refs.inspect}" do
73
+ names = refs.
74
+ inject([]) do |a, ref|
75
+
76
+ # Skip any object after the first which is identified by this reference
77
+ if ref != refs[0] and
78
+ !ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) and
79
+ ref.to and
80
+ ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
81
+ (role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
82
+ role_ref.role == ref.from_role
83
+ trace :columns, "Skipping #{ref}, identifies non-initial object"
84
+ next a
85
+ end
84
86
 
85
- names = ref.to_names(ref != refs.last)
87
+ names = ref.to_names(ref != refs.last)
86
88
 
87
- # When traversing type inheritances, keep the subtype name, not the supertype names as well:
88
- if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
89
- if ref.to != ref.fact_type.subtype # Did we already have the subtype?
90
- trace :columns, "Skipping supertype #{ref}"
91
- next a
89
+ # When traversing type inheritances, keep the subtype name, not the supertype names as well:
90
+ if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
91
+ if ref.to != ref.fact_type.subtype # Did we already have the subtype?
92
+ trace :columns, "Skipping supertype #{ref}"
93
+ next a
94
+ end
95
+ trace :columns, "Eliding supertype in #{ref}"
96
+ last_names.size.times { a.pop } # Remove the last names added
97
+ elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
98
+ # When Xyz is followed by XyzID, truncate that to just ID
99
+ trace :columns, "truncating repeated #{last_names.last} in #{names[0]}"
100
+ names[0] = names[0][last_names.last.size..-1]
101
+ names.shift if names[0] == ''
102
+ elsif last_names.last == names[0]
103
+ # Same, but where an underscore split up the words
104
+ trace :columns, "truncating repeated name in #{names.inspect}"
105
+ names.shift
92
106
  end
93
- trace :columns, "Eliding supertype in #{ref}"
94
- last_names.size.times { a.pop } # Remove the last names added
95
- elsif last_names.last && last_names.last == names[0][0...last_names.last.size]
96
- # When Xyz is followed by XyzID, truncate that to just ID
97
- trace :columns, "truncating repeated #{last_names.last} in #{names[0]}"
98
- names[0] = names[0][last_names.last.size..-1]
99
- names.shift if names[0] == ''
100
- elsif last_names.last == names[0]
101
- # Same, but where an underscore split up the words
102
- trace :columns, "truncating repeated name in #{names.inspect}"
103
- names.shift
104
- end
105
107
 
106
- # If the reference is to the single identifying role of the object_type making the reference,
107
- # strip the object_type name from the start of the reference role
108
- if a.size > 0 and
109
- (et = ref.from).is_a?(ActiveFacts::Metamodel::EntityType) and
110
- # This instead of the next 2 would apply to all identifying roles, but breaks some examples:
111
- # (role_ref = et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == ref.to_role}) and
112
- (role_ref = et.preferred_identifier.role_sequence.all_role_ref.single) and
113
- role_ref.role == ref.to_role and
114
- names[0][0...et.name.size].downcase == et.name.downcase
115
-
116
- trace :columns, "truncating transitive identifying role #{names.inspect}"
117
- names[0] = names[0][et.name.size..-1]
118
- names.shift if names[0] == ""
119
- end
108
+ # If the reference is to the single identifying role of the object_type making the reference,
109
+ # strip the object_type name from the start of the reference role
110
+ if a.size > 0 and
111
+ (et = ref.from).is_a?(ActiveFacts::Metamodel::EntityType) and
112
+ # This instead of the next 2 would apply to all identifying roles, but breaks some examples:
113
+ # (role_ref = et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == ref.to_role}) and
114
+ (role_ref = et.preferred_identifier.role_sequence.all_role_ref.single) and
115
+ role_ref.role == ref.to_role and
116
+ names[0][0...et.name.size].downcase == et.name.downcase
117
+
118
+ trace :columns, "truncating transitive identifying role #{names.inspect}"
119
+ names[0] = names[0][et.name.size..-1]
120
+ names.shift if names[0] == ""
121
+ end
120
122
 
121
- last_names = names
123
+ last_names = names
122
124
 
123
- a += names
124
- a
125
- end.elide_repeated_subsequences { |a, b|
126
- if a.is_a?(Array)
127
- a.map{|e| e.downcase} == b.map{|e| e.downcase}
128
- else
129
- a.downcase == b.downcase
130
- end
131
- }
125
+ a += names
126
+ a
127
+ end.elide_repeated_subsequences { |a, b|
128
+ if a.is_a?(Array)
129
+ a.map{|e| e.downcase} == b.map{|e| e.downcase}
130
+ else
131
+ a.downcase == b.downcase
132
+ end
133
+ }
132
134
 
133
- name_array = names.map{|n| n.sub(/^[a-z]/){|s| s.upcase}}
135
+ name_array = names.map{|n| n.sub(/^[a-z]/){|s| s.upcase}}
136
+ trace :columns, "column name is #{name_array*'.'}"
137
+ end
134
138
  separator ? name_array * separator : name_array
135
139
  end
136
140
 
@@ -163,21 +167,21 @@ module ActiveFacts
163
167
  end
164
168
 
165
169
  vt = references[-1].is_self_value ? references[-1].from : references[-1].to
166
- begin
170
+ begin
167
171
  params[:length] ||= vt.length if vt.length.to_i != 0
168
172
  params[:scale] ||= vt.scale if vt.scale.to_i != 0
169
173
  constraints << vt.value_constraint if vt.value_constraint
170
- last_vt = vt
174
+ last_vt = vt
171
175
  vt = vt.supertype
172
176
  end while vt
173
- params[:underlying_type] = last_vt
177
+ params[:underlying_type] = last_vt
174
178
  return [last_vt.name, params, constraints]
175
179
  end
176
180
 
177
181
  # The comment is the readings from the References expressed as a series of steps (not a full verbalisation)
178
182
  def comment
179
183
  @references.map do |ref|
180
- ref.verbalised_path
184
+ ref.verbalised_path
181
185
  end.compact * " and "
182
186
  end
183
187
 
@@ -192,34 +196,34 @@ module ActiveFacts
192
196
  cols =
193
197
  if is_unary
194
198
  kind = "unary "
195
- objectified_unary_columns =
196
- ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
199
+ objectified_unary_columns =
200
+ ((@to && @to.fact_type) ? @to.all_columns(excluded_supertypes) : [])
197
201
 
198
202
  =begin
199
- # This code omits the unary if it's objectified and that plays a mandatory role
200
- first_mandatory_column = nil
201
- if (@to && @to.fact_type)
202
- trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do
203
- first_mandatory_column =
204
- objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary
205
- trace :unary_col, "checking column #{col.name}" do
206
- !col.references.detect do |ref|
207
- trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}"
208
- !ref.is_mandatory
209
- end
210
- end
211
- end
212
- if is_from_objectified_fact && first_mandatory_column
213
- trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory"
214
- end
215
- end
216
- end
217
-
218
- (is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified
203
+ # This code omits the unary if it's objectified and that plays a mandatory role
204
+ first_mandatory_column = nil
205
+ if (@to && @to.fact_type)
206
+ trace :unary_col, "Deciding whether to skip unary column for #{inspect}" do
207
+ first_mandatory_column =
208
+ objectified_unary_columns.detect do |col| # Detect a mandatory column for the unary
209
+ trace :unary_col, "checking column #{col.name}" do
210
+ !col.references.detect do |ref|
211
+ trace :unary_col, "#{ref} is mandatory=#{ref.is_mandatory.inspect}"
212
+ !ref.is_mandatory
213
+ end
214
+ end
215
+ end
216
+ if is_from_objectified_fact && first_mandatory_column
217
+ trace :unary_col, "Skipping unary column for #{inspect} because #{first_mandatory_column.name} is mandatory"
218
+ end
219
+ end
220
+ end
221
+
222
+ (is_from_objectified_fact && first_mandatory_column ? [] : [Column.new()]) + # The unary itself, unless its objectified
219
223
  =end
220
224
 
221
- [Column.new()] + # The unary itself
222
- objectified_unary_columns
225
+ [Column.new()] + # The unary itself
226
+ objectified_unary_columns
223
227
  elsif is_self_value
224
228
  kind = "self-role "
225
229
  [Column.new()]
@@ -258,7 +262,7 @@ module ActiveFacts
258
262
  end
259
263
 
260
264
  def wipe_columns
261
- @columns = nil
265
+ @columns = nil
262
266
  end
263
267
  end
264
268
 
@@ -327,7 +331,7 @@ module ActiveFacts
327
331
  def identifier_columns
328
332
  trace :columns, "Identifier Columns for #{name}" do
329
333
  if absorbed_via and
330
- # If this is a subtype that has its own identification, use that.
334
+ # If this is a subtype that has its own identification, use that instead
331
335
  (all_type_inheritance_as_subtype.size == 0 ||
332
336
  all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification })
333
337
  return absorbed_via.from.identifier_columns
@@ -28,62 +28,62 @@ module ActiveFacts
28
28
  end
29
29
 
30
30
  def describe
31
- "foreign key from #{from.name}(#{from_columns.map{|c| c.name}*', '}) to #{to.name}(#{to_columns.map{|c| c.name}*', '})"
31
+ "foreign key from #{from.name}(#{from_columns.map{|c| c.name}*', '}) to #{to.name}(#{to_columns.map{|c| c.name}*', '})"
32
32
  end
33
33
 
34
34
  def verbalised_path reverse = false
35
- # REVISIT: This should be a proper join path verbalisation:
36
- refs = reverse ? references.reverse : references
37
- refs.map do |r|
38
- r.verbalised_path reverse
39
- end * ' and '
35
+ # REVISIT: This should be a proper join path verbalisation:
36
+ refs = reverse ? references.reverse : references
37
+ refs.map do |r|
38
+ r.verbalised_path reverse
39
+ end * ' and '
40
40
  end
41
41
 
42
42
  # Which references are absorbed into the "from" table?
43
43
  def precursor_references
44
- fk_jump = @references.detect(&:fk_jump)
45
- jump_index = @references.index(fk_jump)
46
- @references[0, jump_index]
44
+ fk_jump = @references.detect(&:fk_jump)
45
+ jump_index = @references.index(fk_jump)
46
+ @references[0, jump_index]
47
47
  end
48
48
 
49
49
  # Which references are absorbed into the "to" table?
50
50
  def following_references
51
- fk_jump = @references.detect(&:fk_jump)
52
- jump_index = @references.index(fk_jump)
53
- fk_jump != @references.last ? @references[jump_index+1..-1] : []
51
+ fk_jump = @references.detect(&:fk_jump)
52
+ jump_index = @references.index(fk_jump)
53
+ fk_jump != @references.last ? @references[jump_index+1..-1] : []
54
54
  end
55
55
 
56
56
  def jump_reference
57
- @references.detect(&:fk_jump)
57
+ @references.detect(&:fk_jump)
58
58
  end
59
59
 
60
60
  def to_name
61
- p = precursor_references
62
- f = following_references
63
- j = jump_reference
61
+ p = precursor_references
62
+ f = following_references
63
+ j = jump_reference
64
64
 
65
- @references.last.to_names +
66
- (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
65
+ @references.last.to_names +
66
+ (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
67
67
  end
68
68
 
69
69
  # The from_name is the role name of the table with the FK, viewed from the other end
70
70
  # When there are no precursor_references or following_references, it's the jump_reference.from_names
71
71
  # REVISIT: I'm still working out what to do with precursor_references and following_references
72
72
  def from_name
73
- p = precursor_references
74
- f = following_references
75
- j = jump_reference
73
+ p = precursor_references
74
+ f = following_references
75
+ j = jump_reference
76
76
 
77
- # pluralise unless j.is_one_to_one
77
+ # pluralise unless j.is_one_to_one
78
78
 
79
- # REVISIT: references[0].from_names is where the FK lives; but the object of interest may be an absorbed subclass which we should use here instead:
80
- # REVISIT: Should crunch superclasses in subtype traversals
81
- # REVISIT: Need to add "_as_rolename" where rolename is not to.name
79
+ # REVISIT: references[0].from_names is where the FK lives; but the object of interest may be an absorbed subclass which we should use here instead:
80
+ # REVISIT: Should crunch superclasses in subtype traversals
81
+ # REVISIT: Need to add "_as_rolename" where rolename is not to.name
82
82
 
83
- [
84
- @references[0].from_names,
85
- (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
86
- ]
83
+ [
84
+ @references[0].from_names,
85
+ (p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
86
+ ]
87
87
  end
88
88
 
89
89
  end
@@ -103,83 +103,83 @@ module ActiveFacts
103
103
  # REVISIT: Disabled, as this should never happen.
104
104
  # next array if ref.to.absorbed_via != ref.fact_type
105
105
  end
106
- ref.fk_jump = true
106
+ ref.fk_jump = true
107
107
  array << [ref]
108
108
  elsif ref.is_absorbing or (ref.to && !ref.to.is_table)
109
- trace :fk, "getting fks absorbed into #{name} via #{ref}" do
110
- ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
111
- array << aref.insert(0, ref)
112
- end
113
- end
109
+ trace :fk, "getting fks absorbed into #{name} via #{ref}" do
110
+ ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
111
+ array << aref.insert(0, ref)
112
+ end
113
+ end
114
114
  end
115
115
  array
116
116
  end
117
117
  end
118
118
 
119
119
  def foreign_keys_to
120
- @foreign_keys_to ||= []
120
+ @foreign_keys_to ||= []
121
121
  end
122
122
 
123
123
  # Return an array of all the foreign keys from this table
124
124
  def foreign_keys
125
125
 
126
126
  # Get the ForeignKey object for each absorbed reference path
127
- @foreign_keys ||=
128
- begin
129
- fk_ref_paths = all_absorbed_foreign_key_reference_path
130
- fk_ref_paths.map do |fk_ref_path|
131
- trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
132
-
133
- from_columns = (columns||all_columns({})).select{|column|
134
- column.references[0...fk_ref_path.size] == fk_ref_path
135
- }
136
- trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
137
-
138
- # Figure out absorption on the target end:
139
- to = fk_ref_path.last.to
140
- if to.absorbed_via
141
- trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
142
- while (r = to.absorbed_via)
143
- m = r.reversed
144
- trace :fk, "#{m.reading}"
145
- fk_ref_path << m
146
- to = m.from == to ? m.to : m.from
147
- end
148
- trace :fk, "Absorption ends at #{to.name}"
149
- end
150
- end
151
-
152
- # REVISIT: This test may no longer be necessary
153
- raise "REVISIT: #{fk_ref_path.inspect} is bad" unless to and to.columns
154
-
155
- # REVISIT: This fails for absorbed subtypes having their own identification.
156
- # Check the CompanyDirectorEmployee model for example, EmployeeManagerNr -> Person (should reference EmployeeNr)
157
- # Need to use the absorbed identifier_columns of the subtype,
158
- # not the columns of the supertype that absorbs it.
159
- # But in general, that isn't going to work because in most DBMS
160
- # there's no suitable uniquen index on the subtype's identifier_columns
161
-
162
- to_columns = fk_ref_path[-1].to.identifier_columns
163
-
164
- # Put the column pairs in the correct order. They MUST be in the order they appear in the primary key
165
- froms, tos = from_columns.zip(to_columns).sort_by { |pair|
166
- to_columns.index(pair[1])
167
- }.transpose
168
-
169
- fk = ActiveFacts::RMap::ForeignKey.new(self, to, fk_ref_path, froms, tos)
170
- to.foreign_keys_to << fk
171
- fk
172
- end
173
- end.
174
- sort_by do |fk|
175
- # Put the foreign keys in a defined order:
176
- # debugger if !fk.to_columns || fk.to_columns.include?(nil) || !fk.from_columns || fk.from_columns.include?(nil)
177
- [ fk.to.name,
178
- fk.to_columns.map{|col| col.name(nil).sort},
179
- fk.from_columns.map{|col| col.name(nil).sort}
180
- ]
181
- end
182
- end
127
+ @foreign_keys ||=
128
+ begin
129
+ fk_ref_paths = all_absorbed_foreign_key_reference_path
130
+ fk_ref_paths.map do |fk_ref_path|
131
+ trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
132
+
133
+ from_columns = (columns||all_columns({})).select{|column|
134
+ column.references[0...fk_ref_path.size] == fk_ref_path
135
+ }
136
+ trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
137
+
138
+ # Figure out absorption on the target end:
139
+ to = fk_ref_path.last.to
140
+ if to.absorbed_via
141
+ trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
142
+ while (r = to.absorbed_via)
143
+ m = r.reversed
144
+ trace :fk, "#{m.reading}"
145
+ fk_ref_path << m
146
+ to = m.from == to ? m.to : m.from
147
+ end
148
+ trace :fk, "Absorption ends at #{to.name}"
149
+ end
150
+ end
151
+
152
+ # REVISIT: This test may no longer be necessary
153
+ raise "REVISIT: #{fk_ref_path.inspect} is bad" unless to and to.columns
154
+
155
+ # REVISIT: This fails for absorbed subtypes having their own identification.
156
+ # Check the CompanyDirectorEmployee model for example, EmployeeManagerNr -> Person (should reference EmployeeNr)
157
+ # Need to use the absorbed identifier_columns of the subtype,
158
+ # not the columns of the supertype that absorbs it.
159
+ # But in general, that isn't going to work because in most DBMS
160
+ # there's no suitable uniquen index on the subtype's identifier_columns
161
+
162
+ to_columns = fk_ref_path[-1].to.identifier_columns
163
+
164
+ # Put the column pairs in the correct order. They MUST be in the order they appear in the primary key
165
+ froms, tos = from_columns.zip(to_columns).sort_by { |pair|
166
+ to_columns.index(pair[1])
167
+ }.transpose
168
+
169
+ fk = ActiveFacts::RMap::ForeignKey.new(self, to, fk_ref_path, froms, tos)
170
+ to.foreign_keys_to << fk
171
+ fk
172
+ end
173
+ end.
174
+ sort_by do |fk|
175
+ # Put the foreign keys in a defined order:
176
+ # debugger if !fk.to_columns || fk.to_columns.include?(nil) || !fk.from_columns || fk.from_columns.include?(nil)
177
+ [ fk.to.name,
178
+ fk.to_columns.map{|col| col.name(nil).sort},
179
+ fk.from_columns.map{|col| col.name(nil).sort}
180
+ ]
181
+ end
182
+ end
183
183
 
184
184
  end
185
185
  end
@@ -69,13 +69,13 @@ module ActiveFacts
69
69
  end
70
70
 
71
71
  def to_s #:nodoc:
72
- if @uniqueness_constraint
73
- name = @uniqueness_constraint.name
74
- preferred = @uniqueness_constraint.is_preferred_identifier ? " (preferred)" : ""
75
- else
76
- name = "#{@on.name}IsUnique"
77
- preferred = !@on.injected_surrogate_role ? " (preferred)" : ""
78
- end
72
+ if @uniqueness_constraint
73
+ name = @uniqueness_constraint.name
74
+ preferred = @uniqueness_constraint.is_preferred_identifier ? " (preferred)" : ""
75
+ else
76
+ name = "#{@on.name}IsUnique"
77
+ preferred = !@on.injected_surrogate_role ? " (preferred)" : ""
78
+ end
79
79
  colnames = @columns.map(&:name)*", "
80
80
  "Index #{name} on #{@on.name} over #{@over.name}(#{colnames})#{preferred}"
81
81
  end
@@ -85,26 +85,26 @@ module ActiveFacts
85
85
  module Metamodel #:nodoc:
86
86
  class EntityType
87
87
  def self_index
88
- nil
88
+ nil
89
89
  end
90
90
  end
91
91
 
92
92
  class ValueType
93
93
  def self_index
94
- ActiveFacts::RMap::Index.new(
95
- nil, # The implied uniqueness constraint is not created
96
- self, # ValueType being indexed
97
- self, # Absorbed object being indexed
98
- columns.select{|c| c.references[0].is_self_value},
99
- injected_surrogate_role ? false : true
100
- )
94
+ ActiveFacts::RMap::Index.new(
95
+ nil, # The implied uniqueness constraint is not created
96
+ self, # ValueType being indexed
97
+ self, # Absorbed object being indexed
98
+ columns.select{|c| c.references[0].is_self_value},
99
+ injected_surrogate_role ? false : true
100
+ )
101
101
  end
102
102
  end
103
103
 
104
104
  class ObjectType
105
105
  # An array of each Index for this table
106
106
  def indices
107
- @indices || populate_indices
107
+ @indices || populate_indices
108
108
  end
109
109
 
110
110
  def clear_indices #:nodoc:
@@ -146,7 +146,7 @@ module ActiveFacts
146
146
  # trace :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name('.')}*", "}"
147
147
  ref.to_role.all_role_ref.each do |role_ref|
148
148
  all_pcs = role_ref.role_sequence.all_presence_constraint
149
- # 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
149
+ # 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
150
150
  pcs = all_pcs.
151
151
  reject do |pc|
152
152
  !pc.max_frequency or # No maximum freq; cannot be a uniqueness constraint
@@ -179,12 +179,12 @@ module ActiveFacts
179
179
  over = columns[0].references[absorption_level].from
180
180
 
181
181
  # Absorption through a one-to-one forms a UC that we don't need to enforce using an index:
182
- if over != self and
182
+ if over != self and
183
183
  over.absorbed_via == columns[0].references[absorption_level-1] and
184
184
  (rr = uc.role_sequence.all_role_ref.single) and
185
185
  over.absorbed_via.fact_type.all_role.include?(rr.role)
186
- next nil
187
- end
186
+ next nil
187
+ end
188
188
 
189
189
  index = ActiveFacts::RMap::Index.new(
190
190
  uc,
@@ -202,9 +202,9 @@ module ActiveFacts
202
202
  index.columns.map(&:name)+['', index.over.name]
203
203
  end
204
204
  end
205
- si = self_index
206
- @indices.unshift(si) if si
207
- @indices
205
+ si = self_index
206
+ @indices.unshift(si) if si
207
+ @indices
208
208
  end
209
209
 
210
210
  end
@@ -74,9 +74,9 @@ module ActiveFacts
74
74
 
75
75
  # Is this Reference covered by a mandatory constraint (implicitly or explicitly)
76
76
  def is_mandatory
77
- !is_unary &&
78
- (!@from_role || # All phantom roles of fact types are mandatory
79
- @from_role.is_mandatory)
77
+ !is_unary &&
78
+ (!@from_role || # All phantom roles of fact types are mandatory
79
+ @from_role.is_mandatory)
80
80
  end
81
81
 
82
82
  # Is this Reference from a unary Role?
@@ -177,6 +177,7 @@ module ActiveFacts
177
177
  # Flip the reference
178
178
  @to, @from = @from, @to
179
179
  @to_role, @from_role = @from_role, @to_role
180
+ trace :references, "Mirror #{self.inspect} absorbs #{@to.name}" if @to.absorbed_via == self
180
181
  self
181
182
  end
182
183
 
@@ -212,23 +213,23 @@ module ActiveFacts
212
213
  end
213
214
 
214
215
  def verbalised_path reverse = false
215
- return "#{from.name} Value" if is_self_value
216
- objectified = fact_type.entity_type
217
- f = # Switch to the Link Fact Type if we're traversing an objectification
218
- (to_role && to_role.link_fact_type) ||
219
- (from_role && from_role.link_fact_type) ||
220
- fact_type
221
-
222
- start_role =
223
- if objectified
224
- target = reverse ? to : from
225
- [to_role, from_role, f.all_role[0]].compact.detect{|role| role.object_type == target}
226
- else
227
- reverse ? to_role : from_role
228
- end
229
- reading = f.reading_preferably_starting_with_role(start_role)
230
- (is_mandatory || is_unary ? '' : 'maybe ') +
231
- reading.expand
216
+ return "#{from.name} Value" if is_self_value
217
+ objectified = fact_type.entity_type
218
+ f = # Switch to the Link Fact Type if we're traversing an objectification
219
+ (to_role && to_role.link_fact_type) ||
220
+ (from_role && from_role.link_fact_type) ||
221
+ fact_type
222
+
223
+ start_role =
224
+ if objectified
225
+ target = reverse ? to : from
226
+ [to_role, from_role, f.all_role_in_order[0]].compact.detect{|role| role.object_type == target}
227
+ else
228
+ reverse ? to_role : from_role
229
+ end
230
+ reading = f.reading_preferably_starting_with_role(start_role)
231
+ (is_mandatory || is_unary ? '' : 'maybe ') +
232
+ reading.expand
232
233
  end
233
234
 
234
235
  def inspect #:nodoc:
@@ -294,11 +295,8 @@ module ActiveFacts
294
295
  all_role.each do |role|
295
296
  # It's possible that this role is in an implicit or derived fact type. Skip it if so.
296
297
  next if role.fact_type.is_a?(LinkFactType) or
297
- # REVISIT: dafuq? Is this looking for a constraint over a derivation? This looks wrong.
298
- role.fact_type.preferred_reading.role_sequence.all_role_ref.to_a[0].play or
299
- # This is not yet actually set, and wouldn't handle constraint derivations anyhow:
300
- role.variable_as_projection
301
-
298
+ # REVISIT: dafuq? Is this looking for a constraint over a derivation? This looks wrong.
299
+ role.fact_type.preferred_reading.role_sequence.all_role_ref.to_a[0].play
302
300
  populate_reference role
303
301
  end
304
302
  end
@@ -324,13 +322,13 @@ module ActiveFacts
324
322
  when :supertype # A subtype absorbs a reference to its supertype when separate, or all when partitioned
325
323
  # REVISIT: Or when partitioned
326
324
  raise "Internal error, expected TypeInheritance" unless role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
327
- counterpart_role = (role.fact_type.all_role.to_a-[role])[0]
325
+ counterpart_role = (role.fact_type.all_role.to_a-[role])[0]
328
326
  if role.fact_type.assimilation or counterpart_role.object_type.is_separate
329
327
  trace :references, "supertype #{name} doesn't absorb a reference to separate subtype #{role.fact_type.subtype.name}"
330
328
  else
331
329
  r = ActiveFacts::RMap::Reference.new(self, role)
332
330
  r.to.absorbed_via = r
333
- trace :references, "supertype #{name} absorbs subtype #{r.to.name}"
331
+ trace :references, "Supertype #{name} absorbs subtype #{r.to.name}"
334
332
  r.tabulate
335
333
  end
336
334
 
@@ -384,7 +382,7 @@ module ActiveFacts
384
382
  r.tabulate
385
383
  end
386
384
  else
387
- # REVISIT: Should we implicitly objectify this fact type here and add a spanning UC?
385
+ # REVISIT: Should we implicitly objectify this fact type here and add a spanning UC?
388
386
  raise "Role #{role.object_type.name} in '#{role.fact_type.default_reading}' lacks a uniqueness constraint"
389
387
  end
390
388
  end
@@ -45,16 +45,6 @@ module ActiveFacts
45
45
 
46
46
  @is_table
47
47
  end
48
-
49
- # Is this ValueType auto-assigned either at assert or on first save to the database?
50
- def is_auto_assigned
51
- type = self
52
- while type
53
- return true if type.name =~ /^Auto/ || type.transaction_phase
54
- type = type.supertype
55
- end
56
- false
57
- end
58
48
  end
59
49
 
60
50
  class EntityType < DomainObjectType
@@ -150,7 +140,7 @@ module ActiveFacts
150
140
  (rr = c.role_sequence.all_role_ref.single) and
151
141
  rr.role == self
152
142
  end
153
- # REVISIT: check mapping pragmas, e.g. by to_1.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
143
+ # REVISIT: check mapping pragmas, e.g. by to_1.concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
154
144
 
155
145
  if fact_type.entity_type
156
146
  # This is a role in an objectified fact type
@@ -259,10 +249,10 @@ module ActiveFacts
259
249
  pi_ref = nil
260
250
  if pi_roles.size == 1 and
261
251
  object_type.references_to.detect do |ref|
262
- if ref.from_role == first_pi_role and ref.from.is_a?(EntityType) # and ref.is_mandatory # REVISIT
263
- pi_ref = ref
264
- end
265
- end
252
+ if ref.from_role == first_pi_role and ref.from.is_a?(EntityType) # and ref.is_mandatory # REVISIT
253
+ pi_ref = ref
254
+ end
255
+ end
266
256
 
267
257
  trace :absorption, "#{object_type.name} is fully absorbed along its sole reference path into entity type #{pi_ref.from.name}"
268
258
  object_type.definitely_not_table
@@ -275,16 +265,16 @@ module ActiveFacts
275
265
  pi_roles.include?(ref.to_role)
276
266
  }
277
267
  trace :absorption, "#{object_type.name} has #{non_identifying_refs_from.size} non-identifying functional roles" do
278
- non_identifying_refs_from.each do |ref|
279
- trace :absorption, "#{ref.inspect}"
280
- end
281
- end
268
+ non_identifying_refs_from.each do |ref|
269
+ trace :absorption, "#{ref.inspect}"
270
+ end
271
+ end
282
272
 
283
- trace :absorption, "#{object_type.name} has #{object_type.references_to.size} references to it:" do
284
- object_type.references_to.each do |ref|
285
- trace :absorption, ref.inspect
286
- end
287
- end if object_type.references_to.size > 1
273
+ trace :absorption, "#{object_type.name} has #{object_type.references_to.size} references to it:" do
274
+ object_type.references_to.each do |ref|
275
+ trace :absorption, ref.inspect
276
+ end
277
+ end if object_type.references_to.size > 1
288
278
 
289
279
  if object_type.references_to.size > 1 and
290
280
  non_identifying_refs_from.size > 0
@@ -298,7 +288,7 @@ module ActiveFacts
298
288
  non_identifying_refs_from.reject do |ref|
299
289
  !ref.to or ref.to.absorbed_via == ref
300
290
  end +
301
- object_type.references_to
291
+ object_type.references_to
302
292
  ).reject do |ref|
303
293
  next true if !ref.to.is_table or !ref.is_one_to_one
304
294
 
@@ -314,12 +304,12 @@ module ActiveFacts
314
304
  # If this object can be fully absorbed, do that (might require flipping some references)
315
305
  if absorption_paths.size > 0
316
306
  trace :absorption, "#{object_type.name} is fully absorbed through #{absorption_paths.inspect}" do
317
- absorption_paths.each do |ref|
318
- flip = object_type == ref.from
319
- ref.flip if flip
320
- trace :absorption, "#{object_type.name} is FULLY ABSORBED via {ref}#{flip ? ' (flipped)' : ''}"
321
- end
322
- end
307
+ absorption_paths.each do |ref|
308
+ flip = object_type == ref.from
309
+ ref.flip if flip
310
+ trace :absorption, "#{object_type.name} is FULLY ABSORBED via {ref}#{flip ? ' (flipped)' : ''}"
311
+ end
312
+ end
323
313
  object_type.definitely_not_table
324
314
  next object_type
325
315
  end
@@ -1,5 +1,5 @@
1
1
  module Activefacts
2
2
  module RMap
3
- VERSION = "1.8.1"
3
+ VERSION = "1.8.2"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts-rmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clifford Heath
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-04 00:00:00.000000000 Z
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.10'
20
- - - "~>"
21
- - !ruby/object:Gem::Version
22
- version: 1.10.6
23
20
  type: :development
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '1.10'
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: 1.10.6
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rake
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -121,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
115
  version: '0'
122
116
  requirements: []
123
117
  rubyforge_project:
124
- rubygems_version: 2.2.2
118
+ rubygems_version: 2.4.5
125
119
  signing_key:
126
120
  specification_version: 4
127
121
  summary: Relational mapping for ActiveFacts