dcc 0.2.0 → 0.2.1

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.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +19 -0
  3. data/.github/workflows/release.yml +28 -0
  4. data/.gitignore +4 -0
  5. data/.rubocop_todo.yml +560 -10
  6. data/CHANGELOG.adoc +34 -1
  7. data/CONTRIBUTING.adoc +8 -1
  8. data/Gemfile +5 -7
  9. data/README.adoc +162 -7
  10. data/dcc.gemspec +8 -2
  11. data/docs/schema-versions.adoc +20 -7
  12. data/examples/build_minimal_dcc.rb +0 -0
  13. data/examples/convert_all_formats.rb +2 -1
  14. data/examples/parse_and_inspect.rb +0 -0
  15. data/examples/quantity_math_demo.rb +7 -7
  16. data/examples/validate_full.rb +1 -1
  17. data/lib/dcc/base/calibration_laboratory.rb +2 -1
  18. data/lib/dcc/base/condition.rb +5 -2
  19. data/lib/dcc/base/core_data.rb +8 -3
  20. data/lib/dcc/base/formula.rb +3 -3
  21. data/lib/dcc/base/identification.rb +6 -3
  22. data/lib/dcc/base/mathml.rb +33 -0
  23. data/lib/dcc/base/quantity.rb +2 -1
  24. data/lib/dcc/base/resp_person.rb +4 -2
  25. data/lib/dcc/base/text.rb +1 -1
  26. data/lib/dcc/base.rb +2 -1
  27. data/lib/dcc/builder/dsl.rb +1 -1
  28. data/lib/dcc/builder/session.rb +16 -14
  29. data/lib/dcc/builder.rb +3 -3
  30. data/lib/dcc/cli/cli.rb +36 -18
  31. data/lib/dcc/cli/formatters.rb +35 -7
  32. data/lib/dcc/cli.rb +1 -1
  33. data/lib/dcc/context_configuration.rb +2 -1
  34. data/lib/dcc/context_options.rb +4 -2
  35. data/lib/dcc/convert/csv.rb +27 -9
  36. data/lib/dcc/convert/html.rb +26 -110
  37. data/lib/dcc/convert/json.rb +10 -7
  38. data/lib/dcc/convert/liquid_filters.rb +58 -0
  39. data/lib/dcc/convert/result.rb +1 -1
  40. data/lib/dcc/convert/templates/report.liquid +250 -0
  41. data/lib/dcc/convert/yaml.rb +1 -1
  42. data/lib/dcc/convert.rb +1 -0
  43. data/lib/dcc/diff/change.rb +1 -1
  44. data/lib/dcc/diff/result.rb +8 -4
  45. data/lib/dcc/diff.rb +43 -11
  46. data/lib/dcc/error.rb +7 -0
  47. data/lib/dcc/extract/file.rb +3 -4
  48. data/lib/dcc/extract/ring.rb +1 -1
  49. data/lib/dcc/extract.rb +1 -1
  50. data/lib/dcc/i18n/text_lookup.rb +8 -4
  51. data/lib/dcc/i18n.rb +1 -1
  52. data/lib/dcc/inspect/report.rb +51 -14
  53. data/lib/dcc/inspect.rb +1 -1
  54. data/lib/dcc/migrate/route.rb +44 -0
  55. data/lib/dcc/migrate/v2_to_v3.rb +445 -0
  56. data/lib/dcc/migrate.rb +130 -26
  57. data/lib/dcc/plugin/base.rb +64 -0
  58. data/lib/dcc/plugin/registry.rb +7 -2
  59. data/lib/dcc/plugin.rb +6 -2
  60. data/lib/dcc/quantity_format/formatter.rb +16 -10
  61. data/lib/dcc/quantity_format.rb +1 -1
  62. data/lib/dcc/quantity_math/quantity.rb +1 -1
  63. data/lib/dcc/quantity_math/real.rb +6 -6
  64. data/lib/dcc/quantity_math.rb +1 -1
  65. data/lib/dcc/schema/version.rb +2 -2
  66. data/lib/dcc/server/app.html +282 -0
  67. data/lib/dcc/server/app.rb +171 -87
  68. data/lib/dcc/server/demo_cert.xml +244 -0
  69. data/lib/dcc/server/storage/entry.rb +1 -1
  70. data/lib/dcc/server/storage/memory.rb +3 -2
  71. data/lib/dcc/server/storage.rb +1 -1
  72. data/lib/dcc/server.rb +1 -1
  73. data/lib/dcc/si/base/complex.rb +1 -1
  74. data/lib/dcc/si/base/complex_list_xml_list.rb +1 -1
  75. data/lib/dcc/si/base/constant.rb +1 -1
  76. data/lib/dcc/si/base/coverage_interval.rb +36 -0
  77. data/lib/dcc/si/base/coverage_interval_mu.rb +1 -1
  78. data/lib/dcc/si/base/expanded_mu.rb +1 -1
  79. data/lib/dcc/si/base/expanded_unc.rb +34 -0
  80. data/lib/dcc/si/base/expanded_unc_xml_list.rb +3 -2
  81. data/lib/dcc/si/base/hybrid.rb +3 -2
  82. data/lib/dcc/si/base/measurement_uncertainty_univariate.rb +1 -1
  83. data/lib/dcc/si/base/real.rb +13 -3
  84. data/lib/dcc/si/base/real_list_xml_list.rb +5 -3
  85. data/lib/dcc/si/base/si_list.rb +3 -2
  86. data/lib/dcc/si/base/standard_mu.rb +1 -1
  87. data/lib/dcc/si/base.rb +5 -2
  88. data/lib/dcc/si/v1/coverage_interval.rb +8 -0
  89. data/lib/dcc/si/v1/expanded_unc.rb +1 -16
  90. data/lib/dcc/si/v1/measurement_uncertainty_univariate.rb +2 -1
  91. data/lib/dcc/si/v1.rb +5 -2
  92. data/lib/dcc/si/v2/coverage_interval.rb +8 -0
  93. data/lib/dcc/si/v2/coverage_interval_mu_xml_list.rb +4 -2
  94. data/lib/dcc/si/v2/expanded_mu_xml_list.rb +2 -1
  95. data/lib/dcc/si/v2/expanded_unc.rb +8 -0
  96. data/lib/dcc/si/v2/measurement_uncertainty_univariate.rb +2 -1
  97. data/lib/dcc/si/v2.rb +8 -3
  98. data/lib/dcc/signature/result.rb +2 -2
  99. data/lib/dcc/signature/signer.rb +31 -18
  100. data/lib/dcc/signature/verifier.rb +23 -21
  101. data/lib/dcc/signature.rb +1 -1
  102. data/lib/dcc/transform/result.rb +1 -1
  103. data/lib/dcc/transform/xslt.rb +22 -12
  104. data/lib/dcc/transform.rb +5 -4
  105. data/lib/dcc/type/base64_binary.rb +5 -3
  106. data/lib/dcc/type/decimal_xml_list.rb +99 -13
  107. data/lib/dcc/type/iso_country_code.rb +3 -2
  108. data/lib/dcc/type/iso_language_code.rb +3 -2
  109. data/lib/dcc/type/schema_version.rb +3 -2
  110. data/lib/dcc/type/si_unit.rb +3 -2
  111. data/lib/dcc/type_guards.rb +3 -3
  112. data/lib/dcc/v2/calibration_laboratory.rb +2 -1
  113. data/lib/dcc/v2/condition.rb +22 -1
  114. data/lib/dcc/v2/configuration.rb +1 -1
  115. data/lib/dcc/v2/digital_calibration_certificate.rb +2 -1
  116. data/lib/dcc/v2/identification.rb +14 -1
  117. data/lib/dcc/v2/mathml.rb +8 -0
  118. data/lib/dcc/v2/measurement_meta_data_list.rb +2 -1
  119. data/lib/dcc/v2/quantity.rb +4 -2
  120. data/lib/dcc/v2.rb +4 -2
  121. data/lib/dcc/v3/calibration_laboratory.rb +2 -1
  122. data/lib/dcc/v3/configuration.rb +1 -1
  123. data/lib/dcc/v3/digital_calibration_certificate.rb +2 -1
  124. data/lib/dcc/v3/mathml.rb +8 -0
  125. data/lib/dcc/v3/measurement_meta_data_list.rb +2 -1
  126. data/lib/dcc/v3/quantity.rb +4 -2
  127. data/lib/dcc/v3.rb +12 -2
  128. data/lib/dcc/validate/business_rules.rb +23 -9
  129. data/lib/dcc/validate/issue.rb +2 -1
  130. data/lib/dcc/validate/result.rb +6 -4
  131. data/lib/dcc/validate/schematron/profile.rb +73 -18
  132. data/lib/dcc/validate/schematron/rule.rb +13 -3
  133. data/lib/dcc/validate/schematron/rules/administrative_data_completeness.rb +11 -4
  134. data/lib/dcc/validate/schematron/rules/base.rb +1 -2
  135. data/lib/dcc/validate/schematron/rules/date_range_check.rb +11 -5
  136. data/lib/dcc/validate/schematron/rules/id_refid_linking.rb +0 -3
  137. data/lib/dcc/validate/schematron/rules/influence_conditions_placement.rb +22 -12
  138. data/lib/dcc/validate/schematron/rules/iso_code_validation.rb +31 -14
  139. data/lib/dcc/validate/schematron/rules/language_code_dedup.rb +12 -5
  140. data/lib/dcc/validate/schematron/rules/non_si_declaration.rb +7 -4
  141. data/lib/dcc/validate/schematron/rules/release_format_check.rb +7 -4
  142. data/lib/dcc/validate/schematron/rules/schema_version_check.rb +13 -9
  143. data/lib/dcc/validate/schematron/rules/uncertainty_consistency.rb +39 -10
  144. data/lib/dcc/validate/schematron/rules/unit_format_check.rb +2 -3
  145. data/lib/dcc/validate/schematron/rules/used_methods_placement.rb +15 -10
  146. data/lib/dcc/validate/schematron/rules/used_software_placement.rb +15 -10
  147. data/lib/dcc/validate/schematron/rules/xml_list_spacing.rb +4 -3
  148. data/lib/dcc/validate/schematron/rules.rb +28 -14
  149. data/lib/dcc/validate/schematron.rb +1 -1
  150. data/lib/dcc/validate/xsd.rb +9 -3
  151. data/lib/dcc/validate.rb +1 -1
  152. data/lib/dcc/version.rb +1 -1
  153. data/lib/dcc/versioned_parser.rb +1 -1
  154. data/lib/dcc.rb +55 -7
  155. data/sample_dcc.xml +140 -0
  156. data/web/index.html +12 -0
  157. data/web/package-lock.json +1599 -0
  158. data/web/package.json +22 -0
  159. data/web/src/App.vue +349 -0
  160. data/web/src/lib/builder.ts +171 -0
  161. data/web/src/lib/dcc.ts +338 -0
  162. data/web/src/lib/units.json +5581 -0
  163. data/web/src/main.ts +5 -0
  164. data/web/src/style.css +27 -0
  165. data/web/tsconfig.json +15 -0
  166. data/web/vite.config.ts +14 -0
  167. metadata +125 -9
