diff options
| author | Stefan Daschek <[email protected]> | 2013-07-08 16:36:42 +0200 |
|---|---|---|
| committer | Stefan Daschek <[email protected]> | 2013-07-08 16:36:42 +0200 |
| commit | e5e79814670569838621e67249927c104006bcca (patch) | |
| tree | 011363a114926d71aaa15c995c6ea132440c5ba6 | |
| parent | 2a4c39f7ae863ac7e4d14e0110aba11fce4a822f (diff) | |
| download | caxlsx-e5e79814670569838621e67249927c104006bcca.tar.gz caxlsx-e5e79814670569838621e67249927c104006bcca.zip | |
Implement Relationship.clear_cached_instances, use it before serializing the package.
This is necessary to make serialization idempotent (i.e. make sure that Relationship instances are generated with the same IDs everytime the package is serialized). It also fixes a memory leak if Axlsx is used in a long running server process (eg a Rails app).
| -rw-r--r-- | lib/axlsx/package.rb | 2 | ||||
| -rw-r--r-- | lib/axlsx/rels/relationship.rb | 18 | ||||
| -rw-r--r-- | test/tc_package.rb | 11 |
3 files changed, 30 insertions, 1 deletions
diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb index a78b5f5d..0a1bceaa 100644 --- a/lib/axlsx/package.rb +++ b/lib/axlsx/package.rb @@ -100,6 +100,7 @@ module Axlsx # File.open('example_streamed.xlsx', 'w') { |f| f.write(s.read) } def serialize(output, confirm_valid=false) return false unless !confirm_valid || self.validate.empty? + Relationship.clear_cached_instances Zip::ZipOutputStream.open(output) do |zip| write_parts(zip) end @@ -112,6 +113,7 @@ module Axlsx # @return [StringIO|Boolean] False if confirm_valid and validation errors exist. rewound string IO if not. def to_stream(confirm_valid=false) return false unless !confirm_valid || self.validate.empty? + Relationship.clear_cached_instances zip = write_parts(Zip::ZipOutputStream.new("streamed", true)) stream = zip.close_buffer stream.rewind diff --git a/lib/axlsx/rels/relationship.rb b/lib/axlsx/rels/relationship.rb index 5e75a98a..a6b7bdd2 100644 --- a/lib/axlsx/rels/relationship.rb +++ b/lib/axlsx/rels/relationship.rb @@ -11,7 +11,23 @@ module Axlsx @instances ||= [] end - # Generate and return a unique id. Used for setting {#Id}. + # Clear cached instances. + # + # This should be called before serializing a package (see {Package#serialize} and + # {Package#to_stream}) to make sure that serialization is idempotent (i.e. + # Relationship instances are generated with the same IDs everytime the package + # is serialized). + # + # Also, calling this avoids memory leaks (cached instances lingering around + # forever). + def clear_cached_instances + @instances = [] + end + + # Generate and return a unique id (eg. `rId123`) Used for setting {#Id}. + # + # The generated id depends on the number of cached instances, so using + # {clear_cached_instances} will automatically reset the generated ids, too. # @return [String] def next_free_id "rId#{@instances.size + 1}" diff --git a/test/tc_package.rb b/test/tc_package.rb index 86f11dd4..d7aa6904 100644 --- a/test/tc_package.rb +++ b/test/tc_package.rb @@ -133,6 +133,17 @@ class TestPackage < Test::Unit::TestCase assert zip_content_then == zip_content_now, "zip files are not identical" end end + + def test_serialization_creates_identical_files_for_identical_packages + package_1, package_2 = 2.times.map do + Axlsx::Package.new(created_at: Time.utc(2013, 1, 1)).tap do |p| + p.workbook.add_worksheet(:name => "Basic Worksheet") do |sheet| + sheet.add_row [1, 2, 3] + end + end + end + assert package_1.to_stream.string == package_2.to_stream.string, "zip files are not identical" + end def test_validation assert_equal(@package.validate.size, 0, @package.validate) |
