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
data/CHANGELOG.adoc CHANGED
@@ -12,6 +12,36 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
12
12
  * Performance memoization for hot paths
13
13
  * Plugin system for custom validators and converters
14
14
 
15
+ === Fixed
16
+ * `dcc:list` no longer parses as an empty `si:list`. The D-SI re-export was
17
+ overwriting the DCC `list` registration, so every list and the quantities
18
+ inside it were dropped on parse.
19
+ * `si:expandedUnc` and `si:coverageInterval` now survive a parse. Both are
20
+ members of the same `si:real` choice group and neither was mapped, so D-SI
21
+ v1 uncertainties were discarded.
22
+ * `dcc:mathml` now parses. It is a `dcc:xmlType` wrapper around `ml:math`,
23
+ not the `ml:math` element itself, so `formula.mathml` was always `nil`.
24
+ Known gap: `ml:ci/@xref` does not survive a round-trip. `Mml::V3::Ci`
25
+ has no `xref` attribute, so the value is lost on parse and never reaches
26
+ the output. Those xrefs bind the formula variables to their
27
+ `dcc:quantity/@id`. A round-tripped formula keeps its MathML and loses
28
+ those bindings, with no warning. The fix belongs in the `mml` gem, not
29
+ here.
30
+ * Decimal XML lists serialize as one space-separated element again. Each
31
+ value was being written as its own element containing a Ruby array
32
+ literal, e.g. `<si:valueXMLList>[0.72e-1]</si:valueXMLList>`. `NaN`
33
+ round-trips too, which every D-SI decimal type admits.
34
+ * `UncertaintyConsistency` accepts a single entry broadcast across every
35
+ value, and now checks `coverageFactorXMLList`, `coverageProbabilityXMLList`
36
+ and `distributionXMLList` rather than the uncertainty list alone.
37
+ * CSV export renders list values as plain decimals instead of `0.72e-1`.
38
+
39
+ === Changed
40
+ * *Breaking:* `formula.mathml` returns a `Dcc::V2::Mathml` / `Dcc::V3::Mathml`
41
+ wrapper instead of an `Mml::V3::Math`. The MathML tree moves one level
42
+ down, so callers now read `formula.mathml.math`. This matches the XSD,
43
+ where `dcc:mathml` is a `dcc:xmlType` wrapper.
44
+
15
45
  == [0.1.0] — 2026-07-22
16
46
 
17
47
  === Added
@@ -48,7 +78,10 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
48
78
  * No `require "dcc/..."` with internal paths.
49
79
  * No `send` / `instance_variable_set` / `instance_variable_get`.
50
80
  * No `respond_to?` for type checking.
51
- * No Nokogiri outside `lib/dcc/validate/xsd.rb`.
81
+ * No Nokogiri outside `lib/dcc/validate/xsd.rb`. The v2-to-v3 migration later
82
+ added `lib/dcc/migrate/v2_to_v3.rb` as a second sanctioned file, because a
83
+ version transform has to rewrite documents the object model cannot
84
+ round-trip. To be removed once the parser round-trips without loss.
52
85
  * No doubles in specs.
53
86
  * `frozen_string_literal: true` everywhere.
54
87
  * OCP/MECE/DRY throughout.
data/CONTRIBUTING.adoc CHANGED
@@ -34,7 +34,14 @@ constraints:
34
34
  * `send` to call private methods — redesign the API boundary.
35
35
  * `instance_variable_set` / `instance_variable_get` — use accessors.
36
36
  * `respond_to?` for type checking — use `is_a?` or model the type hierarchy.
37
- * Nokogiri anywhere outside `lib/dcc/validate/xsd.rb`.
37
+ * Nokogiri anywhere outside `lib/dcc/validate/xsd.rb` and
38
+ `lib/dcc/migrate/v2_to_v3.rb`.
39
+ +
40
+ The migration exception exists because a version transform rewrites element
41
+ names and moves subtrees on documents the object model cannot round-trip —
42
+ doing it through the model would discard the very constructs the transform
43
+ has to carry. Revisit and remove this exception when the parser round-trips
44
+ a DCC without loss, at which point the transform can run on the model.
38
45
  * Doubles in specs — use real model instances.
