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,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Dcc
6
+ module Convert
7
+ module Yaml
8
+ class << self
9
+ def call(dcc)
10
+ hash = ::Dcc::Convert::Json.xml_to_hash(dcc.to_xml)
11
+ ::Dcc::Convert::Result.new(
12
+ format: :yaml,
13
+ payload: ::YAML.dump(hash),
14
+ source_class: dcc.class.name,
15
+ schema_version: dcc.schema_version.to_s,
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::Convert` is the entry-point module for format converters. Each
4
+ # converter accepts a parsed DCC object tree and returns a `Dcc::Convert::Result`
5
+ # model that can be rendered as text, JSON, or YAML.
6
+ module Dcc
7
+ module Convert
8
+ autoload :Result, "dcc/convert/result"
9
+ autoload :Json, "dcc/convert/json"
10
+ autoload :Yaml, "dcc/convert/yaml"
11
+ autoload :Csv, "dcc/convert/csv"
12
+ autoload :Html, "dcc/convert/html"
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module Diff
5
+ # A single structural change between two DCC object trees.
6
+ Change = ::Struct.new(:path, :kind, :before, :after) do
7
+ def added? = kind == :add
8
+ def removed? = kind == :remove
9
+ def changed? = kind == :change
10
+
11
+ def to_s
12
+ case kind
13
+ when :add then "+ #{path}: #{after}"
14
+ when :remove then "- #{path}: #{before}"
15
+ when :change then "~ #{path}: #{before} -> #{after}"
16
+ else path
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module Diff
5
+ # Result of comparing two DCC object trees.
6
+ class Result
7
+ attr_reader :changes
8
+
9
+ def initialize(changes:)
10
+ @changes = changes
11
+ end
12
+
13
+ def additions
14
+ changes.select(&:added?)
15
+ end
16
+
17
+ def removals
18
+ changes.select(&:removed?)
19
+ end
20
+
21
+ def modifications
22
+ changes.select(&:changed?)
23
+ end
24
+
25
+ def empty?
26
+ changes.empty?
27
+ end
28
+
29
+ def to_s
30
+ if empty?
31
+ "(no structural changes)"
32
+ else
33
+ header = "#{changes.size} change(s): " \
34
+ "#{additions.size} added, #{removals.size} removed, #{modifications.size} changed"
35
+ ([header] + changes.map(&:to_s)).join("\n")
36
+ end
37
+ end
38
+
39
+ def to_json(*_args)
40
+ require "json"
41
+ ::JSON.pretty_generate(
42
+ additions: additions.map { |c| { path: c.path, value: c.after } },
43
+ removals: removals.map { |c| { path: c.path, value: c.before } },
44
+ modifications: modifications.map { |c| { path: c.path, before: c.before, after: c.after } },
45
+ )
46
+ end
47
+
48
+ def to_yaml(*_args)
49
+ require "yaml"
50
+ {
51
+ additions: additions.map { |c| { path: c.path, value: c.after } },
52
+ removals: removals.map { |c| { path: c.path, value: c.before } },
53
+ modifications: modifications.map { |c| { path: c.path, before: c.before, after: c.after } },
54
+ }.to_yaml
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/dcc/diff.rb ADDED
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+ require "lutaml/model"
5
+
6
+ module Dcc
7
+ # `Dcc::Diff` compares two parsed DCC object trees and returns a
8
+ # `Dcc::Diff::Result` describing structural changes (counts, additions,
9
+ # removals, modifications). Identity is determined by `uniqueIdentifier`
10
+ # for the top-level DCC and by element `@id` / `@refId` for nested items.
11
+ module Diff
12
+ autoload :Result, "dcc/diff/result"
13
+ autoload :Change, "dcc/diff/change"
14
+
15
+ class << self
16
+ # @param a [Lutaml::Model::Serializable]
17
+ # @param b [Lutaml::Model::Serializable]
18
+ # @return [Dcc::Diff::Result]
19
+ def call(a, b)
20
+ changes = []
21
+ collect_differences(a, b, "", changes)
22
+ Result.new(changes: changes)
23
+ end
24
+
25
+ private
26
+
27
+ def collect_differences(a, b, path, changes)
28
+ # Both nil - no diff
29
+ return if a.nil? && b.nil?
30
+ # One is nil - add/remove
31
+ if a.nil?
32
+ changes << Change.new(path: path, kind: :add, before: nil, after: summarize(b))
33
+ return
34
+ end
35
+ if b.nil?
36
+ changes << Change.new(path: path, kind: :remove, before: summarize(a), after: nil)
37
+ return
38
+ end
39
+
40
+ # Both primitives - compare values
41
+ if primitive?(a) || primitive?(b)
42
+ if a != b
43
+ changes << Change.new(path: path, kind: :change, before: a, after: b)
44
+ end
45
+ return
46
+ end
47
+
48
+ # Both arrays
49
+ if a.is_a?(::Array) || b.is_a?(::Array)
50
+ max = [a.size, b.size].max
51
+ max.times do |i|
52
+ collect_differences(a[i], b[i], "#{path}[#{i}]", changes)
53
+ end
54
+ return
55
+ end
56
+
57
+ # Both objects
58
+ keys = (a.class.attributes.keys | b.class.attributes.keys)
59
+ keys.each do |key|
60
+ av = Dcc::TypeGuards.has_attribute?(a, key) ? a.public_send(key) : nil
61
+ bv = Dcc::TypeGuards.has_attribute?(b, key) ? b.public_send(key) : nil
62
+ collect_differences(av, bv, "#{path}/#{key}", changes)
63
+ end
64
+ end
65
+
66
+ def primitive?(value)
67
+ return true if value.nil?
68
+ return true if value.is_a?(::String) || value.is_a?(::Numeric) || value.is_a?(::Symbol)
69
+ return true if value == true || value == false
70
+ return true if value.is_a?(::Time) || value.is_a?(::Date) || value.is_a?(::DateTime)
71
+ return true if value.is_a?(::BigDecimal)
72
+
73
+ false
74
+ end
75
+
76
+ def summarize(node)
77
+ return node.to_s if primitive?(node)
78
+
79
+ "#{node.class.name.split('::').last}"
80
+ end
81
+ end
82
+ end
83
+ end
data/lib/dcc/error.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ # Base error class for all dcc gem errors. Subclasses live directly under
5
+ # `Dcc::Error::*` (e.g. `Dcc::ParseError`).
6
+ class Error < StandardError; end
7
+
8
+ # Raised when an XML document cannot be parsed (malformed XML, encoding
9
+ # issues, IO errors).
10
+ class ParseError < Error; end
11
+
12
+ # Base class for all validation failures (XSD, Schematron, business rules).
13
+ class ValidationError < Error
14
+ attr_reader :issues
15
+
16
+ def initialize(message, issues: [])
17
+ @issues = issues
18
+ super(message)
19
+ end
20
+ end
21
+
22
+ # Raised when an XSD validation fails or the XSD itself cannot be loaded.
23
+ class SchemaError < ValidationError; end
24
+
25
+ # Raised when XMLDSig signing or verification fails.
26
+ class SignatureError < Error; end
27
+
28
+ # Raised when a converter cannot produce the requested output format.
29
+ class ConversionError < Error; end
30
+
31
+ # Raised when an extractor encounters malformed content.
32
+ class ExtractionError < Error; end
33
+
34
+ # Raised when an XSLT transform fails.
35
+ class TransformError < Error; end
36
+
37
+ # Raised when an unsupported schema version is requested.
38
+ class UnknownVersionError < Error; end
39
+
40
+ # Raised when the builder DSL is used incorrectly.
41
+ class BuilderError < Error; end
42
+
43
+ # Raised when a soft dependency (`xmldsig`, `sinatra`, etc.) is required
44
+ # but not installed.
45
+ class MissingDependencyError < Error
46
+ attr_reader :gem_name, :feature
47
+
48
+ def initialize(gem_name:, feature:)
49
+ @gem_name = gem_name
50
+ @feature = feature
51
+ super("The '#{gem_name}' gem is required for #{feature}. Add it to your Gemfile or run: gem install #{gem_name}")
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "set"
5
+
6
+ module Dcc
7
+ module Extract
8
+ # `Dcc::Extract::File` enumerates embedded files (`dcc:byteDataType`
9
+ # elements) inside a parsed DCC. Each entry carries the decoded binary
10
+ # payload, MIME type, file name, and the surrounding ring.
11
+ class File
12
+ attr_reader :name, :file_name, :mime_type, :data, :ring
13
+
14
+ # @param name [String, nil] human-readable name (typically from `dcc:name`).
15
+ # @param file_name [String, nil] original file name.
16
+ # @param mime_type [String, nil] MIME type (e.g. `text/plain`).
17
+ # @param data [String] binary payload (decoded from base64).
18
+ # @param ring [Symbol] one of `Dcc::Extract::Ring::ALL`.
19
+ def initialize(name:, file_name:, mime_type:, data:, ring:)
20
+ @name = name
21
+ @file_name = file_name
22
+ @mime_type = mime_type
23
+ @data = data
24
+ @ring = ring
25
+ end
26
+
27
+ # @return [Boolean] true if the embedded payload is textual (text/*).
28
+ def text?
29
+ mime_type.to_s.start_with?("text/")
30
+ end
31
+
32
+ def to_s
33
+ "File #{file_name || '(unnamed)'} [#{ring}] (#{mime_type}, #{data.bytesize} bytes)"
34
+ end
35
+
36
+ class << self
37
+ # Walk a parsed DCC and yield each embedded file in document order.
38
+ # Detects cycles (recursive list) via object_id tracking so the walk
39
+ # terminates for self-referential subtrees.
40
+ # @param dcc [Lutaml::Model::Serializable]
41
+ # @yieldparam [Dcc::Extract::File]
42
+ # @return [Array<Dcc::Extract::File>] the list when no block given.
43
+ def each(dcc, &block)
44
+ list = []
45
+ visited = ::Set.new
46
+
47
+ collect = lambda do |node, ring|
48
+ return unless node.is_a?(::Lutaml::Model::Serializable)
49
+ return if visited.include?(node.object_id)
50
+
51
+ visited << node.object_id
52
+
53
+ new_ring =
54
+ if admin_data?(node)
55
+ Ring::ADMINISTRATIVE_DATA
56
+ elsif measurement_list?(node)
57
+ Ring::MEASUREMENT_RESULTS
58
+ else
59
+ ring
60
+ end
61
+ list << build_file(node, new_ring) if byte_data?(node)
62
+ walk_children(node, new_ring, collect)
63
+ end
64
+
65
+ collect.call(dcc, nil)
66
+ return list unless block_given?
67
+
68
+ list.each(&block)
69
+ end
70
+
71
+ # @param dcc [Lutaml::Model::Serializable]
72
+ # @param index [Integer] zero-based index.
73
+ # @return [Dcc::Extract::File, nil]
74
+ def at(dcc, index)
75
+ each(dcc)[index]
76
+ end
77
+
78
+ # @param dcc [Lutaml::Model::Serializable]
79
+ # @param ring [Symbol] one of Ring::ALL.
80
+ # @return [Array<Dcc::Extract::File>]
81
+ def in_ring(dcc, ring:)
82
+ each(dcc).select { |f| f.ring == ring }
83
+ end
84
+
85
+ private
86
+
87
+ def admin_data?(node)
88
+ node.class.name.end_with?("::AdministrativeData")
89
+ end
90
+
91
+ def measurement_list?(node)
92
+ node.class.name.end_with?("::MeasurementResultList")
93
+ end
94
+
95
+ def byte_data?(node)
96
+ node.class.name.end_with?("::ByteData")
97
+ end
98
+
99
+ # Recursive walk: for each attribute, get its value and descend.
100
+ # Handles both single objects and collection arrays.
101
+ def walk_children(node, ring, walker)
102
+ node.class.attributes.each_key do |attr_name|
103
+ values = node.public_send(attr_name)
104
+ next if values.nil?
105
+
106
+ if values.is_a?(::Array)
107
+ walk_values(values, ring, walker)
108
+ elsif values.is_a?(::Lutaml::Model::Serializable)
109
+ walker.call(values, ring)
110
+ end
111
+ end
112
+ end
113
+
114
+ def walk_values(values, ring, walker)
115
+ Array(values).each do |v|
116
+ walker.call(v, ring) if v.is_a?(::Lutaml::Model::Serializable)
117
+ end
118
+ end
119
+
120
+ def build_file(byte_data, ring)
121
+ name = extract_name(byte_data.name)
122
+ new(
123
+ name: name,
124
+ file_name: byte_data.file_name,
125
+ mime_type: byte_data.mime_type,
126
+ data: ::Dcc::Type::Base64Binary.decode(byte_data.data_base64),
127
+ ring: ring || Ring::ADMINISTRATIVE_DATA,
128
+ )
129
+ end
130
+
131
+ def extract_name(name_obj)
132
+ return nil unless name_obj
133
+
134
+ first = name_obj.content.first
135
+ return nil unless first
136
+
137
+ values = first.value
138
+ values.is_a?(::Array) ? values.first : values
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module Extract
5
+ # The "Ring" of a `dcc:byteDataType` element: which section of the DCC
6
+ # document contained it. PTB defines these as `administrativeData`,
7
+ # `measurementResults`, `comment`, and `document`.
8
+ module Ring
9
+ ADMINISTRATIVE_DATA = :administrativeData
10
+ MEASUREMENT_RESULTS = :measurementResults
11
+ COMMENT = :comment
12
+ DOCUMENT = :document
13
+
14
+ ALL = [ADMINISTRATIVE_DATA, MEASUREMENT_RESULTS, COMMENT, DOCUMENT].freeze
15
+
16
+ class << self
17
+ # @param symbol_or_string [Symbol, String, nil]
18
+ # @return [Symbol, nil] the canonical ring symbol.
19
+ def normalize(symbol_or_string)
20
+ return nil if symbol_or_string.nil?
21
+
22
+ sym = symbol_or_string.to_sym
23
+ ALL.include?(sym) ? sym : nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module Extract
5
+ autoload :File, "dcc/extract/file"
6
+ autoload :Ring, "dcc/extract/ring"
7
+ autoload :Formula, "dcc/extract/formula"
8
+ end
9
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dcc
4
+ module I18n
5
+ # `Dcc::I18n::TextLookup` extracts the best-matching localized string
6
+ # from a `dcc:textType` block.
7
+ #
8
+ # Selection order:
9
+ # 1. Exact match for the requested language
10
+ # 2. First mandatory language declared in the DCC coreData
11
+ # 3. First used language declared in the DCC coreData
12
+ # 4. First content element regardless of language
13
+ # 5. nil if no content at all
14
+ module TextLookup
15
+ class << self
16
+ # @param text_obj [Object, nil] a parsed `dcc:textType` block.
17
+ # @param dcc [Object, nil] the parent DCC (used to read declared langs).
18
+ # @param lang [String, nil] requested ISO 639-1 code.
19
+ # @return [String, nil]
20
+ def call(text_obj, dcc: nil, lang: nil)
21
+ return nil unless text_obj && Dcc::TypeGuards.has_attribute?(text_obj, :content)
22
+
23
+ contents = Array(text_obj.content)
24
+ return nil if contents.empty?
25
+
26
+ return content_value(contents, lang) if lang
27
+
28
+ dcc_langs = declared_languages(dcc)
29
+ dcc_langs.each do |fallback|
30
+ val = content_value(contents, fallback)
31
+ return val if val
32
+ end
33
+
34
+ content_value(contents, nil)
35
+ end
36
+
37
+ private
38
+
39
+ def content_value(contents, lang)
40
+ match = if lang
41
+ contents.find { |c| c.lang.to_s == lang.to_s }
42
+ else
43
+ contents.first
44
+ end
45
+ return nil unless match
46
+
47
+ values = Array(match.value)
48
+ values.first&.to_s
49
+ end
50
+
51
+ def declared_languages(dcc)
52
+ return [] unless dcc && Dcc::TypeGuards.has_attribute?(dcc, :administrative_data)
53
+
54
+ admin = dcc.administrative_data
55
+ return [] unless admin && Dcc::TypeGuards.has_attribute?(admin, :core_data)
56
+
57
+ core = admin.core_data
58
+ return [] unless core
59
+
60
+ mandatory = Array(core.mandatory_lang_code_iso_639_1).map(&:to_s)
61
+ used = Array(core.used_lang_code_iso_639_1).map(&:to_s)
62
+ (mandatory + used).uniq
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
data/lib/dcc/i18n.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `Dcc::I18n` provides helpers for navigating multilingual DCC content.
4
+ # PTB DCC documents carry `usedLangCodeISO639_1` and
5
+ # `mandatoryLangCodeISO639_1` attributes; text blocks (`dcc:textType`)
6
+ # contain one `dcc:content` element per language. These helpers pick the
7
+ # best-matching language with a configurable fallback chain.
8
+ module Dcc
9
+ module I18n
10
+ autoload :TextLookup, "dcc/i18n/text_lookup"
11
+ end
12
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Dcc
6
+ module Inspect
7
+ # `Dcc::Inspect::Report` summarizes a parsed DCC: counts, key
8
+ # identifiers, signature presence, and a human-readable `to_s` /
9
+ # `to_json` / `to_yaml`.
10
+ class Report < Lutaml::Model::Serializable
11
+ attribute :schema_version, :string
12
+ attribute :unique_identifier, :string
13
+ attribute :country_code, :string
14
+ attribute :item_count, :integer
15
+ attribute :measurement_result_count, :integer
16
+ attribute :quantity_count, :integer
17
+ attribute :list_count, :integer
18
+ attribute :embedded_file_count, :integer
19
+ attribute :languages, :string, collection: true
20
+ attribute :signer_count, :integer
21
+ attribute :has_signature, :boolean
22
+
23
+ def to_s
24
+ lines = []
25
+ lines << "DCC Inspection Report"
26
+ lines << "=" * 50
27
+ lines << "Schema version: #{schema_version}"
28
+ lines << "Unique identifier: #{unique_identifier}"
29
+ lines << "Country: #{country_code}"
30
+ lines << "Languages: #{languages.join(', ')}" if languages.any?
31
+ lines << "Items: #{item_count}"
32
+ lines << "Measurement results: #{measurement_result_count}"
33
+ lines << "Quantities: #{quantity_count}"
34
+ lines << "Lists: #{list_count}"
35
+ lines << "Embedded files: #{embedded_file_count}"
36
+ lines << "Signers: #{signer_count}"
37
+ lines << "Has signature: #{has_signature ? 'yes' : 'no'}"
38
+ lines.join("\n")
39
+ end
40
+
41
+ def to_json(*_args)
42
+ require "json"
43
+ payload = {}
44
+ self.class.attributes.each_key { |k| payload[k] = public_send(k) }
45
+ ::JSON.pretty_generate(payload)
46
+ end
47
+
48
+ def to_yaml(*_args)
49
+ require "yaml"
50
+ payload = {}
51
+ self.class.attributes.each_key { |k| payload[k] = public_send(k) }
52
+ payload.to_yaml
53
+ end
54
+
55
+ class << self
56
+ # @param dcc [Lutaml::Model::Serializable]
57
+ # @return [Dcc::Inspect::Report]
58
+ def call(dcc)
59
+ admin = admin_data(dcc)
60
+ mr_list = measurement_results(dcc)
61
+ new(
62
+ schema_version: dcc.schema_version.to_s,
63
+ unique_identifier: core_data(dcc)&.unique_identifier,
64
+ country_code: core_data(dcc)&.country_code_iso_3166_1.to_s,
65
+ item_count: items(dcc).size,
66
+ measurement_result_count: mr_list.size,
67
+ quantity_count: count_quantities(mr_list),
68
+ list_count: count_lists(mr_list),
69
+ embedded_file_count: ::Dcc::Extract::File.each(dcc).size,
70
+ languages: lang_codes(dcc),
71
+ signer_count: resp_persons(dcc).size,
72
+ has_signature: signature_present?(dcc),
73
+ )
74
+ end
75
+
76
+ private
77
+
78
+ def admin_data(dcc)
79
+ Dcc::TypeGuards.has_attribute?(dcc, :administrative_data) ? dcc.administrative_data : nil
80
+ end
81
+
82
+ def core_data(dcc)
83
+ ad = admin_data(dcc)
84
+ ad && Dcc::TypeGuards.has_attribute?(ad, :core_data) ? ad.core_data : nil
85
+ end
86
+
87
+ def items(dcc)
88
+ ad = admin_data(dcc)
89
+ return [] unless ad && Dcc::TypeGuards.has_attribute?(ad, :items) && ad.items && Dcc::TypeGuards.has_attribute?(ad.items, :item)
90
+
91
+ Array(ad.items.item)
92
+ end
93
+
94
+ def measurement_results(dcc)
95
+ Dcc::TypeGuards.has_attribute?(dcc, :measurement_results) && dcc.measurement_results && Dcc::TypeGuards.has_attribute?(dcc.measurement_results, :measurement_result) ?
96
+ Array(dcc.measurement_results.measurement_result) : []
97
+ end
98
+
99
+ def count_quantities(mr_list)
100
+ mr_list.sum do |mr|
101
+ next 0 unless Dcc::TypeGuards.has_attribute?(mr, :results) && mr.results && Dcc::TypeGuards.has_attribute?(mr.results, :result)
102
+
103
+ Array(mr.results.result).sum do |r|
104
+ Dcc::TypeGuards.has_attribute?(r, :data) && r.data && Dcc::TypeGuards.has_attribute?(r.data, :quantity) ? Array(r.data.quantity).size : 0
105
+ end
106
+ end
107
+ end
108
+
109
+ def count_lists(mr_list)
110
+ mr_list.sum do |mr|
111
+ next 0 unless Dcc::TypeGuards.has_attribute?(mr, :results) && mr.results && Dcc::TypeGuards.has_attribute?(mr.results, :result)
112
+
113
+ Array(mr.results.result).sum do |r|
114
+ Dcc::TypeGuards.has_attribute?(r, :data) && r.data && Dcc::TypeGuards.has_attribute?(r.data, :list) ? Array(r.data.list).size : 0
115
+ end
116
+ end
117
+ end
118
+
119
+ def lang_codes(dcc)
120
+ cd = core_data(dcc)
121
+ return [] unless cd
122
+
123
+ [cd.used_lang_code_iso_639_1, cd.mandatory_lang_code_iso_639_1].flatten.compact.map(&:to_s)
124
+ end
125
+
126
+ def resp_persons(dcc)
127
+ ad = admin_data(dcc)
128
+ return [] unless ad && Dcc::TypeGuards.has_attribute?(ad, :resp_persons) && ad.resp_persons && Dcc::TypeGuards.has_attribute?(ad.resp_persons, :resp_person)
129
+
130
+ Array(ad.resp_persons.resp_person)
131
+ end
132
+
133
+ def signature_present?(dcc)
134
+ Dcc::TypeGuards.has_attribute?(dcc, :document) && dcc.document
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Dcc
6
+ module Inspect
7
+ autoload :Report, "dcc/inspect/report"
8
+ end
9
+ end