Hotlinking to my Rails 4 app's assets from a static site - ruby-on-rails

I have a rails 4 app on heroku, and I want to embed some of my image assets in a static website that I'm making.
The problem is that rails adds a digest to the asset path.
<img src="http://myapp.herokuapp.com/assets/tools/my-image-43b65377b7644fae3f34d288f3235b80.png"/>
This makes it very hard for me to hotlink to my assets, because their digests may change over time.
Is there anyway to embed my images without having to insert this digest? I know that rails 3.1 allowed you to do so.

In rails-4, non digested assets are not generated anymore.
If you want to hotlink assets, place a copy of image you'll use in public/. To avoid duplication, you can use a symlink pointing to the proper location in app/assets/images/.
It's worth noting that rails does a very good job at caching images in browser, and it is usually recommended to add far future timeout for assets caching in front server. So you should probably avoid to hotlink images that are prone to change often.

Related

Serving images in rails

For every version of my app, I upload my assets to the cloud CDN, and the images on the website are loaded as something like imagename-somehash.
I have static images that are large, i.e like carousel images, which don't change often or at all. So even when the app changes, they can remain cached by clients who previously visited the website.
How would one do that? Any images I have under app/assets folder gets upload to CDN with the imagename-somehash format, so the image changes with every version of the app.
I'm using rails 4.2. Is this possible?
In rails guides there is an article What is Fingerprinting and Why Should I Care? which gives some insights about that hash on the end of a file and how it works.
Check your environments files. In production you may want config.assets.digest to be true.
You can put these images in public folder and can mention full url in views for these images
OR You can also put it on
vendor/assets/images
but in that case you would have to disabled precompile path
If you want to serve only some assets from your CDN, you can use custom :host option your asset helper, which overwrites value set in config.action_controller.asset_host.
<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>

Can CDN distribution of assets be combined with HTML5 cache manifest in a Rails app?

In a Rails 3.2 app I'm using asset_sync to host my assets on S3, and Cloudfront to distribute these assets, accessible via 4 CNAME'd URLs. It has been a good way of increasing the app's performance and reducing load on the server.
I'm now beginning to explore offline capabilities and HTML5 cache manifest. Obviously the cache manifest can be used to serve the static assets to repeat visitors.
I have two questions:
Can these two approaches be used together? i.e., the first time a
user visits the app, assets are downloaded from the cloudfront CDN.
Thereafter, assets are served from the cache manifest. There appear
to be conflicting reports across the internet as to whether the
manifest file and assets need to be on the same sub-domain. My app
is at http://app.example.com, whereas my assets are at
http://asset0.example.com, http://asset1.example.com, etc.
Secondly, how should I handle the unique ID applied to assets? For
example, assets have names such as
http://asset1.example.com/assets/application-hdggajdjd7672h12bsud8.js.
Do I need to handle these random strings to ensure assets are
properly cached, or are these strings created when updates are
pushed to the server and assets precompiled, hence will remain
static unless changes are made to the files.

How should I handle the rails asset pipeline for uploaded images that change the scope of their public view?

After reading about how rails is handling image assets, I am confused and am having trouble deciding where to keep uploaded images in my app's directory tree. If a user has uploaded an image to my rails app, should I make the image_save_path /assets/images/ or /public/images ?
My concern/question: If I want to allow users to make their images public when they choose to do so, should an uploaded image that is 'un-published' (image is served only if current_user == image.owner) be considered more protected if it resides in assets/images instead of public/images? Equally protected? Less protected somehow?
Am I right to assume that the benefit of using the asset pipeline for images is simply that the file names get hashed in production mode and that's it? Is there some implied additional or diminished security here in terms of how the resource is served? Is this more a matter of subtle opinion or does the rails convention have something to say here?
Thanks!
EDIT1:
How would you handle uploaded content that you want to make available to the uploading user, but not discoverable to guests or other users? Should (can?) one store uploaded content outside the tree under web_root ?
Application assets are those assets that are part of your application, such as backgrounds and icons (in the case of images). The asset pipeline has nothing to do with user-uploaded images. I'd suggest to keep them separated, serve them from separate CDN if needed.
The hashing of filenames in the asset pipeline is done only during asset compilation, so a link to a specific image is made unique for the image contents. This helps to avoid setting complex caching headers to images and other assets, as when the image changes, the filename changes too, and the cached version won't be used at all.
All this does not apply to user-uploaded images, you are not going to compile on-the-fly the assets for their images.

Why should I use the Asset Pipeline to serve images?

In the Ruby on Rails guide to the Asset Pipeline, it says
Any assets under public will be served as static files by the
application or web server. You should use app/assets for files that
must undergo some pre-processing before they are served.
http://guides.rubyonrails.org/asset_pipeline.html
To me, this says that images should be kept in the public directory as they can be served statically by my web server and require no pre-processing.
Are there advantages to putting your images in assets/?
Asset precompile appends unique hash value to image filenames, which allows users to get latest version of it despite of cache or expire settings on the server. This is useful when you want to change images in website design.
You don't want to use /assets/images for images what are unlikely to change (like user uploads).
Because it would harm the versatility of the asset pipeline.
With images being served via the asset pipeline, if you change the asset pipeline (for instance, have it upload files to S3) you would have to then sync your images via some other task.
There may be other, deeper reasons, but that is what would give me pause.
EDIT:
Side note: In production thanks to assets:precompile everything from /assets/ will be served from public.

