How to Use "Gets" on a Rake Task

How do I use gets on a rake task?

The problem is that Kernel#gets (which is what you're calling if you just use gets by itself) assumes you're pulling from a file named by the arguments passed to Rake. That means gets tries to return the content of a file called [rake-task-here], which almost certainly doesn't exist.

Try STDIN.gets.

Asking questions in rake tasks

task :input_test do
input = ''
STDOUT.puts "What is the airspeed velocity of a swallow?"
input = STDIN.gets.chomp
raise "bah, humbug!" unless input == "an african or european swallow?"
end
task :blah_blah => :input_test do
end

i think that should work

Accepting user input from the console/command prompt inside a rake task

Rake tasks are stored in the lib/tasks folder of the Rails application. The rake task's file should end with the .rake extension; for example: populate.rake.

Accepting the input is done with STDIN.gets.chomp instead of gets.chomp.

namespace :db do
desc "Prints the migrated versions"
task :populate => :environment do
puts "\n Is this what you want to happen? [Y/N]"
answer = STDIN.gets.chomp
puts answer
if answer == "Y"
# your code here
elsif answer == "N"
return false # Abort the rake task
end
end
end

You can run this rake task with: rake db:populate

Get rake task name in initializer

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.

How would I force Kernel#gets to use STDIN#gets in a rake task?

(Answered by @the-tin-man in this comment)

There's really very little advantage to be gained in DRYness from forcing Kernel#gets to read from STDIN in any context, including Rake. Although it can be done (by modifying $stdin), it shouldn't be done because it would just be brittle and hacky.

How to pass command line arguments to a rake task

Options and dependencies need to be inside arrays:

namespace :thing do
desc "it does a thing"
task :work, [:option, :foo, :bar] do |task, args|
puts "work", args
end

task :another, [:option, :foo, :bar] do |task, args|
puts "another #{args}"
Rake::Task["thing:work"].invoke(args[:option], args[:foo], args[:bar])
# or splat the args
# Rake::Task["thing:work"].invoke(*args)
end

end

Then

rake thing:work[1,2,3]
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

rake thing:another[1,2,3]
=> another {:option=>"1", :foo=>"2", :bar=>"3"}
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

NOTE: variable task is the task object, not very helpful unless you know/care about Rake internals.

RAILS NOTE:

If running the task from Rails, it's best to preload the environment by adding => [:environment] which is a way to setup dependent tasks.

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
puts "work", args
end


Related Topics



Leave a reply



Submit