ucf 0.5.0 → 0.6.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 CHANGED
@@ -1,5 +1,10 @@
1
1
  = Changes log for the UCF Ruby Gem
2
2
 
3
+ == Version 0.6.0
4
+
5
+ * Move to use version 0.9.0 of the zip-container library.
6
+ * Update and polish the example scripts.
7
+
3
8
  == Version 0.5.0
4
9
 
5
10
  * Add support for managed entries in the container.
data/Rakefile CHANGED
@@ -52,9 +52,10 @@ Jeweler::Tasks.new do |s|
52
52
  s.platform = Gem::Platform::RUBY
53
53
  s.summary = "Universal Container Format (UCF) Ruby Library"
54
54
  s.description = "A Ruby library for working with Universal Container "\
55
- "Format files. See "\
56
- "https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format "\
57
- "for the specification."
55
+ "Format files - a type of EPUB document. See the "\
56
+ "{UCF specification}[https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format] "\
57
+ "for details. They are very similar, although not as restrictive, as the "\
58
+ "{EPUB Open Container Format (OCF)}[http://www.idpf.org/epub/30/spec/epub30-ocf.html]."
58
59
  s.require_path = "lib"
59
60
  s.test_file = "test/ts_ucf.rb"
60
61
  s.has_rdoc = true
@@ -63,7 +64,7 @@ Jeweler::Tasks.new do |s|
63
64
  s.add_development_dependency('rake', '~> 10.0.4')
64
65
  s.add_development_dependency('rdoc', '~> 4.0.1')
65
66
  s.add_development_dependency('jeweler', '~> 1.8.4')
66
- s.add_runtime_dependency('rubyzip', '~> 0.9.9')
67
+ s.add_runtime_dependency('zip-container', '~> 0.9.0')
67
68
  end
68
69
 
69
70
  Rake::TestTask.new do |t|
data/ReadMe.rdoc CHANGED
@@ -13,7 +13,8 @@ Copyright:: (c) 2013 The University of Manchester, UK
13
13
 
14
14
  This is a Ruby library for working with UCF documents. See
15
15
  {the specification}[https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format]
