activefacts-compositions 1.9.6 → 1.9.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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