bunch 0.2.2 → 1.0.0pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -6
- data/Gemfile +3 -1
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +7 -12
- data/bin/bunch +2 -5
- data/bunch.gemspec +30 -23
- data/lib/bunch.rb +37 -81
- data/lib/bunch/cli.rb +40 -74
- data/lib/bunch/combiner.rb +121 -0
- data/lib/bunch/compiler.rb +52 -0
- data/lib/bunch/compilers/coffee_script.rb +23 -0
- data/lib/bunch/compilers/ejs.rb +28 -0
- data/lib/bunch/compilers/jade.rb +28 -0
- data/lib/bunch/compilers/jst.rb +38 -0
- data/lib/bunch/compilers/null.rb +19 -0
- data/lib/bunch/compilers/sass.rb +55 -0
- data/lib/bunch/content_hash.rb +37 -0
- data/lib/bunch/css_minifier.rb +121 -0
- data/lib/bunch/file.rb +18 -0
- data/lib/bunch/file_cache.rb +159 -0
- data/lib/bunch/file_tree.rb +153 -0
- data/lib/bunch/ignorer.rb +38 -0
- data/lib/bunch/js_minifier.rb +38 -0
- data/lib/bunch/middleware.rb +16 -67
- data/lib/bunch/pipeline.rb +30 -0
- data/lib/bunch/server.rb +56 -0
- data/lib/bunch/simple_cache.rb +36 -0
- data/lib/bunch/tree_merge.rb +29 -0
- data/lib/bunch/version.rb +3 -1
- data/spec/bunch/cli_spec.rb +85 -0
- data/spec/bunch/combiner_spec.rb +107 -0
- data/spec/bunch/compiler_spec.rb +73 -0
- data/spec/bunch/compilers/coffee_script_spec.rb +23 -0
- data/spec/bunch/compilers/ejs_spec.rb +27 -0
- data/spec/bunch/compilers/jade_spec.rb +28 -0
- data/spec/bunch/compilers/sass_spec.rb +120 -0
- data/spec/bunch/css_minifier_spec.rb +31 -0
- data/spec/bunch/file_cache_spec.rb +151 -0
- data/spec/bunch/file_tree_spec.rb +127 -0
- data/spec/bunch/ignorer_spec.rb +26 -0
- data/spec/bunch/js_minifier_spec.rb +35 -0
- data/spec/bunch/middleware_spec.rb +41 -0
- data/spec/bunch/pipeline_spec.rb +31 -0
- data/spec/bunch/server_spec.rb +90 -0
- data/spec/bunch/simple_cache_spec.rb +55 -0
- data/spec/bunch/tree_merge_spec.rb +30 -0
- data/spec/bunch_spec.rb +6 -0
- data/spec/example_tree/directory/_combine +2 -0
- data/{example/js/test1.js → spec/example_tree/directory/file1} +0 -0
- data/{example/js/test2/test2a.js → spec/example_tree/directory/file2} +0 -0
- data/{example/js/test2/test2c.js → spec/example_tree/file3} +0 -0
- data/spec/spec_helper.rb +38 -0
- metadata +224 -102
- data/.yardopts +0 -1
- data/README.md +0 -4
- data/config.ru +0 -6
- data/example/config.ru +0 -6
- data/example/css/test1.css +0 -1
- data/example/css/test2/test2a.scss +0 -1
- data/example/css/test2/test2b.css +0 -1
- data/example/js/.bunchignore +0 -1
- data/example/js/test2/_.yml +0 -2
- data/example/js/test2/foo.js +0 -1
- data/example/js/test2/test2b.js +0 -1
- data/example/js/test3/test3a.js +0 -1
- data/example/js/test3/test3b/_.yml +0 -1
- data/example/js/test3/test3b/test3bi.js +0 -1
- data/example/js/test3/test3b/test3bii.js +0 -1
- data/example/js/test4/_.yml +0 -1
- data/example/js/test4/test4a.js +0 -1
- data/example/js/test4/test4b.coffee +0 -1
- data/example/js/test4/test4c.coffee +0 -1
- data/example/js/test5/test5a.jst.ejs +0 -1
- data/lib/bunch/abstract_node.rb +0 -25
- data/lib/bunch/cache.rb +0 -40
- data/lib/bunch/coffee_node.rb +0 -39
- data/lib/bunch/directory_node.rb +0 -82
- data/lib/bunch/ejs_node.rb +0 -50
- data/lib/bunch/file_node.rb +0 -25
- data/lib/bunch/jade_node.rb +0 -50
- data/lib/bunch/null_node.rb +0 -11
- data/lib/bunch/rack.rb +0 -38
- data/lib/bunch/sass_node.rb +0 -39
- data/test/middleware_test.rb +0 -26
- data/test/rack_test.rb +0 -93
- data/test/test_helper.rb +0 -21
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
module Bunch
|
6
|
+
module Compilers
|
7
|
+
describe EJS do
|
8
|
+
it "compiles an EJS file to JS" do
|
9
|
+
tree = FileTree.from_hash(
|
10
|
+
"a" => { "my_file.jst.ejs" => "<% hello %>" }
|
11
|
+
)
|
12
|
+
compiler = EJS.new(
|
13
|
+
tree.get("a").get("my_file.jst.ejs"), tree, "a/my_file.jst.ejs")
|
14
|
+
output = compiler.content
|
15
|
+
|
16
|
+
compiler.path.must_equal "a/my_file.js"
|
17
|
+
output.must_include "JST['a/my_file'] = function"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises if the gem isn't available" do
|
21
|
+
EJS.any_instance.stubs(:require).raises(LoadError)
|
22
|
+
exception = assert_raises(RuntimeError) { EJS.new(nil, nil, nil) }
|
23
|
+
exception.message.must_include "gem install"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
module Bunch
|
6
|
+
module Compilers
|
7
|
+
describe Jade do
|
8
|
+
it "compiles a Jade file to JS" do
|
9
|
+
tree = FileTree.from_hash(
|
10
|
+
"a" => { "my_file.jst.jade" => "h1\n = hello\n hr\n" }
|
11
|
+
)
|
12
|
+
compiler = Jade.new(
|
13
|
+
tree.get("a").get("my_file.jst.jade"), tree, "a/my_file.jst.jade")
|
14
|
+
output = compiler.content
|
15
|
+
|
16
|
+
compiler.path.must_equal "a/my_file.js"
|
17
|
+
output.must_include "JST['a/my_file'] = function"
|
18
|
+
output.must_include "<hr/>"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises if the gem isn't available" do
|
22
|
+
Jade.any_instance.stubs(:require).raises(LoadError)
|
23
|
+
exception = assert_raises(RuntimeError) { Jade.new(nil, nil, nil) }
|
24
|
+
exception.message.must_include "gem install"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
module Bunch
|
6
|
+
module Compilers
|
7
|
+
describe Sass do
|
8
|
+
it "compiles a .scss file to CSS" do
|
9
|
+
tree = FileTree.from_hash(
|
10
|
+
"a" => { "my_file.scss" => "div { span { width: 20px; } }" }
|
11
|
+
)
|
12
|
+
compiler = Sass.new(tree.get("a/my_file.scss"), tree, "a/my_file.scss")
|
13
|
+
compiler.path.must_equal "a/my_file.css"
|
14
|
+
compiler.content.must_equal "div span {\n width: 20px; }\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "compiles a .sass file to CSS" do
|
18
|
+
tree = FileTree.from_hash(
|
19
|
+
"a" => { "my_file.sass" => "div\n span\n width: 20px\n" }
|
20
|
+
)
|
21
|
+
compiler = Sass.new(tree.get("a/my_file.sass"), tree, "a/my_file.sass")
|
22
|
+
compiler.path.must_equal "a/my_file.css"
|
23
|
+
compiler.content.must_equal "div span {\n width: 20px; }\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "raises if the gem isn't available" do
|
27
|
+
Sass.any_instance.stubs(:require).raises(LoadError)
|
28
|
+
exception = assert_raises(RuntimeError) { Sass.new(nil, nil, nil) }
|
29
|
+
exception.message.must_include "gem install"
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "dealing with @imports" do
|
33
|
+
let(:included_file) { "@mixin foobar { width: 20px; }" }
|
34
|
+
|
35
|
+
it "imports a file from the same directory" do
|
36
|
+
including_file = <<-SCSS
|
37
|
+
@import "included.scss";
|
38
|
+
|
39
|
+
div {
|
40
|
+
@include foobar;
|
41
|
+
}
|
42
|
+
SCSS
|
43
|
+
|
44
|
+
tree = FileTree.from_hash(
|
45
|
+
"a" => {
|
46
|
+
"including.scss" => including_file,
|
47
|
+
"included.scss" => included_file
|
48
|
+
}
|
49
|
+
)
|
50
|
+
|
51
|
+
compiler = Sass.new(
|
52
|
+
tree.get("a/including.scss"), tree, "a/including.scss")
|
53
|
+
compiler.path.must_equal "a/including.css"
|
54
|
+
compiler.content.must_equal "div {\n width: 20px; }\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "imports a file with no extension (and a leading underscore)" do
|
58
|
+
including_file = <<-SCSS
|
59
|
+
@import "included";
|
60
|
+
|
61
|
+
div {
|
62
|
+
@include foobar;
|
63
|
+
}
|
64
|
+
SCSS
|
65
|
+
|
66
|
+
tree = FileTree.from_hash(
|
67
|
+
"a" => {
|
68
|
+
"including.scss" => including_file,
|
69
|
+
"_included.scss" => included_file
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
compiler = Sass.new(
|
74
|
+
tree.get("a/including.scss"), tree, "a/including.scss")
|
75
|
+
compiler.path.must_equal "a/including.css"
|
76
|
+
compiler.content.must_equal "div {\n width: 20px; }\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "imports a file from a parent directory" do
|
80
|
+
including_file = <<-SCSS
|
81
|
+
@import "../included.scss";
|
82
|
+
|
83
|
+
div {
|
84
|
+
@include foobar;
|
85
|
+
}
|
86
|
+
SCSS
|
87
|
+
|
88
|
+
tree = FileTree.from_hash(
|
89
|
+
"a" => {
|
90
|
+
"b" => {
|
91
|
+
"including.scss" => including_file,
|
92
|
+
},
|
93
|
+
"included.scss" => included_file
|
94
|
+
}
|
95
|
+
)
|
96
|
+
|
97
|
+
compiler = Sass.new(
|
98
|
+
tree.get("a/b/including.scss"), tree, "a/b/including.scss")
|
99
|
+
compiler.path.must_equal "a/b/including.css"
|
100
|
+
compiler.content.must_equal "div {\n width: 20px; }\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "has an informative error message if the import is missing" do
|
104
|
+
including_file = '@import "../included.scss";'
|
105
|
+
|
106
|
+
tree = FileTree.from_hash(
|
107
|
+
"a" => {
|
108
|
+
"b" => { "including.scss" => including_file }
|
109
|
+
}
|
110
|
+
)
|
111
|
+
|
112
|
+
compiler = Sass.new(
|
113
|
+
tree.get("a/b/including.scss"), tree, "a/b/including.scss")
|
114
|
+
error = proc { compiler.content }.must_raise RuntimeError
|
115
|
+
error.message.must_match /a\/included/
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
module Bunch
|
6
|
+
describe CssMinifier do
|
7
|
+
let(:file_contents) do
|
8
|
+
<<-CSS
|
9
|
+
body :hover {
|
10
|
+
border-left: 10px solid rgb(0, 0, 0);
|
11
|
+
}
|
12
|
+
CSS
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:minified_contents) do
|
16
|
+
"body :hover{border-left:10px solid #000}"
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:input_tree) do
|
20
|
+
FileTree.from_hash(
|
21
|
+
"a" => file_contents, "b" => { "c.css" => file_contents }
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "minifies .css files, ignoring other files" do
|
26
|
+
result = CssMinifier.new(input_tree).result.to_hash
|
27
|
+
result["a"].must_equal file_contents
|
28
|
+
result["b"]["c.css"].must_equal minified_contents
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "fileutils"
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module Bunch
|
8
|
+
describe FileCache do
|
9
|
+
let(:input_1) { FileTree.from_hash("a" => "1", "b" => "2") }
|
10
|
+
let(:input_2) { FileTree.from_hash("a" => "1", "b" => "3") }
|
11
|
+
let(:result_1) { FileTree.from_hash("a" => "!", "b" => "@") }
|
12
|
+
let(:result_2) { FileTree.from_hash("a" => "!", "b" => "#") }
|
13
|
+
let(:result_3) { FileTree.from_hash("a" => "%", "b" => "#") }
|
14
|
+
let(:processor_1) { stub new: stub(result: result_1), to_s: "processor" }
|
15
|
+
let(:processor_2) { stub new: stub(result: result_2), to_s: "processor" }
|
16
|
+
let(:processor_3) { stub new: stub(result: result_3), to_s: "processor" }
|
17
|
+
let(:processor_4) { stub new: stub(result: result_3), to_s: "grocessor" }
|
18
|
+
|
19
|
+
def new_cache(processor, path = "a_path")
|
20
|
+
FileCache.new(processor, path)
|
21
|
+
end
|
22
|
+
|
23
|
+
before do
|
24
|
+
FileUtils.rm_rf ".bunch-cache"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "delegates to the underlying processor on a cold cache" do
|
28
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns the same results for the same input" do
|
32
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
33
|
+
new_cache(processor_2).new(input_1).result.must_equal result_1
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns different results for a different input" do
|
37
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
38
|
+
new_cache(processor_2).new(input_2).result.must_equal result_2
|
39
|
+
end
|
40
|
+
|
41
|
+
it "only updates paths that have changed" do
|
42
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
43
|
+
new_cache(processor_3).new(input_2).result.must_equal result_2
|
44
|
+
end
|
45
|
+
|
46
|
+
it "maintains distinct caches for different processors" do
|
47
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
48
|
+
new_cache(processor_4).new(input_1).result.must_equal result_3
|
49
|
+
end
|
50
|
+
|
51
|
+
it "maintains distinct caches for different input paths" do
|
52
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
53
|
+
new_cache(processor_2).new(input_1).result.must_equal result_1
|
54
|
+
new_cache(processor_2, "abc").new(input_1).result.must_equal result_2
|
55
|
+
end
|
56
|
+
|
57
|
+
it "considers the cache expired if Bunch's version changes" do
|
58
|
+
new_cache(processor_1).new(input_1).result.must_equal result_1
|
59
|
+
begin
|
60
|
+
FileCache::VERSION = "alsdkjalskdj"
|
61
|
+
new_cache(processor_2).new(input_1).result.must_equal result_2
|
62
|
+
ensure
|
63
|
+
FileCache.send :remove_const, :VERSION
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class FileCache
|
69
|
+
describe Partition do
|
70
|
+
let(:empty) { FileTree.from_hash({}) }
|
71
|
+
let(:tree_1) { FileTree.from_hash("a" => {"b" => "1", "c" => "2"}) }
|
72
|
+
|
73
|
+
it "returns input tree for pending if cache is empty" do
|
74
|
+
cache = stub
|
75
|
+
cache.stubs(:read).returns(nil)
|
76
|
+
partition = Partition.new(tree_1, cache)
|
77
|
+
partition.process!
|
78
|
+
partition.cached.must_equal empty
|
79
|
+
partition.pending.must_equal tree_1
|
80
|
+
end
|
81
|
+
|
82
|
+
it "divides input tree into pending and cached" do
|
83
|
+
cache = stub
|
84
|
+
cache.stubs(:read).with("a/b", "1").returns("cache")
|
85
|
+
cache.stubs(:read).with("a/c", "2").returns(nil)
|
86
|
+
partition = Partition.new(tree_1, cache)
|
87
|
+
partition.process!
|
88
|
+
partition.cached.must_equal FileTree.from_hash("a" => {"b" => "cache"})
|
89
|
+
partition.pending.must_equal FileTree.from_hash("a" => {"c" => "2"})
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe Cache do
|
94
|
+
def create_cache
|
95
|
+
Cache.from_trees(
|
96
|
+
FileTree.from_hash("a" => "1", "b" => { "c" => "2" }),
|
97
|
+
FileTree.from_hash("a" => "!", "b" => { "c" => "@" }))
|
98
|
+
end
|
99
|
+
|
100
|
+
def assert_cache_is_correct(cache)
|
101
|
+
cache.read("a", "1").must_equal "!"
|
102
|
+
cache.read("a", "2").must_equal nil
|
103
|
+
cache.read("b/c", "2").must_equal "@"
|
104
|
+
cache.read("b/c/d", "2").must_equal nil
|
105
|
+
cache.read("b/d", "2").must_equal nil
|
106
|
+
end
|
107
|
+
|
108
|
+
it "records a mapping between two trees" do
|
109
|
+
cache = create_cache
|
110
|
+
assert_cache_is_correct cache
|
111
|
+
end
|
112
|
+
|
113
|
+
it "saves to and loads from a file" do
|
114
|
+
original_cache = create_cache
|
115
|
+
loaded_cache = nil
|
116
|
+
|
117
|
+
Tempfile.open(["cache", ".yml"]) do |tempfile|
|
118
|
+
tempfile.close
|
119
|
+
original_cache.write_to_file(tempfile.path)
|
120
|
+
loaded_cache = Cache.read_from_file(tempfile.path)
|
121
|
+
end
|
122
|
+
assert_cache_is_correct loaded_cache
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns a null cache if the file can't be opened" do
|
126
|
+
Cache.read_from_file("asldkasd").must_be_instance_of Cache
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#read" do
|
130
|
+
before do
|
131
|
+
@cache = Cache.new(
|
132
|
+
{ "a" => Digest::MD5.hexdigest("1") },
|
133
|
+
FileTree.from_hash("a" => "!@#")
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "returns the result if the hash matches" do
|
138
|
+
@cache.read("a", "1").must_equal "!@#"
|
139
|
+
end
|
140
|
+
|
141
|
+
it "returns nil if the hash doesn't match" do
|
142
|
+
@cache.read("a", "2").must_equal nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it "returns nil if the file isn't present" do
|
146
|
+
@cache.read("b", "1").must_equal nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
module Bunch
|
6
|
+
describe FileTree do
|
7
|
+
before do
|
8
|
+
@tree = FileTree.from_hash \
|
9
|
+
"a" => {
|
10
|
+
"b.txt" => "foo",
|
11
|
+
"c" => {
|
12
|
+
"d.js" => "bar",
|
13
|
+
"d.coffee" => "brr"
|
14
|
+
}
|
15
|
+
},
|
16
|
+
"e" => "baz"
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".from_path" do
|
20
|
+
it "loads a file hierarchy" do
|
21
|
+
path = ::File.expand_path "../../example_tree", __FILE__
|
22
|
+
tree = FileTree.from_path(path)
|
23
|
+
tree.to_hash.must_equal(
|
24
|
+
"directory" => {
|
25
|
+
"_combine" => "file2\nfile1\n",
|
26
|
+
"file1" => "1\n",
|
27
|
+
"file2" => "2\n"
|
28
|
+
},
|
29
|
+
"file3" => "3\n"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#get" do
|
35
|
+
it "returns an object representing a file" do
|
36
|
+
file = @tree.get("e")
|
37
|
+
file.path.must_equal "e"
|
38
|
+
file.content.must_equal "baz"
|
39
|
+
file.extension.must_equal ""
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns an object representing a nested path" do
|
43
|
+
file = @tree.get("a/b.txt")
|
44
|
+
file.path.must_equal "a/b.txt"
|
45
|
+
file.content.must_equal "foo"
|
46
|
+
file.extension.must_equal ".txt"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns an object representing a more nested path" do
|
50
|
+
file = @tree.get("a/c/d.js")
|
51
|
+
file.path.must_equal "a/c/d.js"
|
52
|
+
file.content.must_equal "bar"
|
53
|
+
file.extension.must_equal ".js"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns nil if the path extends past a file" do
|
57
|
+
@tree.get("a/c/d.js/nonexistent").must_equal nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#get_fuzzy" do
|
62
|
+
it "returns an unambiguous match" do
|
63
|
+
file = @tree.get_fuzzy("a/b")
|
64
|
+
file.path.must_equal "a/b.txt"
|
65
|
+
file.content.must_equal "foo"
|
66
|
+
file.extension.must_equal ".txt"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns the first of two matches" do
|
70
|
+
file = @tree.get_fuzzy("a/c/d")
|
71
|
+
file.path.must_equal "a/c/d.js"
|
72
|
+
file.content.must_equal "bar"
|
73
|
+
file.extension.must_equal ".js"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns nil for no match" do
|
77
|
+
@tree.get_fuzzy("a/fff").must_equal nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns nil if the path extends past a file" do
|
81
|
+
@tree.get_fuzzy("a/c/d.js/nonexistent").must_equal nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#write" do
|
86
|
+
it "creates a file at the top level" do
|
87
|
+
@tree.write "f", "hello"
|
88
|
+
@tree.get("f").content.must_equal "hello"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "creates a file in an existing directory" do
|
92
|
+
@tree.write "a/c/f", "hello"
|
93
|
+
@tree.get("a/c/f").content.must_equal "hello"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "creates nested directories" do
|
97
|
+
@tree.write "a/c/a/f", "hello"
|
98
|
+
@tree.get("a/c/a/f").content.must_equal "hello"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "overwrites an existing file" do
|
102
|
+
@tree.write "a/c/a/f", "hello"
|
103
|
+
@tree.write "a/c/a/f", "goodbye"
|
104
|
+
@tree.get("a/c/a/f").content.must_equal "goodbye"
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises if there's an existing file that conflicts with the path" do
|
108
|
+
proc {
|
109
|
+
@tree.write "a/c/d.js/f", "hello"
|
110
|
+
}.must_raise RuntimeError
|
111
|
+
|
112
|
+
@tree.get("a/c/d.js").content.must_equal "bar"
|
113
|
+
@tree.get("a/c/d.js/f").must_equal nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#write_to_path" do
|
118
|
+
it "mirrors the contents of the tree to the given path" do
|
119
|
+
Dir.mktmpdir do |tmpdir|
|
120
|
+
@tree.write_to_path tmpdir
|
121
|
+
::File.read("#{tmpdir}/e").must_equal "baz"
|
122
|
+
::File.read("#{tmpdir}/a/c/d.js").must_equal "bar"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|