39
46
  * Hand-rolled `to_h`/`from_h` on model classes — use lutaml-model mappings.
40
47
 
data/Gemfile CHANGED
@@ -4,22 +4,20 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
- gem "canon"
8
- gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
9
- gem "mml", "~> 2.4.0"
10
- gem "moxml"
11
- gem "nokogiri"
7
+ gem "canon", github: "lutaml/canon", branch: "main" # main-only Canon::Config
12
8
  gem "rake", "~> 13.0"
13
9
  gem "rspec", "~> 3.0"
14
10
  gem "rubocop", "~> 1.21"
15
11
  gem "rubocop-performance"
16
12
  gem "rubocop-rake"
17
13
  gem "rubocop-rspec"
18
- gem "tty-table"
19
14
 
20
15
  # Optional dependencies — loaded lazily by the feature that needs them.
21
16
  group :optional do
22
17
  gem "sinatra" # Dcc::Server REST API
23
18
  gem "tty-prompt" # dcc issue (interactive builder)
24
- gem "xmldsig" # Dcc::Signature
19
+ gem "tty-table" # dcc extract files (aligned table)
25
20
  end
21
+
22
+ gem "puma", "~> 8.0"
23
+ gem "rackup", "~> 2.3"
data/README.adoc CHANGED
@@ -10,19 +10,66 @@ provides a fully typed Ruby object representation of both the DCC and D-SI
10
10
  XML schemas, with per-major-version class hierarchies mirroring the approach
11
11
  used by the https://github.com/plurimath/mml[`mml`^] gem for MathML.
12
12
 
13
+ == What is a Digital Calibration Certificate?
14
+
15
+ A Digital Calibration Certificate (DCC) is an XML-based, machine-readable
16
+ calibration certificate developed by Germany's national metrology institute,
17
+ the https://ptb.de[Physikalisch-Technische Bundesanstalt (PTB)^]. It is
18
+ designed to replace traditional paper-based calibration certificates with
19
+ a format that both humans and machines can process automatically.
20
+
21
+ A DCC document contains three primary sections:
22
+
23
+ * **Administrative data** — identifies the calibration laboratory, the
24
+ customer, the calibrated item(s), responsible persons, performance dates,
25
+ and any statements about traceability or conformity.
26
+ * **Measurement results** — the actual calibration data: measured values,
27
+ uncertainties, units, environmental conditions, and the methods/equipment
28
+ used. Each measurement quantity is expressed using the D-SI format.
29
+ * **Document** — optional attached files (PDF reports, spreadsheets, images)
30
+ embedded as base64 binary data.
31
+
32
+ The certificate can be digitally signed using W3C XMLDSig to guarantee
33
+ authenticity and integrity.
34
+
35
+ === What is D-SI?
36
+
37
+ D-SI (Digital System of Units) is a companion XML format for expressing
38
+ SI-based measurement quantities with full uncertainty information. A D-SI
39
+ quantity carries the value, unit (in siunitx notation), and an uncertainty
40
+ model (standard, expanded with coverage factor k, or coverage interval).
41
+
42
+ For example, a resistance measurement of 100.0225 Ω ± 0.003 Ω (k=2):
43
+
44
+ [source,xml]
45
+ ----
46
+ <si:real>
47
+ <si:value>100.0225</si:value>
48
+ <si:unit>\ohm</si:unit>
49
+ <si:expandedMU>
50
+ <si:valueExpandedMU>0.003</si:valueExpandedMU>
51
+ <si:coverageFactor>2</si:coverageFactor>
52
+ <si:coverageProbability>0.95</si:coverageProbability>
53
+ </si:expandedMU>
54
+ </si:real>
55
+ ----
56
+
57
+ D-SI also supports complex quantities, constants, lists (both verbose and
58
+ compact XML-list forms), hybrid multi-unit representations, covariance
59
+ matrices, and coverage regions for multivariate uncertainties.
60
+
13
61
  == Highlights
