Updating assets without restarting rails server - ruby-on-rails

So the question basically boild down to this:
How do you efficiently handle changing assets in a production rails environment without the need to restart the server?
The problem we're experiencing is, we have to restart the Thin server that runs the app in order for the updated javascript files to be served.
Some background:
Right now we're generating data from a couple of long running tasks into javascript files once an hour so we can use it in our Rails app.
To be clear, we update/overwrite existing files, not adding new ones.
After generation we run these commands in order to re-precompile all the assets.
bundle exec rake assets:precompile
bundle exec rake rails_group=assets assets:clean RAILS_ENV=production
Still after clearing the browser cache and reloading the page we're being served the old assets.
Have you guys made any similar experiences; what did you do to work around it?
PS. Happy holidays to you all!

So, what we ended up doing is basically letting rails also serve static assets by setting
config.serve_static_assets = true
in config/environments/production.rb
and just putting the frequently changing javascript data files into a directory structure under public/.
This works grate since it also separates assets and data into different locations.

According to the Rails guide:
6 How Caching Works Sprockets uses the default Rails cache store to
cache assets in development and production.
Rails is going to cache your assets unless you tell it to not cache them. The whole point of the asset pipeline is to serve assets as quickly as possible by encouraging browsers and servers and the rails servers themselves to cache the assets.
If your use case involves redoing assets very often, maybe the asset pipeline isn't for you.

Related

heroku precompile assets is it necessary

I've started learning rails and I've already built two apps, one simple blog app and one store app. Now I ran into a term precompile assets when uploading to heroku, can someone explain it to me is that necessary when deploying an app to production, because i've uploaded my store app to heroku without any problems?
Assets is your css + JS. Precompile assets mean that they get joined into single .css and another single .js. file (to load it in one HTTP request). And special mechanism of minifying get applied to both these files (to make them smaller). Rails by default is setup in a way, that it uses average files in dev and compiled files in prod. You can easily change this in configs, but you shouldn't do this unless you really know what you do.
If you want you can compile this files locally running rake assets:precompile and then put it into git. I think that you can disable/enable precompile during heroku deploy in heroku config. But, in general, I would stick with the very defaults.
More info on asset pipeline: http://guides.rubyonrails.org/asset_pipeline.html
Rails has an assets pipeline which consists of Sprockets and the assets helpers.
The assets pipeline will concat and minify your CSS and javascript and takes care of setting the correct paths to images and other assets. This is known as compiling the assets.
In development this is done on the fly for each request which lets you immediately see changes.
In production this would be far to slow so instead the assets should be compiled once at deploy time. Heroku does this automatically for you in a post-commit hook.
Pre-compiling is when you run rake assets:precompile locally and then upload or push the result to a server. This is done if you are deploying to a server without the support for the assets pipeline. For example if the production server does not have a javascript runtime which is required to run uglifier.
It adds tons of noise to the git change history and manually doing anything is a common source of user error. So pretty much it sucks and you only do it if you have to.

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.

How to remove old rails assets on heroku?

