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 +4 -4
- data/Rakefile +5 -7
- data/VERSION +1 -1
- data/lib/epitools/autoloads.rb +5 -2
- data/lib/epitools/browser/cache.rb +1 -1
- data/lib/epitools/browser.rb +4 -4
- data/lib/epitools/clitools.rb +21 -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/gem_ext/oga.rb +31 -0
- data/lib/epitools/hexdump.rb +1 -1
- data/lib/epitools/minimal.rb +2 -3
- data/lib/epitools/path.rb +34 -19
- 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 +8 -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'
|
@@ -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
|
|
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}"] }
|
@@ -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
|
@@ -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
|
@@ -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
|
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
|
####################################################################
|