@@ -15,37 +15,92 @@ module Dcc
15
15
  # result.ok? # => true
16
16
  # result.issues.size # => 0
17
17
  class Profile
18
+ # The rules shipped with the gem, in execution order.
19
+ DEFAULT_RULES = [
20
+ Rules::UsedMethodsPlacement,
21
+ Rules::UsedSoftwarePlacement,
22
+ Rules::InfluenceConditionsPlacement,
23
+ Rules::SchemaVersionCheck,
24
+ Rules::IdRefIdLinking,
25
+ Rules::IsoCodeValidation,
26
+ Rules::DateRangeCheck,
27
+ Rules::ReleaseFormatCheck,
28
+ Rules::UncertaintyConsistency,
29
+ Rules::UnitFormatCheck,
30
+ Rules::NonSiDeclaration,
31
+ Rules::LanguageCodeDedup,
32
+ Rules::XmlListSpacing,
33
+ ].freeze
34
+ private_constant :DEFAULT_RULES
35
+
18
36
  attr_reader :dcc, :rules
19
37
 
38
+ # Plugin validators are read here, not at class-definition time, so
39
+ # a plugin loaded after this class still takes effect. Frozen because
40
+ # `attr_reader :rules` hands the array straight to callers.
20
41
  def initialize(dcc)
