devver-germinate 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -0
- data/README.rdoc +26 -4
- data/TODO +80 -8
- data/bin/germ +162 -38
- data/examples/basic.rb +14 -9
- data/examples/short.rb +2 -0
- data/features/author-formats-article.feature +3 -3
- data/features/{author-lists-info.feature → author-lists-info.pending_feature} +3 -0
- data/features/author-publishes-article.feature +52 -0
- data/features/author-selects-hunks.feature +1 -1
- data/features/author-sets-variables.feature +88 -0
- data/features/{author-views-stuff.feature → author-views-stuff.pending_feature} +4 -0
- data/features/example_articles/escaping.txt +1 -0
- data/features/example_articles/specials.rb +3 -3
- data/features/example_output/specials.txt +9 -5
- data/features/step_definitions/germinate.rb +9 -0
- data/germinate.gemspec +3 -3
- data/lib/germinate.rb +1 -1
- data/lib/germinate/application.rb +82 -31
- data/lib/germinate/hunk.rb +20 -0
- data/lib/germinate/insertion.rb +10 -2
- data/lib/germinate/librarian.rb +129 -31
- data/lib/germinate/origin.rb +5 -0
- data/lib/germinate/pipeline.rb +2 -0
- data/lib/germinate/publisher.rb +57 -0
- data/lib/germinate/reader.rb +51 -8
- data/lib/germinate/selector.rb +18 -6
- data/lib/germinate/shared_style_attributes.rb +18 -1
- data/lib/germinate/{process.rb → shell_process.rb} +27 -8
- data/lib/germinate/shell_publisher.rb +19 -0
- data/lib/germinate/simple_publisher.rb +7 -0
- data/lib/germinate/source_file.rb +41 -0
- data/lib/germinate/text_transforms.rb +38 -9
- data/lib/germinate/transform_process.rb +25 -0
- data/lib/germinate/variable.rb +23 -0
- data/sample.rb +14 -0
- data/spec/germinate/application_spec.rb +18 -1
- data/spec/germinate/article_editor_spec.rb +3 -3
- data/spec/germinate/code_hunk_spec.rb +28 -0
- data/spec/germinate/file_hunk_spec.rb +1 -0
- data/spec/germinate/hunk_spec.rb +1 -0
- data/spec/germinate/insertion_spec.rb +2 -1
- data/spec/germinate/librarian_spec.rb +280 -85
- data/spec/germinate/pipeline_spec.rb +10 -0
- data/spec/germinate/process_spec.rb +31 -6
- data/spec/germinate/publisher_spec.rb +130 -0
- data/spec/germinate/reader_spec.rb +58 -2
- data/spec/germinate/selector_spec.rb +34 -14
- data/spec/germinate/shell_publisher_spec.rb +61 -0
- data/spec/germinate/source_file_spec.rb +99 -0
- data/spec/germinate/text_hunk_spec.rb +45 -0
- data/spec/germinate/text_transforms_spec.rb +90 -2
- data/spec/germinate/transform_process_spec.rb +50 -0
- data/spec/germinate/variable_spec.rb +14 -0
- metadata +19 -7
- data/lib/germinate/article_formatter.rb +0 -75
- data/spec/germinate/article_formatter_spec.rb +0 -153
@@ -21,4 +21,14 @@ module Germinate
|
|
21
21
|
@it.call(@input).should == @output3
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
describe Pipeline, "given no processes" do
|
26
|
+
before :each do
|
27
|
+
@it = Pipeline.new([])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should pass input through unchanged" do
|
31
|
+
@it.call(["foo", "bar"]).should == ["foo", "bar"]
|
32
|
+
end
|
33
|
+
end
|
24
34
|
end
|
@@ -2,21 +2,46 @@ require File.expand_path(
|
|
2
2
|
File.join(File.dirname(__FILE__), %w[.. .. lib germinate]))
|
3
3
|
|
4
4
|
module Germinate
|
5
|
-
describe
|
5
|
+
describe ShellProcess do
|
6
6
|
before :each do
|
7
7
|
@input = Germinate::Hunk.new(
|
8
8
|
["line 1\n", "line 2\n"],
|
9
9
|
:comment_prefix => "//")
|
10
10
|
@output = ["1 enil\n", "2 enil\n"]
|
11
11
|
@command = stub("Command", :readlines => @output).as_null_object
|
12
|
+
@tempfile = stub("Temp File", :path => "TEMP_PATH").as_null_object
|
13
|
+
@status = stub("Child Status", :success => true)
|
12
14
|
IO.stub!(:popen).and_yield(@command)
|
15
|
+
Tempfile.stub!(:open).and_yield(@tempfile)
|
16
|
+
end
|
17
|
+
|
18
|
+
context "given a command and some variables" do
|
19
|
+
before :each do
|
20
|
+
@it = Germinate::ShellProcess.new(
|
21
|
+
"myproc", "mycommand", { "FOO" => 123, "BAR" => 456 })
|
22
|
+
ENV["FOO"] = "xxx"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should set the variables in the environment before execution" do
|
26
|
+
IO.should_receive(:popen) do
|
27
|
+
ENV["FOO"].should == "123"
|
28
|
+
ENV["BAR"].should == "456"
|
29
|
+
end
|
30
|
+
@it.call(@input)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should reset the environment after execution" do
|
34
|
+
@it.call(@input)
|
35
|
+
ENV["FOO"].should == "xxx"
|
36
|
+
ENV["BAR"].should be_nil
|
37
|
+
end
|
13
38
|
end
|
14
39
|
|
15
40
|
context "given a command 'mycommand'" do
|
16
41
|
before :each do
|
17
|
-
@it = Germinate::
|
42
|
+
@it = Germinate::ShellProcess.new("myproc", "mycommand")
|
18
43
|
end
|
19
|
-
|
44
|
+
|
20
45
|
context "when called on a hunk of text" do
|
21
46
|
it "should pipe the input through the command" do
|
22
47
|
@command.should_receive(:<<).with("line 1\n").ordered
|
@@ -33,16 +58,16 @@ module Germinate
|
|
33
58
|
output = @it.call(@input)
|
34
59
|
output.comment_prefix.should == "//"
|
35
60
|
end
|
61
|
+
|
36
62
|
end
|
37
63
|
end
|
38
64
|
|
39
65
|
context "given a command 'mycommand %f' and called on some text" do
|
40
66
|
before :each do
|
41
|
-
@it = Germinate::
|
67
|
+
@it = Germinate::ShellProcess.new("myproc", "mycommand %f")
|
42
68
|
end
|
43
69
|
|
44
70
|
it "should create a temporary file and pass the name to the command" do
|
45
|
-
@tempfile = stub("Temp File", :path => "TEMP_PATH")
|
46
71
|
Tempfile.should_receive(:open).with("germinate_hunk").and_yield(@tempfile)
|
47
72
|
@tempfile.should_receive(:<<).with("line 1\n").ordered
|
48
73
|
@tempfile.should_receive(:<<).with("line 2\n").ordered
|
@@ -66,7 +91,7 @@ module Germinate
|
|
66
91
|
@input = Germinate::FileHunk.new(
|
67
92
|
["line 1\n", "line 2\n"],
|
68
93
|
{:source_path => "SOURCE_PATH"})
|
69
|
-
@it = Germinate::
|
94
|
+
@it = Germinate::ShellProcess.new("myproc", "mycommand %f")
|
70
95
|
end
|
71
96
|
|
72
97
|
it "should pass the source file path to the command" do
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require File.expand_path(
|
2
|
+
File.join(File.dirname(__FILE__), %w[.. .. lib germinate]))
|
3
|
+
|
4
|
+
module Germinate
|
5
|
+
describe Publisher do
|
6
|
+
before :each do
|
7
|
+
@name = "my_pub"
|
8
|
+
@hunk = stub("Input Hunk")
|
9
|
+
@output = stub("Pipeline Output")
|
10
|
+
@pipeline = stub("Pipeline", :call => @output)
|
11
|
+
@librarian = stub("Librarian",
|
12
|
+
:make_pipeline => @pipeline,
|
13
|
+
:[] => @hunk ).as_null_object
|
14
|
+
@options = {:a => 'b'}
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when a new publisher type 'foo' is defined" do
|
18
|
+
before :each do
|
19
|
+
@it = Germinate::Publisher
|
20
|
+
@subclass = Class.new(Publisher) do
|
21
|
+
identifier "foo"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be able to make 'foo' publishers" do
|
26
|
+
@publisher = stub("Publisher")
|
27
|
+
@subclass.should_receive(:new).with(@name, @librarian, @options).
|
28
|
+
and_return(@publisher)
|
29
|
+
|
30
|
+
@it.make(@name, "foo", @librarian, @options)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when an anonymous publisher subclass is defined" do
|
36
|
+
before :each do
|
37
|
+
@it = Germinate::Publisher
|
38
|
+
@subclass = Class.new(Publisher) do
|
39
|
+
self.identifier = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not make the publisher available as an instantiable type" do
|
44
|
+
@librarian = stub("Librarian")
|
45
|
+
@options = {:a => 'b'}
|
46
|
+
@publisher = stub("Publisher")
|
47
|
+
|
48
|
+
lambda do
|
49
|
+
@it.make("mypub", nil, @librarian, @options)
|
50
|
+
end.should raise_error(IndexError)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context "given name, librarian, and options" do
|
56
|
+
before :each do
|
57
|
+
@name = "frank"
|
58
|
+
@librarian = stub("Librarian").as_null_object
|
59
|
+
@options = {:foo => "bar"}
|
60
|
+
@it = Publisher.new(@name, @librarian, @options)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should know its name" do
|
64
|
+
@it.name.should == @name
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should know its librarian" do
|
68
|
+
@it.librarian.should == @librarian
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should know its options" do
|
72
|
+
@it.options.should == @options
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "given a pipeline option" do
|
77
|
+
before :each do
|
78
|
+
@name = "frank"
|
79
|
+
@librarian = stub("Librarian").as_null_object
|
80
|
+
@options = {:pipeline => "foo|bar"}
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should have a pipeline" do
|
84
|
+
@it = Publisher.new(@name, @librarian, @options)
|
85
|
+
@it.pipeline.should_not be_nil
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should use the librarian to construct a pipeline" do
|
89
|
+
@librarian.should_receive(:make_pipeline).with("foo|bar")
|
90
|
+
@it = Publisher.new(@name, @librarian, @options)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "with no selector specified" do
|
95
|
+
before :each do
|
96
|
+
@it = Publisher.new(@name, @librarian, @options)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should select $TEXT|_transform" do
|
100
|
+
@it.selector.should == "$TEXT|_transform"
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
context "with a custom selector specified" do
|
106
|
+
before :each do
|
107
|
+
@it = Publisher.new(@name, @librarian, { :selector => "@sec1|myproc" })
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should have the specified selector" do
|
111
|
+
@it.selector.should == "@sec1|myproc"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should use the selector to get a hunk to process" do
|
115
|
+
@librarian.should_receive(:[]).
|
116
|
+
with("@sec1|myproc", anything)
|
117
|
+
@it.input
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should pass the selected hunk through the pipeline to get input" do
|
121
|
+
@pipeline.should_receive(:call).with(@hunk)
|
122
|
+
@it.input
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should use pipeline output as input to publisher process" do
|
126
|
+
@it.input.should == @output
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -45,8 +45,8 @@ module Germinate
|
|
45
45
|
CONTROL_LINES = [
|
46
46
|
# Line Comment Args
|
47
47
|
[":TEXT:\n", nil, []],
|
48
|
-
["# :CUT: \n", "#",
|
49
|
-
[" ; :TEXT: foo\n", ";",
|
48
|
+
["# :CUT: \n", "# ", []],
|
49
|
+
[" ; :TEXT: foo\n", " ; ", ["foo"]],
|
50
50
|
["//:SAMPLE:\n", "//", []],
|
51
51
|
["$>:END: ", "$>", []],
|
52
52
|
[":SAMPLE: bar, { a: 1, b: 2 }", nil, ["bar", {"a"=>1, "b"=>2}]],
|
@@ -225,6 +225,19 @@ module Germinate
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
+
context "given a :SAMPLE: after a TEXT section with the same name" do
|
229
|
+
before :each do
|
230
|
+
@it << ":TEXT: foobar\n"
|
231
|
+
@it << "This is the text\n"
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should add an implicit insertion to the TEXT section" do
|
235
|
+
@librarian.should_receive(:add_insertion!).with("foobar", "@foobar", {})
|
236
|
+
@it << ":SAMPLE: foobar\n"
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
228
241
|
context "given a :SAMPLE: keyword with custom brackets" do
|
229
242
|
before :each do
|
230
243
|
@line = ':SAMPLE: foobar, { brackets: ["<<", ">>"] }'
|
@@ -314,6 +327,25 @@ module Germinate
|
|
314
327
|
|
315
328
|
end
|
316
329
|
|
330
|
+
context "given an insertion with the special disable_transforms attr" do
|
331
|
+
before :each do
|
332
|
+
@it << ":TEXT: mysection"
|
333
|
+
@line = ":INSERT: foo, { disable_transforms: true } "
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should disable all transforms in the options" do
|
337
|
+
@librarian.should_receive(:add_insertion!) do
|
338
|
+
|section, selector, options|
|
339
|
+
|
340
|
+
TextTransforms.singleton_methods.each do |transform|
|
341
|
+
options[transform.to_sym].should be_false
|
342
|
+
end
|
343
|
+
end
|
344
|
+
@it << @line
|
345
|
+
end
|
346
|
+
|
347
|
+
end
|
348
|
+
|
317
349
|
context "given a process directive" do
|
318
350
|
before :each do
|
319
351
|
@line = ' # :PROCESS: sortail, "sort | tail"'
|
@@ -325,5 +357,29 @@ module Germinate
|
|
325
357
|
end
|
326
358
|
end
|
327
359
|
|
360
|
+
context "given a publisher directive" do
|
361
|
+
before :each do
|
362
|
+
@line = ' # :PUBLISHER: source, shell, { command: "cat %f" }'
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should add the publisher to the librarian" do
|
366
|
+
@librarian.should_receive(:add_publisher!).
|
367
|
+
with("source", "shell", { :command => "cat %f" })
|
368
|
+
@it << @line
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
context "given a set directive" do
|
373
|
+
before :each do
|
374
|
+
@line = ' # :SET: name, value'
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should set a variable on the librarian" do
|
378
|
+
@librarian.should_receive(:set_variable!).
|
379
|
+
with(@line, 1, "name", "value")
|
380
|
+
@it << @line
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
328
384
|
end
|
329
385
|
end
|
@@ -3,6 +3,11 @@ require File.expand_path(
|
|
3
3
|
|
4
4
|
module Germinate
|
5
5
|
describe Selector do
|
6
|
+
|
7
|
+
it "should be comparable to a string" do
|
8
|
+
Selector.new("@A:1..5").should be == "@A:1..5"
|
9
|
+
Selector.new("@A:1..5").should_not be == "@A:1..6"
|
10
|
+
end
|
6
11
|
|
7
12
|
context "given a subscript" do
|
8
13
|
before :each do
|
@@ -31,21 +36,36 @@ module Germinate
|
|
31
36
|
specify { @it.should be_whole }
|
32
37
|
end
|
33
38
|
|
39
|
+
context "given no type sigil or key" do
|
40
|
+
before :each do
|
41
|
+
@it = Selector.new(":2..5")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should have type code" do
|
45
|
+
@it.selector_type.should == :special
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have key SOURCE" do
|
49
|
+
@it.key.should == "SOURCE"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
34
53
|
EXAMPLE_SELECTORS = [
|
35
|
-
# selector type key delim start end length pipeline
|
36
|
-
[ "@A", :code, "A", '..', 1, -1, nil, [] ,false],
|
37
|
-
[ "@A:1", :code, "A", nil, 1, 1, nil, [] ,false],
|
38
|
-
[ "", :code, "DEFAULT", '..', 1, -1, nil, [] ,false],
|
39
|
-
[ nil, :code, "DEFAULT", '..', 1, -1, nil, [] ,false],
|
40
|
-
[ ":2..4", :code, "DEFAULT", '..', 2, 4, nil, [] ,false],
|
41
|
-
[ ":2...4", :code, "DEFAULT", '...', 2, 4, nil, [] ,false],
|
42
|
-
[ "@B:2,5", :code, "B", ',', 2, nil,5, [] ,false],
|
43
|
-
[ "@B:/z/,6", :code, "B", ',', /z/, nil,6, [] ,false],
|
44
|
-
[ "@_:/z/../x/", :code, "_", '..', /z/, /x/,nil, [] ,false],
|
45
|
-
[ "@B:2,4|fnord",:code, "B", ',', 2, nil,4, [
|
46
|
-
[ "$FOO", :special, "FOO", '..', 1, -1, nil, [] ,false],
|
47
|
-
[ "@A|foo|bar", :code, "A", '..', 1, -1, nil, [
|
48
|
-
[ "@B|fnord:2,4",:code, "B", ',', 2, nil,4, [
|
54
|
+
# selector type key delim start end length pipeline excerpt_output?
|
55
|
+
[ "@A", :code, "A", '..', 1, -1, nil, %w[_transform] ,false],
|
56
|
+
[ "@A:1", :code, "A", nil, 1, 1, nil, %w[_transform] ,false],
|
57
|
+
[ "", :code, "DEFAULT", '..', 1, -1, nil, %w[_transform] ,false],
|
58
|
+
[ nil, :code, "DEFAULT", '..', 1, -1, nil, %w[_transform] ,false],
|
59
|
+
[ ":2..4", :code, "DEFAULT", '..', 2, 4, nil, %w[_transform] ,false],
|
60
|
+
[ ":2...4", :code, "DEFAULT", '...', 2, 4, nil, %w[_transform] ,false],
|
61
|
+
[ "@B:2,5", :code, "B", ',', 2, nil,5, %w[_transform] ,false],
|
62
|
+
[ "@B:/z/,6", :code, "B", ',', /z/, nil,6, %w[_transform] ,false],
|
63
|
+
[ "@_:/z/../x/", :code, "_", '..', /z/, /x/,nil, %w[_transform] ,false],
|
64
|
+
[ "@B:2,4|fnord",:code, "B", ',', 2, nil,4, %w[_transform fnord] ,false],
|
65
|
+
[ "$FOO", :special, "FOO", '..', 1, -1, nil, %w[_transform] ,false],
|
66
|
+
[ "@A|foo|bar", :code, "A", '..', 1, -1, nil, %w[_transform foo bar],false],
|
67
|
+
[ "@B|fnord:2,4",:code, "B", ',', 2, nil,4, %w[_transform fnord],true],
|
68
|
+
[ "@B|_transform",:code, "B", '..', 1, -1, nil, %w[_transform], false]
|
49
69
|
]
|
50
70
|
|
51
71
|
EXAMPLE_SELECTORS.each do |selector_attributes|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.expand_path(
|
2
|
+
File.join(File.dirname(__FILE__), %w[.. .. lib germinate]))
|
3
|
+
|
4
|
+
module Germinate
|
5
|
+
describe ShellPublisher do
|
6
|
+
before :each do
|
7
|
+
@name = "frank"
|
8
|
+
@pipeline_output = ["* line 1\n", "* line 2\n"]
|
9
|
+
@pipeline = stub("Pipeline", :call => @pipeline_output)
|
10
|
+
@variables = stub("Variables")
|
11
|
+
@librarian = stub("Librarian",
|
12
|
+
:make_pipeline => @pipeline,
|
13
|
+
:variables => @variables)
|
14
|
+
@command = "cat %f"
|
15
|
+
@options = {:command => @command}
|
16
|
+
@it = ShellPublisher.new(@name, @librarian, @options)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have identifier 'shell'" do
|
20
|
+
ShellPublisher.identifier.should == "shell"
|
21
|
+
end
|
22
|
+
|
23
|
+
context "on publish" do
|
24
|
+
before :each do
|
25
|
+
@result = ["line 1\n", "line 2\n"]
|
26
|
+
@output = StringIO.new
|
27
|
+
@process = stub("ShellProcess", :call => @result).as_null_object
|
28
|
+
@source = stub("Source").as_null_object
|
29
|
+
@librarian.stub!(:[]).and_return(@source)
|
30
|
+
ShellProcess.stub!(:new).and_return(@process)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should create a new process" do
|
34
|
+
ShellProcess.should_receive(:new).with(@name, @command, @variables)
|
35
|
+
@it.publish!(@output)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should execute the created process" do
|
39
|
+
@process.should_receive(:call).with(anything).and_return(@result)
|
40
|
+
@it.publish!(@output)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should pass the pipeline output to the created process" do
|
44
|
+
@process.should_receive(:call).with(@pipeline_output).and_return(@result)
|
45
|
+
@it.publish!(@output)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should ask the librarian for the source file" do
|
49
|
+
@librarian.should_receive(:[]).with("$TEXT|_transform", anything)
|
50
|
+
@it.publish!(@output)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should write process output to given output stream" do
|
54
|
+
@it.publish!(@output)
|
55
|
+
@output.rewind
|
56
|
+
@output.read.should == "line 1\nline 2\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path(
|
2
|
+
File.join(File.dirname(__FILE__), %w[.. .. lib germinate]))
|
3
|
+
|
4
|
+
module Germinate
|
5
|
+
describe SourceFile do
|
6
|
+
before :each do
|
7
|
+
@it = SourceFile.new("DIR/SOURCE_PATH.EXT")
|
8
|
+
@file = stub("File", :flock => 0)
|
9
|
+
@lines = ["Line 1\n", "Line 2\n"]
|
10
|
+
File.stub!(:new).and_return(@file)
|
11
|
+
FileUtils.stub!(:cp)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have a source path" do
|
15
|
+
@it.path.to_s.should == "DIR/SOURCE_PATH.EXT"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should append .germ.back for the backup path" do
|
19
|
+
@it.backup_path.to_s.should == "DIR/SOURCE_PATH.EXT.germ.bak"
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when writing" do
|
23
|
+
before :each do
|
24
|
+
@open_file = stub("Open File", :write => nil)
|
25
|
+
@path = stub("Source Path", :read => "...")
|
26
|
+
@path.stub!(:open).and_yield(@open_file)
|
27
|
+
@backup_path = stub("BACKUP_PATH", :read => "...")
|
28
|
+
@it.backup_path = @backup_path
|
29
|
+
@it.path = @path
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should lock and unlock the source path" do
|
33
|
+
File.should_receive(:new).with(@it.path).and_return(@file)
|
34
|
+
@file.should_receive(:flock).with(File::LOCK_EX).ordered.and_return(0)
|
35
|
+
@file.should_receive(:flock).with(File::LOCK_UN).ordered
|
36
|
+
@it.write!(@lines)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise error if locking fails" do
|
40
|
+
@file.should_receive(:flock).with(File::LOCK_EX).ordered.and_return(1)
|
41
|
+
@file.should_receive(:flock).with(File::LOCK_UN).ordered
|
42
|
+
|
43
|
+
lambda do
|
44
|
+
@it.write!(@lines)
|
45
|
+
end.should raise_error(RuntimeError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should make a backup copy after locking the file" do
|
49
|
+
@file.should_receive(:flock).with(File::LOCK_EX).ordered.and_return(0)
|
50
|
+
FileUtils.should_receive(:cp).with(@it.path, @backup_path).ordered
|
51
|
+
@it.write!(@lines)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should make no backup if locking fails" do
|
55
|
+
@file.should_receive(:flock).with(File::LOCK_EX).ordered.and_return(1)
|
56
|
+
FileUtils.should_not_receive(:cp)
|
57
|
+
@it.write!(@lines) rescue nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should unlock the file if backup fails" do
|
61
|
+
FileUtils.should_receive(:cp).and_raise("Some Error")
|
62
|
+
@file.should_receive(:flock).with(File::LOCK_UN)
|
63
|
+
@it.write!(@lines) rescue nil
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should raise error if source file and backup are not equal" do
|
67
|
+
@path.should_receive(:read).and_return("...")
|
68
|
+
@backup_path.should_receive(:read).and_return("....")
|
69
|
+
lambda do @it.write!(@lines) end.should raise_error(RuntimeError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not open the source file if comparison fails" do
|
73
|
+
@path.should_receive(:read).and_return("...")
|
74
|
+
@backup_path.should_receive(:read).and_return("....")
|
75
|
+
@path.should_not_receive(:open)
|
76
|
+
lambda do @it.write!(@lines) end.should raise_error(RuntimeError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should open the source file for overwriting after backing up" do
|
80
|
+
FileUtils.should_receive(:cp).ordered
|
81
|
+
@path.should_receive(:open).with('w+').ordered
|
82
|
+
@it.write!(@lines)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should write lines to the source file" do
|
86
|
+
@open_file.should_receive(:write).with("Line 1\n").ordered
|
87
|
+
@open_file.should_receive(:write).with("Line 2\n").ordered
|
88
|
+
@it.write!(@lines)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should restore backup if write fails" do
|
92
|
+
@open_file.should_receive(:write).and_raise("Some Failure")
|
93
|
+
FileUtils.should_receive(:cp).with(@backup_path, @path)
|
94
|
+
lambda do @it.write!(@lines) end.should raise_error(RuntimeError)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|