textify 0.1.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.
- data/LICENSE +20 -0
- data/README +33 -0
- data/README.rdoc +33 -0
- data/lib/sample.rb +21 -0
- data/lib/textify.rb +348 -0
- data/test/helper.rb +10 -0
- data/test/test_textify.rb +13 -0
- metadata +62 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Joseph Weissman
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= Textify
|
2
|
+
|
3
|
+
Some basic, non-curses-based functions to aid user
|
4
|
+
interaction in text-only ruby scripts. Included are
|
5
|
+
functions for
|
6
|
+
|
7
|
+
* Justifying strings
|
8
|
+
* List selection, confirmation, data entry
|
9
|
+
* Displaying arrays and hashes as tables
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
require "textify"
|
13
|
+
class MyClass
|
14
|
+
textify
|
15
|
+
def foo
|
16
|
+
[[1,2,3],[4,5,6],[7,8,9]].table({:headings => %w[ one two three ]})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
o = MyClass.new
|
21
|
+
o.foo
|
22
|
+
|
23
|
+
|
24
|
+
The snippet above outputs:
|
25
|
+
|
26
|
+
|
27
|
+
****************************************
|
28
|
+
* one * two * three *
|
29
|
+
****************************************
|
30
|
+
* 1 * 2 * 3 *
|
31
|
+
* 4 * 5 * 6 *
|
32
|
+
* 7 * 8 * 9 *
|
33
|
+
****************************************
|
data/README.rdoc
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= Textify
|
2
|
+
|
3
|
+
Some basic, non-curses-based functions to aid user
|
4
|
+
interaction in text-only ruby scripts. Included are
|
5
|
+
functions for
|
6
|
+
|
7
|
+
* Justifying strings
|
8
|
+
* List selection, confirmation, data entry
|
9
|
+
* Displaying arrays and hashes as tables
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
require "textify"
|
13
|
+
class MyClass
|
14
|
+
textify
|
15
|
+
def foo
|
16
|
+
[[1,2,3],[4,5,6],[7,8,9]].table({:headings => %w[ one two three ]})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
o = MyClass.new
|
21
|
+
o.foo
|
22
|
+
|
23
|
+
|
24
|
+
The snippet above outputs:
|
25
|
+
|
26
|
+
|
27
|
+
****************************************
|
28
|
+
* one * two * three *
|
29
|
+
****************************************
|
30
|
+
* 1 * 2 * 3 *
|
31
|
+
* 4 * 5 * 6 *
|
32
|
+
* 7 * 8 * 9 *
|
33
|
+
****************************************
|
data/lib/sample.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "textify"
|
2
|
+
class MyClass
|
3
|
+
textify
|
4
|
+
def foo
|
5
|
+
# @val = ask "Enter a value: "
|
6
|
+
[[1,2,3],[4,5,6],[7,8,9]].table({:headings => %w[ one two three ]})
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
o = MyClass.new
|
11
|
+
o.foo
|
12
|
+
|
13
|
+
=begin
|
14
|
+
****************************************
|
15
|
+
* one * two * three *
|
16
|
+
****************************************
|
17
|
+
* 1 * 2 * 3 *
|
18
|
+
* 4 * 5 * 6 *
|
19
|
+
* 7 * 8 * 9 *
|
20
|
+
****************************************
|
21
|
+
=end
|
data/lib/textify.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
module Textify
|
2
|
+
=begin
|
3
|
+
|
4
|
+
= Textify
|
5
|
+
|
6
|
+
Some basic, non-curses-based functions to aid user
|
7
|
+
interaction in text-only ruby scripts. Included are
|
8
|
+
functions for
|
9
|
+
|
10
|
+
* Justifying strings
|
11
|
+
* List selection, confirmation, data entry
|
12
|
+
* Displaying arrays and hashes as tables
|
13
|
+
|
14
|
+
== Usage
|
15
|
+
require "textify"
|
16
|
+
class MyClass
|
17
|
+
textify
|
18
|
+
def foo
|
19
|
+
[[1,2,3],[4,5,6],[7,8,9]].table({:headings => %w[ one two three ]})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
o = MyClass.new
|
24
|
+
o.foo
|
25
|
+
|
26
|
+
|
27
|
+
The snippet above outputs:
|
28
|
+
|
29
|
+
|
30
|
+
****************************************
|
31
|
+
* one * two * three *
|
32
|
+
****************************************
|
33
|
+
* 1 * 2 * 3 *
|
34
|
+
* 4 * 5 * 6 *
|
35
|
+
* 7 * 8 * 9 *
|
36
|
+
****************************************
|
37
|
+
|
38
|
+
=end
|
39
|
+
|
40
|
+
DefaultScreenWidth = 40
|
41
|
+
|
42
|
+
# minor utility fns
|
43
|
+
def br n=1; puts until (n-=1)<0; end
|
44
|
+
def cls; br 50; end
|
45
|
+
|
46
|
+
|
47
|
+
=begin
|
48
|
+
|
49
|
+
Textual.justify (direction, text, options={})
|
50
|
+
|
51
|
+
Justifies text within the default screen width. Accepts in the options hash:
|
52
|
+
|
53
|
+
:width set a new default width for this justification
|
54
|
+
:character fill with another characer (default is ' ')
|
55
|
+
|
56
|
+
=end
|
57
|
+
|
58
|
+
def justify(direction, text, options={})
|
59
|
+
# puts "Entering justify..."
|
60
|
+
width = options[:width] || DefaultScreenWidth
|
61
|
+
if text.length >= width
|
62
|
+
# puts "Returning, text '#{text}' (#{text.size}) too big for width #{width}."
|
63
|
+
return text
|
64
|
+
end
|
65
|
+
|
66
|
+
orig_text = text
|
67
|
+
char = options[:character] || ' '
|
68
|
+
|
69
|
+
usable_space = width-text.length
|
70
|
+
offset = char*usable_space
|
71
|
+
left_offset = char * (usable_space.to_f/2).ceil
|
72
|
+
right_offset = char * (usable_space - left_offset.size)
|
73
|
+
|
74
|
+
#half_offset = offset.slice(0..(offset.size/2).floor)
|
75
|
+
# puts "Usable space: #{usable_space}. Left: #{left_offset.size}. Right: #{right_offset.size}."
|
76
|
+
|
77
|
+
text = case direction #.downcase.to_sym
|
78
|
+
when :left then "#{text}#{offset}"
|
79
|
+
when :right then "#{offset}#{text}"
|
80
|
+
when :center then "#{left_offset}#{text}#{right_offset}"
|
81
|
+
else text
|
82
|
+
end
|
83
|
+
|
84
|
+
# puts "- Justifying (#{direction.to_s}) to width (#{width}): '#{orig_text}' => \t'#{text}'"
|
85
|
+
|
86
|
+
text
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def test_justify
|
91
|
+
title " Testing justify command "
|
92
|
+
text = justify :left, "Left Justifed"
|
93
|
+
puts text
|
94
|
+
puts justify(:right, "Right Justifed")
|
95
|
+
puts justify(:center, "Center Justifed")
|
96
|
+
puts "Unjustified"
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
def title name, options={}
|
107
|
+
br options[:offset] || 2
|
108
|
+
print "#{justify :center, name, {:character => options[:character] || '='}}"
|
109
|
+
br options[:offset] || 2
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_title
|
113
|
+
title " Sample Title 1 "
|
114
|
+
title " Sample Title 2 ", {:character => '@', :offset => 3 }
|
115
|
+
title " Sample Title 3 ", {:character => "&", :offset => 2 }
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
def display_selection items, prompt
|
122
|
+
title prompt # print "\n"*3
|
123
|
+
items.each_with_index { |item, n| puts " [#{n+1}] #{item.gsub('_',' ').capitalize} \n" }
|
124
|
+
print "\n > "
|
125
|
+
@input = gets.to_i;
|
126
|
+
end
|
127
|
+
|
128
|
+
def select(items, prompt=" Please select one of the following options: ", options={})
|
129
|
+
#title prompt # print "\n"*3
|
130
|
+
#items.each_with_index { |item, n| puts " [#{n+1}] #{item.gsub('_',' ').capitalize} \n" }
|
131
|
+
#print "\n > "
|
132
|
+
#input = gets.to_i;
|
133
|
+
# display_selection;
|
134
|
+
@input=0; display_selection(items, prompt) until (@input >= 1 and @input <= items.size)
|
135
|
+
#title prompt # print "\n"*3
|
136
|
+
#items.each_with_index { |item, n| puts " [#{n+1}] #{item.gsub('_',' ').capitalize} \n" }
|
137
|
+
#print "\n > "
|
138
|
+
#input = gets.to_i
|
139
|
+
#end
|
140
|
+
|
141
|
+
puts "Selected #{@input} (#{items[@input-1]}).. " #{ }"#{options[input-1]}."
|
142
|
+
return items[@input-1]
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_select
|
146
|
+
title " Selection Test "
|
147
|
+
choices = %w[ apples strawberries bananas mandarin_oranges tangerines oranges cherries watermelons raspberries ]
|
148
|
+
option = select choices
|
149
|
+
title "Thanks for choosing #{option}!"
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
def confirm(prompt = "Please confirm. ", options = {})
|
157
|
+
choices = options[:choices] || %w[ Yes No]
|
158
|
+
input = select choices, prompt
|
159
|
+
return true if input == choices[0]
|
160
|
+
return false
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_confirm
|
164
|
+
puts "Must be nice..." if confirm "Are you human?"
|
165
|
+
true
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
def ask(prompt="\n >",options={})
|
171
|
+
prompt = prompt.to_s + "? " if prompt.class == Symbol
|
172
|
+
print prompt.capitalize
|
173
|
+
|
174
|
+
if options[:validate]
|
175
|
+
input = gets.chomp
|
176
|
+
if options[:validate].downcase == 'numeric'
|
177
|
+
end
|
178
|
+
else
|
179
|
+
return gets.chomp
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_ask
|
184
|
+
title " Test Input Gathering "
|
185
|
+
name = ask "What's your name? "
|
186
|
+
puts "Thanks for answering, #{name}!"
|
187
|
+
age = ask "How old are you? "
|
188
|
+
puts "Great! It must be nice to be #{age} years old."
|
189
|
+
true
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
def tabulate(data, options={}); data.table options if %w[ Hash Array ].include? data.class.name; end
|
198
|
+
|
199
|
+
def test_table
|
200
|
+
title " Testing tables "
|
201
|
+
|
202
|
+
tabulate({:one => "1", :two => "2", :three => "3", :four => "4", :five => "512351234123"}); br 2
|
203
|
+
tabulate([[1,2,3,4],[4,5,6,7],[7,8,9,0]], {:headings => %w[ alpha beta gamma delta ], :width => 100} ); br 3
|
204
|
+
tabulate([[1,2,3,4],[4,5,6,7],[7,8,9,0]], {:headings => %w[ alpha beta gamma delta ], :width => 50} ); br 3
|
205
|
+
|
206
|
+
true
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
def unit_test
|
213
|
+
|
214
|
+
title " Textual Unit Test "
|
215
|
+
|
216
|
+
results = {}
|
217
|
+
|
218
|
+
%w[ title ask select confirm justify table ].each {
|
219
|
+
|operation| results[operation.to_sym] = send("test_"+operation)
|
220
|
+
}
|
221
|
+
=begin
|
222
|
+
results << {:title, test_title}
|
223
|
+
results << {:ask, test_ask}
|
224
|
+
results << {:select, test_select}
|
225
|
+
results << {:confirm, test_confirm}
|
226
|
+
results << {:justify, test_justify}
|
227
|
+
results << {:table, test_table}
|
228
|
+
=end
|
229
|
+
title " Internal Unit Test Results "
|
230
|
+
results.each_pair {|k,v| puts " #{k} => #{v==true ? 'pass' : 'fail'}" }
|
231
|
+
|
232
|
+
assertions = results.size
|
233
|
+
passed = results.select { |k,v| v=true }.size
|
234
|
+
failed = results.select { |k,v| v=false }.size
|
235
|
+
|
236
|
+
title " #{assertions} assertions, #{failed} failed, #{passed} passed "
|
237
|
+
|
238
|
+
return false if failed > 0
|
239
|
+
true
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def textify; include Textify; end
|
244
|
+
|
245
|
+
|
246
|
+
#################################
|
247
|
+
# Extensions to Hash
|
248
|
+
########
|
249
|
+
|
250
|
+
class Hash
|
251
|
+
textify
|
252
|
+
# include Textual
|
253
|
+
def self_analyze
|
254
|
+
@minimal_element, @maximal_element = nil,nil
|
255
|
+
@min_elem_sz, @max_elem_sz = 0, 0
|
256
|
+
each_pair do |k,v|
|
257
|
+
[k,v].each do |element|
|
258
|
+
if element.to_s.size > @max_elem_sz
|
259
|
+
@maximal_element = element; @max_elem_sz = element.to_s.size
|
260
|
+
elsif element.to_s.size < @min_elem_sz
|
261
|
+
@minimal_element = element; @min_elem_sz = element.to_s.size
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
# puts "[Hash.self_analyze] Maximal element => '#{@maximal_element}' (#{@max_elem_sz}), minimal element => '#{@minimal_element}'"
|
266
|
+
end
|
267
|
+
|
268
|
+
def table options={}
|
269
|
+
self_analyze
|
270
|
+
width = options[:width] || DefaultScreenWidth
|
271
|
+
min_column_width = @maximal_element.to_s.size + 2
|
272
|
+
column_width = [min_column_width, (width-3).to_f/2].max.to_i
|
273
|
+
width = column_width*2 + 3
|
274
|
+
hb = "*"*width+"\n"
|
275
|
+
result = hb
|
276
|
+
each_pair{|k,v| result += "*#{justify :center, k.to_s, :width => (column_width)}*#{justify :center, v, :width=>(column_width)}*\n"}
|
277
|
+
result += hb
|
278
|
+
puts result
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
####################
|
284
|
+
# Extensions to Array
|
285
|
+
#####
|
286
|
+
|
287
|
+
class Array
|
288
|
+
# include Textual
|
289
|
+
textify
|
290
|
+
|
291
|
+
def self_analyze
|
292
|
+
@minimal_element, @maximal_element = nil,nil
|
293
|
+
@min_elem_sz, @max_elem_sz = 0, 0
|
294
|
+
each do |row|
|
295
|
+
if row.is_a? Array
|
296
|
+
row.each do |element|
|
297
|
+
if element.to_s.size > @max_elem_sz
|
298
|
+
@maximal_element = element; @max_elem_sz = element.to_s.size
|
299
|
+
elsif element.to_s.size < @min_elem_sz
|
300
|
+
@minimal_element = element; @min_elem_sz = element.to_s.size
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
# puts "[Array.self_analyze] Maximal element => '#{@maximal_element}' (#{@max_elem_sz}), minimal element => '#{@minimal_element}'"
|
306
|
+
end
|
307
|
+
|
308
|
+
def table options = {}
|
309
|
+
width = options[:width] || DefaultScreenWidth
|
310
|
+
self_analyze
|
311
|
+
|
312
|
+
columns = self[0].size
|
313
|
+
|
314
|
+
min_column_width = @maximal_element.to_s.size + 2
|
315
|
+
column_width = [min_column_width, ((width.to_i-columns).to_f/columns)].max.to_i
|
316
|
+
width = (column_width*columns) + columns + 1
|
317
|
+
|
318
|
+
hb = ("*"*(width))+"\n"
|
319
|
+
result = hb
|
320
|
+
|
321
|
+
# puts "[Array.table] Columns = #{columns}. Total width = #{width}. Column width = #{column_width}."
|
322
|
+
|
323
|
+
if options[:headings] then
|
324
|
+
options[:headings].each{ |th| result += "*#{justify :center, th.to_s, :width => column_width}"}
|
325
|
+
result += "*\n"
|
326
|
+
result += hb
|
327
|
+
end
|
328
|
+
each do |row|
|
329
|
+
row.each do |item|
|
330
|
+
result += "*#{justify :center, item.to_s, :width => column_width}"
|
331
|
+
end
|
332
|
+
result += "*\n"
|
333
|
+
end
|
334
|
+
result += hb
|
335
|
+
|
336
|
+
puts result
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
#### sample interface unit test ##############
|
342
|
+
|
343
|
+
|
344
|
+
if __FILE__ == $0
|
345
|
+
class SampleUI; textify; def test_textify; unit_test; end; end
|
346
|
+
sample_user_interface = SampleUI.new
|
347
|
+
sample_user_interface.test_textify
|
348
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'textify'
|
3
|
+
|
4
|
+
class TestTextify < Test::Unit::TestCase
|
5
|
+
should "pass internal unit test" do
|
6
|
+
class SampleUI; textify; def test_textify; unit_test; end; end
|
7
|
+
sample_user_interface = SampleUI.new
|
8
|
+
passed_unit_test = sample_user_interface.test_textify
|
9
|
+
|
10
|
+
assert passed_unit_test
|
11
|
+
flunk "failed internal unit test!" if not passed_unit_test
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: textify
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joseph Weissman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-08 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A collection of simple, non-curses-based functions to assist building user interaction into text-only ruby scripts
|
17
|
+
email: jweissman1986@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README
|
25
|
+
- README.rdoc
|
26
|
+
files:
|
27
|
+
- lib/sample.rb
|
28
|
+
- lib/textify.rb
|
29
|
+
- LICENSE
|
30
|
+
- README
|
31
|
+
- README.rdoc
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/jweissman1986/Textify
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --charset=UTF-8
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.3.5
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: non-curses text-based library to aid user interaction
|
60
|
+
test_files:
|
61
|
+
- test/helper.rb
|
62
|
+
- test/test_textify.rb
|