activefacts-compositions 1.9.1 → 1.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,138 @@
1
+ #
2
+ # ActiveFacts Compositions validator.
3
+ #
4
+ # Quite a few constraints are not enforced during the construction of a composition.
5
+ # This method does a post-validation to ensure that everything looks ok.
6
+ #
7
+ # Copyright (c) 2015 Clifford Heath. Read the LICENSE file.
8
+ #
9
+ require "activefacts/compositions/compositor"
10
+
11
+ module ActiveFacts
12
+ module Compositions
13
+ class Compositor
14
+ def validate &report
15
+ report ||= proc do |component, problem|
16
+ raise "Problem with #{component.inspect}: #{problem}"
17
+ end
18
+
19
+ @composition.all_composite.each do |composite|
20
+ validate_composite composite, &report
21
+ end
22
+ end
23
+
24
+ private
25
+ MM = ActiveFacts::Metamodel
26
+
27
+ def validate_composite composite, &report
28
+ trace :composition_validator?, "Validating #{composite.inspect}" do
29
+ report.call(composite, "Has no Mapping") unless composite.mapping
30
+ report.call(composite, "Mapping is not a mapping") unless composite.mapping.class == MM::Mapping
31
+ report.call(composite.mapping, "Has no ObjectType") unless composite.mapping.object_type
32
+ report.call(composite.mapping, "Has no Name") unless composite.mapping.name
33
+ report.call(composite.mapping, "Should not have an Ordinal rank") if composite.mapping.ordinal
34
+ report.call(composite.mapping, "Should not have a parent mapping") if composite.mapping.parent
35
+ report.call(composite.mapping, "Should be the root of its mapping") if composite.mapping.root != composite
36
+
37
+ validate_members composite.mapping, &report
38
+ validate_access_paths composite, &report
39
+ end
40
+ end
41
+
42
+ def validate_members mapping, &report
43
+ # Names (except of subtype/supertype absorption) must be unique:
44
+ names = mapping.
45
+ all_member.
46
+ reject{|m| m.is_a?(MM::Absorption) && m.parent_role.fact_type.is_a?(MM::TypeInheritance)}.
47
+ map(&:name).
48
+ compact
49
+ duplicate_names = names.select{|name| names.count(name) > 1}.uniq
50
+ report.call(mapping, "Contains duplicated names #{duplicate_names.map(&:inspect)*', '}") unless duplicate_names.empty?
51
+
52
+ mapping.all_member.each do |member|
53
+ trace :composition_validator?, "Validating #{member.inspect}" do
54
+ report.call(member, "Requires a name") unless MM::Absorption === member && member.flattens or member.name && !member.name.empty?
55
+ case member
56
+ when MM::Absorption
57
+ p = member.parent_role
58
+ c = member.child_role
59
+ 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
60
+ report.call(member, "Object type #{member.object_type.name} should play the child role #{c.name}") unless member.object_type == c.object_type
61
+ report.call(member, "Parent mapping object type #{mapping.object_type.name} should play the parent role #{p.name}") unless mapping.object_type == p.object_type
62
+
63
+ validate_reverse member, &report
64
+ validate_nesting member, &report if member.all_nesting.size > 0
65
+
66
+ validate_members member, &report
67
+
68
+ when MM::Scoping
69
+ report.call(member, "REVISIT: Unexpected and unchecked Scoping")
70
+
71
+ when MM::ValueField
72
+ # Nothing to check here
73
+
74
+ when MM::SurrogateKey
75
+ # Nothing to check here
76
+
77
+ when MM::Injection
78
+ report.call(member, "REVISIT: Unexpected and unchecked Injection")
79
+
80
+ when MM::Mapping
81
+ report.call(member, "A child Component should not be a bare Mapping")
82
+
83
+ when MM::Indicator
84
+ report.call(member, "Indicator requires a Role") unless member.role
85
+
86
+ when MM::Discriminator
87
+ report.call(member, "Discriminator requires at least one Discriminated Role") if member.all_discriminated_role.empty?
88
+ member.all_discriminated_role.each do |role|
89
+ report.call(member, "Discriminated Role #{role.name} is not played by parent object type #{mapping.object_type.name}") unless role.object_type == mapping.object_type
90
+ end
91
+ # REVISIT: Discriminated Roles must have distinct values matching the type of the Role
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ def validate_reverse absorption, &report
98
+ reverse = absorption.forward_absorption || absorption.reverse_absorption
99
+ return unless reverse
100
+ report.call(absorption, "Opposite absorption's child role #{reverse.child_role.name} should match parent role #{absorption.parent_role.name}") unless reverse.child_role == absorption.parent_role
101
+ report.call(absorption, "Opposite absorption's parent role #{reverse.parent_role.name} should match child role #{absorption.child_role.name}") unless reverse.parent_role == absorption.child_role
102
+ end
103
+
104
+ def validate_nesting absorption, &report
105
+ report.call(absorption, "REVISIT: Unexpected and unchecked Nesting")
106
+ report.call(absorption, "Nesting Mode must be specified") unless absorption.nesting_mode
107
+ # REVISIT: Nesting names must be unique
108
+ # REVISIT: Nesting roles must be played by...
109
+ # REVISIT: Nesting roles must be value types
110
+ end
111
+
112
+ def validate_access_paths composite, &report
113
+ composite.all_access_path.each do |access_path|
114
+ report.call(access_path, "Must contain at least one IndexField") unless access_path.all_index_field.size > 0
115
+ access_path.all_index_field.each do |index_field|
116
+ report.call(access_path, "#{index_field.inspect} must be an Indicator or played by a ValueType") unless index_field.component.is_a?(MM::Indicator) || index_field.component.object_type.is_a?(MM::ValueType)
117
+ report.call(access_path, "#{index_field.inspect} must be within its composite") unless index_field.component.root == composite
118
+ end
119
+ if MM::ForeignKey === access_path
120
+ if access_path.all_index_field.size == access_path.all_foreign_key_field.size
121
+ access_path.all_index_field.to_a.zip(access_path.all_foreign_key_field.to_a).each do |index_field, foreign_key_field|
122
+ report.call(access_path, "#{index_field.inspect} must have matching target type") unless index_field.component.class == foreign_key_field.component.class
123
+ unless index_field.component.class == foreign_key_field.component.class
124
+ report.call(access_path, "#{index_field.inspect} must have component type matching #{foreign_key_field.inspect}")
125
+ else
126
+ report.call(access_path, "#{index_field.inspect} must have matching target type") unless !index_field.component.is_a?(MM::Absorption) or index_field.component.object_type == foreign_key_field.component.object_type
127
+ end
128
+ report.call(access_path, "#{foreign_key_field.inspect} must be within the target composite") unless foreign_key_field.component.root == access_path.source_composite
129
+ end
130
+ else
131
+ report.call(access_path, "has #{access_path.all_index_field.size} index fields but #{access_path.all_foreign_key_field.size} ForeignKeyField")
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveFacts
2
2
  module Compositions
