I'm creating a services to generate or modify a docx file with some data from my database this is the code:
require 'docx'
require 'prawn'
class DocumentGenerator
TEMPLATE_PATH = Rails.root.join('app', 'assets', 'docs_templates', 'plantilla_word.docx')
def initialize(dni)
@dni = dni
end
def generate_docx
byebug
if File.exist?(TEMPLATE_PATH)
doc = Docx::Document.open(TEMPLATE_PATH)
doc.bookmarks.each_pair do |bookmark_name, bookmark_object|
puts bookmark_name
end
byebug
# doc.bookmarks['first_lastname'].replace(@dni.first_lastname)
# doc.bookmarks['second_lastname'].replace(@dni.second_lastname)
# doc.bookmarks['names'].replace(@dni.names)
# doc.bookmarks['dni_number'].replace(@dni.dni_number)
file_path = Rails.root.join('tmp', 'generated_doc.docx')
doc.save(file_path)
file_path
else
raise "El archivo de plantilla no se encuentra en la ruta: #{TEMPLATE_PATH}"
end
end
def generate_pdf
pdf = Prawn::Document.new
pdf.text "First Lastname: #{@dni.first_lastname}"
pdf.text "Second Lastname: #{@dni.second_lastname}"
pdf.text "Names: #{@dni.names}"
pdf.text "DNI Number: #{@dni.dni_number}"
file_path = Rails.root.join('tmp', 'generated_pdf.pdf')
pdf.render_file(file_path)
file_path
end
end
The problem is when I try to open the file to replace the bookmarks with my data
10: def generate_docx
11: byebug
=> 12: if File.exist?(TEMPLATE_PATH)
13: doc = Docx::Document.open(TEMPLATE_PATH)
14: doc.bookmarks.each_pair do |bookmark_name, bookmark_object|
15: puts bookmark_name
16: end
(byebug) Docx::Document.open(TEMPLATE_PATH)
*** NoMethodError Exception: undefined method `close' for nil
nil
I already try move the file, use
Docx::Document.parse(File.read(TEMPLATE_PATH))
, but nothing appear solve the issue.
Your TEMPLATE_PATH
constant contains a Pathname
object because Rails.root.join
returns a Pathname
rather than just a String.
Now, Docx::Document#initialize
checks the provided argument and decides whether it constitutes a filename (if it is a String) or falls back to assume it's a IO-like object. With your Pathname
object, this detection fails.
As the Docx gem tries to use the Pathname object as an IO object and passes it to Zip::File.open_buffer
, this method call fails with the following exception:
RuntimeError (Zip::File.open_buffer expects a String or IO-like argument (responds to tell, seek, read, eof, close). Found: Pathname)
Now, because Docx::Document#initialize
has a trailing ensure
statement which should close the opened document, this fails again as the document (i.e. @zip
) was never opened and is thus nil
, resulting in the exception you see there.
To circumvent this issue, you should thus pass your TEMPLATE_PATH
as a String by changing the assignment to:
TEMPLATE_PATH = Rails.root.join('app', 'assets', 'docs_templates', 'plantilla_word.docx').to_s