54

I want to run a Rake task that asks the user for input.

I know that I can supply input on the command line, but I want to ask the user if they are sure they want to proceed with a particular action in case they mistyped one of the values supplied to the Rake task.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
RubyRedGrapefruit
  • 12,066
  • 16
  • 92
  • 193
  • 6
    Look into [Thor](https://github.com/wycats/thor) instead for interactive tasks. It's vastly superior to Rake, and it comes with Rails, so you already have it without installing anything. – user229044 Jul 09 '12 at 03:10
  • @meagar just ran into this today and am stuck, did you ever figure that out? I'm on a Mac with zsh . . . – Brad Johnson Jun 06 '16 at 23:45
  • Just figured it out -- it apparently was related to the Rails zsh plugin. When I removed that plugin, restarted zsh, then re-added it the problem disappeared . . . – Brad Johnson Jun 06 '16 at 23:53

4 Answers4

108

Something like this might work

task :action do
  STDOUT.puts "I'm acting!"
end

task :check do
  STDOUT.puts "Are you sure? (y/n)"
  input = STDIN.gets.strip
  if input == 'y'
    Rake::Task["action"].reenable
    Rake::Task["action"].invoke
  else
    STDOUT.puts "So sorry for the confusion"
  end
end

Task reenabling and invoking from How to run Rake tasks from within Rake tasks?

Community
  • 1
  • 1
ricaurte
  • 1,436
  • 1
  • 11
  • 10
10

Here's an example without using another task.

task :solve_earth_problems => :environment do    
  STDOUT.puts "This is risky. Are you sure? (y/n)"

  begin
    input = STDIN.gets.strip.downcase
  end until %w(y n).include?(input)

  if input != 'y'
    STDOUT.puts "So sorry for the confusion"
    return
  end

  # user accepted, carry on
  Humanity.wipe_out!
end
Kamil Sarna
  • 5,993
  • 1
  • 32
  • 22
5

A handy feature for user input is to put it in a do..while loop, to only continue when a user has supplied valid input. Ruby doesn't explicitly have this construct, but you can achieve the same thing with begin and until. That would add to the accepted answer as follows:

task :action do
  STDOUT.puts "I'm acting!"
end

task :check do
  # Loop until the user supplies a valid option
  begin
    STDOUT.puts "Are you sure? (y/n)"
    input = STDIN.gets.strip.downcase
  end until %w(y n).include?(input)

  if input == 'y'
    Rake::Task["action"].reenable
    Rake::Task["action"].invoke
  else
    # We know at this point that they've explicitly said no, 
    # rather than fumble the keyboard
    STDOUT.puts "So sorry for the confusion"
  end
end
Jon Cairns
  • 11,783
  • 4
  • 39
  • 66
1

You could also wrap this up in a service class so it can be unit-tested and used across your rake tasks:

# frozen_string_literal: true

class RakeConfirmDialog
  def initialize(question)
    @question = "#{question} (y/n)"
    @answer = "".inquiry
  end

  def confirm!
    prompt until (proceed? || abort?)

    respond

    proceed?
  end

  private

  def prompt
    STDOUT.puts @question

    @answer = STDIN.gets.strip.downcase.inquiry
  end

  def respond
    STDOUT.puts proceed? ? "Proceeding." : "Aborting."
  end

  def proceed?
    @answer.y?
  end

  def abort?
    @answer.n?
  end
end

Then use it like so in your task:

next unless RakeConfirmDialog.new(
  "About to close the Hellmouth forever. Are you sure you want 'Buffy the Vampire Slayer' to have a happy ending?"
).confirm!
armchairdj
  • 1,030
  • 11
  • 13