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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2aefbc7936ad578ce3f2ae8806e67be1815f4e2a55ddf43a915bcdaa0ca9f68a
4
- data.tar.gz: 6d09789e434d7cda9b66e25ecf1ebf9db61d6236f1342b83743330e42d9ab09f
3
+ metadata.gz: 8b483b258d704fbf993bbab386612c813ae695444da9badcd8797c4fcebdc877
4
+ data.tar.gz: dbdf278b2e463cf45803cd0278243ee1e2bf7737879bdd2fd403eb340a7a5613
5
5
  SHA512:
6
- metadata.gz: 4c98a36281937b7dd6e8f6154af595f5153d51803da0587eafc1a8dec26c96adac899e73c9ef49157b8fadcdf01fb845b60095d9ceb9722b5107c0eeaf6f8550
7
- data.tar.gz: ccf09c13ef7df31e8d6994ba1c5550dc606fd61ac909c14f00c53788160a69cb53e7c425156a373732f4fbc7e021bde12c27e850be0ca1cf4735b86d9cf01285
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 --local epitools-#{gem_version}.gem"
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 -fd -c spec]
20
+ cmd = %w[rspec --format documentation --force-color --pattern spec/*_spec.rb]
21
+ cmd.unshift "rescue" if system *%w[which rescue]
21
22
 
22
- if system *%w[which rescue]
23
- system *(["rescue"]+cmd)
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.134
1
+ 0.5.137
@@ -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 :Ratio, 'epitools/ratio'
72
+ autoload :Fraction, 'epitools/fraction'
73
73
  autoload :ProgressBar, 'epitools/progressbar'
74
74
  autoload :Trie, 'epitools/trie'
75
75
  autoload :MimeMagic, 'epitools/mimemagic'
@@ -37,7 +37,7 @@ class Browser
37
37
  end
38
38
 
39
39
 
40
- def put(page, original_url=nil, options={})
40
+ def put(page, original_url=nil, **options)
41
41
  dmsg [:put, original_url]
42
42
 
43
43
  raise "Invalid page" unless valid_page?(page)
@@ -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.exists? @cookie_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
@@ -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.exists? city_data
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.exists? country_data
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.exists? full_path
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
@@ -27,6 +27,14 @@ class IO
27
27
  end
28
28
  end
29
29
 
30
+ #
31
+ # Unmarshal the IO (transform it into Ruby datatypes).
32
+ #
33
+ def unmarshal
34
+ Marshal.load self
35
+ end
36
+ alias_method :from_marshal, :unmarshal
37
+
30
38
  end
31
39
 
32
40
 
@@ -172,6 +172,11 @@ class DateTime
172
172
  def to_f; to_time.to_f; end
173
173
  end
174
174
 
175
+ class Date
176
+ def inspect
177
+ strftime("#<Date: %Y-%m-%d (%a %b %d, %Y)>")
178
+ end
179
+ end
175
180
 
176
181
  class NilClass
177
182
 
@@ -33,17 +33,28 @@ class Numeric
33
33
  # 12.clamp(0..100) #=> 12
34
34
  # -38817112.clamp(0..100) #=> 0
35
35
  #
36
- def clamp(range)
37
- if self < range.first
38
- range.first
39
- elsif self >= range.last
40
- if range.exclude_end?
41
- range.last - 1
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
- range.last
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=0)
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
- # The Infamous Caesar-Cipher. Unbreakable to this day.
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
- URI.unescape(self)
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.restore self
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
  #
@@ -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'