Rails 3 templates: rendering multiple formats using the same template handler - ruby-on-rails-3

From a single view file containing e.g. LaTeX code with ERB inserts, I would like to be able to:
render to a LaTeX source file, by evaluating the ERB
render to PDF, by compiling the previous result using a custom latex_to_pdf() function
The first case can be achieved by registering a template handler:
ActionView::Template.register_template_handler :latex, LatexSource
where LatexSource is a subclass of ActionView::Template::Handler implementing call(template) or compile(template).
This allows a view file "action.tex.latex" to be accessed and processed correctly as "controller/action.tex".
The second case seems much harder though:
how can the request "controller/action.pdf" be sent to the template handler as if it was "controller/action.tex", and pass the result through latex_to_pdf() before sending the response to the user?
Many thanks

Couldn't you just register another template handler :pdf whose compile method looks similar to this?:
def compile
latex_to_pdf LatexSource.compile(template)
end
Update:
Ok, right, this results in the need of having the view duplicated (action.tex.latex, action.tex.pdf).
Next idea:
respond_to do |format|
format.latex
format.pdf { render :file => latex_to_pdf(render) }
end
As far as I can remember, render returned the rendered template as String in Rails 2.3.
I don't know how it behaves in Rails 3.
You could experiment with render or _render_template. If this works, we could think about how to make this more dry and comfortable for multiple actions.

I didn’t use it myself (yet), but it looks as if https://github.com/jacott/rails-latex could do the job for you.

Related

rails user input with <script>, stored, and displayed

I have an application that collect user input and store to DB and show back to user.
One user entered "alert(1)" into the name field and saved it into DB.
Whenever the name is displayed, the page will be broken.
I know how to fix that input only with validation for input, and h() for output.
However, I have so many input fields and so many outputs that accept users' text.
Is there any simple way to prevent this happening(i.e. overriding params method, etc)?
I also want to know how you expert guys are dealing with this problem?
As of Rails 3, my understanding was that embedded ruby code was html escaped by default. You don't need to use h() to make it that way. That is, if you use <%= "<script>a=1/0;</script>" %> in a view, the string is going to be made html safe, and so the script doesn't execute. You would have to specifically use raw() or something similar to avoid it - which you should naturally not do unless you're really confident about the contents.
For output, Rails 3 automatically html-encode all text unless I use raw() method.
For input, How about making a common validator and apply to all fields that are text or string? Is it desirable?
http://api.rubyonrails.org/classes/ActiveModel/Validator.html
class MyValidator < ActiveModel::Validator
def validate(record)
record.class.columns.each do |c|
if c.type==:text || c.type == :string
record.errors.add c.type, "script tag is not allowed" if c[/<script[^>]*>/]
end
end
end
end

Call recent posts on the layout

Imagine i have a blog, and i want a footer or sidebar displaying my 3 most recent posts at any given time.
What is the best way to do this?
I can call #recent_posts in every single controller to have them ready for the layout but this doesn't seem like the best way...at all...
#recent_posts = Posts.all(:limit => 3)
I've been fiddling around with partials, but they do need an instance variable carrying the #recent_posts.
There may be two parts to your concern: 1) performance, and 2) effort required. Both are easily addressed.
As Andrei S notes in his answer, the convenience/effort issue is mitigated by using a before_filter that calls the method that does the work from the ApplicationController class.
The performance issue is only slightly more work. Instead of the method being
def most_recent_posts
Posts.order(created_at DESC).limit(3)
end
instead do this
def most_recent_posts
#most_recent_posts ||= Posts.order(created_at DESC).limit(3)
end
which checks the instance variable for nil; if nil, it does the query and assigns the result to the instance variable.
You'll also need a way to update when a new post is added, so perhaps something like
def clear_most_recent_posts!
#most_recent_posts = nil
end
and then just call clear_most_recent_posts! from the method(s) that modify the table. The before_filter will do its work only when needed.
I am sure some more eloquent rubyist has a nicer way of doing this, but this is an idea.
You could put the part where you have your posts in a partial and use it in the general layout of your app.
To load them all in every controller you could do a before_filter in your ApplicationController in which you set your instance variable, which will be available in your partial that gets rendered in the layout
This way you only get to do it once, and it will get done everywhere (of course you could set conditions on the filter and the layout to load them when you need, that's if you don't really need them on every page)

Render view from code in /lib

Let's pretend we have a Rails project with a class named Foo residing in the lib directory.
We want to generate some HTML based on its attributes.
Now, creating a big string by concatenating HTML fragments appropriately doesn't feel right in the least bit.
My question is: is there a blessed way to do it?
You have a couple pretty straightforward options. If you're using ERB, you can do something like:
ERB.new(File.read(the_file_path)).result
I'm using a library called Tilt (https://github.com/rtomayko/tilt) in one of my applications to render a HAML template. Here's the exact sausage factory code that I'm running right now:
template = Tilt.new(ITEM_TEMPLATE) # ITEM_TEMPLATE is the path of a .haml file
template.render nil, {:item => item, :item_details => item_details}
# item and item_details are now available as local variables in the partial
If you don't want to go the templating route, you could try something like Erector (http://erector.rubyforge.org/userguide.html) as well.
Worst case scenario: instead of concatenating fragments, you could use the HEREDOC to format it nicely:
<<-HTML
<div id="foo">
<span class="bar">#{thing.method}</span>
<div>#{thing.attribute}</div>
</div>
HTML

