devver-germinate 1.1.0 → 1.2.0
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/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
|