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/lib/dcc/convert/result.rb
CHANGED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="{{ page_lang | default: 'en' }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>DCC {{ dcc.administrativeData.coreData.uniqueIdentifier | default: "Certificate" }}</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=IBM+Plex+Mono:wght@300;400;500&family=IBM+Plex+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--bg: #0a0e1a; --surface: #111827; --surface-2: #1a2236;
|
|
13
|
+
--border: #1e293b; --border-hi: #334155;
|
|
14
|
+
--text: #e2e8f0; --text-dim: #94a3b8; --text-mute: #64748b;
|
|
15
|
+
--accent: #38bdf8; --accent-glow: rgba(56,189,248,0.12);
|
|
16
|
+
--green: #4ade80; --amber: #fbbf24; --red: #f87171;
|
|
17
|
+
}
|
|
18
|
+
* { margin:0; padding:0; box-sizing:border-box; }
|
|
19
|
+
body { background:var(--bg); color:var(--text); font-family:'IBM Plex Sans',sans-serif; font-size:14px; line-height:1.6; -webkit-font-smoothing:antialiased; }
|
|
20
|
+
.mono { font-family:'IBM Plex Mono',monospace; }
|
|
21
|
+
.display { font-family:'DM Sans',sans-serif; letter-spacing:-0.02em; }
|
|
22
|
+
.page { max-width:1100px; margin:0 auto; padding:2.5rem 1.5rem 4rem; }
|
|
23
|
+
|
|
24
|
+
.header { position:relative; border:1px solid var(--border); border-radius:16px; padding:2.5rem; background:linear-gradient(135deg,var(--surface),var(--surface-2)); overflow:hidden; margin-bottom:1.5rem; }
|
|
25
|
+
.header::before { content:''; position:absolute; top:-50%; right:-20%; width:60%; height:200%; background:radial-gradient(ellipse,var(--accent-glow) 0%,transparent 60%); pointer-events:none; }
|
|
26
|
+
.header::after { content:''; position:absolute; top:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,var(--accent),transparent); opacity:0.5; }
|
|
27
|
+
.header-grid { display:grid; grid-template-columns:1fr auto; gap:2rem; align-items:start; position:relative; z-index:1; }
|
|
28
|
+
.badge { display:inline-flex; align-items:center; gap:0.5rem; padding:0.35rem 0.8rem; border:1px solid var(--accent); border-radius:100px; font-size:0.7rem; font-weight:500; letter-spacing:0.1em; text-transform:uppercase; color:var(--accent); background:var(--accent-glow); margin-bottom:1rem; }
|
|
29
|
+
.badge-dot { width:6px; height:6px; border-radius:50%; background:var(--accent); animation:pulse 2s ease-in-out infinite; }
|
|
30
|
+
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.3; } }
|
|
31
|
+
.header h1 { font-size:2.2rem; font-weight:700; line-height:1.1; margin-bottom:0.5rem; background:linear-gradient(180deg,#f8fafc,#94a3b8); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
|
|
32
|
+
.header .subtitle { color:var(--text-dim); font-size:0.95rem; }
|
|
33
|
+
.header .uid { font-family:'IBM Plex Mono',monospace; font-size:0.85rem; color:var(--text-mute); margin-top:0.75rem; }
|
|
34
|
+
.header-side { text-align:right; border-left:1px solid var(--border-hi); padding-left:2rem; }
|
|
35
|
+
.version-tag { font-family:'IBM Plex Mono',monospace; font-size:1.1rem; font-weight:500; color:var(--green); }
|
|
36
|
+
.version-label { font-size:0.7rem; text-transform:uppercase; letter-spacing:0.1em; color:var(--text-mute); }
|
|
37
|
+
|
|
38
|
+
.card { border:1px solid var(--border); border-radius:12px; background:var(--surface); overflow:hidden; animation:slideUp 0.5s ease-out both; }
|
|
39
|
+
.card + .card { margin-top:1.5rem; }
|
|
40
|
+
.card:nth-child(2) { animation-delay:0.08s; }
|
|
41
|
+
.card:nth-child(3) { animation-delay:0.16s; }
|
|
42
|
+
.card:nth-child(4) { animation-delay:0.24s; }
|
|
43
|
+
.card:nth-child(5) { animation-delay:0.32s; }
|
|
44
|
+
.card:nth-child(6) { animation-delay:0.4s; }
|
|
45
|
+
@keyframes slideUp { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:translateY(0); } }
|
|
46
|
+
.card-header { padding:1.25rem 1.5rem; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:0.75rem; }
|
|
47
|
+
.card-icon { width:32px; height:32px; border-radius:8px; display:flex; align-items:center; justify-content:center; font-size:1rem; border:1px solid var(--border-hi); background:var(--surface-2); }
|
|
48
|
+
.card-title { font-size:0.85rem; font-weight:600; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-dim); }
|
|
49
|
+
.card-body { padding:1.5rem; }
|
|
50
|
+
|
|
51
|
+
.data-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(250px,1fr)); gap:1px; background:var(--border); border:1px solid var(--border); border-radius:8px; overflow:hidden; }
|
|
52
|
+
.data-cell { background:var(--surface); padding:1rem 1.25rem; }
|
|
53
|
+
.data-label { font-size:0.7rem; text-transform:uppercase; letter-spacing:0.1em; color:var(--text-mute); margin-bottom:0.25rem; }
|
|
54
|
+
.data-value { font-size:0.95rem; color:var(--text); }
|
|
55
|
+
.data-value.mono { font-family:'IBM Plex Mono',monospace; font-size:0.9rem; }
|
|
56
|
+
|
|
57
|
+
.data-table { width:100%; border-collapse:collapse; }
|
|
58
|
+
.data-table th { text-align:left; padding:0.6rem 1rem; font-size:0.7rem; text-transform:uppercase; letter-spacing:0.08em; color:var(--text-mute); font-weight:500; border-bottom:1px solid var(--border-hi); }
|
|
59
|
+
.data-table td { padding:0.75rem 1rem; border-bottom:1px solid var(--border); font-size:0.875rem; }
|
|
60
|
+
.data-table tr:last-child td { border-bottom:none; }
|
|
61
|
+
.data-table tr:hover td { background:var(--surface-2); }
|
|
62
|
+
|
|
63
|
+
.signer { display:flex; align-items:center; gap:0.75rem; padding:0.75rem 1rem; border:1px solid var(--border); border-radius:8px; background:var(--surface-2); }
|
|
64
|
+
.signer + .signer { margin-top:0.5rem; }
|
|
65
|
+
.signer-avatar { width:36px; height:36px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-weight:600; font-size:0.8rem; color:var(--surface); background:var(--accent); flex-shrink:0; }
|
|
66
|
+
.signer-name { font-weight:500; font-size:0.875rem; }
|
|
67
|
+
.signer-role { font-size:0.75rem; color:var(--text-mute); }
|
|
68
|
+
.signer-main { margin-left:auto; padding:0.2rem 0.6rem; border-radius:100px; font-size:0.65rem; font-weight:600; text-transform:uppercase; letter-spacing:0.08em; color:var(--green); border:1px solid var(--green); background:rgba(74,222,128,0.1); }
|
|
69
|
+
|
|
70
|
+
.mr-block { border:1px solid var(--border); border-radius:8px; padding:1.25rem; background:var(--surface-2); }
|
|
71
|
+
.mr-block + .mr-block { margin-top:0.75rem; }
|
|
72
|
+
.mr-title { font-weight:500; margin-bottom:0.75rem; }
|
|
73
|
+
|
|
74
|
+
@media (max-width:700px) { .header-grid { grid-template-columns:1fr; } .header-side { border-left:none; border-top:1px solid var(--border-hi); padding-left:0; padding-top:1rem; text-align:left; } }
|
|
75
|
+
</style>
|
|
76
|
+
</head>
|
|
77
|
+
<body>
|
|
78
|
+
<div class="page">
|
|
79
|
+
|
|
80
|
+
{% assign admin = dcc.administrativeData %}
|
|
81
|
+
{% assign core = admin.coreData %}
|
|
82
|
+
{% assign lab = admin.calibrationLaboratory %}
|
|
83
|
+
{% assign results = dcc.measurementResults %}
|
|
84
|
+
|
|
85
|
+
<!-- Header -->
|
|
86
|
+
<div class="header">
|
|
87
|
+
<div class="header-grid">
|
|
88
|
+
<div>
|
|
89
|
+
<div class="badge"><span class="badge-dot"></span> Digital Calibration Certificate</div>
|
|
90
|
+
{% assign item_obj = admin.items.item %}
|
|
91
|
+
<h1 class="display">{{ item_obj.name.content | dcc_text | default: "Calibration Certificate" }}</h1>
|
|
92
|
+
<p class="subtitle">{{ lab.contact.name.content | dcc_text | default: "" }}</p>
|
|
93
|
+
<p class="uid mono">ID: {{ core.uniqueIdentifier | default: "—" }}</p>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="header-side">
|
|
96
|
+
<div class="version-label">Schema</div>
|
|
97
|
+
<div class="version-tag mono">v{{ dcc.schemaVersion }}</div>
|
|
98
|
+
{% if core.countryCodeISO3166_1 %}
|
|
99
|
+
<div style="margin-top:0.5rem;">
|
|
100
|
+
<div class="version-label">Country</div>
|
|
101
|
+
<div class="mono" style="font-size:1.1rem;">{{ core.countryCodeISO3166_1 }}</div>
|
|
102
|
+
</div>
|
|
103
|
+
{% endif %}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- Core Data -->
|
|
109
|
+
<div class="card">
|
|
110
|
+
<div class="card-header"><div class="card-icon">⚙</div><span class="card-title">Core Data</span></div>
|
|
111
|
+
<div class="card-body">
|
|
112
|
+
<div class="data-grid">
|
|
113
|
+
<div class="data-cell"><div class="data-label">Unique ID</div><div class="data-value mono">{{ core.uniqueIdentifier | default: "—" }}</div></div>
|
|
114
|
+
{% if core.receiptDate %}<div class="data-cell"><div class="data-label">Receipt Date</div><div class="data-value mono">{{ core.receiptDate }}</div></div>{% endif %}
|
|
115
|
+
{% if core.beginPerformanceDate %}<div class="data-cell"><div class="data-label">Performance Begin</div><div class="data-value mono">{{ core.beginPerformanceDate }}</div></div>{% endif %}
|
|
116
|
+
{% if core.endPerformanceDate %}<div class="data-cell"><div class="data-label">Performance End</div><div class="data-value mono">{{ core.endPerformanceDate }}</div></div>{% endif %}
|
|
117
|
+
{% if core.usedLangCodeISO639_1 %}<div class="data-cell"><div class="data-label">Languages</div><div class="data-value mono">{{ core.usedLangCodeISO639_1 | dcc_items | join: ", " }}</div></div>{% endif %}
|
|
118
|
+
{% if core.mandatoryLangCodeISO639_1 %}<div class="data-cell"><div class="data-label">Mandatory Language</div><div class="data-value mono">{{ core.mandatoryLangCodeISO639_1 }}</div></div>{% endif %}
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- Items -->
|
|
124
|
+
{% assign items_data = admin.items.item %}
|
|
125
|
+
{% if items_data %}
|
|
126
|
+
<div class="card">
|
|
127
|
+
<div class="card-header"><div class="card-icon">☰</div><span class="card-title">Calibrated Item</span></div>
|
|
128
|
+
<div class="card-body">
|
|
129
|
+
{% if items_data.name %}
|
|
130
|
+
{% assign item = items_data %}
|
|
131
|
+
<div class="data-grid">
|
|
132
|
+
<div class="data-cell"><div class="data-label">Name</div><div class="data-value">{{ item.name.content | dcc_text | default: "—" }}</div></div>
|
|
133
|
+
{% if item.model %}<div class="data-cell"><div class="data-label">Model</div><div class="data-value">{{ item.model }}</div></div>{% endif %}
|
|
134
|
+
{% if item.manufacturer %}<div class="data-cell"><div class="data-label">Manufacturer</div><div class="data-value">{{ item.manufacturer.name.content | dcc_text }}</div></div>{% endif %}
|
|
135
|
+
</div>
|
|
136
|
+
{% if item.identifications %}
|
|
137
|
+
<table class="data-table" style="margin-top:0.75rem;">
|
|
138
|
+
<thead><tr><th>Identification</th><th>Issuer</th></tr></thead>
|
|
139
|
+
<tbody>
|
|
140
|
+
{% assign ids = item.identifications.identification %}
|
|
141
|
+
{% if ids.name %}
|
|
142
|
+
<tr><td class="mono">{{ ids.value }}</td><td>{{ ids.issuer }}</td></tr>
|
|
143
|
+
{% else %}
|
|
144
|
+
{% for id in ids %}<tr><td class="mono">{{ id.value }}</td><td>{{ id.issuer }}</td></tr>{% endfor %}
|
|
145
|
+
{% endif %}
|
|
146
|
+
</tbody>
|
|
147
|
+
</table>
|
|
148
|
+
{% endif %}
|
|
149
|
+
{% else %}
|
|
150
|
+
{% for item in items_data %}
|
|
151
|
+
<div class="data-grid">
|
|
152
|
+
<div class="data-cell"><div class="data-label">Name</div><div class="data-value">{{ item.name.content | dcc_text | default: "—" }}</div></div>
|
|
153
|
+
{% if item.model %}<div class="data-cell"><div class="data-label">Model</div><div class="data-value">{{ item.model }}</div></div>{% endif %}
|
|
154
|
+
{% if item.manufacturer %}<div class="data-cell"><div class="data-label">Manufacturer</div><div class="data-value">{{ item.manufacturer.name.content | dcc_text }}</div></div>{% endif %}
|
|
155
|
+
</div>
|
|
156
|
+
{% endfor %}
|
|
157
|
+
{% endif %}
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
{% endif %}
|
|
161
|
+
|
|
162
|
+
<!-- Calibration Laboratory -->
|
|
163
|
+
{% if lab.contact %}
|
|
164
|
+
<div class="card">
|
|
165
|
+
<div class="card-header"><div class="card-icon">☑</div><span class="card-title">Calibration Laboratory</span></div>
|
|
166
|
+
<div class="card-body">
|
|
167
|
+
<div class="data-grid">
|
|
168
|
+
<div class="data-cell"><div class="data-label">Name</div><div class="data-value">{{ lab.contact.name.content | dcc_text }}</div></div>
|
|
169
|
+
{% if lab.contact.eMail %}<div class="data-cell"><div class="data-label">Email</div><div class="data-value mono">{{ lab.contact.eMail }}</div></div>{% endif %}
|
|
170
|
+
{% if lab.contact.phone %}<div class="data-cell"><div class="data-label">Phone</div><div class="data-value mono">{{ lab.contact.phone }}</div></div>{% endif %}
|
|
171
|
+
{% if lab.contact.location %}
|
|
172
|
+
<div class="data-cell"><div class="data-label">Address</div><div class="data-value">
|
|
173
|
+
{% if lab.contact.location.street %}{{ lab.contact.location.street }} {{ lab.contact.location.streetNo }}{% endif %}
|
|
174
|
+
{% if lab.contact.location.postCode %}<br>{{ lab.contact.location.postCode }} {{ lab.contact.location.city }}{% endif %}
|
|
175
|
+
{% if lab.contact.location.countryCode %}<br>{{ lab.contact.location.countryCode }}{% endif %}
|
|
176
|
+
</div></div>
|
|
177
|
+
{% endif %}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
{% endif %}
|
|
182
|
+
|
|
183
|
+
<!-- Responsible Persons -->
|
|
184
|
+
{% assign persons = admin.respPersons.respPerson | dcc_items %}
|
|
185
|
+
{% if persons.size > 0 %}
|
|
186
|
+
<div class="card">
|
|
187
|
+
<div class="card-header"><div class="card-icon">♂</div><span class="card-title">Responsible Persons</span></div>
|
|
188
|
+
<div class="card-body">
|
|
189
|
+
{% for p in persons %}
|
|
190
|
+
{% assign pname = p.person.name.content | dcc_text %}
|
|
191
|
+
<div class="signer">
|
|
192
|
+
<div class="signer-avatar">{{ pname | dcc_initials }}</div>
|
|
193
|
+
<div>
|
|
194
|
+
<div class="signer-name">{{ pname }}</div>
|
|
195
|
+
{% if p.person.eMail %}<div class="signer-role mono">{{ p.person.eMail }}</div>{% endif %}
|
|
196
|
+
</div>
|
|
197
|
+
{% if p.mainSigner %}<span class="signer-main">Main Signer</span>{% endif %}
|
|
198
|
+
</div>
|
|
199
|
+
{% endfor %}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
{% endif %}
|
|
203
|
+
|
|
204
|
+
<!-- Measurement Results -->
|
|
205
|
+
{% assign mrs = results.measurementResult | dcc_items %}
|
|
206
|
+
{% if mrs.size > 0 %}
|
|
207
|
+
<div class="card">
|
|
208
|
+
<div class="card-header"><div class="card-icon">⚡</div><span class="card-title">Measurement Results</span></div>
|
|
209
|
+
<div class="card-body">
|
|
210
|
+
{% for mr in mrs %}
|
|
211
|
+
<div class="mr-block">
|
|
212
|
+
<div class="mr-title">{{ mr.name.content | dcc_text | default: "Untitled Result" }}</div>
|
|
213
|
+
{% if mr.usedMethods %}
|
|
214
|
+
{% for method in mr.usedMethods.usedMethod | dcc_items %}
|
|
215
|
+
{% if method.name %}
|
|
216
|
+
<div style="color:var(--text-dim); font-size:0.8rem; padding-bottom:0.25rem;">{{ method.name.content | dcc_text }}</div>
|
|
217
|
+
{% endif %}
|
|
218
|
+
{% endfor %}
|
|
219
|
+
{% endif %}
|
|
220
|
+
{% if mr.results %}
|
|
221
|
+
{% for r in mr.results.result | dcc_items %}
|
|
222
|
+
{% if r.name %}
|
|
223
|
+
<div style="padding:0.5rem 0.75rem; border-left:2px solid var(--accent); margin-top:0.5rem; background:rgba(56,189,248,0.05); border-radius:0 6px 6px 0;">
|
|
224
|
+
{{ r.name.content | dcc_text }}
|
|
225
|
+
</div>
|
|
226
|
+
{% endif %}
|
|
227
|
+
{% endfor %}
|
|
228
|
+
{% endif %}
|
|
229
|
+
</div>
|
|
230
|
+
{% endfor %}
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
{% endif %}
|
|
234
|
+
|
|
235
|
+
<!-- Customer -->
|
|
236
|
+
{% if admin.customer %}
|
|
237
|
+
<div class="card">
|
|
238
|
+
<div class="card-header"><div class="card-icon">➤</div><span class="card-title">Customer</span></div>
|
|
239
|
+
<div class="card-body">
|
|
240
|
+
<div class="data-grid">
|
|
241
|
+
<div class="data-cell"><div class="data-label">Name</div><div class="data-value">{{ admin.customer.name.content | dcc_text }}</div></div>
|
|
242
|
+
{% if admin.customer.eMail %}<div class="data-cell"><div class="data-label">Email</div><div class="data-value mono">{{ admin.customer.eMail }}</div></div>{% endif %}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
{% endif %}
|
|
247
|
+
|
|
248
|
+
</div>
|
|
249
|
+
</body>
|
|
250
|
+
</html>
|
data/lib/dcc/convert/yaml.rb
CHANGED
data/lib/dcc/convert.rb
CHANGED
data/lib/dcc/diff/change.rb
CHANGED
data/lib/dcc/diff/result.rb
CHANGED
|
@@ -31,7 +31,7 @@ module Dcc
|
|
|
31
31
|
"(no structural changes)"
|
|
32
32
|
else
|
|
33
33
|
header = "#{changes.size} change(s): " \
|
|
34
|
-
|
|
34
|
+
"#{additions.size} added, #{removals.size} removed, #{modifications.size} changed"
|
|
35
35
|
([header] + changes.map(&:to_s)).join("\n")
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -41,7 +41,9 @@ module Dcc
|
|
|
41
41
|
::JSON.pretty_generate(
|
|
42
42
|
additions: additions.map { |c| { path: c.path, value: c.after } },
|
|
43
43
|
removals: removals.map { |c| { path: c.path, value: c.before } },
|
|
44
|
-
modifications: modifications.map
|
|
44
|
+
modifications: modifications.map do |c|
|
|
45
|
+
{ path: c.path, before: c.before, after: c.after }
|
|
46
|
+
end,
|
|
45
47
|
)
|
|
46
48
|
end
|
|
47
49
|
|
|
@@ -50,9 +52,11 @@ module Dcc
|
|
|
50
52
|
{
|
|
51
53
|
additions: additions.map { |c| { path: c.path, value: c.after } },
|
|
52
54
|
removals: removals.map { |c| { path: c.path, value: c.before } },
|
|
53
|
-
modifications: modifications.map
|
|
55
|
+
modifications: modifications.map do |c|
|
|
56
|
+
{ path: c.path, before: c.before, after: c.after }
|
|
57
|
+
end,
|
|
54
58
|
}.to_yaml
|
|
55
59
|
end
|
|
56
60
|
end
|
|
57
61
|
end
|
|
58
|
-
end
|
|
62
|
+
end
|
data/lib/dcc/diff.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bigdecimal"
|
|
4
|
+
require "date"
|
|
4
5
|
require "lutaml/model"
|
|
5
6
|
|
|
6
7
|
module Dcc
|
|
@@ -12,6 +13,12 @@ module Dcc
|
|
|
12
13
|
autoload :Result, "dcc/diff/result"
|
|
13
14
|
autoload :Change, "dcc/diff/change"
|
|
14
15
|
|
|
16
|
+
# Leaf types compared directly rather than descended into. Numeric covers
|
|
17
|
+
# BigDecimal and Date covers DateTime, so neither needs its own entry.
|
|
18
|
+
PRIMITIVE_TYPES = [
|
|
19
|
+
::String, ::Numeric, ::Symbol, ::Time, ::Date
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
15
22
|
class << self
|
|
16
23
|
# @param a [Lutaml::Model::Serializable]
|
|
17
24
|
# @param b [Lutaml::Model::Serializable]
|
|
@@ -27,20 +34,24 @@ module Dcc
|
|
|
27
34
|
def collect_differences(a, b, path, changes)
|
|
28
35
|
# Both nil - no diff
|
|
29
36
|
return if a.nil? && b.nil?
|
|
37
|
+
|
|
30
38
|
# One is nil - add/remove
|
|
31
39
|
if a.nil?
|
|
32
|
-
changes << Change.new(path: path, kind: :add, before: nil,
|
|
40
|
+
changes << Change.new(path: path, kind: :add, before: nil,
|
|
41
|
+
after: summarize(b))
|
|
33
42
|
return
|
|
34
43
|
end
|
|
35
44
|
if b.nil?
|
|
36
|
-
changes << Change.new(path: path, kind: :remove,
|
|
45
|
+
changes << Change.new(path: path, kind: :remove,
|
|
46
|
+
before: summarize(a), after: nil)
|
|
37
47
|
return
|
|
38
48
|
end
|
|
39
49
|
|
|
40
50
|
# Both primitives - compare values
|
|
41
51
|
if primitive?(a) || primitive?(b)
|
|
42
|
-
|
|
43
|
-
changes << Change.new(path: path, kind: :change, before: a,
|
|
52
|
+
unless equivalent?(a, b)
|
|
53
|
+
changes << Change.new(path: path, kind: :change, before: a,
|
|
54
|
+
after: b)
|
|
44
55
|
end
|
|
45
56
|
return
|
|
46
57
|
end
|
|
@@ -63,14 +74,35 @@ module Dcc
|
|
|
63
74
|
end
|
|
64
75
|
end
|
|
65
76
|
|
|
77
|
+
# "Did this document change?" is not "are these numbers equal?".
|
|
78
|
+
# `BigDecimal("NaN") == BigDecimal("NaN")` is false by IEEE, and
|
|
79
|
+
# `Values#==` honours that, so two documents carrying the same
|
|
80
|
+
# not-measured entry would otherwise report a change to itself.
|
|
81
|
+
def equivalent?(a, b)
|
|
82
|
+
values = ::Dcc::Type::DecimalXmlList::Values
|
|
83
|
+
return a == b unless a.is_a?(values) && b.is_a?(values)
|
|
84
|
+
|
|
85
|
+
a.size == b.size && a.to_a.zip(b.to_a).all? { |x, y| same_decimal?(x, y) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# `Values.new` takes whatever it is handed, so a list assigned in Ruby
|
|
89
|
+
# rather than parsed can hold anything. Only BigDecimals are asked
|
|
90
|
+
# whether they are NaN.
|
|
91
|
+
def same_decimal?(left, right)
|
|
92
|
+
return true if left == right
|
|
93
|
+
|
|
94
|
+
left.is_a?(::BigDecimal) && right.is_a?(::BigDecimal) &&
|
|
95
|
+
left.nan? && right.nan?
|
|
96
|
+
end
|
|
97
|
+
|
|
66
98
|
def primitive?(value)
|
|
67
|
-
return true if value.nil?
|
|
68
|
-
return
|
|
69
|
-
return true if
|
|
70
|
-
return true if value.is_a?(::Time) || value.is_a?(::Date) || value.is_a?(::DateTime)
|
|
71
|
-
return true if value.is_a?(::BigDecimal)
|
|
99
|
+
return true if value.nil? || [true, false].include?(value)
|
|
100
|
+
return false if value.is_a?(::Array)
|
|
101
|
+
return true if PRIMITIVE_TYPES.any? { |type| value.is_a?(type) }
|
|
72
102
|
|
|
73
|
-
|
|
103
|
+
# Anything we cannot descend into is compared with `==`, which covers
|
|
104
|
+
# value objects such as decimal XML lists.
|
|
105
|
+
!value.class.respond_to?(:attributes)
|
|
74
106
|
end
|
|
75
107
|
|
|
76
108
|
def summarize(node)
|
|
@@ -80,4 +112,4 @@ module Dcc
|
|
|
80
112
|
end
|
|
81
113
|
end
|
|
82
114
|
end
|
|
83
|
-
end
|
|
115
|
+
end
|
data/lib/dcc/error.rb
CHANGED
|
@@ -37,9 +37,16 @@ module Dcc
|
|
|
37
37
|
# Raised when an unsupported schema version is requested.
|
|
38
38
|
class UnknownVersionError < Error; end
|
|
39
39
|
|
|
40
|
+
# Raised when a migration direction is not supported — any downgrade, and
|
|
41
|
+
# any pair whose incompatibilities have not been verified.
|
|
42
|
+
class UnsupportedMigrationError < Error; end
|
|
43
|
+
|
|
40
44
|
# Raised when the builder DSL is used incorrectly.
|
|
41
45
|
class BuilderError < Error; end
|
|
42
46
|
|
|
47
|
+
# Raised when a plugin gem's entry file cannot be loaded.
|
|
48
|
+
class PluginError < Error; end
|
|
49
|
+
|
|
43
50
|
# Raised when a soft dependency (`xmldsig`, `sinatra`, etc.) is required
|
|
44
51
|
# but not installed.
|
|
45
52
|
class MissingDependencyError < Error
|
data/lib/dcc/extract/file.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "base64"
|
|
4
|
-
require "set"
|
|
5
4
|
|
|
6
5
|
module Dcc
|
|
7
6
|
module Extract
|
|
@@ -40,7 +39,7 @@ module Dcc
|
|
|
40
39
|
# @param dcc [Lutaml::Model::Serializable]
|
|
41
40
|
# @yieldparam [Dcc::Extract::File]
|
|
42
41
|
# @return [Array<Dcc::Extract::File>] the list when no block given.
|
|
43
|
-
def each(dcc, &
|
|
42
|
+
def each(dcc, &)
|
|
44
43
|
list = []
|
|
45
44
|
visited = ::Set.new
|
|
46
45
|
|
|
@@ -65,7 +64,7 @@ module Dcc
|
|
|
65
64
|
collect.call(dcc, nil)
|
|
66
65
|
return list unless block_given?
|
|
67
66
|
|
|
68
|
-
list.each(&
|
|
67
|
+
list.each(&)
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# @param dcc [Lutaml::Model::Serializable]
|
|
@@ -140,4 +139,4 @@ module Dcc
|
|
|
140
139
|
end
|
|
141
140
|
end
|
|
142
141
|
end
|
|
143
|
-
end
|
|
142
|
+
end
|
data/lib/dcc/extract/ring.rb
CHANGED
data/lib/dcc/extract.rb
CHANGED
data/lib/dcc/i18n/text_lookup.rb
CHANGED
|
@@ -18,7 +18,9 @@ module Dcc
|
|
|
18
18
|
# @param lang [String, nil] requested ISO 639-1 code.
|
|
19
19
|
# @return [String, nil]
|
|
20
20
|
def call(text_obj, dcc: nil, lang: nil)
|
|
21
|
-
return nil unless text_obj && Dcc::TypeGuards.has_attribute?(
|
|
21
|
+
return nil unless text_obj && Dcc::TypeGuards.has_attribute?(
|
|
22
|
+
text_obj, :content
|
|
23
|
+
)
|
|
22
24
|
|
|
23
25
|
contents = Array(text_obj.content)
|
|
24
26
|
return nil if contents.empty?
|
|
@@ -49,10 +51,12 @@ module Dcc
|
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
def declared_languages(dcc)
|
|
52
|
-
return [] unless dcc && Dcc::TypeGuards.has_attribute?(dcc,
|
|
54
|
+
return [] unless dcc && Dcc::TypeGuards.has_attribute?(dcc,
|
|
55
|
+
:administrative_data)
|
|
53
56
|
|
|
54
57
|
admin = dcc.administrative_data
|
|
55
|
-
return [] unless admin && Dcc::TypeGuards.has_attribute?(admin,
|
|
58
|
+
return [] unless admin && Dcc::TypeGuards.has_attribute?(admin,
|
|
59
|
+
:core_data)
|
|
56
60
|
|
|
57
61
|
core = admin.core_data
|
|
58
62
|
return [] unless core
|
|
@@ -64,4 +68,4 @@ module Dcc
|
|
|
64
68
|
end
|
|
65
69
|
end
|
|
66
70
|
end
|
|
67
|
-
end
|
|
71
|
+
end
|
data/lib/dcc/i18n.rb
CHANGED
data/lib/dcc/inspect/report.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Dcc
|
|
|
23
23
|
def to_s
|
|
24
24
|
lines = []
|
|
25
25
|
lines << "DCC Inspection Report"
|
|
26
|
-
lines << "=" * 50
|
|
26
|
+
lines << ("=" * 50)
|
|
27
27
|
lines << "Schema version: #{schema_version}"
|
|
28
28
|
lines << "Unique identifier: #{unique_identifier}"
|
|
29
29
|
lines << "Country: #{country_code}"
|
|
@@ -56,7 +56,7 @@ module Dcc
|
|
|
56
56
|
# @param dcc [Lutaml::Model::Serializable]
|
|
57
57
|
# @return [Dcc::Inspect::Report]
|
|
58
58
|
def call(dcc)
|
|
59
|
-
|
|
59
|
+
admin_data(dcc)
|
|
60
60
|
mr_list = measurement_results(dcc)
|
|
61
61
|
new(
|
|
62
62
|
schema_version: dcc.schema_version.to_s,
|
|
@@ -76,42 +76,75 @@ module Dcc
|
|
|
76
76
|
private
|
|
77
77
|
|
|
78
78
|
def admin_data(dcc)
|
|
79
|
-
Dcc::TypeGuards.has_attribute?(dcc,
|
|
79
|
+
if Dcc::TypeGuards.has_attribute?(dcc,
|
|
80
|
+
:administrative_data)
|
|
81
|
+
dcc.administrative_data
|
|
82
|
+
end
|
|
80
83
|
end
|
|
81
84
|
|
|
82
85
|
def core_data(dcc)
|
|
83
86
|
ad = admin_data(dcc)
|
|
84
|
-
ad && Dcc::TypeGuards.has_attribute?(ad,
|
|
87
|
+
if ad && Dcc::TypeGuards.has_attribute?(ad,
|
|
88
|
+
:core_data)
|
|
89
|
+
ad.core_data
|
|
90
|
+
end
|
|
85
91
|
end
|
|
86
92
|
|
|
87
93
|
def items(dcc)
|
|
88
94
|
ad = admin_data(dcc)
|
|
89
|
-
return [] unless ad && Dcc::TypeGuards.has_attribute?(ad,
|
|
95
|
+
return [] unless ad && Dcc::TypeGuards.has_attribute?(ad,
|
|
96
|
+
:items) && ad.items && Dcc::TypeGuards.has_attribute?(
|
|
97
|
+
ad.items, :item
|
|
98
|
+
)
|
|
90
99
|
|
|
91
100
|
Array(ad.items.item)
|
|
92
101
|
end
|
|
93
102
|
|
|
94
103
|
def measurement_results(dcc)
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
if Dcc::TypeGuards.has_attribute?(dcc,
|
|
105
|
+
:measurement_results) && dcc.measurement_results && Dcc::TypeGuards.has_attribute?(
|
|
106
|
+
dcc.measurement_results, :measurement_result
|
|
107
|
+
)
|
|
108
|
+
Array(dcc.measurement_results.measurement_result)
|
|
109
|
+
else
|
|
110
|
+
[]
|
|
111
|
+
end
|
|
97
112
|
end
|
|
98
113
|
|
|
99
114
|
def count_quantities(mr_list)
|
|
100
115
|
mr_list.sum do |mr|
|
|
101
|
-
next 0 unless Dcc::TypeGuards.has_attribute?(mr,
|
|
116
|
+
next 0 unless Dcc::TypeGuards.has_attribute?(mr,
|
|
117
|
+
:results) && mr.results && Dcc::TypeGuards.has_attribute?(
|
|
118
|
+
mr.results, :result
|
|
119
|
+
)
|
|
102
120
|
|
|
103
121
|
Array(mr.results.result).sum do |r|
|
|
104
|
-
|
|
122
|
+
if Dcc::TypeGuards.has_attribute?(r,
|
|
123
|
+
:data) && r.data && Dcc::TypeGuards.has_attribute?(r.data,
|
|
124
|
+
:quantity)
|
|
125
|
+
Array(r.data.quantity).size
|
|
126
|
+
else
|
|
127
|
+
0
|
|
128
|
+
end
|
|
105
129
|
end
|
|
106
130
|
end
|
|
107
131
|
end
|
|
108
132
|
|
|
109
133
|
def count_lists(mr_list)
|
|
110
134
|
mr_list.sum do |mr|
|
|
111
|
-
next 0 unless Dcc::TypeGuards.has_attribute?(mr,
|
|
135
|
+
next 0 unless Dcc::TypeGuards.has_attribute?(mr,
|
|
136
|
+
:results) && mr.results && Dcc::TypeGuards.has_attribute?(
|
|
137
|
+
mr.results, :result
|
|
138
|
+
)
|
|
112
139
|
|
|
113
140
|
Array(mr.results.result).sum do |r|
|
|
114
|
-
|
|
141
|
+
if Dcc::TypeGuards.has_attribute?(r,
|
|
142
|
+
:data) && r.data && Dcc::TypeGuards.has_attribute?(r.data,
|
|
143
|
+
:list)
|
|
144
|
+
Array(r.data.list).size
|
|
145
|
+
else
|
|
146
|
+
0
|
|
147
|
+
end
|
|
115
148
|
end
|
|
116
149
|
end
|
|
117
150
|
end
|
|
@@ -120,12 +153,16 @@ module Dcc
|
|
|
120
153
|
cd = core_data(dcc)
|
|
121
154
|
return [] unless cd
|
|
122
155
|
|
|
123
|
-
[cd.used_lang_code_iso_639_1,
|
|
156
|
+
[cd.used_lang_code_iso_639_1,
|
|
157
|
+
cd.mandatory_lang_code_iso_639_1].flatten.compact.map(&:to_s)
|
|
124
158
|
end
|
|
125
159
|
|
|
126
160
|
def resp_persons(dcc)
|
|
127
161
|
ad = admin_data(dcc)
|
|
128
|
-
return [] unless ad && Dcc::TypeGuards.has_attribute?(ad,
|
|
162
|
+
return [] unless ad && Dcc::TypeGuards.has_attribute?(ad,
|
|
163
|
+
:resp_persons) && ad.resp_persons && Dcc::TypeGuards.has_attribute?(
|
|
164
|
+
ad.resp_persons, :resp_person
|
|
165
|
+
)
|
|
129
166
|
|
|
130
167
|
Array(ad.resp_persons.resp_person)
|
|
131
168
|
end
|
|
@@ -136,4 +173,4 @@ module Dcc
|
|
|
136
173
|
end
|
|
137
174
|
end
|
|
138
175
|
end
|
|
139
|
-
end
|
|
176
|
+
end
|
data/lib/dcc/inspect.rb
CHANGED