How to enable compression in Ruby on Rails? - ruby-on-rails

I posted a similar question here
Serving Compressed Assets in Heroku with Rack-Zippy
but decided to give up on that service, since I couldn't get it to work.
I ran PageSpeed Insights on my website to determine the speed of my website.
The most important suggestion I received was to Enable Compression.
Compressing resources with gzip or deflate can reduce the number of bytes sent over the network.
Enable compression for the following resources to reduce their transfer size by 191.2KiB
(74% reduction).
I've followed the instructions on this website
https://developers.google.com/speed/docs/insights/EnableCompression
and it says to consult the documentation for your web server on how to enable compression:
I've used this website to find out my web server
http://browserspy.dk/webserver.php
It turns out that my web server is WEBrick.
The PageSpeed Insights Page only lists the following 3 servers
Apache: Use mod_deflate
Nginx: Use ngx_http_gzip_module
IIS: Configure HTTP Compression
I've searched for documentation on gzip compression for WEBrick servers but couldn't find anything.
I've searched for how to enable compression in Rails and couldn't find anything. That's why I'm asking here.
I've tried using Rack Zippy but gave up on it.
Right now, I don't even know where to begin. My first step, is finding out what I should do.
Edit
I followed Ahmed's suggestion of using Rack::Deflator
I confirmed that I had it by running
rake middleware
=> use Rack::Deflator
and then
git add .
git commit -m '-'
git push heroku master
Unfortunately PageSpeed still says it needs to be compress. I confirmed that by going into Developer Tools << Network Settings and refreshing the page. Size and content were virtually identical for every resource meaning the files are not compressed.
Is there something wrong with one of my files?
Thank you for your help.
Here is my full config/application.rb file
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(*Rails.groups)
module AppName
class Application < Rails::Application
config.middleware.use Rack::Deflater
config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
config.exceptions_app = self.routes
config.cache_store = :memory_store
end
end
If there is a problem, the source is likely over there, right?
Do I need to install the deflator gem?

Enable compression
Add it to config/application.rb:
module YourApp
class Application < Rails::Application
config.middleware.use Rack::Deflater
end
end
Source: http://robots.thoughtbot.com/content-compression-with-rack-deflater

Rack::Deflater should work if you use insert_before (instead of "use"), to place it near the top of the middleware stack, prior to any other middleware that might send a response. .use places it at the bottom of the stack. On my machine the topmost middleware is Rack::Sendfile. So I would use:
config.middleware.insert_before(Rack::Sendfile, Rack::Deflater)
You can get the list of middleware in order of loading by doing rake middleware from the command line.
Note: A good link for insert_before vs Use in middleware rack

As per the author of Rack::Deflater it should be placed after ActionDispatch::Static in a Rails app. The reasoning is that if your app is also serving static assets (like on Heroku, for example), when assets are served from disk they are already compressed. Inserting it before would only end up in Rack::Deflater attempting to re-compress those assets. Therefore as a performance optimisation:
# application.rb
config.middleware.insert_after ActionDispatch::Static, Rack::Deflater

Related

Rails not precompiling images in the app/assets/images folder?

I have some images (svg) in my app/assets/images folder. According to the Rails Guides, all the files in the assets folder should be automatically precompiled.
However, when I reference the the image using image_tag('filename'), it shows me an Sprockets::Rails::Helper::AssetNotPrecompiled error
Asset was not declared to be precompiled in production.
It tells me to declare the file to be precompiled manually, but why should that be necessary? On top of that, why does it concern itself with the production environment when I am doing everything in development?
If you added the image after you've started the server in development, restart the server. Sprockets will then precompile that image and the error will go away.
I'm pretty sure Rails doesn't support .svg yet, hence why it would ignore it.
You'll need to include the file extensions in your config/application.rb file:
#config/application.rb
config.assets.precompile += %w(.svg)
In regards the application concerning itself with the production environment, you have to remember that the precompilation process is meant for production:
The first feature of the pipeline is to concatenate assets, which can reduce the number of requests that a browser makes to render a web page. Web browsers are limited in the number of requests that they can make in parallel, so fewer requests can mean faster loading for your application.
Concantenating assets essentially means to compile your asset files into a single file, which is typically then minified.
--
Although this can be done in real-time, it's mostly the realm of static assets (which have to be precompiled). This means that if you run the rake asstes:precompile task, it will work on the development environment, unless you call RAILS_ENV=production rake assets:precompile (which sets it to the production environment for that request.
why does it concern itself with the production environment when I am doing everything in development
The application is going to run in production, not development.
Ultimately, everything you do in development should make it easier / better to work in production. In the sense of your assets, it means that you can use many of the quirks of Rails' asset pipeline, from sprockets to preprocessors such as SASS & Coffeescript
It's probably because you didn't specify the complete image name. I ran into this problem after updating the gem too. Before I just used image_tag 'some-image', but it seems that you now have to specify what type of image/extension you want to use.
Try this: image_tag 'some-image.svg'. It worked for me.
Cheers.

