diff options
| author | Stefan Daschek <[email protected]> | 2013-07-08 14:15:32 +0200 |
|---|---|---|
| committer | Stefan Daschek <[email protected]> | 2013-07-08 14:15:32 +0200 |
| commit | b7e14c242c0d9fc3af7b7ad969ec25df21475547 (patch) | |
| tree | 6e08e812a46ebd19463db88027c77f21760d2c87 | |
| parent | a60d1889744205dbc86ce0e23f8ed04ba7093d23 (diff) | |
| download | caxlsx-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.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) |
