epitools 0.5.126 → 0.5.133
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/VERSION +1 -1
- data/lib/epitools/autoloads.rb +15 -14
- data/lib/epitools/clitools.rb +17 -0
- data/lib/epitools/core_ext/array.rb +28 -1
- data/lib/epitools/core_ext/class.rb +5 -19
- data/lib/epitools/core_ext/enumerable.rb +3 -0
- data/lib/epitools/core_ext/numbers.rb +27 -8
- data/lib/epitools/core_ext/truthiness.rb +1 -2
- data/lib/epitools/core_ext/uri.rb +35 -12
- data/lib/epitools/job_runner.rb +115 -0
- data/lib/epitools/numwords.rb +1 -1
- data/lib/epitools/path.rb +19 -10
- data/lib/epitools/sys.rb +1 -0
- data/spec/core_ext_spec.rb +52 -29
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db1a5a887e1a937b76e39d061a8865503f7bfeb33f03094f5d7b8ce3749420e7
|
4
|
+
data.tar.gz: b5075843f8bd030de2e5313e9af3f53f97cae26fc0b52a609a06137fc015324c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f13ae895b759edc3612970e335b743301bd102a4c73f619c16028acab0792a1d229d65ff40626d6c11334f95b020b055f9a18e6afc70a37ae74824b439c2f24
|
7
|
+
data.tar.gz: 8bac272ee77ee4bd4c493b2b6f6c394e1143bd57e73b77196f9798326e73749b1c940622e22ea792875718b59e7a01380d2766296df5c19a3a259a3f7e34fcd3
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.133
|
data/lib/epitools/autoloads.rb
CHANGED
@@ -65,19 +65,20 @@ autoload :IPAddr, 'ipaddr'
|
|
65
65
|
|
66
66
|
## Nonstandard library (epitools)
|
67
67
|
|
68
|
-
autoload :Path,
|
69
|
-
autoload :Ezdb,
|
70
|
-
autoload :Browser,
|
71
|
-
autoload :Rash,
|
72
|
-
autoload :Ratio,
|
73
|
-
autoload :ProgressBar,
|
74
|
-
autoload :Trie,
|
75
|
-
autoload :MimeMagic,
|
76
|
-
autoload :Term,
|
77
|
-
autoload :Iter,
|
78
|
-
autoload :WM,
|
79
|
-
autoload :TypedStruct,
|
80
|
-
autoload :Sys,
|
68
|
+
autoload :Path, 'epitools/path'
|
69
|
+
autoload :Ezdb, 'epitools/ezdb'
|
70
|
+
autoload :Browser, 'epitools/browser'
|
71
|
+
autoload :Rash, 'epitools/rash'
|
72
|
+
autoload :Ratio, 'epitools/ratio'
|
73
|
+
autoload :ProgressBar, 'epitools/progressbar'
|
74
|
+
autoload :Trie, 'epitools/trie'
|
75
|
+
autoload :MimeMagic, 'epitools/mimemagic'
|
76
|
+
autoload :Term, 'epitools/term'
|
77
|
+
autoload :Iter, 'epitools/iter'
|
78
|
+
autoload :WM, 'epitools/wm'
|
79
|
+
autoload :TypedStruct, 'epitools/typed_struct'
|
80
|
+
autoload :Sys, 'epitools/sys'
|
81
|
+
autoload :JobRunner, 'epitools/job_runner'
|
81
82
|
autoload :SemanticVersion, 'epitools/semantic_version'
|
82
83
|
|
83
84
|
autoload :Matrix, 'epitools/core_ext/matrix'
|
@@ -87,7 +88,7 @@ autoreq(:Vector) { Matrix }
|
|
87
88
|
module Epi
|
88
89
|
autoload :Slop, 'epitools/slop'
|
89
90
|
end
|
90
|
-
autoreq(:Slop) do
|
91
|
+
autoreq(:Slop) do
|
91
92
|
Slop = Epi::Slop
|
92
93
|
end
|
93
94
|
|
data/lib/epitools/clitools.rb
CHANGED
@@ -230,3 +230,20 @@ def notify_send(title, body=nil, icon: nil, time: 5)
|
|
230
230
|
fork { system *cmd }
|
231
231
|
end
|
232
232
|
|
233
|
+
def curl(url)
|
234
|
+
curl_open(url).read
|
235
|
+
end
|
236
|
+
|
237
|
+
def curl_open(url, headers={})
|
238
|
+
# 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
|
+
cmd = ["curl", "-LSs"]
|
240
|
+
headers.each { |k,v| cmd += ["-H", "#{k}: #{v}"] }
|
241
|
+
cmd << url
|
242
|
+
IO.popen(cmd)
|
243
|
+
rescue Errno::ENOENT
|
244
|
+
raise "Error: 'curl' isn't installed."
|
245
|
+
end
|
246
|
+
|
247
|
+
def curl_json(url)
|
248
|
+
JSON.parse(curl(url))
|
249
|
+
end
|
@@ -324,6 +324,33 @@ class Array
|
|
324
324
|
super.to_a
|
325
325
|
end
|
326
326
|
|
327
|
-
|
327
|
+
module ToCSV
|
328
|
+
#
|
329
|
+
# Convert this enumerable into a CSV string (nb: enumerable must contain either all Hashes or all Arrays)
|
330
|
+
#
|
331
|
+
def to_csv(delimiter=",")
|
332
|
+
types = count_by(&:class)
|
333
|
+
|
334
|
+
unless types.size == 1 and (types[Array] > 0 or types[Hash] > 0)
|
335
|
+
raise "Error: this array must contain nothing but arrays, or nothing but hashes (actually contains: #{types.inspect})"
|
336
|
+
end
|
337
|
+
|
338
|
+
options = {}
|
339
|
+
options[:col_sep] = delimiter
|
340
|
+
options[:headers] = flat_map(&:keys).uniq if types[Hash] > 0
|
328
341
|
|
342
|
+
CSV.generate(nil, **options) do |csv|
|
343
|
+
each { |obj| csv << obj }
|
344
|
+
end
|
345
|
+
end
|
329
346
|
|
347
|
+
#
|
348
|
+
# Like #to_csv, but with tab-separated CSV fields
|
349
|
+
#
|
350
|
+
def to_tsv
|
351
|
+
to_csv("\t")
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
prepend ToCSV # dirty hack to stop the CSV module from clobbering the to_csv method
|
356
|
+
end
|
@@ -1,31 +1,17 @@
|
|
1
1
|
class Class
|
2
2
|
|
3
|
-
#
|
4
|
-
# Return a copy of the class with modules mixed into it.
|
5
|
-
#
|
6
|
-
def self.using(*args)
|
7
|
-
if block_given?
|
8
|
-
yield using(*args)
|
9
|
-
else
|
10
|
-
copy = self.dup
|
11
|
-
args.each { |arg| copy.send(:include, arg) }
|
12
|
-
copy
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
3
|
#
|
18
4
|
# Trace the specified method calls (`meths`, as symbols) to descendends of this class (or all methods if `:*` is supplied).
|
19
5
|
# Output is printed to $stderr.
|
20
6
|
#
|
21
7
|
def trace_messages_to(*meths)
|
22
8
|
return unless $DEBUG
|
23
|
-
|
9
|
+
|
24
10
|
tracers = Module.new
|
25
11
|
parent = self
|
26
12
|
|
27
13
|
$stderr.puts "[*] Tracing messages sent to #{parent} (messages: #{meths.join(", ")})"
|
28
|
-
|
14
|
+
|
29
15
|
meths.each do |meth|
|
30
16
|
case meth
|
31
17
|
when :*
|
@@ -35,9 +21,9 @@ class Class
|
|
35
21
|
end
|
36
22
|
else
|
37
23
|
tracers.define_method(meth) do |*args, &block|
|
38
|
-
|
39
|
-
|
40
|
-
$stderr.puts "[*] #{parent}##{meth}(#{
|
24
|
+
arg_names = args.map(&:inspect)
|
25
|
+
arg_names << "&block" if block
|
26
|
+
$stderr.puts "[*] #{parent}##{meth}(#{arg_names.join(", ")})"
|
41
27
|
if block
|
42
28
|
super(*args, &block)
|
43
29
|
else
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Number = Numeric # "obj.is_a? Number"
|
2
|
+
Number = Numeric # because "obj.is_a? Number" sounds better!
|
3
3
|
|
4
4
|
class Numeric
|
5
5
|
|
@@ -84,7 +84,8 @@ class Numeric
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
[
|
87
|
+
[
|
88
|
+
:cos,
|
88
89
|
:sin,
|
89
90
|
:tan,
|
90
91
|
:acos,
|
@@ -186,7 +187,8 @@ class Numeric
|
|
186
187
|
end
|
187
188
|
|
188
189
|
BYTE_SIZE_TABLE = {
|
189
|
-
# power
|
190
|
+
# power
|
191
|
+
# of 1024 # units
|
190
192
|
0 => "",
|
191
193
|
1 => "KB",
|
192
194
|
2 => "MB",
|
@@ -213,8 +215,8 @@ class Numeric
|
|
213
215
|
def to_hms
|
214
216
|
seconds = self
|
215
217
|
|
216
|
-
days,
|
217
|
-
hours,
|
218
|
+
days, seconds = seconds.divmod(86400)
|
219
|
+
hours, seconds = seconds.divmod(3600)
|
218
220
|
minutes, seconds = seconds.divmod(60)
|
219
221
|
seconds, frac = seconds.divmod(1)
|
220
222
|
|
@@ -229,8 +231,8 @@ class Numeric
|
|
229
231
|
def to_hms_in_words
|
230
232
|
seconds = self
|
231
233
|
|
232
|
-
days,
|
233
|
-
hours,
|
234
|
+
days, seconds = seconds.divmod(86400)
|
235
|
+
hours, seconds = seconds.divmod(3600)
|
234
236
|
minutes, seconds = seconds.divmod(60)
|
235
237
|
seconds, frac = seconds.divmod(1)
|
236
238
|
|
@@ -243,6 +245,22 @@ class Numeric
|
|
243
245
|
result
|
244
246
|
end
|
245
247
|
|
248
|
+
def to_farenheit
|
249
|
+
(self * 9.0 / 5.0) + 32
|
250
|
+
end
|
251
|
+
|
252
|
+
def to_celcius
|
253
|
+
(self - 32) * 5.0 / 9.0
|
254
|
+
end
|
255
|
+
|
256
|
+
def to_lbs
|
257
|
+
self / 0.45359237
|
258
|
+
end
|
259
|
+
|
260
|
+
def to_kg
|
261
|
+
self * 0.45359237
|
262
|
+
end
|
263
|
+
|
246
264
|
end
|
247
265
|
|
248
266
|
|
@@ -442,7 +460,8 @@ end
|
|
442
460
|
class Prime
|
443
461
|
|
444
462
|
#
|
445
|
-
# Return an array of prime numbers within the specified range
|
463
|
+
# Return an array of prime numbers within the specified range.
|
464
|
+
# (It still has to generate all the primes less than the lower bound, so, yeah... be warned.)
|
446
465
|
#
|
447
466
|
def [](range)
|
448
467
|
ubound = range.end
|
@@ -130,10 +130,9 @@ class String
|
|
130
130
|
#
|
131
131
|
# Is there anything in the string? (ignoring whitespace/newlines)
|
132
132
|
#
|
133
|
-
def
|
133
|
+
def present?
|
134
134
|
not blank?
|
135
135
|
end
|
136
|
-
alias_method :present?, :any?
|
137
136
|
|
138
137
|
#
|
139
138
|
# Does this string contain something that means roughly "true"?
|
@@ -1,35 +1,58 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
|
-
|
3
|
+
class URI::Generic
|
4
|
+
|
5
|
+
#
|
6
|
+
# Get the query string
|
7
|
+
#
|
8
|
+
def query
|
9
|
+
params.to_query
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Set the query string
|
14
|
+
#
|
15
|
+
def query=(new_query)
|
16
|
+
@params = new_query&.to_params
|
17
|
+
@query = new_query
|
18
|
+
end
|
4
19
|
|
5
20
|
#
|
6
21
|
# Return a Hash of the variables in the query string
|
7
22
|
#
|
8
23
|
def params
|
9
|
-
(@query ? @query.to_params : {})
|
24
|
+
@params ||= (@query ? @query.to_params : {})
|
10
25
|
end
|
11
26
|
|
12
27
|
#
|
13
|
-
#
|
14
|
-
# NB: This is super slow. To make it faster, store params directly in a locally cached dict, and only call `to_query` when query is accesed, or to_s/inspect are called
|
28
|
+
# Update all the params at once
|
15
29
|
#
|
16
|
-
def params=(
|
17
|
-
|
18
|
-
|
19
|
-
|
30
|
+
def params=(new_params)
|
31
|
+
# self.query = new_params.to_params
|
32
|
+
raise "params must be a Hash" unless new_params.is_a? Hash
|
33
|
+
@params = new_params
|
20
34
|
end
|
21
35
|
|
22
|
-
|
23
|
-
|
24
|
-
|
36
|
+
# #
|
37
|
+
# # Update one URI parameter
|
38
|
+
# #
|
39
|
+
# def set_param(key, value)
|
40
|
+
# current = params
|
41
|
+
# current[key] = value
|
42
|
+
# self.query = current.to_query
|
43
|
+
# end
|
25
44
|
|
26
45
|
#
|
27
|
-
# URIs are strings, dammit!
|
46
|
+
# URIs *are* strings, dammit!
|
28
47
|
#
|
29
48
|
def to_str
|
30
49
|
to_s
|
31
50
|
end
|
32
51
|
|
52
|
+
end
|
53
|
+
|
54
|
+
module URI
|
55
|
+
|
33
56
|
#
|
34
57
|
# Default user agent for the 'get' method
|
35
58
|
#
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#
|
2
|
+
# Runs many jobs in parallel, and returns their interleaved results.
|
3
|
+
# (NOTE: The JobRunner can be run multiple times; each time the blocks
|
4
|
+
# will be executed again.)
|
5
|
+
#
|
6
|
+
# Examples:
|
7
|
+
#
|
8
|
+
# JobRunner.new do |jr|
|
9
|
+
# jr.add { 3 }
|
10
|
+
# jr.add { sleep 0.1; 2 }
|
11
|
+
# jr.add { sleep 0.2; 1 }
|
12
|
+
#
|
13
|
+
# jr.each_result do |result|
|
14
|
+
# p result
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# jr = JobRunner.new(
|
19
|
+
# proc { 1 },
|
20
|
+
# proc { 2 },
|
21
|
+
# proc { 3 }
|
22
|
+
# )
|
23
|
+
#
|
24
|
+
# 2.times do
|
25
|
+
# jr.each_result { |result| p result }
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
class JobRunner
|
29
|
+
def initialize(*blocks, debug: false)
|
30
|
+
@threads = []
|
31
|
+
@results = Thread::Queue.new
|
32
|
+
@jobs = []
|
33
|
+
@started = false
|
34
|
+
@debug = debug
|
35
|
+
|
36
|
+
if blocks.any?
|
37
|
+
blocks.each { |block| add &block }
|
38
|
+
else
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def dmsg(msg)
|
44
|
+
puts "[#{Time.now}] #{msg}" if @debug
|
45
|
+
end
|
46
|
+
|
47
|
+
def add(&block)
|
48
|
+
dmsg("added job #{block}")
|
49
|
+
@jobs << block
|
50
|
+
end
|
51
|
+
|
52
|
+
def reap!
|
53
|
+
if @threads.any?
|
54
|
+
dmsg("reaping #{@threads.size} threads")
|
55
|
+
@threads.delete_if { |t| not t.alive? }
|
56
|
+
else
|
57
|
+
dmsg("reap failed: no threads")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def go!
|
62
|
+
if @started
|
63
|
+
raise "Error: already started"
|
64
|
+
else
|
65
|
+
dmsg("starting #{@threads.size} jobs")
|
66
|
+
end
|
67
|
+
|
68
|
+
@started = true
|
69
|
+
@jobs.each do |job|
|
70
|
+
dmsg("adding #{job}")
|
71
|
+
@threads << Thread.new do
|
72
|
+
@results << job.call
|
73
|
+
dmsg("job #{job} complete")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def each_result
|
79
|
+
go! unless @started
|
80
|
+
|
81
|
+
loop do
|
82
|
+
yield @results.pop
|
83
|
+
reap!
|
84
|
+
break if @threads.empty? and @results.empty?
|
85
|
+
end
|
86
|
+
|
87
|
+
@started = false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
if __FILE__ == $0
|
93
|
+
JobRunner.new(debug: true) do |jr|
|
94
|
+
jr.add { 3 }
|
95
|
+
jr.add { sleep 0.1; 2 }
|
96
|
+
jr.add { sleep 0.2; 1 }
|
97
|
+
|
98
|
+
jr.each_result do |result|
|
99
|
+
p result
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
puts
|
104
|
+
|
105
|
+
jr = JobRunner.new(
|
106
|
+
proc { 1 },
|
107
|
+
proc { 2 },
|
108
|
+
proc { 3 }
|
109
|
+
)
|
110
|
+
|
111
|
+
2.times do
|
112
|
+
jr.each_result { |r| p r }
|
113
|
+
puts
|
114
|
+
end
|
115
|
+
end
|
data/lib/epitools/numwords.rb
CHANGED
data/lib/epitools/path.rb
CHANGED
@@ -119,11 +119,20 @@ class Path
|
|
119
119
|
# The file extension, including the . (eg: ".mp3")
|
120
120
|
attr_reader :ext
|
121
121
|
|
122
|
+
URI_RE = %r{^[a-z\-]+://}i
|
122
123
|
|
123
124
|
###############################################################################
|
124
125
|
# Initializers
|
125
126
|
###############################################################################
|
126
127
|
|
128
|
+
def self.new(*args)
|
129
|
+
if args.first =~ URI_RE and self != Path::URI
|
130
|
+
Path::URI.new(args.first)
|
131
|
+
else
|
132
|
+
super(*args)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
127
136
|
def initialize(newpath, hints={})
|
128
137
|
send("path=", newpath, hints)
|
129
138
|
|
@@ -156,8 +165,8 @@ class Path
|
|
156
165
|
path
|
157
166
|
when String
|
158
167
|
|
159
|
-
if path =~
|
160
|
-
Path
|
168
|
+
if path =~ URI_RE
|
169
|
+
Path.new(path)
|
161
170
|
|
162
171
|
else
|
163
172
|
# TODO: highlight backgrounds of codeblocks to show indent level & put boxes (or rules?) around (between?) double-spaced regions
|
@@ -186,7 +195,7 @@ class Path
|
|
186
195
|
# Note: The `hints` parameter contains options so `path=` doesn't have to touch the filesytem as much.
|
187
196
|
#
|
188
197
|
def path=(newpath, hints={})
|
189
|
-
if hints[:type] or File.
|
198
|
+
if hints[:type] or File.exist? newpath
|
190
199
|
if hints[:type] == :dir or File.directory? newpath
|
191
200
|
self.dir = newpath
|
192
201
|
else
|
@@ -357,7 +366,7 @@ class Path
|
|
357
366
|
###############################################################################
|
358
367
|
|
359
368
|
def exists?
|
360
|
-
File.
|
369
|
+
File.exist? path
|
361
370
|
end
|
362
371
|
|
363
372
|
def size
|
@@ -430,7 +439,7 @@ class Path
|
|
430
439
|
end
|
431
440
|
|
432
441
|
def broken_symlink?
|
433
|
-
File.symlink?(path) and not File.
|
442
|
+
File.symlink?(path) and not File.exist?(path)
|
434
443
|
end
|
435
444
|
|
436
445
|
def symlink_target
|
@@ -1638,9 +1647,9 @@ class Path::URI < Path
|
|
1638
1647
|
#
|
1639
1648
|
# When this is: http://host.com:port/path/filename.ext?param1=value1¶m2=value2&...
|
1640
1649
|
#
|
1641
|
-
def to_s
|
1642
|
-
|
1643
|
-
end
|
1650
|
+
def to_s; uri.to_s; end
|
1651
|
+
def to_path; to_s; end
|
1652
|
+
def to_str; to_s; end
|
1644
1653
|
|
1645
1654
|
def inspect
|
1646
1655
|
"#<Path::URI:#{to_s}>"
|
@@ -1693,9 +1702,9 @@ class Path::URI < Path
|
|
1693
1702
|
def open(mode="r", &block)
|
1694
1703
|
require 'open-uri'
|
1695
1704
|
if block_given?
|
1696
|
-
|
1705
|
+
::URI.open(to_s, mode, &block)
|
1697
1706
|
else
|
1698
|
-
|
1707
|
+
::URI.open(to_s, mode)
|
1699
1708
|
end
|
1700
1709
|
end
|
1701
1710
|
|
data/lib/epitools/sys.rb
CHANGED
data/spec/core_ext_spec.rb
CHANGED
@@ -127,26 +127,6 @@ end
|
|
127
127
|
|
128
128
|
describe Class do
|
129
129
|
|
130
|
-
it "uses" do
|
131
|
-
module Test1
|
132
|
-
def test1; :test1; end
|
133
|
-
end
|
134
|
-
|
135
|
-
module Test2
|
136
|
-
def test2; :test2; end
|
137
|
-
end
|
138
|
-
|
139
|
-
Hash.using(Test1).new.test1.should == :test1
|
140
|
-
Hash.using(Test2).new.test2.should == :test2
|
141
|
-
h = Hash.using(Test1, Test2).new
|
142
|
-
h.test1.should == :test1
|
143
|
-
h.test2.should == :test2
|
144
|
-
|
145
|
-
Hash.using(Test1) do |h|
|
146
|
-
h.new.test1.should == :test1
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
130
|
it "traces messages (when $DEBUG is set)" do
|
151
131
|
$DEBUG = true
|
152
132
|
|
@@ -166,10 +146,10 @@ describe Class do
|
|
166
146
|
class ButtTest
|
167
147
|
trace_messages_to :*
|
168
148
|
|
169
|
-
def
|
149
|
+
def a
|
170
150
|
end
|
171
151
|
|
172
|
-
def
|
152
|
+
def b
|
173
153
|
end
|
174
154
|
end
|
175
155
|
|
@@ -197,7 +177,7 @@ describe Numeric do
|
|
197
177
|
-12983287123.commatize.should == "-12,983,287,123"
|
198
178
|
-12983287123.4411.commatize.should == "-12,983,287,123.4411"
|
199
179
|
1111.1234567.commatize.should == "1,111.1234567"
|
200
|
-
BigDecimal
|
180
|
+
BigDecimal("1111.1234567").commatize.should == "1,111.1234567"
|
201
181
|
-1234567.1234567.underscorize.should == "-1_234_567.1234567"
|
202
182
|
end
|
203
183
|
|
@@ -237,6 +217,11 @@ describe Numeric do
|
|
237
217
|
32583128.human_size(2).should == "31.07MB"
|
238
218
|
end
|
239
219
|
|
220
|
+
it "temperatures" do
|
221
|
+
t = 18.0
|
222
|
+
t.to_farenheit.to_celcius.should be_within(0.001).of(t)
|
223
|
+
end
|
224
|
+
|
240
225
|
end
|
241
226
|
|
242
227
|
|
@@ -795,6 +780,38 @@ describe Enumerable do
|
|
795
780
|
ps.sort_numerically.map(&:filename).should == proper
|
796
781
|
end
|
797
782
|
|
783
|
+
it "to_csvs and to_tsvs" do
|
784
|
+
data = [
|
785
|
+
["1", "2", "3"],
|
786
|
+
["4", "5", "6"],
|
787
|
+
["7", "8", "9", "10"],
|
788
|
+
]
|
789
|
+
|
790
|
+
hash_data = [
|
791
|
+
{a: 1, b: 2, c: 3},
|
792
|
+
{a: 4, b: 5, c: 6},
|
793
|
+
{a: 7, b: 8, c: 9, d: 10},
|
794
|
+
]
|
795
|
+
|
796
|
+
class EnumedData
|
797
|
+
include Enumerable
|
798
|
+
def initialize(data); @data=data; end
|
799
|
+
def each(&block); @data.each(&block); end
|
800
|
+
end
|
801
|
+
|
802
|
+
[data, hash_data, EnumedData.new(data)].each do |a|
|
803
|
+
str = a.to_csv
|
804
|
+
str.each_line.count.should == 3
|
805
|
+
str.should_not be_nil
|
806
|
+
str["1,2,3"].should_not be_nil
|
807
|
+
|
808
|
+
str = a.to_tsv
|
809
|
+
str.should_not be_nil
|
810
|
+
str["1\t2\t3"].should_not be_nil
|
811
|
+
end
|
812
|
+
|
813
|
+
end
|
814
|
+
|
798
815
|
end
|
799
816
|
|
800
817
|
|
@@ -1279,18 +1296,24 @@ describe URI do
|
|
1279
1296
|
uri.params.should == opts
|
1280
1297
|
end
|
1281
1298
|
|
1282
|
-
it "gets" do
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
end
|
1299
|
+
# it "gets" do
|
1300
|
+
# response = URI("http://google.com/").get
|
1301
|
+
# response.body.size
|
1302
|
+
# (response.size > 0).should == true
|
1303
|
+
# end
|
1287
1304
|
|
1288
1305
|
it "params=" do
|
1289
|
-
u = "http://butt.
|
1306
|
+
u = "http://butt.cx/?q=1".to_uri
|
1290
1307
|
u.query.should == "q=1"
|
1308
|
+
u.params.should == {"q" => "1"}
|
1291
1309
|
u.params["q"] = 2
|
1292
1310
|
u.params["q"].should == 2
|
1311
|
+
u.params.should == {"q" => 2}
|
1293
1312
|
u.query.should == "q=2"
|
1313
|
+
|
1314
|
+
subbed = u.with(query: u.params.reject{|k,v| u.params.keys.include? 'q' }.to_query)
|
1315
|
+
subbed.params.should == {}
|
1316
|
+
subbed.query.should == ""
|
1294
1317
|
end
|
1295
1318
|
|
1296
1319
|
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.133
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- lib/epitools/hexdump.rb
|
73
73
|
- lib/epitools/iter.rb
|
74
74
|
- lib/epitools/its.rb
|
75
|
+
- lib/epitools/job_runner.rb
|
75
76
|
- lib/epitools/lcs.rb
|
76
77
|
- lib/epitools/mimemagic.rb
|
77
78
|
- lib/epitools/mimemagic_tables.rb
|
@@ -126,7 +127,7 @@ homepage: http://github.com/epitron/epitools
|
|
126
127
|
licenses:
|
127
128
|
- WTFPL
|
128
129
|
metadata: {}
|
129
|
-
post_install_message:
|
130
|
+
post_install_message:
|
130
131
|
rdoc_options: []
|
131
132
|
require_paths:
|
132
133
|
- lib
|
@@ -141,8 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
142
|
- !ruby/object:Gem::Version
|
142
143
|
version: '0'
|
143
144
|
requirements: []
|
144
|
-
rubygems_version: 3.
|
145
|
-
signing_key:
|
145
|
+
rubygems_version: 3.2.15
|
146
|
+
signing_key:
|
146
147
|
specification_version: 3
|
147
148
|
summary: Not utils... METILS!
|
148
149
|
test_files: []
|