Rails wicked_pdf gem generating empty pdf - ruby-on-rails

I am trying to download my dynamic content as a pdf file. The pdf is being generated but with no content in it, not even a simple text(when I test with that).
As I need a lot of data which I need to calculate and send parameters without refreshing page so I am doing it through an Ajax request.
$("#download_pdf").click(function() {
report_type = $("#report_type").val();
start_date = $("#start_date").val();
end_date = $("#end_date").val();
date_type = $("#date_type").val();
$.ajax({
data: { report_type: report_type, start_date: start_date, end_date: end_date, date_type: date_type },
url: '/reports/generate_pdf.pdf',
success: function(data) {
var blob=new Blob([data]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Report_"+new Date()+".pdf";
link.click();
console.log("pdf printed");
}
});
});
Here is my ruby code in the controller:
def generate_pdf
#results = get_report_result_by_datetype(params[:report_type], params[:start_date], params[:end_date], params[:date_type])
#type = params[:report_type].to_i
#start_date = params[:start_date]
#end_date = params[:end_date]
respond_to do |format|
format.html
format.pdf do
render pdf: "report",
layout: 'pdf_layout',
template: 'reports/generate_pdf.html.erb',
encoding: 'UTF8',
print_media_type: true,
disposition: 'attachment',
page_size: 'letter',
orientation: 'landscape',
lowquality: 'false',
debug: true
end
end
end
There is no issue in the data which I am fetching.
Note: The most strange thing I noticed is that when the calculated array is small the generated pdf has one page and if it is big the generated pdf shows multiple pages.
I would really appreciate if someone points where I am going wrong.
Thanks in Advance!!

Although I don't know how to fix your wicked_pdf code, I can recommend the following using the gem prawn (which is not rails-specific and removes some of the Rails magic from the equation):
format.pdf do
pdf_path = Rails.root.join("tmp", "my_file_name.pdf")
template_path = Rails.root.join("app", "views", "reports", "generate_pdf.html.erb")
this = binding
Prawn::Document.generate(pdf_path) do
text Erb.new(template_path).result(this)
end
render file: pdf_path
end
Haven't actually tested it so apologies if it doesn't work.

Related

How to return html without layout in Ruby on Rails

This is javascript
$('#course_name, #subject').on('input', () => {
$.ajax({
url: '/search_courses_list',
method: 'get',
data: {
course: {name: $('#course_name').val()},
subject: $('#subject').val()
},
success: function (data) {
$('#course_search_result').html(data)
}
})
}).trigger('input')
This is the controller method.
def search_courses_list
#courses = Course.joins(:segments).where('lower(name) like :str', str: "%#{params["course"]["name"].downcase}%")
if (params["subject"]!="")
#courses = #courses.where({segments: {subject_id: params["subject"]}})
end
render :search_result, courses: #courses
end
def search_result
#courses = courses
end
I tried to use ajax to directly get the Erb generated text as Html strings and insert them directly into the container I want. Is that possible that I get only the Erb file I want without surrounded by the layout?
https://guides.rubyonrails.org/layouts_and_rendering.html#the-layout-option
You can do this on your action
render layout: false
You can simply replace render :search_result, courses: #courses with render :search_result, courses: #courses, layout: false

Can't resolve image into URL: undefined method `polymorphic_url' for #<ActionView::Base

In my Rails app I have a Job to create PDF files with WickedPDF. This worked fine until I migrated to Rails 5.2, now I get this error:
ActionView::Template::Error (Can't resolve image into URL: undefined method `polymorphic_url' for #<ActionView::Base:0x0000000004854590>):
The code where this happens is this line in the view:
image_tag(image)
This view is rendered from the Job, which I added in full below. The error happens when executing the last method in the class: private def render_image_pdf(view, pdf_name, image), where it is rendering a PDF.
After rolling back to Rails 5.1, everything works fine again, so I'm pretty sure it has something to do with an added/changed feature in Rails 5.2. So what should I change to make this working again?
The PDF Job:
class SendInvoiceAsAttachmentJob < ApplicationJob
require 'open-uri'
queue_as :default
def perform(target, invoice, send_invoice, to, cc, subject, body1, body2, body3)
view = ActionView::Base.new(ActionController::Base.view_paths, {})
view.extend(ApplicationHelper)
view.extend(Rails.application.routes.url_helpers)
# Create invoice as PDF
pdf = Rails.root.join('tmp', "tmp_invoice.pdf")
File.open(pdf, 'wb') do |file|
file << render_invoice_pdf(invoice, view, "tmp_invoice.pdf")
end
# Add (signed) timesheet as PDF
projectuser = Projectuser.where(project_id: invoice.project_id, user_id: invoice.user_id).order(:updated_at).last
if projectuser.blank? or invoice.fixed_price? or invoice.project.service?
pdf_name = "#{invoice.set_pdf_filename}.pdf"
pdf_to_email = pdf
else
if invoice.timesheet and invoice.timesheet.signed_copy?(projectuser)
# Download signed copy
timesheet_copy = TimesheetCopy.where(projectuser_id: projectuser.id, timesheet_id: invoice.timesheet_id).first
timesheet_file = Rails.root.join('tmp', timesheet_copy.attachment_file_name)
timesheet_name = timesheet_copy.attachment_file_name
if timesheet_copy.attachment.url.index("http").blank?
download = open("https:#{timesheet_copy.attachment.url}")
else
download = open(timesheet_copy.attachment.url)
end
IO.copy_stream(download, timesheet_file)
# Push into a PDF when image
if timesheet_copy.attachment_file_name.index(".pdf") == nil
timesheet_tmp = Rails.root.join('tmp', "tmp_timesheet.pdf")
File.open(timesheet_tmp, 'wb') do |file|
file << render_image_pdf(view, "tmp_timesheet.pdf", timesheet_file)
end
timesheet_file = timesheet_tmp
File.delete Rails.root.join('tmp', timesheet_copy.attachment_file_name)
end
else
# Create timesheet PDF
timesheet = Timesheet.find(invoice.timesheet_id)
timesheet_builder = TimesheetBuilder.new(timesheet.month, timesheet.year)
timesheet_name = "Timesheet #{timesheet.user.full_name} #{I18n.t("date.month_names")[timesheet.month]} #{timesheet.year}"
timesheet_file = Rails.root.join('tmp', timesheet_name)
File.open(timesheet_file, 'wb') do |file|
file << render_timesheet_pdf(timesheet, view, timesheet_name, projectuser, timesheet_builder)
end
end
# Combine the 2 PDF's
combined_pdf = CombinePDF.new
combined_pdf << CombinePDF.load(pdf, allow_optional_content: true)
combined_pdf << CombinePDF.load(timesheet_file, allow_optional_content: true)
pdf_name = "#{invoice.set_pdf_filename}.pdf"
combined_pdf.save Rails.root.join('tmp', pdf_name)
pdf_to_email = Rails.root.join('tmp', pdf_name)
File.delete(timesheet_file)
end
# Send email
if target == "basecone"
company = Company.find(invoice.company_id)
UserMailer.send_pdf_to_basecone(company.basecone_email, pdf_to_email, pdf_name).deliver
else
UserMailer.invoice_email(invoice, send_invoice, to, cc, subject, body1, body2, body3, pdf_to_email, pdf_name).deliver
end
File.delete(pdf_to_email)
end
private def render_invoice_pdf(invoice, view, pdf_name)
WickedPdf.new.pdf_from_string(
view.render(
pdf: pdf_name,
template: 'admin/invoices/show_as_attachment.pdf.haml',
locals: { invoice: invoice, copy_invoice: nil },
print_media_type: true,
orientation: 'Portrait',
page_size: 'A4'
)
)
end
private def render_timesheet_pdf(timesheet, view, pdf_name, projectuser, timesheet_builder)
WickedPdf.new.pdf_from_string(
view.render(
pdf: pdf_name,
template: 'timesheets/show_as_attachment.pdf.haml',
locals: { timesheet: timesheet, timesheet_builder: timesheet_builder, projectuser: projectuser },
print_media_type: true,
orientation: 'Portrait',
page_size: 'A4'
)
)
end
private def render_image_pdf(view, pdf_name, image)
WickedPdf.new.pdf_from_string(
view.render(
pdf: pdf_name,
template: 'admin/invoices/image_timesheet_as_attachment.pdf.haml',
locals: { image: image },
print_media_type: true,
orientation: 'Portrait',
page_size: 'A4'
)
)
end
end
Use wicked_pdf_image_tag Instead of image_tag
<%=wicked_pdf_image_tag image%>
Instead of manually constructing the ActionView setup (and thus needing to add all the right helpers, which is where you're going wrong -- I think some moved around), you can use the newish ActionController::Renderer API to perform a view render within the context of an arbitrary controller.
# Very untested; please treat this is syntactically-valid pseudocode...
WickedPdf.new.pdf_from_string(
ApplicationController.render(
template: 'admin/invoices/show_as_attachment.pdf.haml',
assigns: {
pdf: pdf_name,
print_media_type: true,
orientation: 'Portrait',
page_size: 'A4',
},
locals: { invoice: invoice, copy_invoice: nil },
)
)

jquery fileupload rails s3 shrine image upload error

I am sort of new to rails and I am trying to upload images directly to S3 with Shrine. I got direct uploads to S3 to work perfectly, however, when I introduced jquery file upload and upload an image, chrome console throws
this error
at me. I'm not sure what I'm doing wrong and I can't seem to find a solution anywhere online. I get that it's a presign error and it's probably not finding the cache link but I don't know how to resolve that.
EDIT: This was solved by including the presign code in the Routes file and altering the storage location in the uploads.js to the correct location. Now, however, I have an issue with the files being rolled back when they attempt to upload.
I'm using the cloud based ide C9,
This is my uploads.js file:
$(document).on("turbolinks:load", function(){
$("[type=file]").fileupload({
add: function(e, data) {
console.log("add", data);
data.progressBar = $('<div class="progress"><div class="determinate"
style="width: 70%"></div></div>').insertBefore("form")
var options = {
extension: data.files[0].name.match(/(\.\w+)?$/)[0], //set the
file extention
_: Date.now() //prevent caching
};
$.getJSON("/autos/upload/cache/presign", options, function(result) {
console.log("presign", result);
data.formData = result['fields'];
data.url = result['url'];
data.paramName = "file";
data.submit()
});
},
progress: function(e, data) {
console.log("progress", data);
var progress = parseInt(data.loaded / data.total * 100, 10);
var percentage = progress.toString() + '%'
data.progressBar.find(".progress-bar").css("width",
percentage).html(percentage);
},
done: function(e, data) {
console.log("done", data);
data.progressBar.remove();
var image = {
id: data.formData.key.match(/cache\/(.+)/)[1], // we have to
remove the prefix part
storage: 'cache',
metadata: {
size: data.files[0].size,
filename: data.files[0].name.match(/[^\/\\]+$/)[0], // IE return full
path
mime_type: data.files[0].type
}
}
form = $(this).closest("form");
form_data = new FormData(form[0]);
form_data.append($(this).attr("name"), JSON.stringify(image))
$.ajax(form.attr("action"), {
contentType: false,
processData: false,
data: form_data,
method: form.attr("method"),
dataType: "json"
}).done(function(data) {
console.log("done from rails", data);
});
}
});
});
My routes.rb file includes:
mount ImageUploader::UploadEndpoint => "/images/upload"
mount Shrine.presign_endpoint(:cache) => "/autos/upload/cache/presign"
I have a model which accepts these images as well as other fields called Autos, this is included in the Autos file:
include ImageUploader[:image]
My Autos Controller is:
class AutosController < ApplicationController
before_action :find_auto, only: [:show, :edit, :update, :destroy]
def index
#autos = Auto.all.order("created_at DESC")
end
def show
end
def new
#auto = current_user.autos.build
end
def create
#auto = current_user.autos.build(auto_params[:auto])
if #auto.save
flash[:notice] = "Successfully created post."
redirect_to autos_path
else
render 'new'
end
end
def edit
end
def update
if #auto.update(auto_params[:auto])
flash[:notice] = "Successfully updated post."
redirect_to auto_path(#auto)
else
render 'edit'
end
end
def destroy
#auto.destroy
redirect_to autos_path
end
private
def auto_params
params.require(:auto).permit(:title, :price, :description, :contact, :image, :remove_image)
end
def find_auto
#auto = Auto.find(params[:id])
end
end
Assuming your image_uploader.rb has the ImageUploader class defined and given that your presign endpoint is something like /autos/upload/cache/presign, your routes.rb should have the presign route defined like so:
mount ImageUploader.presign_endpoint(:cache) => '/autos/upload/cache/presign'
I hope this single change in the route file would make you able to get the presign object that should contain 3 keys: url, fields and headers
# GET /autos/upload/cache/presign
{
"url": "https://my-bucket.s3-eu-west-1.amazonaws.com",
"fields": {
"key": "cache/b7d575850ba61b44c8a9ff889dfdb14d88cdc25f8dd121004c8",
"policy": "eyJleHBpcmF0aW9uIjoiMjAxNS0QwMToxMToyOVoiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJzaHJpbmUtdGVzdGluZyJ9LHsia2V5IjoiYjdkNTc1ODUwYmE2MWI0NGU3Y2M4YTliZmY4OGU5ZGZkYjE2NTQ0ZDk4OGNkYzI1ZjhkZDEyMTAwNGM4In0seyJ4LWFtei1jcmVkZW50aWFsIjoiQUtJQUlKRjU1VE1aWlk0NVVUNlEvMjAxNTEwMjQvZXUtd2VzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LHsieC1hbXotYWxnb3JpdGhtIjoiQVdTNC1ITUFDLVNIQTI1NiJ9LHsieC1hbXotZGF0ZSI6IjIwMTUxMDI0VDAwMTEyOVoifV19",
"x-amz-credential": "AKIAIJF55TMZYT6Q/20151024/eu-west-1/s3/aws4_request",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-date": "20151024T001129Z",
"x-amz-signature": "c1eb634f83f96b69bd675f535b3ff15ae184b102fcba51e4db5f4959b4ae26f4"
},
"headers": {}
}
When upload starts, you will now find this object in developer console instead of the previous 404 not found error.
UPDATE
I think you are very close to the solution. In your create/update actions, use auto_params[:auto] instead of auto_params
You would also like to check the RoR guide on Association Basics for collection methods
I think you following the tutorial of gorails direct upload s3
in you gem file make sure you use the right roda version
gem 'roda', "~> 2.29.0"

I want to import all my table data to xml in rails

controller.rb
def xml_print
byebug
#month = params[:month]
#year = params[:year]
#maps = SalaryMapSap.all
#salaryslip = Salaryslip.limit(50)
respond_to do |format|
format.xml { send_data render_to_string(:index), :filename => 'mydoc.xml', :type=>"application/xml", :disposition => 'attachment' }
# #salary_components = SalaryComponent.all
#maps = SalaryMapSap.all
end
end
routes.rb
resources :salaryslip_components do
collection do
post :xml_print
end
end
new.html.erb
<%= bootstrap_form_for(:salaryslip_component, url: { action: 'xml_print',format: 'xml',month: #month,year: #year}) do |f| %>
I want to generate an XML Data in rails,for that i have created one form,in that two drop downs are there where i would select month and year and on bottom there would be submit button,if i click on it then all my data would be downloaded as an XML.I created index XML builder file.But my data is not generating in XML ,error is coming,that action could not be found.I also mentioned this in controller's action.
format.xml { send_data render_to_string(:index), :filename => 'mydoc.xml', :type=>"application/xml", :disposition => 'attachment' }
So please help me.

Axlsx Rails. Generate .xlsx file and respond filename as json/html

I am generating xlsx files with axlsx_rails gem.
After collecting user input I am posting data to /basic_report_post.xlsx
Controller action looks like
def basic_report_post
#config = params[:config]
#data = params[:data]
#filename = "#{Rails.root}/public/test.xlsx"
respond_to do |format|
format.xlsx {
render xlsx: 'basic_report_post'
}
end
end
View file for this action basic_report_post.xlsx.axlsx
wb = xlsx_package.workbook
wb.add_worksheet(name: 'Data1') do |s|
# Drawing columns
end
xlsx_package.serialize #filename
My problem is that I am getting response data(in post success action) that is raw .xlsx file.
But I need somehow respond #filename (format json/html) to download it after.
It is possible to use the axlsx_rails template renderer to create a string and save it to file:
def basic_report_post
#config = params[:config]
#data = params[:data]
#filename = "#{Rails.root}/public/test.xlsx"
File.open(#filename, 'w') do |f|
f.write render_to_string(handlers: [:axlsx], formats: [:xlsx], template: 'path/to/template')
end
render json: {name: #filename}
end
Then you can use the template to serve the file directly if need be.
After some experiments with respond_to I move .xlsx generation logic to view helper.
So I have included BasicReportHelper in controller.
basic_report_helper.rb
module BasicReportsHelper
def generate_basic_report(filename)
p = Axlsx::Package.new
wb = p.workbook
wb.add_worksheet(:name => "Basic Worksheet") do |sheet|
# Drawing here
end
p.serialize filename
end
end
Changed post call to /basic_report_post.json and changed action to
def basic_report_post
#config = params[:config]
#data = params[:data]
#filename = "#{Rails.root}/public/test.xlsx"
generate_basic_report(#filename)
respond_to do |format|
format.json {
render json: {name: #filename}
}
end
end

Resources