ucf 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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