21
42
  @dcc = dcc
22
- @rules = [
23
- Rules::UsedMethodsPlacement,
24
- Rules::UsedSoftwarePlacement,
25
- Rules::InfluenceConditionsPlacement,
26
- Rules::SchemaVersionCheck,
27
- Rules::IdRefIdLinking,
28
- Rules::IsoCodeValidation,
29
- Rules::DateRangeCheck,
30
- Rules::ReleaseFormatCheck,
31
- Rules::UncertaintyConsistency,
32
- Rules::UnitFormatCheck,
33
- Rules::NonSiDeclaration,
34
- Rules::LanguageCodeDedup,
35
- Rules::XmlListSpacing,
36
- ]
43
+ @plugin_rules = ::Dcc::Plugin.all(:validators).freeze
44
+ @rules = (DEFAULT_RULES + @plugin_rules).freeze
37
45
  end
38
46
 
39
47
  # Run all rules and return a `Dcc::Validate::Result`.
48
+ #
49
+ # Built-in rules fail fast: a crash there is a bug in this gem. A
50
+ # plugin rule is third-party code, so its failure is contained and
51
+ # reported as an error issue naming the rule class — one broken
52
+ # plugin must not cost the caller every built-in finding.
40
53
  def call
41
54
  issues = []
42
55
  rules.each do |rule_class|