14
62
 
15
63
  * Fully typed object model for DCC v2.x and v3.x (no more XPath over `lxml`)
16
64
  * Fully typed object model for D-SI v1.x and v2.x
17
- * Lossless round-trip XML fidelity (parsing then serializing is byte-equivalent after canonicalization)
65
+ * Lossless round-trip XML fidelity (parsing then serializing is byte-equivalent after canonicalization via `moxml`)
18
66
  * Pure-Ruby Schematron engine implementing the 14 PTB Schematron patterns
19
67
  (no Java/Saxon-HE dependency)
20
68
  * XSD validation against all 12 bundled DCC schema versions
21
69
  * JSON / YAML / CSV / HTML converters
22
- * MathML formula extraction with BigDecimal-based evaluation
23
- * XMLDSig signature signing and verification (soft dependency on `xmldsig`)
24
- * XSLT 1.0 transformation via Nokogiri (optional Saxon-HE bridge for XSLT 2.0/3.0)
25
- * Programmatic DCC builder DSL
70
+ * MathML formula support via the `mml` gem (typed model, not raw strings)
71
+ * XMLDSig signature signing and verification via `moxml` native Signature
72
+ * Programmatic DCC builder DSL for issuing new certificates
26
73
  * Structural diff of two certificates
27
74
  * Version migration helpers
28
75
  * Thor-based CLI with `validate`, `convert`, `extract`, `signature`,
@@ -42,7 +89,7 @@ require "dcc"
42
89
 
43
90
  dcc = Dcc.parse(File.read("certificate.xml"))
44
91
  dcc.administrative_data.core_data.unique_identifier
45
- dcc.measurement_results.first.results.first.data.quantities.first.value
92
+ dcc.measurement_results.measurement_result.first.results.result.first.data.first.quantity.first.name
46
93
  ----
47
94
 
48
95
  Validate:
@@ -53,11 +100,119 @@ result = Dcc::Validate::Xsd.call(File.read("certificate.xml"), version: "3.3.0")
53
100
  result.ok? # => true
54
101
  ----
55
102
 
56
- Or via the CLI:
103
+ == Building a new DCC
104
+
105
+ Use the builder DSL to issue a certificate programmatically:
106
+
107
+ [source,ruby]
108
+ ----
109
+ require "dcc"
110
+
111
+ dcc = Dcc.build(version: 3) do
112
+ administrative_data do
113
+ core_data do
114
+ unique_identifier "urn:uuid:00000000-0000-0000-0000-000000000001"
115
+ country_code "DE"
116
+ used_lang "en"
117
+ mandatory_lang "en"
118
+ begin_performance_date Date.today
119
+ end_performance_date Date.today
120
+ end
121
+
122
+ items do
123
+ item(model: "Pt100", manufacturer: "ACME Sensors") do
124
+ name { content lang: "en", value: "Resistance Thermometer" }
125
+ end
126
+ end
127
+
128
+ calibration_laboratory do
129
+ calibration_laboratory_code "PTB-123"
130
+ contact do
131
+ name { content lang: "en", value: "Calibration Lab" }
132
+ e_mail "lab@example.org"
133
+ location do
134
+ city "Berlin"
135
+ country_code "DE"
136
+ end
137
+ end
138
+ end
139
+
140
+ resp_persons do
141
+ resp_person do
142
+ person do
143
+ name { content lang: "en", value: "Dr. Jane Doe" }
144
+ e_mail "jane@example.org"
145
+ end
146
+ main_signer true
147
+ end
148
+ end
149
+
150
+ customer do
151
+ name { content lang: "en", value: "Acme Corp" }
152
+ end
153
+ end
154
+
155
+ measurement_results do
156
+ measurement_result do
157
+ name { content lang: "en", value: "Resistance at 0 °C" }
158
+ results do
159
+ result do
160
+ name { content lang: "en", value: "R(0 °C)" }
161
+ data do
162
+ quantity do
163
+ name { content lang: "en", value: "Measured resistance" }
164
+ # D-SI quantity element (si:real) is wired via the typed model
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ puts dcc.to_xml
174
+ ----
175
+
176
+ == Signing a DCC
177
+
178
+ Sign with an X.509 certificate and RSA private key:
179
+
180
+ [source,ruby]
181
+ ----
182
+ require "dcc"
183
+
184
+ cert_pem = File.read("cert.pem")
185
+ key_pem = File.read("cert.key")
186
+
187
+ signed_xml = Dcc::Signature::Signer.call(
188
+ File.read("certificate.xml"),
189
+ cert_pem: cert_pem,
190
+ key_pem: key_pem,
191
+ )
192
+
193
+ File.write("certificate_signed.xml", signed_xml)
194
+ ----
195
+
196
+ Verify a signed certificate:
197
+
198
+ [source,ruby]
199
+ ----
200
+ result = Dcc::Signature::Verifier.call(
201
+ File.read("certificate_signed.xml"),
202
+ ca_cert_pem: cert_pem,
203
+ )
204
+ result.valid? # => true
205
+ ----
206
+
207
+ == CLI
57
208
 
