Rails 3.1: Access a fingerprinted image from css - ruby-on-rails

Is there an elegant way to access images with fingerprints in production from css?
I know I can use erb for css, but adding urls with erb looks ugly, and I think it should be someway automated. Besides I don't want to change vendor stylesheets.
Thanks!

I think image-url should solve this problem
.btn_back:hover {background-image: image-url('btn_back_push.png');}
it works for me in dev. mode as well as in the production with precompiled assets as result
dev. mode:
.btn_nav:hover {
background-image: url("btn_nav_push.png");
}
production:
.btn_nav:hover{background-image:url(/assets/btn_nav_push-094b577d7e9e1cc6d5aced334f3fe8b3.png)}.

sass-rails has added a helper for this called image-path. You can use it like this:
#image {
background: image-path("rails.png")
}
This won't work for normal css files, but because scss is a superset of css, so you should be able to change the extension to .scss and all will be good.

Related

Rails: How to reference images in CSS within Rails 4

There's a strange issue with Rails 4 on Heroku. When images are compiled they have hashes added to them, yet the reference to those files from within CSS don't have the proper name adjusted. Here's what I mean. I have a file called logo.png. Yet when it shows up on heroku it is viewed as:
/assets/logo-200a00a193ed5e297bb09ddd96afb953.png
However the CSS still states:
background-image:url("./logo.png");
The result: the image doesn't display. Anybody run into this? How can this be resolved?
Sprockets together with Sass has some nifty helpers you can use to get the job done. Sprockets will only process these helpers if your stylesheet file extensions are either .css.scss or .css.sass.
Image specific helper:
background-image: image-url("logo.png")
Agnostic helper:
background-image: asset-url("logo.png", image)
background-image: asset-url($asset, $asset-type)
Or if you want to embed the image data in the css file:
background-image: asset-data-url("logo.png")
Don't know why, but only thing that worked for me was using asset_path instead of image_path, even though my images are under the assets/images/ directory:
Example:
app/assets/images/mypic.png
In Ruby:
asset_path('mypic.png')
In .scss:
url(asset-path('mypic.png'))
UPDATE:
Figured it out- turns out these asset helpers come from the sass-rails gem (which I had installed in my project).
In Rails 4, you can reference an image located in assets/images/ in your .SCSS files easily like this:
.some-div {
background-image: url(image-path('pretty-background-image.jpg'));
}
When you launch the application in development mode (localhost:3000), you should see something like:
background-image: url("/assets/pretty-background-image.jpg");
In production mode, your assets will have the cache helper numbers:
background-image: url("/assets/pretty-background-image-8b313354987c309e3cd76eabdb376c1e.jpg");
The hash is because the asset pipeline and server Optimize caching
http://guides.rubyonrails.org/asset_pipeline.html
Try something like this:
background-image: url(image_path('check.png'));
Goodluck
In css
background: url("/assets/banner.jpg");
although the original path is /assets/images/banner.jpg, by convention you have to add just /assets/ in the url method
None of the answers says about the way, when I'll have .css.erb extension, how to reference images. For me worked both in production and development as well :
2.3.1 CSS and ERB
The asset pipeline automatically evaluates ERB. This means if you add an erb extension to a CSS asset (for example, application.css.erb), then helpers like asset_path are available in your CSS rules:
.class { background-image: url(<%= asset_path 'image.png' %>) }
This writes the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as app/assets/images/image.png, which would be referenced here. If this image is already available in public/assets as a fingerprinted file, then that path is referenced.
If you want to use a data URI - a method of embedding the image data directly into the CSS file - you can use the asset_data_uri helper.
.logo { background: url(<%= asset_data_uri 'logo.png' %>) }
This inserts a correctly-formatted data URI into the CSS source.
Note that the closing tag cannot be of the style -%>.
Only this snippet does not work for me:
background-image: url(image_path('transparent_2x2.png'));
But rename stylename.scss to stylename.css.scss helps me.
WHAT I HAVE FOUND AFTER HOURS OF MUCKING WITH THIS:
WORKS :
background-image: url(image_path('transparent_2x2.png'));
// how to add attributes like repeat, center, fixed?
The above outputs something like: "/assets/transparent_2x2-ec47061dbe4fb88d51ae1e7f41a146db.png"
Notice the leading "/", and it's within quotes.
Also note the scss extension and image_path helper in yourstylesheet.css.scss. The image is in the app/assets/images directory.
Doesn't work:
background: url(image_path('transparent_2x2.png') repeat center center fixed;
doesn't work, invalid property:
background:url(/assets/pretty_photo/default/sprite.png) 2px 1px repeat center fixed;
My last resort was going to be to put these in my public s3 bucket and load from there, but finally got something going.
Referencing the Rails documents we see that there are a few ways to link to images from css. Just go to section 2.3.2.
First, make sure your css file has the .scss extension if it's a sass file.
Next, you can use the ruby method, which is really ugly:
#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
Or you can use the specific form that is nicer:
image-url("rails.png") returns url(/assets/rails.png)
image-path("rails.png") returns "/assets/rails.png"
Lastly, you can use the general form:
asset-url("rails.png") returns url(/assets/rails.png)
asset-path("rails.png") returns "/assets/rails.png"
Interestingly, if I use 'background-image', it does not work:
background-image: url('picture.png');
But just 'background', it does:
background: url('picture.png');
In some cases the following can also be applier
logo { background: url(<%= asset_data_uri 'logo.png' %>) }
Source: http://guides.rubyonrails.org/asset_pipeline.html
You can add to your css .erb extension. Ej: style.css.erb
Then you can put:
background: url(<%= asset_path 'logo.png' %>) no-repeat;
When using gem 'sass-rails', in Rails 5, bootstrap 4, the following worked for me,
in .scss file:
background-image: url(asset_path("black_left_arrow.svg"));
in view file(e.g. .html.slim):
style=("background-image: url(#{ show_image_path("event_background.png") })");
This should get you there every single time.
background-image: url(<%= asset_data_uri 'transparent_2x2.png'%>);
By default Rails 4 will not serve your assets. To enable this functionality you need to go into config/application.rb and add this line:
config.serve_static_assets = true
https://devcenter.heroku.com/articles/rails-4-asset-pipeline#serve-assets
In Rails 4, simply use .hero {
background-image: url("picture.jpg");
} in your style.css file as long as the background image is tucked in app/assets/images.
This worked for me:
background: #4C2516 url('imagename.png') repeat-y 0 0;

How to include twitter bootstrap in my Rails project manually (without using any gems)?

I'm learning Rails and want to play with Rails and Twitter Bootstrap. My Rails project layout is:
├─assets
│ ├─images
│ ├─javascripts
│ └─stylesheets
├─controllers
├─helpers
├─mailers
├─models
└─views
├─course
└─layouts
The Twitter Bootstrap layout is:
├─css
├─img
└─js
I know Bootstrap css file references its image file, such as:
[class*=" icon-"] {
display: inline-block;
width: 14px;
height: 14px;
*margin-right: .3em;
line-height: 14px;
vertical-align: text-top;
background-image: url("../img/glyphicons-halflings.png");
background-position: 14px 14px;
background-repeat: no-repeat;
margin-top: 1px;
}
So they must retain there relative position, or I have to change the CSS file. I want to know, if I don't want to use any Bootstrap related gems, what's the best way to place these Bootstrap files into my Rails project? Thanks.
First, you should probably move the Bootstrap source files to their appropriate locations in the assets folder in your Rails app - that is, CSS files in the stylesheets folder, JS in javascripts, and images in images.
As mentioned already, you'll need to change paths to images in Bootstrap's CSS. However, you'll need to make use of Rails' asset path helpers if you plan on using your app in production.
For example, background-image: url('../images/glyphicons-halflings.png'); is absolutely incorrect when using the asset pipeline. This will work fine in development, but as soon as you pre-compile assets for a production environment things won't work - Rails appends fingerprints to asset file names for caching purposes, which makes the URL above incorrect.
The correct way to code paths in your assets is outlined in the Rails Guide for the Asset Pipeline. If you're using CSS only, you should add the .erb extension to your filename (to get bootstrap.css.erb) and do something like this:
background-image: url(<%= asset_path 'glyphicons-halflings.png' %>);
If you are using SASS/SCSS, you can also use the built-in asset-path or image-path helpers. Again, this is mentioned in the guide I linked to above.
In the end, you probably should be using a gem, as this work will already be done for you. But, if you must, this should work well enough. Of course, if you ever want to update Bootstrap, you'll have to do this again.
I ran into the same trouble, and didn't want to have to install gems, etc.
There's a much easier solution.
Just override the icon selectors for background image in your custom CSS.
[class^="icon-"], [class*=" icon-"] {
background-image: url("/assets/img/glyphicons-halflings.png");
}
[class^="icon-white"], [class*=" icon-white"] {
background-image: url("/assets/img/glyphicons-halflings-white.png");
}
Put the glyphicons PNGs in app/assets/images/img/ (or wherever you want) and you're done.
You have to change every Glyphicons icon to the assets path. That's a lot of work. Also you have to use LESS, so the easiest way to use bootstrap with rails is using a gem. This is the gem that I use gem 'twitter-bootstrap-rails' this gem also includes font awesome so check this too
Redirect calls in routes.rb
get '/img/:name', to: redirect {|params, req| "/assets/#{params[:name]}.#{params[:format]}" }
This will redirect calls for /img to /assets
You can put the css and js files anywhere in your project as they don't reference each other, however, you will have to update the glypicon references in the css file.
In your case:
background-image: url("../images/glyphicons-halflings.png");
EDIT: I updated the path. I originally included "assets" which is incorrect with your given directory structure.

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

CSS in Rails Asset Path not processed by ERB in development

I have a Rails app with the following in /app/assets/stylesheets/styles.css.erb:
...
#nestedbg {
background-position: left top;
background-image: url(<%= asset_path 'siteheader2.png' %>);
background-repeat: repeat-x;
background-attachment: fixed;
}
...
When I run rake assets:precompile and then run rails s -e production, everything works as expected. However, when I remove the precompiled assets and run rails s in development, the CSS file comes up as shown above instead of being properly substituted.
I tried putting config.assets.compile = true in /config/environments/development.rb and that did not help.
Any ideas?
Thanks.
I honestly cannot say why this is not interpreted correctly in your case, but I have a much better workaround to offer: skip erb interpreting altogether.
You can do this like so:
/* styles.css.scss */
background-image:url(image_path("siteheader2.png"));
If you did not have a chance to I would also suggest to have a look at SASS: it is integrated in the Rails asset pipeline and lets you do cool things like variable declarations, nesting, mixins, ...
I found that my css files wouldn't be processed by ERB unless SCSS processing was also added.
I changed my screen.css.erb to screen.css.scss.erb and now <%= asset_path 'file.png' %> is rendered correctly as /assets/file.png.
I'm on Rails 3.1.3.
I was using Rails 3.1.1 and when I switched the app to use Rails 3.1.3, the problem went away. I switched back to 3.1.1 to see if the issue came back and it did not.
I'm guessing that it was a problem with one of the gems and the update to 3.1.3 brought other gem updates with it.
Bizarrely, I found that changing asset_path to asset_data_uri and then back to asset_path worked for me. Was using Rails 3.1.3 all along.
Strange.
Sam Oliver's advice did the trick for me, simply renaming the extensions didn't update the timestamp on the files.
CSS and ERB
The asset pipeline automatically evaluates ERB. This means that if you add an erb extension to a CSS asset (for example, application.css.erb), then helpers like asset_path are available in your CSS rules:
.class { background-image: url(<%= asset_path 'image.png' %>) }
This writes the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as app/assets/images/image.png, which would be referenced here. If this image is already available in public/assets as a fingerprinted file, then that path is referenced.
If you want to use a data URI — a method of embedding the image data directly into the CSS file — you can use the asset_data_uri helper.
CSS and Sass:
When using the asset pipeline, paths to assets must be re-written and sass-rails provides -url and -path helpers (hyphenated in Sass, underscored in Ruby) for the following asset classes: image, font, video, audio, JavaScript and stylesheet.
image-url("rails.png") becomes url(/assets/rails.png)
image-path("rails.png") becomes "/assets/rails.png".
The more generic form can also be used but the asset path and class must both be specified:
asset-url("rails.png", image) becomes url(/assets/rails.png)
asset-path("rails.png", image) becomes "/assets/rails.png"
Referenced By: Rails Guide Asset Pipe Line
Heading: 2.2.1 and 2.2.2 respectively.

Asset Subdirectories in Rails 3.1

I have a Rails 3.1 app with an image:
app/assets/images/icons/button.png
It seems like the image should be served at this URL:
assets/icons/button.png
but if I go to this URL I get a 404. To fix this I created an initializer and added my images/icons subdirectory to the asset path:
Rails.application.assets.append_path "app/assets/images/icons"
However, this does not seem like it can possibly be the recommended way to accomplish this. I'm aware of the require and require_tree directives for JavaScript and CSS assets, is there an equivalent for image assets? How are other people doing this?
EDIT: As of Rails 3.2.rc1 this is now fixed! asset_path now generates proper paths when deploying to sub-uri!
For images it just works. Rails packages everything in images/ tree. I personally use them like this (actual code):
CSS:
a#icon-followers{
background: url(<%= asset_data_uri "icons/followers.png" %>) center center no-repeat;
}
(asset_data_uri actually makes the images inline in the CSS file using base64, but that's irrelevant in this case)
No custom configuration required. After precompiling, images from app/assets/icons/ end up in public/assets/icons/.
You can open public/assets/manifest.yml to see how Rails translates the paths to actual files.

Resources