Rake task failing to load :environment properly - ruby-on-rails

I'm running a custom rake task...
namespace :import do
desc "Import terms of service as HTML from stdin"
task :terms => :environment do
html = STDIN.read
settings = ApplicationWideSetting.first
settings.terms_and_conditions = html
if settings.save
puts "Updated terms of service"
else
puts "There was an error updating terms of service"
end
end
end
The model ApplicationWideSetting is reported as undefined when running the task in the production environment. However, when running the task on other environments (ie. development, staging, test.) the task runs fine.
Running the process in rails console, in all environments, completes ok.
Does anyone know what's going on, things I could check?
note: I ran the task with
puts Rails.env
To check the shell environment var RAILS_ENV was getting set/read correctly. I've also tried both with and without the square brackets around the :environment dependency declaration.
additional info: Rails v3.2.14
further info: I've setup a completely fresh rails app, and the script works fine in any environment. Since the install in question is a real production environment, I'll have to setup another deploy and check it thoroughly. More info as I find it.

In a nutshell, Rails doesn't eager load models (or anything else) when running rake tasks on Production.
The simplest way to work with a model is to require it when you begin the rake task, and it should work as expected, in this case:
# explicitly require model
require 'application_wide_setting'
It's possible to eager load the entire rails app with:
Rails.application.eager_load!
However, you may have issues with some initializers (ie. devise)

Related

rake task not running sub-tasks in order specified

In a rails 4.2 app, in Rakefile, I have this:
task(:default).clear
task :default => [:test, 'bundle:audit']
The output, always has bundle:audit running first. Why is that?
I read in some places that rake executes tasks as dependencies arise, but bundle:audit, as far as I can tell, does not depend on test. It is defined here:
https://github.com/rubysec/bundler-audit/blob/master/lib/bundler/audit/task.rb
To quote a comment discussing the same problem in Rake's GitHub repository:
It turns out that your problem is due to the way rails creates its test tasks:
desc "Run tests quickly by merging all types and not resetting db"
Rails::TestTask.new(:all) do |t|
t.pattern = "test/**/*_test.rb"
end
https://github.com/rails/rails/blob/v4.2.7.1/railties/lib/rails/test_unit/testing.rake#L24-L27
Here Rails uses Rails::TestTask for the test:all target which will load all test files.
def define
task #name do
if ENV['TESTOPTS']
ARGV.replace Shellwords.split ENV['TESTOPTS']
end
libs = #libs - $LOAD_PATH
$LOAD_PATH.unshift(*libs)
file_list.each { |fl|
FileList[fl].to_a.each { |f| require File.expand_path f }
}
end
end
https://github.com/rails/rails/blob/v4.2.7.1/railties/lib/rails/test_unit/sub_test_task.rb#L106-L118
But unlike Rake::TestTask, which immediately runs the tests, Rails::TestTask only requires the files necessary to run the tests then relies on the at_exit handler in Minitest to run the tests. This means rake dependencies are completely ignored for running tests.
I updated the links to the source code, because the discussion was about Rails 4.1.8, but the problem still exists the source code of Rails 4.2.7.1.
This problem was reported as an issue to the Rails team on GitHub and it was fixed in this PR.
That said: This problem should be fixed since Rails 5.0.0.

Rails 4: stop `rake` from running all rake tasks

A developer had authority to drop a DB but not re-create it. While working on a rake tasks, he accidentally ran the entire rake suite, which included destroying the development DB but without the proper authority to re-create and populate it.
How can I ensure this doesn't happen again? Is there someway in the Rails app to override running rake so that it does NOT execute a bunch of unspecified tasks?
The developer was looking for a list of tasks and figured that running rake would provide that listing, similarly to how running rails by itself puts out instructions.
I know there's a binstub for rake, but I really do not know what happens if I mess with things in there.
Are there any good solutions to a situation like this?
Set the default task? IIRC, outside of a namespace block:
task :default => "something_that_doesnt_destroy_the_world"
Taking a note from Dave's answer and another SO question (couldn't find link again), here is how you can override the default rake tasks in Rails 4.
# lib/tasks/default.rake (name is not important)
namespace :override do
task :default do
puts "This is now the default rake task executed via 'rake'"
end
end
# Remove default task and switch to above (still in same file)
task(:default).clear.enhance ["override:default"]
At the terminal:
$ rake
/lib/tasks/default.rake: this is now the default 'rake' task
If there is a "cleaner" or more "conventional" Rails way, anyone's welcome to shout it out. This is the "cleanest" solution I could find.