58
209
  $ dcc inspect certificate.xml
59
210
  $ dcc validate all certificate.xml
60
211
  $ dcc convert json certificate.xml -o certificate.json
212
+ $ dcc extract files certificate.xml
213
+ $ dcc signature sign cert.pem key.pem certificate.xml -o signed.xml
214
+ $ dcc signature verify cert.pem signed.xml
215
+ $ dcc diff a.xml b.xml
61
216
 
62
217
  == Status
63
218
 
data/dcc.gemspec CHANGED
@@ -35,7 +35,13 @@ Gem::Specification.new do |spec|
35
35
 
36
36
  spec.required_ruby_version = ">= 3.2.0"
37
37
 
38
- spec.add_dependency "lutaml-model", "~> 0.8"
38
+ spec.add_dependency "base64" # not a default gem since Ruby 3.4
39
+ spec.add_dependency "csv" # not a default gem since Ruby 3.4
40
+ spec.add_dependency "forwardable" # stdlib, declared for the packaging guard
41
+ spec.add_dependency "liquid", "~> 5"
42
+ spec.add_dependency "lutaml-model", "~> 0.8.114"
39
43
  spec.add_dependency "mml", "~> 2.4.0"
40
- spec.add_dependency "thor"
44
+ spec.add_dependency "moxml", "~> 0.5"
45
+ spec.add_dependency "nokogiri", "~> 1.16"
46
+ spec.add_dependency "thor", "~> 1.4"
41
47
  end
@@ -51,15 +51,28 @@ Dcc::Schema::Version.resolve_dcc(:auto, xml: xml) # => "3.3.0"
51
51
 
52
52
  == Migration
53
53
 
54
- Use `Dcc.migrate` to upgrade or downgrade between schema versions:
54
+ Use `Dcc.migrate` to upgrade a DCC to a newer schema version:
55
55
 
56
56
  [source,ruby]
57
57
  ----
58
- dcc = Dcc.parse(File.read("v3.xml"))
59
- migrated = Dcc.migrate(dcc, from: "3.3.0", to: "3.2.1")
60
- puts migrated.schema_version # => 3.2.1
58
+ xml = File.read("v2.xml")
59
+ migrated = Dcc.migrate(xml, from: "2.3.0", to: "3.3.0")
60
+ puts migrated.schema_version # => 3.3.0
61
61
  ----
62
62
 
