summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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)