3
- VERSION = "1.9.1"
3
+ VERSION = "1.9.4"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts-compositions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clifford Heath
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-12-04 00:00:00.000000000 Z
11
+ date: 2016-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -59,65 +59,105 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: '3.3'
61
61
  - !ruby/object:Gem::Dependency
62
- name: activefacts-metamodel
62
+ name: activefacts-api
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1'
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 1.9.2
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1'
65
78
  - - ">="
66
79
  - !ruby/object:Gem::Version
67
- version: '1.9'
80
+ version: 1.9.2
81
+ - !ruby/object:Gem::Dependency
82
+ name: activefacts-metamodel
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
68
85
  - - "~>"
69
86
  - !ruby/object:Gem::Version
70
- version: 1.9.0
87
+ version: '1'
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.9.1
71
91
  type: :runtime
72
92
  prerelease: false
73
93
  version_requirements: !ruby/object:Gem::Requirement
74
94
  requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1'
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 1.9.1
101
+ - !ruby/object:Gem::Dependency
102
+ name: tracing
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '2'
75
108
  - - ">="
76
109
  - !ruby/object:Gem::Version
77
- version: '1.9'
110
+ version: 2.0.5
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
78
115
  - - "~>"
79
116
  - !ruby/object:Gem::Version
80
- version: 1.9.0
117
+ version: '2'
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 2.0.5
81
121
  - !ruby/object:Gem::Dependency
82
122
  name: activefacts
83
123
  requirement: !ruby/object:Gem::Requirement
84
124
  requirements:
85
125
  - - "~>"
86
126
  - !ruby/object:Gem::Version
87
- version: '1.8'
88
- - - "~>"
127
+ version: '1'
128
+ - - ">="
89
129
  - !ruby/object:Gem::Version
90
- version: 1.8.0
130
+ version: '1.8'
91
131
  type: :development
92
132
  prerelease: false
93
133
  version_requirements: !ruby/object:Gem::Requirement
94
134
  requirements:
95
135
  - - "~>"
96
136
  - !ruby/object:Gem::Version
97
- version: '1.8'
98
- - - "~>"
137
+ version: '1'
138
+ - - ">="
99
139
  - !ruby/object:Gem::Version
100
- version: 1.8.0
140
+ version: '1.8'
101
141
  - !ruby/object:Gem::Dependency
102
142
  name: activefacts-cql
103
143
  requirement: !ruby/object:Gem::Requirement
104
144
  requirements:
105
145
  - - "~>"
106
146
  - !ruby/object:Gem::Version
107
- version: '1.8'
108
- - - "~>"
147
+ version: '1'
148
+ - - ">="
109
149
  - !ruby/object:Gem::Version
110
- version: 1.8.0
150
+ version: '1.8'
111
151
  type: :development
112
152
  prerelease: false
113
153
  version_requirements: !ruby/object:Gem::Requirement
114
154
  requirements:
115
155
  - - "~>"
116
156
  - !ruby/object:Gem::Version
117
- version: '1.8'
118
- - - "~>"
157
+ version: '1'
158
+ - - ">="
119
159
  - !ruby/object:Gem::Version
120
- version: 1.8.0
160
+ version: '1.8'
121
161
  description: Create and represent composite schemas, schema transforms and data transforms
122
162
  over a fact-based model
123
163
  email:
@@ -139,6 +179,7 @@ files:
139
179
  - lib/activefacts/compositions/binary.rb
140
180
  - lib/activefacts/compositions/compositor.rb
141
181
  - lib/activefacts/compositions/relational.rb
182
+ - lib/activefacts/compositions/validator.rb
142
183
  - lib/activefacts/compositions/version.rb
143
184
  homepage: https://github.com/cjheath/activefacts-compositions
144
185
  licenses: