Class: Uniword::Docx::IdAllocator
- Inherits:
-
Object
- Object
- Uniword::Docx::IdAllocator
- Defined in:
- lib/uniword/docx/id_allocator.rb
Overview
Single owner of all ID assignment in a DOCX package.
Every builder, the adapter, and the reconciler call into this class for rId, footnote, endnote, bookmark, comment, and paragraph ID allocation. No other code generates IDs.
Two usage modes:
Creating from scratch — allocator starts empty, all IDs are fresh.
Editing a template — call seed_from_* methods BEFORE any builder
runs, so new IDs don't collide with existing ones.
This is the "populate-first" principle: when loading a template DOCX, parse and seed ALL existing IDs from the template before modification.
Constant Summary collapse
- REL_TYPE_BASE =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships"- IMAGE_REL_TYPE =
"#{REL_TYPE_BASE}/image"- HEADER_REL_TYPE =
"#{REL_TYPE_BASE}/header"- FOOTER_REL_TYPE =
"#{REL_TYPE_BASE}/footer"- HYPERLINK_REL_TYPE =
"#{REL_TYPE_BASE}/hyperlink"- CHART_REL_TYPE =
"#{REL_TYPE_BASE}/chart"- FOOTNOTES_REL_TYPE =
"#{REL_TYPE_BASE}/footnotes"- ENDNOTES_REL_TYPE =
"#{REL_TYPE_BASE}/endnotes"- THEME_REL_TYPE =
"#{REL_TYPE_BASE}/theme"- NUMBERING_REL_TYPE =
"#{REL_TYPE_BASE}/numbering"
Class Method Summary collapse
-
.populate_from_package(package) ⇒ Object
Build an allocator seeded from every source on a package in one call.
Instance Method Summary collapse
-
#all_rels ⇒ Object
Produce the final ordered list of all allocated relationships.
- #alloc_bookmark_id ⇒ Object
- #alloc_comment_id ⇒ Object
- #alloc_endnote_id ⇒ Object
- #alloc_footnote_id ⇒ Object
- #alloc_para_id ⇒ Object
-
#alloc_rid(target:, type:, target_mode: nil) ⇒ Object
Allocate a relationship ID for a target+type pair.
- #alloc_rsid ⇒ Object
-
#initialize ⇒ IdAllocator
constructor
A new instance of IdAllocator.
-
#rid_for(target:, type:) ⇒ Object
Check if a relationship has been registered for a target+type.
-
#seed_from_notes(footnote_entries, endnote_entries) ⇒ Object
Seed footnote/endnote counters from existing note entries.
-
#seed_from_rels(relationships) ⇒ Object
Seed from a relationships collection — preserves existing rIds.
Constructor Details
#initialize ⇒ IdAllocator
Returns a new instance of IdAllocator.
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/uniword/docx/id_allocator.rb', line 33 def initialize @rid_counter = 0 @rid_entries = {} # [target, type] -> { id, type, target, target_mode } @footnote_counter = 1 @endnote_counter = 1 @bookmark_counter = 0 @comment_counter = 0 @para_counter = 0 @rsid_counter = 0 end |
Class Method Details
.populate_from_package(package) ⇒ Object
Build an allocator seeded from every source on a package in one call. Used by both Package#populate_allocator and DocumentBuilder.from_template so they cannot drift apart.
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/uniword/docx/id_allocator.rb', line 131 def self.populate_from_package(package) alloc = new alloc.seed_from_rels(package.document_rels&.relationships) alloc.seed_from_rels(package.package_rels&.relationships) alloc.seed_from_notes( package.footnotes&.footnote_entries, package.endnotes&.endnote_entries, ) alloc end |
Instance Method Details
#all_rels ⇒ Object
Produce the final ordered list of all allocated relationships.
118 119 120 |
# File 'lib/uniword/docx/id_allocator.rb', line 118 def all_rels @rid_entries.values.sort_by { |r| r[:id][/\d+/]&.to_i || 0 } end |
#alloc_bookmark_id ⇒ Object
68 69 70 71 |
# File 'lib/uniword/docx/id_allocator.rb', line 68 def alloc_bookmark_id @bookmark_counter += 1 @bookmark_counter.to_s end |
#alloc_comment_id ⇒ Object
73 74 75 76 |
# File 'lib/uniword/docx/id_allocator.rb', line 73 def alloc_comment_id @comment_counter += 1 @comment_counter.to_s end |
#alloc_endnote_id ⇒ Object
62 63 64 65 66 |
# File 'lib/uniword/docx/id_allocator.rb', line 62 def alloc_endnote_id id = @endnote_counter @endnote_counter += 1 id end |
#alloc_footnote_id ⇒ Object
56 57 58 59 60 |
# File 'lib/uniword/docx/id_allocator.rb', line 56 def alloc_footnote_id id = @footnote_counter @footnote_counter += 1 id end |
#alloc_para_id ⇒ Object
78 79 80 81 |
# File 'lib/uniword/docx/id_allocator.rb', line 78 def alloc_para_id @para_counter += 1 Digest::SHA256.hexdigest("para:#{@para_counter}").upcase[0, 12] end |
#alloc_rid(target:, type:, target_mode: nil) ⇒ Object
Allocate a relationship ID for a target+type pair. Returns existing rId if this target+type was already registered.
46 47 48 49 50 51 52 53 54 |
# File 'lib/uniword/docx/id_allocator.rb', line 46 def alloc_rid(target:, type:, target_mode: nil) key = [target, type.to_s] @rid_entries[key] ||= begin @rid_counter += 1 { id: "rId#{@rid_counter}", type: type.to_s, target: target, target_mode: target_mode } end @rid_entries[key][:id] end |
#alloc_rsid ⇒ Object
83 84 85 86 |
# File 'lib/uniword/docx/id_allocator.rb', line 83 def alloc_rsid @rsid_counter += 1 Digest::SHA256.hexdigest("rsid:#{@rsid_counter}").upcase[0, 12] end |
#rid_for(target:, type:) ⇒ Object
Check if a relationship has been registered for a target+type.
123 124 125 126 |
# File 'lib/uniword/docx/id_allocator.rb', line 123 def rid_for(target:, type:) key = [target, type.to_s] @rid_entries[key]&.fetch(:id, nil) end |
#seed_from_notes(footnote_entries, endnote_entries) ⇒ Object
Seed footnote/endnote counters from existing note entries.
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/uniword/docx/id_allocator.rb', line 106 def seed_from_notes(footnote_entries, endnote_entries) footnote_entries&.each do |e| id = e.id.to_i @footnote_counter = [@footnote_counter, id + 1].max if id > 0 end endnote_entries&.each do |e| id = e.id.to_i @endnote_counter = [@endnote_counter, id + 1].max if id > 0 end end |
#seed_from_rels(relationships) ⇒ Object
Seed from a relationships collection — preserves existing rIds.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/uniword/docx/id_allocator.rb', line 89 def seed_from_rels(relationships) return unless relationships relationships.each do |r| key = [r.target, r.type.to_s] @rid_entries[key] = { id: r.id, type: r.type.to_s, target: r.target, target_mode: r.target_mode, } num = r.id[/\ArId(\d+)\z/, 1]&.to_i || 0 @rid_counter = [@rid_counter, num].max end end |