summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStefan Daschek <[email protected]>2013-07-08 14:15:32 +0200
committerStefan Daschek <[email protected]>2013-07-08 14:15:32 +0200
commitb7e14c242c0d9fc3af7b7ad969ec25df21475547 (patch)
tree6e08e812a46ebd19463db88027c77f21760d2c87
parenta60d1889744205dbc86ce0e23f8ed04ba7093d23 (diff)
downloadcaxlsx-b7e14c242c0d9fc3af7b7ad969ec25df21475547.tar.gz
caxlsx-b7e14c242c0d9fc3af7b7ad969ec25df21475547.zip
Make sure serializing axlsx packages with identical contents always results in identical zip files.
This improves the possibilites for caching and/or consolidating the generated zip (xlsx) files. Up to now, serializing the same package at different times resulted in different zip files because of the timestamp in the zip entry metadata. Note: To generate identical packages (and thus identical zip files), you'll have set Core#created explicitly, eg. with `Package.new(created_at: Time.local(2013, 1, 1)`.
-rw-r--r--axlsx.gemspec1
-rw-r--r--lib/axlsx/package.rb20
-rw-r--r--test/tc_helper.rb1
-rw-r--r--test/tc_package.rb10
4 files changed, 30 insertions, 2 deletions
diff --git a/axlsx.gemspec b/axlsx.gemspec
index 9249452d..630a9de7 100644
--- a/axlsx.gemspec
+++ b/axlsx.gemspec
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'yard'
s.add_development_dependency 'kramdown'
+ s.add_development_dependency 'timecop', "~> 0.6.1"
s.required_ruby_version = '>= 1.8.7'
s.require_path = 'lib'
end
diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb
index 37620b48..7e4bad60 100644
--- a/lib/axlsx/package.rb
+++ b/lib/axlsx/package.rb
@@ -158,12 +158,12 @@ module Axlsx
p = parts
p.each do |part|
unless part[:doc].nil?
- zip.put_next_entry(part[:entry])
+ zip.put_next_entry(zip_entry_for_part(part))
entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc]
zip.puts(entry)
end
unless part[:path].nil?
- zip.put_next_entry(part[:entry]);
+ zip.put_next_entry(zip_entry_for_part(part))
# binread for 1.9.3
zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path])
end
@@ -171,6 +171,22 @@ module Axlsx
zip
end
+ # Generate a ZipEntry for the given package part.
+ # The important part here is to explicitly set the timestamp for the zip entry: Serializing axlsx packages
+ # with identical contents should result in identical zip files – however, the timestamp of a zip entry
+ # defaults to the time of serialization and therefore the zip file contents would be different every time
+ # the package is serialized.
+ #
+ # Note: {Core#created} also defaults to the current time – so to generate identical axlsx packages you have
+ # to set this explicitly, too (eg. with `Package.new(created_at: Time.local(2013, 1, 1))`).
+ #
+ # @param part A hash describing a part of this pacakge (see {#parts})
+ # @return [Zip::ZipEntry]
+ def zip_entry_for_part(part)
+ timestamp = Zip::DOSTime.at(@core.created.to_i)
+ Zip::ZipEntry.new("", part[:entry], "", "", 0, 0, Zip::ZipEntry::DEFLATED, 0, timestamp)
+ end
+
# The parts of a package
# @return [Array] An array of hashes that define the entry, document and schema for each part of the package.
# @private
diff --git a/test/tc_helper.rb b/test/tc_helper.rb
index 08dec0b3..af40a1e4 100644
--- a/test/tc_helper.rb
+++ b/test/tc_helper.rb
@@ -6,4 +6,5 @@ SimpleCov.start do
end
require 'test/unit'
+require "timecop"
require "axlsx.rb"
diff --git a/test/tc_package.rb b/test/tc_package.rb
index df096a31..86f11dd4 100644
--- a/test/tc_package.rb
+++ b/test/tc_package.rb
@@ -123,6 +123,16 @@ class TestPackage < Test::Unit::TestCase
end
end
end
+
+ # See comment for Package#zip_entry_for_part
+ def test_serialization_creates_identical_files_at_any_time_if_created_at_is_set
+ @package.core.created = Time.now
+ zip_content_now = @package.to_stream.string
+ Timecop.travel(3600) do
+ zip_content_then = @package.to_stream.string
+ assert zip_content_then == zip_content_now, "zip files are not identical"
+ end
+ end
def test_validation
assert_equal(@package.validate.size, 0, @package.validate)