activefacts-compositions 1.9.6 → 1.9.8

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.
@@ -15,45 +15,106 @@ module ActiveFacts
15
15
  # * underscore
16
16
  class SQL
17
17
  class Server < SQL
18
- def boolean_type
19
- 'BOOLEAN'
20
- end
21
-
22
- def surrogate_type
23
- 'BIGINT IDENTITY NOT NULL'
24
- end
25
-
26
- def reserved_words
27
- @reserved_words ||= %w{
28
- ADD ALL ALTER AND ANY AS ASC AUTHORIZATION BACKUP BEGIN
29
- BETWEEN BREAK BROWSE BULK BY CASCADE CASE CHECK CHECKPOINT
30
- CLOSE CLUSTERED COALESCE COLLATE COLUMN COMMIT COMPUTE
31
- CONSTRAINT CONTAINS CONTAINSTABLE CONTINUE CONVERT CREATE
32
- CROSS CURRENT CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP
33
- CURRENT_USER CURSOR DATABASE DBCC DEALLOCATE DECLARE
34
- DEFAULT DELETE DENY DESC DISK DISTINCT DISTRIBUTED DOUBLE
35
- DROP DUMMY DUMP ELSE END ERRLVL ESCAPE EXCEPT EXEC EXECUTE
36
- EXISTS EXIT FETCH FILE FILLFACTOR FOR FOREIGN FREETEXT
37
- FREETEXTTABLE FROM FULL FUNCTION GOTO GRANT GROUP HAVING
38
- HOLDLOCK IDENTITY IDENTITYCOL IDENTITY_INSERT IF IN INDEX
39
- INNER INSERT INTERSECT INTO IS JOIN KEY KILL LEFT LIKE
40
- LINENO LOAD NATIONAL NOCHECK NONCLUSTERED NOT NULL NULLIF
41
- OF OFF OFFSETS ON OPEN OPENDATASOURCE OPENQUERY OPENROWSET
42
- OPENXML OPTION OR ORDER OUTER OVER PERCENT PLAN PRECISION
43
- PRIMARY PRINT PROC PROCEDURE PUBLIC RAISERROR READ READTEXT
44
- RECONFIGURE REFERENCES REPLICATION RESTORE RESTRICT RETURN
45
- REVOKE RIGHT ROLLBACK ROWCOUNT ROWGUIDCOL RULE SAVE SCHEMA
46
- SELECT SESSION_USER SET SETUSER SHUTDOWN SOME STATISTICS
47
- SYSTEM_USER TABLE TEXTSIZE THEN TO TOP TRAN TRANSACTION
48
- TRIGGER TRUNCATE TSEQUAL UNION UNIQUE UPDATE UPDATETEXT
49
- USE USER VALUES VARYING VIEW WAITFOR WHEN WHERE WHILE
50
- WITH WRITETEXT
51
- }
52
- end
53
-
54
- def go s = ''
55
- "#{s}\nGO\n" # REVISIT: This is an SQL-Serverism. Move it to a subclass.
56
- end
18
+ def self.options
19
+ super.merge({
20
+ # no: [String, "no new options defined here"]
21
+ })
22
+ end
23
+
24
+ def table_name_max
25
+ 128
26
+ end
27
+
28
+ def data_type_context
29
+ SQLServerDataTypeContext.new
30
+ end
31
+
32
+ def auto_assign_type
33
+ ' IDENTITY'
34
+ end
35
+
36
+ def normalise_type(type_name, length, value_constraint)
37
+ return ['UNIQUEIDENTIFIER', 16] if type_name =~ /^(guid|uuid)$/i
38
+
39
+ type = MM::DataType.normalise(type_name)
40
+ case type
41
+ when MM::DataType::TYPE_Money; 'MONEY'
42
+ when MM::DataType::TYPE_DateTime; 'DATETIME'
43
+ when MM::DataType::TYPE_Timestamp;'DATETIME'
44
+ when MM::DataType::TYPE_Binary;
45
+ if length && length <= 8192
46
+ super
47
+ else
48
+ 'IMAGE'
49
+ end
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ def reserved_words
56
+ @reserved_words ||= %w{
57
+ ADD ALL ALTER AND ANY AS ASC AUTHORIZATION BACKUP BEGIN
58
+ BETWEEN BREAK BROWSE BULK BY CASCADE CASE CHECK CHECKPOINT
59
+ CLOSE CLUSTERED COALESCE COLLATE COLUMN COMMIT COMPUTE
60
+ CONSTRAINT CONTAINS CONTAINSTABLE CONTINUE CONVERT CREATE
61
+ CROSS CURRENT CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP
62
+ CURRENT_USER CURSOR DATABASE DBCC DEALLOCATE DECLARE
63
+ DEFAULT DELETE DENY DESC DISK DISTINCT DISTRIBUTED DOUBLE
64
+ DROP DUMMY DUMP ELSE END ERRLVL ESCAPE EXCEPT EXEC EXECUTE
65
+ EXISTS EXIT FETCH FILE FILLFACTOR FOR FOREIGN FREETEXT
66
+ FREETEXTTABLE FROM FULL FUNCTION GOTO GRANT GROUP HAVING
67
+ HOLDLOCK IDENTITY IDENTITYCOL IDENTITY_INSERT IF IN INDEX
68
+ INNER INSERT INTERSECT INTO IS JOIN KEY KILL LEFT LIKE
69
+ LINENO LOAD NATIONAL NOCHECK NONCLUSTERED NOT NULL NULLIF
70
+ OF OFF OFFSETS ON OPEN OPENDATASOURCE OPENQUERY OPENROWSET
71
+ OPENXML OPTION OR ORDER OUTER OVER PERCENT PLAN PRECISION
72
+ PRIMARY PRINT PROC PROCEDURE PUBLIC RAISERROR READ READTEXT
73
+ RECONFIGURE REFERENCES REPLICATION RESTORE RESTRICT RETURN
74
+ REVOKE RIGHT ROLLBACK ROWCOUNT ROWGUIDCOL RULE SAVE SCHEMA
75
+ SELECT SESSION_USER SET SETUSER SHUTDOWN SOME STATISTICS
76
+ SYSTEM_USER TABLE TEXTSIZE THEN TO TOP TRAN TRANSACTION
77
+ TRIGGER TRUNCATE TSEQUAL UNION UNIQUE UPDATE UPDATETEXT
78
+ USE USER VALUES VARYING VIEW WAITFOR WHEN WHERE WHILE
79
+ WITH WRITETEXT
80
+ }
81
+ end
82
+
83
+ def go s = ''
84
+ "#{s}\nGO\n"
85
+ end
86
+
87
+ class SQLServerDataTypeContext < SQLDataTypeContext
88
+ def integer_ranges
89
+ [
90
+ ['BIT', 0, 1],
91
+ ['TINYINT', -2**7, 2**7-1],
92
+ ] +
93
+ super
94
+ end
95
+
96
+ def boolean_type
97
+ 'BIT'
98
+ end
99
+
100
+ def valid_from_type
101
+ 'DATETIME'
102
+ end
103
+
104
+ def default_char_type
105
+ (@unicode ? 'N' : '') +
106
+ 'CHAR'
107
+ end
108
+
109
+ def default_varchar_type
110
+ (@unicode ? 'N' : '') +
111
+ 'VARCHAR'
112
+ end
113
+
114
+ def date_time_type
115
+ 'DATETIME'
116
+ end
117
+ end
57
118
  end
