I have tried everything and still can't get this to work.
I have two types of application in my system that are pre-qualified and sent to lenders,
1) one generates a pdf
2) second should use active storage attachments and attach them to an ActionMailer
First one is working the second is giving me the following error:
[ActionMailer::DeliveryJob] [905177a5-b0e9-46f4-ba9a-fc4630e873f9]
Error performing ActionMailer::DeliveryJob (Job ID:
905177a5-b0e9-46f4-ba9a-fc4630e873f9) from Async(mailers) in 140.14ms:
Errno::ENOENT (No such file or directory # rb_sysopen -
https://funderhunt.co/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBZ1lIIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--fa91a15681c23d47d767169c7821601aa15ed2b3/Statuses.pages?disposition=attachment):
The link is correct tho:
My mailer code for this part looks like this:
q = 0
statement.files.each do |file|
q += 1
bank_statement = File.read(rails_blob_url(file, disposition: "attachment"))
attachments["statement_#{q}.pdf"] = { :mime_type => 'application/pdf', :content => bank_statement }
end
What is wrong? Can you please help. Thanks in advance.
If somebody lands here looking for a general solution:
modelname.attachments.each do |file|
attachments[file.blob.filename.to_s] = {
mime_type: file.blob.content_type,
content: file.blob.download
}
end
You should be able to do something like,
statement.files.each_with_index do |file, q|
attachments["statement_#{q + 1}.pdf"] = { mime_type: 'application/pdf', content: file.blob.download }
end
file.blob.download will return the content of the file, similar to File.read.
Related
I created an ActiveJob to process my carrier waves uploads. However, when I upload more than one image, I get the following error for the second file:
Errno::ENOENT (No such file or directory # rb_sysopen - C:/Users/tdavi/AppData/Local/Temp/RackMultipart20180830-392-z2s2i.jpg)
Here's the code in my controller:
if #post.save
files = params[:post_attachments].map { |p|
{image: p['photo'][:image].tempfile.path, description: p['photo'][:decription]}
}
ProcessPhotosJob.perform_later(#post.id, files.to_json)
format.html { render :waiting }
end
And my ActiveJob
require 'json'
class ProcessPhotosJob < ApplicationJob
queue_as :default
def perform(post_id, photos_json)
post = Post.friendly.find(post_id)
photos = JSON.parse photos_json
photos.each do |p|
src_file = File.new(p['image'])
post.post_attachments.create!(:photo => src_file, :description => p[:description])
end
post.processed = true
post.save
end
end
When I upload only one file to upload, it works okay.
You should not pass Tempfile to the queued jobs.
First of all - TempFiles can be deleted automatically by Ruby (docs, explanation)
If you would like to upload file(s) and process them later (in a background), then I would suggest you check this question.
I have some code that runs every week using Whenever gem. It creates a file and sends a download link.
ApplicationMailer
def weekly_email
file = CSVData::Report.new(time).create_csv
#filename = File.basename(file.path)
mail(
:from => "from",
:to => "to",
:subject => "Data #{time.strftime("%m/%d/%Y")}"
)
end
The file is stored in downloads/. The issue is, it works in testing, it works sometimes, but sometimes it generate a Missing File error:
Error:
ActionController::MissingFile occurred in file_downloads#download:
Cannot read file downloads/data_9_17_2015.csv
actionpack (3.2.13) lib/action_controller/metal/data_streaming.rb:71:in `send_file'
Here is the download code:
def download
send_file "downloads/#{params[:filename]}.csv", type: "application/csv", x_sendfile: true
end
Here is a shortened version of the file creation:
def create_file
file_data = some_cool_data
file = create_file(File.join(Dir.pwd, "/downloads/#{#file_name}.csv"))
file.write(file_data)
file.close
file
end
def create_file(path)
FileUtils.mkdir_p(File.dirname(path))
File.new(path, "w+")
end
I dont think its an issue with the file reference (i.e. /dowloads vs downloads), because it works fine in testing and sometimes in production. It just seems that the file is getting deleted. What could be causing the missing file error?
I am using ActionMailer in a Ruby on Rails app to read emails (ruby 1.9.3, rails 3.2.13).
I have an email that has a winmail.dat file attached to it (ms-tnef) and I am using the tnef gem to extract its contents.
The problem is that when I read the attachment from the mail, it gets corrupted and tnef can not extract files from it.
$ tnef winmail.dat
ERROR: invalid checksum, input file may be corrupted
Extracting the winmail.dat attachment using any mail app, the extracted winmail.dat works fine with tnef and I got it's content.
Comparing the two files I noticed that:
- original file is bigger (76k against 72k)
- they differ on line breaks: Orginal file has the windows format (0D 0A) and the file saved by rails has the linux format (0A)
I wrote this test:
it 'should extract winmail.dat from email and extract its contents' do
file_path = "#{::Rails.root}/spec/files/winmail-dat-001.eml"
message = Mail::Message.new(File.read(file_path))
anexo = message.attachments[0]
files = []
Tnef.unpack(anexo) do |file|
files << File.basename(file)
end
puts files.inspect
files.size.should == 2
end
That fails with these messages:
WARNING: invalid checksum, input file may be corrupted
Invalid RTF CRC, input file may be corrupted
WARNING: invalid checksum, input file may be corrupted
Assertion failed: ((attr->lvl_type == LVL_MESSAGE) || (attr->lvl_type == LVL_ATTACHMENT)), function attr_read, file attr.c, line 240.
Errno::EPIPE: Broken pipe
anexo = message.attachments[0]
=> #<Mail::Part:2159872060, Multipart: false, Headers: <Content-Type: application/ms-tnef; name="winmail.dat">, <Content-Transfer-Encoding: quoted-printable>, <Content-Disposition: attachment; filename="winmail.dat">>
I tried to save it to disk as bynary, and read it again, but I got the same result
it 'should extract winmail.dat from email and extract its contents' do
file_path = "#{::Rails.root}/spec/files/winmail-dat-001.eml"
message = Mail::Message.new(File.read(file_path))
anexo = message.attachments[0]
tmpfile_name = "#{::Rails.root}/tmp/#{anexo.filename}"
File.open(tmpfile_name, 'w+b', 0644) { |f| f.write anexo.body.decoded }
anexo = File.open(tmpfile_name)
files = []
Tnef.unpack(anexo) do |file|
files << File.basename(file)
end
puts files.inspect
files.size.should == 2
end
How should I read the attachment?
The method anexo.body.decoded calls the decode method of the best suited encoding (Mail::Encodings) for the attachment, in your case quoted_printable.
Some of these encodings (7bit, 8bit and quoted_printable), perform a conversion, changing different types of line breaks to the platform specific line break.
the *quoted_printable" call .to_lf that corrupt the winmail.dat file
# Decode the string from Quoted-Printable. Cope with hard line breaks
# that were incorrectly encoded as hex instead of literal CRLF.
def self.decode(str)
str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first.to_lf
end
mail/core_extensions/string.rb:
def to_lf
to_str.gsub(/\n|\r\n|\r/) { "\n" }
end
To solve it you have perform the same encoding without the last .to_lf.
To do that you can create a new encoding that does not corrupt your file and use it to encode you attachment.
create the file:
lib/encodings/tnef_encoding.rb
require 'mail/encodings/7bit'
module Mail
module Encodings
# Encoding to handle Microsoft TNEF format
# It's pretty similar to quoted_printable, except for the 'to_lf' (decode) and 'to_crlf' (encode)
class TnefEncoding < SevenBit
NAME='tnef'
PRIORITY = 2
def self.can_encode?(str)
EightBit.can_encode? str
end
def self.decode(str)
# **difference here** removed '.to_lf'
str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first
end
def self.encode(str)
# **difference here** removed '.to_crlf'
[str.to_lf].pack("M")
end
def self.cost(str)
# These bytes probably do not need encoding
c = str.count("\x9\xA\xD\x20-\x3C\x3E-\x7E")
# Everything else turns into =XX where XX is a
# two digit hex number (taking 3 bytes)
total = (str.bytesize - c)*3 + c
total.to_f/str.bytesize
end
private
Encodings.register(NAME, self)
end
end
end
To use your custom encoding you must, first, register it:
Mail::Encodings.register('tnef', Mail::Encodings::TnefEncoding)
And then, set it as your preferred encoding for the attachment:
anexo.body.encoding('tnef')
Your test would, then, become:
it 'should extract winmail.dat from email and extract its contents' do
file_path = "#{::Rails.root}/spec/files/winmail-dat-001.eml"
message = Mail::Message.new(File.read(file_path))
anexo = message.attachments[0]
tmpfile_name = "#{::Rails.root}/tmp/#{anexo.filename}"
Mail::Encodings.register('tnef', Mail::Encodings::TnefEncoding)
anexo.body.encoding('tnef')
File.open(tmpfile_name, 'w+b', 0644) { |f| f.write anexo.body.decoded }
anexo = File.open(tmpfile_name)
files = []
Tnef.unpack(anexo) do |file|
files << File.basename(file)
end
puts files.inspect
files.size.should == 2
end
Hope it helps!
I am developing a rails 3.2 application with which users can download pdfs. I enjoy test driven development a lot using rspec and shoulda matchers, but I'm at a loss with this one.
I have the following code inside my controller:
def show_as_pdf
#client = Client.find(params[:client_id])
#invoice = #client.invoices.find(params[:id])
PDFKit.configure do |config|
config.default_options = {
:footer_font_size => "6",
:encoding => "UTF-8",
:margin_top=>"1in",
:margin_right=>"1in",
:margin_bottom=>"1in",
:margin_left=>"1in"
}
end
pdf = PDFKit.new(render_to_string "invoices/pdf", layout: false)
invoice_stylesheet_path = File.expand_path(File.dirname(__FILE__) + "/../assets/stylesheets/pdfs/invoices.css.scss")
bootstrap_path = File.expand_path(File.dirname(__FILE__) + "../../../vendor/assets/stylesheets/bootstrap.min.css")
pdf.stylesheets << invoice_stylesheet_path
pdf.stylesheets << bootstrap_path
send_data pdf.to_pdf, filename: "#{#invoice.created_at.strftime("%Y-%m-%d")}_#{#client.name.gsub(" ", "_")}_#{#client.company.gsub(" ", "_")}_#{#invoice.number.gsub(" ", "_")}", type: "application/pdf"
return true
end
This is fairly simple code, all it does is configure my PDFKit and download the generated pdf. Now I want to test the whole thing, including:
Assignment of instance variables (easy, of course, and that works)
The sending of data, i.e. the rendering of the pdf => And this is where I'm stuck
I have tried the following:
controller.should_receive(:send_data)
but that gives me
Failure/Error: controller.should_receive(:send_data)
(#<InvoicesController:0x007fd96fa3e580>).send_data(any args)
expected: 1 time
received: 0 times
Does anyone know of a way to test that the pdf is actually downloaded/sent? Also, what more things do you see that should be tested for good test coverage? E.g., testing for the data type, i.e. application/pdf, would be nice.
Thanks!
Not sure why you're getting that failure but you could instead test the response headers:
response_headers["Content-Type"].should == "application/pdf"
response_headers["Content-Disposition"].should == "attachment; filename=\"<invoice_name>.pdf\""
You asked for advice regarding better test coverage. I thought I'd recommend this: https://www.destroyallsoftware.com/screencasts. These screencasts have had a huge impact on my understanding of test-driven development -- highly recommended!
I recommend using the pdf-inspector gem for writing specs for PDF related Rails actions.
Here's an exemplary spec (which assumes the Rails #report action writes data about a Ticket model in the generated PDF):
describe 'GET /report.pdf' do
it 'returns downloadable PDF with the ticket' do
ticket = FactoryGirl.create :ticket
get report_path, format: :pdf
expect(response).to be_successful
analysis = PDF::Inspector::Text.analyze response.body
expect(analysis.strings).to include ticket.state
expect(analysis.strings).to include ticket.title
end
end
I do some integration testing like this :
def user.excel_import
fixture_excel = fixture_file_upload('goodsins.xls', 'text/xls')
post excel_import_goods_ins_goods_ins_path, :dump=> {:excel_file=>fixture_excel}, :html => { :multipart => "true" }
assert_response :redirect
assert_redirected_to goods_ins_path
end
But when I run the testing it is said that : goodsins.xls file does not exist.
FYI : I put the file in the folder that named fixtures.
Any idea? Thx u very much
The notes here: http://apidock.com/rails/ActionController/TestProcess/fixture_file_upload indicate that you need to include a slash before the path or file name.
try fixture_file_upload('/goodsins.xls', 'text/xls') and see if that helps.
fixture_file_upload Source:
# File actionpack/lib/action_controller/test_process.rb, line 523
def fixture_file_upload(path, mime_type = nil, binary = false)
if ActionController::TestCase.respond_to?(:fixture_path)
fixture_path = ActionController::TestCase.send(:fixture_path)
end
ActionController::TestUploadedFile.new("#{fixture_path}#{path}",
mime_type, binary)
end
Update from Question Owner:
Solution:
add include ActionDispatch::TestProcess to test_helper.rb