Rails 4: how to compile only changed assets? - ruby-on-rails

For Rails 3, this question and turbo-sprockets-rails3 look great.
For Rails 4, there seems to be some controversy over whether this has been fixed or not.
I'm currently using Rails 4 in production, and it seems that because Capistrano deploy:assets:update_asset_mtimes touches all assets, deploy:assets:precompile likewise re-compiles all of them. That recompilation is the single longest step in my cap deploy.
Ideally, this should be replaced by some checksum based manifest system, so that only those assets that have actually changed (or are dependent on ones that have changed) are re-compiled.
What is the best way to do this? (Assuming we're still doing it on the server, not the dev machine.)

Here is a great blog post from the guys at codeclimate about this. I've taken a look at it but not gone through the steps in production.
http://blog.codeclimate.com/blog/2013/10/02/high-speed-rails-deploys-with-git/

This guy got it right for Capistrano 3. Works well for me.
https://coderwall.com/p/aridag

This issue was discussed in https://github.com/capistrano/capistrano/issues/478
and the solution which seems to be okay is simply to deactivate the touching. In above issue there was talk about a configuration variable to use for this but i could not find any code references to it. Instead we now simply overwrite the task.
namespace :deploy do
namespace :assets do
task :update_asset_mtimes, :roles => lambda { assets_role }, :except => { :no_release => true } do
end
end
end
Note: This is only true for capistrano version 2. I have no idea if version 3 still uses the same tasks or not.

Related

How do I get the Rails (javan) whenever gem to only write the crontab on production (and NOT staging) during a Capistrano deploy?

The whenever gem has worked great for me, for years now, in production. My project is now big enough to need a staging environment, and Capistrano has made this effortless. However, the one thing that I cannot sort out is preventing the whenever gem from writing cron entries on the staging server.
I've been searching for an answer for literally weeks, off and on, among doing other things, and I've tried about 6 different hacks from blogs and other SO posts. I've created an issue on GitHub about the one thing that the documentation suggests: "namespacing" the schedule.rb file. Nothing about my interpretation of that statement works. And all the references here on SO seem to be about the gem NOT writing crontab entries. My problem is that it writes too many!
Rather than going down the rabbit hole about that particular issue, I thought maybe I would try asking the question directly. Surely, people are doing this. It seems like an obvious and basic thing to want from such a process, e.g., to tie jobs to various environments. But, like I said, everything I've tried has failed, and I still wind up with only-production-appropriate crontab entries in staging.
What's the cleanest way of implementing environment-specific jobs, that works?
We pass an environment variable to segment which jobs run on which server environment.
env = #environment
if env == 'production'
# a bunch of scheduled tasks
end

Rake :environment does not work in production

I have a bunch of rake tasks that modify models in my rails project. They all work just fine in development, but in production they fail to load up associated model and service classes.
The problem seems to come from the :environment declaration. My tasks take the form
task :my_task => :environment do
#modify models
end
The documentation says that :environment loads the rails environment so that you can interact with any file in the rails system, but apparently this is not the case in production?
Is there a way to load needed files in production? Or should I not be using the :environment task at all? Seems really weird to have the code behave one way in development and another in production (testing this is gonna be a pain).
Seems to be an issue with the way rake tasks don't eager-load. This answer may be the droids you're looking for: Rails 3 rake task can't find model in production

Running code after Rails is done loading?

I have a periodic task that needs to execute once a minute (using delayed_job). I would like for Rails to automatically queue it up as soon as it's finished loading, if one such task isn't already present in the system.
What is a good place for me to run some code right at the end of the entire Rails boot flow? Someone suggested config/environments/development.rb (or other environment), but delayed_job give me ActiveRecord issues when I queue up jobs from there.
I consulted http://guides.rubyonrails.org/initialization.html, and there doesn't seem to be a clear location for that kind of code either.
Is this kind of post-deployment setup to be done perhaps externally to my app's code, maybe through rake or some other means? Any suggestions?
Thank you!
Regarding http://guides.rubyonrails.org/initialization.html, sorry, we're working hard to rewrite it. For your problem, I would try with config.after_initialize in your application.rb
def after_initialize(&block)
ActiveSupport.on_load(:after_initialize, :yield => true, &block)
end
Add your code in the initializer directory.
http://guides.rubyonrails.org/configuring.html#using-initializer-files
You have two options really
1) add it into initializers directory and this should be ok.
2) add it at the very end of application.rb, this is less clean but it will have things like initializers ready at this point ;p so if 1) fails because of AR problems do 2)

How to universally skip database touches when precompiling assets on Heroku

