zip-container 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
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
+ #
34
+ module ZipContainer
35
+
36
+ # A ManagedDirectory acts as the interface to a set of (possibly) managed
37
+ # files within it and also reserves the directory name in the Container
38
+ # namespace.
39
+ #
40
+ # Once a ManagedDirectory is registered in a Container then only it can be
41
+ # used to write to its contents.
42
+ class ManagedDirectory < ManagedEntry
43
+ include ReservedNames
44
+ include ManagedEntries
45
+
46
+ # :call-seq:
47
+ # new(name, required = false) -> ManagedDirectory
48
+ #
49
+ # Create a new ManagedDirectory with the supplied name and whether it is
50
+ # required to exist or not. Any ManagedFile or ManagedDirectory objects
51
+ # that are within this directory can also be given if required.
52
+ def initialize(name, required = false, entries = [])
53
+ super(name, required)
54
+
55
+ initialize_managed_entries(entries)
56
+ end
57
+
58
+ # :call-seq:
59
+ # verify!
60
+ #
61
+ # Verify this ManagedDirectory for correctness. ManagedFiles registered
62
+ # within it are verified recursively.
63
+ #
64
+ # A MalformedZipContainerError is raised if it does not pass verification.
65
+ def verify!
66
+ super
67
+ @files.values.each { |f| f.verify! }
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,135 @@
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
+ #
34
+ module ZipContainer
35
+
36
+ # ManagedEntry is the superclass of ManagedDirectory and ManagedFile. It
37
+ # should not be used directly but may be subclassed if necessary.
38
+ class ManagedEntry
39
+
40
+ # The name of the ManagedEntry. For the full path name of this entry use
41
+ # full_name.
42
+ attr_reader :name
43
+
44
+ # :call-seq:
45
+ # new(name, required) -> ManagedEntry
46
+ #
47
+ # Create a new ManagedEntry with the supplied name. The entry should also
48
+ # be marked as required or not.
49
+ def initialize(name, required)
50
+ @parent = nil
51
+ @name = name
52
+ @required = required
53
+ end
54
+
55
+ # :call-seq:
56
+ # full_name -> string
57
+ #
58
+ # The fully qualified name of this ManagedEntry.
59
+ def full_name
60
+ @parent.is_a?(Container) ? @name : "#{@parent.name}/#{@name}"
61
+ end
62
+
63
+ # :call-seq:
64
+ # required? -> true or false
65
+ #
66
+ # Is this ManagedEntry required to be present according to the
67
+ # specification of its Container?
68
+ def required?
69
+ @required
70
+ end
71
+
72
+ # :call-seq:
73
+ # exists? -> true or false
74
+ #
75
+ # Does this ManagedEntry exist in the Container?
76
+ def exists?
77
+ container.entries.each do |entry|
78
+ test = (entry.ftype == :directory) ? "#{full_name}/" : full_name
79
+ return true if entry.name == test
80
+ end
81
+
82
+ false
83
+ end
84
+
85
+ # :stopdoc:
86
+ # Allows the object in which this entry has been registered in to tell it
87
+ # who it is.
88
+ def parent=(parent)
89
+ @parent = parent
90
+ end
91
+ # :startdoc:
92
+
93
+ # :call-seq:
94
+ # verify -> true or false
95
+ #
96
+ # Verify this ManagedEntry by checking that it exists if it is required
97
+ # according to its Container specification and validating its contents if
98
+ # necessary.
99
+ def verify
100
+ begin
101
+ verify!
102
+ rescue
103
+ return false
104
+ end
105
+
106
+ true
107
+ end
108
+
109
+ protected
110
+
111
+ # :call-seq:
112
+ # verify!
113
+ #
114
+ # Verify this ManagedEntry raising a MalformedZipContainerError if it
115
+ # fails.
116
+ #
117
+ # Subclasses should override this method if they require more complex
118
+ # verification to be done.
119
+ def verify!
120
+ unless !@required || exists?
121
+ raise MalformedZipContainerError.new("Entry '#{full_name}' is required but "\
122
+ "missing.")
123
+ end
124
+ end
125
+
126
+ # :call-seq:
127
+ # container -> Container
128
+ #
129
+ # Return the Container that this ManagedEntry resides in.
130
+ def container
131
+ @parent.is_a?(Container) ? @parent : @parent.container
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,101 @@
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
+ #
34
+ module ZipContainer
35
+
36
+ # A ManagedFile is used to reserve a filename in a Container namespace.
37
+ class ManagedFile < ManagedEntry
38
+
39
+ # :call-seq:
40
+ # new(name, required = false, validation_proc = nil) -> ManagedFile
41
+ #
42
+ # Create a new ManagedFile with the supplied name and whether it is
43
+ # required to exist or not.
44
+ #
45
+ # If supplied <tt>validation_proc</tt> should be a Proc that takes a
46
+ # single parameter and returns +true+ or +false+ depending on whether the
47
+ # contents of the file were validated or not.
48
+ #
49
+ # For more complex content validation subclasses may override the validate
50
+ # method.
51
+ #
52
+ # The following example creates a ManagedFile that is not required to be
53
+ # present in the container, but if it is, its contents must be the single
54
+ # word "Boo!".
55
+ #
56
+ # valid = Proc.new { |contents| contents == "Boo!" }
57
+ # ManagedFile.new("Surprize.txt", false, valid)
58
+ def initialize(name, required = false, validation_proc = nil)
59
+ super(name, required)
60
+
61
+ @validation_proc = validation_proc.is_a?(Proc) ? validation_proc : nil
62
+ end
63
+
64
+ # :call-seq:
65
+ # verify!
66
+ #
67
+ # Verify this ManagedFile for correctness. The contents are validated if
68
+ # required.
69
+ #
70
+ # A MalformedZipContainerError is raised if it does not pass verification.
71
+ def verify!
72
+ super
73
+ unless (exists? ? validate : true)
74
+ raise MalformedZipContainerError.new("The contents of file "\
75
+ "'#{full_name}' do not pass validation.")
76
+ end
77
+ end
78
+
79
+ protected
80
+
81
+ # :call-seq:
82
+ # validate -> boolean
83
+ #
84
+ # Validate the contents of this ManagedFile. By default this methods uses
85
+ # the validation Proc supplied on object initialization if there is one.
86
+ # If not it simply returns true (no validation was required).
87
+ #
88
+ # For complex validations of content subclasses can override this method.
89
+ def validate
90
+ @validation_proc.nil? ? true : @validation_proc.call(contents)
91
+ end
92
+
93
+ private
94
+
95
+ # Grab the contents of this ManagedFile
96
+ def contents
97
+ container.read(full_name)
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,183 @@
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
+ require 'zip/zip_entry'
34
+
35
+ module ZipContainer
36
+
37
+ # This module provides support for managed file and directory entries.
38
+ #
39
+ # <b>Note!</b> If you mix this module in you *must* call
40
+ # +initialize_managed_entries+ in your constructor to ensure that the
41
+ # internal lists of managed entries are correctly assigned.
42
+ module ManagedEntries
43
+
44
+ # :call-seq:
45
+ # managed_directories -> Array
46
+ #
47
+ # Return the list of managed directories.
48
+ def managed_directories
49
+ @directories.values
50
+ end
51
+
52
+ # :call-seq:
53
+ # managed_directory_names -> Array
54
+ #
55
+ # Return the list of managed directory names.
56
+ def managed_directory_names
57
+ expand_names(@directories.keys)
58
+ end
59
+
60
+ # :call-seq:
61
+ # managed_directory?(entry) -> boolean
62
+ #
63
+ # Is the supplied entry/name a managed directory?
64
+ def managed_directory?(entry)
65
+ managed_entry?(entry, managed_directory_names)
66
+ end
67
+
68
+ # :call-seq:
69
+ # managed_entries -> Array
70
+ #
71
+ # Return the list of managed files and directories.
72
+ def managed_entries
73
+ managed_files + managed_directories
74
+ end
75
+
76
+ # :call-seq:
77
+ # managed_entry_names -> Array
78
+ #
79
+ # Return the list of managed file and directory names.
80
+ def managed_entry_names
81
+ managed_file_names + managed_directory_names
82
+ end
83
+
84
+ # :call-seq:
85
+ # managed_entry?(entry) -> boolean
86
+ #
87
+ # Is the supplied entry/name a managed entry?
88
+ def managed_entry?(entry, list = managed_entry_names)
89
+ name = entry.kind_of?(::Zip::ZipEntry) ? entry.name : entry
90
+ name.chop! if name.end_with? "/"
91
+ list.map { |n| n.downcase }.include? name.downcase
92
+ end
93
+
94
+ # :call-seq:
95
+ # managed_file?(entry) -> boolean
96
+ #
97
+ # Is the supplied entry/name a managed file?
98
+ def managed_file?(entry)
99
+ managed_entry?(entry, managed_file_names)
100
+ end
101
+
102
+ # :call-seq:
103
+ # managed_files -> Array
104
+ #
105
+ # Return the list of managed files.
106
+ def managed_files
107
+ @files.values + managed_directories.map { |d| d.managed_files }.flatten
108
+ end
109
+
110
+ # :call-seq:
111
+ # managed_file_names -> Array
112
+ #
113
+ # Return the list of managed file names.
114
+ def managed_file_names
115
+ expand_names(@files.keys) +
116
+ managed_directories.map { |d| d.managed_file_names }.flatten
117
+ end
118
+
119
+ # :call-seq:
120
+ # verify_managed_entries!
121
+ #
122
+ # All managed files and directories are checked to make sure that they
123
+ # exist, if required.
124
+ def verify_managed_entries!
125
+ @directories.each_value do |dir|
126
+ dir.verify!
127
+ end
128
+
129
+ @files.each_value do |file|
130
+ file.verify!
131
+ end
132
+
133
+ true
134
+ end
135
+
136
+ protected
137
+
138
+ # :call-seq:
139
+ # initialize_managed_entries
140
+ # initialize_managed_entries(entry)
141
+ # initialize_managed_entries(entries)
142
+ #
143
+ # Initialize the managed entries and register any that are supplied. A
144
+ # single ManagedFile or ManagedDirectory or a list of them can be
145
+ # provided.
146
+ def initialize_managed_entries(entries = [])
147
+ list = [*entries]
148
+ @directories ||= {}
149
+ @files ||= {}
150
+
151
+ list.each { |item| register_managed_entry(item) }
152
+ end
153
+
154
+ # :call-seq:
155
+ # register_managed_entry(entry)
156
+ #
157
+ # Register a ManagedFile or ManagedDirectory.
158
+ #
159
+ # A ManagedFile is used to reserve the name of a file in the container
160
+ # namespace and can describe how to verify the contents of it if required.
161
+ #
162
+ # A ManagedDirectory is used to both reserve the name of a directory in
163
+ # the container namespace and act as an interface to the (possibly)
164
+ # managed files within it.
165
+ def register_managed_entry(entry)
166
+ unless entry.is_a?(ManagedDirectory) || entry.is_a?(ManagedFile)
167
+ raise ArgumentError.new("The supplied entry must be of type "\
168
+ "ManagedDirectory or ManagedFile or a subclass of either.")
169
+ end
170
+
171
+ entry.parent = self
172
+ @directories[entry.name] = entry if entry.is_a? ManagedDirectory
173
+ @files[entry.name] = entry if entry.is_a? ManagedFile
174
+ end
175
+
176
+ private
177
+
178
+ def expand_names(names)
179
+ names.map { |n| self.is_a?(Container) ? n : "#{name}/#{n}" }
180
+ end
181
+
182
+ end
183
+ end