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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +19 -0
- data/.github/workflows/release.yml +28 -0
- data/.gitignore +4 -0
- data/.rubocop_todo.yml +560 -10
- data/CHANGELOG.adoc +34 -1
- data/CONTRIBUTING.adoc +8 -1
- data/Gemfile +5 -7
- data/README.adoc +162 -7
- data/dcc.gemspec +8 -2
- data/docs/schema-versions.adoc +20 -7
- data/examples/build_minimal_dcc.rb +0 -0
- data/examples/convert_all_formats.rb +2 -1
- data/examples/parse_and_inspect.rb +0 -0
- data/examples/quantity_math_demo.rb +7 -7
- data/examples/validate_full.rb +1 -1
- data/lib/dcc/base/calibration_laboratory.rb +2 -1
- data/lib/dcc/base/condition.rb +5 -2
- data/lib/dcc/base/core_data.rb +8 -3
- data/lib/dcc/base/formula.rb +3 -3
- data/lib/dcc/base/identification.rb +6 -3
- data/lib/dcc/base/mathml.rb +33 -0
- data/lib/dcc/base/quantity.rb +2 -1
- data/lib/dcc/base/resp_person.rb +4 -2
- data/lib/dcc/base/text.rb +1 -1
- data/lib/dcc/base.rb +2 -1
- data/lib/dcc/builder/dsl.rb +1 -1
- data/lib/dcc/builder/session.rb +16 -14
- data/lib/dcc/builder.rb +3 -3
- data/lib/dcc/cli/cli.rb +36 -18
- data/lib/dcc/cli/formatters.rb +35 -7
- data/lib/dcc/cli.rb +1 -1
- data/lib/dcc/context_configuration.rb +2 -1
- data/lib/dcc/context_options.rb +4 -2
- data/lib/dcc/convert/csv.rb +27 -9
- data/lib/dcc/convert/html.rb +26 -110
- data/lib/dcc/convert/json.rb +10 -7
- data/lib/dcc/convert/liquid_filters.rb +58 -0
- data/lib/dcc/convert/result.rb +1 -1
- data/lib/dcc/convert/templates/report.liquid +250 -0
- data/lib/dcc/convert/yaml.rb +1 -1
- data/lib/dcc/convert.rb +1 -0
- data/lib/dcc/diff/change.rb +1 -1
- data/lib/dcc/diff/result.rb +8 -4
- data/lib/dcc/diff.rb +43 -11
- data/lib/dcc/error.rb +7 -0
- data/lib/dcc/extract/file.rb +3 -4
- data/lib/dcc/extract/ring.rb +1 -1
- data/lib/dcc/extract.rb +1 -1
- data/lib/dcc/i18n/text_lookup.rb +8 -4
- data/lib/dcc/i18n.rb +1 -1
- data/lib/dcc/inspect/report.rb +51 -14
- data/lib/dcc/inspect.rb +1 -1
- data/lib/dcc/migrate/route.rb +44 -0
- data/lib/dcc/migrate/v2_to_v3.rb +445 -0
- data/lib/dcc/migrate.rb +130 -26
- data/lib/dcc/plugin/base.rb +64 -0
- data/lib/dcc/plugin/registry.rb +7 -2
- data/lib/dcc/plugin.rb +6 -2
- data/lib/dcc/quantity_format/formatter.rb +16 -10
- data/lib/dcc/quantity_format.rb +1 -1
- data/lib/dcc/quantity_math/quantity.rb +1 -1
- data/lib/dcc/quantity_math/real.rb +6 -6
- data/lib/dcc/quantity_math.rb +1 -1
- data/lib/dcc/schema/version.rb +2 -2
- data/lib/dcc/server/app.html +282 -0
- data/lib/dcc/server/app.rb +171 -87
- data/lib/dcc/server/demo_cert.xml +244 -0
- data/lib/dcc/server/storage/entry.rb +1 -1
- data/lib/dcc/server/storage/memory.rb +3 -2
- data/lib/dcc/server/storage.rb +1 -1
- data/lib/dcc/server.rb +1 -1
- data/lib/dcc/si/base/complex.rb +1 -1
- data/lib/dcc/si/base/complex_list_xml_list.rb +1 -1
- data/lib/dcc/si/base/constant.rb +1 -1
- data/lib/dcc/si/base/coverage_interval.rb +36 -0
- data/lib/dcc/si/base/coverage_interval_mu.rb +1 -1
- data/lib/dcc/si/base/expanded_mu.rb +1 -1
- data/lib/dcc/si/base/expanded_unc.rb +34 -0
- data/lib/dcc/si/base/expanded_unc_xml_list.rb +3 -2
- data/lib/dcc/si/base/hybrid.rb +3 -2
- data/lib/dcc/si/base/measurement_uncertainty_univariate.rb +1 -1
- data/lib/dcc/si/base/real.rb +13 -3
- data/lib/dcc/si/base/real_list_xml_list.rb +5 -3
- data/lib/dcc/si/base/si_list.rb +3 -2
- data/lib/dcc/si/base/standard_mu.rb +1 -1
- data/lib/dcc/si/base.rb +5 -2
- data/lib/dcc/si/v1/coverage_interval.rb +8 -0
- data/lib/dcc/si/v1/expanded_unc.rb +1 -16
- data/lib/dcc/si/v1/measurement_uncertainty_univariate.rb +2 -1
- data/lib/dcc/si/v1.rb +5 -2
- data/lib/dcc/si/v2/coverage_interval.rb +8 -0
- data/lib/dcc/si/v2/coverage_interval_mu_xml_list.rb +4 -2
- data/lib/dcc/si/v2/expanded_mu_xml_list.rb +2 -1
- data/lib/dcc/si/v2/expanded_unc.rb +8 -0
- data/lib/dcc/si/v2/measurement_uncertainty_univariate.rb +2 -1
- data/lib/dcc/si/v2.rb +8 -3
- data/lib/dcc/signature/result.rb +2 -2
- data/lib/dcc/signature/signer.rb +31 -18
- data/lib/dcc/signature/verifier.rb +23 -21
- data/lib/dcc/signature.rb +1 -1
- data/lib/dcc/transform/result.rb +1 -1
- data/lib/dcc/transform/xslt.rb +22 -12
- data/lib/dcc/transform.rb +5 -4
- data/lib/dcc/type/base64_binary.rb +5 -3
- data/lib/dcc/type/decimal_xml_list.rb +99 -13
- data/lib/dcc/type/iso_country_code.rb +3 -2
- data/lib/dcc/type/iso_language_code.rb +3 -2
- data/lib/dcc/type/schema_version.rb +3 -2
- data/lib/dcc/type/si_unit.rb +3 -2
- data/lib/dcc/type_guards.rb +3 -3
- data/lib/dcc/v2/calibration_laboratory.rb +2 -1
- data/lib/dcc/v2/condition.rb +22 -1
- data/lib/dcc/v2/configuration.rb +1 -1
- data/lib/dcc/v2/digital_calibration_certificate.rb +2 -1
- data/lib/dcc/v2/identification.rb +14 -1
- data/lib/dcc/v2/mathml.rb +8 -0
- data/lib/dcc/v2/measurement_meta_data_list.rb +2 -1
- data/lib/dcc/v2/quantity.rb +4 -2
- data/lib/dcc/v2.rb +4 -2
- data/lib/dcc/v3/calibration_laboratory.rb +2 -1
- data/lib/dcc/v3/configuration.rb +1 -1
- data/lib/dcc/v3/digital_calibration_certificate.rb +2 -1
- data/lib/dcc/v3/mathml.rb +8 -0
- data/lib/dcc/v3/measurement_meta_data_list.rb +2 -1
- data/lib/dcc/v3/quantity.rb +4 -2
- data/lib/dcc/v3.rb +12 -2
- data/lib/dcc/validate/business_rules.rb +23 -9
- data/lib/dcc/validate/issue.rb +2 -1
- data/lib/dcc/validate/result.rb +6 -4
- data/lib/dcc/validate/schematron/profile.rb +73 -18
- data/lib/dcc/validate/schematron/rule.rb +13 -3
- data/lib/dcc/validate/schematron/rules/administrative_data_completeness.rb +11 -4
- data/lib/dcc/validate/schematron/rules/base.rb +1 -2
- data/lib/dcc/validate/schematron/rules/date_range_check.rb +11 -5
- data/lib/dcc/validate/schematron/rules/id_refid_linking.rb +0 -3
- data/lib/dcc/validate/schematron/rules/influence_conditions_placement.rb +22 -12
- data/lib/dcc/validate/schematron/rules/iso_code_validation.rb +31 -14
- data/lib/dcc/validate/schematron/rules/language_code_dedup.rb +12 -5
- data/lib/dcc/validate/schematron/rules/non_si_declaration.rb +7 -4
- data/lib/dcc/validate/schematron/rules/release_format_check.rb +7 -4
- data/lib/dcc/validate/schematron/rules/schema_version_check.rb +13 -9
- data/lib/dcc/validate/schematron/rules/uncertainty_consistency.rb +39 -10
- data/lib/dcc/validate/schematron/rules/unit_format_check.rb +2 -3
- data/lib/dcc/validate/schematron/rules/used_methods_placement.rb +15 -10
- data/lib/dcc/validate/schematron/rules/used_software_placement.rb +15 -10
- data/lib/dcc/validate/schematron/rules/xml_list_spacing.rb +4 -3
- data/lib/dcc/validate/schematron/rules.rb +28 -14
- data/lib/dcc/validate/schematron.rb +1 -1
- data/lib/dcc/validate/xsd.rb +9 -3
- data/lib/dcc/validate.rb +1 -1
- data/lib/dcc/version.rb +1 -1
- data/lib/dcc/versioned_parser.rb +1 -1
- data/lib/dcc.rb +55 -7
- data/sample_dcc.xml +140 -0
- data/web/index.html +12 -0
- data/web/package-lock.json +1599 -0
- data/web/package.json +22 -0
- data/web/src/App.vue +349 -0
- data/web/src/lib/builder.ts +171 -0
- data/web/src/lib/dcc.ts +338 -0
- data/web/src/lib/units.json +5581 -0
- data/web/src/main.ts +5 -0
- data/web/src/style.css +27 -0
- data/web/tsconfig.json +15 -0
- data/web/vite.config.ts +14 -0
- 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 "
|
|
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
|
|
23
|
-
* XMLDSig signature signing and verification
|
|
24
|
-
*
|
|
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.
|
|
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
|
-
|
|
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 "
|
|
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 "
|
|
44
|
+
spec.add_dependency "moxml", "~> 0.5"
|
|
45
|
+
spec.add_dependency "nokogiri", "~> 1.16"
|
|
46
|
+
spec.add_dependency "thor", "~> 1.4"
|
|
41
47
|
end
|
data/docs/schema-versions.adoc
CHANGED
|
@@ -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
|
|
54
|
+
Use `Dcc.migrate` to upgrade a DCC to a newer schema version:
|
|
55
55
|
|
|
56
56
|
[source,ruby]
|
|
57
57
|
----
|
|
58
|
-
|
|
59
|
-
migrated = Dcc.migrate(
|
|
60
|
-
puts migrated.schema_version # => 3.
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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
|
-
|
|
28
|
-
|
|
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}"
|
data/examples/validate_full.rb
CHANGED
|
@@ -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",
|
|
22
|
+
map_element "calibrationLaboratoryCode",
|
|
23
|
+
to: :calibration_laboratory_code
|
|
23
24
|
map_element "contact", to: :contact
|
|
24
25
|
end
|
|
25
26
|
end
|
data/lib/dcc/base/condition.rb
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
module Dcc
|
|
4
4
|
module Base
|
|
5
5
|
# `dcc:conditionType` — single influence condition: name + description +
|
|
6
|
-
# optional
|
|
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 "
|
|
30
|
+
map_element "status", to: :status
|
|
28
31
|
map_element "data", to: :data
|
|
29
32
|
end
|
|
30
33
|
end
|
data/lib/dcc/base/core_data.rb
CHANGED
|
@@ -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,
|
|
15
|
-
|
|
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",
|
|
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
|
data/lib/dcc/base/formula.rb
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
-
#
|
|
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 :
|
|
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 "
|
|
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
|
data/lib/dcc/base/quantity.rb
CHANGED
|
@@ -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,
|
|
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
|
data/lib/dcc/base/resp_person.rb
CHANGED
|
@@ -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",
|
|
32
|
-
|
|
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
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,
|
|
56
|
+
autoload :DigitalCalibrationCertificate,
|
|
57
|
+
"dcc/base/digital_calibration_certificate"
|
|
57
58
|
end
|
|
58
59
|
end
|
data/lib/dcc/builder/dsl.rb
CHANGED