Rails 3.2 chain renderers to render a .css.erb.scss view - ruby-on-rails-3

I want to be able to to have a view file called my.css.erb.scss in which the ERB is handled and then the SCSS is compiled.
So for example this file:
h1 { color: <%= 'red' %>; }
Would result in:
h1 { color: red; }
Currently the only solution I've found to work is a bit of a fudge and I don't like it (and it's proving difficult to render via this method in specs):
lib/renders.rb:
ActionController.add_renderer :css do |template,options|
string = render_to_string template, options
css = Sass::Engine.new(string, :syntax => :scss).render
self.content_type ||= Mime::CSS
self.response_body = css
end
Then in the controller I need to do this:
respond_to do |format|
format.css { render :css => params[:action] }
end
I've tried adding an registering a renderer via the ActionView::Template::Handler module method with a .call method but I couldn't get the ERB to render then.

Related

Wicked_PDF render a string from a template in a background process

I've got a controller "tech" that has an action to email and invoice, from there we use Delayed::Job.enqueue to shove the actual email action into a background process which will be handled via a worker dyno on Heroku.
This is all working fine.
The trouble that I found is that my generated PDF invoice lives over on the Heroku Web Dyno file system and the Worker has no idea where this is.
I will do upload the PDF during the generation process, it takes too damn long.
So I need to create the invoice over on the worker dyno when it goes to execute the mailer action to send the message.
I found this blog with some detailed instructions on creating the pdf from a string: http://viget.com/extend/how-to-create-pdfs-in-rails
But it's not working at all for me, here is the code:
html = render_to_string(:action =>":show", :layout => "invoice.html")
#pdf = WickedPdf.new.pdf_from_string(html)
And the error:
"last_error"=>"undefined method `response_body=' for #<MailSenderJob:0x007fdf7e70a638>
I know this is from the docs:
WickedPdf.new.pdf_from_string(
render_to_string('templates/pdf.html.erb', :layout => 'pdfs/layout_pdf'),
:footer => {
:content => render_to_string(:layout => 'pdfs/layout_pdf')
}
)
And that code has never worked for me at all.
What I'm getting over and over is the response_body= error. It's like it's not getting a response at all.
At the top of my file I'm doing:
include ActionController::Rendering
Because this is the module that has the render_to_string method inside it.
Any help at all - please keep in mind in your response that I'm running this code on a Heroku WORKER dyno - so if there's any dependency that I need to manually include that is naturally included on the web server, please let me know.
I ended up having to do some weird stuff with this to finally get it working.
html = File.read(Rails.root.join('app','views','technician','invoice.html.erb'))
html = ERB.new(html).result(binding)
html = html.gsub!(/\0/,'') # There is a null byte in the rendered html, so we'll strip it out (this is kind of a hack)
# Render the PDF - we're on a worker dyno and have no access to the pdf we rendered already on the web dyno
#pdf = WickedPdf.new.pdf_from_string(
html,
:handlers => [:erb],
:footer => {
:center => "Copyright 2014"
},
:disable_smart_shrinking => true,
)
#pdf = #pdf.gsub!(/\0/,'') # Again with the null bytes!
Using Partials.
I know what you mean, it gets a little funky when you're rendering PDFs in the background job as opposed to a Controller action.
I thought I would share my implementation as a comparison and for others to get another example from.
notification_mailer.rb
def export
header_html = render_to_string( partial: 'exports/header.pdf.erb',
locals: { report_title: 'Emissions Export' } )
body_html = render_to_string( partial: "exports/show.pdf.erb" )
footer_html = render_to_string( partial: 'exports/footer.pdf.erb' )
#pdf = WickedPdf.new.pdf_from_string(
body_html,
orientation: 'Landscape',
margin: { bottom: 20, top: 30 },
header: { content: header_html },
footer: { content: footer_html } )
# Attach to email as attachment.
attachments["Emissions Export.pdf"] = #pdf
# Send email. Attachment assigned above will automatically be included.
mail( { subject: 'Emissions Export PDF', to: 'elon#musk.com' } )
end

Wicked PDF, generating PDF from database table- images and style issues

