Rails 4: stop `rake` from running all rake tasks - ruby-on-rails

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.

Related

Rake task during application initialization rails

I want to execute a rake task when the server of my application starts.
In config/application.rb i put the following:
if !Rails.env.production?
Rake::Task[ "init:db_records" ].invoke
end
The rake task is well defined, and runs without a problem if i invode it from terminal
rake init:db_records
But when placed in config/application.rb (or even in any initializers/*) i got the following error.
Don't know how to build task 'init:db_records'
What is the way to execute a rake task when the server starts ?
Thanks!
Rails already has a mechanism for setting up a development database -- rake db:seed. It does not run automatically when you start the app, but it does run as part of rake db:setup.
Unless you have a good reason, it's usually best to stick the conventions that Rails provides.
For those who encounter the same problem in the future.
I achieved this by creating a new file in the initializers directory, where i put the code of the rake task.
The advantage of this at this point, is that the application is already loaded, so you have access to ActiveRecord functions...
Putting the code directly in config/application.rb didn't work, since my models were not loaded yet.
Hope it will help!
Your Rake tasks are (likely) defined in a Rakefile. The initializer has no idea that file even exists, so it doesn't know about the tasks within.
The easiest way to circumvent this is by doing something like this:
Dir.chdir(Rails.root) do
`rake init:db_records`
end
That is, change the working directory to the root rails directory, then running the command.

Rails can't execute rake task

Rails won't fire off a rake command from my controller because it can't find rake. I know this because I experienced this in my dev environment and fixed it by giving it the absolute path to rake. However this solution isn't working my production environment.
Things I know:
I can run the rake task rom cli
I can run it through irb with a: system "rake ..."
I can't find any errors!
Rakes:
/usr/lib/ruby/1.9.1/rake
/usr/lib/ruby/gems/1.9.1/bin/rake
/usr/lib/ruby/gems/1.9.1/gems/rake-0.9.2/lib/rake
/usr/lib/ruby/gems/1.9.1/gems/rake-0.9.2/bin/rake
/usr/lib/ruby/gems/1.9.1/gems/rake-0.9.2.2/lib/rake
/usr/lib/ruby/gems/1.9.1/gems/rake-0.9.2.2/bin/rake
/usr/lib/ruby/gems/1.9.1/gems/rake-0.8.7/lib/rake
/usr/lib/ruby/gems/1.9.1/gems/rake-0.8.7/bin/rake
/usr/bin/rake
/var/lib/gems/1.8/bin/rake
/var/lib/gems/1.8/doc/rake-0.9.2.2/rdoc/lib/rake
/var/lib/gems/1.8/gems/rake-0.9.2.2/lib/rake
/var/lib/gems/1.8/gems/rake-0.9.2.2/bin/rake
/var/lib/gems/1.8/gems/rake-0.8.7/lib/rake
/var/lib/gems/1.8/gems/rake-0.8.7/bin/rake
/var/lib/gems/1.8/gems/sprockets-2.3.1/lib/rake
How in the heck do I go about troubleshooting this???
The rake task I was calling in production was failing because of my attempt to dump it's contents into a log with logger disabled.
system "rake invite INVITE_ID=5 RAILS_ENV=production >> /root/log/rake.log &"
Thanks for all your help...sorry for being so vague. I was just looking for quick responses hoping I was doing something stupid. Which I was. I SWEAR I tried it without the log entry...probably moving too fast like I always do.
Also, just to answer a couple questions. The rake task I'm using is an internal tool I use myself for some maintenance and want to simply offload that task to my coworker who uses an app I've built. There's no need to industrialize it with a scheduler, etc...

How do I prepare test database(s) for Rails rspec tests without running rake spec?

After significant troubleshooting, I figured out that I needed to run rake spec once (I can abort with control-c) before I can run rspec directly (e.g. on a subset of our specs). We are running Rails 3.0.7 and RSpec 2.5.0.
Clearly, rake is running some important database setup tasks / code (we have custom code in the root level rails Rakefile and possibly other places).
How can I run the rake test database setup tasks / code without running rake spec?
In addition to being able to run rspec on a subset of files, I am using specjour to spread our specs across multiple cores (haven't had success with spreading them across the LAN yet), but I see the same behavior as for running rspec directly: I need to run rake spec on each test database (assuming two cores) before specjour works:
rake spec TEST_ENV_NUMBER=1
control-c (after tests start)
rake spec TEST_ENV_NUMBER=2
control-c (after tests start)
specjour
Note: my config/database.yml has this entry for test (as is common for the parallel testing gems):
test:
adapter: postgresql
encoding: unicode
database: test<%=ENV['TEST_ENV_NUMBER']%>
username: user
password:
parallel_tests seems to set up its databases correctly, but many of our specs fail.
I should also mention that running specjour prepare causes Postgres to log errors that it can't find the databases, but it creates them (without tables). On a subsequent run, no errors are logged, but also no tables are created. It is possible that my whole issue is simply a bug in prepare, so I reported it on github.
I think that I can run arbitrary code on each specjour test database by setting Specjour::Configuration.prepare in .specjour/hooks.rb, so if there's any rake tasks or other code that I need to run, it may work there.
I would recommend dropping your test database, then re-create it and migrate:
bundle exec rake db:drop RAILS_ENV=test
bundle exec rake db:create RAILS_ENV=test
bundle exec rake db:schema:load RAILS_ENV=test
After these steps you can run your specs:
bundle exec rspec spec
gerry3 noted that:
A simpler solution is to just run rake db:test:prepare
However, if you're using PostgreSQL this wont work because the rails environment gets loaded, which opens a database connection. This causes the prepare call to fail, because the DB cannot be dropped. Tricky thing.
I had a similar problem setting up the CI system at work, so I gradually worked up a system to handle this. It may not be the best solution, but it works for me in my situation and I'm always on the lookout for better ways to do things.
I have a test database that I needed setup, but also needed seeded data loaded for our tests to work.
The basics of troubleshooting rake tasks is to run rake with the --trace option to see what is happening under the hood. When i did this, I found that running rake spec did a number of things that I could replicate (or modify as I saw fit) in a custom rake task.
Here's an example of what we do.
desc "Setup test database - drops, loads schema, migrates and seeds the test db"
task :test_db_setup => [:pre_reqs] do
Rails.env = ENV['RAILS_ENV'] = 'test'
Rake::Task['db:drop'].invoke
Rake::Task['db:create'].invoke
result = capture_stdout { Rake::Task['db:schema:load'].invoke }
File.open(File.join(ENV['CC_BUILD_ARTIFACTS'] || 'log', 'schema-load.log'), 'w') { |f| f.write(result) }
Rake::Task['db:seed:load'].invoke
ActiveRecord::Base.establish_connection
Rake::Task['db:migrate'].invoke
end
This is only an example, and specific to our situation, so you'll need to figure out what needs to be done to get your test db setup, but it is quite easy to determine using the --trace option of rake.
Additionally, if you find the test setup is taking too long (as it does in our case), you can also dump the database into .sql format and have the test database pipe it directly into mysql to load. We save several minutes off the test db setup that way. I don't show that here because it complicates things substantially -- it needs to be generated properly without getting stale, etc.
HTH
The provided solutions all require to load the Rails environment, which is, in most cases, not the desired behaviour due to very large overhead and very low speed. DatabaseCleaner gem is also rather slow, and it adds another dependency to your app.
After months of chagrin and vexation thanks to reasons vide supra, I have finally found the following solution to be exactly what I need. It's nice, simple and fast. In spec_helper.rb:
config.after :all do
ActiveRecord::Base.subclasses.each(&:delete_all)
end
The best part about this is: It will only clear those tables that you have effectively touched (untouched Models will not be loaded and thus not appear in subclasses, also the reason why this doesn't work before tests). Also, it executes after the tests, so the (hopefully) green dots will appear right away.
The only downside to this is that if you have a dirty database before running tests, it will not be cleaned. But I doubt that is a major issue, since the test database is usually not touched from outside tests.
Edit
Seeing as this answer has gained some popularity, I wanted to edit it for completeness: if you want to clear all tables, even the ones not touched, you should be able to do something like the "hacks" below.
Hack 1 - pre-loading all models for subclasses method
Evaluate this before calling subclasses:
Dir[Rails.root.join("app", "models", "**", "*.rb")].each(&method(:require))
Note that this method may take some time!
Hack 2 - manually truncating the tables
ActiveRecord::Base.connection.tables.keep_if{ |x| x != 'schema_migrations' }
will get you all table names, with those you can do something like:
case ActiveRecord::Base.configurations[Rails.env]["adapter"]
when /^mysql/, /^postgresql/
ActiveRecord::Base.connection.execute("TRUNCATE #{table_name}")
when /^sqlite/
ActiveRecord::Base.connection.execute("DELETE FROM #{table_name}")
ActiveRecord::Base.connection.execute("DELETE FROM sqlite_sequence where name='#{table_name}'")
end
It appears that in Rails 4.1+, the best solution is simply to add ActiveRecord::Migration.maintain_test_schema! in your rails_helper after require 'rspec/rails'.
i.e. you don't have to worry about having to prepare the database anymore.
https://relishapp.com/rspec/rspec-rails/docs/upgrade#pending-migration-checks
In a spring-ified Rails 4 app, my bin/setup is usually augmented to contain
puts "\n== Preparing test database =="
system "RAILS_ENV=test bin/rake db:setup"
This is very similar to leviathan's answer, plus seeding the test DB, as
rake db:setup # Create the database, load the schema, and initialize with the seed data
(use db:reset to also drop the database first)
As the comment mentions, if we want to drop the DB first, rake db:reset does just that.
I also find that this provides more feedback when compared to rake db:test:prepare.
I started by dropping my test database
rake db:drop RAILS_ENV=test
when trying to create a new test database I ran into an issue because my user account was not the same as the account that owns the databases so I created the database in PostgreSQL instead.
type psql in the command prompt and then run the below to create a test database that uses an account other than your own.
CREATE DATABASE your_database_name OWNER your_db_owner;
then run your migrations in the test environment.
rake db:migrate RAILS_ENV=test

How do I find the source file for a rake task?

I know you can view all possible rake tasks by typing
rake -T
But I need to know what exactly a task does. From the output, how can I find a source file that actually has the task? For example, I'm trying to find the source for the db:schema:dump task.
I know this is an old question, but in any case:
rake -W
This was introduced in rake 0.9.0.
http://rake.rubyforge.org/doc/release_notes/rake-0_9_0_rdoc.html
Support for the –where (-W) flag for showing where a task is defined.
Despite what others have said, you can programmatically get the source location of rake tasks in a rails application. To do this, just run something like the following in your code or from a console:
# load all the tasks associated with the rails app
Rails.application.load_tasks
# get the source locations of actions called by a task
task_name = 'db:schema:load' # fully scoped task name
Rake.application[task_name].actions.map(&:source_location)
This will return the source locations of any code that gets executed for this task. You can also use #prerequisites instead of #source_location to get a list of prerequisite task names (e.g. 'environment', etc).
You can also list all tasks loaded using:
Rake.application.tasks
UPDATE: See Magne's good answer below. For versions of rake >= 0.9.0 you can use rake -W to show the source location of your rake tasks.
There is no programmatic way to do this unfortunately. Rake tasks can be loaded either from rails itself, lib/tasks, or from any plugin with a tasks directory.
This should nab most everything not within Rails itself:
find . -name "*.rake" | xargs grep "whatever"
As for db:schema:dump, here's the source:
desc "Create a db/schema.rb file that can be portably used against any DB supported by AR"
task :dump => :environment do
require 'active_record/schema_dumper'
File.open(ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb", "w") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
end
It can be found on line 242 of lib/tasks/database.rake in the rails 2.2.2 gem. If you've got a different version of Rails, just search for "namespace :schema".
You probably actually want the source of the ActiveRecord::SchemaDumper, but I think you should have no trouble figuring out where that is. :-)
For most rake tasks in Rails, look in the Rails gem directory, in lib/tasks.
If you've vendored Rails into your app directory structure then look in vendor/rails/railties/lib/tasks instead
Either way, db:schema:dump is in databases.rake.

Running Rake Without Shell Access?

For a RoR installation, is there any way to run rake commands without root access?
To put it another way, is there any way to get db:create and db:migrate to be run without root access (perhaps automatically or something)? Or can I run rake commands from a RoR controller?
Take a look at rails-2.X.X/lib/tasks/databases.rake and you can see the code called to create, drop, and migrate your database.
Once a rails environment is initialized, you can use the code inside the rake task file to create, drop, and migrate.
I do not know if you can do this at the controller level before it errors, but you can always try. You could also do it after rails has finished initializing in the environment file.
config/environment.rb
...
ActiveRecord::Migration.verbose = false
ActiveRecord::Migrator.migrate(File.join(Rails.root, 'db', 'migrate'))
Well, it is a bit of a chicken-egg problem, you may be able to start your RoR instance without the database created but I doubt it. If your hosting provider is able to host RoR apps, there must be a way for them to run rake for you or to let you run it somehow.
Since it sounds like you are running into troubles with creating the database, is there a way to do it from the hosting control panel? Still, how are you going to migrate your database? Sounds like you might need to look at a new host. I use Slicehost and think they are great :)
Give this code a try:
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'tasks/rails'
Rake::Task["db:version"].invoke
I just tried it in ./script/console and that worked. It wouldn't work without the require lines.
I use it to call other rake tasks from a rake task (when it's not a pre-req but something that has to happen in the middle).
Note, that won't get you any of the output from the command. If you want that you could just go with good old backticks and run the command like this:
output = `rake db:version`
That'll launch another process, but I don't think there's a problem with that.
Just to be clear, you do not need root access, you need just shell (ssh) access to that machine.
How are you deploying it without access ? If you're using capistrano than you already have shell access and it can run those tasks for you.

Resources