I have a Rails app, in which I need to render one of the views as a downloadable PDF. The view itself is composed of many partials, which in turn are composed of more partials and so on. This is the top-level view:
<meta charset="utf-8" />
<%= javascript_include_tag "jquery-ui.min", media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag "report", media: "all", "data-turbolinks-track" => true %>
<%= stylesheet_link_tag "report_style", media: "all", "data-turbolinks-track" => true %>
<div class="report">
<%= render partial: "details", locals: { examinee: #examinee, te: #test_event } %>
<div class="page-break"></div>
<%= render partial: "grades", locals: { scores: #test_event.scores, tasks: Task.tasks_for_report(#test_event.language) } %>
<div class="page-break"></div>
<%= render partial: "ld", locals: { test_event: #test_event } %>
<%= render partial: "review", locals: { test_event: #test_event } %>
<div class="page-break"></div>
<%= render partial: "appendix" %>
</div>
Due to the complexity of the rendered view, I prefer generating a PDF from html over writing everything twice, once as .html.erb and again as pdf.erb.
I have tried both pdfkit and wickedpdf, but in both cases I have failed:
pdfkit gets my Thin server hanged, no matter what I tried - None of the solutions suggested here, here and here had any impact, things still suck.
wicked_pdf has actually managed to generate a PDF, but then again it ignored all of my css and js code, even when called with wicked_pdf_javascript_include_tag and its corresponding css twin. This can't go as I need my page breaks and all of the content created by the various js scripts (for example, some plots which are made by chartist).
Any Ideas?
If you are running Rails with a single-threaded webserver (like webrick), and your PDF generation process involves getting data from another endpoint in your app (like AJAX in your JavaScript), it is going to hang in a deadlock, because the request for the asset to build your PDF is queued behind the actual building of the PDF.
It should work just fine in production because most production environments are setup to run multiple web workers (with passenger or unicorn or something), but to properly test this in development mode, you'll need to configure your app to run multiple web workers, so your Ajax request can happen in another thread and return back to your main request.
I would suggest you add the puma gem to your Gemfile, and run your dev server with rails server Puma --workers 3 (by the way, puma is included by default in new Rails 5 apps because people were having this same issue with ActionCable requests).
wicked_pdf_javascript_include_tag is provided to read from disk for extra speed in building your PDF, and does have some issues with a few libraries, and depending on your config, may require your JS and CSS to be named in the config.assets.precompile list.
Related
I want to use the controller specific assets only for some controllers, but not for all. Hence, I didn't add the assets for which I don't need separate files. But this leads to 404 for those resources as they are not present, e.g. my app/views/layouts/dashboard.html.erb file contains the following piece of code to generate the controller specific js/css:
<%= stylesheet_link_tag 'dashboard', params[:controller], media: 'all' %>
<%= javascript_include_tag 'dashboard', params[:controller] %>
However, I do not want to create separate files for Admin controller, as the admins page has no new css/js required. But, app tries to locate the assets for the controller and since not found it returns the 404 as follows:
GET http://dashboard.localhost.com:3000/stylesheets/dashboard/admins.css 404 (Not Found)
GET http://dashboard.localhost.com:3000/javascripts/dashboard/admins.js 404 (Not Found)
What should be done to prevent this?
Moreover, there are a few controllers for which the assets are obviously not required, e.g. Registrations controller, leading to 404 for http://0.0.0.0:3000/stylesheets/store/registrations.css for http://0.0.0.0:3000/account/edit page
Here is what I did to fix this in my application.html.erb:
<%= stylesheet_link_tag 'dashboard' %>
<%= stylesheet_link_tag params[:controller] if InmonarchWebsite::Application.assets.find_asset("#{params[:controller]}.css") %>
<%= javascript_include_tag 'dashboard' %>
<%= javascript_include_tag params[:controller] if InmonarchWebsite::Application.assets.find_asset("#{params[:controller]}.js") %>
I am working on a rails website and I need to make it multilingual. I have created yml files for the languages needed for translation.
What I want is to provide the Admin access to files to edit them instead of browsing through the files, by a section in Admin panel. Is there any gem for managing translations through Admin end.
I have never implemented translations before.
By default Rails uses YAML files to store internationalization information. But we are not restricted to use YAML (that is only capable of reading translations but cannot dynamically store them). We can use any database as backend.
To start, you can use this gem: https://github.com/svenfuchs/i18n-active_record
You can change the default backend like that:
I18n.backend = Globalize::Backend::Static.new
You can also use Chan backend to chain multiple backends together.
# config/initializers/locale.rb
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
But, As the translations are accessed frequently in every request page, ActiveRecord is not the best approach. For these reason, a key-value store is the way to go.
You can use Redis database for this:
First install Redis
$ brew install redis
Install redis gem:
# Gemfile
source 'http://rubygems.org'
# ...
gem 'redis'
Now you can change the backend like that:
# config/initializers/locale.rb
I18n.backend = I18n::Backend::KeyValue.new(Redis.new)
Code to add translation:
# /app/views/admin/translations/index.html.erb
<%= form_tag admin_translations_path do %>
<p>
<%= label_tag :locale %><br>
<%= text_field_tag :locale %>
</p>
<p>
<%= label_tag :key %><br>
<%= text_field_tag :key %>
</p>
<p>
<%= label_tag :value %><br>
<%= text_field_tag :value %>
</p>
<p><%= submit_tag "Submit" %></p>
<% end %>
This form will POST to admin/TranslationsController's create action:
# /app/controllers/admin/translations_controller.rb
module Admin
class TranslationsController < ApplicationController
# ....
def create
I18n.backend.store_translations(params[:locale]), {params[:key] => params[:value]}, escape: false)
redirect_to admin_translations_path, notice: "Translation Added"
end
end
end
You can also use redis-i18n gem to do the same: https://github.com/redis-store/redis-i18n
I am using Rails 4
I followed instruction from https://github.com/pdfkit/pdfkit
In controller I have
respond_to do |format|
format.html {render layout:'application'}
format.csv {send_data #testdatares.to_csv}
format.pdf {
html = render_to_string(:layout => 'application' , :action => "testpage.html.erb");
kit = PDFKit.new(html);
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/application.css";
send_data(kit.to_pdf, :filename => "some_name.pdf", :type => 'application/pdf')
}
end
In application.html.erb I have
<!DOCTYPE html>
<html>
<head>
<title>STNEW</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= javascript_include_tag "http://www.google.com/jsapi", "/usr/local/lib/ruby/gems/2.1.0/gems/chartkick-1.3.2/app/assets/javascripts/chartkick",media:'all' %>
<%= csrf_meta_tags %>
</head>
Please note that I already used absolute path to chartkick.js
Now in html the chartkick contents were perfectly displayed.
But in the pdf file generated by pdfkit all chartkick contents were shown as "loading", while text was correctly shown
Googled around but found no answer
Finally, after 3 days of search, trails and error, I found solution for this
first of all, I have to use wicked_pdf rather than pdfkit because the former provides two important helper functions
wicked_pdf_stylesheet_link_tag
wicked_pdf_javascript_include_tag
then I must use these two helper to include stylesheet and javascript, like this
<%= wicked_pdf_stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= wicked_pdf_javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= javascript_include_tag "http://www.google.com/jsapi"%>
<%= wicked_pdf_javascript_include_tag 'chartkick'%>
The difference between javascript_include_tag and wicked_pdf_javascript_include_tag is that the latter will write all original java code into the html file. This is important because wkhtmltopdf, the core of wicked_pdf, does not know where to find the js and css assets. These assets are located at someplace like /usr/local/lib/ruby/gems/2.1.0/gems/ (yours might be different), but only your rails app know where it is
note that for
<%= javascript_include_tag "http://www.google.com/jsapi"%>
you can't and don't need wicked_pdf_javascript_include_tag.
Here the most most important thing come. wicked_pdf (and also pdfkit) will instruct you to install gem wkhtmltopdf-binary , which has the latest version 0.9.9 and then simply call something like this
format.pdf {render pdf :"test",layout:'application',template:'stinfos/testpage.html.erb'}
to generate pdf. This ONLY works for html without chartkick or highchart component. In the PDF file generated, the chartkick or highchart figures will only show as "loading"
To solve this, you need to remove the wkhtmltopdf-binary gem by running
gem uninstall wkhtmltopdf-binary
in your rails app root
Then download the latest version of wkhtmltopdf from http://wkhtmltopdf.org/downloads.html
for me the precompiled version work
http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.1/wkhtmltox-0.12.1_linux-trusty-amd64.deb
and install
dpkg -i wkhtmltox-0.12.1_linux-trusty-amd64.deb
most likely it will be located here /usr/local/bin/wkhtmltopdf
if not, change the config/initializers/wicked_pdf.rb
:exe_path => '/yourpath/wkhtmltopdf'
Then add javascript_delay:2000 to the format.pdf
format.pdf {render pdf:"test",javascript_delay:2000,
layout:'application',template:'stinfos/testpage.html.erb'}
Then you should see the chartkick figures show up in the pdf generated. If not, use a larger delay time.
Note that javascript_delay option is not available for wkhtmlpdf below version 0.10
While WickedPDF probably works just fine, there's a workaround to making PDFKit work as well. You just need to point wkhtmltopdf to the absolute path of the .js file, similar to the approach used for including the CSS files:
html = render_to_string(:layout => 'application' , :action => "testpage.html.erb")
html.gsub!("/assets", "#{Rails.root}/public/assets")
kit = PDFKit.new(html)
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/application.css"
send_data(kit.to_pdf, :filename => "some_name.pdf", :type => 'application/pdf')
The pre-compiled chartkick.js will be in the /public/assets folder and wkhtmltopdf is able to locate it there.
Rails 4.0.2
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]
If I navigate to the page admin/malone_tunes/show.html.erb through the browser the page is delivered with out the styles specified in assets/styles/admin/malone_tunes.css.scss being applied. If I then hit command-r(browser refresh) the page reloads with the correct styles applied. I thought that config.cache_classes = false and config.action_controller.perform_caching = false in config/development.rb would cause each page load to serve up a new version of the layout with the <%= yield(:head) %>. I can get things to work with *= require_tree ., but this applies all stylesheets to the view and for my own understanding of the asset pipeline I want to implement page specific styles.
Thanks for any suggestions or documentation references:)
--config/environments/development.rb
Skunkwerx::Application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations
config.active_record.migration_error = :page_load
# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.
config.assets.debug = true
end
--config/aplication.rb
config.assets.precompile += ['admin/application.css' 'admin/malone_tunes/show.css']
--views/layouts/admin/application.erb
<head>
<title>Skunkwerx Admin Page</title>
<%= stylesheet_link_tag "admin/application", media: "all", "data-turbolinks-track" => false %>
<%= yield(:head) %>
<%#= stylesheet_link_tag "admin/malone_tunes/show", media: "all" %>
<%= javascript_include_tag "admin/application", "data-turbolinks-track" => false %>
<!-- loads controller specific scripts only on refresh -->
<%#= javascript_include_tag params[:controller] %>
<%= csrf_meta_tags %>
</head>
--views/malone_tunes/show.html.erb
<% stylesheet 'admin/malone_tunes/show.css' %>
--helpers/application_helper.rb
module ApplicationHelper
def stylesheet(*files)
content_for(:head) { stylesheet_link_tag(*files) }
end
end
--assets/stylesheets/admin/application.css.scss
*= require_self
*= require admin/main
So it turns out that the javascript_include_tag is causing this when I comment it out the styles work as expected. I feel like this must be a bug in the asset pipeline.
I had the exact same problem with my css and js files.
There is nothing wrong with the asset pipeline.
I corrected this problem by using the javascript_include_tag once and only once in my layouts/application.html.erb file.
Same with the stylesheet_link_tag. Only use it once.
Instead of making two statements, do this,
<%= javascript_include_tag "admin/application", params[:controller], "data-turbolinks-track" => false %>
and this...
<%= stylesheet_link_tag "admin/application", "admin/malone_tunes/show", media: "all", "data-turbolinks-track" => false %>
Thank You for posting your issue, it pushed me to figure out this solution.
The issue seems to be with the Rails asset pipeline using a feature called Turbolinks for sending CSS and JS assets to the browser. Turbolinks uses AJAX to load assets without refreshing the page. Problems can occur with popular javascript and css libraries such as jQuery/Chosen. A developer/user may notice the only way to make the assets work is with a manual browser refresh.
Josh Frankel describes this issue and provides a solution:
http://joshfrankel.me/blog/2015/fix/rails-4-turbolinks-fix-for-jquery-only-working-after-hard-refresh/
Basically:
add gem 'jquery-turbolinks' to Gemfile
bundle to reload your gems.
in app/assets/javascripts/application.js place //= require jquery.turbolinks after //= require jquery and before //= require turbolinks.
Restart any running instances of the Rails server and re-compile assets.
I've been having this annoying problem in which I click over a select dropdown input of my custom form, styled with Zurb Foundation 4 in my Rails application, and the list won't show its elements.
I thought at a start that was a problem with simple form, but I changed the f.association for f.collection_select, my code looks like this:
<h2><%= I18n.t(".sign_up") %></h2>
<%= simple_form_for(resource, :html => {:class => "custom"}, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= f.error_notification %>
<%= devise_error_messages! %>
<%= f.input :rut %>
<%= f.input :name %>
<%= f.input :email %>
<div>
<%= f.label :supplier_type_id %>
<%= f.collection_select :supplier_type_id, SupplierType.all, :id, :name %>
</div>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<%= f.error :base %>
<%= f.submit I18n.t(".sign_up"), :class => "button" %>
<% end %>
<%= render "devise/shared/links" %>
The most strange thing is that sometimes I'm able to see the items when refreshing the page, but when I get to the page navigating from other view of the app then it won't work. I've also noticed this when using hints for forms (i.e: If I have two hints in the same form, in different inputs, only one would show, but the same one message displays in both inputs when each message should show in their respective input. When reloading the page sometimes it shows one hint, and sometimes the other)
The styling looks good, so I think that it might be a foundation javascript problem.
Another thing I've noticed is that when I load the page the styling does a kind of "blinking" when using custom forms. This blinking it looks likes foundation takes a while to load the styling, I've also noticed this on their own custom form documentation site. This may indicate that is a form styled with javascript events or something similar, so this might mean that javascript is working well.
In addition, the checkboxes are having a similar problem, they only can be checked just when you reload the page, it might have some relation with this problem.
I'm very lost, some help would become very handy. Thanks!
--edit: Foundation 5 doesn't include custom forms and works better--
You might need to refresh dropdowns on each page:change event. Try something like this:
$(document).on("page:change", function() {
// SELECTOR_TO_CUSTOM_DROPDOWNS should select any Zurb custom dropdowns you
// are using.
$(SELECTOR_TO_CUSTOM_DROPDOWNS).trigger("change");
});
That's from the documentation on Zurb custom form JS.
Yes, this is caused by turbolinks. It stops $(document).ready from firing on page load, which is required by foundation's custom forms.
Using ssorallen's answer and to be more unobtrusive than the OPs, add this to application.js:
$(function(){ $(document).foundation(); });
$(document).on("page:change", function() {
if ($('form.custom').length > 0) {
$(document).foundation('forms');
}
});
Also, if you have jquery/coffeescript that relies on document ready being fired, add jquery turbolinks to your Gemfile.
Ok, so I just figured out that I have repeated asset load on my browser. This is causing a javascript error. It appears that deleting in the manifest
//= require turbolinks
Solves the problem.
I solved my multiple asset problem changing
<%= javascript_include_tag "application" %>
To the head in my application layout.
After watching the turbolinks railscast I noticed that besides my multiple asset loading, turbolinks and foundation 4 may not be compatible, it might be a solution on this post. But still doesn't work perfect for me.
I also noticed that navbar is also affected by turbolinks.
I think that this is rather a turbolinks problem and not an specific foundation dropdown. I will close this question and open a new on turbolinks and foundation.
Thanks to some Nick Reed insights I found out that the foundation gem was initializing foundation in application.js like this:
$(function(){ $(document).foundation(); });
So I checked the docs and I used this:
<script>
$(document).foundation();
</script>
After the "/body" tag in the application layout and everything seems to be working like a charm!