epitools 0.5.1 → 0.5.2
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.
- data/Guardfile +16 -0
- data/VERSION +1 -1
- data/epitools.gemspec +13 -6
- data/lib/epitools.rb +48 -6
- data/lib/epitools/autoloads.rb +14 -3
- data/lib/epitools/browser.rb +8 -1
- data/lib/epitools/core_ext.rb +207 -0
- data/lib/epitools/core_ext/array.rb +98 -0
- data/lib/epitools/core_ext/enumerable.rb +306 -0
- data/lib/epitools/core_ext/hash.rb +201 -0
- data/lib/epitools/core_ext/numbers.rb +254 -0
- data/lib/epitools/core_ext/object.rb +195 -0
- data/lib/epitools/core_ext/string.rb +338 -0
- data/lib/epitools/core_ext/truthiness.rb +64 -0
- data/lib/epitools/iter.rb +22 -8
- data/lib/epitools/mimemagic.rb +0 -1
- data/lib/epitools/path.rb +149 -36
- data/lib/epitools/rash.rb +49 -37
- data/lib/epitools/term.rb +5 -1
- data/spec/{basetypes_spec.rb → core_ext_spec.rb} +190 -37
- data/spec/iter_spec.rb +9 -27
- data/spec/path_spec.rb +104 -46
- data/spec/permutations_spec.rb +3 -2
- data/spec/rash_spec.rb +21 -7
- data/spec/spec_helper.rb +36 -5
- metadata +34 -12
- data/lib/epitools/basetypes.rb +0 -1135
- data/lib/epitools/string_to_proc.rb +0 -78
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
class Object
|
3
|
+
|
4
|
+
#
|
5
|
+
# Default "integer?" behaviour.
|
6
|
+
#
|
7
|
+
def integer?; false; end
|
8
|
+
|
9
|
+
#
|
10
|
+
# `truthy?` means `not blank?`
|
11
|
+
#
|
12
|
+
def truthy?
|
13
|
+
if respond_to? :blank?
|
14
|
+
not blank?
|
15
|
+
else
|
16
|
+
not nil?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
class TrueClass
|
24
|
+
|
25
|
+
def truthy?; true; end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
class FalseClass
|
31
|
+
|
32
|
+
def truthy?; false; end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class Float
|
38
|
+
|
39
|
+
#
|
40
|
+
# 'true' if the float is 0.0
|
41
|
+
#
|
42
|
+
def blank?; self == 0.0; end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class NilClass
|
48
|
+
|
49
|
+
#
|
50
|
+
# Always 'true'; nil is considered blank.
|
51
|
+
#
|
52
|
+
def blank?; true; end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
class Symbol
|
58
|
+
|
59
|
+
#
|
60
|
+
# Symbols are never blank.
|
61
|
+
#
|
62
|
+
def blank?; false; end
|
63
|
+
|
64
|
+
end
|
data/lib/epitools/iter.rb
CHANGED
@@ -12,11 +12,16 @@ class Iter
|
|
12
12
|
attr_accessor :container
|
13
13
|
|
14
14
|
def initialize(vals)
|
15
|
-
@container = vals.map{|val|
|
15
|
+
@container = vals.map { |val| elem(val) }
|
16
16
|
end
|
17
17
|
|
18
|
+
# Wrap a value in an Elem container
|
19
|
+
def elem(val)
|
20
|
+
Elem.new(self, val)
|
21
|
+
end
|
22
|
+
|
18
23
|
def self.from_elems(elems)
|
19
|
-
new([]).tap{|i| i.container = elems}
|
24
|
+
new([]).tap { |i| i.container = elems }
|
20
25
|
end
|
21
26
|
|
22
27
|
def ==(other)
|
@@ -31,12 +36,14 @@ class Iter
|
|
31
36
|
def each
|
32
37
|
@container.each do |elem|
|
33
38
|
yield elem
|
39
|
+
elem.visited = true
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
37
43
|
def each_cons(num=1)
|
38
44
|
@container.each_cons(num) do |(*elems)|
|
39
45
|
yield *elems
|
46
|
+
elems.each { |e| e.visited = true }
|
40
47
|
end
|
41
48
|
end
|
42
49
|
|
@@ -59,11 +66,16 @@ class Iter
|
|
59
66
|
|
60
67
|
class Elem < BasicObject
|
61
68
|
|
62
|
-
attr_accessor :val
|
63
|
-
|
69
|
+
attr_accessor :val, :visited
|
70
|
+
|
64
71
|
def initialize(iter, val)
|
65
72
|
@iter = iter
|
66
|
-
@val = val
|
73
|
+
@val = val.is_a?(Elem) ? val.value : val
|
74
|
+
@visited = false
|
75
|
+
end
|
76
|
+
|
77
|
+
def elem?
|
78
|
+
true
|
67
79
|
end
|
68
80
|
|
69
81
|
def ==(other)
|
@@ -78,6 +90,10 @@ class Iter
|
|
78
90
|
self
|
79
91
|
end
|
80
92
|
|
93
|
+
def visited?
|
94
|
+
@visited
|
95
|
+
end
|
96
|
+
|
81
97
|
def next
|
82
98
|
p = pos+1
|
83
99
|
if p >= container.size
|
@@ -143,7 +159,5 @@ class Iter
|
|
143
159
|
"<Elem: #{@val.inspect}>"
|
144
160
|
end
|
145
161
|
end
|
146
|
-
|
147
162
|
|
148
|
-
end
|
149
|
-
|
163
|
+
end
|
data/lib/epitools/mimemagic.rb
CHANGED
data/lib/epitools/path.rb
CHANGED
@@ -8,14 +8,13 @@
|
|
8
8
|
require 'epitools'
|
9
9
|
|
10
10
|
#
|
11
|
-
# Path: An object-oriented wrapper for files.
|
12
|
-
# (Combines useful methods from FileUtils, File, Dir, and more!)
|
11
|
+
# Path: An object-oriented wrapper for files. (Combines useful methods from FileUtils, File, Dir, and more!)
|
13
12
|
#
|
14
13
|
# Each Path object has the following attributes:
|
15
14
|
#
|
16
15
|
# path => the entire path
|
17
16
|
# filename => just the name and extension
|
18
|
-
# basename => just the filename
|
17
|
+
# basename => just the filename (without extension)
|
19
18
|
# ext => just the extension
|
20
19
|
# dir => just the directory
|
21
20
|
# dirs => an array of directories
|
@@ -33,25 +32,27 @@ require 'epitools'
|
|
33
32
|
# false
|
34
33
|
# end
|
35
34
|
#
|
36
|
-
#
|
37
|
-
# Examples:
|
35
|
+
# More examples:
|
38
36
|
#
|
39
37
|
# Path["*.jpeg"].each { |path| path.rename(:ext=>"jpg") }
|
40
|
-
#
|
41
38
|
# Path["filename.txt"] << "Append data!"
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# Path
|
39
|
+
# etcfiles = Path["/etc"].ls
|
40
|
+
# Path["*.txt"].each(&:gzip)
|
46
41
|
#
|
47
42
|
# Swap two files:
|
48
43
|
#
|
49
44
|
# a, b = Path["file_a", "file_b"]
|
50
|
-
# temp = a.with(:ext=>a.ext+".swapping")
|
45
|
+
# temp = a.with(:ext=>a.ext+".swapping") # return a modified version of this object
|
51
46
|
# a.mv(temp)
|
52
47
|
# b.mv(a)
|
53
48
|
# temp.mv(b)
|
54
|
-
#
|
49
|
+
#
|
50
|
+
# Paths can be created for existant and non-existant files. If you want to create a nonexistant
|
51
|
+
# directory, just add a '/' at the end, so Path knows. (eg: Path["/etc/nonexistant/"]).
|
52
|
+
#
|
53
|
+
# Performance has been an important factor in Path's design, so doing crazy things with Path
|
54
|
+
# usually doesn't kill your machine. Go nuts!
|
55
|
+
#
|
55
56
|
#
|
56
57
|
class Path
|
57
58
|
|
@@ -170,8 +171,27 @@ class Path
|
|
170
171
|
""
|
171
172
|
end
|
172
173
|
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Path relative to current directory (Path.pwd)
|
177
|
+
#
|
178
|
+
def relative
|
179
|
+
relative_to(pwd)
|
180
|
+
end
|
181
|
+
|
182
|
+
def relative_to(to)
|
183
|
+
from = path.split(File::SEPARATOR)
|
184
|
+
to = Path[to].path.split(File::SEPARATOR)
|
185
|
+
p [from, to]
|
186
|
+
from.length.times do
|
187
|
+
break if from[0] != to[0]
|
188
|
+
from.shift; to.shift
|
189
|
+
end
|
190
|
+
fname = from.pop
|
191
|
+
join(*(from.map { RELATIVE_PARENTDIR } + to))
|
192
|
+
end
|
173
193
|
|
174
|
-
def
|
194
|
+
def relative_to2(anchor=nil)
|
175
195
|
anchor ||= Path.pwd
|
176
196
|
|
177
197
|
# operations to transform anchor into self
|
@@ -231,15 +251,15 @@ class Path
|
|
231
251
|
end
|
232
252
|
|
233
253
|
def mtime
|
234
|
-
|
254
|
+
lstat.mtime
|
235
255
|
end
|
236
256
|
|
237
257
|
def ctime
|
238
|
-
|
258
|
+
lstat.ctime path
|
239
259
|
end
|
240
260
|
|
241
261
|
def atime
|
242
|
-
|
262
|
+
lstat.atime path
|
243
263
|
end
|
244
264
|
|
245
265
|
def dir?
|
@@ -258,6 +278,11 @@ class Path
|
|
258
278
|
File.symlink?(path) and not File.exists?(path)
|
259
279
|
end
|
260
280
|
|
281
|
+
def symlink_target
|
282
|
+
Path.new File.readlink(path)
|
283
|
+
end
|
284
|
+
alias_method :readlink, :symlink_target
|
285
|
+
|
261
286
|
def uri?
|
262
287
|
false
|
263
288
|
end
|
@@ -322,7 +347,7 @@ class Path
|
|
322
347
|
def read(length=nil, offset=nil)
|
323
348
|
File.read(path, length, offset)
|
324
349
|
end
|
325
|
-
|
350
|
+
|
326
351
|
#
|
327
352
|
# All the lines in this file, chomped.
|
328
353
|
#
|
@@ -386,6 +411,79 @@ class Path
|
|
386
411
|
end
|
387
412
|
end
|
388
413
|
end
|
414
|
+
|
415
|
+
#
|
416
|
+
# Parse the file based on the file extension.
|
417
|
+
# (Handles json, html, yaml, marshal.)
|
418
|
+
#
|
419
|
+
def parse
|
420
|
+
case ext.downcase
|
421
|
+
when 'json'
|
422
|
+
read_json
|
423
|
+
when 'html', 'htm'
|
424
|
+
read_html
|
425
|
+
when 'xml', 'rdf', 'rss'
|
426
|
+
read_xml
|
427
|
+
when 'yaml', 'yml'
|
428
|
+
read_yaml
|
429
|
+
when 'marshal'
|
430
|
+
read_marshal
|
431
|
+
when 'bson'
|
432
|
+
read_bson
|
433
|
+
else
|
434
|
+
raise "Unrecognized format: #{ext}"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# Parse the file as JSON
|
439
|
+
def read_json
|
440
|
+
JSON.load(io)
|
441
|
+
end
|
442
|
+
|
443
|
+
# Convert the object to JSON and write it to the file (overwriting the existing file).
|
444
|
+
def write_json(object)
|
445
|
+
write object.to_json
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
def read_html
|
450
|
+
Nokogiri::HTML(io)
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
# Convert the object to YAML and write it to the file (overwriting the existing file).
|
455
|
+
def write_yaml(object)
|
456
|
+
write object.to_yaml
|
457
|
+
end
|
458
|
+
|
459
|
+
# Parse the file as YAML
|
460
|
+
def read_yaml
|
461
|
+
YAML.load(io)
|
462
|
+
end
|
463
|
+
|
464
|
+
|
465
|
+
def read_xml
|
466
|
+
Nokogiri::XML(io)
|
467
|
+
end
|
468
|
+
|
469
|
+
|
470
|
+
def read_marshal
|
471
|
+
Marshal.load(io)
|
472
|
+
end
|
473
|
+
|
474
|
+
def write_marshal(object)
|
475
|
+
write object.marshal
|
476
|
+
end
|
477
|
+
|
478
|
+
|
479
|
+
def read_bson
|
480
|
+
BSON.deserialize(read)
|
481
|
+
end
|
482
|
+
|
483
|
+
def write_bson(object)
|
484
|
+
write BSON.serialize(object)
|
485
|
+
end
|
486
|
+
|
389
487
|
|
390
488
|
#
|
391
489
|
# Examples:
|
@@ -514,6 +612,33 @@ raise "Broken!"
|
|
514
612
|
end
|
515
613
|
end
|
516
614
|
|
615
|
+
def lstat
|
616
|
+
@lstat ||= File.lstat self # to cache or not to cache -- that is the question.
|
617
|
+
#File.lstat self
|
618
|
+
end
|
619
|
+
|
620
|
+
def mode
|
621
|
+
lstat.mode
|
622
|
+
end
|
623
|
+
|
624
|
+
# TODO: Unstub
|
625
|
+
def owner?
|
626
|
+
raise "STUB"
|
627
|
+
end
|
628
|
+
|
629
|
+
def executable?
|
630
|
+
mode & 0o111 > 0
|
631
|
+
end
|
632
|
+
alias_method :exe?, :executable?
|
633
|
+
|
634
|
+
def writable?
|
635
|
+
mode & 0o222 > 0
|
636
|
+
end
|
637
|
+
|
638
|
+
def readable?
|
639
|
+
mode & 0o444 > 0
|
640
|
+
end
|
641
|
+
|
517
642
|
## Dangerous methods.
|
518
643
|
|
519
644
|
def rm
|
@@ -602,15 +727,6 @@ raise "Broken!"
|
|
602
727
|
to_s =~ pattern
|
603
728
|
end
|
604
729
|
|
605
|
-
def lstat
|
606
|
-
#@lstat ||= File.lstat self # to cache or not to cache -- that is the question.
|
607
|
-
File.lstat self
|
608
|
-
end
|
609
|
-
|
610
|
-
def mode
|
611
|
-
lstat.mode
|
612
|
-
end
|
613
|
-
|
614
730
|
#
|
615
731
|
# Find the parent directory. If the `Path` is a filename, it returns the containing directory.
|
616
732
|
#
|
@@ -637,8 +753,6 @@ raise "Broken!"
|
|
637
753
|
end
|
638
754
|
|
639
755
|
|
640
|
-
# Mimetype finding and magic (requires 'mimemagic' gem)
|
641
|
-
|
642
756
|
#
|
643
757
|
# Find the file's mimetype (first from file extension, then by magic)
|
644
758
|
#
|
@@ -661,16 +775,16 @@ raise "Broken!"
|
|
661
775
|
open { |io| MimeMagic.by_magic(io) }
|
662
776
|
end
|
663
777
|
|
664
|
-
# TODO: rename type => magicext
|
665
|
-
|
666
778
|
#
|
667
|
-
#
|
779
|
+
# Returns the filetype (as a standard file extension), verified with Magic.
|
668
780
|
#
|
669
781
|
# (In other words, this will give you the *true* extension, even if the file's
|
670
782
|
# extension is wrong.)
|
671
783
|
#
|
672
784
|
# Note: Prefers long extensions (eg: jpeg over jpg)
|
673
785
|
#
|
786
|
+
# TODO: rename type => magicext?
|
787
|
+
#
|
674
788
|
def type
|
675
789
|
@cached_type ||= begin
|
676
790
|
|
@@ -699,7 +813,8 @@ raise "Broken!"
|
|
699
813
|
|
700
814
|
end
|
701
815
|
end
|
702
|
-
|
816
|
+
|
817
|
+
|
703
818
|
## aliases
|
704
819
|
|
705
820
|
alias_method :to_path, :path
|
@@ -903,15 +1018,14 @@ class Path::URL < Path
|
|
903
1018
|
end
|
904
1019
|
|
905
1020
|
# ...and `path` is /path/filename.ext
|
906
|
-
|
907
1021
|
end
|
908
1022
|
|
909
1023
|
|
910
1024
|
#
|
911
1025
|
# Path("/some/path") is an alias for Path["/some/path"]
|
912
1026
|
#
|
913
|
-
def Path(
|
914
|
-
Path[
|
1027
|
+
def Path(arg)
|
1028
|
+
Path[arg]
|
915
1029
|
end
|
916
1030
|
|
917
1031
|
class String
|
@@ -921,4 +1035,3 @@ class String
|
|
921
1035
|
|
922
1036
|
alias_method :to_P, :to_Path
|
923
1037
|
end
|
924
|
-
|