63
- Cross-major migration (e.g. v2 → v3) serializes the source DCC and
64
- re-parses it under the target version's context. Same-major just
65
- rewrites the `schemaVersion` attribute in place.
63
+ Pass the source XML whenever you have it. `Dcc.migrate` also accepts a
64
+ parsed DCC, but a model has to be serialized back to XML before the
65
+ transform runs — so anything the parser could not round-trip is already
66
+ gone, and the migration can neither carry it nor report it.
67
+
68
+ Migration is one-way. Downgrading raises `Dcc::UnsupportedMigrationError`,
69
+ because v3 adds elements and attributes no older schema accepts:
70
+
71
+ [source,ruby]
72
+ ----
73
+ Dcc.migrate(xml, from: "3.3.0", to: "3.2.1") # raises
74
+ ----
75
+
76
+ Cross-major migration (e.g. v2 → v3) applies the route's field rules to the
77
+ XML and parses the result under the target version's context. Same-major
78
+ just rewrites the `schemaVersion` attribute.
File without changes
@@ -11,7 +11,8 @@ require "dcc"
11
11
  path = ARGV[0] || File.expand_path("../spec/fixtures/dcclib/valid.xml", __dir__)
12
12
  dcc = Dcc.parse(File.read(path))
13
13
 
14
- [Dcc::Convert::Json, Dcc::Convert::Yaml, Dcc::Convert::Csv, Dcc::Convert::Html].each do |service|
14
+ [Dcc::Convert::Json, Dcc::Convert::Yaml, Dcc::Convert::Csv,
15
+ Dcc::Convert::Html].each do |service|
15
16
  result = service.call(dcc)
16
17
  puts "== #{service.name.split('::').last} =="
17
18
  puts result.to_s[0, 200] + "..."
File without changes
@@ -9,12 +9,12 @@
9
9
  require "dcc"
10
10
  require "bigdecimal"
11
11
 
12
- ten_k = Dcc::QuantityMath::Real.new(value: BigDecimal("10"),
12
+ ten_k = Dcc::QuantityMath::Real.new(value: BigDecimal(10),
13
+ unit: "\\kelvin",
14
+ uncertainty: BigDecimal("0.1"))
15
+ twenty_k = Dcc::QuantityMath::Real.new(value: BigDecimal(20),
13
16
  unit: "\\kelvin",
14
- uncertainty: BigDecimal("0.1"))
15
- twenty_k = Dcc::QuantityMath::Real.new(value: BigDecimal("20"),
16
- unit: "\\kelvin",
17
- uncertainty: BigDecimal("0.2"))
17
+ uncertainty: BigDecimal("0.2"))
18
18
 
19
19
  puts "ten: #{ten_k}"
20
20
  puts "twenty: #{twenty_k}"
@@ -24,8 +24,8 @@ puts "ratio: #{twenty_k / ten_k}"
24
24
 
25
25
  # Pretty-print with SmartCom-style formats.
26
26
  fmt = Dcc::QuantityFormat::Formatter.new(value: BigDecimal("42.00"),
27
- uncertainty: BigDecimal("0.05"),
28
- unit: "\\kelvin")
27
+ uncertainty: BigDecimal("0.05"),
28
+ unit: "\\kelvin")
29
29
  puts
30
30
  puts "Short: #{fmt.to_short}"
31
31
  puts "Long: #{fmt.to_long}"
@@ -22,6 +22,6 @@ dcc = Dcc.parse(xml)
22
22
  else service.call(dcc)
23
23
  end
24
24
  puts "== #{service.name} =="
25
- puts result.to_s
25
+ puts result
26
26
  puts
27
27
  end
@@ -19,7 +19,8 @@ module Dcc
19
19
  map_attribute "id", to: :id
20
20
  map_attribute "refId", to: :ref_id
21
21
  map_attribute "refType", to: :ref_type
22
- map_element "calibrationLaboratoryCode", to: :calibration_laboratory_code
22
+ map_element "calibrationLaboratoryCode",
23
+ to: :calibration_laboratory_code
23
24
  map_element "contact", to: :contact
24
25
  end
25
26
  end
@@ -3,7 +3,10 @@
3
3
  module Dcc
4
4
  module Base
5
5
  # `dcc:conditionType` — single influence condition: name + description +
6
- # optional state enum (beforeAdjustment, etc.) + data.
6
+ # optional status enum (beforeAdjustment, etc.) + data.
7
+ #
8
+ # v3 calls that child `status`; v2 calls it `state`, which
9
+ # `Dcc::V2::Condition` maps.
7
10
  module Condition