43
- issues.concat(rule_class.new.check_on(dcc))
56
+ issues.concat(
57
+ if @plugin_rules.include?(rule_class)
58
+ run_plugin_rule(rule_class)
59
+ else
60
+ rule_class.new.check_on(dcc)
61
+ end,
62
+ )
44
63
  end
45
64
 
46
65
  ::Dcc::Validate::Result.new(
47
66
  issues: issues,
48
- schema_version: Dcc::TypeGuards.has_attribute?(dcc, :schema_version) ? dcc.schema_version.to_s : nil,
67
+ schema_version: if Dcc::TypeGuards.has_attribute?(dcc,
68
+ :schema_version)
69
+ dcc.schema_version.to_s
70
+ end,
71
+ source: "schematron",
72
+ )
73
+ end
74
+
75
+ private
76
+
77
+ # Run one plugin-supplied rule with fault isolation. Instantiation
78
+ # and #check_on both run inside the rescue, and a non-Array return
79
+ # (nil included) is reported rather than passed to Array#concat.
80
+ #
81
+ # @param rule_class [Class]
82
+ # @return [Array<Dcc::Validate::Issue>]
83
+ def run_plugin_rule(rule_class)
84
+ result = rule_class.new.check_on(dcc)
85
+ return result if result.is_a?(::Array)
86
+
87
+ [plugin_failure_issue(
88
+ rule_class,
89
+ "returned #{result.inspect} instead of an Array of issues",
90
+ )]
91
+ rescue ::StandardError => e
92
+ [plugin_failure_issue(rule_class, "raised #{e.class}: #{e.message}")]
93
+ end
94
+
95
+ # @param rule_class [Class]
96
+ # @param detail [String]
97
+ # @return [Dcc::Validate::Issue]
98
+ def plugin_failure_issue(rule_class, detail)
99
+ name = rule_class.name || rule_class.inspect
100
+ ::Dcc::Validate::Issue.build(
101
+ severity: ::Dcc::Validate::Severity::ERROR,
102
+ message: "plugin validator #{name} #{detail}",
103
+ code: "dcc.schematron.plugin_failure",
49
104
  source: "schematron",
50
105
  )
51
106
  end
@@ -61,4 +116,4 @@ module Dcc
61
116
  end
62
117
  end
63
118
  end
64
- end
119
+ end
@@ -13,9 +13,18 @@ module Dcc
13
13
  ::Dcc::Validate::Severity::ERROR
14
14
  end
15
15
 
16
+ # A plugin can register an anonymous rule class, whose `name` is nil.
17
+ # Falling back keeps one such rule from taking the whole run down and
18
+ # losing every issue the built-in rules already found.
19
+ #
16
20
  # @return [String] rule code used in `Issue#code`.
17
21
  def code
18
- "dcc.schematron.#{self.class.name.split('::').last.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase}"
22
+ class_name = self.class.name
23
+ return "dcc.schematron.anonymous" unless class_name
24
+
25
+ "dcc.schematron.#{class_name.split('::').last.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(
26
+ /([a-z\d])([A-Z])/, '\1_\2'
27
+ ).downcase}"
19
28
  end
20
29
 
21
30
  # @param dcc [Lutaml::Model::Serializable]
@@ -32,7 +41,8 @@ module Dcc
32
41
  # Override in subclasses to traverse the model tree.
33
42
  end
34
43
 
