epitools 0.5.134 → 0.5.136
Sign up to get free protection for your applications and to get access to all the features.
- 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 +1 -1
- data/lib/epitools/core_ext/numbers.rb +34 -3
- data/lib/epitools/core_ext/object.rb +4 -1
- data/lib/epitools/core_ext/string.rb +99 -3
- data/lib/epitools/fraction.rb +131 -0
- data/lib/epitools/hexdump.rb +1 -1
- 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 +26 -2
- 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 +7 -6
- 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: 4c080c53832ae049dd0ac7abedc41844d515b759fd081f4541855359a21eb75a
|
4
|
+
data.tar.gz: f6871df3e1da9c2cdb315ab49a5c76cea1eb6dfc8f776d6a3f44b3e6d59844fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d93c4452eb2e0537265eb95497bf6a0ca9d9022f2ab099972a64904465eccabbe0d3f1901ff2b711940e4603bb6e7afd7c56f7b9b93eea4d9d7762f6453ed3ee
|
7
|
+
data.tar.gz: 75a2d4e10cf13071f4a2cab7239f6a2db7c3b0bb66a68ce603c75bbe5051c15d742308d9f370cc9fb045e6db2492942f8cd37a65db40ba235655e1ea4d429ac8
|
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.136
|
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}"] }
|
@@ -283,6 +283,16 @@ class Integer
|
|
283
283
|
end
|
284
284
|
alias_method :bits, :to_bits
|
285
285
|
|
286
|
+
#
|
287
|
+
# Convert this number to a Ruby-style string of platform-endian binary digits (eg: "0b011010101").
|
288
|
+
# Note: The `min_length` argument tells how long the string should be (padding the missing digits with 0's); defaults to 8.
|
289
|
+
#
|
290
|
+
def binary(min_length=8)
|
291
|
+
width = (log(2).ceil / min_length.to_f).ceil * min_length
|
292
|
+
"0b%0.#{width}b" % self
|
293
|
+
end
|
294
|
+
alias_method :bin, :binary
|
295
|
+
|
286
296
|
#
|
287
297
|
# Cached constants for encoding numbers into bases up to 64
|
288
298
|
#
|
@@ -443,20 +453,40 @@ end
|
|
443
453
|
class Float
|
444
454
|
|
445
455
|
#
|
446
|
-
# Convert the float to a rounded percentage string (eg: "42%").
|
456
|
+
# Convert the float to a rounded percentage string (eg: "42.5%").
|
447
457
|
# Its argument lets you specify how many decimals to display
|
448
458
|
#
|
449
459
|
# eg:
|
450
|
-
# > 0.32786243.percent # => "33%"
|
460
|
+
# > 0.32786243.percent # => "33.8%"
|
451
461
|
# > 0.32786243.percent(2) # => "32.79%"
|
452
462
|
#
|
453
|
-
def percent(decimals=
|
463
|
+
def percent(decimals=1)
|
454
464
|
"%0.#{decimals}f%%" % (self * 100)
|
455
465
|
end
|
456
466
|
|
457
467
|
end
|
458
468
|
|
459
469
|
|
470
|
+
class Rational
|
471
|
+
|
472
|
+
#
|
473
|
+
# Convert the float to a rounded percentage string (eg: "42.5%")
|
474
|
+
# (see: Float#percent)
|
475
|
+
#
|
476
|
+
def percent(decimals=1)
|
477
|
+
to_f.percent(decimals)
|
478
|
+
end
|
479
|
+
|
480
|
+
#
|
481
|
+
# Alternate initializer (`Rational[1,2]`)
|
482
|
+
#
|
483
|
+
def self.[](*args)
|
484
|
+
Rational(*args)
|
485
|
+
end
|
486
|
+
|
487
|
+
end
|
488
|
+
|
489
|
+
|
460
490
|
class Prime
|
461
491
|
|
462
492
|
#
|
@@ -485,3 +515,4 @@ end
|
|
485
515
|
def primes
|
486
516
|
Prime.instance
|
487
517
|
end
|
518
|
+
|
@@ -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
|
#
|
@@ -28,6 +35,14 @@ class String
|
|
28
35
|
Shellwords.escape(self)
|
29
36
|
end
|
30
37
|
|
38
|
+
#
|
39
|
+
# Do what a browser would do when you type something into the address bar
|
40
|
+
#
|
41
|
+
def urlescape
|
42
|
+
@@uri_parser ||= URI::RFC2396_Parser.new
|
43
|
+
@@uri_parser.escape(self)
|
44
|
+
end
|
45
|
+
|
31
46
|
#
|
32
47
|
# Remove redundant whitespaces (not including newlines).
|
33
48
|
#
|
@@ -115,7 +130,7 @@ class String
|
|
115
130
|
alias_method :chomp_lines, :each_chomped
|
116
131
|
|
117
132
|
|
118
|
-
def split_at(boundary, options
|
133
|
+
def split_at(boundary, **options)
|
119
134
|
include_boundary = options[:include_boundary] || false
|
120
135
|
|
121
136
|
boundary = Regexp.new(Regexp.escape(boundary)) if boundary.is_a?(String)
|
@@ -269,20 +284,39 @@ class String
|
|
269
284
|
tr('n-za-mN-ZA-M', 'a-zA-Z')
|
270
285
|
end
|
271
286
|
|
287
|
+
#
|
288
|
+
# Cache an `URI::RFC2396_Parser` instance, because it's slowwww to initialize
|
289
|
+
#
|
290
|
+
def _rfc2396_parser
|
291
|
+
@@rfc2396_parser ||= URI::RFC2396_Parser.new
|
292
|
+
end
|
293
|
+
|
272
294
|
#
|
273
295
|
# Convert non-URI characters into %XXes.
|
274
296
|
#
|
275
297
|
def urlencode
|
276
|
-
URI.escape(self)
|
298
|
+
#URI.escape(self)
|
299
|
+
_rfc2396_parser.escape(self)
|
277
300
|
end
|
278
301
|
|
279
302
|
#
|
280
303
|
# Convert an URI's %XXes into regular characters.
|
281
304
|
#
|
282
305
|
def urldecode
|
283
|
-
|
306
|
+
_rfc2396_parser.unescape(self)
|
284
307
|
end
|
285
308
|
|
309
|
+
#
|
310
|
+
#
|
311
|
+
#
|
312
|
+
def to_bigdecimal
|
313
|
+
BigDecimal
|
314
|
+
BigDecimal(self)
|
315
|
+
end
|
316
|
+
alias_method :to_d, :to_bigdecimal
|
317
|
+
alias_method :to_dec, :to_bigdecimal
|
318
|
+
alias_method :to_decimal, :to_bigdecimal
|
319
|
+
|
286
320
|
#
|
287
321
|
# URI.parse the string and return an URI object
|
288
322
|
#
|
@@ -495,6 +529,68 @@ class String
|
|
495
529
|
nums_and_units.map { |num, units| num.send(units) }.sum
|
496
530
|
end
|
497
531
|
|
532
|
+
|
533
|
+
#
|
534
|
+
# Translate numbers with units (like 25k, 150GB, 15%, 5 hours) into their expanded numeric value
|
535
|
+
#
|
536
|
+
def parse_units
|
537
|
+
# extract the unit suffix
|
538
|
+
if self =~ /(\d[\d_]*(?:\.\d+)?)\s*([a-zA-Z]+\b|%(?= \s|$))/
|
539
|
+
units = $2.downcase
|
540
|
+
num = $1 #.to_f
|
541
|
+
num = num["."] ? num.to_f : num.to_i
|
542
|
+
|
543
|
+
case units
|
544
|
+
when "%"
|
545
|
+
# 0.01
|
546
|
+
num / 100.0
|
547
|
+
when "k"
|
548
|
+
# 10**3
|
549
|
+
num.thousand
|
550
|
+
when "m", "mm"
|
551
|
+
# 10**6
|
552
|
+
num.million
|
553
|
+
when "b", "bn"
|
554
|
+
# 10**9
|
555
|
+
num.billion
|
556
|
+
when "gib", "gb", "g"
|
557
|
+
num * 2**30
|
558
|
+
when "mib", "mb"
|
559
|
+
num * 2**20
|
560
|
+
when "kib", "kb"
|
561
|
+
num * 2**10
|
562
|
+
when "t", "tb"
|
563
|
+
# 10**12
|
564
|
+
num.trillion
|
565
|
+
when "q"
|
566
|
+
# 10**15
|
567
|
+
num.quadrillion
|
568
|
+
when "Q"
|
569
|
+
# 10**18
|
570
|
+
num.quintillion
|
571
|
+
when "min"
|
572
|
+
# 1.minute
|
573
|
+
num.minutes
|
574
|
+
when "hours", "h", "hr", "hrs"
|
575
|
+
# 1.hour
|
576
|
+
num.hours
|
577
|
+
when "d", "days", "dy"
|
578
|
+
num.days
|
579
|
+
else
|
580
|
+
raise "Invalid units: #{units.inspect}, in: #{self.inspect}"
|
581
|
+
end
|
582
|
+
else
|
583
|
+
raise "Couldn't find any units to parse! (expecting: '<a number><some letters>')"
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
alias_method :from_units, :parse_units
|
588
|
+
alias_method :from_human, :parse_units
|
589
|
+
alias_method :from_size, :parse_units
|
590
|
+
alias_method :from_percent, :parse_units
|
591
|
+
alias_method :from_time, :parse_units
|
592
|
+
|
593
|
+
|
498
594
|
#
|
499
595
|
# Print a hexdump of the string to STDOUT (coloured, if the terminal supports it)
|
500
596
|
#
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#
|
2
|
+
# A fraction! (Like a Rational, but ... uh ... in pure Ruby!)
|
3
|
+
#
|
4
|
+
# Can be compared, added, multiplied, simplified, "percent"ed, "to_f"ed, and printed/inspected.
|
5
|
+
#
|
6
|
+
class Fraction
|
7
|
+
|
8
|
+
attr_accessor :first, :last
|
9
|
+
|
10
|
+
alias_method :top, :first
|
11
|
+
alias_method :bottom, :last
|
12
|
+
|
13
|
+
alias_method :numerator, :first
|
14
|
+
alias_method :denominator, :last
|
15
|
+
|
16
|
+
#
|
17
|
+
# `first` is the top part of the fraction, `last` is the bottom (eg: `first/last`)
|
18
|
+
#
|
19
|
+
def initialize(first, last=1)
|
20
|
+
@first = first
|
21
|
+
@last = last
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.[](*args)
|
25
|
+
new(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
include Comparable
|
29
|
+
|
30
|
+
def <=>(other)
|
31
|
+
to_f <=> other.to_f
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Returns a string representation: "a/b"
|
36
|
+
#
|
37
|
+
def to_s
|
38
|
+
"#{@first}/#{@last}"
|
39
|
+
end
|
40
|
+
alias_method :fraction, :to_s
|
41
|
+
|
42
|
+
#
|
43
|
+
# Returns the fraction as a float. (eg: Fraction[1,2].to_f == 0.5)
|
44
|
+
#
|
45
|
+
def to_f
|
46
|
+
if @last == 0
|
47
|
+
raise ZeroDivisionError
|
48
|
+
else
|
49
|
+
@first.to_f / @last
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Returns a string representing the number in percent
|
55
|
+
#
|
56
|
+
def percent
|
57
|
+
"%0.1f%%" % (to_f * 100)
|
58
|
+
end
|
59
|
+
alias_method :to_percent, :percent
|
60
|
+
|
61
|
+
#
|
62
|
+
# "#<Fraction: 1/2>"
|
63
|
+
#
|
64
|
+
def inspect
|
65
|
+
"#<Fraction: #{to_s}>"
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Adds together the tops and bottoms of the fractions.
|
70
|
+
#
|
71
|
+
# Example: For the fractions `a/c` and `b/d`, returns `a+b/c+d`
|
72
|
+
#
|
73
|
+
def +(r)
|
74
|
+
case r
|
75
|
+
when Integer
|
76
|
+
self + Fraction[r]
|
77
|
+
when Fraction
|
78
|
+
Fraction[ r.last*first + r.first*last, r.last*last ]
|
79
|
+
else
|
80
|
+
raise TypeError.new("Sorry, I can't add a Fraction and a #{r.class}. :(")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Multiply the fractions
|
86
|
+
#
|
87
|
+
def *(v)
|
88
|
+
case v
|
89
|
+
when Integer
|
90
|
+
Fraction[ v*first, v*last ]
|
91
|
+
when Fraction
|
92
|
+
Fraction[ v.first*first, v.last*last ]
|
93
|
+
else
|
94
|
+
raise TypeError.new("I don't know how to multiply a Fraction and a #{v.class}. Sorry. :(")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def simplify
|
99
|
+
require 'prime'
|
100
|
+
|
101
|
+
# factor the numerator and denominator into hashes of { factor => exponent } pairs
|
102
|
+
n_fact, d_fact = [numerator, denominator].map { |n| Prime.prime_division(n).to_h }
|
103
|
+
|
104
|
+
# cancel out common factors by subtracting exponents
|
105
|
+
d_fact.each do |v, d_exp|
|
106
|
+
if n_exp = n_fact[v]
|
107
|
+
if n_exp < d_exp
|
108
|
+
d_fact[v] = d_exp - n_exp
|
109
|
+
n_fact[v] = 0
|
110
|
+
else
|
111
|
+
n_fact[v] = n_exp - d_exp # <= if n_exp == d_exp, this is 0, which covers the 3rd case
|
112
|
+
d_fact[v] = 0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# multiply the simplified factors back into full numbers
|
118
|
+
simp_n, simp_d = [n_fact, d_fact].map { |h| h.map{ |n, exp| n ** exp }.reduce(:*) }
|
119
|
+
|
120
|
+
Fraction[simp_n, simp_d]
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
#####################################################################################
|
126
|
+
#
|
127
|
+
# Fraction(a,b) is a wrapper for Fraction[a,b]
|
128
|
+
#
|
129
|
+
def Fraction(*args)
|
130
|
+
Fraction.new(*args)
|
131
|
+
end
|
data/lib/epitools/hexdump.rb
CHANGED
data/lib/epitools/minimal.rb
CHANGED
@@ -253,7 +253,7 @@ module Kernel
|
|
253
253
|
#
|
254
254
|
# Global AwesomePrint method (which triggers the loading of AwesomePrint the first time it's called)
|
255
255
|
#
|
256
|
-
def ap(object, options
|
256
|
+
def ap(object, **options)
|
257
257
|
AwesomePrint
|
258
258
|
Kernel.ap(object, options)
|
259
259
|
end
|
@@ -275,13 +275,12 @@ end
|
|
275
275
|
|
276
276
|
####################################################################################
|
277
277
|
#
|
278
|
-
# Path("/some/path") is
|
278
|
+
# Path("/some/path") is a wrapper for Path["/some/path"]
|
279
279
|
#
|
280
280
|
def Path(arg)
|
281
281
|
Path[arg]
|
282
282
|
end
|
283
283
|
|
284
|
-
|
285
284
|
####################################################################
|
286
285
|
require 'epitools/autoloads'
|
287
286
|
####################################################################
|
data/lib/epitools/path.rb
CHANGED
@@ -125,16 +125,17 @@ class Path
|
|
125
125
|
# Initializers
|
126
126
|
###############################################################################
|
127
127
|
|
128
|
+
alias_class_method :old_new, :new
|
128
129
|
def self.new(*args)
|
129
130
|
if args.first =~ URI_RE and self != Path::URI
|
130
131
|
Path::URI.new(args.first)
|
131
132
|
else
|
132
|
-
|
133
|
+
old_new(*args)
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
136
|
-
def initialize(newpath, hints
|
137
|
-
send("path=", newpath, hints)
|
137
|
+
def initialize(newpath, **hints)
|
138
|
+
send("path=", newpath, **hints)
|
138
139
|
|
139
140
|
# if hints[:unlink_when_garbage_collected]
|
140
141
|
# backup_path = path.dup
|
@@ -155,8 +156,8 @@ class Path
|
|
155
156
|
Shellwords.escape(str)
|
156
157
|
end
|
157
158
|
|
158
|
-
def self.glob(str, hints
|
159
|
-
Dir[str].map { |entry| new(entry, hints) }
|
159
|
+
def self.glob(str, **hints)
|
160
|
+
Dir[str].map { |entry| new(entry, **hints) }
|
160
161
|
end
|
161
162
|
|
162
163
|
def self.[](path)
|
@@ -194,7 +195,7 @@ class Path
|
|
194
195
|
#
|
195
196
|
# Note: The `hints` parameter contains options so `path=` doesn't have to touch the filesytem as much.
|
196
197
|
#
|
197
|
-
def path=(newpath, hints
|
198
|
+
def path=(newpath, **hints)
|
198
199
|
if hints[:type] or File.exist? newpath
|
199
200
|
if hints[:type] == :dir or File.directory? newpath
|
200
201
|
self.dir = newpath
|
@@ -816,7 +817,7 @@ class Path
|
|
816
817
|
# zopen("otherfile.gz", "w") #=> #<Zlib::GzipWriter:0x7fe30448>>
|
817
818
|
# zopen("test.txt.gz") { |f| f.read } # read the contents of the .gz file, then close the file handle automatically.
|
818
819
|
#
|
819
|
-
def zopen(mode="rb", &block)
|
820
|
+
def zopen(mode="rb", **opts, &block)
|
820
821
|
# if ext == "gz"
|
821
822
|
# io = open(mode)
|
822
823
|
# case mode
|
@@ -829,7 +830,7 @@ class Path
|
|
829
830
|
# raise "Unknown mode: #{mode.inspect}. zopen only supports 'r' and 'w'."
|
830
831
|
# end
|
831
832
|
# elsif bin = COMPRESSORS[ext]
|
832
|
-
if bin = COMPRESSORS[ext]
|
833
|
+
if bin = (opts[:format] || COMPRESSORS[ext])
|
833
834
|
if which(bin)
|
834
835
|
case mode
|
835
836
|
when "w", "wb"
|
@@ -868,18 +869,25 @@ class Path
|
|
868
869
|
|
869
870
|
end
|
870
871
|
|
872
|
+
def self.zopen(filename, mode, &block)
|
873
|
+
Path.new(filename).zopen(mode, &block)
|
874
|
+
end
|
875
|
+
|
871
876
|
###############################################################################
|
872
877
|
# Parsing files
|
873
878
|
###############################################################################
|
874
879
|
|
875
880
|
#
|
876
881
|
# Parse the file based on the file extension.
|
877
|
-
# (Handles json, html, yaml, xml, csv, marshal, and bson.)
|
882
|
+
# (Handles json, html, yaml, xml, csv, tsv, marshal, and bson.)
|
883
|
+
#
|
884
|
+
# The "format" option lets you specify the file format (eg: `Path["something.conf"].parse(format: "yaml")`)
|
885
|
+
# You can also pass CSV parsing options (eg: `Path["thing.csv"].parse(col_sep: "\t")`)
|
878
886
|
#
|
879
|
-
def parse(io=self.io,
|
880
|
-
case (
|
887
|
+
def parse(io=self.io, **opts)
|
888
|
+
case (opts[:format] || ext.downcase)
|
881
889
|
when 'gz', 'bz2', 'xz'
|
882
|
-
parse(zopen, exts[-2])
|
890
|
+
parse(zopen, format: exts[-2])
|
883
891
|
when 'json'
|
884
892
|
read_json(io)
|
885
893
|
when 'html', 'htm'
|
@@ -889,7 +897,10 @@ class Path
|
|
889
897
|
when 'xml', 'rdf', 'rss'
|
890
898
|
read_xml(io)
|
891
899
|
when 'csv'
|
892
|
-
read_csv(io, opts)
|
900
|
+
read_csv(io, **opts)
|
901
|
+
when 'tsv'
|
902
|
+
opts[:col_sep] ||= "\t"
|
903
|
+
read_csv(io, **opts)
|
893
904
|
when 'marshal'
|
894
905
|
read_marshal(io)
|
895
906
|
when 'bson'
|
@@ -920,7 +931,8 @@ class Path
|
|
920
931
|
|
921
932
|
|
922
933
|
def read_html(io=self.io)
|
923
|
-
Nokogiri::HTML(io)
|
934
|
+
#Nokogiri::HTML(io)
|
935
|
+
Oga.parse_html(io)
|
924
936
|
end
|
925
937
|
alias_method :from_html, :read_html
|
926
938
|
|
@@ -938,14 +950,15 @@ class Path
|
|
938
950
|
|
939
951
|
|
940
952
|
# Parse the file as CSV
|
941
|
-
def read_csv(io=self.io, opts
|
953
|
+
def read_csv(io=self.io, **opts)
|
942
954
|
CSV.new(io.read, **opts).each
|
943
955
|
end
|
944
956
|
alias_method :from_csv, :read_csv
|
945
957
|
|
946
958
|
# Parse the file as XML
|
947
959
|
def read_xml(io=self.io)
|
948
|
-
Nokogiri::XML(io)
|
960
|
+
# Nokogiri::XML(io)
|
961
|
+
Oga.parse_xml(io)
|
949
962
|
end
|
950
963
|
|
951
964
|
# Parse the file as a Ruby Marshal dump
|
@@ -1024,6 +1037,7 @@ class Path
|
|
1024
1037
|
dest
|
1025
1038
|
end
|
1026
1039
|
alias_method :ren, :rename
|
1040
|
+
alias_method :rename_to, :rename
|
1027
1041
|
|
1028
1042
|
#
|
1029
1043
|
# Works the same as "rename", but the destination can be on another disk.
|
@@ -1502,7 +1516,8 @@ class Path
|
|
1502
1516
|
# TODO: Remove the tempfile when the Path object is garbage collected or freed.
|
1503
1517
|
#
|
1504
1518
|
def self.tmpfile(prefix="tmp")
|
1505
|
-
path = Path.new(Tempfile.new(prefix).path, unlink_when_garbage_collected: true)
|
1519
|
+
# path = Path.new(Tempfile.new(prefix).path, unlink_when_garbage_collected: true)
|
1520
|
+
path = Path.new(Tempfile.new(prefix).path)
|
1506
1521
|
yield path if block_given?
|
1507
1522
|
path
|
1508
1523
|
end
|
@@ -1633,7 +1648,7 @@ class Path::URI < Path
|
|
1633
1648
|
# TODO: only include certain methods from Path (delegate style)
|
1634
1649
|
# (eg: remove commands that write)
|
1635
1650
|
|
1636
|
-
def initialize(uri, hints
|
1651
|
+
def initialize(uri, **hints)
|
1637
1652
|
@uri = ::URI.parse(uri)
|
1638
1653
|
self.path = @uri.path
|
1639
1654
|
end
|
data/lib/epitools/rash.rb
CHANGED
data/lib/epitools/term.rb
CHANGED
@@ -29,26 +29,62 @@ module Term
|
|
29
29
|
# Return the [width,height] of the terminal.
|
30
30
|
#
|
31
31
|
def size
|
32
|
-
$stdout.winsize.reverse
|
32
|
+
$stdout.winsize.reverse rescue [80,25]
|
33
33
|
end
|
34
34
|
|
35
35
|
def width; size[0]; end
|
36
36
|
def height; size[1]; end
|
37
|
-
def goto(x,y); @x, @y = x, y; end
|
38
|
-
def pos; [@x, @y]; end
|
37
|
+
# def goto(x,y); @x, @y = x, y; end
|
38
|
+
# def pos; [@x, @y]; end
|
39
39
|
|
40
|
-
def clear
|
41
|
-
print "\e[H\e[J"
|
42
|
-
end
|
43
40
|
|
41
|
+
##################################################################################
|
42
|
+
### ANSI Stuff (see: ttps://en.wikipedia.org/wiki/ANSI_escape_code)
|
43
|
+
##################################################################################
|
44
|
+
|
45
|
+
##################################################################################
|
46
|
+
## <n>K = Clear (part of) the line
|
47
|
+
##################################################################################
|
48
|
+
|
49
|
+
# 2 = clear entire line
|
44
50
|
def clear_line
|
45
51
|
print "\e[2K"
|
46
52
|
end
|
47
53
|
|
54
|
+
# 0 = clear to end of line
|
48
55
|
def clear_eol
|
49
56
|
print "\e[0K"
|
50
57
|
end
|
51
58
|
|
59
|
+
##################################################################################
|
60
|
+
## <n>J = Clear (part of) the screen.
|
61
|
+
##################################################################################
|
62
|
+
|
63
|
+
def clear
|
64
|
+
# If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
|
65
|
+
print "\e[2J\e[H"
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear_all_above
|
69
|
+
# If n is 1, clear from cursor to beginning of the screen.
|
70
|
+
print "\e[1J"
|
71
|
+
end
|
72
|
+
|
73
|
+
def clear_all_below
|
74
|
+
# If n is 0 (or missing), clear from cursor to end of screen.
|
75
|
+
print "\e[0J"
|
76
|
+
end
|
77
|
+
|
78
|
+
def clear_scrollback_buffer!
|
79
|
+
# If n is 3, clear entire screen and delete all lines saved in the scrollback buffer (this feature was added for xterm and is supported by other terminal applications).
|
80
|
+
print "\e[3J"
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##################################################################################
|
85
|
+
## <n>;<m>H = Move!
|
86
|
+
##################################################################################
|
87
|
+
|
52
88
|
def move_to(row: 1, col: 1)
|
53
89
|
print "\e[#{row};#{col}H"
|
54
90
|
end
|
@@ -82,11 +118,15 @@ module Term
|
|
82
118
|
@back = back if back
|
83
119
|
end
|
84
120
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
121
|
+
#
|
122
|
+
# curses-style scrollable terminal window
|
123
|
+
#
|
89
124
|
class Window
|
125
|
+
|
126
|
+
# work in progress. probably requires an event loop and a higher order container for having multiple windows and a text-input and stuff.
|
127
|
+
|
128
|
+
attr_accessor :wrap
|
129
|
+
|
90
130
|
def initialize
|
91
131
|
end
|
92
132
|
|
@@ -94,21 +134,39 @@ module Term
|
|
94
134
|
end
|
95
135
|
end
|
96
136
|
|
137
|
+
|
97
138
|
class Table
|
98
139
|
|
99
140
|
# TODO:
|
100
141
|
#
|
101
142
|
# * make Table's configuration eaiser to remember by putting the formatting parameters in initialize
|
102
143
|
# eg: Table.new(elements, :sort=>:vertical).to_s
|
144
|
+
# * strip ansi
|
145
|
+
# * wrap contents
|
146
|
+
# * rounded corners
|
147
|
+
# * [far future] dynamic sortable filterable toggleable table
|
103
148
|
#
|
104
149
|
|
105
150
|
attr_accessor :border, :columns, :padding, :strip_color, :indent, :width, :height
|
106
151
|
|
107
|
-
def self.
|
108
|
-
|
152
|
+
def self.print(thing, **opts)
|
153
|
+
raise "Can't tablize a #{thing.class}" unless thing.class < Enumerable
|
154
|
+
puts new(thing, **opts).display
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.hprint(thing)
|
158
|
+
puts new(thing).in_rows
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.vprint(thing)
|
162
|
+
puts new(thing).in_columns
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.[](data, **opts)
|
166
|
+
new(data, **opts)
|
109
167
|
end
|
110
168
|
|
111
|
-
def initialize(data, options
|
169
|
+
def initialize(data, **options)
|
112
170
|
@data = data.map(&:to_s)
|
113
171
|
@strip_color = options[:ansi] || options[:colorized] || options[:colored] || options[:strip_color] || options[:strip_ansi]
|
114
172
|
|
@@ -185,7 +243,7 @@ module Term
|
|
185
243
|
end
|
186
244
|
alias_method :by_rows, :in_rows
|
187
245
|
|
188
|
-
def display #(opts
|
246
|
+
def display #(**opts)
|
189
247
|
case @direction
|
190
248
|
when :horizontal
|
191
249
|
puts in_rows
|
@@ -198,7 +256,7 @@ module Term
|
|
198
256
|
by_rows
|
199
257
|
end
|
200
258
|
|
201
|
-
def render(rows, options
|
259
|
+
def render(rows, **options)
|
202
260
|
num_cols = rows.first.size
|
203
261
|
result = []
|
204
262
|
|
data/spec/core_ext_spec.rb
CHANGED
@@ -404,6 +404,15 @@ describe String do
|
|
404
404
|
]
|
405
405
|
end
|
406
406
|
|
407
|
+
it "parses units" do
|
408
|
+
"50%".parse_units.should == 0.5
|
409
|
+
"1b".parse_units.should == 1.billion
|
410
|
+
"1gb".parse_units.should == 2**30
|
411
|
+
|
412
|
+
lambda { "whee".parse_units }.should raise_error
|
413
|
+
lambda { "57 butts".parse_units }.should raise_error
|
414
|
+
end
|
415
|
+
|
407
416
|
end
|
408
417
|
|
409
418
|
|
@@ -491,15 +500,30 @@ describe Float do
|
|
491
500
|
end
|
492
501
|
end
|
493
502
|
|
494
|
-
it "
|
503
|
+
it "percents" do
|
495
504
|
f = 0.716237
|
496
|
-
f.percent.should == "
|
505
|
+
f.percent.should == "71.6%"
|
506
|
+
f.percent(0).should == "72%"
|
507
|
+
f.percent(1).should == "71.6%"
|
497
508
|
f.percent(2).should == "71.62%"
|
498
509
|
f.percent(3).should == "71.624%"
|
499
510
|
end
|
500
511
|
|
501
512
|
end
|
502
513
|
|
514
|
+
describe Rational do
|
515
|
+
|
516
|
+
it "makes em" do
|
517
|
+
lambda { Rational[1,2] }.should_not raise_error
|
518
|
+
end
|
519
|
+
|
520
|
+
it "percents" do
|
521
|
+
Rational(1,2).percent.should == "50.0%"
|
522
|
+
Rational(1,3).percent(3).should == "33.333%"
|
523
|
+
end
|
524
|
+
|
525
|
+
end
|
526
|
+
|
503
527
|
describe Number do
|
504
528
|
|
505
529
|
it "number?" do
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'epitools/fraction'
|
2
|
+
|
3
|
+
describe Fraction do
|
4
|
+
|
5
|
+
# before :each do
|
6
|
+
# @a = Fraction[1,1]
|
7
|
+
# @b = Fraction[1,2]
|
8
|
+
# end
|
9
|
+
|
10
|
+
it "adds normally" do
|
11
|
+
( Fraction[1,1] + Fraction[1,2] ).should == Fraction[3,2]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "doesn't let you add weird stuff together" do
|
15
|
+
-> { Fraction[1,2] + :splunge }.should raise_error(TypeError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "timeses normally" do
|
19
|
+
( Fraction[1,1] * 2 ).should == Fraction[2,2]
|
20
|
+
( Fraction[1,2] * 2 ).should == Fraction[2,4]
|
21
|
+
( Fraction[1,1] * Fraction[1,2] ).should == Fraction[1,2]
|
22
|
+
( Fraction[5,3] * Fraction[1,2] ).should == Fraction[5*1, 2*3]
|
23
|
+
( Fraction[5,3] * Fraction[1,2] ).should == Fraction[5, 6]
|
24
|
+
( Fraction[1,2] * Fraction[1,2] ).should == Fraction[1, 4]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "doesn't let you times it with weird stuff" do
|
28
|
+
-> { Fraction[1,2] * ([:ayeeee]*100) }.should raise_error(TypeError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "floats" do
|
32
|
+
Fraction[1,1].to_f.should == 1.0
|
33
|
+
Fraction[1,2].to_f.should == 0.5
|
34
|
+
|
35
|
+
-> { Fraction[1,0].to_f }.should raise_error(ZeroDivisionError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "percents" do
|
39
|
+
Fraction[1,1].percent.should == "100.0%"
|
40
|
+
Fraction[1,2].percent.should == "50.0%"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "simplifies" do
|
44
|
+
Fraction[2,4].simplify.should == Fraction[1,2]
|
45
|
+
Fraction[4,2].simplify.should == Fraction[2,1]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "has a function-style wrapper! (for paren fans)" do
|
49
|
+
Fraction(1,2).should == Fraction[1,2]
|
50
|
+
Fraction(1,2).should == Fraction.new(1,2)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/spec/numwords_spec.rb
CHANGED
@@ -7,17 +7,14 @@ describe Numeric do
|
|
7
7
|
10 => "ten",
|
8
8
|
3_123 => "three thousand, one-hundred and twenty-three",
|
9
9
|
123_124 => "one-hundred and twenty-three thousand, one-hundred and twenty-four",
|
10
|
-
|
11
10
|
8_128_937_981_273_987_129_837_174_612_897_638_613 => "eight undecillion, one-hundred and twenty-eight decillion, nine-hundred and thirty-seven nonillion, nine-hundred and eighty-one octillion, two-hundred and seventy-three septillion, nine-hundred and eighty-seven sextillion, one-hundred and twenty-nine quintillion, eight-hundred and thirty-seven quadrillion, one-hundred and seventy-four trillion, six-hundred and twelve billion, eight-hundred and ninety-seven million, six-hundred and thirty-eight thousand, six-hundred and thirteen",
|
12
|
-
|
13
|
-
3_486_597_230_495_871_304_981_320_498_123_498_263_984_739_841_834_091_823_094_812_039_481_231_623_987_461_293_874_698_123_649_817_236 => "three duotrigintillion, four-hundred and eighty-six untrigintillion, five-hundred and ninety-seven trigintillion, two-hundred and thirty novemvigintillion, four-hundred and ninety-five octovigintillion, eight-hundred and seventy-one septenvigintillion, three-hundred and four sexvigintillion, nine-hundred and eighty-one quinvigintillion, three-hundred and twenty quattuorvigintillion, four-hundred and ninety-eight trevigintillion, one-hundred and twenty-three duovigintillion, four-hundred and ninety-eight unvigintillion, two-hundred and sixty-three vigintillion, nine-hundred and eighty-four novemdecillion, seven-hundred and thirty-nine octodecillion, eight-hundred and fourty-one septendecillion, eight-hundred and thirty-four sexdecillion, ninety-one quindecillion, eight-hundred and twenty-three quattuordecillion, ninety-four tredecillion, eight-hundred and twelve duodecillion, thirty-nine undecillion, four-hundred and eighty-one decillion, two-hundred and thirty-one nonillion, six-hundred and twenty-three octillion, nine-hundred and eighty-seven septillion, four-hundred and sixty-one sextillion, two-hundred and ninety-three quintillion, eight-hundred and seventy-four quadrillion, six-hundred and ninety-eight trillion, one-hundred and twenty-three billion, six-hundred and fourty-nine million, eight-hundred and seventeen thousand, two-hundred and thirty-six",
|
14
|
-
|
11
|
+
3_486_597_230_495_871_304_981_320_498_123_498_263_984_739_841_834_091_823_094_812_039_481_231_623_987_461_293_874_698_123_649_817_236 => "three duotrigintillion, four-hundred and eighty-six untrigintillion, five-hundred and ninety-seven trigintillion, two-hundred and thirty novemvigintillion, four-hundred and ninety-five octovigintillion, eight-hundred and seventy-one septenvigintillion, three-hundred and four sexvigintillion, nine-hundred and eighty-one quinvigintillion, three-hundred and twenty quattuorvigintillion, four-hundred and ninety-eight trevigintillion, one-hundred and twenty-three duovigintillion, four-hundred and ninety-eight unvigintillion, two-hundred and sixty-three vigintillion, nine-hundred and eighty-four novemdecillion, seven-hundred and thirty-nine octodecillion, eight-hundred and forty-one septendecillion, eight-hundred and thirty-four sexdecillion, ninety-one quindecillion, eight-hundred and twenty-three quattuordecillion, ninety-four tredecillion, eight-hundred and twelve duodecillion, thirty-nine undecillion, four-hundred and eighty-one decillion, two-hundred and thirty-one nonillion, six-hundred and twenty-three octillion, nine-hundred and eighty-seven septillion, four-hundred and sixty-one sextillion, two-hundred and ninety-three quintillion, eight-hundred and seventy-four quadrillion, six-hundred and ninety-eight trillion, one-hundred and twenty-three billion, six-hundred and forty-nine million, eight-hundred and seventeen thousand, two-hundred and thirty-six",
|
15
12
|
1763241823498172490817349807213409238409123409128340981234781236487126348791263847961238794612839468917236489712364987162398746129834698172364987123 => "more than a googol! (148 digits)"
|
16
13
|
}.each do |num, result|
|
17
14
|
num.to_words.should == result
|
18
15
|
end
|
19
16
|
|
20
|
-
lambda{ 1.523.million.billion.to_words }.should_not raise_error
|
17
|
+
lambda { 1.523.million.billion.to_words }.should_not raise_error
|
21
18
|
end
|
22
19
|
|
23
20
|
it "has .thousand, .million, etc." do
|
data/spec/path_spec.rb
CHANGED
@@ -147,7 +147,9 @@ describe Path do
|
|
147
147
|
data = "<h1>The best webpage in the universe.</h1>"
|
148
148
|
html = Path.tmpfile
|
149
149
|
html.write data
|
150
|
-
|
150
|
+
|
151
|
+
# html.read_html.at("h1").to_s.should == data
|
152
|
+
html.read_html.at_css("h1").to_s.should == data
|
151
153
|
|
152
154
|
ensure
|
153
155
|
yaml.rm
|
@@ -243,7 +245,7 @@ describe Path do
|
|
243
245
|
|
244
246
|
old_name = path.to_s
|
245
247
|
|
246
|
-
dest = path.rename(:
|
248
|
+
dest = path.rename(path.with(ext: ".dat"))
|
247
249
|
|
248
250
|
dest.to_s.should == old_name+".dat"
|
249
251
|
path.to_s.should == old_name
|
@@ -253,10 +255,10 @@ describe Path do
|
|
253
255
|
path.exists?.should == false
|
254
256
|
|
255
257
|
path.touch
|
256
|
-
lambda { path.rename(:
|
258
|
+
lambda { path.rename(path.with(ext: ".dat")) }.should raise_error(RuntimeError)
|
257
259
|
|
258
260
|
dest.rm
|
259
|
-
path.rename!(:
|
261
|
+
path.rename!(path.with(ext: ".dat"))
|
260
262
|
path.to_s.should_not == old_name
|
261
263
|
path.exists?.should == true
|
262
264
|
|
@@ -413,6 +415,14 @@ describe Path do
|
|
413
415
|
|
414
416
|
# system("gzip -c < #{tmpjson} > #{tmpjson}.gz")
|
415
417
|
|
418
|
+
# class method version
|
419
|
+
Path.zopen("#{tmpjson}.gz", "w") { |io| io.write(JSON.dump(hash)) }
|
420
|
+
tmpgzip = Path["#{tmpjson}.gz"]
|
421
|
+
tmpgzip.exists?.should == true
|
422
|
+
tmpgzip.size.should be > 0
|
423
|
+
tmpgzip&.rm
|
424
|
+
|
425
|
+
# instance method version
|
416
426
|
tmpgzip = Path["#{tmpjson}.gz"]
|
417
427
|
tmpgzip.zopen("w") { |io| io.write(JSON.dump(hash)) }
|
418
428
|
tmpgzip.exists?.should == true
|
data/spec/sys_spec.rb
CHANGED
@@ -30,6 +30,11 @@ describe Sys::ProcessInfo do
|
|
30
30
|
if p2 = p2s[p1.pid]
|
31
31
|
matches += 1
|
32
32
|
p1.command.should == p2.command
|
33
|
+
|
34
|
+
# FIXME: this behaves weirdly with kernel processes, eg:
|
35
|
+
# expected: "[kworker/u8:1-phy5]"
|
36
|
+
# got: "[kworker/u8:1-events_unbound]" (using ==)
|
37
|
+
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
data/spec/zopen_spec.rb
CHANGED
@@ -1,49 +1,49 @@
|
|
1
|
-
require 'epitools/zopen'
|
2
|
-
require 'tempfile'
|
1
|
+
# require 'epitools/zopen'
|
2
|
+
# require 'tempfile'
|
3
3
|
|
4
|
-
describe "zopen()" do
|
4
|
+
# describe "zopen()" do
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# before :all do
|
7
|
+
# @data = ("x"*100+"\n") * 1000
|
8
|
+
# @tmp = Tempfile.new("zopen_spec")
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# @plainfile = @tmp.path
|
11
|
+
# @gzfile = "#{@tmp.path}.gz"
|
12
|
+
# end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
# after :all do
|
15
|
+
# File.unlink @plainfile
|
16
|
+
# File.unlink @gzfile
|
17
|
+
# end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
# it "writes/reads a gz" do
|
20
|
+
# f = zopen(@gzfile, "w")
|
21
|
+
# f.write(@data).should == @data.size
|
22
|
+
# f.close
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
# f = zopen(@gzfile, "r")
|
25
|
+
# f.read.should == @data
|
26
|
+
# f.close
|
27
|
+
# end
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
# it "writes/reads non-gz files" do
|
30
|
+
# zopen(@plainfile, "w") {|f| f.write(@data) }
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
# # readstyle
|
33
|
+
# File.read(@plainfile).should == zopen(@plainfile).read
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
# # blockstyle
|
36
|
+
# open(@plainfile){|f| f.read }.should == zopen(@plainfile){|f| f.read }
|
37
|
+
# end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
# it "is enumerable" do
|
40
|
+
# zopen(@gzfile) do |f|
|
41
|
+
# f.respond_to?(:each).should == true
|
42
|
+
# f.respond_to?(:map).should == true
|
43
|
+
# f.respond_to?(:inject).should == true
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
# f.all?{|line| line =~ /^x+$/ }
|
46
|
+
# end
|
47
|
+
# end
|
48
48
|
|
49
|
-
end
|
49
|
+
# end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epitools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.136
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/epitools/core_ext/truthiness.rb
|
70
70
|
- lib/epitools/core_ext/uri.rb
|
71
71
|
- lib/epitools/daemonize.rb
|
72
|
+
- lib/epitools/fraction.rb
|
72
73
|
- lib/epitools/gem_ext/oga.rb
|
73
74
|
- lib/epitools/hexdump.rb
|
74
75
|
- lib/epitools/iter.rb
|
@@ -86,7 +87,6 @@ files:
|
|
86
87
|
- lib/epitools/progressbar.rb
|
87
88
|
- lib/epitools/rails.rb
|
88
89
|
- lib/epitools/rash.rb
|
89
|
-
- lib/epitools/ratio.rb
|
90
90
|
- lib/epitools/semantic_version.rb
|
91
91
|
- lib/epitools/slop.rb
|
92
92
|
- lib/epitools/slop/LICENSE
|
@@ -105,14 +105,16 @@ files:
|
|
105
105
|
- lib/epitools/wm.rb
|
106
106
|
- spec/.rspec
|
107
107
|
- spec/autoreq_spec.rb
|
108
|
-
- spec/browser_spec.rb
|
109
108
|
- spec/btree_spec.rb
|
110
109
|
- spec/clitools_spec.rb
|
111
110
|
- spec/colored_spec.rb
|
112
111
|
- spec/core_ext_spec.rb
|
112
|
+
- spec/fraction_spec.rb
|
113
113
|
- spec/histogram_spec.rb
|
114
114
|
- spec/iter_spec.rb
|
115
115
|
- spec/lcs_spec.rb
|
116
|
+
- spec/manual/browser_spec.rb
|
117
|
+
- spec/manual/wm_spec.rb
|
116
118
|
- spec/numwords_spec.rb
|
117
119
|
- spec/path_spec.rb
|
118
120
|
- spec/permutations_spec.rb
|
@@ -122,7 +124,6 @@ files:
|
|
122
124
|
- spec/sys_spec.rb
|
123
125
|
- spec/term_spec.rb
|
124
126
|
- spec/typed_struct_spec.rb
|
125
|
-
- spec/wm_spec.rb
|
126
127
|
- spec/zopen_spec.rb
|
127
128
|
homepage: http://github.com/epitron/epitools
|
128
129
|
licenses:
|
@@ -143,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
144
|
- !ruby/object:Gem::Version
|
144
145
|
version: '0'
|
145
146
|
requirements: []
|
146
|
-
rubygems_version: 3.
|
147
|
+
rubygems_version: 3.3.25
|
147
148
|
signing_key:
|
148
149
|
specification_version: 3
|
149
150
|
summary: Not utils... METILS!
|
data/lib/epitools/ratio.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# The ratio between two numbers (eg: 2:1, 3:4)
|
3
|
-
#
|
4
|
-
# Can be compared, added, "percent"ed, "to_f"ed, and displayed.
|
5
|
-
#
|
6
|
-
class Ratio
|
7
|
-
|
8
|
-
include Comparable
|
9
|
-
|
10
|
-
def <=>(other)
|
11
|
-
to_f <=> other.to_f
|
12
|
-
end
|
13
|
-
|
14
|
-
attr_accessor :first, :last
|
15
|
-
|
16
|
-
def self.[](*args)
|
17
|
-
new(*args)
|
18
|
-
end
|
19
|
-
|
20
|
-
#
|
21
|
-
# `first` is the top part of the ratio, `last` is the bottom (eg: `first/last`)
|
22
|
-
#
|
23
|
-
def initialize(first, last=1)
|
24
|
-
@first = first
|
25
|
-
@last = last
|
26
|
-
end
|
27
|
-
|
28
|
-
#
|
29
|
-
# Returns a string representation: "a/b"
|
30
|
-
#
|
31
|
-
def to_s
|
32
|
-
"#{@first}/#{@last}"
|
33
|
-
end
|
34
|
-
alias_method :ratio, :to_s
|
35
|
-
|
36
|
-
#
|
37
|
-
# Returns the ratio as a float. (eg: Ratio[1,2].to_f == 0.5)
|
38
|
-
#
|
39
|
-
def to_f
|
40
|
-
if @last == 0
|
41
|
-
0.0
|
42
|
-
else
|
43
|
-
@first.to_f / @last
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
# Returns a string representing the number in percent
|
49
|
-
#
|
50
|
-
def percent
|
51
|
-
"%0.1f%%" % (to_f * 100)
|
52
|
-
end
|
53
|
-
alias_method :to_percent, :percent
|
54
|
-
|
55
|
-
#
|
56
|
-
# "#<Ratio: 1/2>"
|
57
|
-
#
|
58
|
-
def inspect
|
59
|
-
"#<Ratio: #{to_s}>"
|
60
|
-
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# Adds together the tops and bottoms of the ratios.
|
64
|
-
#
|
65
|
-
# Example: For the ratios `a/c` and `b/d`, returns `a+b/c+d`
|
66
|
-
#
|
67
|
-
def +(other)
|
68
|
-
Ratio.new( first+other.first, last+other.last)
|
69
|
-
end
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
#
|
74
|
-
# Function-style wrapper
|
75
|
-
#
|
76
|
-
def Ratio(*args)
|
77
|
-
Ratio.new(*args)
|
78
|
-
end
|
File without changes
|
File without changes
|