ucf 0.0.1 → 0.0.2
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 +17 -0
- data/Rakefile +1 -1
- data/ReadMe.rdoc +15 -9
- data/examples/ucfinfo +69 -0
- data/lib/ucf/container.rb +63 -17
- data/test/tc_reserved_names.rb +80 -0
- data/test/ts_ucf.rb +1 -0
- data/ucf.gemspec +5 -3
- data/version.yml +1 -1
- metadata +5 -3
data/Changes.rdoc
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
= Changes log for the UCF Ruby Gem
|
2
2
|
|
3
|
+
== Version 0.0.2
|
4
|
+
|
5
|
+
* Update the main ReadMe file.
|
6
|
+
* Add support for multiple reserved names and fix checks.
|
7
|
+
* Ensure UCF document is closed after verifying it.
|
8
|
+
* Expose the "each" method for enumerating UCF entries.
|
9
|
+
* Expose the "size" method to count UCF entries.
|
10
|
+
* Add an example program to list UCF contents.
|
11
|
+
* Add a method to directly iterate over UCF entries.
|
12
|
+
|
13
|
+
== Version 0.0.1
|
14
|
+
|
15
|
+
* Very basic UCF facilities complete (plus examples).
|
16
|
+
* Two ways of verifying UCF files.
|
17
|
+
* API documentation added.
|
18
|
+
* Add tests.
|
19
|
+
|
3
20
|
== About this Changes file
|
4
21
|
|
5
22
|
This file is, at least in part, generated by the following command:
|
data/Rakefile
CHANGED
@@ -48,7 +48,7 @@ Jeweler::Tasks.new do |s|
|
|
48
48
|
s.version = UCF::Version::STRING
|
49
49
|
s.authors = ["Robert Haines"]
|
50
50
|
s.email = ["support@mygrid.org.uk"]
|
51
|
-
s.homepage = "http://
|
51
|
+
s.homepage = "http://mygrid.github.io/ruby-ucf/"
|
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 "\
|
data/ReadMe.rdoc
CHANGED
@@ -2,28 +2,34 @@
|
|
2
2
|
|
3
3
|
Authors:: Robert Haines
|
4
4
|
Contact:: mailto:support@mygrid.org.uk
|
5
|
-
|
5
|
+
Homepage:: http://mygrid.github.io/ruby-ucf
|
6
|
+
Source code:: https://github.com/myGrid/ruby-ucf
|
6
7
|
Licence:: BSD (See Licence file or http://www.opensource.org/licenses/bsd-license.php)
|
7
8
|
Copyright:: (c) 2013 The University of Manchester, UK
|
8
9
|
|
9
10
|
|
10
11
|
== Synopsis
|
11
12
|
|
12
|
-
This is a Ruby library for working with UCF
|
13
|
+
This is a Ruby library for working with UCF documents. See
|
14
|
+
{the specification}[https://learn.adobe.com/wiki/display/PDFNAV/Universal+Container+Format]
|
15
|
+
for more details.
|
13
16
|
|
14
|
-
|
17
|
+
<b>This library is a work in progress!</b> Until we release version 1.0.0 you
|
18
|
+
can expect the API to change in incompatible ways, although we will try to
|
19
|
+
keep this to an absolute minimum. Once version 1.0.0 is released we will be
|
20
|
+
following the principles of {Semantic Versioning}[http://semver.org/] for our
|
21
|
+
version numbering scheme.
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
Examples are provided in the examples directory.
|
23
|
+
There are some examples of how to use the library provided in the examples
|
24
|
+
directory. See the contents of the tests directory for even more.
|
19
25
|
|
20
26
|
== What this library can not do yet
|
21
27
|
|
22
|
-
The basic requirements of a UCF
|
28
|
+
The basic requirements of a UCF document are all implemented but there are a
|
23
29
|
number of optional features that are not yet provided.
|
24
30
|
|
25
|
-
* In memory operation. Presently all operations are performed on
|
26
|
-
are resident on disk.
|
31
|
+
* In memory operation. Presently all operations are performed on documents
|
32
|
+
that are resident on disk.
|
27
33
|
* META-INF directory support. Everything within the META-INF directory is
|
28
34
|
optional but will supported in a near future version.
|
29
35
|
* Changing the mimetype. It is not certain that this is a sensible operation
|
data/examples/ucfinfo
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (c) 2013 The University of Manchester, UK.
|
3
|
+
#
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
14
|
+
# and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# * Neither the names of The University of Manchester nor the names of its
|
17
|
+
# contributors may be used to endorse or promote products derived from this
|
18
|
+
# software without specific prior written permission.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
#
|
32
|
+
# Author: Robert Haines
|
33
|
+
|
34
|
+
require 'rubygems'
|
35
|
+
require 'ucf'
|
36
|
+
|
37
|
+
def usage
|
38
|
+
puts "ucfinfo ucf-file"
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
usage unless ARGV.length == 1
|
43
|
+
|
44
|
+
ucffile = ARGV[0]
|
45
|
+
|
46
|
+
begin
|
47
|
+
ucf = UCF::Container.open(ucffile)
|
48
|
+
rescue UCF::MalformedUCFError, Zip::ZipError => err
|
49
|
+
puts err.to_s
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
|
53
|
+
puts "Archive: #{ucf.to_s}"
|
54
|
+
puts "UCF file size: #{File.size(ucffile)} bytes, number of entries: #{ucf.size}"
|
55
|
+
|
56
|
+
total_size = 0
|
57
|
+
total_comp = 0
|
58
|
+
|
59
|
+
ucf.each do |entry|
|
60
|
+
total_size += entry.size
|
61
|
+
total_comp += entry.compressed_size
|
62
|
+
comp = entry.compression_method == 0 ? "stor" : "defN"
|
63
|
+
size = entry.size.to_s.rjust(8)
|
64
|
+
puts "#{size} #{comp} #{entry.time} #{entry.name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
ratio = ((total_size - total_comp) / total_size.to_f) * 100
|
68
|
+
puts "%d files, %d bytes uncompressed, %d bytes compressed: %.1f%%" %
|
69
|
+
[ucf.size, total_size, total_comp, ratio]
|
data/lib/ucf/container.rb
CHANGED
@@ -47,9 +47,9 @@ module UCF
|
|
47
47
|
class Container
|
48
48
|
|
49
49
|
extend Forwardable
|
50
|
-
def_delegators :@zipfile, :add, :close, :comment, :commit, :dir, :
|
51
|
-
:file, :find_entry, :get_entry, :get_input_stream,
|
52
|
-
:glob, :mkdir, :name, :read
|
50
|
+
def_delegators :@zipfile, :add, :close, :comment, :commit, :dir, :each,
|
51
|
+
:extract, :file, :find_entry, :get_entry, :get_input_stream,
|
52
|
+
:get_output_stream, :glob, :mkdir, :name, :read, :size
|
53
53
|
|
54
54
|
private_class_method :new
|
55
55
|
|
@@ -59,7 +59,12 @@ module UCF
|
|
59
59
|
|
60
60
|
# :stopdoc:
|
61
61
|
DEFAULT_MIMETYPE = "application/epub+zip"
|
62
|
+
|
63
|
+
# Reserved root file names. File names in UCF documents are
|
64
|
+
# case-insensitive so downcase where required in the reserved list.
|
62
65
|
MIMETYPE_FILE = "mimetype"
|
66
|
+
META_INF_DIR = "META-INF"
|
67
|
+
RESERVED_ROOT_NAMES = [MIMETYPE_FILE, META_INF_DIR.downcase]
|
63
68
|
|
64
69
|
ERR_MT_NONE = "Not a UCF file. 'mimetype' file is missing."
|
65
70
|
ERR_MT_BAD_OFF = "Not a UCF file. 'mimetype' file is not at offset 0."
|
@@ -86,6 +91,27 @@ module UCF
|
|
86
91
|
Container.open(filename, &block)
|
87
92
|
end
|
88
93
|
|
94
|
+
# :call-seq:
|
95
|
+
# Container.each_entry -> Enumerator
|
96
|
+
# Container.each_entry {|entry| ...}
|
97
|
+
#
|
98
|
+
# Iterate over the entries in the UCF document. The entry objects returned
|
99
|
+
# by this method are Zip::ZipEntry objects. Please see the rubyzip
|
100
|
+
# documentation for details.
|
101
|
+
def Container.each_entry(filename, &block)
|
102
|
+
c = new(filename)
|
103
|
+
|
104
|
+
if block_given?
|
105
|
+
begin
|
106
|
+
c.each(&block)
|
107
|
+
ensure
|
108
|
+
c.close
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
c.each
|
113
|
+
end
|
114
|
+
|
89
115
|
# :call-seq:
|
90
116
|
# Container.open(filename) -> container
|
91
117
|
# Container.open(filename) {|container| ...}
|
@@ -129,28 +155,29 @@ module UCF
|
|
129
155
|
# This method raises exceptions when errors are found or if there is
|
130
156
|
# something fundamental wrong with the file itself (e.g. not found).
|
131
157
|
def Container.verify!(filename)
|
132
|
-
new(filename)
|
158
|
+
new(filename).close
|
133
159
|
nil
|
134
160
|
end
|
135
161
|
|
136
162
|
# :call-seq:
|
137
163
|
# remove(entry)
|
138
164
|
#
|
139
|
-
# Removes the specified entry. If asked to remove
|
140
|
-
# header file this method will do nothing.
|
165
|
+
# Removes the specified entry. If asked to remove any reserved files such
|
166
|
+
# as the special mimetype header file this method will do nothing.
|
141
167
|
def remove(entry)
|
142
|
-
return if
|
168
|
+
return if reserved_entry?(entry)
|
143
169
|
@zipfile.remove(entry)
|
144
170
|
end
|
145
171
|
|
146
172
|
# :call-seq:
|
147
173
|
# rename(entry, new_name, &continueOnExistsProc)
|
148
174
|
#
|
149
|
-
# Renames the specified entry. If asked to rename
|
150
|
-
# header file this method will do nothing. See the
|
151
|
-
# for details of the +continue_on_exists_proc+
|
175
|
+
# Renames the specified entry. If asked to rename any reserved files such
|
176
|
+
# as the special mimetype header file this method will do nothing. See the
|
177
|
+
# rubyzip documentation for details of the +continue_on_exists_proc+
|
178
|
+
# parameter.
|
152
179
|
def rename(entry, new_name, &continue_on_exists_proc)
|
153
|
-
return if
|
180
|
+
return if reserved_entry?(entry)
|
154
181
|
@zipfile.rename(entry, new_name, continue_on_exists_proc)
|
155
182
|
end
|
156
183
|
|
@@ -158,10 +185,10 @@ module UCF
|
|
158
185
|
# replace(entry, src_path)
|
159
186
|
#
|
160
187
|
# Replaces the specified entry with the contents of +src_path+ (from the
|
161
|
-
# file system). If asked to replace
|
162
|
-
# method will do nothing.
|
188
|
+
# file system). If asked to replace any reserved files such as the special
|
189
|
+
# mimetype header file this method will do nothing.
|
163
190
|
def replace(entry, src_path)
|
164
|
-
return if
|
191
|
+
return if reserved_entry?(entry)
|
165
192
|
@zipfile.replace(entry, src_path)
|
166
193
|
end
|
167
194
|
|
@@ -193,9 +220,11 @@ module UCF
|
|
193
220
|
@zipfile.read(MIMETYPE_FILE)
|
194
221
|
end
|
195
222
|
|
196
|
-
|
197
|
-
|
198
|
-
|
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
|
199
228
|
end
|
200
229
|
|
201
230
|
public
|
@@ -245,6 +274,16 @@ module UCF
|
|
245
274
|
#
|
246
275
|
# See the rubyzip documentation for details.
|
247
276
|
|
277
|
+
##
|
278
|
+
# :method: each
|
279
|
+
# :call-seq:
|
280
|
+
# each -> Enumerator
|
281
|
+
# each {|entry| ...}
|
282
|
+
#
|
283
|
+
# Iterate over the entries in the UCF document. The entry objects returned
|
284
|
+
# by this method are Zip::ZipEntry objects. Please see the rubyzip
|
285
|
+
# documentation for details.
|
286
|
+
|
248
287
|
##
|
249
288
|
# :method: extract
|
250
289
|
# :call-seq:
|
@@ -340,5 +379,12 @@ module UCF
|
|
340
379
|
#
|
341
380
|
# Returns a string containing the contents of the specified entry.
|
342
381
|
|
382
|
+
##
|
383
|
+
# :method: size
|
384
|
+
# :call-seq:
|
385
|
+
# size -> int
|
386
|
+
#
|
387
|
+
# Returns the number of entries in the UCF document.
|
388
|
+
|
343
389
|
end
|
344
390
|
end
|
@@ -0,0 +1,80 @@
|
|
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 'ucf'
|
34
|
+
|
35
|
+
class TestReservedNames < Test::Unit::TestCase
|
36
|
+
|
37
|
+
# Check that nothing happens when trying to delete the mimetype file.
|
38
|
+
def test_delete_mimetype
|
39
|
+
UCF::Container.open($ucf_example) do |ucf|
|
40
|
+
assert(ucf.file.exists?("mimetype"))
|
41
|
+
assert_nil(ucf.remove("mimetype"))
|
42
|
+
assert(ucf.file.exists?("mimetype"))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check that nothing happens when trying to rename the mimetype file.
|
47
|
+
def test_rename_mimetype
|
48
|
+
UCF::Container.open($ucf_example) do |ucf|
|
49
|
+
assert(ucf.file.exists?("mimetype"))
|
50
|
+
assert_nil(ucf.rename("mimetype", "something-else"))
|
51
|
+
assert(ucf.file.exists?("mimetype"))
|
52
|
+
assert(!ucf.file.exists?("something-else"))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Check that nothing happens when trying to replace the contents of the
|
57
|
+
# mimetype file.
|
58
|
+
def test_replace_mimetype
|
59
|
+
UCF::Container.open($ucf_example) do |ucf|
|
60
|
+
assert(ucf.file.exists?("mimetype"))
|
61
|
+
assert_nil(ucf.replace("mimetype", $zip_empty))
|
62
|
+
assert_equal("application/epub+zip", ucf.file.read("mimetype"))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check that nothing happens when trying to delete the META-INF directory.
|
67
|
+
def test_delete_metainf
|
68
|
+
UCF::Container.open($ucf_example) do |ucf|
|
69
|
+
assert_nil(ucf.remove("META-INF"))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check that nothing happens when trying to rename the META-INF directory.
|
74
|
+
def test_rename_metainf
|
75
|
+
UCF::Container.open($ucf_example) do |ucf|
|
76
|
+
assert_nil(ucf.rename("META-INF", "something-else"))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
data/test/ts_ucf.rb
CHANGED
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.0.2"
|
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-05-
|
12
|
+
s.date = "2013-05-29"
|
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 = [
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
"Rakefile",
|
24
24
|
"ReadMe.rdoc",
|
25
25
|
"examples/create_ucf.rb",
|
26
|
+
"examples/ucfinfo",
|
26
27
|
"examples/verify_ucf.rb",
|
27
28
|
"lib/ucf.rb",
|
28
29
|
"lib/ucf/container.rb",
|
@@ -33,11 +34,12 @@ Gem::Specification.new do |s|
|
|
33
34
|
"test/data/null.file",
|
34
35
|
"test/tc_create.rb",
|
35
36
|
"test/tc_read.rb",
|
37
|
+
"test/tc_reserved_names.rb",
|
36
38
|
"test/ts_ucf.rb",
|
37
39
|
"ucf.gemspec",
|
38
40
|
"version.yml"
|
39
41
|
]
|
40
|
-
s.homepage = "http://
|
42
|
+
s.homepage = "http://mygrid.github.io/ruby-ucf/"
|
41
43
|
s.rdoc_options = ["-N", "--tab-width=2", "--main=ReadMe.rdoc"]
|
42
44
|
s.require_paths = ["lib"]
|
43
45
|
s.rubygems_version = "1.8.21"
|
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.0.2
|
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-05-
|
12
|
+
date: 2013-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- Rakefile
|
92
92
|
- ReadMe.rdoc
|
93
93
|
- examples/create_ucf.rb
|
94
|
+
- examples/ucfinfo
|
94
95
|
- examples/verify_ucf.rb
|
95
96
|
- lib/ucf.rb
|
96
97
|
- lib/ucf/container.rb
|
@@ -101,10 +102,11 @@ files:
|
|
101
102
|
- test/data/null.file
|
102
103
|
- test/tc_create.rb
|
103
104
|
- test/tc_read.rb
|
105
|
+
- test/tc_reserved_names.rb
|
104
106
|
- test/ts_ucf.rb
|
105
107
|
- ucf.gemspec
|
106
108
|
- version.yml
|
107
|
-
homepage: http://
|
109
|
+
homepage: http://mygrid.github.io/ruby-ucf/
|
108
110
|
licenses: []
|
109
111
|
post_install_message:
|
110
112
|
rdoc_options:
|