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
Related
I have two Rails apps that use the same database. One app is managing the database through migrations but the other is just accessing it.
For some reason when I run tests with RSpec in the app that is not managing the database it drops the database before running the tests. But because this app does not know how to recreate the database all tests will fail.
How can I tell RSpec not to drop the database, just use it as it is?
If you don't need to migrate the database you can redefine rspecs-rails
spec:prepare task like this:
lib/tasks/patch_rspec_rails.rb
Rake::Task["spec:prepare"].clear
namespace :spec do
task :prepare do
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test'
end
end
The original spec:prepare task callstest:prepare, which setups the db.
The task test:prepare exists since Rails 4.0 (or maybe earlier). This task also exists within Rails 5.0. It is a hook for railsties to add test dependent setups. You can check its definition with rake -W test:prepare. That
the task is hit you can check with rake --trace spec.
ActiveRecord uses this task to check the migration state and setup the db.
When this task is not called, no db will be dropped or created.
But be aware, when some other gem uses test:prepare as a hook too plug into tests, it will not work.
Edit:
Since Rails 4.1 you can set config.active_record.maintain_test_schema = false within config/environments/test.rb. This way Rails should no longer try to migrate your test schema.
Ideally RSpec should be reinitialisation the database for testing to ensure your environment is in a reliably, predictable state.
What you could do is for the Rails app that isn't managing the database carry out a rake db:schema:dump to generate the schema.rb which will then be used by RSpec - of course make sure that your database.yml test configuration isn't pointing to your live database.
I know that this isn't technically a solution to your question but it should prevent the underlying issue which is causing your tests to fail.
Sorry for what seems like a basic question, but I'm stumped.
I have a Rails app that relies in part on third party data that I download periodically (generally daily) and integrate into the app's database. The Ruby code that I use to get the third party data is in separate Ruby files, i.e., not integrated as code in any of my controllers. So, I run these programs when needed via rails runner program.rb (probably not relevant, but all of them use the mechanize gem in gathering the third party data).
I want to try using Heroku's scheduler to make my data gathering more automated, and the recommendation for doing this is to set up rake tasks. Is there a rake task equivalent for rails runner program.rb?
Thanks
As far as converting a rails command into a rake task it's pretty easy, you just namespace it like this:
# /lib/tasks/my_task.rake
namespace :mytask do
desc 'the description of your task'
task :my_task => :environment do
puts 'my task was executed'
end
end
Then you'd call it with 'rake mytask:my_task' later on, which you should be able to do with Heroku's scheduler. Also I'd like to recommend the 'whenever' gem for setting things up on a schedule as I have no experience with Heroku but have found that gem to be fantastic.
I'm using postgresql on a rails 3 app. I've been using sqlite3 for test environment, but decided to finally switch to the same db I use in production for testing purposes. Problem is, I'm only creating one database and diverse schemas for each environment. This is somethiing I can't change, since the environment is enterprise-constrained.
Hence, I have a test schema. I rund db:schema:load on it and it works fine. I run rake spec on it (I'm using rspec) and it breaks, exactly on the 'db:test:purge' task which comes from rails. Now, this task drops the database. Not only is the database owner different from the schema owner in my case, I'd rather have the task recreate the schema instead of recreating the database.
How can I do this?
Actually there is no real solution for this problem, since rails by default erases the database. It expects this database not to be shared with production (and they might have a point). Nevertheless, I found some interesting links and the way is to patch the rake task. Following the patch and links:
namespace :db do
namespace :test do
task :purge do
ActiveRecord::Migration.verbose = false
Rake::Task["db:schema:load"].invoke
end
end
end
https://github.com/rails/rails/issues/12117#issuecomment-25961999
Run Rails Tests without Dropping Test Database
http://www.pervasivecode.com/blog/2007/09/22/making-rails-raketest-not-drop-your-pgsql-database/
This is not a direct answer to your question, but you can use rspec command instead of rake spec. rspec command doesn't purge your database.
I know in Rails application, I can write tests for controllers and models by using Rspec.
But:
How about to test some rake task? What is the good way to test some rake task?
How about to test a cron job which run certain rake task every day at a fixed time?
Can Rspec also be used for above two scenarios in Rails app development or are there some other ways to implement those tests?
In addition:
I have a rake task which is used to update the database of the Rails app by fetching data from another database and insert to the app database (clean the app database first of course)
I would like to test these, how to do it?
Instead of having code in your rake tasks, do something like this:
desc "Charge Customers Daily"
task :charge_customers => :environment do
CustomerCharges.create
end
That way, you can write rspec tests in the customer_charges_spec.rb file as you normally would.
Maybe this helps :
http://www.philsergi.com/2009/02/testing-rake-tasks-with-rspec.html
To test a cron job, you can just redirect to a log file on cron itself, like :
command > /tmp/log.txt 2>&1
Generally, if you intend on doing db insertions and stuff in a rake task, i would reconsider and write that as a separate ruby module. I think it's much more flexible this way.
For ad hoc Rails tasks we have a few implementation alternatives, chief among which would seem to be:
script/runner some_useful_thing
and:
rake some:other_useful_thing
Which option should I prefer? If there's a clear favourite then when, if ever, should I consider using the other? If never, then why would you suppose it's still present in the framework without deprecation warnings?
The difference between them is that script/runner boots Rails whereas a Rake task doesn't unless you tell it to by making the task depend on :environment, like this:
task :some_useful_task => :environment do
# do some useful task
end
Since booting Rails is expensive, it might be worth skipping if you can avoid it.
Other than that, they are roughly equivalent. I use both, but lately I've used script/runner executing a script separately more.
Passing parameters to a rake task is a pain in the butt, to say the least. You either need to resort to environment variables or a very hackish parameter system that is not intuitive and has lots of caveats.
If your task needs to handle command line arguments gracefully then writing a script is the way to go.
Luke Francl mentions script/runner booting up Rails. That's true. But if you don't want to boot up rails then just run the script as is without script/runner. So the only real difference between scripts and rake tasks are their aesthetics. Choose whatever feels right to you.
I use rake tasks for little tasks (one or two lines). Anything more complicated goes into the script/ directory. I'll break this rule if I think other developers will expect the code to live in one place over another.
FWIW there seems to be some movement away from using script runner in favor of rake:
Update (4/25/2009): I recommend using rake tasks as opposed to script/runner for recurring tasks.
Also, as per this post you can use rake for recurring tasks just fine:
If I then wanted this to run nightly on my production database at midnight, I might write a cronjob that looks something like this:
0 0 * * * cd /var/www/apps/rails_app/ && /usr/local/bin/rake RAILS_ENV=production utils:send_expire_soon_emails
Corrected based on comment 2 down. Give them the karma!
FWIW - Rails 3.0+ changes how you initialize the Rails system in a standalone script.
require File.dirname(__FILE__) + '/config/environment'
As mentioned above you can also do:
rails runner script/<script name>
Or put all the code in a Rake task, but I have a lot of legacy code from Rails 2; so I didn't want to go down that path immediately.
Each has its advantages and disadvantages.
One thing I've done is just write normal ruby scripts and put them in the script/maintenance directory.
All you need to do to load rails and get access to all your models, etc, is put require '../../config/environment.rb' at the top of your file, then you're away.
For one off commands script/runner can be fine. For anything repeated, a rake task is easier in the long-run, and has a summary if you forget what it does.
In Rails 3.0+, the config/environment.rb requires the config/application.rb, that requires the config/boot.rb.
So, to load an app in Rails 3, you still only have to require the environment.rb
I got the impression script/runner was primarily for periodic tasks. E.g., a cron job that runs:
SomeClass.update_from_web('http://www.sourcefordata.gov/')