diff options
| author | Noel Peden <[email protected]> | 2019-10-02 09:05:06 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-10-02 09:05:06 -0700 |
| commit | 4487cd10237055a3f35bada21a1718489fa01226 (patch) | |
| tree | 1b2d49ce08b667c001da8955cc234f6d1779869d /lib | |
| parent | 94a94192f5e63358a4df6a672be355774735b903 (diff) | |
| parent | 09469669ccadf8580643eae8352372e92e1ddbb0 (diff) | |
| download | caxlsx-4487cd10237055a3f35bada21a1718489fa01226.tar.gz caxlsx-4487cd10237055a3f35bada21a1718489fa01226.zip | |
Merge pull request #10 from marshall-lee/caxlsx-fix_relationship_cache
Fix Relationship.instances cache
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/axlsx/package.rb | 8 | ||||
| -rw-r--r-- | lib/axlsx/rels/relationship.rb | 51 |
2 files changed, 32 insertions, 27 deletions
diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb index da661b34..56410339 100644 --- a/lib/axlsx/package.rb +++ b/lib/axlsx/package.rb @@ -100,11 +100,13 @@ 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 + Relationship.initialize_ids_cache Zip::OutputStream.open(output) do |zip| write_parts(zip) end true + ensure + Relationship.clear_ids_cache end @@ -113,11 +115,13 @@ 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 + Relationship.initialize_ids_cache zip = write_parts(Zip::OutputStream.new(StringIO.new, true)) stream = zip.close_buffer stream.rewind stream + ensure + Relationship.clear_ids_cache end # Encrypt the package into a CFB using the password provided diff --git a/lib/axlsx/rels/relationship.rb b/lib/axlsx/rels/relationship.rb index 99dad367..ad47c9a3 100644 --- a/lib/axlsx/rels/relationship.rb +++ b/lib/axlsx/rels/relationship.rb @@ -5,32 +5,40 @@ module Axlsx class Relationship class << self - # Keeps track of all instances of this class. + # Keeps track of relationship ids in use. # @return [Array] - def instances - @instances ||= [] + def ids_cache + Thread.current[:axlsx_relationship_ids_cache] ||= {} end - - # Clear cached instances. + + # Initialize cached ids. # # 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). + def initialize_ids_cache + Thread.current[:axlsx_relationship_ids_cache] = {} + end + + # Clear cached ids. # - # Also, calling this avoids memory leaks (cached instances lingering around + # This should be called after serializing a package (see {Package#serialize} and + # {Package#to_stream}) to free the memory allocated for cache. + # + # Also, calling this avoids memory leaks (cached ids lingering around # forever). - def clear_cached_instances - @instances = [] + def clear_ids_cache + Thread.current[:axlsx_relationship_ids_cache] = nil 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. + # The generated id depends on the number of previously cached ids, so using + # {clear_ids_cache} will automatically reset the generated ids, too. # @return [String] def next_free_id - "rId#{@instances.size + 1}" + "rId#{ids_cache.size + 1}" end end @@ -80,12 +88,7 @@ module Axlsx self.Target=target self.Type=type self.TargetMode = options[:target_mode] if options[:target_mode] - @Id = if (existing = self.class.instances.find{ |i| should_use_same_id_as?(i) }) - existing.Id - else - self.class.next_free_id - end - self.class.instances << self + @Id = (self.class.ids_cache[ids_cache_key] ||= self.class.next_free_id) end # @see Target @@ -106,7 +109,7 @@ module Axlsx str << '/>' end - # Whether this relationship should use the same id as `other`. + # A key that determines whether this relationship should use already generated id. # # Instances designating the same relationship need to use the same id. We can not simply # compare the {#Target} attribute, though: `foo/bar.xml`, `../foo/bar.xml`, @@ -116,13 +119,11 @@ module Axlsx # then {#Target} will be an absolute URL and thus can safely be compared). # # @todo Implement comparison of {#Target} based on normalized path names. - # @param other [Relationship] - def should_use_same_id_as?(other) - result = self.source_obj == other.source_obj && self.Type == other.Type && self.TargetMode == other.TargetMode - if self.TargetMode == :External - result &&= self.Target == other.Target - end - result + # @return [Array] + def ids_cache_key + key = [source_obj, self.Type, self.TargetMode] + key << self.Target if self.TargetMode == :External + key end end |