58
119
 
59
120
  end
@@ -12,85 +12,93 @@ module ActiveFacts
12
12
  module Metamodel
13
13
  class Composition
14
14
  def summary
15
- classify_constraints
16
- "Summary of #{name}\n" +
17
- all_composite.
18
- sort_by{|composite| composite.mapping.name}.
19
- flat_map do |composite|
20
- composite.summary
21
- end*''
15
+ classify_constraints
16
+ "Summary of #{name}\n" +
17
+ all_composite.
18
+ sort_by{|composite| composite.mapping.name}.
19
+ flat_map do |composite|
20
+ composite.summary
21
+ end*''
22
22
  end
23
23
  end
24
24
 
25
25
  class Composite
26
26
  def summary
27
- indices = self.all_indices_by_rank
27
+ indices = self.all_indices_by_rank
28
28
 
29
- (
30
- [mapping.name+"\n"] +
31
- mapping.
32
- leaves.
33
- reject{|leaf| leaf.is_a?(Absorption) && leaf.forward_absorption}.
34
- flat_map do |leaf|
29
+ (
30
+ [mapping.name+"\n"] +
31
+ mapping.
32
+ all_leaf.
33
+ reject{|leaf| leaf.is_a?(Absorption) && leaf.forward_absorption}.
34
+ flat_map do |leaf|
35
35
 
36
- # Build a display of the names in this absorption path, with FK and optional indicators
37
- path_names = leaf.path.map do |component|
38
- is_mandatory = case component
39
- when Indicator
40
- false
41
- when Absorption
42
- component.parent_role.is_mandatory
43
- else
44
- true
45
- end
36
+ # Build a display of the names in this absorption path, with FK and optional indicators
37
+ path_names = leaf.path.map do |component|
38
+ is_mandatory = case component
39
+ when Indicator
40
+ false
41
+ when Absorption
42
+ component.parent_role.is_mandatory
43
+ else
44
+ true
45
+ end
46
46
 
47
- if component.is_a?(Absorption) && component.foreign_key
48
- "[#{component.name}]"
49
- else
50
- component.name
51
- end +
52
- (is_mandatory ? '' : '?')
53
- end*'->'
47
+ if component.all_foreign_key_field.size > 0
48
+ "[#{component.name}]"
49
+ elsif component.is_a?(Absorption) && component.foreign_key
50
+ "{#{component.name}}"
51
+ else
52
+ component.name
53
+ end +
54
+ (is_mandatory ? '' : '?')
55
+ end*'->'
54
56
 
55
- # Build a symbolic representation of the index participation of this leaf
56
- pos = 0
57
- indexing = indices.inject([]) do |a, index|
58
- pos += 1
59
- if part = index.position_in_index(leaf)
60
- a << "#{pos}.#{part}"
61
- end
62
- a
63
- end
64
- if indexing.empty?
65
- indexing = ''
66
- else
67
- indexing = "[#{indexing*','}]"
68
- end
57
+ # Build a symbolic representation of the index participation of this leaf
58
+ pos = 0
59
+ indexing = indices.inject([]) do |a, index|
60
+ pos += 1
61
+ if part = index.position_in_index(leaf)
62
+ a << "#{pos}.#{part}"
63
+ end
64
+ a
65
+ end
66
+ if indexing.empty?
67
+ indexing = ''
68
+ else
69
+ indexing = "[#{indexing*','}]"
70
+ end
69
71
 
70
- column_name = leaf.column_name.capwords*' '
71
- ["\t#{path_names}#{indexing} as #{column_name.inspect}\n"] +
72
- leaf.all_leaf_constraint.map{|leaf_constraint| "\t\t### #{leaf_constraint.leaf_constraint.describe}\n"}
73
- end +
74
- all_local_constraint.map do |local_constraint|
75
- "\t### #{local_constraint.local_constraint.describe}\n"
76
- end.sort +
77
- all_spanning_constraint.map do |spanning_constraint|
78
- "### #{spanning_constraint.spanning_constraint.describe}\n"
79
- end.sort
72
+ column_name = leaf.column_name.capwords*' '
73
+ ["\t#{path_names}#{indexing} as #{column_name.inspect}\n"] +
74
+ leaf.all_leaf_constraint.map{|leaf_constraint| "\t\t### #{leaf_constraint.leaf_constraint.describe}\n"}.sort
75
+ end +
76
+ all_local_constraint.map do |local_constraint|
77
+ "\t### #{local_constraint.local_constraint.describe}\n"
78
+ end.sort +
79
+ all_spanning_constraint.map do |spanning_constraint|
80
+ "### #{spanning_constraint.spanning_constraint.describe}\n"
81
+ end.sort
80
82
 
81
- )*''
83
+ )*''
82
84
  end