My rails app is working but its slug size is huge (220mb).
I am storing all assets on cloudfront with config.assets.digest = true and config.action_controller.asset_host = "something.cloudfront.net"
heroku run bash
In the slug, there is 30 times application-onedigest.js in public/assets, and same thing for stylesheets and images, so now the slug size is 220 mb.
Maybe one of my gem is doing middleware things (Rails asset_host, cloudfront and heroku) or maybe heroku is trying to optimize things (https://devcenter.heroku.com/changelog-items/328), but i don't think so. I don't want to exceed the maximum heroku slug size that is 300 mb, and i feel that is not normal.
How to remove old rails assets on heroku ?
EDIT
I made little commits in a css file, push to heroku, and compared slug. In the deploy process, there is those lines:
Running: rake assets:precompile
INFO -- : Writing /tmp/build_ff4eb6d7-303e-444d-9c88-938ab504ea8a/public/assets/application-700c7e1849c55312a94a353e60312500.css
Asset precompilation completed (9.60s)
Cleaning assets
Running: rake assets:clean
INFO -- : Removed application-48375ba5495e14b36afab9d4b9d97033.css
This is what i want. But when i compare the old and the new slug, there is the new application-700....css, and an another new mysterious application-a9b33ae58934ad161038cb3ebcee146c.css
And this one is actually used is the heroku webapp (when i explore css resources from the browser, after clearing cache).
So i guess precompile is done twice somewhere?
Temporary solution : heroku fork the app, i am back to 40 mb but there is still the problem.
EDIT
It seems it works when i precompile locally, but i don't like it as it violates 12factors.
Some things you'll want to consider:
rake assets:clobber
Heroku's file system
CDN
rake assets:clobber is an inbuilt way to remove old asset files from your public/assets directory. By running this command, either locally or on Heroku, you'll be able to actively remove the precompiled asset files on your system, allowing you to repopulate the folder with new files
Secondly, you have to remember Heroku runs an ephemeral file system. This means that each time you push your application to Heroku, it will just overwrite any of the files you had before, meaning (importantly), that it's not going to store your last assets. Each time you push, it will precompile the assets for you - which should ensure minimal space taken up with them.
--
Finally, if you find that your assets are taking up too much space on your application server, you may consider using a content delivery network (CDN) to serve the assets you need. You can use the asset_sync gem to push your assets to your CDN of choice
Most people tend to use a CDN with storage system to serve their assets. We use S3 with Cloudfront currently, but are looking to move to Rackspace's file serve system!
You can use the Heroku repo plugin (https://github.com/heroku/heroku-repo) and the repo:purge command to clean all the build assets out of your slug.
Using asset_sync gem is not ideal as you can run into deploy timeouts pushing all the assets over to S3 plus you end up with assets in two places. Remember S3 is not a content delivery network, you're better off using a CloudFront distribution and have it cache the assets directly from your application. See https://devcenter.heroku.com/articles/using-amazon-cloudfront-cdn for more details.
This solve it for me, in production.rb :
config.assets.compile = false
Heroku is no longer generating useless assets. I don't know how to remove the old assets in the same heroku app, but i used "heroku fork" to create a brand new heroku app with the same config / database and without old assets.
heroku run rake assets:clobber

Rails asset pipeline workflow when there are hundreds of asset files

We recently converted a Rails 3.2 project to use the asset pipeline. Our app uses many asset files (~250 *.js.coffee files, another 200 or so *.jst.hamljs templates, and ~100 *.css.sass stylesheets). As you'd expect, these are packaged into a much smaller number of asset files in production.
In development and test environments, we deal with frequent timeouts and slow page-loads while recompiling and requesting individual assets. Is there a better workflow that still allows automatic recompilation?
We're running with default environment settings, except have set debug to false in development:
config.assets.debug = false
This saves us hundreds of requests when reloading the page in development, but initial compilation still causes timeouts. There must be a better way, right?
Some options:
Limit what is required to be recompiled when requested via use of require_asset. See this question and Sprockets #90.
Precompile assets:
$ RAILS_ENV=development bundle exec rake assets:precompile
Increase timeouts. This depends on how you have things setup and can be painful and problematic, e.g. Heroku is not a big fan of long requests.
Use a faster interpreter.
Faster hardware/VM.
Are you precompiling the assets in development?
bundle exec rake assets:precompile
That should keep the assets from recompiling each time.

Conditionally disable asset precompile in Capistrano

I've seen various convoluted and generally ineffective solutions to performing lazy asset precompile in Rails. As a backend developer I don't particularly want to recompile assets I never touch every time the program deploys, but because assets are loaded in Capfile via load 'deploy/assets', and not by defining a task in deploy.rb, I can't think of a way to conditionally disable it.
The behaviour I'm after is to use cap deploy for regular with-precompile deployment, and to use cap deploy:no_assets to skip asset deployment.
Both turbo-sporocket-rails and the that auto-skip scripts have some pitfalls (I will mention later). So I use the following hack, so I can pass a parameter to skip asset pre-compile at my will:
callback = callbacks[:after].find{|c| c.source == "deploy:assets:precompile" }
callbacks[:after].delete(callback)
after 'deploy:update_code', 'deploy:assets:precompile' unless fetch(:skip_assets, false)
This script will change the built-in asset-precompile hook, so it will be hooked based on the skip_assets parameter. I can call cap deploy -S skip_assets=true to skip asset precompile as a whole.
For me, turbo-sporocket-rails still takes minutes to do the checking when nothing has changed. This can be crucial when I need to push a fix to the server asap. Therefore I need my force-skipping method.
rails4 addresses this issue with it's new version of sprockets, by only precompiling assets that have changed. In the mean time, for your rails3 apps I recommend the turbo-sprockets-rails3 gem.
This gem started out as a set of patches for sprockets-rails by Nathan Broadbent, which were not merged into master because the problem was already addressed in rails4. From the README:
Speeds up your Rails 3 rake assets:precompile by only recompiling changed assets, based on a hash of their source files
Only compiles once to generate both fingerprinted and non-fingerprinted assets
And:
turbo-sprockets-rails3 should work out of the box with the latest version of Capistrano.
I can confirm that it works well for me on rails-3.2.x apps deploying with Capistrano.
As a side note for GitHubbers, the original pull request is an excellent example of how to submit code to an open source project, even if it wasn't merged.
This gist looks very promising https://gist.github.com/3072362
It checks your git log from the last deploy to now to see if there are any changes in %w(app/assets lib/assets vendor/assets Gemfile.lock config/routes.rb) and if so, only precompiles then.

Resources