activefacts-compositions 1.9.1 → 1.9.4

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