I'm deploying a Rails 3.1 app to Heroku's Cedar stack. With Heroku Cedar and Rails 3.1, you can compile the assets yourself locally, let Heroku compile them when you push (during "slug compilation"), or have them be compiled just-in-time while the app is running. I want to do the middle option, letting Heroku precompile the assets.
When Heroku runs the assets:precompile task, it errors with "could not connect to server" because the app is trying to connect to the database but no database is available at that stage of slug compilation. The lack of database connection is expected and unavoidable at this point. I'm looking for a way to move past it, because a database connection isn't crucial to asset precompilation.
The part of my app that's trying to connect to the database is Devise. There's a devise_for :users line in routes.rb that wants to look at the User model.
I could just write a rake task that stubs out devise_for and make it a prereq of assets:precompile. I think that would solve my problem, but I'm looking for a more universal solution that I could use on any Rails 3.1 app with this problem on Heroku.
Is there anything out there, or can you conceive of anything that silences database connection errors while still running the app enough to have route and asset path generation?
Obviously if an app needs to read/write data during startup, we can't stub that, but can we fake every ActiveRecord model automatically?
add this to config/application.rb
config.assets.initialize_on_precompile=false
took me a while to hunt this down... adding it to config/environments/*.rb did NOT work
UPDATE: It doesn't work with rails 4
Heroku now makes a labs flag available that'll make the runtime environment available during compilation time, which means your app will be able to successfully connect to your DATABASE_URL database.
First you need to install the labs plugin:
$ heroku plugins:install http://github.com/heroku/heroku-labs.git
then enable the user-env-compile labs feature:
$ heroku labs:enable user-env-compile --app your-app-name
For me the problem is activerecord calling instantiate_observer in lib/active_record/railtie.rb:92. This will load the observers and the respective models. has_and_belongs_to_many then connects to the db.
I think I'll override this method when ENV["RAILS_ASSETS_PRECOMPILE"] is present, which is used by devise in the fix Bradley linked to.
EDIT: So this snippet fixed it for me:
namespace :assets do
# Prepend the assets:precompile_prepare task to assets:precompile.
task :precompile => :precompile_prepare
# This task will be called before assets:precompile to optimize the
# compilation, i.e. to prevent any DB calls.
task 'precompile_prepare' do
# Without this assets:precompile will call itself again with this var set.
# This basically speeds things up.
ENV['RAILS_GROUPS'] = 'assets'
# Devise uses this flag to prevent connecting to the db.
ENV['RAILS_ASSETS_PRECOMPILE'] = 'true'
# Prevent loading observers which will load the models which in turn may hit
# the DB.
module ActiveModel::Observing::ClassMethods
def instantiate_observers; end
end
# Prevent route drawing because certain gems might get called which will hit
# the DB.
class ActionDispatch::Routing::RouteSet
def draw; end
end
end
end
Workaround for Rails (4.2 edge):
Add the following as /config/initializers/precompile.rb:
module Precompile
# Public: ignore the following block during rake assets:precompile
def self.ignore
unless ARGV.any? { |e| e == 'assets:precompile' }
yield
else
line = caller.first
puts "Ignoring line '#{line}' during precompile"
end
end
end
and use it in your routes.rb like this:
Precompile.ignore { ActiveAdmin.routes(self) }
EDIT: This answer is out of date and no longer works - See fringd's answer.
Not quite a universal stubbing but devise has added a check now to fix this particular problem . See the issue and fix on Github. By providing a RAILS_ASSETS_PRECOMPILE environment config devise should skip building the routes
I stuck this in 'lib/tasks/assets.rake' and was able to get assets:precompile to actually succeed. This should work as long as you don't actually access the database as a result of requiring your environment. It obviously won't help with ActiveRecord, but it should work for all mongoid-based apps.
task 'assets:precompile' => 'assets:stub_mongoid'
task 'assets:stub_mongoid' do
def Mongoid.load!(*args)
true
end
end
Heroku added an unofficial flag to make the environment (i.e. also the DB) accessible during precompilation. Just ask them to switch it on and DB dependencies during asset precompilations are no longer an issue. Not sure, if/when this flag is officially available though, or if it will simply be the new default.
Spork.trap_method is also an interesting solution to the problem of Devise's routes_for calling the model early in the load process. Solution can't be applied directly AFAIK, but it's solving the same sort of problem, so it might provide inspiration for somebody.
Spork.trap_method
I lack sufficient reputation to comment, so here's another answer.
It's true that #fringd's top-rated answer does not work on Rails 4. I have, however, found this technique to work:
https://iprog.com/posting/2013/07/errors-when-precompiling-assets-in-rails-4-0
Although, I rearranged the BASH variables like so:
~$ RAILS_ENV=production DATABASE_URL=postgresql://user:pass#127.0.0.1/dbname bundle exec rake assets:precompile
BTW, This is a fantastic aid if you need to build a Docker image. Put that line into your Dockerfile so your DB can live in a different container and your app containers don't need to precompile assets every time they start up!
Disable AR:
config = Rails.application.config
def config.database_configuration
{}
end
ar = ActiveRecord::Base
def ar.establish_connection
end

Rails routes change in production

The situation is simple. In Rails 2.3.3, I've got a "Staff" namespace, and controllers in there inherit from the StaffController. That StaffController itself handles the Staff namespace's root:
map.namespace :staff do |staff|
staff.root :controller=>'staff',
:action=>'index'
# ...
end
In development mode, that works fine. In production mode, however, this breaks:
uninitialized constant Staff::StaffController
among other issues, such as certain helpers rendering incorrectly in the Staff namespace.
Why do development and production mode act behave differently in this context, and what can I do to fix it?
What happens if you run rake routes in both production and development modes?
That might help you narrow it down to (as bensie mentioned) a hosting stack vs. framework/code issue.
What does your production environment look like? Passenger/Apache? Latest version (2.2.4)? Inconsistencies like that have typically been a stack problem for me as opposed to a code problem, so couldn't hurt to start there.
It seems you've solved this already, but two things to watch out for:
Some subtle changes can happen when using Apache vs Webrick/Mongrel (best practice would be to actually setup Apache+Passenger locally for development
In certain deployment situations you can shoot yourself in the foot when you implement a conditional route and make a db migration happen at the same time (best practice would be to wrap the conditional route in a check for the DB migration. This may mean that you need to do a second restart of your server after load and migration, but still better than the alternative.

Resources