How to catch errors and send them to Bugsnag when using `rails runner`?

We're running several cron tasks in our server, and we start them all using rails runner, like this:
rails runner 'MyTask.run'
where MyTask is a class in the project. The thing is, we use Bugsnag to handle errors in case anything fails. When we run a rake task, Bugsnag saves the errors and lists them in their website. But this does not happen when using rails runner. How can I configure rails to send errors to Bugsnag when this happens?
Rails runner is very difficult to configure or to customize. That is because all it really is, is a script with this main body:
if code_or_file.nil?
$stderr.puts "Run '#{$0} -h' for help."
exit 1
elsif File.exist?(code_or_file)
$0 = code_or_file
Kernel.load code_or_file
else
eval(code_or_file, binding, __FILE__, __LINE__)
end
As you can see, it just does an eval of the code you sent, so there's no wrapper, no class you can extend, and basically nothing you can configure. It is better to create a rake task to perform things the same way as runner, but this time in an environment that will be controlled by Rake, therefore allowing you to configure everything you need:
desc 'Wraps a runner command with rake'
task :runner, [:command] => :environment do |t, args|
eval(args[:command])
end
Then, you call it using
rake 'runner["MyTask.run"]'
This will run the tasks in a very similar way to using rails runner, but in the context of rake (which will include using Bugsnag).

How to drop test and development database in one rake task?

I tried to drop the test and development databases from one rake task like this:
task :regenerate do
Rails.env = "test"
Rake::Task["db:drop"].invoke
Rails.env = "development"
Rake::Task["db:drop"].invoke
end
The test database was dropped successfully. But the development database was not dropped.
Any ideas on how to make this work?
NB: This is on Rails 3.2.3
UPDATE:
Very odd, but reversing the order works:
task :regenerate do
Rails.env = "development"
Rake::Task["db:drop"].invoke
Rails.env = "test"
Rake::Task["db:drop"].invoke
end
What is going on?!
You can write it like this:
namespace :db do
desc "Database custom drop"
task :mydrop do
system("rake db:drop RAILS_ENV=test")
system("rake db:drop RAILS_ENV=development")
end
end
Reversing it does work, because there is some strange code in database_tasks.rb:
def each_current_configuration(environment)
environments = [environment]
environments << 'test' if environment == 'development'
configurations = ActiveRecord::Base.configurations.values_at(*environments)
configurations.compact.each do |configuration|
yield configuration unless configuration['database'].blank?
end
end
It always adds test if env is development. I solved the case of wanting to do a custom db:rebuild task for simultaneous development and test by running development first, and test second. In addition, before running the tasks, I call my set_env method which makes sure to set ActiveRecord::Tasks::DatabaseTasks.env, without this, the database connections don't seem to be handled discretely for environments as expected. I tried all other sorts of disconnect etc, but this worked without further code.
def set_env(env)
Rails.env = env.to_s
ENV['RAILS_ENV'] = env.to_s
ActiveRecord::Tasks::DatabaseTasks.env = env.to_s
end
Here is a gist of my full db.rake file with simultaneous multi-environment db:rebuild and db:truncate
On my system with Ruby 2 and Rails 3.2.13 I can run rake db:drop
This drops both test and development databases. Much easier now than messing with rake tasks

Set RAILS_ENV for test rake tasks

I've done this patch to my test_helper.rb
ENV["RAILS_ENV"] = ENV["RAILS_ENV_TEST"] || "test"
This works in that I can run
RAILS_ENV_TEST=test_dan ruby -Itest test/unit/post_test.rb
but I want to be able to run all kinds of test things, including rake db:test:clone but not using the environment test. How can I do this?
Most rake tasks that are namespaced with "test" will only run on your test environment and not in other environments. It's hardcoded into the task as to mitigate potentially devastating affects they might have in an environment such as production.
You can see that these tasks don't take into account the environment in which they are called in the source.
If you want to run these tasks in whatever environment you want, your best bet is to recreate these tasks and pass in the environment.
namespace :any_environment_test do
task :load => :environment do
...
task :clone => :environment do
...
In this specific case, it's a little trickier, as it sounds like you want to clone from any environment to any environment. If this is the case, you should probably have two vars that are passed, such as FROM_ENV= and TO_ENV=.
Long story longer, you're going to write custom tasks, but can inspire yourself from the link I posted above. :)

Resources