83
85
  end
84
86
  end
85
87
 
86
88
  module Generators
87
89
  class Summary
88
- def initialize composition
89
- @composition = composition
90
+ def self.options
91
+ {
92
+ }
93
+ end
94
+
95
+ def initialize composition, options = {}
96
+ @composition = composition
97
+ @options = options
90
98
  end
91
99
 
92
100
  def generate
93
- @composition.summary
101
+ @composition.summary
94
102
  end
95
103
  end
96
104
  publish_generator Summary
@@ -7,152 +7,37 @@
7
7
  # Copyright (c) 2015 Clifford Heath. Read the LICENSE file.
8
8
  #
9
9
  require "activefacts/metamodel"
10
+ require "activefacts/metamodel/validate/composition"
10
11
  require "activefacts/compositions/compositor"
11
12
  require "activefacts/generator"
12
13
 
13
14
  module ActiveFacts
14
15
  module Generators
15
16
  class Validate
16
- def initialize composition, options = {}
17
- @composition = composition
18
- @options = options
19
- end
20
-
21
- def generate &b
22
- @composition.validate &b
23
- nil
24
- end
25
- end
26
- publish_generator Validate
27
- end
28
-
29
- module Metamodel
30
- class Composition
31
- def validate &report
32
- trace.enable 'composition_validator'
33
- report ||= proc do |component, problem|
34
- trace :composition_validator, "!!PROBLEM!! #{component.inspect}: #{problem}"
35
- end
36
-
37
- all_composite.each do |composite|
38
- composite.validate &report
39
- end
40
- end
41
- end
42
-
43
- class Composite
44
- def validate &report
45
- trace :composition_validator?, "Validating #{inspect}" do
46
- report.call(self, "Has no Mapping") unless mapping
47
- report.call(self, "Mapping is not a mapping") unless mapping.class == Mapping
48
- report.call(mapping, "Has no ObjectType") unless mapping.object_type
49
- report.call(mapping, "Has no Name") unless mapping.name
50
- report.call(mapping, "Should not have an Ordinal rank") if mapping.ordinal
51
- report.call(mapping, "Should not have a parent mapping") if mapping.parent
52
- report.call(mapping, "Should be the root of its mapping") if mapping.root != self
53
-
54
- mapping.validate_members &report
55
- validate_access_paths &report
56
- end
57
- end
58
-
59
- def validate_access_paths &report
60
- all_access_path.each do |access_path|
61
- report.call(access_path, "Must contain at least one IndexField") unless access_path.all_index_field.size > 0
62
- access_path.all_index_field.each do |index_field|
63
- report.call(access_path, "#{index_field.inspect} must be an Indicator or played by a ValueType") unless index_field.component.is_a?(Indicator) || index_field.component.object_type.is_a?(ValueType)
64
- report.call(access_path, "#{index_field.inspect} must be within its composite") unless index_field.component.root == self
65
- end
66
- if ForeignKey === access_path
67
- if access_path.all_index_field.size == access_path.all_foreign_key_field.size
68
- access_path.all_index_field.to_a.zip(access_path.all_foreign_key_field.to_a).each do |index_field, foreign_key_field|
69
- report.call(access_path, "#{index_field.inspect} must have matching target type") unless index_field.component.class == foreign_key_field.component.class
70
- unless index_field.component.class == foreign_key_field.component.class
71
- report.call(access_path, "#{index_field.inspect} must have component type matching #{foreign_key_field.inspect}")
72
- else
73
- report.call(access_path, "#{index_field.inspect} must have matching target type") unless !index_field.component.is_a?(Absorption) or index_field.component.object_type == foreign_key_field.component.object_type
74
- end
75
- report.call(access_path, "#{foreign_key_field.inspect} must be within the target composite") unless foreign_key_field.component.root == access_path.source_composite
76
- end
77
- else
78
- report.call(access_path, "has #{access_path.all_index_field.size} index fields but #{access_path.all_foreign_key_field.size} ForeignKeyField")
79
- end
80
- end
81
- end
17
+ def self.options
18
+ {
19
+ }
82
20
  end