Sinatra static assets empty

I have a Sinatra app in a Rails app that serves a static asset from a directory. The app is mounted in the Rails routes like this:
mount BeanstalkdView::Server, :at => "/beanstalk"
When I run this locally it works fine, using Thin, but when I run it on my test server (Nginx/Passenger) the static assets behave strange. A request to a static file return 200 OK, but there is no content.
I tell Sinatra where my static files are via set :public_folder, "#{root}/resources" and in the template I load the static files, e.g. a CSS file with #{request.env['SCRIPT_NAME']}/css/file.css. I verified that both paths are correct.
That happens due to ::Rack::Sendfile middleware which is enabled by default in Rails 3.0.x in production (disabled by default in any environment since 3.1.x).
This middleware is really simple but yet powerful. When you pass ::Rack::File or ::Sinatra::StaticFile (or any other object) which respond to :path, this middlware adds X-SendFile (for Apache) or X-SendFile-Redirect (for NGinx) and does not sends actual body. So that Apache or NGinx will handle real file delivery. It's a good and most efficient way to serve static assets in production, however you may disable this middleware (if you don't want to mess with your Nginx/Apache config). Find and comment following config option in your config/environments/production.rb file:
config.action_dispatch.x_sendfile_header
this config option is used to instruct Sendfile middleware which header to set (do not do anything if not specified).
Aleksey V's answer helped me a lot. In the end I fixed this using the proper setting for Nginx in production.rb:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
Make sure you restart your Rails app, Nginx and do a hard refresh in your browser to get the files.
For more info check out: http://jimneath.org/2011/04/05/rails-3-nginx-and-send_file.html.

Rails3: How to use the asset pipeline to build static assets