8
11
  def self.included(klass)
9
12
  klass.class_eval do
@@ -24,7 +27,7 @@ module Dcc
24
27
  map_attribute "refType", to: :ref_type
25
28
  map_element "name", to: :name
26
29
  map_element "description", to: :description
27
- map_element "state", to: :status
30
+ map_element "status", to: :status
28
31
  map_element "data", to: :data
29
32
  end
30
33
  end
@@ -11,13 +11,16 @@ module Dcc
11
11
  attribute :ref_id, :string
12
12
  attribute :ref_type, :string
13
13
  attribute :country_code_iso_3166_1, ::Dcc::Type::IsoCountryCode
14
- attribute :used_lang_code_iso_639_1, ::Dcc::Type::IsoLanguageCode, collection: true
15
- attribute :mandatory_lang_code_iso_639_1, ::Dcc::Type::IsoLanguageCode, collection: true
14
+ attribute :used_lang_code_iso_639_1, ::Dcc::Type::IsoLanguageCode,
15
+ collection: true
16
+ attribute :mandatory_lang_code_iso_639_1,
17
+ ::Dcc::Type::IsoLanguageCode, collection: true
16
18
  attribute :unique_identifier, :string
17
19
  attribute :identifications, :identifications
18
20
  attribute :receipt_date, :date
19
21
  attribute :begin_performance_date, :date
20
22
  attribute :end_performance_date, :date
23
+ attribute :performance_location, :string
21
24
  attribute :previous_report, :previousReport
22
25
 
23
26
  xml do
@@ -29,12 +32,14 @@ module Dcc
29
32
  map_attribute "refType", to: :ref_type
30
33
  map_element "countryCodeISO3166_1", to: :country_code_iso_3166_1
31
34
  map_element "usedLangCodeISO639_1", to: :used_lang_code_iso_639_1
32
- map_element "mandatoryLangCodeISO639_1", to: :mandatory_lang_code_iso_639_1
35
+ map_element "mandatoryLangCodeISO639_1",
36
+ to: :mandatory_lang_code_iso_639_1
33
37
  map_element "uniqueIdentifier", to: :unique_identifier
34
38
  map_element "identifications", to: :identifications
35
39
  map_element "receiptDate", to: :receipt_date
36
40
  map_element "beginPerformanceDate", to: :begin_performance_date
37
41
  map_element "endPerformanceDate", to: :end_performance_date
42
+ map_element "performanceLocation", to: :performance_location
38
43
  map_element "previousReport", to: :previous_report
39
44
  end
40
45
  end
@@ -5,7 +5,7 @@ require "mml"
5
5
  module Dcc
6
6
  module Base
7
7
  # `dcc:formulaType` — formula expression. Contains `latex` string,
8
- # `mathml` (a typed `Mml::V3::Math` model), or legacy `siunitx`.
8
+ # `mathml` (a `dcc:xmlType` wrapper around `ml:math`), or legacy `siunitx`.
9
9
  module Formula
10
10
  def self.included(klass)
11
11
  klass.class_eval do
@@ -13,7 +13,7 @@ module Dcc
13
13
  attribute :ref_id, :string
14
14
  attribute :ref_type, :string
15
15
  attribute :latex, :string
16
- attribute :mathml, ::Mml::V3::Math
16
+ attribute :mathml, :mathml
17
17
  attribute :siunitx, :string
18
18
 
19
19
  xml do
@@ -30,4 +30,4 @@ module Dcc
30
30
  end
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -3,14 +3,17 @@
3
3
  module Dcc
4
4
  module Base
5
5
  # `dcc:identificationType` — single identifier (issuer, value, optional
6
- # description). Issuer enum: manufacturer, calibrationLaboratory, customer,
6
+ # name). Issuer enum: manufacturer, calibrationLaboratory, customer,
7
7
  # owner, other.
8
+ #
9
+ # v3 calls the third child `name`; v2 calls it `description`, which
10
+ # `Dcc::V2::Identification` maps.
8
11
  module Identification
