43

I'm trying to do Ruby password input with the Highline gem and since I have the user input the password twice, I'd like to eliminate the duplication on the blocks I'm passing in. For example, a simple version of what I'm doing right now is:

new_pass = ask("Enter your new password: ") { |prompt| prompt.echo = false }
verify_pass = ask("Enter again to verify: ") { |prompt| prompt.echo = false }

And what I'd like to change it to is something like this:

foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ") foo
verify_pass = ask("Enter again to verify: ") foo

Which unfortunately doesn't work. What's the correct way to do this?

niton
  • 8,771
  • 21
  • 32
  • 52
Chris Bunch
  • 87,773
  • 37
  • 126
  • 127

5 Answers5

74

The code by David will work fine, but this is an easier and shorter solution:

foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ", &foo)
verify_pass = ask("Enter again to verify: ", &foo)

You can also use an ampersand to assign a block to a variable when defining a method:

def ask(msg, &block)
  puts block.inspect
end
BrendanDean
  • 285
  • 3
  • 13
Adam Byrtek
  • 12,011
  • 2
  • 32
  • 32
  • I had tried that initially (before asking this question), but when I do that, Highline ignores the contents of the block and dies with this error: undefined method `&' for "inputstring":String (NoMethodError) Where inputstring is what I typed in to the first prompt. – Chris Bunch Nov 11 '08 at 18:06
  • Sounds strange. Maybe you forgot about the comma and Ruby assumed you want to invoke method "&" on the prompt string? I've just tried the same code with HighLine and it works fine. – Adam Byrtek Nov 11 '08 at 21:13
  • Yea, I think I was doing ask("goo") &foo instead of ask("goo", &foo). That works now. Thanks Adam! – Chris Bunch Nov 13 '08 at 07:03
  • Perhaps, it would be interesting for you. http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html – Kirby May 16 '16 at 13:00
13

This is how you should do it, clean and simple:

def ask(question)
    yield(question)
end

proc = Proc.new { |question| puts question }
new_pass = ask("Enter your new password: ", &proc)
verify_pass = ask("Enter again to verify: ", &proc)
Honza
  • 4,349
  • 2
  • 24
  • 40
4
foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ") {|x| foo.call(x)}
verify_pass = ask("Enter again to verify: ") {|x| foo.call(x)}
nickgrim
  • 5,387
  • 1
  • 22
  • 28
David Nehme
  • 21,379
  • 8
  • 78
  • 117
2

Here is an example that will prefix the index with the yield method and append the index with the call method.

class Array
  def alter_each!
    self.each_with_index do |n, i|
      self[i] = yield(n,i)
    end
  end
  def modify_each!(add_one = true, &block)
    self.each_with_index do |n, i|
      j = (add_one) ? (i + 1) : i
      self[i] = block.call(n,j)
    end
  end
end

a = ["dog", "cat", "cow"]

a.alter_each! do |n, i|
  "#{i}_#{n}"
end

a.modify_each! false do |n,i|
  "#{n}_#{i}"
end

puts a
jspooner
  • 10,975
  • 11
  • 58
  • 81
-2

I don't think the language supports a construct like this. The only way I can see to generalize this in any way is:

def foo(prompt)
  prompt.echo = false
end
new_pass = ask("Enter your new password: ") { |prompt| foo(prompt) }
verify_pass = ask("Enter again to verify: ") { |prompt| foo(prompt) }

It doesn't really shorten the code, though it does remove some duplication--if you wanted to do more than set prompt.echo to false, you'd only have to add code in one place.

Lucas Oman
  • 15,597
  • 2
  • 44
  • 45