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
data/spec/iter_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'epitools/iter'
|
2
|
+
require 'pry'
|
2
3
|
|
3
4
|
describe Iter do
|
4
5
|
|
@@ -16,6 +17,7 @@ describe Iter do
|
|
16
17
|
@i.to_a.should == [1,2,3,4,5]
|
17
18
|
end
|
18
19
|
|
20
|
+
|
19
21
|
it "reverses" do
|
20
22
|
@i.iterate(2) do |a, b|
|
21
23
|
b.move_before(a)
|
@@ -39,6 +41,8 @@ describe Iter do
|
|
39
41
|
it "replaces" do
|
40
42
|
@i.first.replace_with(-1)
|
41
43
|
@i.to_a.should == [-1,2,3,4,5]
|
44
|
+
@i.last.replace_with(8)
|
45
|
+
@i.to_a.should == [-1,2,3,4,8]
|
42
46
|
end
|
43
47
|
|
44
48
|
it "slices, values, indexes, etc." do
|
@@ -58,32 +62,10 @@ describe Iter do
|
|
58
62
|
@i.should == [1,2,3,4,5]
|
59
63
|
end
|
60
64
|
|
61
|
-
it "
|
62
|
-
|
63
|
-
|
64
|
-
a, b = other.max, other.min
|
65
|
-
x, y = max, min
|
66
|
-
[a-x, a-y, b-x, b-y].map(&:abs).min
|
67
|
-
end
|
65
|
+
it "sorts an array" do
|
66
|
+
i = Iter.new [3,7,3,1,3]
|
67
|
+
i.each { |a|
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
sort!
|
72
|
-
other.clear
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
a = [1,2,5,6,7,10,11,13].map { |e| Cluster.new [e] }
|
77
|
-
i = Iter.new(a)
|
78
|
-
|
79
|
-
i.each_cons(2) do |a,b|
|
80
|
-
if b.any? and a.any? and a.min_distance(b) <= 1
|
81
|
-
b.absorb(a)
|
82
|
-
a.remove
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
i.to_a.should == [[1,2],[5,6,7],[10,11],[13]]
|
87
|
-
end
|
88
|
-
|
69
|
+
}
|
70
|
+
end
|
89
71
|
end
|
data/spec/path_spec.rb
CHANGED
@@ -5,35 +5,39 @@ describe Path do
|
|
5
5
|
it "initializes and accesses everything" do
|
6
6
|
path = Path.new("/blah/what.mp4/.mp3/hello.avi")
|
7
7
|
|
8
|
-
path.dirs.should
|
9
|
-
path.dir.should
|
8
|
+
path.dirs.should == %w[ blah what.mp4 .mp3 ]
|
9
|
+
path.dir.should == "/blah/what.mp4/.mp3"
|
10
10
|
path.filename.should == "hello.avi"
|
11
|
-
path.ext.should
|
12
|
-
path.base.should
|
11
|
+
path.ext.should == "avi"
|
12
|
+
path.base.should == "hello"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "works with relative paths" do
|
16
16
|
path = Path.new("../hello.mp3/blah")
|
17
17
|
|
18
18
|
path.filename.should == "blah"
|
19
|
-
path.ext.should
|
19
|
+
path.ext.should == nil
|
20
20
|
|
21
|
-
abs_path
|
22
|
-
path.dir.should
|
21
|
+
abs_path = File.join(File.expand_path(".."), "hello.mp3")
|
22
|
+
path.dir.should == abs_path
|
23
|
+
end
|
24
|
+
|
25
|
+
it "'relative_to's" do
|
26
|
+
Path["/etc"].relative_to(Path["/tmp"]).should == "../tmp"
|
23
27
|
end
|
24
28
|
|
25
29
|
it "handles directories" do
|
26
30
|
path = Path.new("/etc/")
|
27
31
|
|
28
32
|
path.dirs.should_not == nil
|
29
|
-
path.dir.should
|
33
|
+
path.dir.should == "/etc"
|
30
34
|
path.filename.should == nil
|
31
35
|
end
|
32
36
|
|
33
37
|
it "replaces ext" do
|
34
38
|
path = Path.new("/blah/what.mp4/.mp3/hello.avi")
|
35
|
-
|
36
39
|
path.ext.should == "avi"
|
40
|
+
|
37
41
|
path.ext = "mkv"
|
38
42
|
path.ext.should == "mkv"
|
39
43
|
|
@@ -41,35 +45,36 @@ describe Path do
|
|
41
45
|
end
|
42
46
|
|
43
47
|
it "replaces filename" do
|
44
|
-
path
|
48
|
+
path = Path.new(__FILE__)
|
45
49
|
path.dir?.should == false
|
46
|
-
|
50
|
+
|
51
|
+
path.filename = nil
|
47
52
|
path.dir?.should == true
|
48
53
|
end
|
49
54
|
|
50
55
|
it "fstats" do
|
51
56
|
path = Path.new(__FILE__)
|
52
57
|
|
53
|
-
path.exists?.should
|
54
|
-
path.dir?.should
|
55
|
-
path.file?.should
|
56
|
-
path.symlink?.should
|
58
|
+
path.exists?.should == true
|
59
|
+
path.dir?.should == false
|
60
|
+
path.file?.should == true
|
61
|
+
path.symlink?.should == false
|
57
62
|
path.mtime.class.should == Time
|
58
63
|
end
|
59
64
|
|
60
65
|
it "globs" do
|
61
|
-
path
|
62
|
-
glob
|
66
|
+
path = Path.new(__FILE__)
|
67
|
+
glob = path.dir + "/*spec.rb"
|
63
68
|
specs = Path.glob(glob)
|
64
69
|
path.in?(specs).should == true
|
65
70
|
end
|
66
71
|
|
67
72
|
it "Path[file] and Path[glob]s" do
|
68
|
-
path
|
69
|
-
path.should
|
70
|
-
|
71
|
-
glob
|
72
|
-
specs
|
73
|
+
path = Path.new(__FILE__)
|
74
|
+
path.should == Path[__FILE__]
|
75
|
+
|
76
|
+
glob = path.dir + "/*spec.rb"
|
77
|
+
specs = Path.glob(glob)
|
73
78
|
Path[glob].should == specs
|
74
79
|
end
|
75
80
|
|
@@ -100,6 +105,31 @@ describe Path do
|
|
100
105
|
s2[15..-1].should == s1
|
101
106
|
end
|
102
107
|
|
108
|
+
it "reads/writes various formats (json, yaml, etc.)" do
|
109
|
+
data = { "hello" => "there", "amazing" => [1,2,3,4] }
|
110
|
+
|
111
|
+
yaml = Path.tmpfile
|
112
|
+
yaml.write_yaml(data)
|
113
|
+
yaml.read_yaml.should == data
|
114
|
+
|
115
|
+
json = Path.tmpfile
|
116
|
+
json.write_json(data)
|
117
|
+
json.read_json.should == data
|
118
|
+
|
119
|
+
data = "<h1>The best webpage in the universe.</h1>"
|
120
|
+
html = Path.tmpfile
|
121
|
+
html.write data
|
122
|
+
html.read_html.at("h1").to_s.should == data
|
123
|
+
end
|
124
|
+
|
125
|
+
it "parses files" do
|
126
|
+
data = {"hello"=>"there"}
|
127
|
+
|
128
|
+
f = Path["/tmp/something.json"]
|
129
|
+
f.write_json(data)
|
130
|
+
f.parse.should == data
|
131
|
+
end
|
132
|
+
|
103
133
|
it "makes paths THREE WAYS!" do
|
104
134
|
[
|
105
135
|
Path(__FILE__),
|
@@ -119,16 +149,16 @@ describe Path do
|
|
119
149
|
path = Path['/spam/spam/spam/']
|
120
150
|
path.dirs.should == %w[ spam spam spam ]
|
121
151
|
path.dirs << 'humbug'
|
122
|
-
path.dir.should
|
152
|
+
path.dir.should == '/spam/spam/spam/humbug'
|
123
153
|
path.path.should == '/spam/spam/spam/humbug/'
|
124
154
|
end
|
125
155
|
|
126
156
|
it "handles URLs" do
|
127
157
|
path = Path["http://google.com/?search=blah"]
|
128
|
-
path.host.should
|
129
|
-
path.port.should
|
158
|
+
path.host.should == "google.com"
|
159
|
+
path.port.should == 80
|
130
160
|
path.query.should == {"search" => "blah"}
|
131
|
-
path.uri?.should
|
161
|
+
path.uri?.should == true
|
132
162
|
end
|
133
163
|
|
134
164
|
it "tempfiles" do
|
@@ -172,7 +202,7 @@ describe Path do
|
|
172
202
|
path = Path.tmpfile
|
173
203
|
path << "data"
|
174
204
|
path.exists?.should == true
|
175
|
-
path.rm.should
|
205
|
+
path.rm.should == true
|
176
206
|
path.exists?.should == false
|
177
207
|
end
|
178
208
|
|
@@ -230,7 +260,7 @@ describe Path do
|
|
230
260
|
|
231
261
|
gunzipped = gzipped.gunzip
|
232
262
|
gunzipped.size.should == before
|
233
|
-
gunzipped.should
|
263
|
+
gunzipped.should == path
|
234
264
|
end
|
235
265
|
|
236
266
|
it "exts" do
|
@@ -275,7 +305,6 @@ describe Path do
|
|
275
305
|
# => ["../../.ssh/id_rsairb.pub"]
|
276
306
|
|
277
307
|
Path["~/.ssh/id_{dsa,rsa}.pub"].size.should > 0
|
278
|
-
|
279
308
|
end
|
280
309
|
|
281
310
|
it "modes" do
|
@@ -315,25 +344,12 @@ describe Path do
|
|
315
344
|
file.filename.should != tmp.filename
|
316
345
|
end
|
317
346
|
|
318
|
-
it 'swaps two files' do
|
319
|
-
raise "errorn!"
|
320
|
-
# swap two regular files
|
321
|
-
# swap a symlink and a regular file
|
322
|
-
# swap two symlinks
|
323
|
-
end
|
324
|
-
|
325
|
-
it 'realpaths' do
|
326
|
-
Path["/etc"].realpath.should == Path["/etc"]
|
327
|
-
|
328
|
-
end
|
329
|
-
|
330
347
|
it 'parents and childs properly' do
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
child = Path["/blah/stuff/what"]
|
348
|
+
root = Path["/"]
|
349
|
+
parent = Path["/blah/stuff"]
|
350
|
+
child = Path["/blah/stuff/what"]
|
335
351
|
neither = Path["/whee/yay"]
|
336
|
-
|
352
|
+
|
337
353
|
# Table of parent=>child relationships
|
338
354
|
{
|
339
355
|
parent => child,
|
@@ -352,4 +368,46 @@ describe Path do
|
|
352
368
|
neither.parent_of?(parent).should == false
|
353
369
|
end
|
354
370
|
|
371
|
+
it "checks file modes" do
|
372
|
+
path = Path.tmpfile
|
373
|
+
path.exe?.should == false
|
374
|
+
path.chmod(0o666)
|
375
|
+
p path.mode
|
376
|
+
(path.mode & 0o666).should > 0
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'symlinks and symlink_targets' do
|
380
|
+
path = Path.tmpfile
|
381
|
+
path << "some data"
|
382
|
+
|
383
|
+
target = "#{path}-symlinked"
|
384
|
+
|
385
|
+
path.ln_s target
|
386
|
+
|
387
|
+
target = Path[target]
|
388
|
+
target.symlink?.should == true
|
389
|
+
target.read.should == path.read
|
390
|
+
target.symlink_target.should == path
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'swaps two files' do
|
394
|
+
raise "errorn!"
|
395
|
+
# swap two regular files
|
396
|
+
# swap a symlink and a regular file
|
397
|
+
# swap two symlinks
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'realpaths' do
|
401
|
+
etc = Path["/etc"]
|
402
|
+
tmp = Path.tmpfile
|
403
|
+
tmp.rm
|
404
|
+
etc.ln_s tmp
|
405
|
+
|
406
|
+
tmp.symlink_target.should == etc
|
407
|
+
tmp.realpath.should == etc
|
408
|
+
Path["/etc/../etc"].realpath.should == etc
|
409
|
+
end
|
410
|
+
|
411
|
+
|
412
|
+
|
355
413
|
end
|
data/spec/permutations_spec.rb
CHANGED
data/spec/rash_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'epitools
|
1
|
+
require 'epitools'
|
2
2
|
|
3
3
|
describe Rash do
|
4
4
|
|
@@ -10,25 +10,39 @@ describe Rash do
|
|
10
10
|
/world/ => "world",
|
11
11
|
"other" => "whee",
|
12
12
|
true => false,
|
13
|
-
1 => "awesome"
|
13
|
+
1 => "awesome",
|
14
|
+
1..1000 => "rangey",
|
14
15
|
#/.+/ => "EVERYTHING"
|
15
16
|
)
|
16
17
|
end
|
17
18
|
|
18
19
|
it "string lookups" do
|
19
20
|
r["other"].should == "whee"
|
20
|
-
r["well hello there"].should ==
|
21
|
-
r["the world is round"].should ==
|
22
|
-
r
|
21
|
+
r["well hello there"].should == "hello"
|
22
|
+
r["the world is round"].should == "world"
|
23
|
+
r.all("hello world").sort.should == ["hello", "world"]
|
23
24
|
end
|
24
25
|
|
25
26
|
it "regex lookups" do
|
26
|
-
r[/other/].should ==
|
27
|
+
r[/other/].should == "whee"
|
27
28
|
end
|
28
29
|
|
29
30
|
it "other objects" do
|
30
31
|
r[true].should == false
|
31
32
|
r[1].should == "awesome"
|
32
33
|
end
|
33
|
-
|
34
|
+
|
35
|
+
it "does ranges" do
|
36
|
+
@r[250].should == "rangey"
|
37
|
+
@r[999].should == "rangey"
|
38
|
+
@r[1000].should == "rangey"
|
39
|
+
@r[1001].should == nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "calls procs on matches when they're values" do
|
43
|
+
r = Rash.new( /(ello)/ => proc { |m| m[1] } )
|
44
|
+
r["hello"].should == "ello"
|
45
|
+
r["ffffff"].should == nil
|
46
|
+
end
|
47
|
+
|
34
48
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spork'
|
3
|
+
|
1
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
5
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib', 'epitools'))
|
3
6
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
|
5
7
|
p $:
|
6
8
|
|
7
|
-
require 'rspec'
|
8
|
-
require 'epitools'
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
Spork.prefork do
|
11
|
+
# Loading more in this block will cause your tests to run faster. However,
|
12
|
+
# if you change any configuration or code from libraries loaded here, you'll
|
13
|
+
# need to restart spork for it take effect.
|
14
|
+
|
15
|
+
require 'rspec'
|
16
|
+
require 'epitools'
|
17
|
+
|
18
|
+
Rspec.configure do |c|
|
19
|
+
c.mock_with :rspec
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Spork.each_run do
|
24
|
+
# This code will be run each time you run your specs.
|
25
|
+
|
12
26
|
end
|
27
|
+
|
28
|
+
# --- Instructions ---
|
29
|
+
# - Sort through your spec_helper file. Place as much environment loading
|
30
|
+
# code that you don't normally modify during development in the
|
31
|
+
# Spork.prefork block.
|
32
|
+
# - Place the rest under Spork.each_run block
|
33
|
+
# - Any code that is left outside of the blocks will be ran during preforking
|
34
|
+
# and during each_run!
|
35
|
+
# - These instructions should self-destruct in 10 seconds. If they don't,
|
36
|
+
# feel free to delete them.
|
37
|
+
#
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-04-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 2.2.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.2.0
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: mechanize
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: 1.0.0
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.0.0
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: sqlite3-ruby
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,7 +53,12 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
description: Miscellaneous utility libraries to make my life easier.
|
48
63
|
email: chris@ill-logic.com
|
49
64
|
executables: []
|
@@ -54,6 +69,7 @@ extra_rdoc_files:
|
|
54
69
|
- TODO
|
55
70
|
files:
|
56
71
|
- .document
|
72
|
+
- Guardfile
|
57
73
|
- LICENSE
|
58
74
|
- README.rdoc
|
59
75
|
- Rakefile
|
@@ -62,12 +78,19 @@ files:
|
|
62
78
|
- epitools.gemspec
|
63
79
|
- lib/epitools.rb
|
64
80
|
- lib/epitools/autoloads.rb
|
65
|
-
- lib/epitools/basetypes.rb
|
66
81
|
- lib/epitools/browser.rb
|
67
82
|
- lib/epitools/browser/cache.rb
|
68
83
|
- lib/epitools/browser/mechanize_progressbar.rb
|
69
84
|
- lib/epitools/clitools.rb
|
70
85
|
- lib/epitools/colored.rb
|
86
|
+
- lib/epitools/core_ext.rb
|
87
|
+
- lib/epitools/core_ext/array.rb
|
88
|
+
- lib/epitools/core_ext/enumerable.rb
|
89
|
+
- lib/epitools/core_ext/hash.rb
|
90
|
+
- lib/epitools/core_ext/numbers.rb
|
91
|
+
- lib/epitools/core_ext/object.rb
|
92
|
+
- lib/epitools/core_ext/string.rb
|
93
|
+
- lib/epitools/core_ext/truthiness.rb
|
71
94
|
- lib/epitools/ezdb.rb
|
72
95
|
- lib/epitools/hexdump.rb
|
73
96
|
- lib/epitools/iter.rb
|
@@ -84,16 +107,15 @@ files:
|
|
84
107
|
- lib/epitools/rails.rb
|
85
108
|
- lib/epitools/rash.rb
|
86
109
|
- lib/epitools/ratio.rb
|
87
|
-
- lib/epitools/string_to_proc.rb
|
88
110
|
- lib/epitools/sys.rb
|
89
111
|
- lib/epitools/term.rb
|
90
112
|
- lib/epitools/trie.rb
|
91
113
|
- lib/epitools/zopen.rb
|
92
114
|
- spec/autoreq_spec.rb
|
93
|
-
- spec/basetypes_spec.rb
|
94
115
|
- spec/browser_spec.rb
|
95
116
|
- spec/clitools_spec.rb
|
96
117
|
- spec/colored_spec.rb
|
118
|
+
- spec/core_ext_spec.rb
|
97
119
|
- spec/ezdb_spec.rb
|
98
120
|
- spec/iter_spec.rb
|
99
121
|
- spec/lcs_spec.rb
|
@@ -128,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
150
|
version: '0'
|
129
151
|
requirements: []
|
130
152
|
rubyforge_project:
|
131
|
-
rubygems_version: 1.8.
|
153
|
+
rubygems_version: 1.8.21
|
132
154
|
signing_key:
|
133
155
|
specification_version: 3
|
134
156
|
summary: NOT UTILS... METILS!
|