16
- for more details.
16
+ for more details. UCF is a type of EPUB and very similar to the
17
+ {EPUB Open Container Format (OCF)}[http://www.idpf.org/epub/30/spec/epub30-ocf.html].
17
18
 
18
19
  <b>This library is a work in progress!</b> Until we release version 1.0.0 you
19
20
  can expect the API to change in incompatible ways, although we will try to
@@ -21,6 +22,12 @@ keep this to an absolute minimum. Once version 1.0.0 is released we will be
21
22
  following the principles of {Semantic Versioning}[http://semver.org/] for our
22
23
  version numbering scheme.
23
24
 
25
+ Most of this library's API is provided by the underlying
26
+ {zip-container gem}[https://rubygems.org/gems/zip-container] so you will need
27
+ to consult
28
+ {that documentation as well}[http://mygrid.github.io/ruby-zip-container/] in
29
+ addition to this.
30
+
24
31
  There are some examples of how to use the library provided in the examples
25
32
  directory. See the contents of the tests directory for even more.
26
33
 
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env ruby
1
2
  # Copyright (c) 2013 The University of Manchester, UK.
2
3
  #
3
4
  # All rights reserved.
@@ -33,13 +34,22 @@
33
34
  require 'rubygems'
34
35
  require 'ucf'
35
36
 
36
- ZIP_FILE = "example.zip"
37
- file = ARGV.length > 0 ? ARGV[0] : ZIP_FILE
37
+ def usage
38
+ puts "Usage:\n create-ucf <ucf-file>"
39
+ exit 1
40
+ end
41
+
42
+ usage unless ARGV.length == 1
38
43
 
39
- File.delete(file) if File.exists?(file)
44
+ ucffile = ARGV[0]
45
+
46
+ if File.exists?(ucffile)
47
+ puts "File '#{ucffile}' already exists. Exiting."
48
+ exit 1
49
+ end
40
50
 
41
51
  begin
42
- UCF::Container.create(file) do |c|
52
+ UCF::Container.create(ucffile) do |c|
43
53
 
44
54
  # Add a cheery greeting file from a string.
45
55
  c.file.open("greeting.txt", "w") do |f|
@@ -55,7 +65,7 @@ begin
55
65
  # Add a explanation of this file.
56
66
  c.comment = "This is an example UCF file!"
57
67
  end
58
- rescue UCF::MalformedUCFError, Zip::ZipError => err
68
+ rescue ZipContainer::MalformedContainerError, Zip::ZipError => err
59
69
  puts err.to_s
60
70
  exit 1
61
71
  end
data/examples/ucfinfo CHANGED
@@ -35,7 +35,7 @@ require 'rubygems'
35
35
  require 'ucf'
36
36
 
37
37
  def usage
38
- puts "ucfinfo ucf-file"
38
+ puts "Usage:\n ucfinfo <ucf-file>"
39
39
  exit 1
40
40
  end
41
41
 
@@ -45,7 +45,7 @@ ucffile = ARGV[0]
45
45
 
46
46
  begin
47
47
  ucf = UCF::Container.open(ucffile)
48
- rescue UCF::MalformedUCFError, Zip::ZipError => err
48
+ rescue ZipContainer::MalformedContainerError, Zip::ZipError => err
49
49
  puts err.to_s
50
50
  exit 1
51
51
  end
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env ruby
1
2
  # Copyright (c) 2013 The University of Manchester, UK.
2
3
  #
3
4
  # All rights reserved.
@@ -33,12 +34,18 @@
33
34
  require 'rubygems'
34
35
  require 'ucf'
35
36
 
36
- ZIP_FILE = "example.zip"
37
- file = ARGV.length > 0 ? ARGV[0] : ZIP_FILE
37
+ def usage
38
+ puts "Usage:\n verify-ucf <ucf-file>"
39
+ exit 1
40
+ end
41
+
42
+ usage unless ARGV.length == 1
43
+
44
+ ucffile = ARGV[0]
38
45
 
39
46
  begin
40
- UCF::Container.verify!(file)
41
- rescue UCF::MalformedUCFError, Zip::ZipError => err
47
+ UCF::Container.verify!(ucffile)
48
+ rescue ZipContainer::MalformedContainerError, Zip::ZipError => err
42
49
  puts err.to_s
43
50
  exit 1
44
51
  end
data/lib/ucf.rb CHANGED
@@ -31,12 +31,6 @@
31
31
  # Author: Robert Haines
32
32
 
33
33
  require 'yaml'
34
- require 'ucf/exceptions'
35
- require 'ucf/entries/reserved'
36
- require 'ucf/entries/managed'
37
- require 'ucf/entries/entry'
38
- require 'ucf/entries/file'
39
- require 'ucf/entries/directory'
40
34
  require 'ucf/meta-inf'
41
35
  require 'ucf/container'
42
36
 
data/lib/ucf/container.rb CHANGED
@@ -30,462 +30,51 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 'forwardable'
34
- require 'zip/zipfilesystem'
33
+ require 'zip-container'
35
34
 
36
35
  module UCF
37
36
 
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]
37
+ # This class represents a UCF document - also known as an EPUB and very
38
+ # similar to the
39
+ # {EPUB Open Container Format (OCF)}[http://www.idpf.org/epub/30/spec/epub30-ocf.html].
40
+ # See the
41
+ # {UCF specification}[https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format]
40
42
  # for more details.
41
43
  #
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.
44
+ # This class is a specialization of ZipContainer so you should see the
45
+ # {ZipContainer documentation}[http://mygrid.github.io/ruby-zip-container/]
46
+ # for much more information and a list of all the other methods available in
47
+ # this class. RDoc does not list inherited methods, unfortunately.
46
48
  #
47
49
  # There are code examples available with the source code of this library.
48
- class Container
49
- include ReservedNames
50
- include ManagedEntries
51
-
52
- extend Forwardable
53
- def_delegators :@zipfile, :comment, :comment=, :commit_required?, :each,
54
- :entries, :extract, :find_entry, :get_entry, :get_input_stream, :glob,
55
- :name, :read, :size
50
+ class Container < ZipContainer::Container
56
51
 
57
52
  private_class_method :new
58
53
 
59
- # The mime-type of this UCF document. By default this is
60
- # "application/epub+zip".
61
- attr_reader :mimetype
62
-
63
54
  # :stopdoc:
64
55
  DEFAULT_MIMETYPE = "application/epub+zip"
65
56
 
66
- # The reserved mimetype file name for standard UCF documents.
67
- MIMETYPE_FILE = "mimetype"
68
-
69
- def initialize(document)
70
- @zipfile = open_document(document)
71
- check_mimetype!
72
-
73
- @mimetype = read_mimetype
74
- @on_disk = true
75
-
76
- # Reserved entry names. Just the mimetype file by default.
77
- register_reserved_name(MIMETYPE_FILE)
57
+ def initialize(filename)
58
+ super(filename)
78
59
 
79
60
  # Initialize the managed entries and register the META-INF directory.
80
61
  initialize_managed_entries(MetaInf.new)
81
-
82
- # Here we fake up the connection to the rubyzip filesystem classes so
83
- # that they also respect the reserved names that we define.
84
- mapped_zip = ::Zip::ZipFileSystem::ZipFileNameMapper.new(self)
85
- @fs_dir = ::Zip::ZipFileSystem::ZipFsDir.new(mapped_zip)
86
- @fs_file = ::Zip::ZipFileSystem::ZipFsFile.new(mapped_zip)
87
- @fs_dir.file = @fs_file
88
- @fs_file.dir = @fs_dir
89
62
  end
90
63
  # :startdoc:
91
64
 
92
65
  # :call-seq:
93
- # Container.create(filename, mimetype = "application/epub+zip") -> document
94
- # Container.create(filename, mimetype = "application/epub+zip") {|document| ...}
66
+ # Container.create(filename) -> Container
67
+ # Container.create(filename) {|container| ...}
95
68
  #
96
- # Create a new UCF document on disk with the specified mimetype.
97
- def Container.create(filename, mimetype = DEFAULT_MIMETYPE, &block)
98
- ::Zip::ZipOutputStream.open(filename) do |stream|
99
- stream.put_next_entry(MIMETYPE_FILE, nil, nil, ::Zip::ZipEntry::STORED)
100
- stream.write mimetype
101
- end
102
-
103
- # Now open the newly created container.
104
- c = new(filename)
105
-
106
- if block_given?
107
- begin
108
- yield c
109
- ensure
110
- c.close
111
- end
112
- end
113
-
114
- c
115
- end
116
-
117
- # :call-seq:
118
- # Container.each_entry -> Enumerator
119
- # Container.each_entry {|entry| ...}
69
+ # Create a new UCF document on disk and open it for editing.
120
70
  #
121
- # Iterate over the entries in the UCF document. The entry objects returned
122
- # by this method are Zip::ZipEntry objects. Please see the rubyzip
123
- # documentation for details.
124
- def Container.each_entry(filename, &block)
125
- c = new(filename)
126
-
127
- if block_given?
128
- begin
129
- c.each(&block)
130
- ensure
131
- c.close
132
- end
133
- end
134
-
135
- c.each
71
+ # Please see the
72
+ # {ZipContainer documentation}[http://mygrid.github.io/ruby-zip-container/]
73
+ # for much more information and a list of all the other methods available
74
+ # in this class. RDoc does not list inherited methods, unfortunately.
75
+ def Container.create(filename, &block)
76
+ super(filename, DEFAULT_MIMETYPE, &block)
136
77
  end
137
78
 
138
- # :call-seq:
139
- # Container.open(filename) -> document
140
- # Container.open(filename) {|document| ...}
141
- #
142
- # Open an existing UCF document from disk. It will be checked for
143
- # conformance to the UCF specification upon first access.
144
- def Container.open(filename, &block)
145
- c = new(filename)
146
-
147
- if block_given?
148
- begin
149
- yield c
150
- ensure
151
- c.close
152
- end
153
- end
154
-
155
- c
156
- end
157
-
158
- # :call-seq:
159
- # Container.verify(filename) -> boolean
160
- #
161
- # Verify that the specified UCF document conforms to the UCF
162
- # specification. This method returns +false+ if there are any problems at
163
- # all with the file (including if it cannot be found).
164
- def Container.verify(filename)
165
- begin
166
- new(filename).verify!
167
- rescue
168
- return false
169
- end
170
-
171
- true
172
- end
173
-
174
- # :call-seq:
175
- # Container.verify!(filename)
176
- #
177
- # Verify that the specified UCF document conforms to the UCF
178
- # specification. This method raises exceptions when errors are found or if
179
- # there is something fundamental wrong with the file itself (e.g. it
180
- # cannot be found).
181
- def Container.verify!(filename)
182
- new(filename).verify!
183
- end
184
-
185
- # :call-seq:
186
- # add(entry, src_path, &continue_on_exists_proc)
187
- #
188
- # Convenience method for adding the contents of a file to the UCF
189
- # document. If asked to add a file with a reserved name, such as the
190
- # special mimetype header file, this method will raise a
191
- # ReservedNameClashError.
192
- #
193
- # See the rubyzip documentation for details of the
194
- # +continue_on_exists_proc+ parameter.
195
- def add(entry, src_path, &continue_on_exists_proc)
196
- if reserved_entry?(entry) || managed_directory?(entry)
197
- raise ReservedNameClashError.new(entry.to_s)
198
- end
199
-
200
- @zipfile.add(entry, src_path, &continue_on_exists_proc)
201
- end
202
-
203
- # :call-seq:
204
- # commit -> boolean
205
- # close -> boolean
206
- #
207
- # Commits changes that have been made since the previous commit to the
208
- # UCF document. Returns +true+ if anything was actually done, +false+
209
- # otherwise.
210
- def commit
211
- return false unless commit_required?
212
-
213
- if on_disk?
214
- @zipfile.commit
215
- end
216
- end
217
-
218
- alias :close :commit
219
-
220
- # :call-seq:
221
- # dir -> Zip::ZipFsDir
222
- #
223
- # Returns an object which can be used like ruby's built in +Dir+ (class)
224
- # object, except that it works on the UCF document on which this method is
225
- # invoked.
226
- #
227
- # See the rubyzip documentation for details.
228
- def dir
229
- @fs_dir
230
- end
231
-
232
- # :call-seq:
233
- # file -> Zip::ZipFsFile
234
- #
235
- # Returns an object which can be used like ruby's built in +File+ (class)
236
- # object, except that it works on the UCF document on which this method is
237
- # invoked.
238
- #
239
- # See the rubyzip documentation for details.
240
- def file
241
- @fs_file
242
- end
243
-
244
- # :call-seq:
245
- # get_output_stream(entry, permission = nil) -> stream
246
- # get_output_stream(entry, permission = nil) {|stream| ...}
247
- #
248
- # Returns an output stream to the specified entry. If a block is passed
249
- # the stream object is passed to the block and the stream is automatically
250
- # closed afterwards just as with ruby's built-in +File.open+ method.
251
- #
252
- # See the rubyzip documentation for details of the +permission_int+
253
- # parameter.
254
- def get_output_stream(entry, permission = nil, &block)
255
- if reserved_entry?(entry) || managed_directory?(entry)
256
- raise ReservedNameClashError.new(entry.to_s)
257
- end
258
-
259
- @zipfile.get_output_stream(entry, permission, &block)
260
- end
261
-
262
- # :call-seq:
263
- # in_memory? -> boolean
264
- #
265
- # Is this UCF document memory resident as opposed to stored on disk?
266
- def in_memory?
267
- !@on_disk
268
- end
269
-
270
- # :call-seq:
271
- # mkdir(name, permission = 0755)
272
- #
273
- # Creates a directory in the UCF document. If asked to create a directory
274
- # with a reserved name this method will raise a ReservedNameClashError.
275
- #
276
- # The new directory will be created with the supplied unix-style
277
- # permissions. The default (+0755+) is owner read, write and list; group
278
- # read and list; and world read and list.
279
- def mkdir(name, permission = 0755)
280
- if reserved_entry?(name) || managed_file?(name)
281
- raise ReservedNameClashError.new(name)
282
- end
283
-
284
- @zipfile.mkdir(name, permission)
285
- end
286
-
287
- # :call-seq:
288
- # on_disk? -> boolean
289
- #
290
- # Is this UCF document stored on disk as opposed to memory resident?
291
- def on_disk?
292
- @on_disk
293
- end
294
-
295
- # :call-seq:
296
- # remove(entry)
297
- #
298
- # Removes the specified entry from the UCF document. If asked to remove
299
- # any reserved files such as the special mimetype header file this method
300
- # will do nothing.
301
- def remove(entry)
302
- return if reserved_entry?(entry)
303
- @zipfile.remove(entry)
304
- end
305
-
306
- # :call-seq:
307
- # rename(entry, new_name, &continue_on_exists_proc)
308
- #
309
- # Renames the specified entry in the UCF document. If asked to rename any
310
- # reserved files such as the special mimetype header file this method will
311
- # do nothing. If asked to rename a file _to_ one of the reserved names a
312
- # ReservedNameClashError is raised.
313
- #
314
- # See the rubyzip documentation for details of the
315
- # +continue_on_exists_proc+ parameter.
316
- def rename(entry, new_name, &continue_on_exists_proc)
317
- return if reserved_entry?(entry)
318
- raise ReservedNameClashError.new(new_name) if reserved_entry?(new_name)
319
-
320
- @zipfile.rename(entry, new_name, &continue_on_exists_proc)
321
- end
322
-
323
- # :call-seq:
324
- # replace(entry, src_path)
325
- #
326
- # Replaces the specified entry of the UCF document with the contents of
327
- # +src_path+ (from the file system). If asked to replace any reserved
328
- # files such as the special mimetype header file this method will do
329
- # nothing.
330
- def replace(entry, src_path)
331
- return if reserved_entry?(entry)
332
- @zipfile.replace(entry, src_path)
333
- end
334
-
335
- # :call-seq:
336
- # to_s -> String
337
- #
338
- # Return a textual summary of this UCF document.
339
- def to_s
340
- @zipfile.to_s + " - #{@mimetype}"
341
- end
342
-
343
- # :call-seq:
344
- # verify!
345
- #
346
- # Verify the contents of this UCF document. All managed files and
347
- # directories are checked to make sure that they exist, if required.
348
- def verify!
349
- verify_managed_entries!
350
- end
351
-
352
- private
353
-
354
- def open_document(document)
355
- ::Zip::ZipFile.new(document)
356
- end
357
-
358
- def check_mimetype!
359
- # Check mimetype file is present and correct.
360
- entry = @zipfile.find_entry(MIMETYPE_FILE)
361
-
362
- raise MalformedUCFError.new("'mimetype' file is missing.") if entry.nil?
363
- if entry.localHeaderOffset != 0
364
- raise MalformedUCFError.new("'mimetype' file is not at offset 0 in the archive.")
365
- end
366
- if entry.compression_method != ::Zip::ZipEntry::STORED
367
- raise MalformedUCFError.new("'mimetype' file is compressed.")
368
- end
369
-
370
- true
371
- end
372
-
373
- def read_mimetype
374
- @zipfile.read(MIMETYPE_FILE)
375
- end
376
-
377
- public
378
-
379
- # Lots of extra docs out of the way at the end here...
380
-
381
- ##
382
- # :method: comment
383
- # :call-seq:
384
- # comment -> String
385
- #
386
- # Returns the UCF document comment, if it has one.
387
-
388
- ##
389
- # :method: comment=
390
- # :call-seq:
391
- # comment = comment
392
- #
393
- # Set the UCF document comment to the new value.
394
-
395
- ##
396
- # :method: commit_required?
397
- # :call-seq:
398
- # commit_required? -> boolean
399
- #
400
- # Returns +true+ if any changes have been made to this UCF document since
401
- # the last commit, +false+ otherwise.
402
-
403
- ##
404
- # :method: each
405
- # :call-seq:
406
- # each -> Enumerator
407
- # each {|entry| ...}
408
- #
409
- # Iterate over the entries in the UCF document. The entry objects returned
410
- # by this method are Zip::ZipEntry objects. Please see the rubyzip
411
- # documentation for details.
412
-
413
- ##
414
- # :method:
415
- # :call-seq:
416
- # entries -> Enumerable
417
- #
418
- # Returns an Enumerable containing all the entries in the UCF Document.
419
- # The entry objects returned by this method are Zip::ZipEntry objects.
420
- # Please see the rubyzip documentation for details.
421
-
422
- ##
423
- # :method: extract
424
- # :call-seq:
425
- # extract(entry, dest_path, &on_exists_proc)
426
- #
427
- # Extracts the specified entry of the UCF document to +dest_path+.
428
- #
429
- # See the rubyzip documentation for details of the +on_exists_proc+
430
- # parameter.
431
-
432
- ##
433
- # :method: find_entry
434
- # :call-seq:
435
- # find_entry(entry) -> Zip::ZipEntry
436
- #
437
- # Searches for entries within the UCF document with the specified name.
438
- # Returns +nil+ if no entry is found. See also +get_entry+.
439
-
440
- ##
441
- # :method: get_entry
442
- # :call-seq:
443
- # get_entry(entry) -> Zip::ZipEntry
444
- #
445
- # Searches for an entry within the UCF document in a similar manner to
446
- # +find_entry+, but throws +Errno::ENOENT+ if no entry is found.
447
-
448
- ##
449
- # :method: get_input_stream
450
- # :call-seq:
451
- # get_input_stream(entry) -> stream
452
- # get_input_stream(entry) {|stream| ...}
453
- #
454
- # Returns an input stream to the specified entry. If a block is passed the
455
- # stream object is passed to the block and the stream is automatically
456
- # closed afterwards just as with ruby's built in +File.open+ method.
457
-
458
- ##
459
- # :method: glob
460
- # :call-seq:
461
- # glob(*args) -> Array of Zip::ZipEntry
462
- # glob(*args) {|entry| ...}
463
- #
464
- # Searches for entries within the UCF document that match the given glob.
465
- #
466
- # See the rubyzip documentation for details of the parameters that can be
467
- # passed in.
468
-
469
- ##
470
- # :method: name
471
- # :call-seq:
472
- # name -> String
473
- #
474
- # Returns the filename of this UCF document.
475
-
476
- ##
477
- # :method: read
478
- # :call-seq:
479
- # read(entry) -> String
480
- #
481
- # Returns a string containing the contents of the specified entry.
482
-
483
- ##
484
- # :method: size
485
- # :call-seq:
486
- # size -> int
487
- #
488
- # Returns the number of entries in the UCF document.
489
-
490
79
  end
491
80
  end