83
- end
84
-
85
- class Mapping
86
- def validate_members &report
87
- # Names (except of subtype/supertype absorption) must be unique:
88
- names = all_member.
89
- reject{|m| m.is_a?(Absorption) && m.parent_role.fact_type.is_a?(TypeInheritance)}.
90
- map(&:name).
91
- compact
92
- duplicate_names = names.select{|name| names.count(name) > 1}.uniq
93
- report.call(self, "Contains duplicated names #{duplicate_names.map(&:inspect)*', '}") unless duplicate_names.empty?
94
-
95
- all_member.each do |member|
96
- trace :composition_validator?, "Validating #{member.inspect}" do
97
- report.call(member, "Requires a name") unless Absorption === member && member.flattens or member.name && !member.name.empty?
98
- case member
99
- when Absorption
100
- p = member.parent_role
101
- c = member.child_role
102
- report.call(member, "Roles should belong to the same fact type, but instead we have #{p.name} in #{p.fact_type.default_reading} and #{c.name} in #{c.fact_type.default_reading}") unless p.fact_type == c.fact_type
103
- report.call(member, "Object type #{member.object_type.name} should play the child role #{c.name}") unless member.object_type == c.object_type
104
- report.call(member, "Parent mapping object type #{object_type.name} should play the parent role #{p.name}") unless object_type == p.object_type
105
-
106
- member.validate_reverse &report
107
- member.validate_nesting &report if member.all_nesting.size > 0
108
- member.validate_members &report
109
-
110
- when Scoping
111
- report.call(member, "REVISIT: Unexpected and unchecked Scoping")
112
-
113
- when ValueField
114
- # Nothing to check here
115
-
116
- when SurrogateKey
117
- # Nothing to check here
118
-
119
- when Injection
120
- report.call(member, "REVISIT: Unexpected and unchecked Injection")
121
21
 
