bunch 0.2.2 → 1.0.0pre1
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.
- 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
|