ucf 0.0.2 → 0.1.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/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
|