Skip to content

Commit acd4935

Browse files
authored
Merge pull request #196 from ruby-docx/fix-192-missing-rels
Read documents that have no relationships part
2 parents fb0a6ec + b8dfcb6 commit acd4935

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

‎lib/docx/document.rb‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ def hyperlinks
113113
end
114114

115115
def hyperlink_relationships
116+
# @rels is nil when the document has no relationships part (#192)
117+
return Nokogiri::XML::NodeSet.new(Nokogiri::XML::Document.new) unless @rels
118+
116119
@rels.xpath("//xmlns:Relationship[contains(@Type,'hyperlink')]")
117120
end
118121

‎spec/docx/document_spec.rb‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,22 @@
157157
expect { doc.hyperlinks }.not_to raise_error
158158
expect(doc.hyperlinks).to be_a(Hash)
159159
end
160+
161+
# Regression tests for #192
162+
context 'when the document has no relationships part' do
163+
let(:doc) do
164+
described_class.open(fixture_without('basic.docx', 'word/_rels/document.xml.rels'))
165+
end
166+
167+
it 'is empty' do
168+
expect(doc.hyperlinks).to eq({})
169+
end
170+
171+
it 'still reads the document' do
172+
expect(doc.paragraphs.map(&:text)).to eq(%w[hello world])
173+
expect(doc.to_html).to include('hello')
174+
end
175+
end
160176
end
161177

162178
describe '#document_properties' do

‎spec/support/fixture_helpers.rb‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ def fixture_path(name)
99
File.join(FIXTURES_DIR, name)
1010
end
1111

12+
# The binary contents of fixture +name+ with the given zip entries left out,
13+
# for testing documents that lack a part (open it with Docx::Document.open).
14+
def fixture_without(name, *excluded_entries)
15+
Zip::File.open(fixture_path(name)) do |zip|
16+
Zip::OutputStream.write_buffer do |out|
17+
zip.each do |entry|
18+
next if !entry.file? || excluded_entries.include?(entry.name)
19+
20+
out.put_next_entry(entry.name)
21+
out.write(zip.read(entry.name))
22+
end
23+
end.string
24+
end
25+
end
26+
1227
# A path inside a per-example temporary directory, so saved documents never
1328
# land in spec/fixtures and are always cleaned up.
1429
def tmp_path(name)

0 commit comments

Comments
 (0)