I have an uploader (internal use only) that will upload an HTML document to a binary column of a table in my client-facing website. The client facing site has an index that allows the user to view the page as a normal website (using send_data h_t.html_code, :type => "html", :disposition => "inline"). I also want to give the user the ability to download a PDF of the page. For that I'm using wicked_pdf.
The entire problem seems to stem from the fact that the data is stored in the database. As strange as it sounds, it is vital to business operations that I get formatting exact. The issue is I can't see any image, and the stylesheets/style tags don't have any effect.
What I've tried-
Gsub-
def show
html = HtmlTranscript.find(params[:id])
html_code = html.html_code.gsub('<img src="/images/bwTranscriptLogo.gif" alt="Logo">','<%= wicked_pdf_image_tag "bwTranscriptLogo.gif" %>')
html_code = html_code.gsub('<link rel="StyleSheet" href="" type="text/css">','<%= wicked_pdf_stylesheet_link_tag "transcripts.css" %>')
transcript = WickedPdf.new.pdf_from_string(html_code)
respond_to do |format|
format.html do
send_data transcript, :type => "pdf", :disposition => "attachment"
end
##### i never could get this part figured out, so if you have a fix for this...
# format.pdf do
# render :pdf => "transcript_for_#{#html.created_at}", :template => "html_transcripts/show.html.erb", :layout => false
# end
end
end
Using a template-
#Controller (above, modified)
html = HtmlTranscript.find(params[:id])
#html_code = html.html_code.gsub('<img src="/images/bwTranscriptLogo.gif" alt="Logo">','<%= wicked_pdf_image_tag "bwTranscriptLogo.gif" %>')
#html_code = #html_code.gsub('<link rel="StyleSheet" href="" type="text/css">','<%= wicked_pdf_stylesheet_link_tag "transcripts.css" %>')
transcript = WickedPdf.new.pdf_from_string(render_to_string(:template => "html_transcripts/show.html.erb", :layout => false))
#view
<!-- tried with stylesheet & image link tags, with wicked_pdf stylesheet & image link tags, with html style & img tags, etc -->
<%= raw(#html_code) %>
And both will generate a transcript- but neither will have style OR image.
Creating an initializer-
module WickedPdfHelper
def wicked_pdf_stylesheet_link_tag(*sources)
sources.collect { |source|
"<style type='text/css'>#{Rails.application.assets.find_asset("#{source}.css")}</style>"
}.join("\n").gsub(/url\(['"](.+)['"]\)(.+)/,%[url("#{wicked_pdf_image_location("\\1")}")\\2]).html_safe
end
def wicked_pdf_image_tag(img, options={})
image_tag wicked_pdf_image_location(img), options
end
def wicked_pdf_image_location(img)
"file://#{Rails.root.join('app', 'assets', 'images', img)}"
end
def wicked_pdf_javascript_src_tag(source)
"<script type='text/javascript'>#{Rails.application.assets.find_asset("#{source}.js").body}</script>"
end
def wicked_pdf_javascript_include_tag(*sources)
sources.collect{ |source| wicked_pdf_javascript_src_tag(source) }.join("\n").html_safe
end
end
did absolutely nothing, and I have no idea what to try next.
As a side note, the code to view the HTML version of the transcript is as follows:
def transcript_data
h_t = HtmlTranscript.find(params[:id])
send_data h_t.html_code, :type => "html", :disposition => "inline"
end
It requires no view, as the html data is stored in the database, but I get image, style, etc. Everything works with the HTML version- just not the PDF.
I'm on ruby 1.8.7 with rails 3.0.20.
Solved-
As it turns out, there was more than one issue at hand.
1- Installation of wkhtmltopdf for Ubuntu via $apt-get install does not quite do the trick for what I wanted...
see http://rubykitchen.in/blog/2013/03/17/pdf-generation-with-rails
(there may have also been an issue with having not previously run sudo apt-get install openssl build-essential xorg libssl-dev libxrender-dev, as when I did, it installed a number of components I did not previously have.)
2- The HTML files I had uploaded contained image & style code that was breaking the formatting. I fixed it with this...
def rm_by_line(which = 0, line1 = 0, line2 = 0)
h_t = HtmlTranscript.find(which)
line_by_line = h_t.html_code.split('
')
for i in line1..line2
line_by_line[i] = ''
end
line_by_line = line_by_line.join('
').strip
return line_by_line
end
Then, all I had to do was pass which lines I wanted to remove.
(I had to split the parens with a carriage return because '\n' didn't function properly when calling 'raw' on the returned string.)
3- wicked_pdf_stylesheet_link_tag and wicked_pdf_image_tag were undefined. I had to inline the style formatting I wanted into a layout I created (turns out wicked_pdf_stylesheet_link_tag used asset pipeline wich my ruby/rails did not implement, which also means I had to get rid of the javascript helpers) and created a helper for wicked_pdf_image_tag, making a switch in the layout for which image tag (image_tag or wicked_pdf_image_tag) to be used.
4- I needed both a .html.erb & a .pdf.erb for my templates, so I made both.
5- Got rid of WickedPdf.new.pdf_from_string in favor of linking to either html or pdf by using :format => 'html' or :format => 'pdf' in the link_to tag.

Is jquery not availble in integration test for rails 3.2 app?

We use a fields_for and jquery to add a partial view on a form in rails 3.2 app. Here is the code:
def link_to_add_fields(name, f, association)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render :partial => association.to_s, :locals => {:f => builder, :i_id => 0}
end
link_to_function(name, "add_fields(this, \"#{association}\", \"#{j fields}\")")
end
In applicaton.js:
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
$(link).parent().before(content.replace(regexp, new_id));
}
Whenever the 'Add Field' link is clicked, the partial view is rendered and a few input fields are added to the current form. The code works in execution without any problem. However in integration test (capybara & launchy), the click_link('Add Field') did not do anything and failed bringing up the partial. Is jquery not enabled in integration test?
By default Capybara use :rake_test driver on all tests, which is fast but dosen't support JavaScript.
Since this test needs JavaScript, make sure you have turned JS driver on.
describe "some feature", js: true do
# test code
end
This will use default JS driver Selenium.

Mercury editor (0.9.0) cannot save changes in Rails 3.2.11

I am adding mercury to a simple blogging application and would like to use the newest version, but I have not been able to get it to save updates. Here is my code:
Gemfile:
gem 'rails', '3.2.11'
gem "thin", "~> 1.3.1"
gem 'mercury-rails'
gem 'paperclip'
group :development do
gem 'taps'
gem "nifty-generators"
gem 'sqlite3'
end
gem 'jquery-rails'
blogs_controller.rb:
def update
#blog = Blog.find(params[:id])
if #blog.update_attributes(params[:blog])
redirect_to #blog
else
render 'edit'
end
end
def mercury_update
blog = Blog.find(params[:id])
blog.content = params[:content][:blogContent][:value]
blog.save!
render text: ""
end
blog.rb:
class Blog < ActiveRecord::Base
attr_accessible :title, :content, :author
end
routes.rb:
Mercury::Engine.routes
mount Mercury::Engine => '/'
root :to => "pages#home"
namespace :mercury do
resources :images
end
resources :blogs do
member { post :mercury_update }
end
mercury.html.erb
<body>
<script type="text/javascript">
// Set to the url that you want to save any given page to, leave null for default handling.
var saveUrl = null;
// Instantiate the PageEditor
new Mercury.PageEditor(saveUrl, {
saveStyle: 'form', // 'form', or 'json' (default json)
saveMethod: null, // 'PUT', or 'POST', (create, vs. update -- default PUT)
visible: true // boolean - if the interface should start visible or not
});
</script>
</body>
blogs/show.html.erb:
<div id="blogFull" class="rounded-corners-mild">
<div id="blogTitle">
<h1 style="font-size:150%; text-align:center; " class="mercury-region" ><%= #blog.title%></h1>
</div>
<div data-mercury="full" id="blogContent" class="mercury-region">
<%= raw #blog.content %>
</div><br />
<%= link_to "mercury", "/editor" + "/blogs" + "/#{#blog.id}", :class => 'bestButton',
id: "edit_link", data: {save_url: mercury_update_blog_path(#blog)} %>
added to: mercury.js:
jQuery(window).on('mercury:ready', function() {
var saveUrl = $("#edit_link").data("save-url");
Mercury.saveUrl = saveUrl;
});
Mercury.on('saved', function() {
window.location.href = window.location.href.replace(/\/editor\//i, '/');
});
I also tried with this: to mercury.js:
onload: function() {
//Mercury.PageEditor.prototype.iframeSrc = function(url) { return '/testing'; }
Mercury.on('ready', function() {
var link = $('#mercury_iframe').contents().find('#edit_link');
Mercury.saveUrl = link.data('save-url');
link.hide();
});
Mercury.on('saved', function() {
window.location.href = window.location.href.replace(/\/editor\//i, '/');
});
}
Neither worked. Both got me to the editor and allowed me to edit. I have not been able to save any changes yet. Thank You.
Do you get an error message when trying to save? What does it say? If it says it's unable to save but the URL is correct you want to check your log file.
I had problems setting the correct saveUrl and adding the following to mercury.js fixed it for me;
onload: function() {
Mercury.on('ready', function() {
var link = $('#mercury_iframe').contents().find('#edit_link');
console.log("mercury ready ready", link);
mercuryInstance.saveUrl = link.data('save-url');
link.hide();
});
Mercury.on('saved', function() {
window.location.href = window.location.href.replace(/\/editor\//i, '/');
});
}
Found it in this thread on RailsCasts.
Edit:
I notice something else in your code. You created a route for a POST action. In mercury.html.erb the saveMethod gets set to 'PUT' by default. Change the saveMethod to 'POST'.
For me I had trouble with "mercuryInstance" not being around with the solution above. I did get it to work with this javascript added to the end of mercury.js:
jQuery(window).on('mercury:ready', function() {
var link = $('#edit_link');
Mercury.saveUrl =link.attr('data-save-url');
link.hide();
});

rendering a partial Rails3.x + coffeescript

I have the following requirement. I have a 'school' drop down and as the last options I have add new school, so if the user selects that option I want to load the new_school form as a partial via ajax.
I'm on
gem 'rails', '3.2.9'
gem 'coffee-rails', '~> 3.2.1'
Jquery via gem 'jquery-rails'
Earlier with rails < 3 and prototype I used to do it with
Ajax.Updater (aka Rails link_to_remote :update => 'some_div')
and with rails > 3 + JQuery I'm familiar with *.js.erb, and having something like
$("#school_form").html("<%= escape_javascript(render(:partial => "form"))%>");
But I'm new to coffeescript and I have no idea on how to do this with coffeescript, can someone help me :), (because I believe you shouldn't have to do a server request for this)
So far I have done following to catch the select_tag change event
$ ->
$('#school_name_select').change ->
unless $(this).val()
$('school_name').html([I want to have the _new_school_form partial here])
Use a hidden div.
In general, you don't want to bother trying to mix JS and HTML. The escaping can be complicated, error-prone, and flat out dangerous due to the possibility of cross-site scripting attacks.
Simply render your form partial in a div that's not displayed by default. In ERB:
<div id="school_name_form" style="display: none;">
<%= render 'form' %>
</div>
In your CoffeeScript:
$ ->
$('#school_name_select').change ->
if $(this).val()
$('#school_name_form').slideUp()
else
$('#school_name_form').slideDown()
I recommend using a small, tasteful transition like slide or fade. It gives your app a more polished feel.
No AJAX is required. This pattern is so common that I have an application-wide style defined as follows.
.not-displayed {
display: none;
}
Then using HAML (if you're into that), the HTML template becomes simply:
#school_name_form.not-displayed
= render 'form'
You can try to render the form partial inside hidden div (not too correct from semantic point of view), or put the form html as data attribute of any relevant element, something like
f.select school_name, ... , data: {form: escape_javascript(render(:partial => "form"))}
And the Coffeescript
$ ->
$('#school_name_select').change ->
unless $(this).val()
$('school_name').html($('#school_name_select').data('form'))