epitools 0.4.49 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/TODO +16 -0
- data/VERSION +1 -1
- data/epitools.gemspec +14 -7
- data/lib/epitools.rb +3 -2
- data/lib/epitools/autoloads.rb +11 -1
- data/lib/epitools/basetypes.rb +220 -71
- data/lib/epitools/browser.rb +1 -0
- data/lib/epitools/clitools.rb +7 -3
- data/lib/epitools/colored.rb +45 -23
- data/lib/epitools/ezdb.rb +100 -0
- data/lib/epitools/path.rb +354 -80
- data/lib/epitools/term.rb +147 -0
- data/lib/epitools/trie.rb +422 -0
- data/lib/epitools/zopen.rb +2 -2
- data/spec/basetypes_spec.rb +42 -2
- data/spec/ezdb_spec.rb +47 -0
- data/spec/path_spec.rb +49 -3
- data/spec/rash_spec.rb +1 -1
- data/spec/term_spec.rb +35 -0
- metadata +16 -11
data/TODO
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
===========================
|
2
|
+
TODOs
|
3
|
+
===========================
|
4
|
+
|
5
|
+
+ Logger
|
6
|
+
|_ colourized ("ramaze" has a nice style)
|
7
|
+
|_ output to STDERR (default)
|
8
|
+
|_ no setup (default)
|
9
|
+
|_ Log "message" <- defaults to info
|
10
|
+
|_ Log.warn "message"
|
11
|
+
|_ Log object <- prettyprints it
|
12
|
+
|_ Verbose mode shows the calling method's info
|
13
|
+
|_ eg: [file:line:method]
|
14
|
+
|_ "gsmartcontrol" has good warning messages
|
15
|
+
ie: "|13<warn> |07[hz] Warning: exit: The device error log contains records of errors."
|
16
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/epitools.gemspec
CHANGED
@@ -5,22 +5,24 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{epitools}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-
|
11
|
+
s.authors = [%q{epitron}]
|
12
|
+
s.date = %q{2011-10-13}
|
13
13
|
s.description = %q{Miscellaneous utility libraries to make my life easier.}
|
14
14
|
s.email = %q{chris@ill-logic.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.rdoc"
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
21
22
|
"LICENSE",
|
22
23
|
"README.rdoc",
|
23
24
|
"Rakefile",
|
25
|
+
"TODO",
|
24
26
|
"VERSION",
|
25
27
|
"epitools.gemspec",
|
26
28
|
"lib/epitools.rb",
|
@@ -31,6 +33,7 @@ Gem::Specification.new do |s|
|
|
31
33
|
"lib/epitools/browser/mechanize_progressbar.rb",
|
32
34
|
"lib/epitools/clitools.rb",
|
33
35
|
"lib/epitools/colored.rb",
|
36
|
+
"lib/epitools/ezdb.rb",
|
34
37
|
"lib/epitools/hexdump.rb",
|
35
38
|
"lib/epitools/its.rb",
|
36
39
|
"lib/epitools/lcs.rb",
|
@@ -47,12 +50,15 @@ Gem::Specification.new do |s|
|
|
47
50
|
"lib/epitools/ratio.rb",
|
48
51
|
"lib/epitools/string_to_proc.rb",
|
49
52
|
"lib/epitools/sys.rb",
|
53
|
+
"lib/epitools/term.rb",
|
54
|
+
"lib/epitools/trie.rb",
|
50
55
|
"lib/epitools/zopen.rb",
|
51
56
|
"spec/autoreq_spec.rb",
|
52
57
|
"spec/basetypes_spec.rb",
|
53
58
|
"spec/browser_spec.rb",
|
54
59
|
"spec/clitools_spec.rb",
|
55
60
|
"spec/colored_spec.rb",
|
61
|
+
"spec/ezdb_spec.rb",
|
56
62
|
"spec/lcs_spec.rb",
|
57
63
|
"spec/numwords_spec.rb",
|
58
64
|
"spec/path_spec.rb",
|
@@ -62,12 +68,13 @@ Gem::Specification.new do |s|
|
|
62
68
|
"spec/spec.opts",
|
63
69
|
"spec/spec_helper.rb",
|
64
70
|
"spec/sys_spec.rb",
|
71
|
+
"spec/term_spec.rb",
|
65
72
|
"spec/zopen_spec.rb"
|
66
73
|
]
|
67
74
|
s.homepage = %q{http://github.com/epitron/epitools}
|
68
|
-
s.licenses = [
|
69
|
-
s.require_paths = [
|
70
|
-
s.rubygems_version = %q{1.6
|
75
|
+
s.licenses = [%q{WTFPL}]
|
76
|
+
s.require_paths = [%q{lib}]
|
77
|
+
s.rubygems_version = %q{1.8.6}
|
71
78
|
s.summary = %q{NOT UTILS... METILS!}
|
72
79
|
|
73
80
|
if s.respond_to? :specification_version then
|
data/lib/epitools.rb
CHANGED
data/lib/epitools/autoloads.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
## Standard library
|
2
|
+
autoload :Set, 'set'
|
2
3
|
autoload :URI, 'uri'
|
3
4
|
autoload :CGI, 'cgi'
|
4
5
|
autoload :Base64, 'base64'
|
@@ -10,6 +11,10 @@ autoload :Tempfile, 'tempfile'
|
|
10
11
|
autoload :BigDecimal, 'bigdecimal'
|
11
12
|
autoload :StringIO, 'stringio'
|
12
13
|
autoload :Curses, 'curses'
|
14
|
+
autoload :DateTime, 'date'
|
15
|
+
autoload :Date, 'date'
|
16
|
+
autoload :Open3, 'open3'
|
17
|
+
#autoload :DelegateClass, 'delegate'
|
13
18
|
|
14
19
|
module Digest
|
15
20
|
autoload :SHA1, 'digest/sha1'
|
@@ -18,11 +23,16 @@ module Digest
|
|
18
23
|
end
|
19
24
|
|
20
25
|
## Nonstandard library
|
21
|
-
autoload :MimeMagic, 'epitools/mimemagic'
|
22
26
|
autoload :Path, 'epitools/path'
|
27
|
+
autoload :Ezdb, 'epitools/ezdb'
|
23
28
|
autoload :Browser, 'epitools/browser'
|
24
29
|
autoload :Rash, 'epitools/rash'
|
25
30
|
autoload :Ratio, 'epitools/ratio'
|
26
31
|
autoload :Sys, 'epitools/sys'
|
27
32
|
autoload :ProgressBar, 'epitools/progressbar'
|
33
|
+
autoload :Trie, 'epitools/trie'
|
34
|
+
autoload :MimeMagic, 'epitools/mimemagic'
|
35
|
+
autoload :Term, 'epitools/term'
|
28
36
|
|
37
|
+
## Gems
|
38
|
+
autoreq :ANSI, 'ansi'
|
data/lib/epitools/basetypes.rb
CHANGED
@@ -15,6 +15,8 @@ unless defined? Enum
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
RbConfig = Config unless defined? RbConfig
|
19
|
+
|
18
20
|
class Object
|
19
21
|
|
20
22
|
#
|
@@ -32,7 +34,22 @@ class Object
|
|
32
34
|
#
|
33
35
|
# `truthy?` means `not blank?`
|
34
36
|
#
|
35
|
-
def truthy
|
37
|
+
def truthy?
|
38
|
+
if respond_to? :blank?
|
39
|
+
not blank?
|
40
|
+
else
|
41
|
+
not nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def marshal
|
46
|
+
Marshal.dump self
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Lets you say: `object.is_an? Array`
|
51
|
+
#
|
52
|
+
alias_method :is_an?, :is_a?
|
36
53
|
|
37
54
|
end
|
38
55
|
|
@@ -44,7 +61,31 @@ class FalseClass
|
|
44
61
|
def truthy?; false; end
|
45
62
|
end
|
46
63
|
|
64
|
+
class Float
|
65
|
+
#
|
66
|
+
# 'true' if the float is 0.0
|
67
|
+
#
|
68
|
+
def blank?; self == 0.0; end
|
69
|
+
end
|
70
|
+
|
71
|
+
class NilClass
|
72
|
+
#
|
73
|
+
# Always 'true'; nil is considered blank.
|
74
|
+
#
|
75
|
+
def blank?; true; end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Symbol
|
79
|
+
#
|
80
|
+
# Symbols are never blank.
|
81
|
+
#
|
82
|
+
def blank?; false; end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
47
87
|
class Numeric
|
88
|
+
|
48
89
|
def integer?; true; end
|
49
90
|
|
50
91
|
def truthy?; self > 0; end
|
@@ -53,8 +94,9 @@ class Numeric
|
|
53
94
|
to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
|
54
95
|
end
|
55
96
|
|
56
|
-
#
|
57
|
-
|
97
|
+
#
|
98
|
+
# Time methods
|
99
|
+
#
|
58
100
|
{
|
59
101
|
|
60
102
|
'second' => 1,
|
@@ -77,29 +119,95 @@ class Numeric
|
|
77
119
|
def from_now
|
78
120
|
Time.now + self
|
79
121
|
end
|
122
|
+
|
80
123
|
end
|
81
124
|
|
82
|
-
class
|
125
|
+
class Integer
|
126
|
+
|
83
127
|
#
|
84
|
-
# 'true' if the
|
128
|
+
# 'true' if the integer is 0
|
85
129
|
#
|
86
|
-
def blank?; self == 0
|
87
|
-
end
|
130
|
+
def blank?; self == 0; end
|
88
131
|
|
89
|
-
class NilClass
|
90
132
|
#
|
91
|
-
#
|
133
|
+
# Convert the number into a hexadecimal string representation.
|
134
|
+
# (Identical to to_s(16), except that numbers < 16 will have a 0 in front of them.)
|
92
135
|
#
|
93
|
-
def
|
94
|
-
|
136
|
+
def to_hex
|
137
|
+
"%0.2x" % self
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Convert the number to an array of bits (least significant digit first, or little-endian).
|
142
|
+
#
|
143
|
+
def to_bits
|
144
|
+
# TODO: Why does thos go into an infinite loop in 1.8.7?
|
145
|
+
("%b" % self).chars.to_a.reverse.map(&:to_i)
|
146
|
+
end
|
147
|
+
alias_method :bits, :to_bits
|
148
|
+
|
149
|
+
#
|
150
|
+
# Cached constants for base62 encoding
|
151
|
+
#
|
152
|
+
BASE62_DIGITS = ['0'..'9', 'A'..'Z', 'a'..'z'].map(&:to_a).flatten
|
153
|
+
BASE62_BASE = BASE62_DIGITS.size
|
95
154
|
|
96
|
-
class Symbol
|
97
155
|
#
|
98
|
-
#
|
156
|
+
# Convert a number to a string representation (in "base62" encoding).
|
157
|
+
#
|
158
|
+
# Base62 encoding represents the number using the characters: 0..9, A..Z, a..z
|
99
159
|
#
|
100
|
-
|
160
|
+
# It's the same scheme that url shorteners and YouTube uses for their
|
161
|
+
# ID strings. (eg: http://www.youtube.com/watch?v=dQw4w9WgXcQ)
|
162
|
+
#
|
163
|
+
def to_base62
|
164
|
+
result = []
|
165
|
+
remainder = self
|
166
|
+
max_power = ( Math.log(self) / Math.log(BASE62_BASE) ).floor
|
167
|
+
|
168
|
+
max_power.downto(0) do |power|
|
169
|
+
divisor = BASE62_BASE**power
|
170
|
+
#p [:div, divisor, :rem, remainder]
|
171
|
+
digit, remainder = remainder.divmod(divisor)
|
172
|
+
result << digit
|
173
|
+
end
|
174
|
+
|
175
|
+
result << remainder if remainder > 0
|
176
|
+
|
177
|
+
result.map{|digit| BASE62_DIGITS[digit]}.join ''
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
#
|
183
|
+
# Monkeypatch [] into Bignum and Fixnum using class_eval.
|
184
|
+
#
|
185
|
+
# (This is necessary because [] is defined directly on the classes, and a mixin
|
186
|
+
# module will still be overridden by Big/Fixnum's native [] method.)
|
187
|
+
#
|
188
|
+
[Bignum, Fixnum].each do |klass|
|
189
|
+
|
190
|
+
klass.class_eval do
|
191
|
+
|
192
|
+
alias_method :bit, :"[]"
|
193
|
+
|
194
|
+
#
|
195
|
+
# Extends [] so that Integers can be sliced as if they were arrays.
|
196
|
+
#
|
197
|
+
def [](arg)
|
198
|
+
case arg
|
199
|
+
when Integer
|
200
|
+
self.bit(arg)
|
201
|
+
when Range
|
202
|
+
self.bits[arg]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
101
208
|
end
|
102
209
|
|
210
|
+
|
103
211
|
class String
|
104
212
|
|
105
213
|
#
|
@@ -141,7 +249,7 @@ class String
|
|
141
249
|
def tighten
|
142
250
|
gsub(/[\t ]+/,' ').strip
|
143
251
|
end
|
144
|
-
|
252
|
+
|
145
253
|
#
|
146
254
|
# Remove redundant whitespace AND newlines.
|
147
255
|
#
|
@@ -149,6 +257,14 @@ class String
|
|
149
257
|
gsub(/\s+/,' ').strip
|
150
258
|
end
|
151
259
|
|
260
|
+
#
|
261
|
+
# Remove ANSI color codes.
|
262
|
+
#
|
263
|
+
def strip_color
|
264
|
+
gsub(/\e\[.*?(\d)+m/, '')
|
265
|
+
end
|
266
|
+
alias_method :strip_ansi, :strip_color
|
267
|
+
|
152
268
|
#
|
153
269
|
# Like #lines, but skips empty lines and removes \n's.
|
154
270
|
#
|
@@ -157,6 +273,7 @@ class String
|
|
157
273
|
split($/).select{|l| not l.blank? }
|
158
274
|
end
|
159
275
|
|
276
|
+
alias_method :nicelines, :nice_lines
|
160
277
|
alias_method :clean_lines, :nice_lines
|
161
278
|
|
162
279
|
#
|
@@ -194,20 +311,51 @@ class String
|
|
194
311
|
end
|
195
312
|
end
|
196
313
|
|
314
|
+
|
315
|
+
|
316
|
+
#
|
317
|
+
# Cached constants for base62 decoding.
|
318
|
+
#
|
319
|
+
BASE62_DIGITS = Hash[ Integer::BASE62_DIGITS.map.with_index{|letter,index| [letter,index]} ]
|
320
|
+
BASE62_BASE = Integer::BASE62_BASE
|
321
|
+
|
322
|
+
#
|
323
|
+
# Convert a string (encoded in base16 "hex" -- for example, an MD5 or SHA1 hash)
|
324
|
+
# into "base62" format. (See Integer#to_base62 for more info.)
|
325
|
+
#
|
326
|
+
def to_base62
|
327
|
+
to_i(16).to_base62
|
328
|
+
end
|
329
|
+
|
330
|
+
#
|
331
|
+
# Convert a string encoded in base62 into an integer.
|
332
|
+
# (See Integer#to_base62 for more info.)
|
333
|
+
#
|
334
|
+
def from_base62
|
335
|
+
accumulator = 0
|
336
|
+
digits = chars.map { |c| BASE62_DIGITS[c] }.reverse
|
337
|
+
digits.each_with_index do |digit, power|
|
338
|
+
accumulator += (BASE62_BASE**power) * digit if digit > 0
|
339
|
+
end
|
340
|
+
accumulator
|
341
|
+
end
|
342
|
+
|
197
343
|
#
|
198
344
|
# Decode a mime64/base64 encoded string
|
199
345
|
#
|
200
|
-
def
|
346
|
+
def from_base64
|
201
347
|
Base64.decode64 self
|
202
348
|
end
|
349
|
+
alias_method :decode64, :from_base64
|
203
350
|
|
204
351
|
#
|
205
352
|
# Encode into a mime64/base64 string
|
206
353
|
#
|
207
|
-
def
|
354
|
+
def to_base64
|
208
355
|
Base64.encode64 self
|
209
356
|
end
|
210
|
-
alias_method :base64,
|
357
|
+
alias_method :base64, :to_base64
|
358
|
+
alias_method :encode64, :to_base64
|
211
359
|
|
212
360
|
#
|
213
361
|
# MD5 the string
|
@@ -274,59 +422,16 @@ class String
|
|
274
422
|
JSON.parse self
|
275
423
|
end
|
276
424
|
|
277
|
-
end
|
278
|
-
|
279
|
-
|
280
|
-
class Integer
|
281
|
-
|
282
|
-
#
|
283
|
-
# 'true' if the integer is 0
|
284
|
-
#
|
285
|
-
def blank?; self == 0; end
|
286
|
-
|
287
425
|
#
|
288
|
-
# Convert the
|
426
|
+
# Convert the string to a Path object.
|
289
427
|
#
|
290
|
-
def
|
291
|
-
|
428
|
+
def as_path
|
429
|
+
Path[self]
|
292
430
|
end
|
293
|
-
|
294
|
-
#
|
295
|
-
# Convert the number to an array of bits (least significant digit first, or little-endian).
|
296
|
-
#
|
297
|
-
def to_bits
|
298
|
-
# TODO: Why does thos go into an infinite loop in 1.8.7?
|
299
|
-
("%b" % self).chars.to_a.reverse.map(&:to_i)
|
300
|
-
end
|
301
|
-
alias_method :bits, :to_bits
|
302
|
-
|
303
|
-
end
|
304
|
-
|
305
|
-
|
306
|
-
#
|
307
|
-
# Monkeypatch [] into Bignum and Fixnum using class_eval.
|
308
|
-
#
|
309
|
-
# (This is necessary because [] is defined directly on the classes, and a mixin
|
310
|
-
# module will still be overridden by Big/Fixnum's native [] method.)
|
311
|
-
#
|
312
|
-
[Bignum, Fixnum].each do |klass|
|
431
|
+
alias_method :to_p, :as_path
|
313
432
|
|
314
|
-
|
315
|
-
|
316
|
-
alias_method :bit, :"[]"
|
317
|
-
|
318
|
-
#
|
319
|
-
# Extends [] so that Integers can be sliced as if they were arrays.
|
320
|
-
#
|
321
|
-
def [](arg)
|
322
|
-
case arg
|
323
|
-
when Integer
|
324
|
-
self.bit(arg)
|
325
|
-
when Range
|
326
|
-
self.bits[arg]
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
433
|
+
def unmarshal
|
434
|
+
Marshal.restore self
|
330
435
|
end
|
331
436
|
|
332
437
|
end
|
@@ -392,6 +497,21 @@ class Array
|
|
392
497
|
(self | other) - (self & other)
|
393
498
|
end
|
394
499
|
|
500
|
+
#
|
501
|
+
# Pick a random element.
|
502
|
+
#
|
503
|
+
def pick
|
504
|
+
self[rand(size)]
|
505
|
+
end
|
506
|
+
|
507
|
+
#
|
508
|
+
# Divide the array into n pieces.
|
509
|
+
#
|
510
|
+
def / pieces
|
511
|
+
piece_size = (size.to_f / pieces).ceil
|
512
|
+
each_slice(piece_size).to_a
|
513
|
+
end
|
514
|
+
|
395
515
|
end
|
396
516
|
|
397
517
|
|
@@ -417,6 +537,7 @@ module Enumerable
|
|
417
537
|
# (default: false)
|
418
538
|
# :after => true #=> split after the matched element (only has an effect when used with :include_boundary)
|
419
539
|
# (default: false)
|
540
|
+
# :once => flase #=> only perform one split (default: false)
|
420
541
|
#
|
421
542
|
# Examples:
|
422
543
|
# [1,2,3,4,5].split{ |e| e == 3 }
|
@@ -448,10 +569,13 @@ module Enumerable
|
|
448
569
|
|
449
570
|
chunks = []
|
450
571
|
current_chunk = []
|
572
|
+
|
573
|
+
splits = 0
|
574
|
+
max_splits = options[:once] == true ? 1 : options[:max_splits]
|
451
575
|
|
452
576
|
each do |e|
|
453
577
|
|
454
|
-
if boundary_test_proc.call(e)
|
578
|
+
if boundary_test_proc.call(e) and (max_splits == nil or splits < max_splits)
|
455
579
|
|
456
580
|
if current_chunk.empty? and not include_boundary
|
457
581
|
next # hit 2 boundaries in a row... just keep moving, people!
|
@@ -468,6 +592,8 @@ module Enumerable
|
|
468
592
|
current_chunk = [] # start a new result
|
469
593
|
current_chunk << e if include_boundary # include the boundary, if necessary
|
470
594
|
end
|
595
|
+
|
596
|
+
splits += 1
|
471
597
|
|
472
598
|
else
|
473
599
|
current_chunk << e
|
@@ -507,7 +633,11 @@ module Enumerable
|
|
507
633
|
# Sum the elements
|
508
634
|
#
|
509
635
|
def sum
|
510
|
-
|
636
|
+
if block_given?
|
637
|
+
inject(0) { |total,elem| total + yield(elem) }
|
638
|
+
else
|
639
|
+
inject(0) { |total,elem| total + elem }
|
640
|
+
end
|
511
641
|
end
|
512
642
|
|
513
643
|
#
|
@@ -656,11 +786,12 @@ class Object
|
|
656
786
|
#
|
657
787
|
def bench(message=nil)
|
658
788
|
start = Time.now
|
659
|
-
yield
|
789
|
+
result = yield
|
660
790
|
elapsed = Time.now - start
|
661
791
|
|
662
792
|
print "[#{message}] " if message
|
663
|
-
puts "elapsed time: %0.5fs" % elapsed
|
793
|
+
puts "elapsed time: %0.5fs" % elapsed
|
794
|
+
result
|
664
795
|
end
|
665
796
|
alias time bench
|
666
797
|
|
@@ -937,3 +1068,21 @@ def dmsg(msg)
|
|
937
1068
|
end
|
938
1069
|
|
939
1070
|
|
1071
|
+
def del(x)
|
1072
|
+
case thing
|
1073
|
+
when String
|
1074
|
+
del(x.to_sym)
|
1075
|
+
when Class, Module
|
1076
|
+
Object.send(:remove_const, x)
|
1077
|
+
when Method
|
1078
|
+
x.owner.send(:undef_method, x.name)
|
1079
|
+
when Symbol
|
1080
|
+
if Object.const_get(x)
|
1081
|
+
Object.send(:remove_const, x)
|
1082
|
+
elsif method(x)
|
1083
|
+
undef_method x
|
1084
|
+
end
|
1085
|
+
else
|
1086
|
+
raise "Error: don't know how to 'del #{x.inspect}'"
|
1087
|
+
end
|
1088
|
+
end
|