Module: Uniword::Ooxml::PartRegistry
- Defined in:
- lib/uniword/ooxml/part_registry.rb
Overview
Single source of truth for OOXML package part metadata.
Maps every part kind the library writes to its package path, content type, relationship type, and [Content_Types].xml entry form (Default vs Override). Content type and relationship type literals live ONLY here; consumers (Uniword::ContentTypes, Docx::PackageDefaults, Docx::Reconciler::PackageStructure, and the Docx::PackageSerialization inject_* methods) derive from this registry instead of holding their own literals.
Open/closed: a new part kind is added by registering a PartDefinition — consumers need no further changes.
Constant Summary collapse
- OFFICE_REL_BASE =
Relationship type base URIs. These literals appear only here.
"http://schemas.openxmlformats.org/officeDocument/2006/relationships"- PACKAGE_REL_BASE =
"http://schemas.openxmlformats.org/package/2006/relationships"- CT_OFFICE =
Content type prefixes (literals appear only here).
"application/vnd.openxmlformats-officedocument"- CT_WML =
"#{CT_OFFICE}.wordprocessingml".freeze
- CT_PACKAGE =
"application/vnd.openxmlformats-package"- BUILT_INS =
Built-in registrations. Registration order is observable in emitted output: the :standard entries reproduce the historic ContentTypes.generate ordering exactly (defaults first — jpeg, png, gif, rels, xml — then overrides in the historic order). Non-standard parts follow; consumers select their own ordered subsets by key.
[ # -- Content-type Defaults (extension → content type) -- { key: :jpeg, kind: :default, extension: "jpeg", content_type: "image/jpeg", standard: true }, { key: :png, kind: :default, extension: "png", content_type: "image/png", standard: true }, { key: :gif, kind: :default, extension: "gif", content_type: "image/gif", standard: true }, { key: :rels, kind: :default, extension: "rels", content_type: "#{CT_PACKAGE}.relationships+xml", required: true, standard: true }, { key: :xml, kind: :default, extension: "xml", content_type: "application/xml", required: true, standard: true }, # -- Standard Override parts (historic generate order) -- { key: :document, kind: :override, path: "word/document.xml", content_type: "#{CT_WML}.document.main+xml", rel_type: "#{OFFICE_REL_BASE}/officeDocument", required: true, standard: true, rels_scope: :package }, { key: :numbering, kind: :override, path: "word/numbering.xml", target: "numbering.xml", content_type: "#{CT_WML}.numbering+xml", rel_type: "#{OFFICE_REL_BASE}/numbering", standard: true, rels_scope: :document }, { key: :styles, kind: :override, path: "word/styles.xml", target: "styles.xml", content_type: "#{CT_WML}.styles+xml", rel_type: "#{OFFICE_REL_BASE}/styles", required: true, standard: true, rels_scope: :document }, { key: :settings, kind: :override, path: "word/settings.xml", target: "settings.xml", content_type: "#{CT_WML}.settings+xml", rel_type: "#{OFFICE_REL_BASE}/settings", required: true, standard: true, rels_scope: :document }, { key: :web_settings, kind: :override, path: "word/webSettings.xml", target: "webSettings.xml", content_type: "#{CT_WML}.webSettings+xml", rel_type: "#{OFFICE_REL_BASE}/webSettings", required: true, standard: true, rels_scope: :document }, { key: :font_table, kind: :override, path: "word/fontTable.xml", target: "fontTable.xml", content_type: "#{CT_WML}.fontTable+xml", rel_type: "#{OFFICE_REL_BASE}/fontTable", required: true, standard: true, rels_scope: :document }, { key: :theme, kind: :override, path: "word/theme/theme1.xml", target: "theme/theme1.xml", content_type: "#{CT_OFFICE}.theme+xml", rel_type: "#{OFFICE_REL_BASE}/theme", standard: true, rels_scope: :document }, { key: :core_properties, kind: :override, path: "docProps/core.xml", content_type: "#{CT_PACKAGE}.core-properties+xml", rel_type: "#{PACKAGE_REL_BASE}/metadata/core-properties", required: true, standard: true, rels_scope: :package }, { key: :app_properties, kind: :override, path: "docProps/app.xml", content_type: "#{CT_OFFICE}.extended-properties+xml", rel_type: "#{OFFICE_REL_BASE}/extended-properties", required: true, standard: true, rels_scope: :package }, # -- Optional document parts -- { key: :footnotes, kind: :override, path: "word/footnotes.xml", target: "footnotes.xml", content_type: "#{CT_WML}.footnotes+xml", rel_type: "#{OFFICE_REL_BASE}/footnotes", rels_scope: :document }, { key: :endnotes, kind: :override, path: "word/endnotes.xml", target: "endnotes.xml", content_type: "#{CT_WML}.endnotes+xml", rel_type: "#{OFFICE_REL_BASE}/endnotes", rels_scope: :document }, { key: :comments, kind: :override, path: "word/comments.xml", target: "comments.xml", content_type: "#{CT_WML}.comments+xml", rel_type: "#{OFFICE_REL_BASE}/comments", rels_scope: :document }, { key: :bibliography, kind: :override, path: "word/sources.xml", target: "sources.xml", content_type: "#{CT_OFFICE}.bibliography+xml", rel_type: "#{OFFICE_REL_BASE}/bibliography", rels_scope: :document }, { key: :header, kind: :override, path_pattern: "word/header%<counter>d.xml", target_pattern: "header%<counter>d.xml", content_type: "#{CT_WML}.header+xml", rel_type: "#{OFFICE_REL_BASE}/header", rels_scope: :document }, { key: :footer, kind: :override, path_pattern: "word/footer%<counter>d.xml", target_pattern: "footer%<counter>d.xml", content_type: "#{CT_WML}.footer+xml", rel_type: "#{OFFICE_REL_BASE}/footer", rels_scope: :document }, { key: :custom_properties, kind: :override, path: "docProps/custom.xml", content_type: "#{CT_OFFICE}.custom-properties+xml", rel_type: "#{OFFICE_REL_BASE}/custom-properties", rels_scope: :package }, { key: :chart, kind: :override, path_pattern: "word/charts/chart%<n>d.xml", target_pattern: "charts/chart%<n>d.xml", content_type: "#{CT_OFFICE}.drawingml.chart+xml", rel_type: "#{OFFICE_REL_BASE}/chart", rels_scope: :document }, # Images carry a per-extension Default content type resolved # from the image data at save time, so content_type is nil. { key: :image, kind: :default, path_pattern: "word/media/%<name>s", target_pattern: "media/%<name>s", rel_type: "#{OFFICE_REL_BASE}/image", rels_scope: :document }, { key: :ole_object, kind: :override, path_pattern: "word/embeddings/%<name>s", target_pattern: "embeddings/%<name>s", content_type: "#{CT_OFFICE}.oleObject", rel_type: "#{OFFICE_REL_BASE}/oleObject", rels_scope: :document }, # customXml items themselves fall under the "xml" Default; # only their itemProps parts get an Override. { key: :custom_xml_item, kind: :none, path_pattern: "customXml/item%<index>d.xml" }, { key: :custom_xml_item_props, kind: :override, path_pattern: "customXml/itemProps%<index>d.xml", content_type: "#{CT_OFFICE}.customXmlProperties+xml" }, # Hyperlink relationships target external URLs; no part, no # content type. { key: :hyperlink, kind: :none, rel_type: "#{OFFICE_REL_BASE}/hyperlink", rels_scope: :document }, # Theme part of THMX (theme) packages, rooted at /theme. { key: :thmx_theme, kind: :override, path: "theme/theme1.xml", content_type: "#{CT_OFFICE}.theme+xml", required: true }, ].freeze
Class Method Summary collapse
-
.all ⇒ Array<PartDefinition>
All definitions, in registration order (a copy; safe to mutate).
-
.default_for(extension) ⇒ PartDefinition?
Find a Default definition by file extension.
-
.find_by_content_type(content_type) ⇒ PartDefinition?
First definition with this type.
- .find_by_key(key) ⇒ PartDefinition?
-
.find_by_path(path) ⇒ PartDefinition?
Find by package path; accepts both "word/styles.xml" and "/word/styles.xml" forms, and matches numbered paths against registered patterns ("word/header2.xml" → :header).
-
.override_for(part_name) ⇒ PartDefinition?
Find an Override definition by content-type part name.
-
.package_rel_types ⇒ Array<String>
Relationship type URIs whose relationships belong in the package-level _rels/.rels part.
-
.register(definition) ⇒ PartDefinition
Register a part definition.
-
.standard_defaults ⇒ Array<PartDefinition>
Definitions included in the comprehensive [Content_Types].xml that ContentTypes.generate emits for new DOCX packages.
-
.standard_overrides ⇒ Array<PartDefinition>
In registration order.
-
.unregister(key) ⇒ Array<PartDefinition>?
Remove a registered definition (primarily for tests).
Class Method Details
.all ⇒ Array<PartDefinition>
Returns all definitions, in registration order (a copy; safe to mutate).
212 213 214 |
# File 'lib/uniword/ooxml/part_registry.rb', line 212 def all definitions.dup end |
.default_for(extension) ⇒ PartDefinition?
Find a Default definition by file extension.
254 255 256 257 258 |
# File 'lib/uniword/ooxml/part_registry.rb', line 254 def default_for(extension) definitions.find do |d| d.default? && d.extension == extension.to_s end end |
.find_by_content_type(content_type) ⇒ PartDefinition?
Returns first definition with this type.
235 236 237 |
# File 'lib/uniword/ooxml/part_registry.rb', line 235 def find_by_content_type(content_type) definitions.find { |d| d.content_type == content_type } end |
.find_by_key(key) ⇒ PartDefinition?
218 219 220 |
# File 'lib/uniword/ooxml/part_registry.rb', line 218 def find_by_key(key) definitions.find { |d| d.key == key.to_sym } end |
.find_by_path(path) ⇒ PartDefinition?
Find by package path; accepts both "word/styles.xml" and "/word/styles.xml" forms, and matches numbered paths against registered patterns ("word/header2.xml" → :header).
228 229 230 231 |
# File 'lib/uniword/ooxml/part_registry.rb', line 228 def find_by_path(path) normalized = path.to_s.delete_prefix("/") definitions.find { |d| path_match?(d, normalized) } end |
.override_for(part_name) ⇒ PartDefinition?
Find an Override definition by content-type part name.
243 244 245 246 247 248 |
# File 'lib/uniword/ooxml/part_registry.rb', line 243 def override_for(part_name) normalized = part_name.to_s.delete_prefix("/") definitions.find do |d| d.override? && path_match?(d, normalized) end end |
.package_rel_types ⇒ Array<String>
Relationship type URIs whose relationships belong in the package-level _rels/.rels part.
277 278 279 |
# File 'lib/uniword/ooxml/part_registry.rb', line 277 def package_rel_types definitions.filter_map { |d| d.rel_type if d.package_rel? } end |
.register(definition) ⇒ PartDefinition
Register a part definition. Re-registering an existing key replaces it in place (preserving position); new keys append.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/uniword/ooxml/part_registry.rb', line 186 def register(definition) unless definition.is_a?(PartDefinition) raise ArgumentError, "expected PartDefinition, got #{definition.class}" end index = definitions.index { |d| d.key == definition.key } if index definitions[index] = definition else definitions << definition end definition end |
.standard_defaults ⇒ Array<PartDefinition>
Definitions included in the comprehensive [Content_Types].xml that ContentTypes.generate emits for new DOCX packages.
264 265 266 |
# File 'lib/uniword/ooxml/part_registry.rb', line 264 def standard_defaults definitions.select { |d| d.standard? && d.default? } end |
.standard_overrides ⇒ Array<PartDefinition>
Returns in registration order.
269 270 271 |
# File 'lib/uniword/ooxml/part_registry.rb', line 269 def standard_overrides definitions.select { |d| d.standard? && d.override? } end |
.unregister(key) ⇒ Array<PartDefinition>?
Remove a registered definition (primarily for tests).
206 207 208 |
# File 'lib/uniword/ooxml/part_registry.rb', line 206 def unregister(key) definitions.reject! { |d| d.key == key.to_sym } end |