epitools 0.5.134 → 0.5.137
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.
- checksums.yaml +4 -4
- data/Rakefile +5 -7
- data/VERSION +1 -1
- data/lib/epitools/autoloads.rb +1 -1
- data/lib/epitools/browser/cache.rb +1 -1
- data/lib/epitools/browser.rb +4 -4
- data/lib/epitools/clitools.rb +11 -4
- data/lib/epitools/core_ext/array.rb +2 -1
- data/lib/epitools/core_ext/date.rb +84 -0
- data/lib/epitools/core_ext/io.rb +8 -0
- data/lib/epitools/core_ext/misc.rb +5 -0
- data/lib/epitools/core_ext/numbers.rb +54 -12
- data/lib/epitools/core_ext/object.rb +4 -1
- data/lib/epitools/core_ext/string.rb +156 -5
- data/lib/epitools/core_ext.rb +2 -0
- data/lib/epitools/fraction.rb +131 -0
- data/lib/epitools/hexdump.rb +1 -1
- data/lib/epitools/mimemagic_tables.rb +1266 -1266
- data/lib/epitools/minimal.rb +2 -3
- data/lib/epitools/path.rb +33 -18
- data/lib/epitools/pretty_backtrace.rb +1 -1
- data/lib/epitools/rash.rb +1 -1
- data/lib/epitools/term.rb +73 -15
- data/spec/core_ext_spec.rb +53 -3
- data/spec/fraction_spec.rb +53 -0
- data/spec/numwords_spec.rb +2 -5
- data/spec/path_spec.rb +14 -4
- data/spec/sys_spec.rb +5 -0
- data/spec/zopen_spec.rb +37 -37
- metadata +8 -9
- data/lib/epitools/ratio.rb +0 -78
- /data/spec/{browser_spec.rb → manual/browser_spec.rb} +0 -0
- /data/spec/{wm_spec.rb → manual/wm_spec.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b483b258d704fbf993bbab386612c813ae695444da9badcd8797c4fcebdc877
|
|
4
|
+
data.tar.gz: dbdf278b2e463cf45803cd0278243ee1e2bf7737879bdd2fd403eb340a7a5613
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0585860f4e91323260513ed1c3e9cc6c49be6ceef4345a184d14b734ff61529c0bce4be6ffd2520e019d51cd9dc3f34a7895792c3c16da1cebd7b5b1ef2907b6'
|
|
7
|
+
data.tar.gz: 6459b4b90495b0c1f274bfa0053d99c386beba31beb0ce649c157cba7777155e96bed02255b108f096210062f95270bdba124207015cb8d62814ae1f863db0c9
|
data/Rakefile
CHANGED
|
@@ -9,7 +9,7 @@ task :release => :build do
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
task :install => :build do
|
|
12
|
-
system "gem install --
|
|
12
|
+
system "gem install --user epitools-#{gem_version}.gem"
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
task :pry do
|
|
@@ -17,13 +17,11 @@ task :pry do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
task :spec do
|
|
20
|
-
cmd = %w[rspec
|
|
20
|
+
cmd = %w[rspec --format documentation --force-color --pattern spec/*_spec.rb]
|
|
21
|
+
cmd.unshift "rescue" if system *%w[which rescue]
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
else
|
|
25
|
-
system *cmd
|
|
26
|
-
end
|
|
23
|
+
p cmd
|
|
24
|
+
system *cmd
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
task :default => :spec
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.5.
|
|
1
|
+
0.5.137
|
data/lib/epitools/autoloads.rb
CHANGED
|
@@ -69,7 +69,7 @@ autoload :Path, 'epitools/path'
|
|
|
69
69
|
autoload :Ezdb, 'epitools/ezdb'
|
|
70
70
|
autoload :Browser, 'epitools/browser'
|
|
71
71
|
autoload :Rash, 'epitools/rash'
|
|
72
|
-
autoload :
|
|
72
|
+
autoload :Fraction, 'epitools/fraction'
|
|
73
73
|
autoload :ProgressBar, 'epitools/progressbar'
|
|
74
74
|
autoload :Trie, 'epitools/trie'
|
|
75
75
|
autoload :MimeMagic, 'epitools/mimemagic'
|
data/lib/epitools/browser.rb
CHANGED
|
@@ -36,7 +36,7 @@ class Browser
|
|
|
36
36
|
# :use_logs => false, # Don't log the detailed transfer info
|
|
37
37
|
# :cookie_file => "cookies.txt" # Save cookies to file
|
|
38
38
|
#
|
|
39
|
-
def initialize(options
|
|
39
|
+
def initialize(**options)
|
|
40
40
|
@last_get = Time.at(0)
|
|
41
41
|
@delay = options[:delay] || 1
|
|
42
42
|
@delay_jitter = options[:delay_jitter] || 0.2
|
|
@@ -85,7 +85,7 @@ class Browser
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def load_cookies!
|
|
88
|
-
if File.
|
|
88
|
+
if File.exist? @cookie_file
|
|
89
89
|
agent.cookie_jar.load @cookie_file
|
|
90
90
|
true
|
|
91
91
|
else
|
|
@@ -126,7 +126,7 @@ class Browser
|
|
|
126
126
|
# Options:
|
|
127
127
|
# :cached => true/false | check cache before getting page
|
|
128
128
|
#
|
|
129
|
-
def get(url, options
|
|
129
|
+
def get(url, **options)
|
|
130
130
|
|
|
131
131
|
# TODO: Have a base-URL option
|
|
132
132
|
|
|
@@ -151,7 +151,7 @@ class Browser
|
|
|
151
151
|
if use_cache and page = cache.get(url)
|
|
152
152
|
puts " |_ cached (#{page.content_type})"
|
|
153
153
|
else
|
|
154
|
-
page = agent.get(url)
|
|
154
|
+
page = agent.get(url, [], options[:referer])
|
|
155
155
|
@last_get = Time.now
|
|
156
156
|
cache_put(page, url) if use_cache
|
|
157
157
|
end
|
data/lib/epitools/clitools.rb
CHANGED
|
@@ -56,6 +56,13 @@ rescue Errno::EPIPE, Interrupt
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
#
|
|
60
|
+
# Colorized puts (see: `String#colorize`)
|
|
61
|
+
#
|
|
62
|
+
def cputs(*args)
|
|
63
|
+
puts args.join("\n").colorize
|
|
64
|
+
end
|
|
65
|
+
|
|
59
66
|
#
|
|
60
67
|
# Execute a `system()` command using SQL-style escaped arguments.
|
|
61
68
|
#
|
|
@@ -179,11 +186,11 @@ end
|
|
|
179
186
|
def geoip(addr, city_data='/usr/share/GeoIP/GeoIPCity.dat', country_data='/usr/share/GeoIP/GeoIP.dat')
|
|
180
187
|
(
|
|
181
188
|
$geoip ||= begin
|
|
182
|
-
if city_data and File.
|
|
189
|
+
if city_data and File.exist? city_data
|
|
183
190
|
geo = GeoIP.new city_data
|
|
184
191
|
proc { |addr| geo.city(addr) }
|
|
185
192
|
|
|
186
|
-
elsif country_data and File.
|
|
193
|
+
elsif country_data and File.exist? country_data
|
|
187
194
|
geo = GeoIP.new country_data
|
|
188
195
|
proc { |addr| geo.country(addr) }
|
|
189
196
|
|
|
@@ -202,7 +209,7 @@ def which(*bins)
|
|
|
202
209
|
bins.flatten.each do |bin|
|
|
203
210
|
ENV["PATH"].split(":").each do |dir|
|
|
204
211
|
full_path = File.join(dir, bin)
|
|
205
|
-
return full_path if File.
|
|
212
|
+
return full_path if File.exist? full_path
|
|
206
213
|
end
|
|
207
214
|
end
|
|
208
215
|
nil
|
|
@@ -234,7 +241,7 @@ def curl(url)
|
|
|
234
241
|
curl_open(url).read
|
|
235
242
|
end
|
|
236
243
|
|
|
237
|
-
def curl_open(url, headers
|
|
244
|
+
def curl_open(url, **headers)
|
|
238
245
|
# headers["User-Agent"] ||= "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/85 Version/11.1.1 Safari/605.1.15"
|
|
239
246
|
cmd = ["curl", "-LSs"]
|
|
240
247
|
headers.each { |k,v| cmd += ["-H", "#{k}: #{v}"] }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'epitools/minimal'
|
|
1
2
|
|
|
2
3
|
class Array
|
|
3
4
|
|
|
@@ -266,7 +267,7 @@ class Array
|
|
|
266
267
|
# 5.0...9.0 => 5
|
|
267
268
|
# }
|
|
268
269
|
#
|
|
269
|
-
def histogram(n_buckets=10, options
|
|
270
|
+
def histogram(n_buckets=10, **options)
|
|
270
271
|
|
|
271
272
|
use_ranges = options[:ranges] || options[:hash]
|
|
272
273
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'epitools/minimal'
|
|
2
|
+
|
|
3
|
+
class Date
|
|
4
|
+
|
|
5
|
+
def self.find(direction=:backward, &block)
|
|
6
|
+
Date.today.find(direction, &block)
|
|
7
|
+
end
|
|
8
|
+
alias_class_method :search, :find
|
|
9
|
+
|
|
10
|
+
def self.find(direction=:backward, &block)
|
|
11
|
+
Date.today.find(direction, &block)
|
|
12
|
+
end
|
|
13
|
+
alias_class_method :search, :find
|
|
14
|
+
|
|
15
|
+
def self.find_bidirectional(&block)
|
|
16
|
+
Date.today.find_bidirectional(&block)
|
|
17
|
+
end
|
|
18
|
+
alias_class_method :find_both, :find_bidirectional
|
|
19
|
+
alias_class_method :search_both, :find_bidirectional
|
|
20
|
+
alias_class_method :search_bidirectional, :find_bidirectional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Returns an enumerator which searches through the calendar a day at a time, returning every date that match the block.
|
|
25
|
+
#
|
|
26
|
+
# Examples:
|
|
27
|
+
# >> Date.find(:forward) { year % 42 == 0 }
|
|
28
|
+
# >> Date.find(:backward) { month == 1 }
|
|
29
|
+
# >> Date.parse("1900-01-01").find_bidirectional { wednesday? }
|
|
30
|
+
# >> Date.today.find_forward { wednesday? and day == 4 }.take(10)
|
|
31
|
+
#
|
|
32
|
+
# (Note: the block doesn't need a parameter; every method you call is sent directly to the date object that's being evaluated)
|
|
33
|
+
#
|
|
34
|
+
def find(direction=:backward, &block)
|
|
35
|
+
raise ArgumentError.new("This method requires a block") unless block_given?
|
|
36
|
+
Enumerator.new do |y|
|
|
37
|
+
p = self
|
|
38
|
+
|
|
39
|
+
loop do
|
|
40
|
+
y << p if p.instance_eval(&block)
|
|
41
|
+
|
|
42
|
+
case direction
|
|
43
|
+
when :forward, :forwards
|
|
44
|
+
p = p.next_day
|
|
45
|
+
when :backward, :backwards
|
|
46
|
+
p = p.prev_day
|
|
47
|
+
else
|
|
48
|
+
raise "Error: Unknown date search direction #{direction.inspect}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
alias_method :search, :find
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def find_forwards(&block)
|
|
58
|
+
find(:forwards, &block)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def find_backwards(&block)
|
|
62
|
+
find(:backwards, &block)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def find_bidirectional(&block)
|
|
66
|
+
f = find(:forward, &block)
|
|
67
|
+
b = find(:backward, &block)
|
|
68
|
+
|
|
69
|
+
Enumerator.new do |y|
|
|
70
|
+
loop do
|
|
71
|
+
y << b.next
|
|
72
|
+
y << f.next
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
alias_method :find_backward, :find_backwards
|
|
78
|
+
alias_method :find_forward, :find_forwards
|
|
79
|
+
|
|
80
|
+
alias_method :find_both, :find_bidirectional
|
|
81
|
+
alias_method :search_both, :find_bidirectional
|
|
82
|
+
alias_method :search_bidirectional, :find_bidirectional
|
|
83
|
+
|
|
84
|
+
end
|
data/lib/epitools/core_ext/io.rb
CHANGED
|
@@ -33,17 +33,28 @@ class Numeric
|
|
|
33
33
|
# 12.clamp(0..100) #=> 12
|
|
34
34
|
# -38817112.clamp(0..100) #=> 0
|
|
35
35
|
#
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
unless method_defined? :clamp
|
|
37
|
+
def clamp(*args)
|
|
38
|
+
case args.size
|
|
39
|
+
when 0
|
|
40
|
+
raise "Error: clamp takes either a Range object or 2 arguments"
|
|
41
|
+
when 1
|
|
42
|
+
range = args.first
|
|
43
|
+
when 2
|
|
44
|
+
range = (args.first..args.second)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if self < range.first
|
|
48
|
+
range.first
|
|
49
|
+
elsif self >= range.last
|
|
50
|
+
if range.exclude_end?
|
|
51
|
+
range.last - 1
|
|
52
|
+
else
|
|
53
|
+
range.last
|
|
54
|
+
end
|
|
42
55
|
else
|
|
43
|
-
|
|
56
|
+
self
|
|
44
57
|
end
|
|
45
|
-
else
|
|
46
|
-
self
|
|
47
58
|
end
|
|
48
59
|
end
|
|
49
60
|
|
|
@@ -283,6 +294,16 @@ class Integer
|
|
|
283
294
|
end
|
|
284
295
|
alias_method :bits, :to_bits
|
|
285
296
|
|
|
297
|
+
#
|
|
298
|
+
# Convert this number to a Ruby-style string of platform-endian binary digits (eg: "0b011010101").
|
|
299
|
+
# Note: The `min_length` argument tells how long the string should be (padding the missing digits with 0's); defaults to 8.
|
|
300
|
+
#
|
|
301
|
+
def binary(min_length=8)
|
|
302
|
+
width = (log(2).ceil / min_length.to_f).ceil * min_length
|
|
303
|
+
"0b%0.#{width}b" % self
|
|
304
|
+
end
|
|
305
|
+
alias_method :bin, :binary
|
|
306
|
+
|
|
286
307
|
#
|
|
287
308
|
# Cached constants for encoding numbers into bases up to 64
|
|
288
309
|
#
|
|
@@ -443,20 +464,40 @@ end
|
|
|
443
464
|
class Float
|
|
444
465
|
|
|
445
466
|
#
|
|
446
|
-
# Convert the float to a rounded percentage string (eg: "42%").
|
|
467
|
+
# Convert the float to a rounded percentage string (eg: "42.5%").
|
|
447
468
|
# Its argument lets you specify how many decimals to display
|
|
448
469
|
#
|
|
449
470
|
# eg:
|
|
450
|
-
# > 0.32786243.percent # => "33%"
|
|
471
|
+
# > 0.32786243.percent # => "33.8%"
|
|
451
472
|
# > 0.32786243.percent(2) # => "32.79%"
|
|
452
473
|
#
|
|
453
|
-
def percent(decimals=
|
|
474
|
+
def percent(decimals=1)
|
|
454
475
|
"%0.#{decimals}f%%" % (self * 100)
|
|
455
476
|
end
|
|
456
477
|
|
|
457
478
|
end
|
|
458
479
|
|
|
459
480
|
|
|
481
|
+
class Rational
|
|
482
|
+
|
|
483
|
+
#
|
|
484
|
+
# Convert the float to a rounded percentage string (eg: "42.5%")
|
|
485
|
+
# (see: Float#percent)
|
|
486
|
+
#
|
|
487
|
+
def percent(decimals=1)
|
|
488
|
+
to_f.percent(decimals)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
#
|
|
492
|
+
# Alternate initializer (`Rational[1,2]`)
|
|
493
|
+
#
|
|
494
|
+
def self.[](*args)
|
|
495
|
+
Rational(*args)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
|
|
460
501
|
class Prime
|
|
461
502
|
|
|
462
503
|
#
|
|
@@ -485,3 +526,4 @@ end
|
|
|
485
526
|
def primes
|
|
486
527
|
Prime.instance
|
|
487
528
|
end
|
|
529
|
+
|
|
@@ -11,6 +11,9 @@ class Object
|
|
|
11
11
|
Hash[ vars.zip(vals) ]
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
def write_to(file)
|
|
15
|
+
open(file, "wb") { |f| f.write self.to_s }
|
|
16
|
+
end
|
|
14
17
|
|
|
15
18
|
#
|
|
16
19
|
# Gives you a copy of the object with its attributes changed to whatever was
|
|
@@ -36,7 +39,7 @@ class Object
|
|
|
36
39
|
#
|
|
37
40
|
# Good for chaining lots of operations together in a REPL.
|
|
38
41
|
#
|
|
39
|
-
def with(options
|
|
42
|
+
def with(**options)
|
|
40
43
|
if block_given?
|
|
41
44
|
yield self
|
|
42
45
|
else
|
|
@@ -14,6 +14,13 @@ class String
|
|
|
14
14
|
#
|
|
15
15
|
STOP_WORDS = %w[a cannot into our thus about co is ours to above could it ourselves together across down its out too after during itself over toward afterwards each last own towards again eg latter per under against either latterly perhaps until all else least rather up almost elsewhere less same upon alone enough ltd seem us along etc many seemed very already even may seeming via also ever me seems was although every meanwhile several we always everyone might she well among everything more should were amongst everywhere moreover since what an except most so whatever and few mostly some when another first much somehow whence any for must someone whenever anyhow former my something where anyone formerly myself sometime whereafter anything from namely sometimes whereas anywhere further neither somewhere whereby are had never still wherein around has nevertheless such whereupon as have next than wherever at he no that whether be hence nobody the whither became her none their which because here noone them while become hereafter nor themselves who becomes hereby not then whoever becoming herein nothing thence whole been hereupon now there whom before hers nowhere thereafter whose beforehand herself of thereby why behind him off therefore will being himself often therein with below his on thereupon within beside how once these without besides however one they would between i only this yet beyond ie onto those you both if or though your but in other through yours by inc others throughout yourself can indeed otherwise thru yourselves]
|
|
16
16
|
|
|
17
|
+
#
|
|
18
|
+
# Are there any non-whitespace characters in the string?
|
|
19
|
+
#
|
|
20
|
+
def any?
|
|
21
|
+
not blank?
|
|
22
|
+
end
|
|
23
|
+
|
|
17
24
|
#
|
|
18
25
|
# Convert \r\n to \n
|
|
19
26
|
#
|
|
@@ -21,6 +28,27 @@ class String
|
|
|
21
28
|
gsub("\r\n", "\n")
|
|
22
29
|
end
|
|
23
30
|
|
|
31
|
+
#
|
|
32
|
+
# Convert the string from one encoding into another (eg: convert_encoding('cp437', 'utf-8'))
|
|
33
|
+
#
|
|
34
|
+
def convert_encoding(from, to)
|
|
35
|
+
force_encoding(from).encode(to)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Convert the string to utf-8, forcing another encoding
|
|
40
|
+
#
|
|
41
|
+
def to_utf8(from="cp437")
|
|
42
|
+
convert_encoding(from, "utf-8")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Convert a string from cp437 into utf-8
|
|
47
|
+
#
|
|
48
|
+
def from_cp437
|
|
49
|
+
to_utf8
|
|
50
|
+
end
|
|
51
|
+
|
|
24
52
|
#
|
|
25
53
|
# Escape shell characters (globs, quotes, parens, etc.)
|
|
26
54
|
#
|
|
@@ -28,6 +56,14 @@ class String
|
|
|
28
56
|
Shellwords.escape(self)
|
|
29
57
|
end
|
|
30
58
|
|
|
59
|
+
#
|
|
60
|
+
# Do what a browser would do when you type something into the address bar
|
|
61
|
+
#
|
|
62
|
+
def urlescape
|
|
63
|
+
@@uri_parser ||= URI::RFC2396_Parser.new
|
|
64
|
+
@@uri_parser.escape(self)
|
|
65
|
+
end
|
|
66
|
+
|
|
31
67
|
#
|
|
32
68
|
# Remove redundant whitespaces (not including newlines).
|
|
33
69
|
#
|
|
@@ -115,7 +151,7 @@ class String
|
|
|
115
151
|
alias_method :chomp_lines, :each_chomped
|
|
116
152
|
|
|
117
153
|
|
|
118
|
-
def split_at(boundary, options
|
|
154
|
+
def split_at(boundary, **options)
|
|
119
155
|
include_boundary = options[:include_boundary] || false
|
|
120
156
|
|
|
121
157
|
boundary = Regexp.new(Regexp.escape(boundary)) if boundary.is_a?(String)
|
|
@@ -263,25 +299,61 @@ class String
|
|
|
263
299
|
enumerable :each_slice
|
|
264
300
|
|
|
265
301
|
#
|
|
266
|
-
#
|
|
302
|
+
# Implements the Infamous Caesar-Cipher. Unbreakable to this day.
|
|
267
303
|
#
|
|
268
304
|
def rot13
|
|
269
305
|
tr('n-za-mN-ZA-M', 'a-zA-Z')
|
|
270
306
|
end
|
|
271
307
|
|
|
308
|
+
#
|
|
309
|
+
# Implements the unfamous non-Caesar ciphers. Want to `rot` your string more or less than 13? You've come to the right place!
|
|
310
|
+
# (NOTE: Currently only works on American letters. No fancy accents or cyrillic letters please!)
|
|
311
|
+
#
|
|
312
|
+
def rot(amount=1)
|
|
313
|
+
chars.map do |c|
|
|
314
|
+
case c
|
|
315
|
+
when ?a..?z
|
|
316
|
+
(((c.ord - ?a.ord + amount) % 26) + ?a.ord).chr
|
|
317
|
+
when ?A..?Z
|
|
318
|
+
(((c.ord - ?A.ord + amount) % 26) + ?A.ord).chr
|
|
319
|
+
else
|
|
320
|
+
c
|
|
321
|
+
end
|
|
322
|
+
end.join
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
#
|
|
326
|
+
# Cache an `URI::RFC2396_Parser` instance, because it's slowwww to initialize
|
|
327
|
+
#
|
|
328
|
+
def _rfc2396_parser
|
|
329
|
+
@@rfc2396_parser ||= URI::RFC2396_Parser.new
|
|
330
|
+
end
|
|
331
|
+
|
|
272
332
|
#
|
|
273
333
|
# Convert non-URI characters into %XXes.
|
|
274
334
|
#
|
|
275
335
|
def urlencode
|
|
276
|
-
URI.escape(self)
|
|
336
|
+
#URI.escape(self)
|
|
337
|
+
_rfc2396_parser.escape(self)
|
|
277
338
|
end
|
|
278
339
|
|
|
279
340
|
#
|
|
280
341
|
# Convert an URI's %XXes into regular characters.
|
|
281
342
|
#
|
|
282
343
|
def urldecode
|
|
283
|
-
|
|
344
|
+
_rfc2396_parser.unescape(self)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
#
|
|
348
|
+
#
|
|
349
|
+
#
|
|
350
|
+
def to_bigdecimal
|
|
351
|
+
BigDecimal
|
|
352
|
+
BigDecimal(self)
|
|
284
353
|
end
|
|
354
|
+
alias_method :to_d, :to_bigdecimal
|
|
355
|
+
alias_method :to_dec, :to_bigdecimal
|
|
356
|
+
alias_method :to_decimal, :to_bigdecimal
|
|
285
357
|
|
|
286
358
|
#
|
|
287
359
|
# URI.parse the string and return an URI object
|
|
@@ -317,6 +389,13 @@ class String
|
|
|
317
389
|
bs.with_index.inject(0) { |sum,(b,i)| (b << (8*i)) + sum }
|
|
318
390
|
end
|
|
319
391
|
|
|
392
|
+
#
|
|
393
|
+
# Convert the bytes of this string to hexadecimcal digits
|
|
394
|
+
#
|
|
395
|
+
def to_hex
|
|
396
|
+
unpack("H*").first
|
|
397
|
+
end
|
|
398
|
+
|
|
320
399
|
#
|
|
321
400
|
# Cached constants for base conversion.
|
|
322
401
|
#
|
|
@@ -371,6 +450,14 @@ class String
|
|
|
371
450
|
BEncode.load(self)
|
|
372
451
|
end
|
|
373
452
|
|
|
453
|
+
#
|
|
454
|
+
# CRC32 the string
|
|
455
|
+
#
|
|
456
|
+
def crc32
|
|
457
|
+
Zlib.crc32(self).to_hex
|
|
458
|
+
end
|
|
459
|
+
alias_method :crc, :crc32
|
|
460
|
+
|
|
374
461
|
#
|
|
375
462
|
# MD5 the string
|
|
376
463
|
#
|
|
@@ -392,6 +479,8 @@ class String
|
|
|
392
479
|
Digest::SHA256.hexdigest self
|
|
393
480
|
end
|
|
394
481
|
|
|
482
|
+
|
|
483
|
+
|
|
395
484
|
#
|
|
396
485
|
# gzip the string
|
|
397
486
|
#
|
|
@@ -456,7 +545,7 @@ class String
|
|
|
456
545
|
# Unmarshal the string (transform it into Ruby datatypes).
|
|
457
546
|
#
|
|
458
547
|
def unmarshal
|
|
459
|
-
Marshal.
|
|
548
|
+
Marshal.load self
|
|
460
549
|
end
|
|
461
550
|
alias_method :from_marshal, :unmarshal
|
|
462
551
|
|
|
@@ -495,6 +584,68 @@ class String
|
|
|
495
584
|
nums_and_units.map { |num, units| num.send(units) }.sum
|
|
496
585
|
end
|
|
497
586
|
|
|
587
|
+
|
|
588
|
+
#
|
|
589
|
+
# Translate numbers with units (like 25k, 150GB, 15%, 5 hours) into their expanded numeric value
|
|
590
|
+
#
|
|
591
|
+
def parse_units
|
|
592
|
+
# extract the unit suffix
|
|
593
|
+
if self =~ /(\d[\d_]*(?:\.\d+)?)\s*([a-zA-Z]+\b|%(?= \s|$))/
|
|
594
|
+
units = $2.downcase
|
|
595
|
+
num = $1 #.to_f
|
|
596
|
+
num = num["."] ? num.to_f : num.to_i
|
|
597
|
+
|
|
598
|
+
case units
|
|
599
|
+
when "%"
|
|
600
|
+
# 0.01
|
|
601
|
+
num / 100.0
|
|
602
|
+
when "k"
|
|
603
|
+
# 10**3
|
|
604
|
+
num.thousand
|
|
605
|
+
when "m", "mm"
|
|
606
|
+
# 10**6
|
|
607
|
+
num.million
|
|
608
|
+
when "b", "bn"
|
|
609
|
+
# 10**9
|
|
610
|
+
num.billion
|
|
611
|
+
when "gib", "gb", "g"
|
|
612
|
+
num * 2**30
|
|
613
|
+
when "mib", "mb"
|
|
614
|
+
num * 2**20
|
|
615
|
+
when "kib", "kb"
|
|
616
|
+
num * 2**10
|
|
617
|
+
when "t", "tb"
|
|
618
|
+
# 10**12
|
|
619
|
+
num.trillion
|
|
620
|
+
when "q"
|
|
621
|
+
# 10**15
|
|
622
|
+
num.quadrillion
|
|
623
|
+
when "Q"
|
|
624
|
+
# 10**18
|
|
625
|
+
num.quintillion
|
|
626
|
+
when "min"
|
|
627
|
+
# 1.minute
|
|
628
|
+
num.minutes
|
|
629
|
+
when "hours", "h", "hr", "hrs"
|
|
630
|
+
# 1.hour
|
|
631
|
+
num.hours
|
|
632
|
+
when "d", "days", "dy"
|
|
633
|
+
num.days
|
|
634
|
+
else
|
|
635
|
+
raise "Invalid units: #{units.inspect}, in: #{self.inspect}"
|
|
636
|
+
end
|
|
637
|
+
else
|
|
638
|
+
raise "Couldn't find any units to parse! (expecting: '<a number><some letters>')"
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
alias_method :from_units, :parse_units
|
|
643
|
+
alias_method :from_human, :parse_units
|
|
644
|
+
alias_method :from_size, :parse_units
|
|
645
|
+
alias_method :from_percent, :parse_units
|
|
646
|
+
alias_method :from_time, :parse_units
|
|
647
|
+
|
|
648
|
+
|
|
498
649
|
#
|
|
499
650
|
# Print a hexdump of the string to STDOUT (coloured, if the terminal supports it)
|
|
500
651
|
#
|
data/lib/epitools/core_ext.rb
CHANGED
|
@@ -15,7 +15,9 @@ require 'epitools/core_ext/hash'
|
|
|
15
15
|
require 'epitools/core_ext/numbers'
|
|
16
16
|
require 'epitools/core_ext/truthiness'
|
|
17
17
|
require 'epitools/core_ext/range'
|
|
18
|
+
|
|
18
19
|
require 'epitools/core_ext/time'
|
|
20
|
+
require 'epitools/core_ext/date'
|
|
19
21
|
|
|
20
22
|
require 'epitools/core_ext/argv'
|
|
21
23
|
require 'epitools/core_ext/file'
|