35
- def issue(severity:, message:, line: nil, column: nil, path: nil, source: "schematron")
44
+ def issue(severity:, message:, line: nil, column: nil, path: nil,
45
+ source: "schematron")
36
46
  ::Dcc::Validate::Issue.build(
37
47
  severity: severity,
38
48
  message: message,
@@ -46,4 +56,4 @@ module Dcc
46
56
  end
47
57
  end
48
58
  end
49
- end
59
+ end
@@ -19,13 +19,20 @@ module Dcc
19
19
 
20
20
  def check_on(dcc)
21
21
  issues = []
22
- return issues unless Dcc::TypeGuards.has_attribute?(dcc, :administrative_data)
22
+ return issues unless Dcc::TypeGuards.has_attribute?(dcc,
23
+ :administrative_data)
23
24
 
24
25
  admin = dcc.administrative_data
25
- return [issue(severity: :error, message: "administrativeData is missing")] unless admin
26
+ unless admin
27
+ return [issue(severity: :error,
28
+ message: "administrativeData is missing")]
29
+ end
26
30
 
27
31
  REQUIRED_SECTIONS.each do |attr, label|
28
- value = Dcc::TypeGuards.has_attribute?(admin, attr) ? admin.public_send(attr) : nil
32
+ value = if Dcc::TypeGuards.has_attribute?(admin,
33
+ attr)
34
+ admin.public_send(attr)
35
+ end
29
36
  next unless value.nil? || ((value.is_a?(::String) || value.is_a?(::Array)) && value.empty?)
30
37
 
31
38
  issues << issue(
@@ -39,4 +46,4 @@ module Dcc
39
46
  end
40
47
  end
41
48
  end
42
- end
49
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -12,4 +11,4 @@ module Dcc
12
11
  end
13
12
  end
14
13
  end
15
- end
14
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -8,13 +7,20 @@ module Dcc
8
7
  # Errors if `endPerformanceDate` precedes `beginPerformanceDate`.
9
8
  class DateRangeCheck < Base
10
9
  def check_on(dcc)
11
- return [] unless Dcc::TypeGuards.has_attribute?(dcc, :administrative_data)
10
+ return [] unless Dcc::TypeGuards.has_attribute?(dcc,
11
+ :administrative_data)
12
12
 
13
13
  core = safe_attr(dcc.administrative_data, :core_data)
14
14
  return [] if core.nil?
15
15
 
16
- begin_d = Dcc::TypeGuards.has_attribute?(core, :begin_performance_date) ? core.begin_performance_date : nil
17
- end_d = Dcc::TypeGuards.has_attribute?(core, :end_performance_date) ? core.end_performance_date : nil
16
+ begin_d = if Dcc::TypeGuards.has_attribute?(core,
17
+ :begin_performance_date)
18
+ core.begin_performance_date
19
+ end
20
+ end_d = if Dcc::TypeGuards.has_attribute?(core,
21
+ :end_performance_date)
22
+ core.end_performance_date
23
+ end
18
24
 
19
25
  return [] if begin_d.nil? || end_d.nil?
20
26
  return [] unless end_d < begin_d
@@ -38,4 +44,4 @@ module Dcc
38
44
  end
39
45
  end
40
46
  end
41
- end
47
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -57,5 +56,3 @@ module Dcc
57
56
  end
58
57
  end
59
58
  end
60
-
61
- require "set"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -9,25 +8,31 @@ module Dcc
9
8
  # quantity level). Error if missing.
10
9
  class InfluenceConditionsPlacement < Base
11
10
  def check_on(dcc)
12
- return [] unless Dcc::TypeGuards.has_attribute?(dcc, :measurement_results)
11
+ return [] unless Dcc::TypeGuards.has_attribute?(dcc,
12
+ :measurement_results)
13
13
 
14
14
  mr_list = safe_attr(dcc.measurement_results, :measurement_result)
15
15
  return [] if mr_list.empty?
16
16
 
17
17
  any_present = mr_list.any? do |mr|
18
- next false unless Dcc::TypeGuards.has_attribute?(mr, :influence_conditions)
18
+ next false unless Dcc::TypeGuards.has_attribute?(mr,
19
+ :influence_conditions)
19
20
  next true unless mr.influence_conditions.nil?
20
21
 
21
22
  Dcc::TypeGuards.has_attribute?(mr, :results) && mr.results &&
22
23
  has_in_descendants?(mr.results)
23
24
  end
24
25
 
25
- any_present ? [] : [
26
- issue(
27
- severity: :error,
28
- message: "dcc:influenceConditions is missing everywhere",
29
- ),
30
- ]
26
+ if any_present
27
+ []
28
+ else
29
+ [
30
+ issue(
31
+ severity: :error,
32
+ message: "dcc:influenceConditions is missing everywhere",
33
+ ),
34
+ ]
35
+ end
31
36
  end
32
37
 
33
38
  private
@@ -35,13 +40,18 @@ module Dcc
35
40
  def has_in_descendants?(node)
36
41
  return false unless node.is_a?(::Lutaml::Model::Serializable)
37
42
 
38
- if Dcc::TypeGuards.has_attribute?(node, :influence_conditions) && !node.influence_conditions.nil?
43
+ if Dcc::TypeGuards.has_attribute?(node,
44
+ :influence_conditions) && !node.influence_conditions.nil?
39
45
  return true
40
46
  end
41
47
 
42
48
  node.class.attributes.each_key do |attr|
43
49
  value = node.public_send(attr)
44
- Array(value).any? { |c| has_in_descendants?(c) } if value.is_a?(::Array) || value.is_a?(::Lutaml::Model::Serializable)
50
+ if value.is_a?(::Array) || value.is_a?(::Lutaml::Model::Serializable)
51
+ Array(value).any? do |c|
52
+ has_in_descendants?(c)
53
+ end
54
+ end
45
55
  end
46
56
  false
47
57
  end
@@ -55,4 +65,4 @@ module Dcc
55
65
  end
56
66
  end
57
67
  end
58
- end
68
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -10,13 +9,17 @@ module Dcc
10
9
  # flag empties that escaped earlier casts.
11
10
  class IsoCodeValidation < Base
12
11
  def check_on(dcc)
13
- return [] unless Dcc::TypeGuards.has_attribute?(dcc, :administrative_data)
12
+ return [] unless Dcc::TypeGuards.has_attribute?(dcc,
13
+ :administrative_data)
14
14
 
15
15
  core = safe_attr(dcc.administrative_data, :core_data)
16
16
  return [] if core.nil?
17
17
 
18
18
  issues = []
19
- country = Dcc::TypeGuards.has_attribute?(core, :country_code_iso_3166_1) ? core.country_code_iso_3166_1 : nil
19
+ country = if Dcc::TypeGuards.has_attribute?(core,
20
+ :country_code_iso_3166_1)
21
+ core.country_code_iso_3166_1
22
+ end
20
23
  if country.nil? || country.to_s.empty?
21
24
  issues << issue(
22
25
  severity: :error,
@@ -24,17 +27,31 @@ module Dcc
24
27
  )
25
28
  end
26
29
 
27
- used = Dcc::TypeGuards.has_attribute?(core, :used_lang_code_iso_639_1) ? Array(core.used_lang_code_iso_639_1) : []
28
- issues << issue(
29
- severity: :error,
30
- message: "dcc:usedLangCodeISO639_1 must have at least one entry",
31
- ) if used.empty?
30
+ used = if Dcc::TypeGuards.has_attribute?(core,
31
+ :used_lang_code_iso_639_1)
32
+ Array(core.used_lang_code_iso_639_1)
33
+ else
34
+ []
35
+ end
36
+ if used.empty?
37
+ issues << issue(
38
+ severity: :error,
39
+ message: "dcc:usedLangCodeISO639_1 must have at least one entry",
40
+ )
41
+ end
32
42
 
33
- mandatory = Dcc::TypeGuards.has_attribute?(core, :mandatory_lang_code_iso_639_1) ? Array(core.mandatory_lang_code_iso_639_1) : []
34
- issues << issue(
35
- severity: :error,
36
- message: "dcc:mandatoryLangCodeISO639_1 must have at least one entry",
37
- ) if mandatory.empty?
43
+ mandatory = if Dcc::TypeGuards.has_attribute?(core,
44
+ :mandatory_lang_code_iso_639_1)
45
+ Array(core.mandatory_lang_code_iso_639_1)
46
+ else
47
+ []
48
+ end
49
+ if mandatory.empty?
50
+ issues << issue(
51
+ severity: :error,
52
+ message: "dcc:mandatoryLangCodeISO639_1 must have at least one entry",
53
+ )
54
+ end
38
55
 
39
56
  issues
40
57
  end
@@ -50,4 +67,4 @@ module Dcc
50
67
  end
51
68
  end
52
69
  end
53
- end
70
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -8,14 +7,22 @@ module Dcc
8
7
  # Errors on duplicate language codes in `dcc:usedLangCodeISO639_1`.
9
8
  class LanguageCodeDedup < Base
10
9
  def check_on(dcc)
11
- return [] unless Dcc::TypeGuards.has_attribute?(dcc, :administrative_data)
10
+ return [] unless Dcc::TypeGuards.has_attribute?(dcc,
11
+ :administrative_data)
12
12
 
13
13
  core = safe_attr(dcc.administrative_data, :core_data)
14
14
  return [] if core.nil?
15
15
 
16
- used = Dcc::TypeGuards.has_attribute?(core, :used_lang_code_iso_639_1) ? Array(core.used_lang_code_iso_639_1) : []
16
+ used = if Dcc::TypeGuards.has_attribute?(core,
17
+ :used_lang_code_iso_639_1)
18
+ Array(core.used_lang_code_iso_639_1)
19
+ else
20
+ []
21
+ end
17
22
  used_strings = used.map(&:to_s)
18
- duplicates = used_strings.group_by(&:itself).select { |_k, v| v.size > 1 }.keys
23
+ duplicates = used_strings.group_by(&:itself).select do |_k, v|
24
+ v.size > 1
25
+ end.keys
19
26
 
20
27
  duplicates.map do |dup|
21
28
  issue(
@@ -36,4 +43,4 @@ module Dcc
36
43
  end
37
44
  end
38
45
  end
39
- end
46
+ end
@@ -36,7 +36,10 @@ module Dcc
36
36
  node.class.attributes.each_key do |attr_name|
37
37
  value = node.public_send(attr_name)
38
38
  Array(value).each do |child|
39
- declared += collect_declared_non_si_units(child, visited) if child.is_a?(::Lutaml::Model::Serializable)
39
+ if child.is_a?(::Lutaml::Model::Serializable)
40
+ declared += collect_declared_non_si_units(child,
41
+ visited)
42
+ end
40
43
  end
41
44
  end
42
45
  declared
@@ -57,7 +60,9 @@ module Dcc
57
60
  extract_non_si_unit(node, accumulator)
58
61
  node.class.attributes.each_key do |attr_name|
59
62
  value = node.public_send(attr_name)
60
- Array(value).each { |child| walk_for_used_units(child, accumulator, visited) }
63
+ Array(value).each do |child|
64
+ walk_for_used_units(child, accumulator, visited)
65
+ end
61
66
  end
62
67
  end
63
68
 
@@ -78,5 +83,3 @@ module Dcc
78
83
  end
79
84
  end
80
85
  end
81
-
82
- require "set"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -11,13 +10,17 @@ module Dcc
11
10
 
12
11
  def check_on(dcc)
13
12
  issues = []
14
- return issues unless Dcc::TypeGuards.has_attribute?(dcc, :administrative_data)
13
+ return issues unless Dcc::TypeGuards.has_attribute?(dcc,
14
+ :administrative_data)
15
15
 
16
16
  software_list = safe_attr(dcc.administrative_data, :dcc_software)
17
17
  return issues if software_list.nil?
18
18
 
19
19
  Array(safe_attr(software_list, :software)).each do |sw|
20
- release = Dcc::TypeGuards.has_attribute?(sw, :release) ? sw.release : nil
20
+ release = if Dcc::TypeGuards.has_attribute?(sw,
21
+ :release)
22
+ sw.release
23
+ end
21
24
  next if release.nil? || release.empty?
22
25
  next if release.match?(PATTERN)
23
26
 
@@ -41,4 +44,4 @@ module Dcc
41
44
  end
42
45
  end
43
46
  end
44
- end
47
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Dcc
5
4
  module Validate
6
5
  module Schematron
@@ -12,20 +11,25 @@ module Dcc
12
11
  end
13
12
 
14
13
  def check_on(dcc)
15
- return [] unless Dcc::TypeGuards.has_attribute?(dcc, :schema_version)
14
+ return [] unless Dcc::TypeGuards.has_attribute?(dcc,
15
+ :schema_version)
16
16
 
17
17
  current = dcc.schema_version.to_s
18
18
  latest = ::Dcc::Schema::Version::DCC_LATEST
19
19
 
20
- current == latest ? [] : [
21
- issue(
22
- severity: :warning,
23
- message: "schemaVersion '#{current}' is not the latest release (#{latest})",
24
- ),
25
- ]
20
+ if current == latest
21
+ []
22
+ else
23
+ [
24
+ issue(
25
+ severity: :warning,
26
+ message: "schemaVersion '#{current}' is not the latest release (#{latest})",
27
+ ),
28
+ ]
29
+ end
26
30
  end
27
31
  end
28
32
  end
29
33
  end
30
34
  end
31
- end
35
+ end
@@ -5,9 +5,16 @@ module Dcc
5
5
  module Schematron
6
6
  module Rules
7
7
  # Validates that for every `si:realListXMLList` paired with an
8
- # `si:expandedUncXMLList`, the count of values matches the count
9
- # of uncertainty entries.
8
+ # `si:expandedUncXMLList`, each uncertainty list holds either one
9
+ # entry per value or a single entry broadcast across all of them.
10
10
  class UncertaintyConsistency < Base
11
+ UNCERTAINTY_LISTS = {
12
+ uncertainty_xml_list: "uncertaintyXMLList",
13
+ coverage_factor_xml_list: "coverageFactorXMLList",
14
+ coverage_probability_xml_list: "coverageProbabilityXMLList",
15
+ distribution_xml_list: "distributionXMLList",
16
+ }.freeze
17
+
11
18
  def check_on(dcc)
12
19
  issues = []
13
20
  return issues unless dcc.is_a?(::Lutaml::Model::Serializable)
@@ -24,7 +31,10 @@ module Dcc
24
31
 
25
32
  visited << node.object_id
26
33
 
27
- check_node(node, issues) if node.class.name.end_with?("::RealListXmlList")
34
+ if node.class.name.end_with?("::RealListXmlList")
35
+ check_node(node,
36
+ issues)
37
+ end
28
38
  descend(node, issues, visited)
29
39
  end
30
40
 
@@ -33,16 +43,37 @@ module Dcc
33
43
  unc = list_node.expanded_unc_xml_list
34
44
  return unless unc && values
35
45
 
36
- unc_count = Array(unc.uncertainty_xml_list).size
37
- value_count = Array(values).size
38
- return if unc_count == value_count || unc_count.zero?
46
+ value_count = entry_count(values)
47
+ UNCERTAINTY_LISTS.each do |attribute, xml_name|
48
+ count = entry_count(unc.public_send(attribute))
49
+ next if consistent?(count, value_count)
50
+
51
+ issues << count_mismatch(xml_name, count, value_count)
52
+ end
53
+ end
39
54
 
40
- issues << issue(
55
+ def count_mismatch(xml_name, count, value_count)
56
+ issue(
41
57
  severity: :error,
42
- message: "valueXMLList count (#{value_count}) does not match expandedUncertaintyXMLList count (#{unc_count})",
58
+ message: "valueXMLList count (#{value_count}) does not " \
59
+ "match #{xml_name} count (#{count})",
43
60
  )
44
61
  end
45
62
 
63
+ # Every list here is an xs:list, so its entries are whitespace
64
+ # separated whether the attribute holds a string or a decimal list.
65
+ def entry_count(list)
66
+ list.to_s.split.size
67
+ end
68
+
69
+ # A list either carries one entry per value or a single entry that
70
+ # broadcasts across all of them, the way unitXMLList does. Siblings
71
+ # decide independently: PTB ships documents pairing nine
72
+ # uncertainties with one shared coverage factor.
73
+ def consistent?(count, value_count)
74
+ count.zero? || count == 1 || count == value_count
75
+ end
76
+
46
77
  def descend(node, issues, visited)
47
78
  node.class.attributes.each_key do |attr_name|
48
79
  value = node.public_send(attr_name)
@@ -54,5 +85,3 @@ module Dcc
54
85
  end
55
86
  end
56
87
  end
57
-
58
- require "set"
@@ -29,7 +29,8 @@ module Dcc
29
29
  end
30
30
 
31
31
  def check_single_unit(node, issues)
32
- return unless node.class.name.end_with?("::Real", "::Complex", "::Constant")
32
+ return unless node.class.name.end_with?("::Real", "::Complex",
33
+ "::Constant")
33
34
  return unless node.respond_to?(:unit)
34
35
 
35
36
  unit = node.unit
@@ -70,5 +71,3 @@ module Dcc
70
71
  end
71
72
  end
72
73
  end
73
-
74
- require "set"