Rails with backbone-rails: asset helpers (image_path) in EJS files - ruby-on-rails

I have a Rails 3.1 app that uses the codebrew/backbone-rails. In a .jst.ejs template, I would like to include an image, like so:
<img src="<%= image_path("foo.png") %>"/>
But of course the asset helpers are not available in JavaScript.
Chaining ERB (.jst.ejs.erb) does not work, because the EJS syntax conflicts with ERB.
Here is what I know:
The asset helpers are not available in the browser, so I need to run them on the server side.
I can work around the problem by making the server dump various asset paths into the HTML (through data attributes or <script> and JSON) and reading them back in JS, but this seems rather kludgy.
Is there a way to somehow use the asset helpers in EJS files?

There is a way, actually, to chain a .jst.ejs.erb file, although it's fairly undocumented, and I only found it through looking at the EJS test cases. You can tell EJS to use {{ }} (or [% %] or whatever else you want) instead of <% %>, and then ERB won't try to evaluate your EJS calls.
Make sure to require EJS somewhere in your code (I just included gem 'ejs' in my Gemfile), and then create an initializer (I called it ejs.rb) that includes the following:
EJS.evaluation_pattern = /\{\{([\s\S]+?)\}\}/
EJS.interpolation_pattern = /\{\{=([\s\S]+?)\}\}/
Then just make sure to rename your templates to .jst.ejs.erb, and replace your existing <% %> EJS-interpreted code with {{ }}. If you want to use something other than {{ }}, change the regular expressions in the initializer.
I wish there were an option in Sprockets to handle this through the config rather than having to explicitly include EJS, but as of the moment, there's no way to do that that I know of.

I can see two ways. Neither are great.
When you say <%%= variable %> then this is rendered by ERB as <%= variable %>, so you could double percent escape everything but the asset_tags and that would survive the trip through one ERB pass on the way to EJS.
If you find that too gross...
How about making a different javascript file, with an ERB extension, that defines your asset paths? And then use the asset pipeline to require that.
So say assets.js.erb defines something like:
MyAssets = {
'foo': <%= image_path("foo.png") %>,
...
}
And then require this somewhere near the top of your manifest. And then reference the globals however that works in EJS.

