class Annoy
Annoy ¶ ↑
Like your annoying friend that asks you questions all the time.
Rudy uses Annoy to present the user with a simple question before continuing with a destructive action.
Attributes
Public Class Methods
Runs a challenge with the message, “Are you sure?” See: ::challenge?
# File lib/annoy.rb, line 154 def Annoy.are_you_sure?(factor=:medium, flavor=:rand, writer=STDOUT) Annoy.challenge?("Are you sure?", factor, flavor, writer) end
Prints a question to writer
and waits for a response on STDIN.
It checks whether STDIN is connected a tty so it doesn't block on gets
when there's no human around to annoy. It will return
TRUE when STDIN is NOT connected to a tty (when STDIN.tty?
returns false).
-
msg
The message to print. Default: “Please confirm.”
Returns true when the answer is correct, otherwise false.
# File lib/annoy.rb, line 113 def Annoy.challenge?(msg="Please confirm.", factor=:medium, flavor=:rand, writer=STDOUT, period=nil) return true unless STDIN.tty? # Humans only! return true if Annoy.skip? begin success = Timeout::timeout(period || @@period) do flavor = Annoy.get_flavor(flavor) question, answer = Annoy.question(factor, flavor) msg = "#{msg} To continue, #{Annoy.verb(flavor)} #{question}: " #writer.print msg #if ![:medium, :high, :insane].member?(factor) && flavor == :numeric #writer.print "(#{answer}) " #writer.flush #end #response = Annoy.get_response(writer) highline = HighLine.new response = highline.ask(msg) { |q| q.echo = '*' # Don't display response q.overwrite = true # Erase the question afterwards q.whitespace = :strip # Remove whitespace from the response q.answer_type = Integer if flavor == :numeric } ret = (response == answer) writer.puts "Incorrect" unless ret ret end rescue Interrupt writer.puts $/, "Giving up!" false rescue Annoy::GiveUp => ex writer.puts $/, "Giving up!" false rescue Timeout::Error => ex writer.puts $/, "Times up!" false end end
Tells annoy to prompt for a response.
# File lib/annoy.rb, line 54 def Annoy.disable_skip; @@skip = false; end
Calling this method tells Annoy to not prompt for a response. All questions will return true.
# File lib/annoy.rb, line 52 def Annoy.enable_skip; @@skip = true; end
Get a response from the user. Returns the string as typed by the user with extraneous whitespace removed.
-
msg
(required) text to display to user -
echo
(optional) character to display as user types. Used for hiding passwords. -
period
(optional) amount of time to wait.
NOTE: Annoy uses Highline to get user responses.
If msg
ends with a space character, Highline will not print a
new line. If there is no space character Highline will print a new line.
Annoy.get_user_input("Password?") # => Password? # => 'user types here' Annoy.get_user_input("Password? ") # => Password? 'user types here'
# File lib/annoy.rb, line 219 def Annoy.get_user_input(msg, echo=nil, period=nil) return unless STDIN.tty? # Only ask a question if there's a human return if Annoy.skip? response = nil begin success = Timeout::timeout(period || @@period) do highline = HighLine.new response = highline.ask(msg) { |q| unless echo.nil? q.overwrite = true # Erase the question afterwards q.echo = echo # Don't display response end q.whitespace = :strip # Remove whitespace from the response } end rescue Timeout::Error => ex puts $/, "Times up!" end response end
-
factor
annoyance factor, one of :low (default), :medium, :high, :insane -
flavor
annoyance flavor, one of :rand (default), :numeric, string -
writer
an IO object to write to. Default: STDERR -
period
the amount of time to wait in seconds. Default: 60
# File lib/annoy.rb, line 62 def initialize(opts={:factor=>:medium, :flavor=>:rand, :writer=>STDOUT, :period=>nil}) @factor = opts[:factor] @flavor = Annoy.get_flavor(opts[:flavor]) @writer = opts[:writer] @period = opts[:period] || @@period unless Annoy.respond_to?("#{@flavor}_question") raise "Hey, hey, hey. I don't know that flavor! (#{@flavor})" end end
-
Generates a rudimentary numeric equation in the form: (Integer OPERATOR Integer).
-
Returns [equation, answer]
# File lib/annoy.rb, line 95 def Annoy.numeric_question(factor=:medium) equation = answer = 0 while answer < 10 vals = [rand(@@randsize[factor])+1, @@operators[factor][ rand(@@operators[factor].size) ], rand(@@randsize[factor])+1 ] equation = "(%d %s %d)" % vals answer = eval(equation) end [equation, answer] end
Prints a question to writer and waits for a response on STDIN. It checks
whether STDIN is connected a tty so it doesn't block on gets. when
there's no human around to annoy. It will return TRUE
when STDIN is NOT connected to a tty or if writer
is nil.
-
msg
The question to pose to the user -
regexp
The regular expression to match the answer.
# File lib/annoy.rb, line 182 def Annoy.pose_question(msg, regexp, writer=STDOUT, period=nil) return true unless STDIN.tty? # Only ask a question if there's a human return true if Annoy.skip? return true if writer.nil? begin success = Timeout::timeout(period || @@period) do regexp &&= Regexp.new regexp highline = HighLine.new response = highline.ask(msg) { |q| q.echo = '*' # Don't display response q.overwrite = true # Erase the question afterwards q.whitespace = :strip # Remove whitespace from the response } regexp.match(response) end rescue Timeout::Error => ex writer.puts $/, "Times up!" false end end
Runs a challenge with the message, “Proceed?” See: ::challenge?
# File lib/annoy.rb, line 160 def Annoy.proceed?(factor=:medium, flavor=:rand, writer=STDOUT) Annoy.challenge?("Proceed?", factor, flavor, writer) end
A wrapper for ::string_question and numberic_question
# File lib/annoy.rb, line 80 def Annoy.question(factor=:medium, flavor=:rand) raise "Come on, you ruined the flavor!" unless flavor Annoy.send("#{flavor}_question", factor) end
Returns true of Annoy is in skip mode
# File lib/annoy.rb, line 56 def Annoy.skip?; @@skip; end
Generates a random string
# File lib/annoy.rb, line 86 def Annoy.string_question(factor=:medium) # Strings don't need to be evaluated so the answer is the # same as the question. str = strand @@strlen[factor] [str,str] end
Display msg
for period
seconds. NOTE:
msg
should be a short, single line. This is a naive approach
which simply overwrites the current line.
# File lib/annoy.rb, line 243 def Annoy.timed_display(msg, writer, period=nil) return true unless STDIN.tty? # Only ask a question if there's a human if Annoy.skip? #writer.puts msg return true end if writer.nil? sleep period+1 return true end begin period ||= @@period success = Timeout::timeout(period) do writer.puts "Message will display for #{period} seconds" writer.print msg writer.flush if writer.respond_to?(:flush) sleep period+1 end rescue Timeout::Error => ex writer.print "\r" << ' '*msg.size end true end
Private Class Methods
Return a random flavor
# File lib/annoy.rb, line 328 def Annoy.flavor_rand @@flavors[rand(@@flavors.size)] end
-
f
a prospective flavor name
# File lib/annoy.rb, line 323 def Annoy.get_flavor(f) f.to_sym == :rand ? flavor_rand : f.to_sym end
# File lib/annoy.rb, line 269 def Annoy.get_response(writer=STDOUT) return true unless STDIN.tty? # Humans only return true if Annoy.skip? # TODO: Count the number of keystrokes to prevent copy/paste. # We can probably use Highline. # We likely need to be more specific but this will do for now. #if ::SysInfo.new.os == :unix # begin # response = [] # char = nil # system("stty raw -echo") # Raw mode, no echo # while char != "\r" || response.size > 5 # char = STDIN.getc.chr # writer.print char # writer.flush # response << char # end # writer.print "\n\r" # response = response.join('') # rescue => ex # ensure # system("stty -raw echo") # Reset terminal mode # end #else response = (STDIN.gets || "") #end response.chomp.gsub(/["']/, '') end
Generates a string of random alphanumeric characters.
-
len
is the length, an Integer. Default: 8 -
safe
in safe-mode, ambiguous characters are removed (default: true):i l o 1 0
# File lib/annoy.rb, line 314 def Annoy.strand( len=8, safe=true ) chars = ("a".."z").to_a + ("0".."9").to_a chars.delete_if { |v| %w(i l o 1 0).member?(v) } if safe str = "" 1.upto(len) { |i| str << chars[rand(chars.size-1)] } str end
Returns a verb appropriate to the flavor.
-
:numeric => resolve
-
:string => type
# File lib/annoy.rb, line 300 def Annoy.verb(flavor) case flavor when :numeric then "resolve" when :string then "type" else nil end end
Public Instance Methods
See: ::challenge? Uses the value of @flavor, @factor, and @writer
# File lib/annoy.rb, line 166 def challenge?(msg="Please confirm.") Annoy.challenge?(msg, @factor, @flavor, @writer) end
See: #pose_question Uses the value of @writer
# File lib/annoy.rb, line 172 def pose_question(msg, regexp) Annoy.pose_question(msg, regexp, @writer) end
Generates and returns a question. The correct response is available as +@answer+.
# File lib/annoy.rb, line 74 def question q, @answer =Annoy.question(@factor, @flavor) q end