dcc 0.2.0

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 (336) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.rubocop_todo.yml +10 -0
  6. data/CHANGELOG.adoc +54 -0
  7. data/CONTRIBUTING.adoc +64 -0
  8. data/Gemfile +25 -0
  9. data/LICENSE +21 -0
  10. data/README.adoc +69 -0
  11. data/Rakefile +13 -0
  12. data/dcc.gemspec +41 -0
  13. data/docs/cli-reference.adoc +67 -0
  14. data/docs/getting-started.adoc +117 -0
  15. data/docs/schema-versions.adoc +65 -0
  16. data/examples/build_minimal_dcc.rb +23 -0
  17. data/examples/convert_all_formats.rb +19 -0
  18. data/examples/parse_and_inspect.rb +19 -0
  19. data/examples/quantity_math_demo.rb +32 -0
  20. data/examples/validate_full.rb +27 -0
  21. data/exe/dcc +7 -0
  22. data/lib/dcc/base/administrative_data.rb +43 -0
  23. data/lib/dcc/base/byte_data.rb +36 -0
  24. data/lib/dcc/base/calibration_laboratory.rb +29 -0
  25. data/lib/dcc/base/comment.rb +23 -0
  26. data/lib/dcc/base/condition.rb +34 -0
  27. data/lib/dcc/base/contact.rb +38 -0
  28. data/lib/dcc/base/contact_not_strict.rb +38 -0
  29. data/lib/dcc/base/core_data.rb +44 -0
  30. data/lib/dcc/base/data.rb +42 -0
  31. data/lib/dcc/base/digital_calibration_certificate.rb +32 -0
  32. data/lib/dcc/base/equipment_class.rb +24 -0
  33. data/lib/dcc/base/formula.rb +33 -0
  34. data/lib/dcc/base/hash_type.rb +34 -0
  35. data/lib/dcc/base/identification.rb +27 -0
  36. data/lib/dcc/base/identifications.rb +27 -0
  37. data/lib/dcc/base/influence_condition.rb +16 -0
  38. data/lib/dcc/base/influence_condition_list.rb +40 -0
  39. data/lib/dcc/base/item.rb +38 -0
  40. data/lib/dcc/base/item_list.rb +38 -0
  41. data/lib/dcc/base/list.rb +44 -0
  42. data/lib/dcc/base/location.rb +36 -0
  43. data/lib/dcc/base/measurement_meta_data.rb +15 -0
  44. data/lib/dcc/base/measurement_meta_data_list.rb +27 -0
  45. data/lib/dcc/base/measurement_result.rb +41 -0
  46. data/lib/dcc/base/measurement_result_list.rb +28 -0
  47. data/lib/dcc/base/measuring_equipment.rb +41 -0
  48. data/lib/dcc/base/measuring_equipment_list.rb +38 -0
  49. data/lib/dcc/base/quantity.rb +61 -0
  50. data/lib/dcc/base/ref_type_definition.rb +36 -0
  51. data/lib/dcc/base/ref_type_definition_list.rb +21 -0
  52. data/lib/dcc/base/resp_person.rb +38 -0
  53. data/lib/dcc/base/resp_person_list.rb +27 -0
  54. data/lib/dcc/base/result.rb +31 -0
  55. data/lib/dcc/base/result_list.rb +27 -0
  56. data/lib/dcc/base/rich_content.rb +34 -0
  57. data/lib/dcc/base/software.rb +35 -0
  58. data/lib/dcc/base/software_list.rb +27 -0
  59. data/lib/dcc/base/statement.rb +50 -0
  60. data/lib/dcc/base/statement_list.rb +27 -0
  61. data/lib/dcc/base/string_with_lang.rb +24 -0
  62. data/lib/dcc/base/text.rb +33 -0
  63. data/lib/dcc/base/used_method.rb +31 -0
  64. data/lib/dcc/base/used_method_list.rb +27 -0
  65. data/lib/dcc/base/xml_blob.rb +20 -0
  66. data/lib/dcc/base.rb +58 -0
  67. data/lib/dcc/builder/dsl.rb +10 -0
  68. data/lib/dcc/builder/session.rb +170 -0
  69. data/lib/dcc/builder.rb +34 -0
  70. data/lib/dcc/cli/cli.rb +149 -0
  71. data/lib/dcc/cli/formatters.rb +77 -0
  72. data/lib/dcc/cli.rb +7 -0
  73. data/lib/dcc/common_elements.rb +20 -0
  74. data/lib/dcc/context_configuration.rb +167 -0
  75. data/lib/dcc/context_options.rb +47 -0
  76. data/lib/dcc/convert/csv.rb +129 -0
  77. data/lib/dcc/convert/html.rb +127 -0
  78. data/lib/dcc/convert/json.rb +62 -0
  79. data/lib/dcc/convert/result.rb +51 -0
  80. data/lib/dcc/convert/yaml.rb +21 -0
  81. data/lib/dcc/convert.rb +14 -0
  82. data/lib/dcc/diff/change.rb +21 -0
  83. data/lib/dcc/diff/result.rb +58 -0
  84. data/lib/dcc/diff.rb +83 -0
  85. data/lib/dcc/error.rb +54 -0
  86. data/lib/dcc/extract/file.rb +143 -0
  87. data/lib/dcc/extract/ring.rb +28 -0
  88. data/lib/dcc/extract.rb +9 -0
  89. data/lib/dcc/i18n/text_lookup.rb +67 -0
  90. data/lib/dcc/i18n.rb +12 -0
  91. data/lib/dcc/inspect/report.rb +139 -0
  92. data/lib/dcc/inspect.rb +9 -0
  93. data/lib/dcc/migrate.rb +41 -0
  94. data/lib/dcc/namespace/dcc.rb +30 -0
  95. data/lib/dcc/namespace/dsig.rb +15 -0
  96. data/lib/dcc/namespace/math_ml.rb +14 -0
  97. data/lib/dcc/namespace/qudt.rb +15 -0
  98. data/lib/dcc/namespace/si.rb +26 -0
  99. data/lib/dcc/namespace.rb +18 -0
  100. data/lib/dcc/plugin/registry.rb +45 -0
  101. data/lib/dcc/plugin.rb +42 -0
  102. data/lib/dcc/quantity_format/formatter.rb +90 -0
  103. data/lib/dcc/quantity_format.rb +10 -0
  104. data/lib/dcc/quantity_math/quantity.rb +46 -0
  105. data/lib/dcc/quantity_math/real.rb +116 -0
  106. data/lib/dcc/quantity_math.rb +14 -0
  107. data/lib/dcc/schema/resources/dcc/dsi/v1.0.1.xsd +415 -0
  108. data/lib/dcc/schema/resources/dcc/dsi/v1.3.0.xsd +814 -0
  109. data/lib/dcc/schema/resources/dcc/dsi/v1.3.1.xsd +814 -0
  110. data/lib/dcc/schema/resources/dcc/dsi/v2.0.0.xsd +869 -0
  111. data/lib/dcc/schema/resources/dcc/dsi/v2.1.0.xsd +1031 -0
  112. data/lib/dcc/schema/resources/dcc/dsi/v2.2.1.xsd +1574 -0
  113. data/lib/dcc/schema/resources/dcc/qudt/v2.2.1.xsd +44 -0
  114. data/lib/dcc/schema/resources/dcc/v2.1.0/dcc.xsd +650 -0
  115. data/lib/dcc/schema/resources/dcc/v2.1.1/dcc.xsd +669 -0
  116. data/lib/dcc/schema/resources/dcc/v2.2.0/dcc.xsd +730 -0
  117. data/lib/dcc/schema/resources/dcc/v2.3.0/dcc.xsd +708 -0
  118. data/lib/dcc/schema/resources/dcc/v2.4.0/dcc.xsd +725 -0
  119. data/lib/dcc/schema/resources/dcc/v3.0.0/dcc.xsd +749 -0
  120. data/lib/dcc/schema/resources/dcc/v3.1.0/dcc.xsd +843 -0
  121. data/lib/dcc/schema/resources/dcc/v3.1.1/dcc.xsd +843 -0
  122. data/lib/dcc/schema/resources/dcc/v3.1.2/dcc.xsd +844 -0
  123. data/lib/dcc/schema/resources/dcc/v3.2.0/dcc.xsd +940 -0
  124. data/lib/dcc/schema/resources/dcc/v3.2.1/dcc.xsd +963 -0
  125. data/lib/dcc/schema/resources/dcc/v3.3.0/dcc.xsd +1008 -0
  126. data/lib/dcc/schema/resources/dcc/xmldsig-core-schema.xsd +312 -0
  127. data/lib/dcc/schema/resources/dsi/qudt/v2.2.1.xsd +44 -0
  128. data/lib/dcc/schema/resources/dsi/v1.0.1/SI_Format.xsd +415 -0
  129. data/lib/dcc/schema/resources/dsi/v1.3.0/SI_Format.xsd +814 -0
  130. data/lib/dcc/schema/resources/dsi/v1.3.1/SI_Format.xsd +814 -0
  131. data/lib/dcc/schema/resources/dsi/v2.0.0/SI_Format.xsd +869 -0
  132. data/lib/dcc/schema/resources/dsi/v2.1.0/SI_Format.xsd +1031 -0
  133. data/lib/dcc/schema/resources/dsi/v2.2.1/SI_Format.xsd +1574 -0
  134. data/lib/dcc/schema/resources/qudt/qudt.xsd +44 -0
  135. data/lib/dcc/schema/resources/schematron/dcc.sch +523 -0
  136. data/lib/dcc/schema/resources/schematron/dcc.svrl +2302 -0
  137. data/lib/dcc/schema/resources/xmldsig/xmldsig-core-schema.xsd +312 -0
  138. data/lib/dcc/schema/version.rb +94 -0
  139. data/lib/dcc/schema.rb +35 -0
  140. data/lib/dcc/server/app.rb +133 -0
  141. data/lib/dcc/server/storage/entry.rb +15 -0
  142. data/lib/dcc/server/storage/memory.rb +57 -0
  143. data/lib/dcc/server/storage.rb +13 -0
  144. data/lib/dcc/server.rb +33 -0
  145. data/lib/dcc/si/base/complex.rb +43 -0
  146. data/lib/dcc/si/base/complex_list_xml_list.rb +39 -0
  147. data/lib/dcc/si/base/constant.rb +39 -0
  148. data/lib/dcc/si/base/coverage_interval_mu.rb +31 -0
  149. data/lib/dcc/si/base/expanded_mu.rb +29 -0
  150. data/lib/dcc/si/base/expanded_unc_xml_list.rb +30 -0
  151. data/lib/dcc/si/base/hybrid.rb +42 -0
  152. data/lib/dcc/si/base/measurement_uncertainty_univariate.rb +28 -0
  153. data/lib/dcc/si/base/real.rb +48 -0
  154. data/lib/dcc/si/base/real_list_xml_list.rb +45 -0
  155. data/lib/dcc/si/base/si_list.rb +37 -0
  156. data/lib/dcc/si/base/standard_mu.rb +25 -0
  157. data/lib/dcc/si/base.rb +24 -0
  158. data/lib/dcc/si/common_elements.rb +13 -0
  159. data/lib/dcc/si/namespace.rb +7 -0
  160. data/lib/dcc/si/v1/common_elements.rb +13 -0
  161. data/lib/dcc/si/v1/complex.rb +8 -0
  162. data/lib/dcc/si/v1/complex_list_xml_list.rb +8 -0
  163. data/lib/dcc/si/v1/configuration.rb +13 -0
  164. data/lib/dcc/si/v1/constant.rb +8 -0
  165. data/lib/dcc/si/v1/coverage_interval_mu.rb +8 -0
  166. data/lib/dcc/si/v1/expanded_mu.rb +8 -0
  167. data/lib/dcc/si/v1/expanded_unc.rb +23 -0
  168. data/lib/dcc/si/v1/expanded_unc_xml_list.rb +8 -0
  169. data/lib/dcc/si/v1/hybrid.rb +8 -0
  170. data/lib/dcc/si/v1/measurement_uncertainty_univariate.rb +8 -0
  171. data/lib/dcc/si/v1/namespace.rb +9 -0
  172. data/lib/dcc/si/v1/real.rb +8 -0
  173. data/lib/dcc/si/v1/real_list_xml_list.rb +8 -0
  174. data/lib/dcc/si/v1/si_list.rb +8 -0
  175. data/lib/dcc/si/v1/standard_mu.rb +8 -0
  176. data/lib/dcc/si/v1.rb +46 -0
  177. data/lib/dcc/si/v2/common_elements.rb +13 -0
  178. data/lib/dcc/si/v2/complex.rb +8 -0
  179. data/lib/dcc/si/v2/complex_list_xml_list.rb +8 -0
  180. data/lib/dcc/si/v2/configuration.rb +13 -0
  181. data/lib/dcc/si/v2/constant.rb +8 -0
  182. data/lib/dcc/si/v2/coverage_interval_mu.rb +8 -0
  183. data/lib/dcc/si/v2/coverage_interval_mu_xml_list.rb +24 -0
  184. data/lib/dcc/si/v2/expanded_mu.rb +8 -0
  185. data/lib/dcc/si/v2/expanded_mu_xml_list.rb +22 -0
  186. data/lib/dcc/si/v2/expanded_unc_xml_list.rb +8 -0
  187. data/lib/dcc/si/v2/hybrid.rb +8 -0
  188. data/lib/dcc/si/v2/measurement_uncertainty_univariate.rb +8 -0
  189. data/lib/dcc/si/v2/namespace.rb +9 -0
  190. data/lib/dcc/si/v2/real.rb +8 -0
  191. data/lib/dcc/si/v2/real_list_xml_list.rb +8 -0
  192. data/lib/dcc/si/v2/si_list.rb +8 -0
  193. data/lib/dcc/si/v2/standard_mu.rb +8 -0
  194. data/lib/dcc/si/v2/standard_mu_xml_list.rb +18 -0
  195. data/lib/dcc/si/v2.rb +49 -0
  196. data/lib/dcc/si.rb +34 -0
  197. data/lib/dcc/signature/result.rb +43 -0
  198. data/lib/dcc/signature/signer.rb +36 -0
  199. data/lib/dcc/signature/verifier.rb +41 -0
  200. data/lib/dcc/signature.rb +13 -0
  201. data/lib/dcc/transform/result.rb +24 -0
  202. data/lib/dcc/transform/xslt.rb +72 -0
  203. data/lib/dcc/transform.rb +12 -0
  204. data/lib/dcc/type/base64_binary.rb +55 -0
  205. data/lib/dcc/type/decimal_xml_list.rb +47 -0
  206. data/lib/dcc/type/iso_country_code.rb +23 -0
  207. data/lib/dcc/type/iso_language_code.rb +23 -0
  208. data/lib/dcc/type/schema_version.rb +37 -0
  209. data/lib/dcc/type/si_unit.rb +26 -0
  210. data/lib/dcc/type.rb +16 -0
  211. data/lib/dcc/type_guards.rb +140 -0
  212. data/lib/dcc/v2/administrative_data.rb +8 -0
  213. data/lib/dcc/v2/byte_data.rb +8 -0
  214. data/lib/dcc/v2/calibration_laboratory.rb +8 -0
  215. data/lib/dcc/v2/comment.rb +8 -0
  216. data/lib/dcc/v2/common_elements.rb +13 -0
  217. data/lib/dcc/v2/condition.rb +8 -0
  218. data/lib/dcc/v2/configuration.rb +17 -0
  219. data/lib/dcc/v2/contact.rb +8 -0
  220. data/lib/dcc/v2/contact_not_strict.rb +8 -0
  221. data/lib/dcc/v2/core_data.rb +8 -0
  222. data/lib/dcc/v2/data.rb +8 -0
  223. data/lib/dcc/v2/digital_calibration_certificate.rb +8 -0
  224. data/lib/dcc/v2/equipment_class.rb +8 -0
  225. data/lib/dcc/v2/formula.rb +8 -0
  226. data/lib/dcc/v2/hash_type.rb +8 -0
  227. data/lib/dcc/v2/identification.rb +8 -0
  228. data/lib/dcc/v2/identifications.rb +8 -0
  229. data/lib/dcc/v2/influence_condition.rb +7 -0
  230. data/lib/dcc/v2/influence_condition_list.rb +8 -0
  231. data/lib/dcc/v2/item.rb +8 -0
  232. data/lib/dcc/v2/item_list.rb +8 -0
  233. data/lib/dcc/v2/list.rb +8 -0
  234. data/lib/dcc/v2/location.rb +8 -0
  235. data/lib/dcc/v2/measurement_meta_data.rb +8 -0
  236. data/lib/dcc/v2/measurement_meta_data_list.rb +8 -0
  237. data/lib/dcc/v2/measurement_result.rb +8 -0
  238. data/lib/dcc/v2/measurement_result_list.rb +8 -0
  239. data/lib/dcc/v2/measuring_equipment.rb +8 -0
  240. data/lib/dcc/v2/measuring_equipment_list.rb +8 -0
  241. data/lib/dcc/v2/namespace.rb +8 -0
  242. data/lib/dcc/v2/quantity.rb +49 -0
  243. data/lib/dcc/v2/ref_type_definition.rb +11 -0
  244. data/lib/dcc/v2/ref_type_definition_list.rb +15 -0
  245. data/lib/dcc/v2/resp_person.rb +8 -0
  246. data/lib/dcc/v2/resp_person_list.rb +8 -0
  247. data/lib/dcc/v2/result.rb +8 -0
  248. data/lib/dcc/v2/result_list.rb +8 -0
  249. data/lib/dcc/v2/rich_content.rb +12 -0
  250. data/lib/dcc/v2/software.rb +8 -0
  251. data/lib/dcc/v2/software_list.rb +8 -0
  252. data/lib/dcc/v2/statement.rb +8 -0
  253. data/lib/dcc/v2/statement_list.rb +8 -0
  254. data/lib/dcc/v2/string_with_lang.rb +8 -0
  255. data/lib/dcc/v2/text.rb +8 -0
  256. data/lib/dcc/v2/used_method.rb +8 -0
  257. data/lib/dcc/v2/used_method_list.rb +8 -0
  258. data/lib/dcc/v2/xml_blob.rb +8 -0
  259. data/lib/dcc/v2.rb +84 -0
  260. data/lib/dcc/v3/administrative_data.rb +8 -0
  261. data/lib/dcc/v3/byte_data.rb +8 -0
  262. data/lib/dcc/v3/calibration_laboratory.rb +8 -0
  263. data/lib/dcc/v3/comment.rb +8 -0
  264. data/lib/dcc/v3/common_elements.rb +11 -0
  265. data/lib/dcc/v3/condition.rb +8 -0
  266. data/lib/dcc/v3/configuration.rb +17 -0
  267. data/lib/dcc/v3/contact.rb +8 -0
  268. data/lib/dcc/v3/contact_not_strict.rb +8 -0
  269. data/lib/dcc/v3/core_data.rb +8 -0
  270. data/lib/dcc/v3/data.rb +8 -0
  271. data/lib/dcc/v3/digital_calibration_certificate.rb +8 -0
  272. data/lib/dcc/v3/equipment_class.rb +8 -0
  273. data/lib/dcc/v3/formula.rb +8 -0
  274. data/lib/dcc/v3/hash_type.rb +8 -0
  275. data/lib/dcc/v3/identification.rb +8 -0
  276. data/lib/dcc/v3/identifications.rb +8 -0
  277. data/lib/dcc/v3/influence_condition.rb +7 -0
  278. data/lib/dcc/v3/influence_condition_list.rb +8 -0
  279. data/lib/dcc/v3/item.rb +8 -0
  280. data/lib/dcc/v3/item_list.rb +8 -0
  281. data/lib/dcc/v3/list.rb +8 -0
  282. data/lib/dcc/v3/location.rb +8 -0
  283. data/lib/dcc/v3/measurement_meta_data.rb +8 -0
  284. data/lib/dcc/v3/measurement_meta_data_list.rb +8 -0
  285. data/lib/dcc/v3/measurement_result.rb +8 -0
  286. data/lib/dcc/v3/measurement_result_list.rb +8 -0
  287. data/lib/dcc/v3/measuring_equipment.rb +8 -0
  288. data/lib/dcc/v3/measuring_equipment_list.rb +8 -0
  289. data/lib/dcc/v3/namespace.rb +7 -0
  290. data/lib/dcc/v3/quantity.rb +49 -0
  291. data/lib/dcc/v3/ref_type_definition.rb +8 -0
  292. data/lib/dcc/v3/ref_type_definition_list.rb +8 -0
  293. data/lib/dcc/v3/resp_person.rb +8 -0
  294. data/lib/dcc/v3/resp_person_list.rb +8 -0
  295. data/lib/dcc/v3/result.rb +8 -0
  296. data/lib/dcc/v3/result_list.rb +8 -0
  297. data/lib/dcc/v3/rich_content.rb +8 -0
  298. data/lib/dcc/v3/software.rb +8 -0
  299. data/lib/dcc/v3/software_list.rb +8 -0
  300. data/lib/dcc/v3/statement.rb +8 -0
  301. data/lib/dcc/v3/statement_list.rb +8 -0
  302. data/lib/dcc/v3/string_with_lang.rb +8 -0
  303. data/lib/dcc/v3/text.rb +8 -0
  304. data/lib/dcc/v3/used_method.rb +8 -0
  305. data/lib/dcc/v3/used_method_list.rb +8 -0
  306. data/lib/dcc/v3/xml_blob.rb +8 -0
  307. data/lib/dcc/v3.rb +111 -0
  308. data/lib/dcc/validate/business_rules.rb +121 -0
  309. data/lib/dcc/validate/issue.rb +56 -0
  310. data/lib/dcc/validate/result.rb +105 -0
  311. data/lib/dcc/validate/schematron/profile.rb +64 -0
  312. data/lib/dcc/validate/schematron/rule.rb +49 -0
  313. data/lib/dcc/validate/schematron/rules/administrative_data_completeness.rb +42 -0
  314. data/lib/dcc/validate/schematron/rules/base.rb +15 -0
  315. data/lib/dcc/validate/schematron/rules/date_range_check.rb +41 -0
  316. data/lib/dcc/validate/schematron/rules/id_refid_linking.rb +61 -0
  317. data/lib/dcc/validate/schematron/rules/influence_conditions_placement.rb +58 -0
  318. data/lib/dcc/validate/schematron/rules/iso_code_validation.rb +53 -0
  319. data/lib/dcc/validate/schematron/rules/language_code_dedup.rb +39 -0
  320. data/lib/dcc/validate/schematron/rules/non_si_declaration.rb +82 -0
  321. data/lib/dcc/validate/schematron/rules/release_format_check.rb +44 -0
  322. data/lib/dcc/validate/schematron/rules/schema_version_check.rb +31 -0
  323. data/lib/dcc/validate/schematron/rules/uncertainty_consistency.rb +58 -0
  324. data/lib/dcc/validate/schematron/rules/unit_format_check.rb +74 -0
  325. data/lib/dcc/validate/schematron/rules/used_methods_placement.rb +65 -0
  326. data/lib/dcc/validate/schematron/rules/used_software_placement.rb +43 -0
  327. data/lib/dcc/validate/schematron/rules/xml_list_spacing.rb +42 -0
  328. data/lib/dcc/validate/schematron/rules.rb +30 -0
  329. data/lib/dcc/validate/schematron.rb +22 -0
  330. data/lib/dcc/validate/severity.rb +39 -0
  331. data/lib/dcc/validate/xsd.rb +97 -0
  332. data/lib/dcc/validate.rb +15 -0
  333. data/lib/dcc/version.rb +5 -0
  334. data/lib/dcc/versioned_parser.rb +48 -0
  335. data/lib/dcc.rb +141 -0
  336. metadata +425 -0
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Migrate` upgrades or downgrades a parsed DCC between schema versions,
4
+ # applying the necessary field renames and D-SI version transitions.
5
+ #
6
+ # Currently a thin shim: serializes the source DCC and re-parses under
7
+ # the target version's context. Future work will add per-field transformations.
8
+ module Dcc
9
+ module Migrate
10
+ class << self
11
+ # @param dcc [Lutaml::Model::Serializable]
12
+ # @param from [String] source version (e.g. "2.3.0").
13
+ # @param to [String] target version (e.g. "3.3.0").
14
+ # @return [Dcc::V2::DigitalCalibrationCertificate, Dcc::V3::DigitalCalibrationCertificate]
15
+ def call(dcc, from:, to:)
16
+ from_major = ::Dcc::Schema::Version.major(from)
17
+ to_major = ::Dcc::Schema::Version.major(to)
18
+ if from_major == to_major
19
+ # Same major: just rewrite the schemaVersion attribute.
20
+ return rewrite_schema_version(dcc, to)
21
+ end
22
+
23
+ # Cross-major migration: serialize then re-parse under target.
24
+ xml = dcc.to_xml
25
+ target_parser = ::Dcc.parser_for(to_major)
26
+ target_parser.load_all!
27
+ migrated = target_parser.parse(xml)
28
+ rewrite_schema_version(migrated, to)
29
+ end
30
+
31
+ private
32
+
33
+ def rewrite_schema_version(dcc, version)
34
+ return dcc unless Dcc::TypeGuards.has_writer?(dcc, :schema_version)
35
+
36
+ dcc.schema_version = version
37
+ dcc
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/xml"
4
+
5
+ module Dcc
6
+ module Namespace
7
+ # The DCC XML namespace. PTB publishes the canonical URI as
8
+ # `https://ptb.de/dcc`; older documents occasionally use the
9
+ # `https://ptb.de/dcc.xsd` alias which we accept via `uri_aliases`.
10
+ #
11
+ # Schema location is versioned (e.g. `https://ptb.de/dcc/v3.3.0/dcc.xsd`).
12
+ # The default points to the latest published version; per-version overrides
13
+ # happen in `Dcc::V2::Namespace` and `Dcc::V3::Namespace`.
14
+ class Dcc < Lutaml::Xml::Namespace
15
+ uri "https://ptb.de/dcc"
16
+ uri_aliases "https://ptb.de/dcc.xsd"
17
+ schema_location "https://ptb.de/dcc/v3.3.0/dcc.xsd"
18
+ prefix_default "dcc"
19
+ element_form_default :qualified
20
+ attribute_form_default :unqualified
21
+ version "3.3.0"
22
+
23
+ # DCC imports D-SI for quantity expressions, MathML for formulae, and
24
+ # (from v3.4.0) W3C XMLDSig for digital signatures.
25
+ imports ::Dcc::Namespace::Si,
26
+ ::Dcc::Namespace::MathMl,
27
+ ::Dcc::Namespace::Dsig
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/xml"
4
+
5
+ module Dcc
6
+ module Namespace
7
+ # W3C XML Digital Signature namespace. Imported by DCC v3.4.0+ to support
8
+ # `ds:Signature` elements on the root `digitalCalibrationCertificate`.
9
+ class Dsig < Lutaml::Xml::Namespace
10
+ uri "http://www.w3.org/2000/09/xmldsig#"
11
+ prefix_default "ds"
12
+ element_form_default :qualified
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/xml"
4
+
5
+ module Dcc
6
+ module Namespace
7
+ # W3C MathML namespace used inside DCC `dcc:formula` elements.
8
+ class MathMl < Lutaml::Xml::Namespace
9
+ uri "http://www.w3.org/1998/Math/MathML"
10
+ prefix_default "ml"
11
+ element_form_default :qualified
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/xml"
4
+
5
+ module Dcc
6
+ module Namespace
7
+ # QUDT vocabulary namespace. Referenced by D-SI for quantity-kind
8
+ # identifiers (e.g. `https://qudt.org/vocab/quantitykind/Length`).
9
+ class Qudt < Lutaml::Xml::Namespace
10
+ uri "http://qudt.org/vocab/"
11
+ prefix_default "qudt"
12
+ element_form_default :qualified
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/xml"
4
+
5
+ module Dcc
6
+ module Namespace
7
+ # The D-SI XML namespace (https://ptb.de/si). Used to express measurement
8
+ # quantities (real, complex, constant, list) and their uncertainties inside
9
+ # DCC documents.
10
+ #
11
+ # Schema location is versioned. The default points to the latest D-SI
12
+ # release; per-version overrides live in `Dcc::Si::V1::Namespace` and
13
+ # `Dcc::Si::V2::Namespace`.
14
+ class Si < Lutaml::Xml::Namespace
15
+ uri "https://ptb.de/si"
16
+ uri_aliases "https://ptb.de/si.xsd"
17
+ schema_location "https://ptb.de/si/v2.2.1/SI_Format.xsd"
18
+ prefix_default "si"
19
+ element_form_default :qualified
20
+ attribute_form_default :unqualified
21
+ version "2.2.1"
22
+
23
+ imports ::Dcc::Namespace::Qudt
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Namespace` aggregates the five XML namespaces used by DCC documents:
4
+ #
5
+ # * `dcc:` — the Digital Calibration Certificate itself (https://ptb.de/dcc)
6
+ # * `si:` — D-SI quantity values (https://ptb.de/si)
7
+ # * `ml:` — MathML formulae (http://www.w3.org/1998/Math/MathML)
8
+ # * `ds:` — W3C XML Digital Signature (http://www.w3.org/2000/09/xmldsig#)
9
+ # * `qudt:` — QUDT quantity kinds (http://qudt.org/vocab/)
10
+ module Dcc
11
+ module Namespace
12
+ autoload :Dcc, "dcc/namespace/dcc"
13
+ autoload :Si, "dcc/namespace/si"
14
+ autoload :MathMl, "dcc/namespace/math_ml"
15
+ autoload :Dsig, "dcc/namespace/dsig"
16
+ autoload :Qudt, "dcc/namespace/qudt"
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module Plugin
5
+ # Category-based registry. Each category (validators, converters,
6
+ # cli_commands) gets its own array. Thread-safe via a Mutex.
7
+ class Registry
8
+ @categories = ::Hash.new { |h, k| h[k] = [] }
9
+ @mutex = ::Mutex.new
10
+
11
+ class << self
12
+ # @param category [Symbol]
13
+ # @param entry [Class, Object]
14
+ # @return [Class, Object]
15
+ def register(category, entry)
16
+ @mutex.synchronize do
17
+ @categories[category.to_sym] << entry
18
+ entry
19
+ end
20
+ end
21
+
22
+ # @param category [Symbol]
23
+ # @return [Array<Object>]
24
+ def all(category)
25
+ @mutex.synchronize { @categories[category.to_sym].dup }
26
+ end
27
+
28
+ # @param category [Symbol]
29
+ # @return [Boolean]
30
+ def any?(category)
31
+ @mutex.synchronize { !@categories[category.to_sym].empty? }
32
+ end
33
+
34
+ def reset!
35
+ @mutex.synchronize { @categories.clear }
36
+ end
37
+
38
+ # @return [Array<Symbol>] the registered category names.
39
+ def categories
40
+ @mutex.synchronize { @categories.keys.dup }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/dcc/plugin.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Plugin` is the registry-based extension point for the gem.
4
+ # Plugins can register custom validators, converters, or CLI commands
5
+ # without modifying the core codebase (Open/Closed Principle).
6
+ #
7
+ # @example Register a custom validator
8
+ # Dcc::Plugin.register(:validators, MyCustomRule)
9
+ #
10
+ # @example List all registered validators
11
+ # Dcc::Plugin.all(:validators)
12
+ module Dcc
13
+ module Plugin
14
+ autoload :Registry, "dcc/plugin/registry"
15
+
16
+ class << self
17
+ # @param category [Symbol] e.g. :validators, :converters, :cli_commands.
18
+ # @param entry [Class, Object] the plugin to register.
19
+ # @return [Class, Object] the entry.
20
+ def register(category, entry)
21
+ ::Dcc::Plugin::Registry.register(category, entry)
22
+ end
23
+
24
+ # @param category [Symbol]
25
+ # @return [Array<Object>]
26
+ def all(category)
27
+ ::Dcc::Plugin::Registry.all(category)
28
+ end
29
+
30
+ # @param category [Symbol]
31
+ # @return [Boolean]
32
+ def any?(category)
33
+ ::Dcc::Plugin::Registry.any?(category)
34
+ end
35
+
36
+ # Reset the registry (test-only helper).
37
+ def reset!
38
+ ::Dcc::Plugin::Registry.reset!
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module QuantityFormat
5
+ # SmartCom-style quantity pretty-printing. Significant digits derived
6
+ # from uncertainty (PTB convention: 2 sig digits in uncertainty).
7
+ class Formatter
8
+ attr_reader :value, :uncertainty, :unit
9
+
10
+ # @param value [BigDecimal, Numeric]
11
+ # @param uncertainty [BigDecimal, Numeric, nil]
12
+ # @param unit [String, nil] siunitx expression
13
+ def initialize(value:, uncertainty: nil, unit: nil)
14
+ @value = BigDecimal(value.to_s)
15
+ @uncertainty = uncertainty.nil? ? nil : BigDecimal(uncertainty.to_s)
16
+ @unit = unit
17
+ end
18
+
19
+ # @return [String] compact notation: `42.00(5) K`
20
+ def to_short
21
+ return format_value.to_s + unit_suffix if uncertainty.nil?
22
+
23
+ v_str, u_str = align_value_and_uncertainty
24
+ "#{v_str}(#{u_str})#{unit_suffix}"
25
+ end
26
+
27
+ # @return [String] verbose: `42.00 ± 0.05 K`
28
+ def to_long
29
+ if uncertainty.nil?
30
+ "#{format_value}#{unit_suffix}"
31
+ else
32
+ "#{format_value} ± #{format_uncertainty}#{unit_suffix}"
33
+ end
34
+ end
35
+
36
+ # @return [String] LaTeX siunitx: `\qty{42.00 +- 0.05}{\kelvin}`
37
+ def to_latex
38
+ if uncertainty.nil?
39
+ "\\qty{#{format_value}}{#{unit || ''}}"
40
+ else
41
+ "\\qty{#{format_value} +- #{format_uncertainty}}{#{unit || ''}}"
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def format_value
48
+ # Two decimals by default; tightened to uncertainty precision when known.
49
+ return @value.round(2).to_s("F") if uncertainty.nil?
50
+
51
+ # Decimal places = 1 - floor(log10(uncertainty))
52
+ decimal_places = [0, 1 - Integer(Math.log10(uncertainty.to_f).floor)].max
53
+ @value.round(decimal_places).to_s("F")
54
+ end
55
+
56
+ def format_uncertainty
57
+ return "" if uncertainty.nil?
58
+
59
+ decimal_places = [0, 1 - Integer(Math.log10(uncertainty.to_f).floor)].max
60
+ uncertainty.round(decimal_places).to_s("F")
61
+ end
62
+
63
+ def align_value_and_uncertainty
64
+ return [@value.to_s("F"), uncertainty.to_s("F")] if uncertainty.nil?
65
+
66
+ decimal_places = [0, 1 - Integer(Math.log10(uncertainty.to_f).floor)].max
67
+ v_str = @value.round(decimal_places).to_s("F")
68
+ u_str = uncertainty.round(decimal_places).to_s("F")
69
+ # Short form: digits of uncertainty after the last common digit
70
+ last_digits = u_str.gsub(/[^0-9]/, "")[-2..]
71
+ [v_str, last_digits]
72
+ end
73
+
74
+ def unit_suffix
75
+ return "" if unit.nil? || unit.empty?
76
+
77
+ " #{human_unit}"
78
+ end
79
+
80
+ def human_unit
81
+ unit
82
+ .to_s
83
+ .gsub(/\\([a-z]+)/, '\\1') # \kelvin → kelvin
84
+ .gsub(/\\per/, '/')
85
+ .gsub(/\\cubic/, '^3')
86
+ .gsub(/\\square/, '^2')
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::QuantityFormat` renders quantities in human-readable form. Three
4
+ # formats: `:short` (compact, e.g. `42.00(5) K`), `:long` (verbose), and
5
+ # `:latex` (siunitx).
6
+ module Dcc
7
+ module QuantityFormat
8
+ autoload :Formatter, "dcc/quantity_format/formatter"
9
+ end
10
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+
5
+ module Dcc
6
+ module QuantityMath
7
+ # A simple `(value, unit, uncertainty)` value object used by
8
+ # `Dcc::QuantityMath::Real` for arithmetic. Immutable.
9
+ class Quantity
10
+ attr_reader :value, :unit, :uncertainty
11
+
12
+ # @param value [BigDecimal, Numeric]
13
+ # @param unit [String, nil] siunitx expression
14
+ # @param uncertainty [BigDecimal, Numeric, nil] 1σ absolute uncertainty
15
+ def initialize(value:, unit: nil, uncertainty: nil)
16
+ @value = cast_decimal(value)
17
+ @unit = unit
18
+ @uncertainty = uncertainty.nil? ? nil : cast_decimal(uncertainty)
19
+ end
20
+
21
+ # @return [Boolean] whether the quantity carries uncertainty info.
22
+ def uncertain?
23
+ !uncertainty.nil?
24
+ end
25
+
26
+ # @return [Boolean] whether units match (or both are nil).
27
+ def same_unit?(other)
28
+ unit == other.unit
29
+ end
30
+
31
+ def to_s
32
+ u = uncertain? ? " ± #{uncertainty}" : ""
33
+ n = unit ? " #{unit}" : ""
34
+ "#{value}#{u}#{n}"
35
+ end
36
+
37
+ private
38
+
39
+ def cast_decimal(value)
40
+ return value if value.is_a?(::BigDecimal)
41
+
42
+ BigDecimal(value.to_s)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+
5
+ module Dcc
6
+ module QuantityMath
7
+ # BigDecimal arithmetic on real-valued quantities with RSS uncertainty
8
+ # propagation for add/sub and fractional propagation for mul/div.
9
+ class Real < Quantity
10
+ # @return [Real]
11
+ def +(other)
12
+ ensure_same_unit!(other)
13
+ Real.new(
14
+ value: value + other.value,
15
+ unit: unit,
16
+ uncertainty: rss_uncertainty(other),
17
+ )
18
+ end
19
+
20
+ # @return [Real]
21
+ def -(other)
22
+ ensure_same_unit!(other)
23
+ Real.new(
24
+ value: value - other.value,
25
+ unit: unit,
26
+ uncertainty: rss_uncertainty(other),
27
+ )
28
+ end
29
+
30
+ # @return [Real]
31
+ def *(other)
32
+ new_value = value * other.value
33
+ new_unit = compose_unit(unit, other.unit)
34
+ Real.new(
35
+ value: new_value,
36
+ unit: new_unit,
37
+ uncertainty: fractional_uncertainty_mul_div(other, new_value),
38
+ )
39
+ end
40
+
41
+ # @return [Real]
42
+ def /(other)
43
+ new_value = value / other.value
44
+ new_unit = compose_unit(unit, inverse_unit(other.unit))
45
+ Real.new(
46
+ value: new_value,
47
+ unit: new_unit,
48
+ uncertainty: fractional_uncertainty_mul_div(other, new_value),
49
+ )
50
+ end
51
+
52
+ # @param exponent [Integer]
53
+ # @return [Real]
54
+ def **(exponent)
55
+ new_value = value ** exponent
56
+ Real.new(
57
+ value: new_value,
58
+ unit: unit && "^#{exponent}",
59
+ uncertainty: power_uncertainty(exponent, new_value),
60
+ )
61
+ end
62
+
63
+ private
64
+
65
+ def ensure_same_unit!(other)
66
+ return if same_unit?(other)
67
+
68
+ raise ::ArgumentError,
69
+ "unit mismatch: #{unit.inspect} vs #{other.unit.inspect}"
70
+ end
71
+
72
+ # Root-sum-square for add/sub: σ_result = sqrt(σ1² + σ2²)
73
+ def rss_uncertainty(other)
74
+ return nil unless uncertain? && other.uncertain?
75
+ return other.uncertainty if uncertainty.nil?
76
+ return uncertainty if other.uncertainty.nil?
77
+
78
+ sum_of_squares = (uncertainty ** 2) + (other.uncertainty ** 2)
79
+ BigDecimal(Math.sqrt(sum_of_squares.to_f).to_s)
80
+ end
81
+
82
+ # Fractional for mul/div: σ_y / y = sqrt((σ_a/a)² + (σ_b/b)²)
83
+ def fractional_uncertainty_mul_div(other, new_value)
84
+ return nil unless uncertain? && other.uncertain?
85
+ return nil if value.zero? || other.value.zero?
86
+
87
+ a_rel = uncertainty / value
88
+ b_rel = other.uncertainty / other.value
89
+ sum_of_squares = (a_rel ** 2) + (b_rel ** 2)
90
+ BigDecimal(Math.sqrt(sum_of_squares.to_f).to_s) * new_value.abs
91
+ end
92
+
93
+ # Power rule: σ_y = |n * x^(n-1)| * σ_x
94
+ def power_uncertainty(exponent, new_value)
95
+ return nil unless uncertain?
96
+
97
+ derivative = exponent * (value ** (exponent - 1))
98
+ (derivative.abs * uncertainty)
99
+ end
100
+
101
+ def compose_unit(u1, u2)
102
+ return nil if u1.nil? && u2.nil?
103
+ return u1 if u2.nil?
104
+ return u2 if u1.nil?
105
+
106
+ "#{u1}\\cdot#{u2}"
107
+ end
108
+
109
+ def inverse_unit(u)
110
+ return nil if u.nil?
111
+
112
+ "#{u}^-1"
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+ require "bigdecimal/math"
5
+
6
+ module Dcc
7
+ # `Dcc::QuantityMath` provides BigDecimal-based arithmetic on D-SI
8
+ # quantities with proper uncertainty propagation. RSS (root-sum-square)
9
+ # for addition/subtraction, fractional for multiplication/division.
10
+ module QuantityMath
11
+ autoload :Quantity, "dcc/quantity_math/quantity"
12
+ autoload :Real, "dcc/quantity_math/real"
13
+ end
14
+ end