122
- when Mapping
123
- report.call(member, "A child Component should not be a bare Mapping")
124
-
125
- when Indicator
126
- report.call(member, "Indicator requires a Role") unless member.role
127
-
128
- when Discriminator
129
- report.call(member, "Discriminator requires at least one Discriminated Role") if member.all_discriminated_role.empty?
130
- member.all_discriminated_role.each do |role|
131
- report.call(member, "Discriminated Role #{role.name} is not played by parent object type #{object_type.name}") unless role.object_type == object_type
132
- end
133
- # REVISIT: Discriminated Roles must have distinct values matching the type of the Role
134
- end
135
- end
136
- end
22
+ def initialize composition, options = {}
23
+ @composition = composition
24
+ @options = options
137
25
  end
138
- end
139
26
 
140
- class Absorption
141
- def validate_reverse &report
142
- reverse = forward_absorption || reverse_absorption
143
- return unless reverse
144
- report.call(self, "Opposite absorption's child role #{reverse.child_role.name} should match parent role #{parent_role.name}") unless reverse.child_role == parent_role
145
- report.call(self, "Opposite absorption's parent role #{reverse.parent_role.name} should match child role #{child_role.name}") unless reverse.parent_role == child_role
146
- end
27
+ def generate &report
28
+ if !report
29
+ trace.enable 'composition_validator'
30
+ report ||= proc do |component, problem|
31
+ trace :composition_validator, "!!PROBLEM!! #{component.inspect}: #{problem}"
32
+ debugger if trace :composition_validator_debug
33
+ component
34
+ end
35
+ end
147
36
 
148
- def validate_nesting &report
149
- report.call(self, "REVISIT: Unexpected and unchecked Nesting")
150
- report.call(self, "Nesting Mode must be specified") unless self.nesting_mode
151
- # REVISIT: Nesting names must be unique
152
- # REVISIT: Nesting roles must be played by...
153
- # REVISIT: Nesting roles must be value types
37
+ @composition.validate &report
38
+ nil
154
39
  end
155
40
  end
156
-
41
+ publish_generator Validate
157
42
  end
158
43
  end