diff --git a/lib/docx/document.rb b/lib/docx/document.rb index 5cd222e..7900ad0 100755 --- a/lib/docx/document.rb +++ b/lib/docx/document.rb @@ -113,6 +113,9 @@ def hyperlinks end def hyperlink_relationships + # @rels is nil when the document has no relationships part (#192) + return Nokogiri::XML::NodeSet.new(Nokogiri::XML::Document.new) unless @rels + @rels.xpath("//xmlns:Relationship[contains(@Type,'hyperlink')]") end diff --git a/spec/docx/document_spec.rb b/spec/docx/document_spec.rb index cf9a49c..007cf02 100755 --- a/spec/docx/document_spec.rb +++ b/spec/docx/document_spec.rb @@ -157,6 +157,22 @@ expect { doc.hyperlinks }.not_to raise_error expect(doc.hyperlinks).to be_a(Hash) end + + # Regression tests for #192 + context 'when the document has no relationships part' do + let(:doc) do + described_class.open(fixture_without('basic.docx', 'word/_rels/document.xml.rels')) + end + + it 'is empty' do + expect(doc.hyperlinks).to eq({}) + end + + it 'still reads the document' do + expect(doc.paragraphs.map(&:text)).to eq(%w[hello world]) + expect(doc.to_html).to include('hello') + end + end end describe '#document_properties' do diff --git a/spec/support/fixture_helpers.rb b/spec/support/fixture_helpers.rb index 416d375..dd506c2 100644 --- a/spec/support/fixture_helpers.rb +++ b/spec/support/fixture_helpers.rb @@ -9,6 +9,21 @@ def fixture_path(name) File.join(FIXTURES_DIR, name) end + # The binary contents of fixture +name+ with the given zip entries left out, + # for testing documents that lack a part (open it with Docx::Document.open). + def fixture_without(name, *excluded_entries) + Zip::File.open(fixture_path(name)) do |zip| + Zip::OutputStream.write_buffer do |out| + zip.each do |entry| + next if !entry.file? || excluded_entries.include?(entry.name) + + out.put_next_entry(entry.name) + out.write(zip.read(entry.name)) + end + end.string + end + end + # A path inside a per-example temporary directory, so saved documents never # land in spec/fixtures and are always cleaned up. def tmp_path(name)