Reputation: 1375
I want to know, if the app instance is started by rake
, so I tried out catching rake
task name in initializers
. For example, if I run rake db:migrate
I want get db:migrate
or something like this.
I tried this;
[7] pry(main)> $0
=> "spring app | bilet18 | started 3 secs ago | development mode"
[8] pry(main)> ARGV
=> []
[9] pry(main)> Rake.application.top_level_tasks
=> []
but everything is empty.
What can I do that? please help.
UPDATE
if add in Rakefile line like
ENV["RAKE_CURRENT_TASKS"] = Rake.application.top_level_tasks.join(' ')
then in future you'll be able to catch it in. but this solution is not good for me, I need to catch rake task name earlier
Upvotes: 4
Views: 2056
Reputation: 15954
None of the other answers presented here will work unless you stop using Spring, because Spring changes the way rake tasks are called significantly.
When using Spring, the command being run is handed over to the Spring server process using a UNIX socket and unfortunately Spring server reads this socket to get the command and its arguments after initializing the rails environment. Thus, during rails initialization, there seems to be no way of getting the command and its arguments (e.g. the rake task name) when using Spring, as Spring itself does not know yet! Even the after_fork
hook that Spring provides won't help, because it is being also run after rails initialization.
A proof can be seen in the Spring source code. It is the serve
method in which Spring gets the ARGV
of the command being run from the socket, forks itself and runs the command. The relevant parts of the method are these:
def serve(client)
# ... getting standard input / output streams from the client socket
# this is where rails initialization occurs
preload unless preloaded?
# this is where Spring gets the command name and it's ARGV and environment
args, env = JSON.load(client.read(client.gets.to_i)).values_at("args", "env")
command = Spring.command(args.shift)
# ...
# fork and run the command
pid = fork {
# ...
# run the command
ARGV.replace(args)
$0 = command.exec_name
# ...
# run the after_fork hook
invoke_after_fork_callbacks
command.call
}
# ...
end
The rails initializers are run in the preload
method which is run before the command name is read from the socket. The $0
and ARGV
variables are also set after initialization, in the fork
block.
So, unless you monkey-patched Spring significantly (replaced the serve
method with your own, but you'd need to handle working with the socket yourself), you need to stop calling your rake tasks inside the Spring environment. If the rake
command is a binstub in the RAILS_ROOT/bin/
directory, you need to remove the binstub with spring binstup --remove rake
.
Only then, I believe, you can use one of the solutions in the other answers.
Upvotes: 3
Reputation: 32047
You could redefine tasks using Rake::Task#enhance
to create a variable before invoking the original task, but that seems a bit messy and you'll have to worry about what to do with tasks like :environment
and :default
.
I think the easiest way is probably to just extend the Rake::Task#invoke
method to add a global variable which you could then access in initializers, or from wherever else you want.
Here's a really quick an dirty example which you could do in the Rakefile
:
module RakeTaskName
def invoke(*args)
$rake_task_name = name
super *args
end
end
Rake::Task.send :prepend, RakeTaskName
Then, in an initializer, you can do:
if defined? $rake_task_name
puts "Running from rake task: #{$rake_task_name}"
end
Edit
Here's a new rails project with this code & 1 migration. The output looks like this:
Running from rake task: db:migrate
== 20160411150810 CreateFoo: migrating ========================================
-- create_table(:foos)
-> 0.0010s
== 20160411150810 CreateFoo: migrated (0.0011s) ===============================
Upvotes: 1
Reputation: 1190
I believe this answers your needs. Task name should include also the namespace.
config/initializers/rake.rb
module Rake
class Application
attr_accessor :current_task
end
class Task
alias :old_execute :execute
def execute(args=nil)
Rake.application.current_task = @name
old_execute(args)
end
end #class Task
end #module Rake
models/something.rb:
class Something < ActiveRecord::Base
puts Rake.application.current_task
end
lib/tasks/some_task.rake:
require 'rake'
namespace :ns do
task :some_task => :environment do
Something.all.to_a
end
end
console:
rake ns:some_task
ns:some_task
also works:
bundle exec rake ns:some_task
ns:some_task
Upvotes: 0
Reputation: 1276
There is two ways to achieve what you want,
1 .Not using spring
, just run:
spring binstub --remove --all
and then run your rake task.
2 .Using spring:
create a spring.rb file config/spring.rb
and use after_fork
Spring.after_fork do
# run your code here
# you have access to ARGV
binding.pry
end
Upvotes: 0
Reputation: 5414
Did you include the => :environment
in your task? E.g.
task :sometask => :environment do
...
end
Otherwise the initializers wont run when you run a rake task
Upvotes: 0