When I run a rake task for an application that uses Models defined in a plugin I get an Uninitialized Constant error, but when I run the model process, with script/runner, that is fired in the rake task then the job runs fine?
Is there some difference between script/runner that loads all my plugins that doesn't happen when I fire up a rake task even though it is being passed an environment?
Your rake task needs to be dependent upon :environment. That will spin up your app's environment and give you access to your models, etc.
Eg
desc "Make DB Views"
task :views => [:environment] do |t|
# your task's code
end
You need to specify that your Rake task requires the environment to be loaded:
task :your_task => :environment do |t| ...
or
task :your_task => [:environment] do |t| ...
or
task :your_task, :param1, :param2, :needs => :environment do |t, args| ...
or
task :your_task, :param1, :param2, :needs => [:environment] do |t, args| ...
If you did specify this, then there is another problem. I think one common source of errors is due to the fact that the plugins are loaded inside a namespace called Rails::Plugin. So if you defined a class called Foo in your plugin, then the Rake task needs to reference it as Rails::Plugin::Foo instead of simply Foo.
If this does not solve your problem then try to add puts "Check" on the first line of the plugin's init.rb file, and make sure that Check is displayed when you run your rake task. If it is, then your plugin is being loaded, but perhaps it fails silently after that.
One last thing: maybe you are trying to use the plugin outside the task, for example at the beginning of your Rake file, in some initialization code? If so, then it will fail because the plugins only get loaded when the task is executed (when the environment is loaded).
Hope this helps.
Related
Somebody has asked a similar question here:
https://github.com/jimweirich/rake/issues/257
The answer from the maintainer was:
I am going to reject this since it allows you to use tasks in non-rake-like ways.
So what are the correct way of using rake if a task depends of other tasks.
task 'succeed' => ['db:drop','stats'] do something end
displays results of stats even if Postgres threw an error and db:drop failded because of active connections.
If rake is not suitable for system maintenace, what tools should I use?
I need to be able to run a backup of a database, then do some tests, then drop the database and finally restore from backup.
to hel you understand my problem look at folowing fragment
namespace :experiment do
desc "TODO"
task 'succeed' => ['stopme', 'stats'] do
puts 'this and stats task should not run'
end
desc "TODO"
task stopme: :environment do
Rake::Task['db:drop'].invoke
end
end
You can invoke tasks manually like that:
task :stats => :environment do
Rake::Task['db:drop'].invoke rescue nil
# do something
end
If I have one rake which invokes multiple other rakes.
Once I initiate the parent rake
rake myapp:main
Then invokes done within the rake would load environment for each task or its just one time activity done while running rake myapp:main ?
namespace :myapp do
desc "Main Run"
task :main => :environment do
Rake::Task['myapp:task1'].invoke
Rake::Task['myapp:task2'].invoke
end
task :task1 => :environment do
# Does the first task
end
task :task2 => :environment do
# Does the second task
end
end
Adding details to #Shadwell's answer..
The => :environment is specifying that the :environment task (defined by rails) is a dependency of your tasks and must be invoked before your tasks are.
You can see :environment task's definition here
https://github.com/rails/rails/blob/d70ba48c4dd6b57d8f38612ea95a3842337c1419/railties/lib/rails/application.rb#L428-432
Rake keeps track of which tasks have invoked though and when it reaches a dependency that has already been invoked it knows it can skip it.
https://github.com/jimweirich/rake/blob/5e59bccecaf480d1de565ab34fd15e54ff667660/lib/rake/task.rb#L195-204
# Invoke all the prerequisites of a task.
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
if application.options.always_multitask
invoke_prerequisites_concurrently(task_args, invocation_chain)
else
prerequisite_tasks.each { |p|
prereq_args = task_args.new_scope(p.arg_names)
p.invoke_with_call_chain(prereq_args, invocation_chain)
}
end
end
Rake maintains an intance variable #already_invoked to know if a task has already been called. The same can be seen in the below method
https://github.com/jimweirich/rake/blob/5e59bccecaf480d1de565ab34fd15e54ff667660/lib/rake/task.rb#L170-184
def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
new_chain = InvocationChain.append(self, invocation_chain)
#lock.synchronize do
if application.options.trace
application.trace "** Invoke #{name} #{format_trace_flags}"
end
return if #already_invoked
#already_invoked = true
invoke_prerequisites(task_args, new_chain)
execute(task_args) if needed?
end
rescue Exception => ex
add_chain_to(ex, new_chain)
raise ex
end
The environment would only be set up once.
The => :environment is specifying that the :environment task (defined by rails) is a dependency of your tasks and must be invoked before your tasks are. Rake keeps track of which tasks have invoked though and when it reaches a dependency that has already been invoked it knows it can skip it.
(Aside: this can cause problems if you actually want the dependency to be invoked multiple times)
You could also define your main task using dependencies:
task :main => [:task1, :task2] do
# Blank
end
When you run rake myapp:main it will look at the dependencies and invoke task1 and then task2. Because task1 has a dependency environment it will invoke that first too. It'll skip the environment dependency on task2 though.
Answer is NO, Environment is not loaded when executing another Rake task from the parent Task. Simple explanation for this is the code below :
namespace :myapp do
desc "Main Run"
task :main => :environment do
puts "Start Time : #{Time.now.to_i}"
Rake::Task['myapp:task1'].invoke
puts "End Time1 : #{Time.now.to_i}"
Rake::Task['myapp:task2'].invoke
puts "End Time2 : #{Time.now.to_i}"
end
task :task1 => :environment do
# Does the first task
puts "Executing..1"
end
task :task2 => :environment do
# Does the second task
puts "Executing..2"
end
end
But it is not a good practice to do the two or multiple rake tasks. If you want to achieve the same thing, you can modularize the code, and create two functions and call the function to achieve the same result.
I have a Rails 2.2 project in which I want to override the functionality of the rake db:test:prepare task. I thought this would work, but it doesn't:
#lib/tasks/db.rake
namespace :db do
namespace :test do
desc "Overridden version of rails' standard db:test:prepare task since the schema dump used in that can't handle DB enums"
task :prepare => [:environment] do
puts "doing db:structure:dump"
Rake::Task['db:structure:dump'].invoke
puts "doing db:test:clone_structure"
Rake::Task['db:test:clone_structure'].invoke
end
end
end
I get the standard task's behaviour. If I change the name of the task to :prepare2 and then do rake db:test:prepare2, then it works fine. The natural conclusion I draw from this is that my rake tasks are being defined before the built-in Rails ones, so mine is overridden by the standard :prepare task.
Can anyone see how I can fix this? I'd rather override it than have to use a new task. Thanks, max
If you define a rake task that already exists, its execution gets appended to the original task's execution; both tasks will be executed.
If you want to redefine a task you need to clear the original task first:
Rake::Task["db:test:prepare"].clear
It's also useful to note that once a task has been executed in rake, it won't execute again even if you call it again. This is by design but you can call .reset on a task to allow it to be run again.
You have to remove the default task before adding your own:
Rake.application.instance_variable_get('#tasks').delete('db:test:prepare')
namespace 'db' do
namespace 'test' do
task 'prepare' do
# ...
end
end
end
A fairly popular idiom is to create a convenience method called remove_task like so:
Rake::TaskManager.class_eval do
def remove_task(task_name)
#tasks.delete(task_name.to_s)
end
end
def remove_task(task_name)
Rake.application.remove_task(task_name)
end
(Source: drnic/newgem)
Create a new project.rake file at lib/tasks/, and paster below code into it.
namespace :mv do
desc "Display hint and info for your rails 4 project"
task info: :environment do
puts 'Run rake test to test'
end
end
task(:default).clear.enhance ['mv:info']
inspired by Krasimir Angelov's blog
In Rake task definition, like following:
desc 'SOME description'
task :some_task => :environment do
# DO SOMETHING
end
What does the :some_task in task :some_task => :environment means?
Is it the method name which will be invoked in the DO SOMETHING part?
Can :some_task be any arbitrary string which describe the task?
In fact, when you're creating a rake task, :some_task is the name of the task you are calling.
For instance, in this case, you will call rake some_task
You also could define namespaces for your tasks :
namespace :my_tasks do
desc "My first task"
task :first_task => :environment do
# DO SOMETHING
end
end
And then you will call rake my_tasks:first_task in your console.
Hope it will help you,
Edit:
As explained by Holger Just, the :environment executes the "environment" task and if you are on rails, loads the environment. This could take a long time but il also helps you if your tasks works with the database.
With your example, you define a task called some_task which can be invoked by calling rake some_task on the command line.
It will depend on the environment task which will be rune before your new some_task. In rails, the environment task sets up the rails environment (loading libraries, preparing database connection, ...) which is quite expensive and thus optional.
I need to create a script that imports data from a file system source. How do I do that?
I already tried to create a rake task but there the models are not loaded. How do I get the whole rails environment into my task?
desc 'Do stuff with models'
task :do_stuff => :environment do
1000.times.each do |i|
Model.create :name => "model number #{i}"
end
end
You declare :environment as a dependency of your rake task. This loads up rails and all of your app code before it runs.