9
12
  def self.included(klass)
10
13
  klass.class_eval do
11
14
  attribute :issuer, :string
12
15
  attribute :value, :string
13
- attribute :description, :text
16
+ attribute :name, :text
14
17
 
15
18
  xml do
16
19
  namespace ::Dcc::Namespace::Dcc
@@ -18,7 +21,7 @@ module Dcc
18
21
  ordered
19
22
  map_element "issuer", to: :issuer
20
23
  map_element "value", to: :value
21
- map_element "description", to: :description
24
+ map_element "name", to: :name
22
25
  end
23
26
  end
24
27
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mml"
4
+
5
+ module Dcc
6
+ module Base
7
+ # `dcc:mathml` — a `dcc:xmlType` wrapper carrying a single foreign-namespace
8
+ # child, which for a formula is `ml:math`. The MathML tree hangs below the
9
+ # wrapper rather than replacing it.
10
+ module Mathml
11
+ # rubocop:disable Metrics/MethodLength -- one lutaml mapping block,
12
+ # split the same way every sibling module in Dcc::Base is written.
13
+ def self.included(klass)
14
+ klass.class_eval do
15
+ attribute :id, :string
16
+ attribute :ref_id, :string
17
+ attribute :ref_type, :string
18
+ attribute :math, ::Mml::V3::Math
19
+
20
+ xml do
21
+ namespace ::Dcc::Namespace::Dcc
22
+ element "mathml"
23
+ map_attribute "id", to: :id
24
+ map_attribute "refId", to: :ref_id
25
+ map_attribute "refType", to: :ref_type
26
+ map_element "math", to: :math
27
+ end
28
+ end
29
+ end
30
+ # rubocop:enable Metrics/MethodLength
31
+ end
32
+ end
33
+ end
@@ -25,7 +25,8 @@ module Dcc
25
25
  attribute :constant, :constant, collection: true
26
26
  attribute :hybrid, :hybrid, collection: true
27
27
  attribute :real_list_xml_list, :realListXMLList, collection: true
28
- attribute :complex_list_xml_list, :complexListXMLList, collection: true
28
+ attribute :complex_list_xml_list, :complexListXMLList,
29
+ collection: true
29
30
  attribute :list, :list, collection: true
30
31
  # Ancillary DCC sections
31
32
  attribute :used_methods, :usedMethods
@@ -28,8 +28,10 @@ module Dcc
28
28
  map_element "description", to: :description
29
29
  map_element "mainSigner", to: :main_signer
30
30
  map_element "cryptElectronicSeal", to: :crypt_electronic_seal
31
- map_element "cryptElectronicSignature", to: :crypt_electronic_signature
32
- map_element "cryptElectronicTimeStamp", to: :crypt_electronic_time_stamp
31
+ map_element "cryptElectronicSignature",
32
+ to: :crypt_electronic_signature
33
+ map_element "cryptElectronicTimeStamp",
34
+ to: :crypt_electronic_time_stamp
33
35
  end
34
36
  end
35
37
  end
data/lib/dcc/base/text.rb CHANGED
@@ -30,4 +30,4 @@ module Dcc
30
30
  end
31
31
  end
32
32
  end
33
- end
33
+ end
data/lib/dcc/base.rb CHANGED
@@ -53,6 +53,7 @@ module Dcc
53
53
  autoload :MeasurementMetaData, "dcc/base/measurement_meta_data"
54
54
  autoload :MeasurementMetaDataList, "dcc/base/measurement_meta_data_list"
55
55
  autoload :AdministrativeData, "dcc/base/administrative_data"
56
- autoload :DigitalCalibrationCertificate, "dcc/base/digital_calibration_certificate"
56
+ autoload :DigitalCalibrationCertificate,
57
+ "dcc/base/digital_calibration_certificate"
57
58
  end
58
59
  end
@@ -7,4 +7,4 @@ module Dcc
7
7
  module Dsl
8
8
  end
9
9
  end
10
- end
10
+ end