config.assets.compile=true in Rails production, why not?

The default Rails app installed by rails new has config.assets.compile = false in production.
And the ordinary way to do things is to run rake assets:precompile before deploying your app, to make sure all asset pipeline assets are compiled.
So what happens if I set config.assets.compile = true in production?
I wont' need to run precompile anymore. What I believe will happen is the first time an asset is requested, it will be compiled. This will be a performance hit that first time (and it means you generally need a js runtime in production to do it). But other than these downsides, after the asset was lazily compiled, I think all subsequent access to that asset will have no performance hit, the app's performance will be exactly the same as with precompiled assets after this initial first-hit lazy compilation. is this true?
Is there anything I'm missing? Any other reasons not to set config.assets.compile = true in production? If I've got a JS runtime in production, and am willing to take the tradeoff of degraded performance for the first access of an asset, in return for not having to run precompile, does this make sense?
I wrote that bit of the guide.
You definitely do not want to live compile in production.
When you have compile on, this is what happens:
Every request for a file in /assets is passed to Sprockets. On the first request for each and every asset it is compiled and cached in whatever Rails is using for cache (usually the filesystem).
On subsequent requests Sprockets receives the request and has to look up the fingerprinted filename, check that the file (image) or files(css and js) that make up the asset were not modified, and then if there is a cached version serve that.
That is everything in the assets folder and in any vendor/assets folders used by plugins.
That is a lot of overhead as, to be honest, the code is not optimized for speed.
This will have an impact on how fast asset go over the wire to the client, and will negatively impact the page load times of your site.
Compare with the default:
When assets are precompiled and compile is off, assets are compiled and fingerprinted to the public/assets. Sprockets returns a mapping table of the plain to fingerprinted filenames to Rails, and Rails writes this to the filesystem. The manifest file (YML in Rails 3 or JSON with a randomised name in Rails 4) is loaded into Memory by Rails at startup and cached for use by the asset helper methods.
This makes the generation of pages with the correct fingerprinted assets very fast, and the serving of the files themselves are web-server-from-the-filesystem fast. Both dramatically faster than live compiling.
To get the maximum advantage of the pipeline and fingerprinting, you need to set far-future headers on your web server, and enable gzip compression for js and css files. Sprockets writes gzipped versions of assets which you can set your server to use, removing the need for it to do so for each request.
This get assets out to the client as fast as possible, and in the smallest size possible, speeding up client-side display of the pages, and reducing (with far-future header) requests.
So if you are live compiling it is:
Very slow
Lacks compression
Will impact render time of pages
Versus
As fast as possible
Compressed
Remove compression overheard from server (optionally).
Minimize render time of pages.
Edit: (Answer to follow up comment)
The pipeline could be changed to precompile on the first request but there are some major roadblocks to doing so. The first is that there has to be a lookup table for fingerprinted names or the helper methods are too slow. Under a compile-on-demand senario there would need to be some way to append to the lookup table as each new asset is compiled or requested.
Also, someone would have to pay the price of slow asset delivery for an unknown period of time until all the assets are compiled and in place.
The default, where the price of compiling everything is paid off-line at one time, does not impact public visitors and ensures that everything works before things go live.
The deal-breaker is that it adds a lot of complexity to production systems.
[Edit, June 2015] If you are reading this because you are looking for a solution for slow compile times during a deploy, then you could consider precompiling the assets locally. Information on this is in the asset pipeline guide. This allows you to precompile locally only when there is a change, commit that, and then have a fast deploy with no precompile stage.
To have less overhead with Pre-compiling thing.
Precompile everything initially with these settings in production.rb
# Precompile *all* assets, except those that start with underscore
config.assets.precompile << /(^[^_\/]|\/[^_])[^\/]*$/
you can then simply use images and stylesheets as as "/assets/stylesheet.css" in *.html.erb
or "/assets/web.png"
For anyone using Heroku:
If you deploy to Herkou, it will do the precompile for you automatically during the deploy if compiled assets are not included (i.e. public/assets not committed) so no need for config.assets.compile = true, or to commit the precompiled assets.
Heroku's docs are here. A CDN is recommended to remove the load on the dyno resource.
It won't be the same as precompiling, even after that first hit: because the files aren't written to the filesystem they can't be served directly by the web server. Some ruby code will always be involved, even if it just reads a cache entry.
Set config.asset.compile = false
Add to your Gemfile
group :assets do
gem 'turbo-sprockets-rails3'
end
Install the bundle
Run rake assets:precompile
Then Start your server
From the official guide:
On the first request the assets are compiled and cached as outlined in development above, and the manifest names used in the helpers are altered to include the MD5 hash.
Sprockets also sets the Cache-Control HTTP header to max-age=31536000. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
This mode uses more memory, performs poorer than the default and is not recommended.
Also, precompile step is not trouble at all if you use Capistrano for your deploys. It takes care of it for you. You just run
cap deploy
or (depending on your setup)
cap production deploy
and you're all set. If you still don't use it, I highly recommend checking it out.
Because it is opening a directory traversal vulnerability - https://blog.heroku.com/rails-asset-pipeline-vulnerability

Resources