epitools 0.5.133 → 0.5.136

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db1a5a887e1a937b76e39d061a8865503f7bfeb33f03094f5d7b8ce3749420e7
4
- data.tar.gz: b5075843f8bd030de2e5313e9af3f53f97cae26fc0b52a609a06137fc015324c
3
+ metadata.gz: 4c080c53832ae049dd0ac7abedc41844d515b759fd081f4541855359a21eb75a
4
+ data.tar.gz: f6871df3e1da9c2cdb315ab49a5c76cea1eb6dfc8f776d6a3f44b3e6d59844fd
5
5
  SHA512:
6
- metadata.gz: 0f13ae895b759edc3612970e335b743301bd102a4c73f619c16028acab0792a1d229d65ff40626d6c11334f95b020b055f9a18e6afc70a37ae74824b439c2f24
7
- data.tar.gz: 8bac272ee77ee4bd4c493b2b6f6c394e1143bd57e73b77196f9798326e73749b1c940622e22ea792875718b59e7a01380d2766296df5c19a3a259a3f7e34fcd3
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 --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.133
1
+ 0.5.136
@@ -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'
@@ -98,8 +98,11 @@ autoreq :Mechanize, 'mechanize'
98
98
  autoreq :HTTP, 'http'
99
99
 
100
100
  autoreq :Nokogiri, 'nokogiri'
101
- autoreq :Oga, 'oga'
102
101
  autoreq :Ox, 'ox'
102
+ autoreq :Oga do
103
+ require 'oga'
104
+ require 'epitools/gem_ext/oga'
105
+ end
103
106
 
104
107
  autoreq :ANSI, 'ansi'
105
108
 
@@ -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}"] }
@@ -247,3 +254,13 @@ end
247
254
  def curl_json(url)
248
255
  JSON.parse(curl(url))
249
256
  end
257
+
258
+ def cached_curl(url)
259
+ cache = "/tmp/curl-#{url.sha1}.cache"
260
+ if File.exist?(cache)
261
+ $stderr.puts "cached! => #{cache}"
262
+ else
263
+ File.write(cache, curl(url))
264
+ end
265
+ File.read(cache)
266
+ end
@@ -266,7 +266,7 @@ class Array
266
266
  # 5.0...9.0 => 5
267
267
  # }
268
268
  #
269
- def histogram(n_buckets=10, options={})
269
+ def histogram(n_buckets=10, **options)
270
270
 
271
271
  use_ranges = options[:ranges] || options[:hash]
272
272
 
@@ -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=0)
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
- URI.unescape(self)
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
@@ -0,0 +1,31 @@
1
+
2
+ module Oga
3
+ module XML
4
+ # Serialize this node to HAML
5
+ module ElementToHAML
6
+ def to_haml
7
+ require 'html2haml'
8
+ require 'html2haml/html'
9
+ Html2haml::HTML.new(to_xml, {}).render.rstrip
10
+ end
11
+ alias_method :haml, :to_haml
12
+
13
+ def pretty
14
+ require 'coderay'
15
+ puts CodeRay.scan(haml, :haml).term
16
+ end
17
+ #alias_method :pp, :pretty
18
+ end
19
+
20
+ module PrettyNodeSet
21
+ def pretty
22
+ require 'coderay'
23
+ lesspipe(wrap: true) { |less| each { |node| less.puts CodeRay.scan(node.to_haml, :haml).term; less.puts; less.puts } }
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+ Oga::XML::Element.include Oga::XML::ElementToHAML
31
+ Oga::XML::NodeSet.include Oga::XML::PrettyNodeSet
@@ -26,7 +26,7 @@ module Hex
26
26
  :default => 7
27
27
  )
28
28
 
29
- def self.dump(data, options={})
29
+ def self.dump(data, **options)
30
30
  base_offset = options[:base_offset] || 0
31
31
  color = options[:color].nil? ? true : options[:color]
32
32
  highlight = options[:highlight]
@@ -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 an alias for Path["/some/path"]
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
  ####################################################################