Class: Annoy

Inherits:
Object
  • Object
show all
Defined in:
lib/annoy.rb

Overview

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.

Defined Under Namespace

Classes: GiveUp

Constant Summary collapse

@@operators =
{
  :low      => %w(+ -),
  :medium   => %w(* -),
  :high     => %w(& * -),
  :insane   => %w(** << | & *)
}.freeze
@@strlen =
{
  :low      => 2,
  :medium   => 3,
  :high     => 4,
  :insane   => 32
}.freeze
@@randsize =
{
  :low      => 10,
  :medium   => 12,
  :high     => 50,
  :insane   => 1000
}.freeze
@@period =

max seconds to wait

3600.freeze
@@flavors =
[:numeric, :string].freeze
@@skip =

skip questions

false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {:factor=>:medium, :flavor=>:rand, :writer=>STDOUT, :period=>nil}) ⇒ Annoy

  • 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



62
63
64
65
66
67
68
69
70
# 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

Instance Attribute Details

#answerObject

Returns the value of attribute answer.



20
21
22
# File 'lib/annoy.rb', line 20

def answer
  @answer
end

#factorObject

Returns the value of attribute factor.



18
19
20
# File 'lib/annoy.rb', line 18

def factor
  @factor
end

#flavorObject

Returns the value of attribute flavor.



19
20
21
# File 'lib/annoy.rb', line 19

def flavor
  @flavor
end

#periodObject

Returns the value of attribute period.



22
23
24
# File 'lib/annoy.rb', line 22

def period
  @period
end

#systemObject

Returns the value of attribute system.



23
24
25
# File 'lib/annoy.rb', line 23

def system
  @system
end

#writerObject

Returns the value of attribute writer.



21
22
23
# File 'lib/annoy.rb', line 21

def writer
  @writer
end

Class Method Details

.are_you_sure?(factor = :medium, flavor = :rand, writer = STDOUT) ⇒ Boolean

Runs a challenge with the message, “Are you sure?” See: Annoy.challenge?

Returns:

  • (Boolean)


154
155
156
# 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

.challenge?(msg = "Please confirm.", factor = :medium, flavor = :rand, writer = STDOUT, period = nil) ⇒ Boolean

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.

Returns:

  • (Boolean)


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# 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

.disable_skipObject

Tells annoy to prompt for a response.



54
# File 'lib/annoy.rb', line 54

def Annoy.disable_skip; @@skip = false; end

.enable_skipObject

Calling this method tells Annoy to not prompt for a response. All questions will return true.



52
# File 'lib/annoy.rb', line 52

def Annoy.enable_skip; @@skip = true; end

.get_user_input(msg, echo = nil, period = nil) ⇒ Object

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'


219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# 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

.numeric_question(factor = :medium) ⇒ Object

  • Generates a rudimentary numeric equation in the form: (Integer OPERATOR Integer).

  • Returns [equation, answer]



95
96
97
98
99
100
101
102
103
104
105
# 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

.pose_question(msg, regexp, writer = STDOUT, period = nil) ⇒ Object

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.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# 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

.proceed?(factor = :medium, flavor = :rand, writer = STDOUT) ⇒ Boolean

Runs a challenge with the message, “Proceed?” See: Annoy.challenge?

Returns:

  • (Boolean)


160
161
162
# File 'lib/annoy.rb', line 160

def Annoy.proceed?(factor=:medium, flavor=:rand, writer=STDOUT)
  Annoy.challenge?("Proceed?", factor, flavor, writer)
end

.question(factor = :medium, flavor = :rand) ⇒ Object

A wrapper for string_question and numberic_question



80
81
82
83
# 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

.skip?Boolean

Returns true of Annoy is in skip mode

Returns:

  • (Boolean)


56
# File 'lib/annoy.rb', line 56

def Annoy.skip?; @@skip; end

.string_question(factor = :medium) ⇒ Object

Generates a random string



86
87
88
89
90
91
# 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

.timed_display(msg, writer, period = nil) ⇒ Object

Display msg for period seconds. NOTE: msg should be a short, single line. This is a naive approach which simply overwrites the current line.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# 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

Instance Method Details

#challenge?(msg = "Please confirm.") ⇒ Boolean

See: Annoy.challenge? Uses the value of @flavor, @factor, and @writer

Returns:

  • (Boolean)


166
167
168
# File 'lib/annoy.rb', line 166

def challenge?(msg="Please confirm.")
  Annoy.challenge?(msg, @factor, @flavor, @writer)
end

#pose_question(msg, regexp) ⇒ Object

See: Annoy.pose_question Uses the value of @writer



172
173
174
# File 'lib/annoy.rb', line 172

def pose_question(msg, regexp)
  Annoy.pose_question(msg, regexp, @writer)
end

#questionObject

Generates and returns a question. The correct response is available as @answer.



74
75
76
77
# File 'lib/annoy.rb', line 74

def question
  q, @answer =Annoy.question(@factor, @flavor)
  q
end