delano-annoy 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (7) hide show
  1. data/CHANGES.txt +7 -0
  2. data/LICENSE.txt +19 -0
  3. data/README.rdoc +23 -0
  4. data/Rakefile +104 -0
  5. data/annoy.gemspec +56 -0
  6. data/lib/annoy.rb +297 -0
  7. metadata +82 -0
data/CHANGES.txt ADDED
@@ -0,0 +1,7 @@
1
+ ANNOY, CHANGES
2
+
3
+
4
+ #### 0.5 (2009-05-07) ###############################
5
+
6
+ * First public release. See commit history for solutious-rudy.
7
+
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Delano Mandelbaum, Solutious Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,23 @@
1
+ = Annoy - v0.5
2
+
3
+ All your system-independent infoz in one handy class.
4
+
5
+ == Installation
6
+
7
+ Via Rubygems, one of:
8
+
9
+ $ sudo gem install annoy
10
+ $ sudo gem install delano-annoy --source http://gems.github.com/
11
+
12
+ or via download:
13
+ * annoy-latest.tar.gz[http://github.com/delano/annoy/tarball/latest]
14
+ * annoy-latest.zip[http://github.com/delano/annoy/zipball/latest]
15
+
16
+
17
+ == Credits
18
+
19
+ * Delano Mandelbaum (delano@solutious.com)
20
+
21
+ == License
22
+
23
+ See: LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,104 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'hanna/rdoctask'
5
+ require 'fileutils'
6
+ include FileUtils
7
+
8
+ task :default => :package
9
+
10
+ # CONFIG =============================================================
11
+
12
+ # Change the following according to your needs
13
+ README = "README.rdoc"
14
+ CHANGES = "CHANGES.txt"
15
+ LICENSE = "LICENSE.txt"
16
+
17
+ # Files and directories to be deleted when you run "rake clean"
18
+ CLEAN.include [ 'pkg', '*.gem', '.config']
19
+
20
+ # Virginia assumes your project and gemspec have the same name
21
+ name = (Dir.glob('*.gemspec') || ['virginia']).first.split('.').first
22
+ load "#{name}.gemspec"
23
+ version = @spec.version
24
+
25
+ # That's it! The following defaults should allow you to get started
26
+ # on other things.
27
+
28
+
29
+ # TESTS/SPECS =========================================================
30
+
31
+
32
+
33
+ # INSTALL =============================================================
34
+
35
+ Rake::GemPackageTask.new(@spec) do |p|
36
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
37
+ end
38
+
39
+ task :release => [ :rdoc, :package ]
40
+ task :install => [ :rdoc, :package ] do
41
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
42
+ end
43
+ task :uninstall => [ :clean ] do
44
+ sh %{sudo gem uninstall #{name}}
45
+ end
46
+
47
+
48
+ # RUBYFORGE RELEASE / PUBLISH TASKS ==================================
49
+
50
+ if @spec.rubyforge_project
51
+ desc 'Publish website to rubyforge'
52
+ task 'publish:rdoc' => 'doc/index.html' do
53
+ sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/"
54
+ end
55
+
56
+ desc 'Public release to rubyforge'
57
+ task 'publish:gem' => [:package] do |t|
58
+ sh <<-end
59
+ rubyforge add_release -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
60
+ rubyforge add_file -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+
67
+ # RUBY DOCS TASK ==================================
68
+
69
+ Rake::RDocTask.new do |t|
70
+ t.rdoc_dir = 'doc'
71
+ t.title = @spec.summary
72
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
73
+ t.options << '--charset' << 'utf-8'
74
+ t.rdoc_files.include(LICENSE)
75
+ t.rdoc_files.include(README)
76
+ t.rdoc_files.include(CHANGES)
77
+ #t.rdoc_files.include('bin/*')
78
+ t.rdoc_files.include('lib/**/*.rb')
79
+ end
80
+
81
+
82
+
83
+
84
+ #Hoe.new('rspec', Spec::VERSION::STRING) do |p|
85
+ # p.summary = Spec::VERSION::SUMMARY
86
+ # p.description = "Behaviour Driven Development for Ruby."
87
+ # p.rubyforge_name = 'rspec'
88
+ # p.developer('RSpec Development Team', 'rspec-devel@rubyforge.org')
89
+ # p.extra_dev_deps = [["cucumber",">= 0.1.13"]]
90
+ # p.remote_rdoc_dir = "rspec/#{Spec::VERSION::STRING}"
91
+ # p.rspec_options = ['--options', 'spec/spec.opts']
92
+ # p.history_file = 'History.rdoc'
93
+ # p.readme_file = 'README.rdoc'
94
+ # p.post_install_message = <<-POST_INSTALL_MESSAGE
95
+ ##{'*'*50}
96
+ #
97
+ # Thank you for installing rspec-#{Spec::VERSION::STRING}
98
+ #
99
+ # Please be sure to read History.rdoc and Upgrade.rdoc
100
+ # for useful information about this release.
101
+ #
102
+ #{'*'*50}
103
+ #POST_INSTALL_MESSAGE
104
+ #end
data/annoy.gemspec ADDED
@@ -0,0 +1,56 @@
1
+ @spec = Gem::Specification.new do |s|
2
+ s.name = "annoy"
3
+ s.rubyforge_project = "annoy"
4
+ s.version = "0.5.0"
5
+ s.summary = "Annoy: Like your annoying friend that asks you questions all the time."
6
+ s.description = s.summary
7
+ s.author = "Delano Mandelbaum"
8
+ s.email = "delano@solutious.com"
9
+ s.homepage = "http://solutious.com/"
10
+
11
+
12
+ # = EXECUTABLES =
13
+ # The list of executables in your project (if any). Don't include the path,
14
+ # just the base filename.
15
+ s.executables = %w[]
16
+
17
+ # = DEPENDENCIES =
18
+ # Add all gem dependencies
19
+ #s.add_dependency ''
20
+ s.add_dependency 'highline', '>= 1.5.0'
21
+
22
+ # = MANIFEST =
23
+ # The complete list of files to be included in the release. When GitHub packages your gem,
24
+ # it doesn't allow you to run any command that accesses the filesystem. You will get an
25
+ # error. You can ask your VCS for the list of versioned files:
26
+ # git ls-files
27
+ # svn list -R
28
+ s.files = %w(
29
+ CHANGES.txt
30
+ LICENSE.txt
31
+ README.rdoc
32
+ Rakefile
33
+ lib/annoy.rb
34
+ annoy.gemspec
35
+ )
36
+
37
+ s.extra_rdoc_files = %w[README.rdoc LICENSE.txt]
38
+ s.has_rdoc = true
39
+ s.rdoc_options = ["--line-numbers", "--title", s.summary, "--main", "README.rdoc"]
40
+ s.require_paths = %w[lib]
41
+ s.rubygems_version = '1.3.0'
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 2
46
+
47
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<RedCloth>, [">= 4.0.4"])
49
+ else
50
+ s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
54
+ end
55
+
56
+ end
data/lib/annoy.rb ADDED
@@ -0,0 +1,297 @@
1
+ #---
2
+ # TODO: Use Matrix to give a more accurate annoyance factor
3
+ # TODO: Add trivia questions
4
+ #+++
5
+
6
+ require 'timeout'
7
+ require 'highline'
8
+
9
+ # = Annoy
10
+ #
11
+ # Like your annoying friend that asks you questions all the time.
12
+ #
13
+ # Rudy uses Annoy to present the user with a simple question before
14
+ # continuing with a destructive action.
15
+ #
16
+ class Annoy
17
+
18
+ attr_accessor :factor
19
+ attr_accessor :flavor
20
+ attr_accessor :answer
21
+ attr_accessor :writer
22
+ attr_accessor :period
23
+ attr_accessor :system
24
+
25
+ @@operators = {
26
+ :low => %w(+ -),
27
+ :medium => %w(* -),
28
+ :high => %w(& * -),
29
+ :insane => %w(** << | & *)
30
+ }.freeze
31
+
32
+ @@strlen = {
33
+ :low => 2,
34
+ :medium => 3,
35
+ :high => 4,
36
+ :insane => 32
37
+ }.freeze
38
+
39
+ @@randsize = {
40
+ :low => 10,
41
+ :medium => 12,
42
+ :high => 50,
43
+ :insane => 1000
44
+ }.freeze
45
+
46
+ @@period = 60.freeze # max seconds to wait
47
+ @@flavors = [:numeric, :string].freeze
48
+ @@skip = false # skip questions
49
+
50
+ # Calling this method tells Annoy to not prompt for
51
+ # a response. All questions will return true.
52
+ def Annoy.enable_skip; @@skip = true; end
53
+ # Tells annoy to prompt for a response.
54
+ def Annoy.disable_skip; @@skip = false; end
55
+ # Returns true of Annoy is in skip mode
56
+ def Annoy.skip?; @@skip; end
57
+
58
+ # * +factor+ annoyance factor, one of :low (default), :medium, :high, :insane
59
+ # * +flavor+ annoyance flavor, one of :rand (default), :numeric, string
60
+ # * +writer+ an IO object to write to. Default: STDERR
61
+ # * +period+ the amount of time to wait in seconds. Default: 60
62
+ def initialize(opts={:factor=>:medium, :flavor=>:rand, :writer=>STDOUT, :period=>nil})
63
+ @factor = opts[:factor]
64
+ @flavor = Annoy.get_flavor(opts[:flavor])
65
+ @writer = opts[:writer]
66
+ @period = opts[:period] || @@period
67
+ unless Annoy.respond_to?("#{@flavor}_question")
68
+ raise "Hey, hey, hey. I don't know that flavor! (#{@flavor})"
69
+ end
70
+ end
71
+
72
+ # Generates and returns a question. The correct response is available
73
+ # as +@answer+.
74
+ def question
75
+ q, @answer =Annoy.question(@factor, @flavor)
76
+ q
77
+ end
78
+
79
+ # A wrapper for string_question and numberic_question
80
+ def Annoy.question(factor=:medium, flavor=:rand)
81
+ raise "Come on, you ruined the flavor!" unless flavor
82
+ Annoy.send("#{flavor}_question", factor)
83
+ end
84
+
85
+ # Generates a random string
86
+ def Annoy.string_question(factor=:medium)
87
+ # Strings don't need to be evaluated so the answer is the
88
+ # same as the question.
89
+ str = strand @@strlen[factor]
90
+ [str,str]
91
+ end
92
+
93
+ # * Generates a rudimentary numeric equation in the form: (Integer OPERATOR Integer).
94
+ # * Returns [equation, answer]
95
+ def Annoy.numeric_question(factor=:medium)
96
+ equation = answer = 0
97
+ while answer < 10
98
+ vals = [rand(@@randsize[factor])+1,
99
+ @@operators[factor][ rand(@@operators[factor].size) ],
100
+ rand(@@randsize[factor])+1 ]
101
+ equation = "(%d %s %d)" % vals
102
+ answer = eval(equation)
103
+ end
104
+ [equation, answer]
105
+ end
106
+
107
+ # Prints a question to +writer+ and waits for a response on STDIN.
108
+ # It checks whether STDIN is connected a tty so it doesn't block on gets
109
+ # when there's no human around to annoy. It will return <b>TRUE</b> when
110
+ # STDIN is NOT connected to a tty (when STDIN.tty? returns false).
111
+ # * +msg+ The message to print. Default: "Please confirm."
112
+ # Returns true when the answer is correct, otherwise false.
113
+ def Annoy.challenge?(msg="Please confirm.", factor=:medium, flavor=:rand, writer=STDOUT, period=nil)
114
+ return true unless STDIN.tty? # Humans only!
115
+ return true if Annoy.skip?
116
+ begin
117
+ success = Timeout::timeout(period || @@period) do
118
+ flavor = Annoy.get_flavor(flavor)
119
+ question, answer = Annoy.question(factor, flavor)
120
+ msg = "#{msg} To continue, #{Annoy.verb(flavor)} #{question}: "
121
+ #writer.print msg
122
+ #if ![:medium, :high, :insane].member?(factor) && flavor == :numeric
123
+ #writer.print "(#{answer}) "
124
+ #writer.flush
125
+ #end
126
+ #response = Annoy.get_response(writer)
127
+
128
+ highline = HighLine.new
129
+ response = highline.ask(msg) { |q|
130
+ q.echo = '*' # Don't display response
131
+ q.overwrite = true # Erase the question afterwards
132
+ q.whitespace = :strip # Remove whitespace from the response
133
+ q.answer_type = Integer if flavor == :numeric
134
+ }
135
+
136
+ ret = (response == answer)
137
+ writer.puts "Incorrect" unless ret
138
+ ret
139
+ end
140
+ rescue Interrupt
141
+ writer.puts $/, "Giving up!"
142
+ false
143
+ rescue Annoy::GiveUp => ex
144
+ writer.puts $/, "Giving up!"
145
+ false
146
+ rescue Timeout::Error => ex
147
+ writer.puts $/, "Times up!"
148
+ false
149
+ end
150
+ end
151
+
152
+ # Runs a challenge with the message, "Are you sure?"
153
+ # See: Annoy.challenge?
154
+ def Annoy.are_you_sure?(factor=:medium, flavor=:rand, writer=STDOUT)
155
+ Annoy.challenge?("Are you sure?", factor, flavor, writer)
156
+ end
157
+
158
+ # Runs a challenge with the message, "Proceed?"
159
+ # See: Annoy.challenge?
160
+ def Annoy.proceed?(factor=:medium, flavor=:rand, writer=STDOUT)
161
+ Annoy.challenge?("Proceed?", factor, flavor, writer)
162
+ end
163
+
164
+ # See: Annoy.challenge?
165
+ # Uses the value of @flavor, @factor, and @writer
166
+ def challenge?(msg="Please confirm.")
167
+ Annoy.challenge?(msg, @factor, @flavor, @writer)
168
+ end
169
+
170
+ # See: Annoy.pose_question
171
+ # Uses the value of @writer
172
+ def pose_question(msg, regexp)
173
+ Annoy.pose_question(msg, regexp, @writer)
174
+ end
175
+
176
+ # Prints a question to writer and waits for a response on STDIN.
177
+ # It checks whether STDIN is connected a tty so it doesn't block on gets.
178
+ # when there's no human around to annoy. It will return <b>TRUE</b> when
179
+ # STDIN is NOT connected to a tty.
180
+ # * +msg+ The question to pose to the user
181
+ # * +regexp+ The regular expression to match the answer.
182
+ def Annoy.pose_question(msg, regexp, writer=STDOUT, period=nil)
183
+ return true unless STDIN.tty? # Only ask a question if there's a human
184
+ return true if Annoy.skip?
185
+ begin
186
+ success = Timeout::timeout(period || @@period) do
187
+ regexp &&= Regexp.new regexp
188
+ highline = HighLine.new
189
+ response = highline.ask(msg) { |q|
190
+ q.echo = '*' # Don't display response
191
+ q.overwrite = true # Erase the question afterwards
192
+ q.whitespace = :strip # Remove whitespace from the response
193
+ }
194
+ regexp.match(response)
195
+ end
196
+ rescue Timeout::Error => ex
197
+ writer.puts $/, "Times up!"
198
+ false
199
+ end
200
+ end
201
+
202
+
203
+ def Annoy.timed_display(msg, writer, period=nil)
204
+ return true unless STDIN.tty? # Only ask a question if there's a human
205
+ if Annoy.skip?
206
+ #writer.puts msg
207
+ return true
208
+ end
209
+ begin
210
+ period ||= @@period
211
+ success = Timeout::timeout(period) do
212
+ writer.puts "Message will display for #{period} seconds"
213
+ writer.print msg
214
+ writer.flush if writer.respond_to?(:flush)
215
+ sleep period+1
216
+ end
217
+ rescue Timeout::Error => ex
218
+ writer.print "\r" << ' '*msg.size
219
+ end
220
+
221
+ true
222
+ end
223
+
224
+ private
225
+ def Annoy.get_response(writer=STDOUT)
226
+ return true unless STDIN.tty? # Humans only
227
+ return true if Annoy.skip?
228
+ # TODO: Count the number of keystrokes to prevent copy/paste.
229
+ # We can probably use Highline.
230
+ # We likely need to be more specific but this will do for now.
231
+ #if ::SysInfo.new.os == :unix
232
+ # begin
233
+ # response = []
234
+ # char = nil
235
+ # system("stty raw -echo") # Raw mode, no echo
236
+ # while char != "\r" || response.size > 5
237
+ # char = STDIN.getc.chr
238
+ # writer.print char
239
+ # writer.flush
240
+ # response << char
241
+ # end
242
+ # writer.print "\n\r"
243
+ # response = response.join('')
244
+ # rescue => ex
245
+ # ensure
246
+ # system("stty -raw echo") # Reset terminal mode
247
+ # end
248
+ #else
249
+ response = (STDIN.gets || "")
250
+ #end
251
+ response.chomp.gsub(/["']/, '')
252
+ end
253
+ # Returns a verb appropriate to the flavor.
254
+ # * :numeric => resolve
255
+ # * :string => type
256
+ def Annoy.verb(flavor)
257
+ case flavor
258
+ when :numeric then "resolve"
259
+ when :string then "type"
260
+ else
261
+ nil
262
+ end
263
+ end
264
+
265
+ #
266
+ # Generates a string of random alphanumeric characters.
267
+ # * +len+ is the length, an Integer. Default: 8
268
+ # * +safe+ in safe-mode, ambiguous characters are removed (default: true):
269
+ # i l o 1 0
270
+ def Annoy.strand( len=8, safe=true )
271
+ chars = ("a".."z").to_a + ("0".."9").to_a
272
+ chars.delete_if { |v| %w(i l o 1 0).member?(v) } if safe
273
+ str = ""
274
+ 1.upto(len) { |i| str << chars[rand(chars.size-1)] }
275
+ str
276
+ end
277
+
278
+ # * +f+ a prospective flavor name
279
+ def Annoy.get_flavor(f)
280
+ f.to_sym == :rand ? flavor_rand : f.to_sym
281
+ end
282
+
283
+ # Return a random flavor
284
+ def Annoy.flavor_rand
285
+ @@flavors[rand(@@flavors.size)]
286
+ end
287
+
288
+
289
+ end
290
+
291
+ # = Annoy::GiveUp
292
+ #
293
+ # This is what happens when you don't answer Annoy's questions.
294
+ class Annoy::GiveUp < RuntimeError
295
+ end
296
+
297
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delano-annoy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Delano Mandelbaum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-08 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: highline
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.5.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: RedCloth
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.4
34
+ version:
35
+ description: "Annoy: Like your annoying friend that asks you questions all the time."
36
+ email: delano@solutious.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ - LICENSE.txt
44
+ files:
45
+ - CHANGES.txt
46
+ - LICENSE.txt
47
+ - README.rdoc
48
+ - Rakefile
49
+ - lib/annoy.rb
50
+ - annoy.gemspec
51
+ has_rdoc: true
52
+ homepage: http://solutious.com/
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --line-numbers
56
+ - --title
57
+ - "Annoy: Like your annoying friend that asks you questions all the time."
58
+ - --main
59
+ - README.rdoc
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: annoy
77
+ rubygems_version: 1.2.0
78
+ signing_key:
79
+ specification_version: 2
80
+ summary: "Annoy: Like your annoying friend that asks you questions all the time."
81
+ test_files: []
82
+