ucf 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changes.rdoc +19 -0
- data/ReadMe.rdoc +5 -4
- data/examples/create_ucf.rb +3 -0
- data/lib/ucf/container.rb +231 -127
- data/lib/ucf/exceptions.rb +68 -0
- data/lib/ucf.rb +4 -10
- data/test/data/example.ucf +0 -0
- data/test/tc_create.rb +37 -2
- data/test/tc_read.rb +12 -4
- data/test/tc_reserved_names.rb +229 -1
- data/ucf.gemspec +3 -2
- data/version.yml +2 -2
- metadata +3 -2
data/Changes.rdoc
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
= Changes log for the UCF Ruby Gem
|
2
2
|
|
3
|
+
== Version 0.1.0
|
4
|
+
|
5
|
+
* Improvements to the reserved names code to allow sub-classing.
|
6
|
+
* Move exceptions to a new source file.
|
7
|
+
* Use a base class for UCF exceptions.
|
8
|
+
* Standardize the MalformedUCFError exception messages.
|
9
|
+
* Add an exception for clashes with reserved names.
|
10
|
+
* Implement Container#add to avoid using reserved names.
|
11
|
+
* Raise an exception if renaming to a reserved name.
|
12
|
+
* Implement Container#mkdir to avoid using reserved names.
|
13
|
+
* Make sure testing reserved names copes with trailing slashes.
|
14
|
+
* Fake up the connection to ZipFileSystem.
|
15
|
+
* Implement Container#get_output_stream to respect reserved names.
|
16
|
+
* Can now set comments on the UCF document.
|
17
|
+
* Implement the close and commit methods.
|
18
|
+
* Forward the commit_required? method.
|
19
|
+
* Separate the opening and checking of a UCF document.
|
20
|
+
* Documentation improvements and cleanup.
|
21
|
+
|
3
22
|
== Version 0.0.2
|
4
23
|
|
5
24
|
* Update the main ReadMe file.
|
data/ReadMe.rdoc
CHANGED
@@ -7,6 +7,7 @@ Source code:: https://github.com/myGrid/ruby-ucf
|
|
7
7
|
Licence:: BSD (See Licence file or http://www.opensource.org/licenses/bsd-license.php)
|
8
8
|
Copyright:: (c) 2013 The University of Manchester, UK
|
9
9
|
|
10
|
+
{<img src="https://codeclimate.com/github/myGrid/ruby-ucf.png" />}[https://codeclimate.com/github/myGrid/ruby-ucf]
|
10
11
|
|
11
12
|
== Synopsis
|
12
13
|
|
@@ -28,12 +29,12 @@ directory. See the contents of the tests directory for even more.
|
|
28
29
|
The basic requirements of a UCF document are all implemented but there are a
|
29
30
|
number of optional features that are not yet provided.
|
30
31
|
|
31
|
-
*
|
32
|
-
that are resident on disk
|
32
|
+
* Memory resident UCF documents. Presently all operations are performed on
|
33
|
+
documents that are resident on disk as the underlying
|
34
|
+
{rubyzip library}[https://github.com/aussiegeek/rubyzip] currently
|
35
|
+
{cannot do anything else}[https://github.com/aussiegeek/rubyzip/issues/74].
|
33
36
|
* META-INF directory support. Everything within the META-INF directory is
|
34
37
|
optional but will supported in a near future version.
|
35
|
-
* Changing the mimetype. It is not certain that this is a sensible operation
|
36
|
-
but is being considered for a future version.
|
37
38
|
* Digital signatures (this feature has been deferred until a future revision
|
38
39
|
of the UCF specification. It will be supported by this gem when it is added
|
39
40
|
to the specification).
|
data/examples/create_ucf.rb
CHANGED
@@ -51,6 +51,9 @@ begin
|
|
51
51
|
|
52
52
|
# Copy this example code in straight from a file.
|
53
53
|
c.add("dir/code.rb", __FILE__)
|
54
|
+
|
55
|
+
# Add a explanation of this file.
|
56
|
+
c.comment = "This is an example UCF file!"
|
54
57
|
end
|
55
58
|
rescue UCF::MalformedUCFError, Zip::ZipError => err
|
56
59
|
puts err.to_s
|
data/lib/ucf/container.rb
CHANGED
@@ -35,53 +35,58 @@ require 'zip/zipfilesystem'
|
|
35
35
|
|
36
36
|
module UCF
|
37
37
|
|
38
|
-
# This class represents a UCF
|
39
|
-
# https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format
|
38
|
+
# This class represents a UCF document in PK Zip format. See
|
39
|
+
# {the specification}[https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format]
|
40
40
|
# for more details.
|
41
41
|
#
|
42
|
-
# This class
|
43
|
-
# class in the rubyzip gem. Please also consult the
|
44
|
-
# http://rubydoc.info/gems/rubyzip/0.9.9/frames
|
42
|
+
# This class provides most of the facilities of the <tt>Zip::ZipFile</tt>
|
43
|
+
# class in the rubyzip gem. Please also consult the
|
44
|
+
# {rubyzip documentation}[http://rubydoc.info/gems/rubyzip/0.9.9/frames]
|
45
|
+
# alongside these pages.
|
45
46
|
#
|
46
47
|
# There are code examples available with the source code of this library.
|
47
48
|
class Container
|
48
49
|
|
49
50
|
extend Forwardable
|
50
|
-
def_delegators :@zipfile, :
|
51
|
-
:extract, :
|
52
|
-
:
|
51
|
+
def_delegators :@zipfile, :comment, :comment=, :commit_required?, :each,
|
52
|
+
:extract, :find_entry, :get_entry, :get_input_stream, :glob, :name,
|
53
|
+
:read, :size
|
53
54
|
|
54
55
|
private_class_method :new
|
55
56
|
|
56
|
-
# The mime-type of this UCF
|
57
|
+
# The mime-type of this UCF document. By default this is
|
57
58
|
# "application/epub+zip".
|
58
59
|
attr_reader :mimetype
|
59
60
|
|
60
61
|
# :stopdoc:
|
61
62
|
DEFAULT_MIMETYPE = "application/epub+zip"
|
62
63
|
|
63
|
-
# Reserved
|
64
|
-
# case-insensitive so downcase where required in the reserved list.
|
64
|
+
# Reserved file and directory names for standard UCF documents.
|
65
65
|
MIMETYPE_FILE = "mimetype"
|
66
66
|
META_INF_DIR = "META-INF"
|
67
|
-
RESERVED_ROOT_NAMES = [MIMETYPE_FILE, META_INF_DIR.downcase]
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def initialize(filename)
|
74
|
-
@zipfile = open_and_check_ucf(filename)
|
68
|
+
def initialize(document)
|
69
|
+
@zipfile = open_document(document)
|
70
|
+
check_document!
|
75
71
|
|
76
72
|
@mimetype = read_mimetype
|
73
|
+
@on_disk = true
|
74
|
+
|
75
|
+
# Here we fake up the connection to the rubyzip filesystem classes so
|
76
|
+
# that they also respect the reserved names that we define.
|
77
|
+
mapped_zip = ::Zip::ZipFileSystem::ZipFileNameMapper.new(self)
|
78
|
+
@fs_dir = ::Zip::ZipFileSystem::ZipFsDir.new(mapped_zip)
|
79
|
+
@fs_file = ::Zip::ZipFileSystem::ZipFsFile.new(mapped_zip)
|
80
|
+
@fs_dir.file = @fs_file
|
81
|
+
@fs_file.dir = @fs_dir
|
77
82
|
end
|
78
83
|
# :startdoc:
|
79
84
|
|
80
85
|
# :call-seq:
|
81
|
-
# Container.create(filename, mimetype = "application/epub+zip") ->
|
82
|
-
# Container.create(filename, mimetype = "application/epub+zip") {|
|
86
|
+
# Container.create(filename, mimetype = "application/epub+zip") -> document
|
87
|
+
# Container.create(filename, mimetype = "application/epub+zip") {|document| ...}
|
83
88
|
#
|
84
|
-
# Create a new UCF
|
89
|
+
# Create a new UCF document on disk with the specified mimetype.
|
85
90
|
def Container.create(filename, mimetype = DEFAULT_MIMETYPE, &block)
|
86
91
|
::Zip::ZipOutputStream.open(filename) do |stream|
|
87
92
|
stream.put_next_entry(MIMETYPE_FILE, nil, nil, ::Zip::ZipEntry::STORED)
|
@@ -113,11 +118,11 @@ module UCF
|
|
113
118
|
end
|
114
119
|
|
115
120
|
# :call-seq:
|
116
|
-
# Container.open(filename) ->
|
117
|
-
# Container.open(filename) {|
|
121
|
+
# Container.open(filename) -> document
|
122
|
+
# Container.open(filename) {|document| ...}
|
118
123
|
#
|
119
|
-
# Open an existing UCF
|
120
|
-
# to the UCF specification upon first access.
|
124
|
+
# Open an existing UCF document from disk. It will be checked for
|
125
|
+
# conformance to the UCF specification upon first access.
|
121
126
|
def Container.open(filename, &block)
|
122
127
|
c = new(filename)
|
123
128
|
|
@@ -135,9 +140,9 @@ module UCF
|
|
135
140
|
# :call-seq:
|
136
141
|
# Container.verify(filename) -> boolean
|
137
142
|
#
|
138
|
-
# Verify that the specified UCF
|
139
|
-
# This method returns +false+ if there are any problems at
|
140
|
-
# file (including if it
|
143
|
+
# Verify that the specified UCF document conforms to the UCF
|
144
|
+
# specification. This method returns +false+ if there are any problems at
|
145
|
+
# all with the file (including if it cannot be found).
|
141
146
|
def Container.verify(filename)
|
142
147
|
begin
|
143
148
|
Container.verify!(filename)
|
@@ -151,128 +156,261 @@ module UCF
|
|
151
156
|
# :call-seq:
|
152
157
|
# Container.verify!(filename)
|
153
158
|
#
|
154
|
-
# Verify that the specified UCF
|
155
|
-
# This method raises exceptions when errors are found or if
|
156
|
-
# something fundamental wrong with the file itself (e.g.
|
159
|
+
# Verify that the specified UCF document conforms to the UCF
|
160
|
+
# specification. This method raises exceptions when errors are found or if
|
161
|
+
# there is something fundamental wrong with the file itself (e.g. it
|
162
|
+
# cannot be found).
|
157
163
|
def Container.verify!(filename)
|
158
164
|
new(filename).close
|
159
165
|
nil
|
160
166
|
end
|
161
167
|
|
168
|
+
# :call-seq:
|
169
|
+
# add(entry, src_path, &continue_on_exists_proc)
|
170
|
+
#
|
171
|
+
# Convenience method for adding the contents of a file to the UCF
|
172
|
+
# document. If asked to add a file with a reserved name, such as the
|
173
|
+
# special mimetype header file, this method will raise a
|
174
|
+
# ReservedNameClashError.
|
175
|
+
#
|
176
|
+
# See the rubyzip documentation for details of the
|
177
|
+
# +continue_on_exists_proc+ parameter.
|
178
|
+
def add(entry, src_path, &continue_on_exists_proc)
|
179
|
+
raise ReservedNameClashError.new(entry.to_s) if reserved_entry?(entry)
|
180
|
+
|
181
|
+
@zipfile.add(entry, src_path, &continue_on_exists_proc)
|
182
|
+
end
|
183
|
+
|
184
|
+
# :call-seq:
|
185
|
+
# commit -> boolean
|
186
|
+
# close -> boolean
|
187
|
+
#
|
188
|
+
# Commits changes that have been made since the previous commit to the
|
189
|
+
# UCF document. Returns +true+ if anything was actually done, +false+
|
190
|
+
# otherwise.
|
191
|
+
def commit
|
192
|
+
return false unless commit_required?
|
193
|
+
|
194
|
+
if on_disk?
|
195
|
+
@zipfile.commit
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
alias :close :commit
|
200
|
+
|
201
|
+
# :call-seq:
|
202
|
+
# dir -> Zip::ZipFsDir
|
203
|
+
#
|
204
|
+
# Returns an object which can be used like ruby's built in +Dir+ (class)
|
205
|
+
# object, except that it works on the UCF document on which this method is
|
206
|
+
# invoked.
|
207
|
+
#
|
208
|
+
# See the rubyzip documentation for details.
|
209
|
+
def dir
|
210
|
+
@fs_dir
|
211
|
+
end
|
212
|
+
|
213
|
+
# :call-seq:
|
214
|
+
# file -> Zip::ZipFsFile
|
215
|
+
#
|
216
|
+
# Returns an object which can be used like ruby's built in +File+ (class)
|
217
|
+
# object, except that it works on the UCF document on which this method is
|
218
|
+
# invoked.
|
219
|
+
#
|
220
|
+
# See the rubyzip documentation for details.
|
221
|
+
def file
|
222
|
+
@fs_file
|
223
|
+
end
|
224
|
+
|
225
|
+
# :call-seq:
|
226
|
+
# get_output_stream(entry, permission = nil) -> stream
|
227
|
+
# get_output_stream(entry, permission = nil) {|stream| ...}
|
228
|
+
#
|
229
|
+
# Returns an output stream to the specified entry. If a block is passed
|
230
|
+
# the stream object is passed to the block and the stream is automatically
|
231
|
+
# closed afterwards just as with ruby's built-in +File.open+ method.
|
232
|
+
#
|
233
|
+
# See the rubyzip documentation for details of the +permission_int+
|
234
|
+
# parameter.
|
235
|
+
def get_output_stream(entry, permission = nil, &block)
|
236
|
+
raise ReservedNameClashError.new(entry.to_s) if reserved_entry?(entry)
|
237
|
+
|
238
|
+
@zipfile.get_output_stream(entry, permission, &block)
|
239
|
+
end
|
240
|
+
|
241
|
+
# :call-seq:
|
242
|
+
# in_memory? -> boolean
|
243
|
+
#
|
244
|
+
# Is this UCF document memory resident as opposed to stored on disk?
|
245
|
+
def in_memory?
|
246
|
+
!@on_disk
|
247
|
+
end
|
248
|
+
|
249
|
+
# :call-seq:
|
250
|
+
# mkdir(name, permission = 0755)
|
251
|
+
#
|
252
|
+
# Creates a directory in the UCF document. If asked to create a directory
|
253
|
+
# with a reserved name this method will raise a ReservedNameClashError.
|
254
|
+
#
|
255
|
+
# The new directory will be created with the supplied unix-style
|
256
|
+
# permissions. The default (+0755+) is owner read, write and list; group
|
257
|
+
# read and list; and world read and list.
|
258
|
+
def mkdir(name, permission = 0755)
|
259
|
+
raise ReservedNameClashError.new(name) if reserved_entry?(name)
|
260
|
+
|
261
|
+
@zipfile.mkdir(name, permission)
|
262
|
+
end
|
263
|
+
|
264
|
+
# :call-seq:
|
265
|
+
# on_disk? -> boolean
|
266
|
+
#
|
267
|
+
# Is this UCF document stored on disk as opposed to memory resident?
|
268
|
+
def on_disk?
|
269
|
+
@on_disk
|
270
|
+
end
|
271
|
+
|
162
272
|
# :call-seq:
|
163
273
|
# remove(entry)
|
164
274
|
#
|
165
|
-
# Removes the specified entry. If asked to remove
|
166
|
-
# as the special mimetype header file this method
|
275
|
+
# Removes the specified entry from the UCF document. If asked to remove
|
276
|
+
# any reserved files such as the special mimetype header file this method
|
277
|
+
# will do nothing.
|
167
278
|
def remove(entry)
|
168
279
|
return if reserved_entry?(entry)
|
169
280
|
@zipfile.remove(entry)
|
170
281
|
end
|
171
282
|
|
172
283
|
# :call-seq:
|
173
|
-
# rename(entry, new_name, &
|
284
|
+
# rename(entry, new_name, &continue_on_exists_proc)
|
174
285
|
#
|
175
|
-
# Renames the specified entry. If asked to rename any
|
176
|
-
# as the special mimetype header file this method will
|
177
|
-
#
|
178
|
-
#
|
286
|
+
# Renames the specified entry in the UCF document. If asked to rename any
|
287
|
+
# reserved files such as the special mimetype header file this method will
|
288
|
+
# do nothing. If asked to rename a file _to_ one of the reserved names a
|
289
|
+
# ReservedNameClashError is raised.
|
290
|
+
#
|
291
|
+
# See the rubyzip documentation for details of the
|
292
|
+
# +continue_on_exists_proc+ parameter.
|
179
293
|
def rename(entry, new_name, &continue_on_exists_proc)
|
180
294
|
return if reserved_entry?(entry)
|
181
|
-
|
295
|
+
raise ReservedNameClashError.new(new_name) if reserved_entry?(new_name)
|
296
|
+
|
297
|
+
@zipfile.rename(entry, new_name, &continue_on_exists_proc)
|
182
298
|
end
|
183
299
|
|
184
300
|
# :call-seq:
|
185
301
|
# replace(entry, src_path)
|
186
302
|
#
|
187
|
-
# Replaces the specified entry with the contents of
|
188
|
-
# file system). If asked to replace any reserved
|
189
|
-
# mimetype header file this method will do
|
303
|
+
# Replaces the specified entry of the UCF document with the contents of
|
304
|
+
# +src_path+ (from the file system). If asked to replace any reserved
|
305
|
+
# files such as the special mimetype header file this method will do
|
306
|
+
# nothing.
|
190
307
|
def replace(entry, src_path)
|
191
308
|
return if reserved_entry?(entry)
|
192
309
|
@zipfile.replace(entry, src_path)
|
193
310
|
end
|
194
311
|
|
312
|
+
# :call-seq:
|
313
|
+
# reserved_files -> Array
|
314
|
+
#
|
315
|
+
# Return a list of reserved file names for this UCF document.
|
316
|
+
#
|
317
|
+
# When creating a more specialized sub-class of this class then this
|
318
|
+
# method should be overridden to add any extra reserved file names.
|
319
|
+
def reserved_files
|
320
|
+
[MIMETYPE_FILE]
|
321
|
+
end
|
322
|
+
|
323
|
+
# :call-seq:
|
324
|
+
# reserved_directories -> Array
|
325
|
+
#
|
326
|
+
# Return a list of reserved directory names for this UCF document.
|
327
|
+
#
|
328
|
+
# When creating a more specialized sub-class of this class then this
|
329
|
+
# method should be overridden to add any extra reserved directory names.
|
330
|
+
def reserved_directories
|
331
|
+
[META_INF_DIR]
|
332
|
+
end
|
333
|
+
|
334
|
+
# :call-seq:
|
335
|
+
# reserved_entry?(entry) -> boolean
|
336
|
+
#
|
337
|
+
# Is the given entry name in the reserved list of file or directory names?
|
338
|
+
def reserved_entry?(entry)
|
339
|
+
name = entry.kind_of?(::Zip::ZipEntry) ? entry.name : entry
|
340
|
+
name.chop! if name.end_with? "/"
|
341
|
+
reserved_names.map { |n| n.downcase }.include? name.downcase
|
342
|
+
end
|
343
|
+
|
344
|
+
# :call-seq:
|
345
|
+
# reserved_names -> Array
|
346
|
+
#
|
347
|
+
# Return a list of reserved file and directory names for this UCF
|
348
|
+
# document.
|
349
|
+
#
|
350
|
+
# In practice this method simply returns the joined lists of reserved file
|
351
|
+
# and directory names.
|
352
|
+
def reserved_names
|
353
|
+
reserved_files + reserved_directories
|
354
|
+
end
|
355
|
+
|
195
356
|
# :call-seq:
|
196
357
|
# to_s -> String
|
197
358
|
#
|
198
|
-
# Return a
|
359
|
+
# Return a textual summary of this UCF document.
|
199
360
|
def to_s
|
200
361
|
@zipfile.to_s + " - #{@mimetype}"
|
201
362
|
end
|
202
363
|
|
203
364
|
private
|
204
365
|
|
205
|
-
def
|
206
|
-
|
366
|
+
def open_document(document)
|
367
|
+
::Zip::ZipFile.new(document)
|
368
|
+
end
|
207
369
|
|
370
|
+
def check_document!
|
208
371
|
# Check mimetype file is present and correct.
|
209
|
-
entry =
|
210
|
-
|
211
|
-
raise MalformedUCFError.new(
|
372
|
+
entry = @zipfile.find_entry(MIMETYPE_FILE)
|
373
|
+
|
374
|
+
raise MalformedUCFError.new("'mimetype' file is missing.") if entry.nil?
|
375
|
+
if entry.localHeaderOffset != 0
|
376
|
+
raise MalformedUCFError.new("'mimetype' file is not at offset 0 in the archive.")
|
377
|
+
end
|
212
378
|
if entry.compression_method != ::Zip::ZipEntry::STORED
|
213
|
-
raise MalformedUCFError.new(
|
379
|
+
raise MalformedUCFError.new("'mimetype' file is compressed.")
|
214
380
|
end
|
215
381
|
|
216
|
-
|
382
|
+
true
|
217
383
|
end
|
218
384
|
|
219
385
|
def read_mimetype
|
220
386
|
@zipfile.read(MIMETYPE_FILE)
|
221
387
|
end
|
222
388
|
|
223
|
-
# Remember that file names in UCF documents are case-insensitive so
|
224
|
-
# compare downcased versions.
|
225
|
-
def reserved_entry?(entry)
|
226
|
-
name = entry.kind_of?(::Zip::ZipEntry) ? entry.name : entry
|
227
|
-
RESERVED_ROOT_NAMES.include? name.downcase
|
228
|
-
end
|
229
|
-
|
230
389
|
public
|
231
390
|
|
232
391
|
# Lots of extra docs out of the way at the end here...
|
233
392
|
|
234
|
-
##
|
235
|
-
# :method: add
|
236
|
-
# :call-seq:
|
237
|
-
# add(entry, src_path, &continue_on_exists_proc)
|
238
|
-
#
|
239
|
-
# Convenience method for adding the contents of a file to the UCF file.
|
240
|
-
#
|
241
|
-
# See the rubyzip documentation for details of the
|
242
|
-
# +continue_on_exists_proc+ parameter.
|
243
|
-
|
244
|
-
##
|
245
|
-
# :method: close
|
246
|
-
# :call-seq:
|
247
|
-
# close
|
248
|
-
#
|
249
|
-
# Closes the UCF file committing any changes that have been made.
|
250
|
-
|
251
393
|
##
|
252
394
|
# :method: comment
|
253
395
|
# :call-seq:
|
254
396
|
# comment -> String
|
255
397
|
#
|
256
|
-
# Returns the UCF
|
398
|
+
# Returns the UCF document comment, if it has one.
|
257
399
|
|
258
400
|
##
|
259
|
-
# :method:
|
401
|
+
# :method: comment=
|
260
402
|
# :call-seq:
|
261
|
-
#
|
403
|
+
# comment = comment
|
262
404
|
#
|
263
|
-
#
|
264
|
-
# UCF file.
|
405
|
+
# Set the UCF document comment to the new value.
|
265
406
|
|
266
407
|
##
|
267
|
-
# :method:
|
408
|
+
# :method: commit_required?
|
268
409
|
# :call-seq:
|
269
|
-
#
|
410
|
+
# commit_required? -> boolean
|
270
411
|
#
|
271
|
-
# Returns
|
272
|
-
#
|
273
|
-
# invoked.
|
274
|
-
#
|
275
|
-
# See the rubyzip documentation for details.
|
412
|
+
# Returns +true+ if any changes have been made to this UCF document since
|
413
|
+
# the last commit, +false+ otherwise.
|
276
414
|
|
277
415
|
##
|
278
416
|
# :method: each
|
@@ -289,37 +427,26 @@ module UCF
|
|
289
427
|
# :call-seq:
|
290
428
|
# extract(entry, dest_path, &on_exists_proc)
|
291
429
|
#
|
292
|
-
# Extracts the specified entry to +dest_path+.
|
430
|
+
# Extracts the specified entry of the UCF document to +dest_path+.
|
293
431
|
#
|
294
432
|
# See the rubyzip documentation for details of the +on_exists_proc+
|
295
433
|
# parameter.
|
296
434
|
|
297
|
-
##
|
298
|
-
# :method: file
|
299
|
-
# :call-seq:
|
300
|
-
# dir -> Zip::ZipFsFile
|
301
|
-
#
|
302
|
-
# Returns an object which can be used like ruby's built in +File+ (class)
|
303
|
-
# object, except that it works on the UCF file on which this method is
|
304
|
-
# invoked.
|
305
|
-
#
|
306
|
-
# See the rubyzip documentation for details.
|
307
|
-
|
308
435
|
##
|
309
436
|
# :method: find_entry
|
310
437
|
# :call-seq:
|
311
438
|
# find_entry(entry) -> Zip::ZipEntry
|
312
439
|
#
|
313
|
-
# Searches for entries with the specified name.
|
314
|
-
# is found. See also +get_entry+.
|
440
|
+
# Searches for entries within the UCF document with the specified name.
|
441
|
+
# Returns +nil+ if no entry is found. See also +get_entry+.
|
315
442
|
|
316
443
|
##
|
317
444
|
# :method: get_entry
|
318
445
|
# :call-seq:
|
319
446
|
# get_entry(entry) -> Zip::ZipEntry
|
320
447
|
#
|
321
|
-
# Searches for an entry
|
322
|
-
# no entry is found.
|
448
|
+
# Searches for an entry within the UCF document in a similar manner to
|
449
|
+
# +find_entry+, but throws +Errno::ENOENT+ if no entry is found.
|
323
450
|
|
324
451
|
##
|
325
452
|
# :method: get_input_stream
|
@@ -331,46 +458,23 @@ module UCF
|
|
331
458
|
# stream object is passed to the block and the stream is automatically
|
332
459
|
# closed afterwards just as with ruby's built in +File.open+ method.
|
333
460
|
|
334
|
-
##
|
335
|
-
# :method: get_output_stream
|
336
|
-
# :call-seq:
|
337
|
-
# get_output_stream(entry, permission_int = nil) -> stream
|
338
|
-
# get_output_stream(entry, permission_int = nil) {|stream| ...}
|
339
|
-
#
|
340
|
-
# Returns an output stream to the specified entry. If a block is passed
|
341
|
-
# the stream object is passed to the block and the stream is automatically
|
342
|
-
# closed afterwards just as with ruby's built-in +File.open+ method.
|
343
|
-
#
|
344
|
-
# See the rubyzip documentation for details of the +permission_int+
|
345
|
-
# parameter.
|
346
|
-
|
347
461
|
##
|
348
462
|
# :method: glob
|
349
463
|
# :call-seq:
|
350
464
|
# glob(*args) -> Array of Zip::ZipEntry
|
351
465
|
# glob(*args) {|entry| ...}
|
352
466
|
#
|
353
|
-
# Searches for entries given
|
467
|
+
# Searches for entries within the UCF document that match the given glob.
|
354
468
|
#
|
355
469
|
# See the rubyzip documentation for details of the parameters that can be
|
356
470
|
# passed in.
|
357
471
|
|
358
|
-
##
|
359
|
-
# :method: mkdir
|
360
|
-
# :call-seq:
|
361
|
-
# mkdir(entryName, permission_int = 0755)
|
362
|
-
#
|
363
|
-
# Creates a directory.
|
364
|
-
#
|
365
|
-
# See the rubyzip documentation for details of the +permission_int+
|
366
|
-
# parameter.
|
367
|
-
|
368
472
|
##
|
369
473
|
# :method: name
|
370
474
|
# :call-seq:
|
371
475
|
# name -> String
|
372
476
|
#
|
373
|
-
# Returns the filename of this UCF
|
477
|
+
# Returns the filename of this UCF document.
|
374
478
|
|
375
479
|
##
|
376
480
|
# :method: read
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Copyright (c) 2013 The University of Manchester, UK.
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
13
|
+
# and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# * Neither the names of The University of Manchester nor the names of its
|
16
|
+
# contributors may be used to endorse or promote products derived from this
|
17
|
+
# software without specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
#
|
31
|
+
# Author: Robert Haines
|
32
|
+
|
33
|
+
module UCF
|
34
|
+
|
35
|
+
# The base class of all other exceptions raised by this library.
|
36
|
+
class UCFError < RuntimeError
|
37
|
+
end
|
38
|
+
|
39
|
+
# This exception is raised when a bad UCF is detected.
|
40
|
+
class MalformedUCFError < UCFError
|
41
|
+
|
42
|
+
# :call-seq:
|
43
|
+
# new(reason = "")
|
44
|
+
#
|
45
|
+
# Create a new MalformedUCFError with an optional reason for why the UCF
|
46
|
+
# document is malformed.
|
47
|
+
def initialize(reason = nil)
|
48
|
+
if reason.nil?
|
49
|
+
super("Malformed UCF Document.")
|
50
|
+
else
|
51
|
+
super("Malformed UCF Document: #{reason}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# This exception is raised when a clash occurs with a reserved name.
|
57
|
+
class ReservedNameClashError < UCFError
|
58
|
+
|
59
|
+
# :call-seq:
|
60
|
+
# new(name)
|
61
|
+
#
|
62
|
+
# Create a new ReservedNameClashError with the name of the clash supplied.
|
63
|
+
def initialize(name)
|
64
|
+
super("'#{name}' is reserved for internal use in this UCF document.")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/lib/ucf.rb
CHANGED
@@ -31,13 +31,15 @@
|
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
33
|
require 'yaml'
|
34
|
+
require 'ucf/exceptions'
|
34
35
|
require 'ucf/container'
|
35
36
|
|
36
37
|
# This is a ruby library to read and write UCF files in PK Zip format. See the
|
37
38
|
# UCF::Container class for more information.
|
38
39
|
#
|
39
|
-
# See
|
40
|
-
#
|
40
|
+
# See
|
41
|
+
# {the UCF specification}[https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format]
|
42
|
+
# for more details.
|
41
43
|
module UCF
|
42
44
|
|
43
45
|
# Library version information.
|
@@ -50,12 +52,4 @@ module UCF
|
|
50
52
|
STRING = [:major, :minor, :patch].map {|d| INFO[d]}.compact.join('.')
|
51
53
|
end
|
52
54
|
|
53
|
-
# Exception raised when a bad UCF is detected.
|
54
|
-
class MalformedUCFError < RuntimeError
|
55
|
-
# :stopdoc:
|
56
|
-
def initialize(message = "")
|
57
|
-
super(message)
|
58
|
-
end
|
59
|
-
# :startdoc:
|
60
|
-
end
|
61
55
|
end
|
data/test/data/example.ucf
CHANGED
Binary file
|
data/test/tc_create.rb
CHANGED
@@ -42,6 +42,9 @@ class TestCreation < Test::Unit::TestCase
|
|
42
42
|
|
43
43
|
assert_nothing_raised do
|
44
44
|
UCF::Container.create(filename) do |c|
|
45
|
+
assert(c.on_disk?)
|
46
|
+
refute(c.in_memory?)
|
47
|
+
|
45
48
|
assert(c.find_entry("mimetype").localHeaderOffset == 0)
|
46
49
|
end
|
47
50
|
end
|
@@ -61,6 +64,9 @@ class TestCreation < Test::Unit::TestCase
|
|
61
64
|
|
62
65
|
assert_nothing_raised do
|
63
66
|
UCF::Container.create(filename) do |c|
|
67
|
+
assert(c.on_disk?)
|
68
|
+
refute(c.in_memory?)
|
69
|
+
|
64
70
|
assert(c.find_entry("mimetype").localHeaderOffset == 0)
|
65
71
|
end
|
66
72
|
end
|
@@ -71,31 +77,60 @@ class TestCreation < Test::Unit::TestCase
|
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
74
|
-
# Check creation of stuff in ucf files.
|
80
|
+
# Check creation of stuff in ucf files. Check the commit status a few times
|
81
|
+
# to ensure that what we expect to happen, happens.
|
75
82
|
def test_create_contents_file
|
76
83
|
Dir.mktmpdir do |dir|
|
77
84
|
filename = File.join(dir, "test.ucf")
|
78
85
|
|
79
86
|
assert_nothing_raised do
|
80
87
|
UCF::Container.create(filename) do |ucf|
|
88
|
+
assert(ucf.on_disk?)
|
89
|
+
refute(ucf.in_memory?)
|
90
|
+
|
81
91
|
ucf.file.open("test.txt", "w") do |f|
|
82
92
|
f.print "testing"
|
83
93
|
end
|
84
94
|
|
95
|
+
assert(ucf.commit_required?)
|
96
|
+
assert(ucf.commit)
|
97
|
+
refute(ucf.commit_required?)
|
98
|
+
refute(ucf.commit)
|
99
|
+
|
85
100
|
ucf.dir.mkdir("dir1")
|
86
101
|
ucf.mkdir("dir2")
|
102
|
+
|
103
|
+
assert(ucf.commit_required?)
|
104
|
+
assert(ucf.commit)
|
105
|
+
refute(ucf.commit_required?)
|
106
|
+
refute(ucf.commit)
|
107
|
+
|
108
|
+
ucf.comment = "A comment!"
|
109
|
+
|
110
|
+
assert(ucf.commit_required?)
|
111
|
+
assert(ucf.commit)
|
112
|
+
refute(ucf.commit_required?)
|
113
|
+
refute(ucf.commit)
|
87
114
|
end
|
88
115
|
end
|
89
116
|
|
90
117
|
assert_nothing_raised(UCF::MalformedUCFError, Zip::ZipError) do
|
91
118
|
UCF::Container.open(filename) do |ucf|
|
119
|
+
assert(ucf.on_disk?)
|
120
|
+
refute(ucf.in_memory?)
|
121
|
+
|
92
122
|
assert(ucf.file.exists?("test.txt"))
|
93
123
|
assert(ucf.file.exists?("dir1"))
|
94
124
|
assert(ucf.file.exists?("dir2"))
|
95
|
-
|
125
|
+
refute(ucf.file.exists?("dir3"))
|
96
126
|
|
97
127
|
text = ucf.file.read("test.txt")
|
98
128
|
assert_equal("testing", text)
|
129
|
+
|
130
|
+
assert_equal("A comment!", ucf.comment)
|
131
|
+
|
132
|
+
refute(ucf.commit_required?)
|
133
|
+
refute(ucf.commit)
|
99
134
|
end
|
100
135
|
end
|
101
136
|
end
|
data/test/tc_read.rb
CHANGED
@@ -40,7 +40,7 @@ class TestRead < Test::Unit::TestCase
|
|
40
40
|
UCF::Container.verify!($file_null)
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
refute(UCF::Container.verify($file_null))
|
44
44
|
end
|
45
45
|
|
46
46
|
# Check that the empty ucf file does verify.
|
@@ -58,7 +58,7 @@ class TestRead < Test::Unit::TestCase
|
|
58
58
|
UCF::Container.verify!($zip_empty)
|
59
59
|
end
|
60
60
|
|
61
|
-
|
61
|
+
refute(UCF::Container.verify($zip_empty))
|
62
62
|
end
|
63
63
|
|
64
64
|
# Check that a compressed mimetype file is detected.
|
@@ -67,7 +67,7 @@ class TestRead < Test::Unit::TestCase
|
|
67
67
|
UCF::Container.verify!($ucf_compressed_mimetype)
|
68
68
|
end
|
69
69
|
|
70
|
-
|
70
|
+
refute(UCF::Container.verify($ucf_compressed_mimetype))
|
71
71
|
end
|
72
72
|
|
73
73
|
# Check the raw mimetype bytes
|
@@ -79,10 +79,13 @@ class TestRead < Test::Unit::TestCase
|
|
79
79
|
assert_not_equal("application/epub+zip", compressed_mimetype[38..57])
|
80
80
|
end
|
81
81
|
|
82
|
-
# Check reading files out of a ucf file.
|
82
|
+
# Check reading files out of a ucf file and make sure we don't change it.
|
83
83
|
def test_read_files_from_ucf
|
84
84
|
assert_nothing_raised(UCF::MalformedUCFError, Zip::ZipError) do
|
85
85
|
UCF::Container.open($ucf_example) do |ucf|
|
86
|
+
assert(ucf.on_disk?)
|
87
|
+
refute(ucf.in_memory?)
|
88
|
+
|
86
89
|
assert(ucf.file.exists?("greeting.txt"))
|
87
90
|
|
88
91
|
greeting = ucf.file.read("greeting.txt")
|
@@ -92,6 +95,11 @@ class TestRead < Test::Unit::TestCase
|
|
92
95
|
assert(ucf.file.directory?("dir"))
|
93
96
|
|
94
97
|
assert(ucf.file.exists?("dir/code.rb"))
|
98
|
+
|
99
|
+
assert_equal("This is an example UCF file!", ucf.comment)
|
100
|
+
|
101
|
+
refute(ucf.commit_required?)
|
102
|
+
refute(ucf.commit)
|
95
103
|
end
|
96
104
|
end
|
97
105
|
end
|
data/test/tc_reserved_names.rb
CHANGED
@@ -32,8 +32,78 @@
|
|
32
32
|
|
33
33
|
require 'ucf'
|
34
34
|
|
35
|
+
# A class to test the overriding of reserved names.
|
36
|
+
class NewUCF < UCF::Container
|
37
|
+
def reserved_files
|
38
|
+
super + ["index.html"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def reserved_directories
|
42
|
+
super + ["src", "test", "lib"]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
35
46
|
class TestReservedNames < Test::Unit::TestCase
|
36
47
|
|
48
|
+
# Check the reserved names stuff all works correctly, baring in mind that
|
49
|
+
# such comparisons for UCF documents should be case sensitive.
|
50
|
+
def test_reserved_names
|
51
|
+
UCF::Container.open($ucf_example) do |ucf|
|
52
|
+
assert_equal(1, ucf.reserved_files.length)
|
53
|
+
assert_equal(["mimetype"], ucf.reserved_files)
|
54
|
+
assert(ucf.reserved_entry?("mimetype"))
|
55
|
+
assert(ucf.reserved_entry?("mimetype/"))
|
56
|
+
assert(ucf.reserved_entry?("MimeType"))
|
57
|
+
|
58
|
+
assert_equal(1, ucf.reserved_directories.length)
|
59
|
+
assert_equal(["META-INF"], ucf.reserved_directories)
|
60
|
+
assert(ucf.reserved_entry?("META-INF"))
|
61
|
+
assert(ucf.reserved_entry?("META-INF/"))
|
62
|
+
assert(ucf.reserved_entry?("MeTa-iNf"))
|
63
|
+
assert(ucf.reserved_entry?("MeTa-iNf/"))
|
64
|
+
|
65
|
+
assert_equal(2, ucf.reserved_names.length)
|
66
|
+
assert_equal(["mimetype", "META-INF"], ucf.reserved_names)
|
67
|
+
|
68
|
+
refute(ucf.reserved_entry?("This_should_fail"))
|
69
|
+
refute(ucf.reserved_entry?("META_INF"))
|
70
|
+
refute(ucf.reserved_entry?("META_INF/"))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check that overriding the reserved names in a sub-class works correctly
|
75
|
+
def test_subclass_reserved_names
|
76
|
+
NewUCF.open($ucf_example) do |ucf|
|
77
|
+
assert_equal(2, ucf.reserved_files.length)
|
78
|
+
assert_equal(["mimetype", "index.html"], ucf.reserved_files)
|
79
|
+
assert(ucf.reserved_entry?("mimetype"))
|
80
|
+
assert(ucf.reserved_entry?("mimetype/"))
|
81
|
+
assert(ucf.reserved_entry?("MimeType"))
|
82
|
+
assert(ucf.reserved_entry?("index.html"))
|
83
|
+
assert(ucf.reserved_entry?("Index.HTML"))
|
84
|
+
|
85
|
+
assert_equal(4, ucf.reserved_directories.length)
|
86
|
+
assert_equal(["META-INF", "src", "test", "lib"], ucf.reserved_directories)
|
87
|
+
assert(ucf.reserved_entry?("META-INF"))
|
88
|
+
assert(ucf.reserved_entry?("META-INF/"))
|
89
|
+
assert(ucf.reserved_entry?("MeTa-iNf"))
|
90
|
+
assert(ucf.reserved_entry?("src"))
|
91
|
+
assert(ucf.reserved_entry?("SRC"))
|
92
|
+
assert(ucf.reserved_entry?("test"))
|
93
|
+
assert(ucf.reserved_entry?("lib"))
|
94
|
+
assert(ucf.reserved_entry?("lIb/"))
|
95
|
+
|
96
|
+
assert_equal(6, ucf.reserved_names.length)
|
97
|
+
assert_equal(["mimetype", "index.html", "META-INF", "src", "test", "lib"],
|
98
|
+
ucf.reserved_names)
|
99
|
+
|
100
|
+
refute(ucf.reserved_entry?("This_should_fail"))
|
101
|
+
refute(ucf.reserved_entry?("META_INF"))
|
102
|
+
refute(ucf.reserved_entry?("META_INF/"))
|
103
|
+
refute(ucf.reserved_entry?("index.htm"))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
37
107
|
# Check that nothing happens when trying to delete the mimetype file.
|
38
108
|
def test_delete_mimetype
|
39
109
|
UCF::Container.open($ucf_example) do |ucf|
|
@@ -49,7 +119,7 @@ class TestReservedNames < Test::Unit::TestCase
|
|
49
119
|
assert(ucf.file.exists?("mimetype"))
|
50
120
|
assert_nil(ucf.rename("mimetype", "something-else"))
|
51
121
|
assert(ucf.file.exists?("mimetype"))
|
52
|
-
|
122
|
+
refute(ucf.file.exists?("something-else"))
|
53
123
|
end
|
54
124
|
end
|
55
125
|
|
@@ -63,6 +133,34 @@ class TestReservedNames < Test::Unit::TestCase
|
|
63
133
|
end
|
64
134
|
end
|
65
135
|
|
136
|
+
# Check that an exception is raised when trying to add file with a reserved
|
137
|
+
# name.
|
138
|
+
def test_add_reserved
|
139
|
+
UCF::Container.open($ucf_empty) do |ucf|
|
140
|
+
assert_raises(UCF::ReservedNameClashError) do
|
141
|
+
ucf.add("META-INF", $zip_empty)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Check that an exception is raised when trying to add file with a reserved
|
147
|
+
# name to a subclassed container.
|
148
|
+
def test_subclass_add_reserved
|
149
|
+
NewUCF.open($ucf_empty) do |ucf|
|
150
|
+
assert_raises(UCF::ReservedNameClashError) do
|
151
|
+
ucf.add("META-INF", $zip_empty)
|
152
|
+
end
|
153
|
+
|
154
|
+
assert_raises(UCF::ReservedNameClashError) do
|
155
|
+
ucf.add("index.html", $zip_empty)
|
156
|
+
end
|
157
|
+
|
158
|
+
assert_raises(UCF::ReservedNameClashError) do
|
159
|
+
ucf.add("SRC", $zip_empty)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
66
164
|
# Check that nothing happens when trying to delete the META-INF directory.
|
67
165
|
def test_delete_metainf
|
68
166
|
UCF::Container.open($ucf_example) do |ucf|
|
@@ -77,4 +175,134 @@ class TestReservedNames < Test::Unit::TestCase
|
|
77
175
|
end
|
78
176
|
end
|
79
177
|
|
178
|
+
# Check that an exception is raised when trying to create a directory with a
|
179
|
+
# reserved name.
|
180
|
+
def test_mkdir_reserved
|
181
|
+
UCF::Container.open($ucf_empty) do |ucf|
|
182
|
+
assert_raises(UCF::ReservedNameClashError) do
|
183
|
+
ucf.mkdir("META-INF")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Check that an exception is raised when trying to create a directory with a
|
189
|
+
# reserved name in a subclassed container.
|
190
|
+
def test_subclass_mkdir_reserved
|
191
|
+
NewUCF.open($ucf_empty) do |ucf|
|
192
|
+
assert_raises(UCF::ReservedNameClashError) do
|
193
|
+
ucf.mkdir("META-INF")
|
194
|
+
end
|
195
|
+
|
196
|
+
assert_raises(UCF::ReservedNameClashError) do
|
197
|
+
ucf.mkdir("index.html")
|
198
|
+
end
|
199
|
+
|
200
|
+
assert_raises(UCF::ReservedNameClashError) do
|
201
|
+
ucf.mkdir("Lib")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Check that a file cannot be renamed to one of the reserved names.
|
207
|
+
def test_rename_to_reserved
|
208
|
+
UCF::Container.open($ucf_example) do |ucf|
|
209
|
+
assert_raises(UCF::ReservedNameClashError) do
|
210
|
+
ucf.rename("dir/code.rb", "mimetype")
|
211
|
+
end
|
212
|
+
|
213
|
+
assert_raises(UCF::ReservedNameClashError) do
|
214
|
+
ucf.rename("dir", "META-INF")
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Check that a file cannot be renamed to one of the reserved names in a
|
220
|
+
# subclassed container.
|
221
|
+
def test_subclass_rename_to_reserved
|
222
|
+
NewUCF.open($ucf_example) do |ucf|
|
223
|
+
assert_raises(UCF::ReservedNameClashError) do
|
224
|
+
ucf.rename("dir/code.rb", "mimetype")
|
225
|
+
end
|
226
|
+
|
227
|
+
assert_raises(UCF::ReservedNameClashError) do
|
228
|
+
ucf.rename("dir", "META-INF")
|
229
|
+
end
|
230
|
+
|
231
|
+
assert_raises(UCF::ReservedNameClashError) do
|
232
|
+
ucf.rename("dir/code.rb", "index.html")
|
233
|
+
end
|
234
|
+
|
235
|
+
assert_raises(UCF::ReservedNameClashError) do
|
236
|
+
ucf.rename("dir", "Test")
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Check that the ruby-like File and Dir classes respect reserved names.
|
242
|
+
def test_file_dir_ops_reserved
|
243
|
+
UCF::Container.open($ucf_empty) do |ucf|
|
244
|
+
assert_raises(UCF::ReservedNameClashError) do
|
245
|
+
ucf.file.open("META-INF", "w") do |f|
|
246
|
+
f.puts "TESTING"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
assert_nothing_raised(UCF::ReservedNameClashError) do
|
251
|
+
ucf.file.open("mimetype") do |f|
|
252
|
+
assert_equal("application/epub+zip", f.read)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
assert_nothing_raised(UCF::ReservedNameClashError) do
|
257
|
+
ucf.file.delete("mimetype")
|
258
|
+
assert(ucf.file.exists?("mimetype"))
|
259
|
+
end
|
260
|
+
|
261
|
+
assert_raises(UCF::ReservedNameClashError) do
|
262
|
+
ucf.dir.mkdir("meta-inf")
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Check that the ruby-like File and Dir classes respect reserved names in a
|
268
|
+
# subclassed container.
|
269
|
+
def test_subclass_file_dir_ops_reserved
|
270
|
+
NewUCF.open($ucf_empty) do |ucf|
|
271
|
+
assert_raises(UCF::ReservedNameClashError) do
|
272
|
+
ucf.file.open("META-INF", "w") do |f|
|
273
|
+
f.puts "TESTING"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
assert_raises(UCF::ReservedNameClashError) do
|
278
|
+
ucf.file.open("INDEX.HTML", "w") do |f|
|
279
|
+
f.puts "TESTING"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
assert_nothing_raised(UCF::ReservedNameClashError) do
|
284
|
+
ucf.file.open("mimetype") do |f|
|
285
|
+
assert_equal("application/epub+zip", f.read)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
assert_nothing_raised(UCF::ReservedNameClashError) do
|
290
|
+
ucf.file.delete("mimetype")
|
291
|
+
assert(ucf.file.exists?("mimetype"))
|
292
|
+
end
|
293
|
+
|
294
|
+
assert_raises(UCF::ReservedNameClashError) do
|
295
|
+
ucf.dir.mkdir("meta-inf")
|
296
|
+
end
|
297
|
+
|
298
|
+
assert_raises(UCF::ReservedNameClashError) do
|
299
|
+
ucf.dir.mkdir("TEST")
|
300
|
+
end
|
301
|
+
|
302
|
+
assert_raises(UCF::ReservedNameClashError) do
|
303
|
+
ucf.dir.mkdir("index.html")
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
80
308
|
end
|
data/ucf.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "ucf"
|
8
|
-
s.version = "0.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Robert Haines"]
|
12
|
-
s.date = "2013-
|
12
|
+
s.date = "2013-06-03"
|
13
13
|
s.description = "A Ruby library for working with Universal Container Format files. See https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format for the specification."
|
14
14
|
s.email = ["support@mygrid.org.uk"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
"examples/verify_ucf.rb",
|
28
28
|
"lib/ucf.rb",
|
29
29
|
"lib/ucf/container.rb",
|
30
|
+
"lib/ucf/exceptions.rb",
|
30
31
|
"test/data/compressed_mimetype.ucf",
|
31
32
|
"test/data/empty.ucf",
|
32
33
|
"test/data/empty.zip",
|
data/version.yml
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ucf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- examples/verify_ucf.rb
|
96
96
|
- lib/ucf.rb
|
97
97
|
- lib/ucf/container.rb
|
98
|
+
- lib/ucf/exceptions.rb
|
98
99
|
- test/data/compressed_mimetype.ucf
|
99
100
|
- test/data/empty.ucf
|
100
101
|
- test/data/empty.zip
|