So I started investigating the asset pipeline in Rails3 and I have a desired use case that is somewhat off the beaten path... so I'm looking for a recipe.
I often run webrick locally for development and then run
Passenger+Apache for deployed instances of the app.
the app is configured with a suburi path, e.g. http:// server/approot/...
to make webrick paths work like deployed instances, I added map '/approot' do run app to config.ru. Now webrick is also at http:// local:3000/approot/...
The Confusion
Given this setup, I tried to use rake assets:precompile and have been having a lot of configuration problems between local, deployed -- missing files, incorrect paths, 404s in firebug, etc. Here's a smattering of solutions I've tried:
config.assets.initialize_on_precompile = false to application.rb to prevent trying to initialize the app for production, (we have several deployed environments and call them different names, ug.) when precompiling the assets.
config.assets.precompile += %w( *.js *.css ) to application.rb to include things like jquery.js and ujs and rails.js that were missing.
config.assets.prefix = "/approot/assets" to correct a problem where the map above (in config.ru) doesn't apply to assets, so assets had to be precompiled to ./public/approot/assets, but I'm not sure if that only works locally, i.e. if I deploy, will my asset paths be http:// server/approot/approot/(js|css|...)?
When precompiling assets, the rake task switches to env production, but then it is unclear from the Rails3 doc whether sprockets continues to compile on the fly locally and use those dynamic assets, or whether it will serve the static precompiled assets instead?
I tried putting config.serve_static_assets = true in environments/development.rb, but I'm not sure how this works with config.assets.compress = false and config.assets.debug = true. Setting the assets.debug to false just seems to hide the GET requests in the webrick log, although I saw a post saying that "solved the problem" [sic].
Requirments for a Recipe
So I'd like a recipe that does the following:
assets are consistently and correctly served from a path http://server/approot/assets/... whether run in passenger or webrick (i.e. deployed or local). If this isn't possible, then I can switch my local dev environment to use Passenger+Apache as well, it's not a big deal, but I just want to know if it's possible.
raw assets exist in /app/assets like normal Rails3, but when I precompile them, they work exactly the same way in deployed envs so that asset file references don't break (i.e. right now, there are a lot of refs looking for /assets/image/... when the path is clearly set up as /approot/assets/image.... (It's unclear from the Rails3 doc whether there are assumptions about deploying to root vs a suburi, e.g. http:// server/assets/... vs. http:// server/approot/assets/...)
sprockets can't be used in deployed environments (our restriction, sorry). So this means the rails3 app has to effectively look like a static asset app. I know this is what precompiled assets are supposed to do, but the pathing issues are preventing me from getting this working as advertised.
TL;DR - I feel like I'm trying a lot of separate things that might work if I only knew the right combination of them.
References
http://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
http://blog.55minutes.com/2012/02/untangling-the-rails-asset-pipeline-part-2-production/
http://blog.55minutes.com/2012/02/untangling-the-rails-asset-pipeline-part-3-configuration/
https://github.com/rails/rails/pull/3946
Ok, here's a potentially horrible answer, but it seems to work with webrick in two contexts now and it's the day after Halloween (although I haven't tried this approach in deployed slots yet).
Configuration
Unless otherwise mentioned, everything is defaults from a rails new app generation.
config/application.rb
config.assets.initialize_on_precompile = false
environments/production.rb (not really production, only used for rake asset:precompile)
config.assets.css_compressor = :yui
config.assets.js_compressor = :uglifier
environments/stage.rb (this is one of our deployed envs)
config.serve_static_assets = true
config.ru
This is the horrible part. I duplicated the map so that Rack would serve both the suburi and the root. So the controller action that shows the layout can have http:// server/approot/foo/index, while the assets within the layout can be loaded from http:// server/assets/...
map '/approot' do
run AppRoot::Application
end
map '/' do
run AppRoot::Application
end
Running it locally
$ rake assets:precompile
$ rails s
and in firebug I see the separate parts served by sprockets (all 200 OK):
GET /approot/
GET /assets/application.css?body=1
GET /assets/jquery.js?body=1
GET /assets/jquery.ujs.js?body=1
GET /assets/application.js?body=1
Ok, so now test a 'deployed' slot locally and see if the compiled assets work?
$ rails s -e stage
and then I see the correct precompiled assets (all 200 OK):
GET /approot/
GET /assets/application-xxxxxxxxxxxxxxx.css
GET /assets/application-xxxxxxxxxxxxxxx.js
Ok, so this isn't as nice as a real suburi solution and I think I'm going to have problems in deployed slots. Round 2, fight!

Rails + Heroku + Jammit

I'm working to install Jammit on my Rails 3 app and then to deploy to Heroku.
I installed the Jammit Gem, and configured assets.yml just fine, it works on dev. But when I pushed to heroku, the files were 404'ing.
Jammit's Usage instructions say: "You can easily use Jammit within your Rakefile, and other scripts:
require 'jammit'
Jammit.package!
I'm not following where/how that works. Running Jammit in my sites command like on the Mac yields a command not found.
Any Jammit users able to help me understand how to move to production with Jammit?
Thanks
I'm using jammit on a Rails 3.0.7 app, on Heroku
gem "jammit", :git => "git://github.com/documentcloud/jammit.git"
I have this in a rake file, to package up the assets before I commit/deploy
desc 'jammit'
task :jam => :environment do
require 'jammit'
Jammit.package!
end
And this in .git/hooks/pre-commit so it is done automatically
echo "jamming it"
rake jam
git add public/assets/*
git add public/javascripts/*
By default, the expires time on Heroku was only 12hrs, to increase it (because I have a cache-busting scheme that I am confident in) I put this in config/initializers/heroku.rb
module Heroku
class StaticAssetsMiddleware
def cache_static_asset(reply)
return reply unless can_cache?(reply)
status, headers, response = reply
headers["Expires"] = CGI.rfc1123_date(11.months.from_now)
build_new_reply(status, headers, response)
end
end
end
To decrease the load on my Heroku Rails server, I am also using a free account at CloudFlare which provides a lightweight, reverse-proxy/cdn, with some decent analytics and security features.
When I get around to caching common content, this thing is really gonna scream!
You could, as I do, use jammit force to pack your assets, upload everything to s3 and define an asset host(s) in rails. This has the added advantage of keeping your slug smaller and more responsive as you can add your public directory to .slugignore .
Alternatively you'll need to work out how to make the heroku version work due to the read only file system.
You can also use a git pre-commit hook to ensure your assets are packaged prior to pushing to heroku (or any server). See https://gist.github.com/862102 for an example. You can copy that file to .git/hooks/pre-commit in your project directory.
this one is the solution
https://github.com/kylejginavan/heroku_jammit
Heroku has a read-only file system, so Jammit is unable to actually store compressed and minified CSS/JS files.
Here is a very good article on the challenge of asset packaging on heroku: http://jimmycuadra.com/posts/the-challenge-of-asset-packaging-on-heroku

What is the significance of the dispatch.fcgi file in Rails

There is a file under public folder in Rails environment called dispatch.fcgi. What is the significance of this particular file?
I opened that file and it has this
# # Default log path, 50 requests between GC.
# RailsFCGIHandler.process! nil, 50
#
# # Custom log path, normal GC behavior.
# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
#
require File.dirname(__FILE__) + "/../config/environment"
require 'fcgi_handler'
RailsFCGIHandler.process!
Cannot understand what this does. Can someone please explain?
That must be an old version of rails, because this file is a relic for servers which start the rails app with fcgi in your http server.
Apache and Nginx are now supported via passenger, or you can use a proxy with a mongrels cluster, all these solutions don't need a dispatch.fcgi.
https://serverfault.com/questions/60222/apache-dispatch-fcgi-doesnt-get-interpreted-with-passenger

Resources