cotta 1.0.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.
@@ -0,0 +1,90 @@
1
+ module Cotta
2
+ class PhysicalSystem
3
+ def initialize
4
+ end
5
+
6
+ # Invoke the command passed in through the CommandRunner
7
+ # and pass in the closure.
8
+ def shell(command, &block)
9
+ runner = CommandRunner.new(command)
10
+ runner.execute(&block)
11
+ end
12
+
13
+ def environment!(variable)
14
+ value = ENV[variable]
15
+ raise "#{variable} environment variable not found" unless value
16
+ return value
17
+ end
18
+
19
+ def environment(variable, default)
20
+ value = ENV[variable]
21
+ value = default unless value
22
+ return value
23
+ end
24
+
25
+ def dir_exists?(dir_path)
26
+ return FileTest.directory?(dir_path)
27
+ end
28
+
29
+ def file_exists?(file_path)
30
+ return FileTest.file?(file_path)
31
+ end
32
+
33
+ def dir_stat(path)
34
+ File.stat(path)
35
+ end
36
+
37
+ def file_stat(path)
38
+ File.stat(path)
39
+ end
40
+
41
+ def list(dir_path)
42
+ Dir.entries(dir_path).find_all {|item| item != '.' && item != '..'}
43
+ end
44
+
45
+ def mkdir(dir_path)
46
+ Dir.mkdir(dir_path)
47
+ end
48
+
49
+ def io(file_path, argument)
50
+ return File.open(file_path, argument)
51
+ end
52
+
53
+ def delete_file(file_path)
54
+ return File.delete(file_path)
55
+ end
56
+
57
+ def delete_dir(dir_path)
58
+ return Dir.delete(dir_path)
59
+ end
60
+
61
+ def copy_file(source, target)
62
+ FileUtils.copy(source, target)
63
+ end
64
+
65
+ def move_file(source, target)
66
+ FileUtils.move(source, target)
67
+ end
68
+
69
+ def copy_dir(source, target)
70
+ FileUtils.copy_entry(source, target)
71
+ end
72
+
73
+ def move_dir(source, target)
74
+ FileUtils.mv(source, target)
75
+ end
76
+
77
+ def chdir(target, &block)
78
+ Dir.chdir(target, &block)
79
+ end
80
+
81
+ def pwd
82
+ Dir.pwd
83
+ end
84
+
85
+ def ==(other)
86
+ other.class == PhysicalSystem
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+
3
+ module Cotta
4
+ describe CommandInterface do
5
+ it 'delegate output to io' do
6
+ io = mock('io')
7
+ io.should_receive(:puts).with('content to output')
8
+ interface = CommandInterface.new(io)
9
+ interface.puts('content to output')
10
+ end
11
+
12
+ it 'prompt outputs a message and get the response from io' do
13
+ io = mock('io')
14
+ io.should_receive(:puts).with('question')
15
+ io.should_receive(:gets).and_return('answer')
16
+ interface = CommandInterface.new(io)
17
+ actual = interface.prompt('question')
18
+ actual.should == 'answer'
19
+ end
20
+
21
+ it 'prompt for choice' do
22
+ io = mock('io')
23
+ io.should_receive(:puts).once.with('select one of the following')
24
+ io.should_receive(:puts).once.with('[1] item one')
25
+ io.should_receive(:puts).once.with('[2] item two')
26
+ io.should_receive(:gets).once.and_return('2')
27
+ interface = CommandInterface.new(io)
28
+ actual = interface.prompt_for_choice('select one of the following', ['item one', 'item two'])
29
+ actual.should == 'item two'
30
+ end
31
+
32
+ it 'prompt for choice returns nil for invalid choice' do
33
+ io = mock('io')
34
+ io.should_receive(:puts).with('select one')
35
+ io.should_receive(:puts).exactly(2).times
36
+ io.should_receive(:gets).and_return('9')
37
+ interface = CommandInterface.new(io)
38
+ actual = interface.prompt_for_choice('select one', ['one', 'two'])
39
+ actual.should be_nil
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+
3
+ module Cotta
4
+ describe CommandRunner do
5
+ it 'return content' do
6
+ runner = CommandRunner.new('ruby --version')
7
+ runner.execute[0..3].should == 'ruby'
8
+ end
9
+
10
+ it 'raise error on abnormal exits' do
11
+ runner = CommandRunner.new('ruby ----')
12
+ Proc.new{runner.execute}.should raise_error(CommandError)
13
+ end
14
+
15
+ it 'take closure as io processor' do
16
+ runner = CommandRunner.new('echo test')
17
+ message_logged = nil
18
+ runner.execute {|io| message_logged = io.gets}
19
+ message_logged.should == "test\n"
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,3 @@
1
+ line one
2
+ line two
3
+ line three
@@ -0,0 +1,202 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+ require 'spec'
3
+ require 'pathname'
4
+
5
+ describe 'CottaDirBehaviors', :shared=>true do
6
+ before do
7
+ @system = create_system
8
+ @root = Cotta::CottaDir.new(@system, Pathname.new('.'))
9
+ @dir = @root.dir('dir')
10
+ @current = Dir.pwd
11
+ end
12
+
13
+ after do
14
+ Dir.chdir @current
15
+ end
16
+
17
+ it 'load dir with basic information' do
18
+ @dir.name.should == 'dir'
19
+ end
20
+
21
+ it 'should show path on to_s for convenience in scripting' do
22
+ @dir.to_s.should == 'dir'
23
+ "#{@dir}".should == 'dir'
24
+ end
25
+
26
+ it 'dir objects are value objects, equal on system and path' do
27
+ (Cotta::CottaDir.new(@system, Pathname.new('dir')) == @dir).should == true
28
+ end
29
+
30
+ it 'dir should not be equal if path different' do
31
+ (Cotta::CottaDir.new(@system, Pathname.new('/dir')) == @dir).should == false
32
+ end
33
+
34
+ it 'should support relative path' do
35
+ sub_dir = @dir.dir('one/two/three')
36
+ sub_dir.relative_path_from(@dir).to_s.should == 'one/two/three'
37
+ end
38
+
39
+ it 'should know its parent' do
40
+ @dir.parent.name.should == '.'
41
+ @dir.parent.parent.should be_nil
42
+ end
43
+
44
+ it 'should look up parent' do
45
+ sub_dir = @dir.dir('one/two/three')
46
+ result = sub_dir.parent {|dir| dir.name == 'one'}
47
+ result.should == @dir.dir('one')
48
+ end
49
+
50
+ it 'should return nil if parent lookup fails' do
51
+ sub_dir = @dir.dir('one/two/three')
52
+ sub_dir.parent {|dir| false}.should be_nil
53
+ end
54
+
55
+ it 'should raise error if not exits stat' do
56
+ Proc.new {
57
+ @dir.stat
58
+ }.should raise_error(Errno::ENOENT)
59
+ end
60
+
61
+ it 'support stat' do
62
+ @dir.mkdirs
63
+ @dir.stat.should_not be_nil
64
+ end
65
+
66
+ it 'dir should handle root dir' do
67
+ dir = Cotta::CottaDir.new(@system, Pathname.new('/root'))
68
+ dir.parent.path.should == Pathname.new('/')
69
+ dir.parent.name.should == '/'
70
+ dir.parent.exists?.should == true
71
+ dir.parent.root?.should == true
72
+ dir.parent.parent.should be_nil
73
+ end
74
+
75
+ it 'dir should handle root dir for drive letters' do
76
+ dir = Cotta::CottaDir.new(@system, Pathname.new('C:/Windows'))
77
+ dir.name.should == 'Windows'
78
+ dir.parent.path.should == Pathname.new('C:/')
79
+ dir.parent.name.should == 'C:/'
80
+ end
81
+
82
+ it 'dir should return sub directory' do
83
+ @dir.dir('sub').path.should == Pathname.new('dir/sub')
84
+ @dir.dir('sub').parent.should == @dir
85
+ end
86
+
87
+ it 'dir should return a directory from a relative pathname' do
88
+ @dir.dir(Pathname.new('one/two/three')).should == @dir.dir('one').dir('two').dir('three')
89
+ end
90
+
91
+ it 'should get file in current directory' do
92
+ file = @dir.file('file.txt')
93
+ file.name.should == 'file.txt'
94
+ file.path.should == Pathname.new('dir/file.txt')
95
+ file.parent.should == @dir
96
+ end
97
+
98
+ it 'should create dir and its parent' do
99
+ dir = @dir.dir('one').dir('two')
100
+ dir.exists?.should == false
101
+ dir.parent.exists?.should == false
102
+ dir.mkdirs
103
+ dir.exists?.should == true
104
+ dir.parent.exists?.should == true
105
+ end
106
+
107
+ it 'should delete dir and its children' do
108
+ dir = @dir.dir('one').dir('two').dir('three')
109
+ dir.mkdirs
110
+ @dir.exists?.should == true
111
+ @dir.delete
112
+ dir.exists?.should == false
113
+ @dir.exists?.should == false
114
+ end
115
+
116
+ it 'should do nothing if dir does not exist' do
117
+ dir = @dir.dir('one/two')
118
+ dir.delete
119
+ dir.delete
120
+ end
121
+
122
+ it 'should do nothing on mkdir if dir already exists' do
123
+ dir = @dir.dir('one').dir('two')
124
+ dir.mkdirs
125
+ dir.mkdirs
126
+ end
127
+
128
+ it 'should list dirs' do
129
+ @dir.dir('one').mkdirs
130
+ @dir.file('one.txt').save
131
+ actual_dir_list = @dir.list
132
+ actual_dir_list.size.should == 2
133
+ actual_dir_list[0].name.should == 'one'
134
+ actual_dir_list[0].list.size.should == 0
135
+ actual_dir_list[1].name.should == 'one.txt'
136
+ actual_dir_list[1].save
137
+ end
138
+
139
+ it 'should move directory with its children' do
140
+ dir = Cotta::CottaDir.new(@system, Pathname.new('targetdir/child_dir'))
141
+ @dir.file('file.txt').save('file.txt')
142
+ @dir.dir('subdir').mkdirs
143
+ @dir.list.size.should == 2
144
+ @dir.move_to(dir)
145
+ @dir.exists?.should == false
146
+ dir.list.size.should == 2
147
+ dir.file('file.txt').load.should == 'file.txt'
148
+ dir.dir('subdir').exists?.should == true
149
+ end
150
+
151
+ it 'should copy directory with its children' do
152
+ dir = Cotta::CottaDir.new(@system, Pathname.new('targetdir/child_dir'))
153
+ @dir.file('file.txt').save('file.txt')
154
+ @dir.dir('subdir').mkdirs
155
+ @dir.list.size.should == 2
156
+ @dir.copy_to(dir)
157
+ @dir.exists?.should == true
158
+ dir.list.size.should == 2
159
+ dir.file('file.txt').load.should == 'file.txt'
160
+ dir.dir('subdir').exists?.should == true
161
+ end
162
+
163
+ it 'dir takes relative path' do
164
+ dir = Cotta::CottaDir.new(@system, Pathname.new('targetdir/dir'))
165
+ @dir.dir('one/two/three').mkdirs
166
+ @dir.dir('one').exists?.should == true
167
+ @dir.dir('one').dir('two').exists?.should == true
168
+ @dir.dir('one').dir('two').dir('three').exists?.should == true
169
+ end
170
+
171
+ it 'list on not existing directory' do
172
+ dir = Cotta::CottaDir.new(@system, Pathname.new('no/such/directory'))
173
+ Proc.new do
174
+ dir.list
175
+ end.should raise_error(Errno::ENOENT)
176
+ end
177
+
178
+ it 'allow filter for archive' do
179
+ @dir.file('in/in.txt').save('test')
180
+ @dir.file('out/out.txt').save('test')
181
+ result = @dir.archive {|entry|entry.name != 'out' }
182
+ target = result.extract(@dir.dir('extract'))
183
+ target.dir('out').should_not be_exist
184
+ target.dir('in').should be_exist
185
+ end
186
+
187
+ it 'should support changing directory' do
188
+ dir = @dir.dir('one/two')
189
+ file = @dir.file('marker').save('marker')
190
+ dir.mkdirs
191
+ result = dir.chdir
192
+ result.should == 0
193
+ current = dir.factory.pwd
194
+ value = @root.chdir do
195
+ dir.factory.pwd.should == @root
196
+ 'value'
197
+ end
198
+ value.should == 'value'
199
+ dir.factory.pwd.should == current
200
+ end
201
+
202
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec'
2
+
3
+ dir = File.dirname(__FILE__)
4
+ require dir + '/../test'
5
+ require dir + '/cotta_dir_behaviors'
6
+
7
+ describe Cotta::InMemorySystem, 'with Cotta Dir' do
8
+ it_should_behave_like 'CottaDirBehaviors'
9
+
10
+ def create_system
11
+ @system = Cotta.in_memory
12
+ end
13
+
14
+ it 'dir should not be equal if system different' do
15
+ (Cotta::CottaDir.new(Cotta.in_memory, Pathname.new('dir')) == @dir).should == false
16
+ end
17
+
18
+ it 'to_s and inspect' do
19
+ file = Cotta::CottaFile.new(@system, '/one/two/file.txt')
20
+ "#{file.to_s}".should == '/one/two/file.txt'
21
+ end
22
+
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec'
2
+ require File.join(File.dirname(__FILE__)) + '/../test'
3
+ require File.join(File.dirname(__FILE__)) + '/cotta_dir_behaviors'
4
+
5
+ module Cotta
6
+ describe PhysicalSystem, 'work with CottaDir' do
7
+ it_should_behave_like 'CottaDirBehaviors'
8
+
9
+ def create_system
10
+ @system = Cotta::FileFactory.new(PhysicalSystemStub.new)
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,139 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+ require 'spec'
3
+ require 'pathname'
4
+
5
+ module Cotta
6
+ describe 'CottaFileBehaviors', :shared => true do
7
+ before do
8
+ @system = create_system
9
+ @file = CottaFile.new(@system, Pathname.new('dir/file.txt'))
10
+ end
11
+ it 'file can be created with system and pathname' do
12
+ @file.name.should == 'file.txt'
13
+ @file.path.should == Pathname.new('dir/file.txt')
14
+ @file.exists?.should == false
15
+ end
16
+
17
+ it 'should return path on to_s for scripting convenience' do
18
+ @file.to_s.should == 'dir/file.txt'
19
+ "#{@file}".should == 'dir/file.txt'
20
+ end
21
+
22
+ it 'file should know properties like parent, name, etc.' do
23
+ @file.parent.should == CottaDir.new(@system, Pathname.new('dir'))
24
+ @file.name.should == 'file.txt'
25
+ @file.path.should == Pathname.new('dir/file.txt')
26
+ @file.extname.should == '.txt'
27
+ @file.basename.should == 'file'
28
+ end
29
+
30
+ it 'should support relative path' do
31
+ parent = @file.parent
32
+ file = parent.file('one/two/three.txt')
33
+ file.relative_path_from(parent).to_s.should == 'one/two/three.txt'
34
+ end
35
+
36
+ it 'file should support stat' do
37
+ @file.save('test')
38
+ @file.stat.should_not be_nil
39
+ @file.stat.size.should == 4
40
+ @file.stat.writable?.should == true
41
+ end
42
+
43
+ it 'should raise error if does not exist' do
44
+ Proc.new {
45
+ @file.stat
46
+ }.should raise_error(Errno::ENOENT)
47
+ end
48
+
49
+ it 'should load and save file content' do
50
+ @file.exists?.should == false
51
+ @file.parent.exists?.should == false
52
+ @file.save("content to save\nsecond line")
53
+ @file.exists?.should == true
54
+ @file.load.should ==("content to save\nsecond line")
55
+ end
56
+
57
+ it 'should open file to read' do
58
+ @file.save("one\ntwo")
59
+ @file.read do |file|
60
+ file.gets.should ==("one\n")
61
+ file.gets.should ==('two')
62
+ end
63
+ end
64
+
65
+ it 'should equal if same system and pathname' do
66
+ file2 = CottaFile.new(@system, Pathname.new('dir/file.txt'))
67
+ file2.should == @file
68
+ end
69
+
70
+ it 'should copy to another file' do
71
+ file2 = CottaFile.new(@system, Pathname.new('dir2/file.txt'))
72
+ file2.exists?.should == false
73
+ @file.save('my content')
74
+ @file.copy_to(file2)
75
+ file2.exists?.should == true
76
+ file2.load.should == 'my content'
77
+ end
78
+
79
+ it 'should move file' do
80
+ file2 = CottaFile.new(@system, Pathname.new('dir2/file.txt'))
81
+ file2.exists?.should == false
82
+ @file.save('content')
83
+ @file.move_to(file2)
84
+ file2.exists?.should == true
85
+ file2.load.should == 'content'
86
+ @file.exists?.should == false
87
+ end
88
+
89
+ it 'should support foreach' do
90
+ @file.write do |file|
91
+ file.puts 'line one'
92
+ file.puts 'line two'
93
+ end
94
+ collected = Array.new
95
+ @file.foreach do |line|
96
+ collected.push line
97
+ end
98
+ collected.size.should == 2
99
+ collected[0].should == "line one\n"
100
+ collected[1].should == "line two\n"
101
+ end
102
+
103
+ it 'should delete file' do
104
+ @file.save
105
+ @file.exists?.should == true
106
+ @file.delete
107
+ @file.exists?.should == false
108
+ end
109
+
110
+ it 'should raise error if file to delete does not exist' do
111
+ lambda {
112
+ @file.delete
113
+ }.should raise_error(Errno::ENOENT)
114
+ end
115
+
116
+ it 'should check timestamp to see which one is older' do
117
+ @file.save
118
+ file2 = @file.parent.file('another.txt')
119
+ sleep 1
120
+ file2.save
121
+ file2.older_than?(@file).should == false
122
+ @file.older_than?(file2).should == true
123
+ end
124
+
125
+ it 'copy binary io' do
126
+ file = FileFactory.parent_dir(__FILE__).file('logo.gif')
127
+ target = @file
128
+ file.read_binary do |input|
129
+ target.write_binary do |output|
130
+ CottaFile.copy_io(input, output)
131
+ end
132
+ end
133
+ expect_stat = file.stat
134
+ actual_stat = target.stat
135
+ actual_stat.size.should == expect_stat.size
136
+ end
137
+ end
138
+
139
+ end