cotta 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,75 @@
1
+ Welcome to the Ruby version of the {Cotta file API}[http://cotta.rubyforge.org]
2
+
3
+ = Introduction
4
+
5
+ Cotta project is created to provide a lightweight, simple and sensible API to file operation and testing.
6
+ See {Cotta Power}[http://cotta.sourceforge.net/power.html] for its motivation
7
+
8
+
9
+ = Install It
10
+
11
+ The easiest way to install the install cotta using RubyGems:
12
+
13
+ sudo gem install cotta
14
+
15
+ = Features
16
+
17
+ Cotta is just a plain Ruby API, so you can use it wherever you can use Ruby.
18
+
19
+ To used the new API just require the client driver:
20
+
21
+ require "rubygems"
22
+ require "cotta"
23
+
24
+ For a fully backward compatible API you can start with:
25
+
26
+ require "rubygems"
27
+ gem "cotta"
28
+ require "cotta"
29
+
30
+ For instance
31
+ to write a little Ruby script using cotta you could write something like:
32
+
33
+ #!/usr/bin/env ruby
34
+ #
35
+ # Sample Ruby script using the Cotta API
36
+ #
37
+ require "rubygems"
38
+ gem "cotta", ">=1.0.0"
39
+ require "cotta"
40
+
41
+ #system implementation is injected here
42
+ cotta = Cotta.physical
43
+ file = cotta.file('dir/file.txt')
44
+ file.should_not be_exists
45
+ # parent directories are created automatically
46
+ file.save('my content')
47
+ file2 = cotta.file('dir/file2.txt')
48
+ file2.should_not be_exists
49
+ file.copy_to(file2)
50
+ file2.should be_exists
51
+ file2.load.should == 'my content'
52
+ file2.read {|file| puts file.gets}
53
+
54
+ = Writing Tests
55
+
56
+ To test your code that uses Cotta API, you just need to pass in a Cotta instance that is backed by an in-memory
57
+ file system:
58
+
59
+ cotta = Cotta.in_memory
60
+
61
+ = Resources
62
+
63
+ * Source Code at http://github.com/wolfdancer/cotta
64
+ * Report bugs at http://github.com/wolfdancer/cotta/issues
65
+ * Browse API at http://cotta.rubyforge.org
66
+ * Discuss at http://groups.google.com/group/cotta
67
+
68
+ = Reports
69
+
70
+ * rSpec: {rspec}[link:rspec/index.html]
71
+ * code coverage:{rcov}[link:rcov/index.html]
72
+
73
+ = Team
74
+
75
+ * Shane Duan
@@ -0,0 +1,12 @@
1
+ require 'pathname'
2
+ require File.dirname(__FILE__) + '/cotta/cotta'
3
+ require File.dirname(__FILE__) + '/cotta/cotta_dir'
4
+ require File.dirname(__FILE__) + '/cotta/cotta_file'
5
+ require File.dirname(__FILE__) + '/cotta/impl/cotta_pathname'
6
+ require File.dirname(__FILE__) + '/cotta/impl/in_memory_system'
7
+ require File.dirname(__FILE__) + '/cotta/impl/physical_system'
8
+ require File.dirname(__FILE__) + '/cotta/impl/command_error'
9
+ require File.dirname(__FILE__) + '/cotta/impl/command_interface'
10
+ require File.dirname(__FILE__) + '/cotta/file_not_found_error'
11
+ require File.dirname(__FILE__) + '/cotta/impl/command_runner'
12
+ require File.dirname(__FILE__) + '/cotta/file_factory'
@@ -0,0 +1,37 @@
1
+ # Cotta module that contains all the classes used for file operations
2
+ # see link:files/README.html
3
+ # There are also four methods on the module for the most common ways to
4
+ # start with Cotta API
5
+ module Cotta
6
+ # Creates CottaFile repersenting physical file
7
+ def self::file(path)
8
+ FileFactory.file(path)
9
+ end
10
+
11
+ # Creates CottaDir representing physical directory
12
+ def self::dir(path)
13
+ FileFactory.dir(path)
14
+ end
15
+
16
+ # Creates CottaDir that is the parent of the path
17
+ # This is typically used with __FILE__
18
+ # e.g. dir = Cotta.parent_dir(__FILE__)
19
+ def self.parent_dir(path)
20
+ FileFactory.parent_dir(path)
21
+ end
22
+
23
+ # Returns the file facotry backed by physical system
24
+ def self::physical
25
+ FileFactory.physical
26
+ end
27
+
28
+ # Returns the file factory backed by in-memory system
29
+ def self::in_memory
30
+ FileFactory.in_memory
31
+ end
32
+
33
+ # returns the file factory backed by the given system
34
+ def self::factory(system)
35
+ FileFactory.new(system)
36
+ end
37
+ end
@@ -0,0 +1,184 @@
1
+ module Cotta
2
+ # This class represents a directory
3
+ class CottaDir
4
+ # Path of the directory
5
+ attr_reader :path
6
+
7
+ # file factory of the directory
8
+ attr_reader :factory
9
+
10
+ # Create an instance of CottaDir that is on
11
+ # the given path and
12
+ # backed by the given system
13
+ def initialize(factory, path)
14
+ @path = path
15
+ @factory = factory
16
+ @name = @path.basename.to_s
17
+ end
18
+
19
+ # name of the directory
20
+ def name
21
+ name = nil
22
+ if root?
23
+ name = path.to_s
24
+ else
25
+ name = @path.basename.to_s
26
+ end
27
+ return name
28
+ end
29
+
30
+ # returns true if this directory is the root directory
31
+ def root?
32
+ parent.nil?
33
+ end
34
+
35
+ # returns true if this directory exists
36
+ def exists?
37
+ factory.system.dir_exists?(@path)
38
+ end
39
+
40
+ # returns the stat of the current directory
41
+ def stat
42
+ factory.system.dir_stat(@path)
43
+ end
44
+
45
+ # returns the parent directory of this directory
46
+ # or nil if this is root
47
+ def parent
48
+ parent_path = @path.cotta_parent
49
+ return nil unless parent_path
50
+ candidate = CottaDir.new(factory, parent_path)
51
+ if (block_given?)
52
+ candidate = candidate.parent until candidate.nil? or yield candidate
53
+ end
54
+ candidate
55
+ end
56
+
57
+ # returns the relative path from the given file or directory
58
+ def relative_path_from(entry)
59
+ @path.relative_path_from(entry.path)
60
+ end
61
+
62
+ # returns the sub-directory with the given name
63
+ def dir(name)
64
+ return CottaDir.new(factory, @path.join(name))
65
+ end
66
+
67
+ # returns the file under this directory with the given name
68
+ def file(name)
69
+ return CottaFile.new(factory, @path.join(name))
70
+ end
71
+
72
+ # creates this directory and its parent directory
73
+ def mkdirs
74
+ if (not exists?)
75
+ parent.mkdirs
76
+ factory.system.mkdir @path
77
+ end
78
+ end
79
+
80
+ # deletes this directory and all its children
81
+ def delete
82
+ if (exists?)
83
+ list.each do |children|
84
+ children.delete
85
+ end
86
+ factory.system.delete_dir(@path)
87
+ end
88
+ end
89
+
90
+ # move this directory to target directory
91
+ # this method assumes that this directory and the target directory
92
+ # are backed by the same file system
93
+ def move_to(target)
94
+ target.parent.mkdirs
95
+ factory.system.move_dir(@path, target.path)
96
+ end
97
+
98
+ # move this directory to target path
99
+ # this method assumes that this directory and the target directory
100
+ # are backed by the same file system
101
+ def move_to_path(target_path)
102
+ move_to(cotta.dir(target_path))
103
+ end
104
+
105
+ # copy this directory to target directory
106
+ # this method assumes that this directory and the target directory
107
+ # are backed by the same file system
108
+ def copy_to(target)
109
+ target.parent.mkdirs
110
+ factory.system.copy_dir(@path, target.path)
111
+ end
112
+
113
+ # archive this directory and call the given block
114
+ # to determine if a file or directory should be included
115
+ def archive(target = nil, &block)
116
+ require 'rubygems/package'
117
+ unless target
118
+ target = parent.file("#{name}.tar")
119
+ end
120
+ target.write_binary do |io|
121
+ writer = Gem::Package::TarWriter.new(io) do |tar_io|
122
+ archive_dir(tar_io, self, &block)
123
+ end
124
+ end
125
+ target
126
+ end
127
+
128
+ def archive_dir(tar_io, dir, &block)
129
+ dir.list.each do |child|
130
+ if (block_given? and not yield child)
131
+ next
132
+ end
133
+ stat = child.stat
134
+ entry_name = child.relative_path_from(self).to_s
135
+ mode = stat.mode
136
+ if (stat.file?)
137
+ tar_io.add_file(entry_name, mode) do |entry_output|
138
+ child.read_binary do |input|
139
+ CottaFile.copy_io(input, entry_output)
140
+ end
141
+ end
142
+ elsif (stat.directory?)
143
+ tar_io.mkdir(entry_name, mode)
144
+ archive_dir(tar_io, child, &block)
145
+ end
146
+ end
147
+ end
148
+
149
+ private :archive_dir
150
+
151
+ def copy_to_path(target_path)
152
+ copy_to(cotta.dir(target_path))
153
+ end
154
+
155
+ # returns the content of this directory
156
+ # as an array of CottaFile and CottaDirectory
157
+ def list
158
+ factory.system.list(@path).collect do |item|
159
+ candidate = dir(item)
160
+ if (not candidate.exists?)
161
+ candidate = file(item)
162
+ end
163
+ candidate
164
+ end
165
+ end
166
+
167
+ def chdir(&block)
168
+ factory.system.chdir(@path, &block)
169
+ end
170
+
171
+ def ==(other)
172
+ return @path == other.path && factory.system == other.factory.system
173
+ end
174
+
175
+ def inspect
176
+ return "#{self.class}:#{self.object_id}-#@path"
177
+ end
178
+
179
+ def to_s
180
+ @path.to_s
181
+ end
182
+
183
+ end
184
+ end
@@ -0,0 +1,253 @@
1
+ require 'fileutils'
2
+
3
+ module Cotta
4
+ # This class represents a file
5
+ class CottaFile
6
+ # factory with the backing system
7
+ attr_reader :factory
8
+ # path of this file
9
+ attr_reader :path
10
+ # stats of this file
11
+ attr_reader :stat
12
+
13
+ # creates an instance of file with the given factory and path
14
+ def initialize(factory, path)
15
+ @path = path
16
+ @factory = factory
17
+ end
18
+
19
+ # name of this file
20
+ def name
21
+ return @path.basename.to_s
22
+ end
23
+
24
+ # extension of this file, with '.'
25
+ def extname
26
+ return @path.extname
27
+ end
28
+
29
+ # basename of this file
30
+ def basename
31
+ return @path.basename(extname).to_s
32
+ end
33
+
34
+ # stats of this file
35
+ def stat
36
+ factory.system.file_stat(@path)
37
+ end
38
+
39
+ # returns true if this file is older than the given file
40
+ def older_than?(file)
41
+ (stat <=> file.stat) == -1
42
+ end
43
+
44
+ # returns true if this file exists
45
+ def exists?
46
+ return factory.system.file_exists?(@path)
47
+ end
48
+
49
+ # returns the relative path from the given file or directory
50
+ def relative_path_from(entry)
51
+ path.relative_path_from(entry.path)
52
+ end
53
+
54
+ # returns the parent directory
55
+ def parent
56
+ return CottaDir.new(factory, @path.parent)
57
+ end
58
+
59
+ # copy this file to the target file
60
+ def copy_to(target_file)
61
+ target_file.parent.mkdirs
62
+ factory.system.copy_file(path, target_file.path)
63
+ target_file
64
+ end
65
+
66
+ # copy this file to the target path
67
+ def copy_to_path(target_path)
68
+ copy_to(cotta.file(target_path))
69
+ end
70
+
71
+ # move this file to the target file
72
+ def move_to(target_file)
73
+ target_file.parent.mkdirs
74
+ factory.system.move_file(path, target_file.path)
75
+ end
76
+
77
+ # move this file to the target path
78
+ def move_to_path(target_path)
79
+ move_to(cotta.file(target_path))
80
+ end
81
+
82
+ # save conent to this file
83
+ # this will create the parent directory if necessary
84
+ def save(content = '')
85
+ write {|file| file.printf content.to_s}
86
+ self
87
+ end
88
+
89
+ # Calls open with 'w' argument and makes sure that the
90
+ # parent directory of the file exists
91
+ def write(&block)
92
+ parent.mkdirs
93
+ open('w', &block)
94
+ end
95
+
96
+ # Calls open with 'a' argument and make sure that the
97
+ # parent directory of the file exists
98
+ def append(&block)
99
+ parent.mkdirs
100
+ open('a', &block)
101
+ end
102
+
103
+ # Calls open with 'wb' argument, sets the io to binmode
104
+ # and make sure that the parent directory of the file exists
105
+ def write_binary(&block)
106
+ parent.mkdirs
107
+ if (block_given?)
108
+ open('wb') do |io|
109
+ io.binmode
110
+ yield io
111
+ end
112
+ else
113
+ io = open('wb')
114
+ io.binmode
115
+ io
116
+ end
117
+ end
118
+
119
+ =begin rdoc
120
+ Loading the file full total. This is used generally for loading
121
+ an ascii file content. It does not work with binary file on
122
+ windows system because it does no put the system on binary mode
123
+ =end
124
+ def load
125
+ content = nil
126
+ size = stat.size
127
+ read do |io|
128
+ content = io.read
129
+ end
130
+ return content
131
+ end
132
+
133
+ # calls open with 'r' as argument.
134
+ def read(&block)
135
+ open('r', &block)
136
+ end
137
+
138
+ # Calls open with 'r' as argument and sets the io to binary mode
139
+ def read_binary
140
+ if block_given?
141
+ open('r') do |io|
142
+ io.binmode
143
+ yield io
144
+ end
145
+ else
146
+ io = open('r')
147
+ io.binmode
148
+ io
149
+ end
150
+ end
151
+
152
+ # reads the file and calls back for each line
153
+ def foreach()
154
+ open('r') do |file|
155
+ file.each {|line| yield line}
156
+ end
157
+ end
158
+
159
+ # opens the file with the g iven arguments
160
+ def open(*args)
161
+ result = f = factory.system.io(@path, *args)
162
+ if block_given?
163
+ begin
164
+ result = yield f
165
+ ensure
166
+ f.close unless f.closed?
167
+ end
168
+ end
169
+ result
170
+ end
171
+
172
+ # deletes the file
173
+ def delete
174
+ factory.system.delete_file(@path)
175
+ end
176
+
177
+ # reads this file as an archive and extracts the content
178
+ # to the target directory. If the target directory
179
+ # is missing, it will create a directory in the same
180
+ # directory as this file with the same basename
181
+ def extract(directory = nil)
182
+ require 'rubygems/package'
183
+ directory = parent.dir(basename) unless directory
184
+ read_binary do |io|
185
+ reader = Gem::Package::TarReader.new(io)
186
+ reader.each do |entry|
187
+ full_name = entry.full_name
188
+ if (entry.file?)
189
+ directory.file(full_name).write_binary do |output|
190
+ CottaFile.copy_io(entry, output)
191
+ end
192
+ elsif (entry.directory?)
193
+ directory.dir(full_name).mkdirs
194
+ end
195
+ end
196
+ end
197
+ directory
198
+ end
199
+
200
+ # Create the zip file from current file
201
+ # When target is nil, the output will be the name of the current file appended with ".zip"
202
+ def zip(target = nil, level=nil, strategy=nil)
203
+ target = parent.file("#{name}.zip") unless target
204
+ read_binary do |read_io|
205
+ target.write_binary do |write_io|
206
+ gz = Zlib::GzipWriter.new(write_io, level, strategy)
207
+ CottaFile.copy_io(read_io, gz)
208
+ gz.close
209
+ end
210
+ end
211
+ target
212
+ end
213
+
214
+ # unzips the file
215
+ def unzip
216
+ name = basename
217
+ if (extname.length == 0)
218
+ name = "#{name}.unzip"
219
+ end
220
+ target = parent.file(name)
221
+ read_binary do |read_io|
222
+ target.write_binary do |write_io|
223
+ gz = Zlib::GzipReader.new(read_io)
224
+ CottaFile.copy_io(gz, write_io)
225
+ gz.close
226
+ end
227
+ end
228
+ target
229
+ end
230
+
231
+ # copy from input handle to output handle
232
+ def self::copy_io(input, output)
233
+ # output.write input.read
234
+ while (content = input.read(1024)) do
235
+ output.write content
236
+ end
237
+ end
238
+
239
+ def ==(other)
240
+ return false unless other.kind_of? CottaFile
241
+ return factory.system == other.factory.system && @path == other.path
242
+ end
243
+
244
+ def to_s
245
+ @path.to_s
246
+ end
247
+
248
+ def inspect
249
+ return "#{self.class}:#{self.object_id}-#{factory.system.inspect}-#@path"
250
+ end
251
+
252
+ end
253
+ end