Rails - Use same attachment for all emails using layout

I'm probably missing something obvious, but I've got a logo I'd like to include in all of the emails I send from my app. I have a master layout I'm using for all of those Mailers. I assume there's a way to do keep it DRY and not have to add the line of code to attach the file in every mailer method. Can someone point me in the right direction or correct my line of thought.
Thanks!
Callbacks using before_filter and after_filter will be supported in a future Rails release:
http://github.com/rails/rails/commit/4f28c4fc9a51bbab76d5dcde033c47aa6711339b
Since they will be implemented using AbstractController::Callbacks, you can do the following to mimic the functionality that will be present in ActionMailer::Base once Rails 4 is released:
class YourMailer < ActionMailer::Base
if self.included_modules.include?(AbstractController::Callbacks)
raise "You've already included AbstractController::Callbacks, remove this line."
else
include AbstractController::Callbacks
end
before_filter :add_inline_attachments!
private
def add_inline_attachments!
attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
end
end
This includes the module that will be used in a future rails version, so the callback hooks available to you will be the same to ensure future compatibility. The code will raise when you try to upgrade to a Rails version that already includes AbstractController::Callbacks, so you will be reminded to remove the conditional logic.
I hacked a little something, it's not ideal, but it works.
If you use
default "SOMEHEADER", Proc.new { set_layout }
And then define set_layout
def set_layout
attachments.inline["logo.png"] = File.read("logopath.png")
attachments.inline["footer.jpg"] = File.read("footerpath.png")
"SOME HEADER VALUE"
end
Then because set_layout gets called to set the header, it also adds the inline attachments. It basically creates a callback for adding attachments.
An actual callback system in ActionMailer would be preferable, but this works too.
Thought I would share since I was looking for this answer on this question earlier today.
in the layout file that your mailer uses u can add the following
<%= image_tag('logo.png') %>
I am assuming that the mail being sent out is html or multipart.
Also you will need to make changes in the environment files. ActionMailer does not get a default base_url. For e.g in environments/development.rb I added the following
config.action_mailer.default_url_options = { :host => "localhost:3000" }
If you want to do it in a dRY manner (and as an attachment) maybe you could do something like
class MyMailer < ActionMailer::Base
default :attachment => File.read(File.join(Rails.root,'public','images','logo.png'))
end
I know you've asked about attaching inline images, but here's a different approach that achieves the same thing with less complexity..
Using inline base64 encoded images in the html layout - no attachments required!
Basically just change the src="..." of your logo image to the format:
<img alt="example logo"
width="32px"
height="32px"
src="data:image/png;base64,iVBORw0KGgoAAA....."/>
I use the online base64 encoder / decoder tool at http://www.base64-image.net for generating the complete <img /> tag
This approach has a few benefits:
- no attachment code, which makes the backend server code cleaner and easier to read
- no increase in email size - inline image attachments are converted to base64 anyway so this approach doesn't make the email payload any larger
- it's widely supported - if the receiving email client is showing html, it's pretty likely it also supports this method

Can you remove the _snowman in Rails 3?

I'm building an app on Rails 3 RC. I understand the point behind the _snowman param (http://railssnowman.info/)...however, I have a search form which makes a GET request to the index. Therefore, submitting the form is creating the following query string:
?_snowman=☃&search=Box
I don't know that supporting UTF encoding is as important as a clean query string for this particular form. (Perhaps I'm just too much of a perfectionist...hehe) Is there some way to remove the _snowman param for just this form? I'd rather not convert the form to a POST request to hide the snowman, but I'd also prefer it not be in my query string. Any thoughts?
You can avoid the snowman (now a checkmark) in Rails 3 by.... not using Rails for the search form. Instead of using form_tag, write your own as outlined in:
Rails 3 UTF-8 query string showing up in URL?
Rails helpers are great unless they're not helping. Do-it-yourself is good as long as you understand the consequences, and are willing to maintain it in the future.
I believe the snowman has to be sent over the wire to ensure your data is being encoded properly, which means you can't really remove the snowman input from forms. Since, it's being sent in your GET request, it will have to be appended to the URL.
I suppose you could write some javascript to clean up the URL once the search page loads, or you could setup a redirect to the equivalent URL minus the snowman. Both options don't really feel right to me.
Also, it doesn't seem there is any way to configure Rails to not output it. If you really wanted to get rid of it, you could comment out those lines in Rails' source (the committed patches at the bottom of railssnowman.info should lead you to the files and line numbers). This adds some maintenance chores for you when you upgrade Rails. Perhaps you can submit a patch to be able to turn this off?
EDIT: Looks like they just switched it to what looks like a checkmark instead of a snowman.
EDIT: Oops, back to a snowman.
In Rails 4.1 you can use the option :enforce_utf8 => false to disable utf8 input tag.
However I want to use this in Rails 3, so I monkey-patched my Rails. I put the following in the config/initializers directory.
# allow removing utf8 using enforce_utf8, remove after Rails 4.1
module ActionView
module Helpers
module FormTagHelper
def extra_tags_for_form(html_options)
authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s
method_tag = case method
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
html_options["method"] = "get"
''
when /^post$/i, "", nil
html_options["method"] = "post"
token_tag(authenticity_token)
else
html_options["method"] = "post"
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
end
enforce_utf8 = html_options.delete("enforce_utf8") { true }
tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
end
end
end
end