diff options
| -rw-r--r-- | axlsx.gemspec | 1 | ||||
| -rw-r--r-- | lib/axlsx/package.rb | 20 | ||||
| -rw-r--r-- | test/tc_helper.rb | 1 | ||||
| -rw-r--r-- | test/tc_package.rb | 10 |
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) |
