Skip to content

Commit b16fbbd

Browse files
committed
Added opening streams
1 parent fea81fd commit b16fbbd

3 files changed

Lines changed: 70 additions & 48 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ doc.bookmarks.each_pair do |bookmark_name, bookmark_object|
5151
end
5252
```
5353

54+
Don't have a local file but a buffer? Docx handles those to:
55+
56+
```ruby
57+
require 'docx'
58+
59+
# Create a Docx::Document object from a remote file
60+
doc = Docx::Document.open_buffer(buffer)
61+
62+
# Everything about reading is the same as shown above
63+
```
64+
5465
### Rendering html
5566
``` ruby
5667
require 'docx'

lib/docx/document.rb

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ module Docx
2020
class Document
2121
attr_reader :xml, :doc, :zip, :styles
2222

23-
def initialize(path, &block)
23+
def initialize(path_or_io, options = {})
2424
@replace = {}
25-
@zip = Zip::File.open(path)
25+
26+
@zip = options[:use_buffer] ? Zip::File.open_buffer(path_or_io) : Zip::File.open(path_or_io)
27+
2628
@document_xml = @zip.read('word/document.xml')
2729
@doc = Nokogiri::XML(@document_xml)
2830
load_styles
@@ -32,33 +34,35 @@ def initialize(path, &block)
3234
end
3335
end
3436

35-
3637
# This stores the current global document properties, for now
3738
def document_properties
3839
{
3940
font_size: font_size
4041
}
4142
end
4243

43-
4444
# With no associated block, Docx::Document.open is a synonym for Docx::Document.new. If the optional code block is given, it will be passed the opened +docx+ file as an argument and the Docx::Document oject will automatically be closed when the block terminates. The values of the block will be returned from Docx::Document.open.
4545
# call-seq:
4646
# open(filepath) => file
4747
# open(filepath) {|file| block } => obj
4848
def self.open(path, &block)
49-
self.new(path, &block)
49+
new(path, &block)
50+
end
51+
52+
def self.open_buffer(buffer, &block)
53+
new(buffer, use_buffer: true, &block)
5054
end
5155

5256
def paragraphs
5357
@doc.xpath('//w:document//w:body/w:p').map { |p_node| parse_paragraph_from p_node }
5458
end
5559

5660
def bookmarks
57-
bkmrks_hsh = Hash.new
61+
bkmrks_hsh = {}
5862
bkmrks_ary = @doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node }
5963
# auto-generated by office 2010
60-
bkmrks_ary.reject! {|b| b.name == "_GoBack" }
61-
bkmrks_ary.each {|b| bkmrks_hsh[b.name] = b }
64+
bkmrks_ary.reject! { |b| b.name == '_GoBack' }
65+
bkmrks_ary.each { |b| bkmrks_hsh[b.name] = b }
6266
bkmrks_hsh
6367
end
6468

@@ -104,6 +108,7 @@ def save(path)
104108
Zip::OutputStream.open(path) do |out|
105109
zip.each do |entry|
106110
next unless entry.file?
111+
107112
out.put_next_entry(entry.name)
108113

109114
if @replace[entry.name]
@@ -116,7 +121,7 @@ def save(path)
116121
zip.close
117122
end
118123

119-
alias_method :text, :to_s
124+
alias text to_s
120125

121126
def replace_entry(entry_path, file_contents)
122127
@replace[entry_path] = file_contents
@@ -138,7 +143,7 @@ def load_styles
138143
# end of methods that make edits?
139144
#++
140145
def update
141-
replace_entry "word/document.xml", doc.serialize(:save_with => 0)
146+
replace_entry 'word/document.xml', doc.serialize(save_with: 0)
142147
end
143148

144149
# generate Elements::Containers::Paragraph from paragraph XML node

spec/docx/document_spec.rb

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
# coding: utf-8
21
require 'docx'
32
require 'tempfile'
43

54
describe Docx::Document do
65
before(:all) do
7-
@fixtures_path = "spec/fixtures"
6+
@fixtures_path = 'spec/fixtures'
87
@formatting_line_count = 12 # number of lines the formatting.docx file has
98
end
109

11-
describe 'reading' do
12-
before do
13-
@doc = Docx::Document.open(@fixtures_path + '/basic.docx')
14-
end
15-
10+
shared_examples_for 'reading' do
1611
it 'should read the document' do
1712
expect(@doc.paragraphs.size).to eq(2)
1813
expect(@doc.paragraphs.first.text).to eq('hello')
@@ -53,12 +48,31 @@
5348
end
5449
end
5550

51+
describe 'reading' do
52+
context 'using normal file' do
53+
before do
54+
@doc = Docx::Document.open(@fixtures_path + '/basic.docx')
55+
end
56+
57+
it_behaves_like 'reading'
58+
end
59+
60+
context 'using stream' do
61+
before do
62+
stream = File.binread(@fixtures_path + '/basic.docx')
63+
@doc = Docx::Document.open_buffer(stream)
64+
end
65+
66+
it_behaves_like 'reading'
67+
end
68+
end
69+
5670
describe 'read tables' do
5771
before do
5872
@doc = Docx::Document.open(@fixtures_path + '/tables.docx')
5973
end
6074

61-
it "should have tables with rows and cells" do
75+
it 'should have tables with rows and cells' do
6276
expect(@doc.tables.count).to eq 2
6377
@doc.tables.each do |table|
6478
expect(table).to be_an_instance_of(Docx::Elements::Containers::Table)
@@ -71,7 +85,7 @@
7185
end
7286
end
7387

