Module: Pubid::Ieee::Compaction
- Defined in:
- lib/pubid/ieee/compaction.rb
Overview
Serialization compaction for IEEE hashes — a recursive transform hooked
into Ieee::Identifier#to_hash (collapse) and .from_hash (expand). Two
compactions, both provably lossless and nested-safe (they descend into a
Corrigendum base too):
-
typed_stage ->
stagescalar. The multi-fieldtyped_stageobject becomes a singlestagekey holding its canonical abbreviation, rebuilt from the TYPED_STAGES registry. The abbreviation keys the registry uniquely, and the collapse fires only when the registry's reconstruction is byte-identical to the serialized sub-hash — an unrecognised / non-matching stage is left as the full object (fails closed). -
draftslash strip.Components::Draft#to_srenders"/D3"; the leading/is boilerplate, so the serialized form is"D3". No expand step is needed —Draft.parsealready accepts the slashless form and reproduces"/D3"(verified across the whole corpus), so from_hash and rendering are unchanged.
A recursive hash transform (rather than lutaml attributes) is deliberate:
stage collides with the inherited Components::Stage attribute name (a
redefinition would reintroduce the multi-flavor nondeterminism we hit with
number), and only a whole-hash walk reaches a nested Corrigendum base,
which a per-object to_hash override cannot (the nested-serialization
wall). The runtime typed_stage/draft_obj objects are untouched.
Class Method Summary collapse
- .build_sub_hash_map ⇒ Object
-
.collapse(node) ⇒ Object
Recursively compact every hash in the tree: collapse a reproducible
typed_stageto astagescalar and strip the leading/offdraft. - .collapse_hash(node) ⇒ Object
-
.collapse_stage(node) ⇒ Object
Collapse only when the registry rebuilds the exact same sub-hash, so no information is ever lost.
- .deep_dup(node) ⇒ Object
-
.entry_sub_hash(typed_stage) ⇒ Object
NB: this must list every Components::TypedStage attribute.
-
.expand(node) ⇒ Object
Inverse of Compaction.collapse for
stage: replace a recognisedstagescalar with its registry typed_stage sub-hash so lutaml rebuilds the component (nested bases included). - .expand_hash(node) ⇒ Object
- .stage_key(node) ⇒ Object
-
.sub_hash_for(abbr) ⇒ Object
abbr => canonical serialized typed_stage sub-hash, mirroring what the parent serializer + canonicalizer emit (nils dropped, and a default
project_status: falsedropped).
Class Method Details
.build_sub_hash_map ⇒ Object
40 41 42 |
# File 'lib/pubid/ieee/compaction.rb', line 40 def build_sub_hash_map TypedStages::TYPED_STAGES.to_h { |ts| [ts.abbr.first, entry_sub_hash(ts)] } end |
.collapse(node) ⇒ Object
Recursively compact every hash in the tree: collapse a reproducible
typed_stage to a stage scalar and strip the leading / off draft.
62 63 64 65 66 |
# File 'lib/pubid/ieee/compaction.rb', line 62 def collapse(node) collapse_hash(node) if node.is_a?(::Hash) node.each { |v| collapse(v) } if node.is_a?(::Array) node end |
.collapse_hash(node) ⇒ Object
68 69 70 71 72 73 |
# File 'lib/pubid/ieee/compaction.rb', line 68 def collapse_hash(node) collapse_stage(node) draft = node["draft"] node["draft"] = draft.delete_prefix("/") if draft.is_a?(::String) node.each_value { |v| collapse(v) } end |
.collapse_stage(node) ⇒ Object
Collapse only when the registry rebuilds the exact same sub-hash, so no information is ever lost.
77 78 79 80 81 82 83 84 |
# File 'lib/pubid/ieee/compaction.rb', line 77 def collapse_stage(node) ts = node["typed_stage"] abbr = ts["abbr"]&.first if ts.is_a?(::Hash) return unless abbr && sub_hash_for(abbr) == ts node.delete("typed_stage") node["stage"] = abbr end |
.deep_dup(node) ⇒ Object
112 113 114 115 116 117 118 |
# File 'lib/pubid/ieee/compaction.rb', line 112 def deep_dup(node) case node when ::Hash then node.transform_values { |v| deep_dup(v) } when ::Array then node.map { |v| deep_dup(v) } else node end end |
.entry_sub_hash(typed_stage) ⇒ Object
NB: this must list every Components::TypedStage attribute. If a field is added there but not here, the collapse guard fails closed — affected stages stay full objects (still lossless) but stop compacting; extend this list to keep them compact.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/pubid/ieee/compaction.rb', line 48 def entry_sub_hash(typed_stage) { "abbr" => typed_stage.abbr, "stage_code" => typed_stage.stage_code, "type_code" => typed_stage.type_code, "iso_stage_equivalent" => typed_stage.iso_stage_equivalent, "ieee_draft_equivalent" => typed_stage.ieee_draft_equivalent, "approval_status" => typed_stage.approval_status, "project_status" => typed_stage.project_status || nil, }.compact end |
.expand(node) ⇒ Object
Inverse of collapse for stage: replace a recognised stage scalar
with its registry typed_stage sub-hash so lutaml rebuilds the component
(nested bases included). draft needs no expansion — Draft.parse
accepts the slashless form. An unrecognised value is left alone.
90 91 92 93 94 |
# File 'lib/pubid/ieee/compaction.rb', line 90 def (node) (node) if node.is_a?(::Hash) node.each { |v| (v) } if node.is_a?(::Array) node end |
.expand_hash(node) ⇒ Object
96 97 98 99 100 101 102 103 104 |
# File 'lib/pubid/ieee/compaction.rb', line 96 def (node) key = stage_key(node) sub = sub_hash_for(node[key].to_s) if key if sub node.delete(key) node["typed_stage"] = deep_dup(sub) end node.each_value { |v| (v) } end |
.stage_key(node) ⇒ Object
106 107 108 109 110 |
# File 'lib/pubid/ieee/compaction.rb', line 106 def stage_key(node) return :stage if node.key?(:stage) "stage" if node.key?("stage") end |
.sub_hash_for(abbr) ⇒ Object
abbr => canonical serialized typed_stage sub-hash, mirroring what the
parent serializer + canonicalizer emit (nils dropped, and a default
project_status: false dropped). Memoized.
36 37 38 |
# File 'lib/pubid/ieee/compaction.rb', line 36 def sub_hash_for(abbr) (@sub_hash_for ||= build_sub_hash_map)[abbr] end |