For those willing to try HAML instead of EJS: Using haml-coffee through haml_coffee_assets has worked well for me as well.
You can have the following in a .hamlc.erb file:
%img(src="<%= image_path('foo.png') %>")
(It still doesn't give you routing helpers though, only asset helpers.)

Ryan Fitzgerald was kind enough to post a gist of his JavaScript asset helpers (which get precompiled with ERB): https://gist.github.com/1406349

You can use corresponding Javascript helper via the following gem:
https://github.com/kavkaz/js_assets
Finally (after installing and configuring) you will be able to use it like this:
<img src="<%= asset_path("foo.png") %>"/>

Related

Using Asset Pipeline in Rails View

I have a Coffeescript view, something like widget.js.coffee which needs to include jQuery, as I can't be sure that jQuery is available. This idea is for other people to use the JS file, e.g.
<script src="http://my.rails.app/40/widget.js"></script>
My app already has jQuery via the asset pipeline, so I want to do the equivalent of the application.coffee manifest, where I can simply say something like:
#= require jquery
So far, it looks like I can output jQuery via so:
<%= Rails.application.assets["jquery"].source %>
but this seems to break the Coffeescript code (looks like there are backticks in the jQuery source).
I'm not sure the best way to proceed. Any thoughts on the best way to do this?
Make a .coffee file in the asset pipeline that includes all the libraries you require. Let's call it "widget.js.coffee" and it lives in assets/javascripts
In your controller, pull the generated source like so:
code = Rails.application.assets['widget'].source
Compress it
#js_libraries = Uglifier.compile code
Use it in your view. If your view is coffeescrtipt, make sure it is enclosed in backticks
<%= raw #js_libraries %>

Is there an advantage to using link_to over the anchor tag in Rails?

Don't these two do the same thing?
<%= link_to "Example", '#', class: "somestyle" %>
Example
If I'm writing a static .html.erb page, if everything else is written with HTML tags, doesn't it make sense to use HTML tags for links as well? I'm not sure why one should use a helper. Similarly, for linking style sheets, javascripts, etc.
For the link tags, it may not make a difference which way you go. Unless you're linking to more than "#". For instance, using a routed path.
For the stylesheets and javascript, I think you will need to continue to use the Rails helpers if you're taking advantage of the asset pipeline. If so, the hash in the filename changes at each asset compilation (I believe), and manually trying to edit the filename each time could become a pain.

Acquire Asset Path from JavaScript

I need to display images on an HTML5 canvas that are in the Rails asset pipeline, but I need to know the path for the asset from JavaScript. I'm using js-routes for other parts of the application, but it doesn't appear to provide a way to get the path to something in the asset pipeline.
What's the correct way to obtain the path to a Rails asset (e.g., an image) from JavaScript?
In the Rails Asset Pipeline guide, they give an example of coding assets in your stylesheets by preprocessing the stylesheets with ERB. You can use the same technique with JavaScript, assuming you tack an .erb to the end of the filename:
var someAssetPath = "<%= asset_path('some_image.png') %>";
Checkout the js_assets(Javascript helper in rails projects) gem.
I think it is precisely what you need.
From the documentation:
Get the path to the template app/assets/javascripts/rubrics/views/index.html in javascript:
var path = asset_path('rubrics/views/index.html')
Why not add a data attribute for the path inside an element in your .erb file and then retrieve that with JQuery?
inside some_template.html.erb
<%= content_tag(:div, "", id: 'some-id', data:{path_to_asset: asset_path("some_image.png")}) %>
then in some_javascript.js
var assetPath = $("#some-id").data("pathToAsset");
For those using HAML you can do:
:javascript
var assetPath = "#{asset_path('some_image.jpg')}";
I came across the same issue in Rails 4.1 and used referencing rails assets in coffeescript for images. No additional libraries needed.
In my case, I wanted to get the stylesheet path and the hash that rails generates for cache busting made it impossible to hardcode.
What ended up working quite well for me is to assign an ID to the main stylesheet link element in the html (layout) and then use javascript to extract the href. If you want the base asset path, perhaps create a generic element with the data you need as an attribute.
Rendered HTML
<link rel="stylesheet" href="mypath/main.css" type="text/css" id="main-css">
JS
$("#main-css").attr("href"); // "mypath/main.css"

How to inline css when using the rails asset pipeline

Instead of having the page include a style tag with a link where to get the css from, which I could add to my view using rails' stylesheet_link_tag helper method, I want to have the css inline directly inside the page.
This is what I came up with so far:
%style(type="text/css")=File.read(physical_asset_path("email.css"))
But I can't find any rails' helper method which gives me the physical path of an asset - physical_asset_path is just a dummy method invented by me.
Anybody knows how to get the physical path of an asset when using rails 3.2.x?
Is there an easier/ better way to get stylesheets - from css files inside the common rails assets paths - inline?
Use case: most email clients don't access external sources (like css, images) without user confirmation. So to get the emails properly displayed I need to embed the CSS inside the emails' HTML.
Rails.application.assets.find_asset('email').to_s will return the compiled asset as a string.
Use premailer or premailer-rails3
https://github.com/fphilipe/premailer-rails3
or
https://github.com/alexdunae/premailer
Joe's Nerd Party say:
We also used the Premailer gem to automatically inline the linked
stylesheet in the email views. Our email layout looks something like:
%html
%head
= stylesheet_link_tag 'email'
%style{:type => "text/css"}
:sass
#media all and (max-width: 480px)
table#container
width: auto !important
max-width: 600px !important
... and so on for the mobile code
%body
Email body here.
%table
Lots of tables.
We include a stylesheet in the HTML. Premailer downloads it, processes
it, and inserts the css rules inline in the HTML.
The #media rules need to be inline in the email layout, since
Premailer can’t handle those being in a separate css file yet.
We use premailer-rails3 to integrate Premailer into Rails 3.
Unfortunately, we found a bunch of bugs in premailer and
premailer-rails3. Our forks of the projects are at
https://github.com/joevandyk/premailer and
https://github.com/joevandyk/premailer-rails3. The forks fix some
encoding bugs, remove some weird css processing stuff done by
premailer-rails3, allow premailer to not strip out embedded
rules in the email layouts, and some other things.
We also found a bug in sass-rails, where you can’t embed image-urls in
inline sass code. See https://github.com/rails/sass-rails/issues/71
Premailer-rails3 hooks into ActionMailer when the email actually being
delivered, not just generated. When running tests, email is not
actually sent, so the premailer-rails3 hooks don’t get ran during
tests. I haven’t spent the time to see if it’s possible to get the
premailer processing to run during tests, but that would be a nice
thing to do.
Also, our forks on premailer-rails3 assume that you want premailer to
go out and actually download the linked CSS files. It should be
possible to use the Rails 3.1 asset pipeline to get the processed css
without downloading it. A very special thanks goes to Jordan Isip who
did the super annoying job of making sure the emails look great in all
the different clients out there. Writing that CSS/HTML did not look
fun.
Update:
Roadie appears to be a better option. Thanks to Seth Bro for pointing it out.
(Sorry this answer is in html, not HAML… but that shouldn't be a problem for HAML fans)
I found this question when looking for a way to inline Sass compiled as css into html for creating html email templates.
Combining the above advice, I used the following code in the head of my html page:
<style type="text/css">
<%= Rails.application.assets['path/to/sass/file'].to_s.html_safe %>
</style>
This code compiles Sass as CSS and then inserts the css into a <style> tag. The html_safe ensures that any quotes (' and ") or angle brackets (> and <) used in the css are not escaped.
The path/to/sass/file is the same as you would use when creating a stylesheet link tag:
<%= stylesheet_link_tag 'path/to/sass/file', :media => 'all' %>
Rails.application.assets['asset.js'] will work only in local environment, as rails asset compilation is disabled in both production and staging environment.
Rails.application.assets_manifest.find_sources('asset.js').first.to_s.html_safe should be used to inline css when using rails asset pipeline.
Can't add comment to Seth Bro's answer. You better use #[] instead of #find_asset:
Rails.application.assets["email"].to_s.
Re "asset will not be compressed". It's not true. It will be compressed if you have compressors enabled (in rails config):
Rails.application.configure do
# ...
config.assets.css_compressor = :sass
config.assets.js_compressor = :uglify
end
Notice, that by default this is enabled in production environment (config/environments/production.rb).
Had the same problem, solved it using #phlegx's answer to a similar issue in Premailer.
For an environment-safe solution you need to use
(Rails.application.assets || ::Sprockets::Railtie.build_environment(Rails.application)).find_asset('email.css').to_s
I've packaged it into a helper in my app:
# app/helpers/application_helper.rb
# Returns the contents of the compiled asset (CSS, JS, etc) or an empty string
def asset_body(name)
(Rails.application.assets || ::Sprockets::Railtie.build_environment(Rails.application)).find_asset(name).to_s
end
I was trying to inline css for use in google amp compatible pages with rails. I found the following helper from vyachkonovalov which was the only thing for me working in production and locally.
Add the following to the erb template:
<style amp-custom>
<%= asset_to_string('application.css').html_safe %>
</style>
And the helper to ApplicationHelper. It works perfectly locally and in production.
module ApplicationHelper
def asset_to_string(name)
app = Rails.application
if Rails.configuration.assets.compile
app.assets.find_asset(name).to_s
else
controller.view_context.render(file: File.join('public/assets', app.assets_manifest.assets[name]))
end
end
tl;dr (without Roadie):
%style(type="text/css")
= render template: '../assets/stylesheets/email_responsive.css'
For actually applying the CSS as inline styles, I recommend roadie-rails (which is a Rails wrapper for Roadie). It also has other neat features like absolutizing hrefs, srcs etc.
A usage combining both inlined (email.scss) and non-inlined (email_responsive.css) stylesheets, both residing in app/assets/stylesheets:
-# This will be inlined and applied to HTML elements.
-# Note that you need to include this in your asset config, e.g.:
-# Rails.application.config.assets.precompile += %w(... email.css)
-# (You need to list it as `email.css` even if it's actually `email.scss`.)
= stylesheet_link_tag 'email'
-# E.g. for media queries which can't be inlined - yeah, some iOS devices support them.
-# This will not be inlined and will be included as is (thanks to `data-roadie-ignore`).
-# `template:` marks it as a full template rather than a partial and disables `_` prefix.
-# We need to add the extension (`.css`) since it's non-standard for a view.
%style(type="text/css" data-roadie-ignore)
= render template: '../assets/stylesheets/email_responsive.css'
You can use this:
Rails.root.join('public', ActionController::Base.helpers.asset_path("email.css")[1..-1]).read.html_safe

Where in the Rails framework should I place my Backbone templates?

I'm a rails developer trying to learn Backbone and then I ran into this problem: since Underscore templates include symbols like <%=%>, I guess templates can't be included into erb files, so is it okay to have a rails partial for every single template? And what extension should it be?
You can escape the erb symbols by using two % in the opening tag, and put your backbone templates in the rails views:
<script type='text/template' id="my-template'>
<%%= name %>
</script>
will output the following in your page:
<script type='text/template' id="my-template'>
<%= name %>
</script>
Putting your Backbone templates directly in your rails views is IMHO the best option when you're trying to learn. You're already wrestling with the new concepts, no need to add another hurdle.
Starting with Rails 3.1, it provides two things that make working with Backbone templates a little easier: the asset pipeline, and automatic JST (JavaScript Template) compilation.
Create a directory in your app/assets folder called templates. This directory will automatically be picked up by the asset pipeline.
Next, name the files in that directory with an extension of jst and the type of template you are creating ejs (embedded javascript). You can even nest them in directories. For example:
app/assets/templates/my_template.jst.ejs
app/assets/templates/bookmarks/show.jst.ejs
The asset pipeline also allows you to use other templating languages like embedded coffeescript, mustache, handlebars, etc. by simply changing the file extension (and including any necessary gems).
Now to reference your JST templates in your Backbone views, simply use the path to the filename:
var Bookmark = Backbone.View.extend({
template: JST['bookmarks/show'],
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
You may need to add this line to your application.js:
// require_tree ../templates
Here's a nice article which explains all of this in a little more detail: http://www.bigjason.com/blog/precompiled-javascript-templates-rails-3-1
Where should you put your Backbone templates? I'd say nowhere. I believe that in most Rails applications, the server should be responsible for all rendering of HTML, while the client-side JavaScript should just be responsible for inserting that rendered HTML into the DOM. Among other things, this makes I18n easier.
The exception would be if Rails is simply being used as a lightweight backend for an application that runs mostly on the client side (though in that case, you might want to use Sinatra or something instead). In this case, Rails should probably render nothing, and have the JS do all the rendering.
Notice the underlying principle here. Either the server should be responsible for all rendering, or the client should. Splitting it will make life harder.

Resources