74-
it "should have tables with columns and cells" do
88+
it 'should have tables with columns and cells' do
7589
@doc.tables.each do |table|
7690
table.columns.each do |column|
7791
expect(column).to be_an_instance_of(Docx::Elements::Containers::TableColumn)
@@ -82,23 +96,23 @@
8296
end
8397
end
8498

85-
it "should have proper count" do
99+
it 'should have proper count' do
86100
expect(@doc.tables[0].row_count).to eq 171
87101
expect(@doc.tables[1].row_count).to eq 2
88102
expect(@doc.tables[0].column_count).to eq 2
89103
expect(@doc.tables[1].column_count).to eq 2
90104
end
91105

92-
it "should have tables with proper text" do
93-
expect(@doc.tables[0].rows[0].cells[0].text).to eq "ENGLISH"
94-
expect(@doc.tables[0].rows[0].cells[1].text).to eq "FRANÇAIS"
95-
expect(@doc.tables[1].rows[0].cells[0].text).to eq "Second table"
96-
expect(@doc.tables[1].rows[0].cells[1].text).to eq "Second tableau"
97-
expect(@doc.tables[0].columns[0].cells[5].text).to eq "aphids"
98-
expect(@doc.tables[0].columns[1].cells[5].text).to eq "puceron"
106+
it 'should have tables with proper text' do
107+
expect(@doc.tables[0].rows[0].cells[0].text).to eq 'ENGLISH'
108+
expect(@doc.tables[0].rows[0].cells[1].text).to eq 'FRANÇAIS'
109+
expect(@doc.tables[1].rows[0].cells[0].text).to eq 'Second table'
110+
expect(@doc.tables[1].rows[0].cells[1].text).to eq 'Second tableau'
111+
expect(@doc.tables[0].columns[0].cells[5].text).to eq 'aphids'
112+
expect(@doc.tables[0].columns[1].cells[5].text).to eq 'puceron'
99113
end
100114

101-
it "should read embedded links" do
115+
it 'should read embedded links' do
102116
expect(@doc.tables[0].columns[1].cells[1].text).to match(/^Directive/)
103117
end
104118

@@ -109,7 +123,7 @@
109123
end
110124
end
111125

112-
describe 'editing' do
126+
describe 'editing' do
113127
before do
114128
@doc = Docx::Document.open(@fixtures_path + '/editing.docx')
115129
end
@@ -173,8 +187,7 @@
173187
end
174188

175189
it 'should allow content deletion' do
176-
expect{@doc.paragraphs.first.remove!}.to change{@doc.paragraphs.size}.by(-1)
177-
190+
expect { @doc.paragraphs.first.remove! }.to change { @doc.paragraphs.size }.by(-1)
178191
end
179192
end
180193

@@ -223,7 +236,6 @@
223236
end
224237

225238
it 'should contain a paragraph with multiple text runs' do
226-
227239
end
228240

229241
it 'should detect normal formatting' do
@@ -337,13 +349,11 @@
337349
temp_file.close
338350
temp_file.unlink
339351
# ensure temp file has been removed
340-
expect(File.exists?(@new_doc_path)).to eq(false)
352+
expect(File.exist?(@new_doc_path)).to eq(false)
341353
end
342354

343355
after do
344-
if File.exists?(@new_doc_path)
345-
File.delete(@new_doc_path)
346-
end
356+
File.delete(@new_doc_path) if File.exist?(@new_doc_path)
347357
end
348358

349359
context 'wps modified docx file' do
@@ -400,7 +410,7 @@
400410
expect(@doc.paragraphs[8].to_html.scan(regex).flatten.first.split(';').include?('text-align:right')).to eq(true)
401411
end
402412

403-
it "should set font size on styled paragraphs" do
413+
it 'should set font size on styled paragraphs' do
404414
regex = /(\<p{1})[^\>]+style\=\"([^\"]+).+(<\/p>)/
405415
scan = @doc.paragraphs[9].to_html.scan(regex).flatten
406416
expect(scan.first).to eq '<p'
@@ -441,32 +451,28 @@
441451
it 'should output styled html' do
442452
expect(@formatted_line.to_html.scan('<span style="text-decoration:underline;"><strong><em>all</em></strong></span>').size).to eq 1
443453
end
444-
445454
end
446455

447456
describe 'replacing contents' do
448457
let(:replacement_file_path) { @fixtures_path + '/replacement.png' }
449-
let(:temp_file_path){ Tempfile.new(['docx_gem', '.docx']).path }
450-
let(:entry_path){ 'word/media/image1.png' }
451-
let(:doc){ Docx::Document.open(@fixtures_path + '/replacement.docx') }
458+
let(:temp_file_path) { Tempfile.new(['docx_gem', '.docx']).path }
459+
let(:entry_path) { 'word/media/image1.png' }
460+
let(:doc) { Docx::Document.open(@fixtures_path + '/replacement.docx') }
452461

453462
it 'should replace existing file within the document' do
454-
File.open replacement_file_path, "rb" do |io|
463+
File.open replacement_file_path, 'rb' do |io|
455464
doc.replace_entry entry_path, io.read
456465
end
457466

458467
doc.save(temp_file_path)
459468

460-
File.open replacement_file_path, "rb" do |io|
461-
expect(Zip::File.open(temp_file_path).read entry_path).to eq io.read
469+
File.open replacement_file_path, 'rb' do |io|
470+
expect(Zip::File.open(temp_file_path).read(entry_path)).to eq io.read
462471
end
463472
end
464473

465474
after do
466-
if File.exists?(temp_file_path)
467-
File.delete(temp_file_path)
468-
end
475+
File.delete(temp_file_path) if File.exist?(temp_file_path)
469476
end
470477
end
471478
end
472-

0 commit comments

Comments
 (0)