From 38e1638fa715398429797276058f2c18e9e21e9b Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sat, 16 Mar 2013 11:40:54 +0900 Subject: First run at 'hidden' comments WIP The xml is genrated correcty, but the comment still shows until it is selected once in the excel ui - so I must be missing something. --- test/workbook/worksheet/tc_comment.rb | 6 +++++- test/workbook/worksheet/tc_comments.rb | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'test/workbook') diff --git a/test/workbook/worksheet/tc_comment.rb b/test/workbook/worksheet/tc_comment.rb index 0cb79bcc..eacce376 100644 --- a/test/workbook/worksheet/tc_comment.rb +++ b/test/workbook/worksheet/tc_comment.rb @@ -5,7 +5,7 @@ class TestComment < Test::Unit::TestCase p = Axlsx::Package.new wb = p.workbook @ws = wb.add_worksheet - @c1 = @ws.add_comment :ref => 'A1', :text => 'penut machine', :author => 'crank' + @c1 = @ws.add_comment :ref => 'A1', :text => 'penut machine', :author => 'crank', :visible => false @c2 = @ws.add_comment :ref => 'C3', :text => 'rust bucket', :author => 'PO' end @@ -28,6 +28,10 @@ class TestComment < Test::Unit::TestCase assert_equal(@c2.author_index, 0) end + def test_visible + assert_equal(false, @c1.visible) + assert_equal(true, @c2.visible) + end def test_ref assert(@c1.ref == 'A1') assert(@c2.ref == 'C3') diff --git a/test/workbook/worksheet/tc_comments.rb b/test/workbook/worksheet/tc_comments.rb index 665f3598..acadf73d 100644 --- a/test/workbook/worksheet/tc_comments.rb +++ b/test/workbook/worksheet/tc_comments.rb @@ -25,9 +25,9 @@ class TestComments < Test::Unit::TestCase end def test_authors assert_equal(@ws.comments.authors.size, @ws.comments.size) - @ws.add_comment(:text => 'Yes We Can!', :author => :bob, :ref => 'F1') + @ws.add_comment(:text => 'Yes We Can!', :author => 'bob', :ref => 'F1') assert_equal(@ws.comments.authors.size, 3) - @ws.add_comment(:text => 'Yes We Can!', :author => :bob, :ref => 'F1') + @ws.add_comment(:text => 'Yes We Can!', :author => 'bob', :ref => 'F1') assert_equal(@ws.comments.authors.size, 3, 'only unique authors are returned') end def test_pn -- cgit v1.2.3 From e56b36a74fadf2f1c1334ade8388c1e046dcad63 Mon Sep 17 00:00:00 2001 From: Noel Peden Date: Thu, 4 Apr 2013 10:29:54 -0700 Subject: Added support for specifying between/notBetween formula in an array. --- examples/conditional_formatting/example_conditional_formatting.rb | 6 ++++-- examples/example.rb | 6 ++++-- lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb | 6 +++--- test/workbook/worksheet/tc_conditional_formatting.rb | 7 +++++++ 4 files changed, 18 insertions(+), 7 deletions(-) (limited to 'test/workbook') diff --git a/examples/conditional_formatting/example_conditional_formatting.rb b/examples/conditional_formatting/example_conditional_formatting.rb index ab49d238..f5823ab4 100644 --- a/examples/conditional_formatting/example_conditional_formatting.rb +++ b/examples/conditional_formatting/example_conditional_formatting.rb @@ -11,8 +11,8 @@ percent = book.styles.add_style(:format_code => "0.00%", :border => Axlsx::STYLE money = book.styles.add_style(:format_code => '0,000', :border => Axlsx::STYLE_THIN_BORDER) # define the style for conditional formatting -profitable = book.styles.add_style( :fg_color=>"FF428751", - :type => :dxf) +profitable = book.styles.add_style( :fg_color => "FF428751", :type => :dxf ) +unprofitable = wb.styles.add_style( :fg_color => "FF0000", :type => :dxf ) book.add_worksheet(:name => "Cell Is") do |ws| @@ -27,6 +27,8 @@ book.add_worksheet(:name => "Cell Is") do |ws| # Apply conditional formatting to range B3:B100 in the worksheet ws.add_conditional_formatting("B3:B100", { :type => :cellIs, :operator => :greaterThan, :formula => "100000", :dxfId => profitable, :priority => 1 }) +# Apply conditional using the between operator; NOTE: supply an array to :formula for between/notBetween + sheet.add_conditional_formatting("C3:C100", { :type => :cellIs, :operator => :between, :formula => ["0.00%","100.00%"], :dxfId => unprofitable, :priority => 1 }) end book.add_worksheet(:name => "Color Scale") do |ws| diff --git a/examples/example.rb b/examples/example.rb index 87814b97..31706c3d 100755 --- a/examples/example.rb +++ b/examples/example.rb @@ -647,8 +647,8 @@ if examples.include? :conditional_formatting money = wb.styles.add_style(:format_code => '0,000', :border => Axlsx::STYLE_THIN_BORDER) # define the style for conditional formatting - profitable = wb.styles.add_style( :fg_color=>"FF428751", - :type => :dxf) + profitable = wb.styles.add_style( :fg_color => "FF428751", :type => :dxf ) + unprofitable = wb.styles.add_style( :fg_color => "FF0000", :type => :dxf ) wb.add_worksheet(:name => "Conditional Cell Is") do |sheet| @@ -663,6 +663,8 @@ if examples.include? :conditional_formatting # Apply conditional formatting to range B3:B100 in the worksheet sheet.add_conditional_formatting("B3:B100", { :type => :cellIs, :operator => :greaterThan, :formula => "100000", :dxfId => profitable, :priority => 1 }) + # Apply conditional using the between operator; NOTE: supply an array to :formula for between/notBetween + sheet.add_conditional_formatting("C3:C100", { :type => :cellIs, :operator => :between, :formula => ["0.00%","100.00%"], :dxfId => unprofitable, :priority => 1 }) end wb.add_worksheet(:name => "Conditional Color Scale") do |sheet| diff --git a/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb index a0ce6a41..39b9612b 100644 --- a/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +++ b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb @@ -25,7 +25,7 @@ module Axlsx # @option options [Integer] stdDev The number of standard deviations above or below the average to match # @option options [Boolean] stopIfTrue Stop evaluating rules after this rule matches # @option options [Symbol] timePeriod The time period in a date occuring... rule - # @option options [String] formula The formula to match against in i.e. an equal rule + # @option options [String] formula The formula to match against in i.e. an equal rule. Use a [minimum, maximum] array for cellIs between/notBetween conditionals. def initialize(options={}) @color_scale = @data_bar = @icon_set = @formula = nil parse_options options @@ -180,7 +180,7 @@ module Axlsx # @see timePeriod def timePeriod=(v); Axlsx::validate_time_period_type(v); @timePeriod = v end # @see formula - def formula=(v); Axlsx::validate_string(v); @formula = v end + def formula=(v); [*v].each {|x| Axlsx::validate_string(x) }; @formula = v end # @see color_scale def color_scale=(v) @@ -208,7 +208,7 @@ module Axlsx str << '' - str << '' << self.formula << '' if @formula + str << '' << [*self.formula].join('') << '' if @formula @color_scale.to_xml_string(str) if @color_scale && @type == :colorScale @data_bar.to_xml_string(str) if @data_bar && @type == :dataBar @icon_set.to_xml_string(str) if @icon_set && @type == :iconSet diff --git a/test/workbook/worksheet/tc_conditional_formatting.rb b/test/workbook/worksheet/tc_conditional_formatting.rb index 087fd40e..42e29fa6 100644 --- a/test/workbook/worksheet/tc_conditional_formatting.rb +++ b/test/workbook/worksheet/tc_conditional_formatting.rb @@ -130,6 +130,13 @@ class TestConditionalFormatting < Test::Unit::TestCase assert doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='greaterThan']//xmlns:formula='0.5'") end + def test_multiple_formulas + @ws.add_conditional_formatting "B3:B3", { :type => :cellIs, :dxfId => 0, :priority => 1, :operator => :between, :formula => ["1","5"] } + doc = Nokogiri::XML.parse(@ws.to_xml_string) + assert doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='between']//xmlns:formula='1'") + assert doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='between']//xmlns:formula='5'") + end + def test_sqref assert_raise(ArgumentError) { @cf.sqref = 10 } assert_nothing_raised { @cf.sqref = "A1:A1" } -- cgit v1.2.3 From be5cbe443d7cea3dd7cfc8aec4d984c34a0fb9bf Mon Sep 17 00:00:00 2001 From: Adam Gardiner Date: Wed, 10 Apr 2013 14:29:11 +0100 Subject: Add support for preserving leading and trailing spaces in cell values --- lib/axlsx/workbook/worksheet/cell_serializer.rb | 10 ++++++++-- test/workbook/worksheet/tc_cell.rb | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/cell_serializer.rb b/lib/axlsx/workbook/worksheet/cell_serializer.rb index e4c35c3a..b054316b 100644 --- a/lib/axlsx/workbook/worksheet/cell_serializer.rb +++ b/lib/axlsx/workbook/worksheet/cell_serializer.rb @@ -23,6 +23,12 @@ module Axlsx # @param [String] str The string instance this run will be concated to. # @return [String] def run_xml_string(cell, str = '') + t = cell.value.to_s + if t[0, 1] == ' ' || t[-1, 1] == ' ' + t = '' << t << '' + else + t = '' << t << '' + end if cell.is_text_run? data = cell.instance_values.reject{|key, value| value == nil || key == 'value' || key == 'type' } keys = data.keys & Cell::INLINE_STYLES @@ -37,9 +43,9 @@ module Axlsx str << "<" << key.to_s << " val='" << data[key].to_s << "'/>" end end - str << "" << "" << cell.value.to_s << "" + str << "" << t << "" else - str << "" << cell.value.to_s << "" + str << t end str end diff --git a/test/workbook/worksheet/tc_cell.rb b/test/workbook/worksheet/tc_cell.rb index 208b15e0..9b8cd769 100644 --- a/test/workbook/worksheet/tc_cell.rb +++ b/test/workbook/worksheet/tc_cell.rb @@ -275,7 +275,20 @@ class TestCell < Test::Unit::TestCase end doc = Nokogiri::XML(ws.to_xml_string) assert(doc.xpath("//f[@text()='IF(2+2=4,4,5)']")) + end + def test_to_xml_string_with_leading_or_trailing_spaces + # Check that xml:space="preserve" has been added when cell contains leading or trailing spaces + @c.type = :string + @c.value = " a" + c_xml = Nokogiri::XML(@c.to_xml_string(1,1)) + assert(c_xml.xpath("//t/@xml:space='preserve'")) + @c.value = "a " + c_xml = Nokogiri::XML(@c.to_xml_string(1,1)) + assert(c_xml.xpath("//t/@xml:space='preserve'")) + @c.value = "a" + c_xml = Nokogiri::XML(@c.to_xml_string(1,1)) + assert(!c_xml.xpath("//t/@xml:space='preserve'")) end def test_font_size_with_custom_style_and_no_sz -- cgit v1.2.3 From e76c93bcbf842f01a02a2485873c5eeed3838bf4 Mon Sep 17 00:00:00 2001 From: Adam Gardiner Date: Sat, 27 Apr 2013 16:33:35 +0100 Subject: Revert changes to cell serialization --- lib/axlsx/workbook/worksheet/cell_serializer.rb | 10 ++-------- test/benchmark.rb | 16 +--------------- test/workbook/worksheet/tc_cell.rb | 13 ------------- 3 files changed, 3 insertions(+), 36 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/cell_serializer.rb b/lib/axlsx/workbook/worksheet/cell_serializer.rb index b054316b..e4c35c3a 100644 --- a/lib/axlsx/workbook/worksheet/cell_serializer.rb +++ b/lib/axlsx/workbook/worksheet/cell_serializer.rb @@ -23,12 +23,6 @@ module Axlsx # @param [String] str The string instance this run will be concated to. # @return [String] def run_xml_string(cell, str = '') - t = cell.value.to_s - if t[0, 1] == ' ' || t[-1, 1] == ' ' - t = '' << t << '' - else - t = '' << t << '' - end if cell.is_text_run? data = cell.instance_values.reject{|key, value| value == nil || key == 'value' || key == 'type' } keys = data.keys & Cell::INLINE_STYLES @@ -43,9 +37,9 @@ module Axlsx str << "<" << key.to_s << " val='" << data[key].to_s << "'/>" end end - str << "" << t << "" + str << "" << "" << cell.value.to_s << "" else - str << t + str << "" << cell.value.to_s << "" end str end diff --git a/test/benchmark.rb b/test/benchmark.rb index 3f017874..2ef82eaf 100644 --- a/test/benchmark.rb +++ b/test/benchmark.rb @@ -8,7 +8,6 @@ Axlsx::trust_input = true row = [] input = (32..126).to_a.pack('U*').chars.to_a 20.times { row << input.shuffle.join} -row_with_spaces = row.map{ |v| " #{v}" } times = 3000 Benchmark.bmbm(30) do |x| @@ -70,18 +69,5 @@ Benchmark.bmbm(30) do |x| end end } - - x.report('preserve-spaces') { - p = Axlsx::Package.new - p.workbook do |wb| - wb.add_worksheet do |sheet| - times.times do - sheet << row_with_spaces - end - end - end - s = p.to_stream() - File.open('example_preserve_spaces.xlsx', 'w') { |f| f.write(s.read) } - } end -File.delete("example.csv", "example_streamed.xlsx", "example_shared.xlsx", "example_autowidth.xlsx", "example_noautowidth.xlsx", "example_preserve_spaces.xlsx") +File.delete("example.csv", "example_streamed.xlsx", "example_shared.xlsx", "example_autowidth.xlsx", "example_noautowidth.xlsx") diff --git a/test/workbook/worksheet/tc_cell.rb b/test/workbook/worksheet/tc_cell.rb index 9b8cd769..208b15e0 100644 --- a/test/workbook/worksheet/tc_cell.rb +++ b/test/workbook/worksheet/tc_cell.rb @@ -275,20 +275,7 @@ class TestCell < Test::Unit::TestCase end doc = Nokogiri::XML(ws.to_xml_string) assert(doc.xpath("//f[@text()='IF(2+2=4,4,5)']")) - end - def test_to_xml_string_with_leading_or_trailing_spaces - # Check that xml:space="preserve" has been added when cell contains leading or trailing spaces - @c.type = :string - @c.value = " a" - c_xml = Nokogiri::XML(@c.to_xml_string(1,1)) - assert(c_xml.xpath("//t/@xml:space='preserve'")) - @c.value = "a " - c_xml = Nokogiri::XML(@c.to_xml_string(1,1)) - assert(c_xml.xpath("//t/@xml:space='preserve'")) - @c.value = "a" - c_xml = Nokogiri::XML(@c.to_xml_string(1,1)) - assert(!c_xml.xpath("//t/@xml:space='preserve'")) end def test_font_size_with_custom_style_and_no_sz -- cgit v1.2.3 From d68df9e5088c7ce23f550968df1280a31f6a8d48 Mon Sep 17 00:00:00 2001 From: Adam Gardiner Date: Sat, 27 Apr 2013 18:01:29 +0100 Subject: Add a preserve_spaces option to worksheet, defaults to true --- lib/axlsx/workbook/worksheet/worksheet.rb | 9 ++++++++- test/workbook/worksheet/tc_worksheet.rb | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index 5f650263..36031be7 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -37,6 +37,7 @@ module Axlsx @page_setup = PageSetup.new options[:page_setup] if options[:page_setup] @print_options = PrintOptions.new options[:print_options] if options[:print_options] @header_footer = HeaderFooter.new options[:header_footer] if options[:header_footer] + @preserve_spaces = options.fetch(:preserve_spaces, true) end # The name of the worksheet @@ -327,6 +328,10 @@ module Axlsx auto_filter.range = v end + # Accessor for controlling whether leading and trailing spaces in cells are + # preserved or ignored. The default is to preserve spaces. + attr_accessor :preserve_spaces + # The part name of this worksheet # @return [String] def pn @@ -699,7 +704,9 @@ module Axlsx # Helper method for parsingout the root node for worksheet # @return [String] def worksheet_node - "" % [XML_NS, XML_NS_R] + (@preserve_spaces ? + "" : + "") % [XML_NS, XML_NS_R] end def sheet_data diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index ed8c7cab..c2bb0faa 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -352,6 +352,15 @@ class TestWorksheet < Test::Unit::TestCase assert(schema.validate(doc).map{ |e| puts e.message; e }.empty?, "error free validation") end + def test_to_xml_string_with_preserve_spaces + # Check that xml:space="preserve" has been added when preserve_spaces is set + ws_xml = Nokogiri::XML(@ws.to_xml_string) + assert(ws_xml.xpath("//xmlns:worksheet/@xml:space='preserve'")) + @ws.preserve_spaces = false + ws_xml = Nokogiri::XML(@ws.to_xml_string) + assert(!ws_xml.xpath("//xmlns:worksheet/@xml:space='preserve'")) + end + def test_styles assert(@ws.styles.is_a?(Axlsx::Styles), 'worksheet provides access to styles') end -- cgit v1.2.3 From e49aee3e2f700569a519c5346746d239a05383cf Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sun, 28 Apr 2013 12:55:46 +0900 Subject: Refactored and renamed space preservation preserve_spaces has been moved to the workbook and renamed xml_space as that provides a good reference for people trying to figure out what it does, and let's the author specify space preservation for serializations using the shared strings table as well as the default inline serialization in cells. --- README.md | 8 +++++++- examples/2010_comments.rb | 17 +++++++++++++++++ lib/axlsx/version.rb | 2 +- lib/axlsx/workbook/shared_strings_table.rb | 14 +++++++++++--- lib/axlsx/workbook/workbook.rb | 20 +++++++++++++++++++- lib/axlsx/workbook/worksheet/worksheet.rb | 11 +++++++---- lib/schema/sml.xsd | 3 +++ test/workbook/tc_shared_strings_table.rb | 6 ++++++ test/workbook/tc_workbook.rb | 17 +++++++++++++++++ test/workbook/worksheet/tc_worksheet.rb | 9 --------- 10 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 examples/2010_comments.rb (limited to 'test/workbook') diff --git a/README.md b/README.md index cb69e4fb..20f67088 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,10 @@ This gem has 100% test coverage using test/unit. To execute tests for this gem, #Change log --------- +- **April.??.13**:1.37 + - Added space preservation for cell text. This will allow whitespace + in cell text both when using shared strings and when serializing + directly to the cell. - **April.24.13**:1.3.6 - Fixed LibreOffice/OpenOffice issue to properly apply colors to lines in charts. @@ -271,7 +275,9 @@ air and our feet on the ground. [nibus](https://github.com/nibus) - For patching sheet name uniqueness. -[scambra](https://github.com/scambra) - for keeping our lines in line! +[scambra](https://github.com/scambra) - For keeping our lines in line! + +[agardiner](https://github.com/agardiner) - For the preservation of space. #Copyright and License ---------- diff --git a/examples/2010_comments.rb b/examples/2010_comments.rb new file mode 100644 index 00000000..6a7bedf8 --- /dev/null +++ b/examples/2010_comments.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby -w -s +# -*- coding: utf-8 -*- +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" + +#```ruby +require 'axlsx' +p = Axlsx::Package.new +p.workbook.add_worksheet(:name => 'Excel 2010 comments') do |sheet| + sheet.add_row ['Cell with visible comment'] + sheet.add_row + sheet.add_row + sheet.add_row ['Cell with hidden comment'] + + sheet.add_comment :ref => 'A1', :author => 'XXX', :text => 'Visibile' + sheet.add_comment :ref => 'A4', :author => 'XXX', :text => 'Hidden', :visible => false +end +p.serialize('excel_2010_comment_test.xlsx') diff --git a/lib/axlsx/version.rb b/lib/axlsx/version.rb index 546f601d..6c9907ab 100644 --- a/lib/axlsx/version.rb +++ b/lib/axlsx/version.rb @@ -1,5 +1,5 @@ module Axlsx # The current version - VERSION = "1.3.6" + VERSION = "1.3.7" end diff --git a/lib/axlsx/workbook/shared_strings_table.rb b/lib/axlsx/workbook/shared_strings_table.rb index 43e8a1a8..f1bee3ae 100644 --- a/lib/axlsx/workbook/shared_strings_table.rb +++ b/lib/axlsx/workbook/shared_strings_table.rb @@ -26,10 +26,16 @@ module Axlsx # @see Cell#sharable attr_reader :unique_cells + # The xml:space attribute + # @see Workbook#xml_space + attr_reader :xml_space + # Creates a new Shared Strings Table agains an array of cells # @param [Array] cells This is an array of all of the cells in the workbook - def initialize(cells) + # @param [Symbol] xml_space The xml:space behavior for the shared string table. + def initialize(cells, xml_space=:preserve) @index = 0 + @xml_space = xml_space @unique_cells = {} @shared_xml_string = "" shareable_cells = cells.flatten.select{ |cell| cell.plain_string? } @@ -40,8 +46,10 @@ module Axlsx # Serializes the object # @param [String] str # @return [String] - def to_xml_string - '' << @shared_xml_string << '' + def to_xml_string(str='') + str << '" : - "") % [XML_NS, XML_NS_R] + "" % [XML_NS, XML_NS_R] end def sheet_data diff --git a/lib/schema/sml.xsd b/lib/schema/sml.xsd index fa396e80..f3994e0a 100644 --- a/lib/schema/sml.xsd +++ b/lib/schema/sml.xsd @@ -13,6 +13,8 @@ + + @@ -2213,6 +2215,7 @@ + diff --git a/test/workbook/tc_shared_strings_table.rb b/test/workbook/tc_shared_strings_table.rb index e3c9bf7b..7a333f4f 100644 --- a/test/workbook/tc_shared_strings_table.rb +++ b/test/workbook/tc_shared_strings_table.rb @@ -24,6 +24,12 @@ class TestSharedStringsTable < Test::Unit::TestCase assert_equal(sst.unique_count, 4) end + def test_uses_workbook_xml_space + assert_equal(@p.workbook.xml_space, @p.workbook.shared_strings.xml_space) + @p.workbook.xml_space = :default + assert_equal(:default, @p.workbook.shared_strings.xml_space) + end + def test_valid_document schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD)) doc = Nokogiri::XML(@p.workbook.shared_strings.to_xml_string) diff --git a/test/workbook/tc_workbook.rb b/test/workbook/tc_workbook.rb index 1a9b9669..591160f4 100644 --- a/test/workbook/tc_workbook.rb +++ b/test/workbook/tc_workbook.rb @@ -9,6 +9,23 @@ class TestWorkbook < Test::Unit::TestCase def teardown end + def test_worksheet_users_xml_space + sheet = @wb.add_worksheet(:name => 'foo') + ws_xml = Nokogiri::XML(sheet.to_xml_string) + assert(ws_xml.xpath("//xmlns:worksheet/@xml:space='preserve'")) + + @wb.xml_space = :default + ws_xml = Nokogiri::XML(sheet.to_xml_string) + assert(ws_xml.xpath("//xmlns:worksheet/@xml:space='default'")) + end + + def test_xml_space + assert_equal(:preserve, @wb.xml_space) + @wb.xml_space = :default + assert_equal(:default, @wb.xml_space) + assert_raise(ArgumentError) { @wb.xml_space = :none } + end + def test_no_autowidth assert_equal(@wb.use_autowidth, true) assert_raise(ArgumentError) {@wb.use_autowidth = 0.1} diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index c2bb0faa..ed8c7cab 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -352,15 +352,6 @@ class TestWorksheet < Test::Unit::TestCase assert(schema.validate(doc).map{ |e| puts e.message; e }.empty?, "error free validation") end - def test_to_xml_string_with_preserve_spaces - # Check that xml:space="preserve" has been added when preserve_spaces is set - ws_xml = Nokogiri::XML(@ws.to_xml_string) - assert(ws_xml.xpath("//xmlns:worksheet/@xml:space='preserve'")) - @ws.preserve_spaces = false - ws_xml = Nokogiri::XML(@ws.to_xml_string) - assert(!ws_xml.xpath("//xmlns:worksheet/@xml:space='preserve'")) - end - def test_styles assert(@ws.styles.is_a?(Axlsx::Styles), 'worksheet provides access to styles') end -- cgit v1.2.3 From dc9e0523fb92f67002bc474f28f47e7255c33d3c Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sun, 12 May 2013 22:31:23 +0100 Subject: implement transpose for SimpleTypedList that supports sparse data. --- lib/axlsx/util/simple_typed_list.rb | 46 ++++++++++++++++++++++--------- lib/axlsx/workbook/worksheet/worksheet.rb | 7 ++--- test/workbook/worksheet/tc_worksheet.rb | 2 +- 3 files changed, 37 insertions(+), 18 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/util/simple_typed_list.rb b/lib/axlsx/util/simple_typed_list.rb index 4d188ffd..9a5f2e3d 100644 --- a/lib/axlsx/util/simple_typed_list.rb +++ b/lib/axlsx/util/simple_typed_list.rb @@ -1,21 +1,9 @@ # encoding: UTF-8 module Axlsx + # A SimpleTypedList is a type restrictive collection that allows some of the methods from Array and supports basic xml serialization. # @private class SimpleTypedList - # The class constants of allowed types - # @return [Array] - attr_reader :allowed_types - - # The index below which items cannot be removed - # @return [Integer] - attr_reader :locked_at - - # The tag name to use when serializing this object - # by default the parent node for all items in the list is the classname of the first allowed type with the first letter in lowercase. - # @return [String] - attr_reader :serialize_as - # Creats a new typed list # @param [Array, Class] type An array of Class objects or a single Class object # @param [String] serialize_as The tag name to use in serialization @@ -33,6 +21,38 @@ module Axlsx @serialize_as = serialize_as end + # The class constants of allowed types + # @return [Array] + attr_reader :allowed_types + + # The index below which items cannot be removed + # @return [Integer] + attr_reader :locked_at + + # The tag name to use when serializing this object + # by default the parent node for all items in the list is the classname of the first allowed type with the first letter in lowercase. + # @return [String] + attr_reader :serialize_as + + # Transposes the list (without blowing up like ruby does) + # any non populated cell in the matrix will be a nil value + def transpose + return @list.clone if @list.size == 0 + row_count = @list.size + max_column_count = @list.map{|row| row.cells.size}.max + result = Array.new(max_column_count) { Array.new(row_count) } + 0..row_count.times do |row_index| + 0..max_column_count.times do |column_index| + datum = if @list[row_index].cells.size >= max_column_count + @list[row_index].cells[column_index] + elsif block_given? + yield(column_index, row_index) + end + result[column_index][row_index] = datum + end + end + result + end # Lock this list at the current size # @return [self] def lock diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index 32e616e2..1187bf83 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -39,7 +39,6 @@ module Axlsx @header_footer = HeaderFooter.new options[:header_footer] if options[:header_footer] end - # The name of the worksheet # @return [String] def name @@ -115,14 +114,14 @@ module Axlsx @rows ||= SimpleTypedList.new Row end - # returns the sheet data as columnw + # returns the sheet data as columns def cols @rows.transpose end - # An range that excel will apply an autfilter to "A1:B3" + # An range that excel will apply an auto-filter to "A1:B3" # This will turn filtering on for the cells in the range. - # The first row is considered the header, while subsequent rows are considerd to be data. + # The first row is considered the header, while subsequent rows are considered to be data. # @return String def auto_filter @auto_filter ||= AutoFilter.new self diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index ed8c7cab..b05a1c56 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -205,7 +205,7 @@ class TestWorksheet < Test::Unit::TestCase def test_cols @ws.add_row [1,2,3,4] @ws.add_row [1,2,3,4] - @ws.add_row [1,2,3,4] + @ws.add_row [1,2,3] @ws.add_row [1,2,3,4] c = @ws.cols[1] assert_equal(c.size, 4) -- cgit v1.2.3 From 10bc965ebf71f6c851f927a646b83a3629e92929 Mon Sep 17 00:00:00 2001 From: Samuel de Framond Date: Thu, 13 Jun 2013 20:37:54 +0800 Subject: Allow a different subtotal function for each data field. --- lib/axlsx/workbook/worksheet/pivot_table.rb | 24 +++++++++------- test/workbook/worksheet/tc_pivot_table.rb | 44 ++++++++++++++++++----------- 2 files changed, 42 insertions(+), 26 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/pivot_table.rb b/lib/axlsx/workbook/worksheet/pivot_table.rb index baa08988..da87162d 100644 --- a/lib/axlsx/workbook/worksheet/pivot_table.rb +++ b/lib/axlsx/workbook/worksheet/pivot_table.rb @@ -23,13 +23,10 @@ module Axlsx @columns = [] @data = [] @pages = [] - @subtotal = 'sum' parse_options options yield self if block_given? end - attr_writer :subtotal - # The reference to the table data # @return [String] attr_reader :ref @@ -88,10 +85,17 @@ module Axlsx # (see #data) def data=(v) DataTypeValidator.validate "#{self.class}.data", [Array], v - v.each do |ref| - DataTypeValidator.validate "#{self.class}.data[]", [String], ref + @data = [] + v.each do |data_field| + if data_field.is_a? String + data_field = {:ref => data_field} + end + data_field.values.each do |value| + DataTypeValidator.validate "#{self.class}.data[]", [String], value + end + @data << data_field end - @data = v + @data end # The pages @@ -189,11 +193,11 @@ module Axlsx str << '' end unless data.empty? - str << '' + str << "" data.each do |datum_value| - str << "' + str << "" end str << '' end diff --git a/test/workbook/worksheet/tc_pivot_table.rb b/test/workbook/worksheet/tc_pivot_table.rb index 9adbaf93..d909eeb6 100644 --- a/test/workbook/worksheet/tc_pivot_table.rb +++ b/test/workbook/worksheet/tc_pivot_table.rb @@ -1,5 +1,17 @@ require 'tc_helper.rb' + +def shared_test_pivot_table_xml_validity(pivot_table) + schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD)) + doc = Nokogiri::XML(pivot_table.to_xml_string) + errors = [] + schema.validate(doc).each do |error| + errors.push error + puts error.message + end + assert(errors.empty?, "error free validation") +end + class TestPivotTable < Test::Unit::TestCase def setup p = Axlsx::Package.new @@ -33,10 +45,17 @@ class TestPivotTable < Test::Unit::TestCase end assert_equal(['Year', 'Month'], pivot_table.rows) assert_equal(['Type'], pivot_table.columns) - assert_equal(['Sales'], pivot_table.data) + assert_equal([{:ref=>"Sales"}], pivot_table.data) assert_equal(['Region'], pivot_table.pages) end + def test_add_pivot_table_with_options_on_data_field + pivot_table = @ws.add_pivot_table('G5:G6', 'A1:D5') do |pt| + pt.data = [{:ref=>"Sales", :subtotal => 'average'}] + end + assert_equal([{:ref=>"Sales", :subtotal => 'average'}], pivot_table.data) + end + def test_header_indices pivot_table = @ws.add_pivot_table('G5:G6', 'A1:E5') assert_equal(0, pivot_table.header_index_of('Year' )) @@ -73,14 +92,7 @@ class TestPivotTable < Test::Unit::TestCase def test_to_xml_string pivot_table = @ws.add_pivot_table('G5:G6', 'A1:D5') - schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD)) - doc = Nokogiri::XML(pivot_table.to_xml_string) - errors = [] - schema.validate(doc).each do |error| - errors.push error - puts error.message - end - assert(errors.empty?, "error free validation") + shared_test_pivot_table_xml_validity(pivot_table) end def test_to_xml_string_with_configuration @@ -90,13 +102,13 @@ class TestPivotTable < Test::Unit::TestCase pt.data = ['Sales'] pt.pages = ['Region'] end - schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD)) - doc = Nokogiri::XML(pivot_table.to_xml_string) - errors = [] - schema.validate(doc).each do |error| - errors.push error - puts error.message + shared_test_pivot_table_xml_validity(pivot_table) + end + + def test_to_xml_string_with_options_on_data_field + pivot_table = @ws.add_pivot_table('G5:G6', 'A1:E5') do |pt| + pt.data = [{:ref=>"Sales", :subtotal => 'average'}] end - assert(errors.empty?, "error free validation") + shared_test_pivot_table_xml_validity(pivot_table) end end -- cgit v1.2.3 From 86180711acbb7f9c6a04688fc9e6ffbf27bacf19 Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sun, 23 Jun 2013 10:11:23 +0100 Subject: added sparse array transposition with blocks for rows/cols switching and some docs updates for release prep --- README.md | 8 +++++--- cd | 1 + examples/underline.rb | 13 +++++++++++++ exclusive.rb | 32 +++++++++++++++++++++++++++++++ lib/axlsx/package.rb | 12 ++++++------ lib/axlsx/workbook/worksheet/worksheet.rb | 9 +++++++-- test/workbook/worksheet/tc_worksheet.rb | 7 +++++++ 7 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 cd create mode 100644 examples/underline.rb create mode 100644 exclusive.rb (limited to 'test/workbook') diff --git a/README.md b/README.md index 20f67088..ff3fe557 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ appreciation for the gem, please don't hesitate to make a donation. **License**: MIT License -**Latest Version**: 1.3.6 +**Latest Version**: 1.3.7 **Ruby Version**: 1.8.7 (soon to be depreciated!!!), 1.9.2, 1.9.3, 2.0.0 @@ -29,7 +29,7 @@ appreciation for the gem, please don't hesitate to make a donation. **Rubinius Version**: rubinius 2.0.0dev * lower versions may run, this gem always tests against head. -**Release Date**: April 24th 2013 +**Release Date**: June ? 2013 If you are working in rails, or with active record see: [acts_as_xlsx](http://github.com/randym/acts_as_xlsx) @@ -160,7 +160,9 @@ This gem has 100% test coverage using test/unit. To execute tests for this gem, #Change log --------- -- **April.??.13**:1.37 +- **June.?.13**:1.3.7 + - Bugfix: transposition of cells for Worksheet#cols now supports + incongruent column counts.counts - Added space preservation for cell text. This will allow whitespace in cell text both when using shared strings and when serializing directly to the cell. diff --git a/cd b/cd new file mode 100644 index 00000000..8588caed --- /dev/null +++ b/cd @@ -0,0 +1 @@ +/opt/boxen/rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/axlsx-1.3.6/lib/axlsx.rb diff --git a/examples/underline.rb b/examples/underline.rb new file mode 100644 index 00000000..ccd1b394 --- /dev/null +++ b/examples/underline.rb @@ -0,0 +1,13 @@ +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +require 'axlsx' +p = Axlsx::Package.new +p.workbook do |wb| + wb.styles do |s| + no_underline = s.add_style :sz => 10, :b => true, :u => false, :alignment => { :horizontal=> :right } + wb.add_worksheet(:name => 'wunderlinen') do |sheet| + sheet.add_row %w{a b c really?}, :style => no_underline + end + end +end +p.serialize 'no_underline.xlsx' + diff --git a/exclusive.rb b/exclusive.rb new file mode 100644 index 00000000..71339146 --- /dev/null +++ b/exclusive.rb @@ -0,0 +1,32 @@ + +def exclusively_true(hash, key) + #clone = hash.clone + return false unless hash.delete(key) == true + !hash.has_value? true +end + +require 'test/unit' +class TestExclusive < Test::Unit::TestCase + def setup + @test_hash = {foo: true, bar: false, hoge: false} + end + def test_exclusive + assert_equal(true, exclusively_true(@test_hash, :foo)) + end + def test_inexclusive + @test_hash[:bar] = true + assert_equal(false, exclusively_true(@test_hash, :foo)) + end +end + +require 'benchmark' + +h = {foo: true} +999.times {|i| h["a#{i}"] = false} +Benchmark.bmbm(30) do |x| + x.report('exclusively_true') do + 1000.times do + exclusively_true(h, :foo) + end + end +end diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb index df87ed12..13a3bd13 100644 --- a/lib/axlsx/package.rb +++ b/lib/axlsx/package.rb @@ -35,12 +35,6 @@ module Axlsx end - # Shortcut to specify that the workbook should use shared strings - # @see Workbook#use_shared_strings - def use_shared_strings=(v) - Axlsx::validate_boolean(v); - workbook.use_shared_strings = v - end # Shortcut to determine if the workbook is configured to use shared strings # @see Workbook#use_shared_strings @@ -48,6 +42,12 @@ module Axlsx workbook.use_shared_strings end + # Shortcut to specify that the workbook should use shared strings + # @see Workbook#use_shared_strings + def use_shared_strings=(v) + Axlsx::validate_boolean(v); + workbook.use_shared_strings = v + end # The workbook this package will serialize or validate. # @return [Workbook] If no workbook instance has been assigned with this package a new Workbook instance is returned. # @raise ArgumentError if workbook parameter is not a Workbook instance. diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index 1187bf83..7324181a 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -115,8 +115,13 @@ module Axlsx end # returns the sheet data as columns - def cols - @rows.transpose + # If you pass a block, it will be evaluated whenever a row does not have a + # cell at a specific index. The block will be called with the row and column + # index in the missing cell was found. + # @example + # cols { |row_index, column_index| p "warn - row #{row_index} is does not have a cell at #{column_index} + def cols(&block) + @rows.transpose(&block) end # An range that excel will apply an auto-filter to "A1:B3" diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index b05a1c56..20bd0771 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -212,6 +212,13 @@ class TestWorksheet < Test::Unit::TestCase assert_equal(c[0].value, 2) end + def test_cols_with_block + @ws.add_row [1,2,3] + @ws.add_row [1] + cols = @ws.cols {|row, column| :foo } + asser_equal(:foo, cols[1][1]) + end + def test_row_style @ws.add_row [1,2,3,4] @ws.add_row [1,2,3,4] -- cgit v1.2.3 From d7ce6f10daad57fc28ca79e10d673f7bc4512673 Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sun, 23 Jun 2013 10:35:42 +0100 Subject: fix typo in specs --- test/workbook/worksheet/tc_worksheet.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/workbook') diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index 20bd0771..980c9e01 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -216,7 +216,7 @@ class TestWorksheet < Test::Unit::TestCase @ws.add_row [1,2,3] @ws.add_row [1] cols = @ws.cols {|row, column| :foo } - asser_equal(:foo, cols[1][1]) + assert_equal(:foo, cols[1][1]) end def test_row_style -- cgit v1.2.3 From 4954543cc0892008f580f05cfb810fb0986b107f Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sun, 23 Jun 2013 10:36:03 +0100 Subject: escape formula for conditional formatting --- lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb | 2 +- test/workbook/worksheet/tc_conditional_formatting.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb index 9db091be..916b31c2 100644 --- a/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +++ b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb @@ -182,7 +182,7 @@ module Axlsx # @see timePeriod def timePeriod=(v); Axlsx::validate_time_period_type(v); @timePeriod = v end # @see formula - def formula=(v); [*v].each {|x| Axlsx::validate_string(x) }; @formula = v end + def formula=(v); [*v].each {|x| Axlsx::validate_string(x) }; @formula = [*v].map { |form| ::CGI.escapeHTML(form) } end # @see color_scale def color_scale=(v) diff --git a/test/workbook/worksheet/tc_conditional_formatting.rb b/test/workbook/worksheet/tc_conditional_formatting.rb index 42e29fa6..9e5e01cf 100644 --- a/test/workbook/worksheet/tc_conditional_formatting.rb +++ b/test/workbook/worksheet/tc_conditional_formatting.rb @@ -131,9 +131,11 @@ class TestConditionalFormatting < Test::Unit::TestCase end def test_multiple_formulas - @ws.add_conditional_formatting "B3:B3", { :type => :cellIs, :dxfId => 0, :priority => 1, :operator => :between, :formula => ["1","5"] } + @ws.add_conditional_formatting "B3:B3", { :type => :cellIs, :dxfId => 0, :priority => 1, :operator => :between, :formula => ["1 <> 2","5"] } doc = Nokogiri::XML.parse(@ws.to_xml_string) - assert doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='between']//xmlns:formula='1'") + p doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='between']") + + assert doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='between']//xmlns:formula='1 <> 2'") assert doc.xpath("//xmlns:worksheet/xmlns:conditionalFormatting//xmlns:cfRule[@type='cellIs'][@dxfId=0][@priority=1][@operator='between']//xmlns:formula='5'") end -- cgit v1.2.3 From c26177a9ec5de20a5e3ecbac635e8ce209102645 Mon Sep 17 00:00:00 2001 From: Stefan Daschek Date: Wed, 3 Jul 2013 16:34:05 +0200 Subject: Fix incorrectly named test case This test case was completely ignored when running the tests, because its name didn't start with "test_". Some details in the test case needed to be fixed, too. --- test/workbook/worksheet/tc_comment.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test/workbook') diff --git a/test/workbook/worksheet/tc_comment.rb b/test/workbook/worksheet/tc_comment.rb index eacce376..e57409ca 100644 --- a/test/workbook/worksheet/tc_comment.rb +++ b/test/workbook/worksheet/tc_comment.rb @@ -49,11 +49,11 @@ class TestComment < Test::Unit::TestCase assert(@c1.vml_shape.bottom_row == pos[1]+4) end - def to_xml_string + def test_to_xml_string doc = Nokogiri::XML(@c1.to_xml_string) assert_equal(doc.xpath("//comment[@ref='#{@c1.ref}']").size, 1) - assert_equal(doc.xpath("//comment[@authorId='#{@c1.author_index.to}']").size, 1) - assert_equal(doc.xpath("//t[text()='#{@c1.author}']").size, 1) + assert_equal(doc.xpath("//comment[@authorId='#{@c1.author_index.to_s}']").size, 1) + assert_equal(doc.xpath("//t[text()='#{@c1.author}:\n']").size, 1) assert_equal(doc.xpath("//t[text()='#{@c1.text}']").size, 1) end -- cgit v1.2.3 From 7bb62e8870ae369a9b2423c87d5e0875873c3834 Mon Sep 17 00:00:00 2001 From: Stefan Daschek Date: Wed, 3 Jul 2013 16:37:35 +0200 Subject: Escape special chars for comments’ text and author. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/axlsx/workbook/worksheet/comment.rb | 4 ++-- test/workbook/worksheet/tc_comment.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/comment.rb b/lib/axlsx/workbook/worksheet/comment.rb index 0c885fda..eee2b58a 100644 --- a/lib/axlsx/workbook/worksheet/comment.rb +++ b/lib/axlsx/workbook/worksheet/comment.rb @@ -66,11 +66,11 @@ module Axlsx str << '' str << '' str << ' ' - str << '' << author.to_s << ': + str << '' << ::CGI.escapeHTML(author.to_s) << ': ' str << '' str << '' - str << '' << text << '' + str << '' << ::CGI.escapeHTML(text) << '' str << '' end diff --git a/test/workbook/worksheet/tc_comment.rb b/test/workbook/worksheet/tc_comment.rb index e57409ca..9f30436d 100644 --- a/test/workbook/worksheet/tc_comment.rb +++ b/test/workbook/worksheet/tc_comment.rb @@ -5,7 +5,7 @@ class TestComment < Test::Unit::TestCase p = Axlsx::Package.new wb = p.workbook @ws = wb.add_worksheet - @c1 = @ws.add_comment :ref => 'A1', :text => 'penut machine', :author => 'crank', :visible => false + @c1 = @ws.add_comment :ref => 'A1', :text => 'text with special char <', :author => 'author with special char <', :visible => false @c2 = @ws.add_comment :ref => 'C3', :text => 'rust bucket', :author => 'PO' end @@ -14,12 +14,12 @@ class TestComment < Test::Unit::TestCase end def test_author - assert(@c1.author == 'crank') + assert(@c1.author == 'author with special char <') assert(@c2.author == 'PO') end def test_text - assert(@c1.text == 'penut machine') + assert(@c1.text == 'text with special char <') assert(@c2.text == 'rust bucket') end -- cgit v1.2.3 From a60d1889744205dbc86ce0e23f8ed04ba7093d23 Mon Sep 17 00:00:00 2001 From: Stefan Daschek Date: Wed, 3 Jul 2013 16:49:34 +0200 Subject: Do not start comment text with stray colon if author is blank --- lib/axlsx/workbook/worksheet/comment.rb | 9 +++++---- test/workbook/worksheet/tc_comment.rb | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/workbook/worksheet/comment.rb b/lib/axlsx/workbook/worksheet/comment.rb index eee2b58a..7035f4cf 100644 --- a/lib/axlsx/workbook/worksheet/comment.rb +++ b/lib/axlsx/workbook/worksheet/comment.rb @@ -64,10 +64,11 @@ module Axlsx def to_xml_string(str = "") author = @comments.authors[author_index] str << '' - str << '' - str << ' ' - str << '' << ::CGI.escapeHTML(author.to_s) << ': -' + str << '' + unless author.to_s == "" + str << '' + str << "" << ::CGI.escapeHTML(author.to_s) << ":\n" + end str << '' str << '' str << '' << ::CGI.escapeHTML(text) << '' diff --git a/test/workbook/worksheet/tc_comment.rb b/test/workbook/worksheet/tc_comment.rb index 9f30436d..e66abb9a 100644 --- a/test/workbook/worksheet/tc_comment.rb +++ b/test/workbook/worksheet/tc_comment.rb @@ -57,5 +57,16 @@ class TestComment < Test::Unit::TestCase assert_equal(doc.xpath("//t[text()='#{@c1.text}']").size, 1) end + def test_comment_text_contain_author_and_text + comment = @ws.add_comment :ref => 'C4', :text => 'some text', :author => 'Bob' + doc = Nokogiri::XML(comment.to_xml_string) + assert_equal("Bob:\nsome text", doc.xpath("//comment/text").text) + end + + def test_comment_text_does_not_contain_stray_colon_if_author_blank + comment = @ws.add_comment :ref => 'C5', :text => 'some text', :author => '' + doc = Nokogiri::XML(comment.to_xml_string) + assert_equal("some text", doc.xpath("//comment/text").text) + end end -- cgit v1.2.3 From 92b73ffb1e5cdfb001da684c463856f79aff6e11 Mon Sep 17 00:00:00 2001 From: Stefan Daschek Date: Fri, 5 Jul 2013 04:22:35 +0200 Subject: Make relationship ids more reliable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relationship instances now keep track of their own id – this should be much more reliable than the old way of more or less “guessing” the relationship id based on the position of some object in some array. Fixes https://github.com/randym/axlsx/issues/212, especially. Each relationship now has its own, unique id – except for the cases when it doesn’t: Some relationships need to share the same id, see `Relation#should_use_same_id_as?` for the gory details. All tests pass, and the full example.xlsx is generated without errors and looks fine in Excel for Mac 2011. The pivot table example still has the problems mentioned in https://github.com/randym/axlsx/issues/168 – but as far as I can tell I didn’t make it worse (Excel is still be able to “repair” the file, and the repaired file then contains the pivot table). --- lib/axlsx/drawing/chart.rb | 6 +- lib/axlsx/drawing/drawing.rb | 16 +----- lib/axlsx/drawing/graphic_frame.rb | 11 +--- lib/axlsx/drawing/hyperlink.rb | 17 ++---- lib/axlsx/drawing/pic.rb | 13 ++--- lib/axlsx/package.rb | 6 +- lib/axlsx/rels/relationship.rb | 67 +++++++++++++++++++--- lib/axlsx/rels/relationships.rb | 9 ++- lib/axlsx/workbook/workbook.rb | 13 ++--- lib/axlsx/workbook/worksheet/comments.rb | 6 +- lib/axlsx/workbook/worksheet/pivot_table.rb | 10 +--- .../worksheet/pivot_table_cache_definition.rb | 5 +- lib/axlsx/workbook/worksheet/pivot_tables.rb | 2 +- lib/axlsx/workbook/worksheet/table.rb | 5 +- lib/axlsx/workbook/worksheet/tables.rb | 2 +- lib/axlsx/workbook/worksheet/worksheet.rb | 13 +---- lib/axlsx/workbook/worksheet/worksheet_comments.rb | 11 ++-- lib/axlsx/workbook/worksheet/worksheet_drawing.rb | 12 +--- .../workbook/worksheet/worksheet_hyperlink.rb | 15 ++--- test/drawing/tc_drawing.rb | 5 -- test/drawing/tc_graphic_frame.rb | 11 ++-- test/drawing/tc_hyperlink.rb | 4 -- test/drawing/tc_pic.rb | 6 ++ test/rels/tc_relationship.rb | 40 +++++++++---- test/rels/tc_relationships.rb | 13 ++++- test/tc_helper.rb | 4 ++ test/workbook/tc_workbook.rb | 7 ++- test/workbook/worksheet/tc_pivot_table.rb | 5 -- .../worksheet/tc_pivot_table_cache_definition.rb | 2 +- test/workbook/worksheet/tc_table.rb | 4 +- test/workbook/worksheet/tc_worksheet.rb | 10 ++-- test/workbook/worksheet/tc_worksheet_hyperlink.rb | 13 +---- 32 files changed, 192 insertions(+), 171 deletions(-) (limited to 'test/workbook') diff --git a/lib/axlsx/drawing/chart.rb b/lib/axlsx/drawing/chart.rb index 1da3a253..c1d408e6 100644 --- a/lib/axlsx/drawing/chart.rb +++ b/lib/axlsx/drawing/chart.rb @@ -80,10 +80,10 @@ module Axlsx # Default :gap (although this really should vary by chart type and grouping) attr_reader :display_blanks_as - # returns a relationship object for the chart - # @return [Axlsx::Relationship] + # The relationship object for this chart. + # @return [Relationship] def relationship - Relationship.new(CHART_R, "../#{pn}") + Relationship.new(self, CHART_R, "../#{pn}") end # The index of this chart in the workbooks charts collection diff --git a/lib/axlsx/drawing/drawing.rb b/lib/axlsx/drawing/drawing.rb index 58f0f81e..1748e9f4 100644 --- a/lib/axlsx/drawing/drawing.rb +++ b/lib/axlsx/drawing/drawing.rb @@ -121,12 +121,6 @@ module Axlsx @worksheet.workbook.drawings.index(self) end - # The relation reference id for this drawing - # @return [String] - def rId - "rId#{index+1}" - end - # The part name for this drawing # @return [String] def pn @@ -140,15 +134,7 @@ module Axlsx "#{DRAWING_RELS_PN % (index+1)}" end - # The index of a chart, image or hyperlink object this drawing contains - def index_of(object) - child_objects.index(object) - end - - - # An ordered list of objects this drawing holds - # It is important that the objects are returned in the same order each time for - # releationship indexing in the package + # A list of objects this drawing holds. # @return [Array] def child_objects charts + images + hyperlinks diff --git a/lib/axlsx/drawing/graphic_frame.rb b/lib/axlsx/drawing/graphic_frame.rb index 943caeae..9b921275 100644 --- a/lib/axlsx/drawing/graphic_frame.rb +++ b/lib/axlsx/drawing/graphic_frame.rb @@ -22,15 +22,10 @@ module Axlsx @chart = chart_type.new(self, options) end - # The relationship id for this graphic + # The relationship id for this graphic frame. # @return [String] - # - # NOTE: Discontinued. This should not be part of GraphicFrame. - # The drawing object maintains relationships and needs to be queried to determine the relationship id of any given graphic data child object. - # def rId - warn('axlsx::DEPRECIATED: GraphicFrame#rId has been depreciated. relationship id is determed by the drawing object') - "rId#{@anchor.index+1}" + @anchor.drawing.relationships.for(chart).Id end # Serializes the object @@ -49,7 +44,7 @@ module Axlsx str << '' str << '' str << '' - str << '' + str << '' str << '' str << '' str << '' diff --git a/lib/axlsx/drawing/hyperlink.rb b/lib/axlsx/drawing/hyperlink.rb index e886f766..850baeef 100644 --- a/lib/axlsx/drawing/hyperlink.rb +++ b/lib/axlsx/drawing/hyperlink.rb @@ -83,27 +83,20 @@ module Axlsx # @return [String] attr_accessor :tooltip - # Returns a relationship object for this hyperlink - # @return [Axlsx::Relationship] + # The relationship object for this hyperlink. + # @return [Relationship] def relationship - Relationship.new(HYPERLINK_R, href, :target_mode => :External) + Relationship.new(self, HYPERLINK_R, href, :target_mode => :External) end + # Serializes the object # @param [String] str # @return [String] def to_xml_string(str = '') str << ' "rId#{id}", :'xmlns:r' => XML_NS_R } + serialized_attributes str, {:'r:id' => relationship.Id, :'xmlns:r' => XML_NS_R } str << '/>' end - private - - # The relational ID for this hyperlink - # @return [Integer] - def id - @parent.anchor.drawing.index_of(self)+1 - end - end end diff --git a/lib/axlsx/drawing/pic.rb b/lib/axlsx/drawing/pic.rb index 8ed8d042..89f4c1ea 100644 --- a/lib/axlsx/drawing/pic.rb +++ b/lib/axlsx/drawing/pic.rb @@ -102,15 +102,10 @@ module Axlsx "#{IMAGE_PN % [(index+1), extname]}" end - # The relational id withing the drawing's relationships - def id - @anchor.drawing.charts.size + @anchor.drawing.images.index(self) + 1 - end - - # Returns a relationship object for this object - # @return Axlsx::Relationship + # The relationship object for this pic. + # @return [Relationship] def relationship - Relationship.new(IMAGE_R, "../#{pn}") + Relationship.new(self, IMAGE_R, "../#{pn}") end # providing access to the anchor's width attribute @@ -177,7 +172,7 @@ module Axlsx picture_locking.to_xml_string(str) str << '' str << '' - str << '' + str << '' str << '' str << '' str << '' diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb index 7e4bad60..a78b5f5d 100644 --- a/lib/axlsx/package.rb +++ b/lib/axlsx/package.rb @@ -339,9 +339,9 @@ module Axlsx # @private def relationships rels = Axlsx::Relationships.new - rels << Relationship.new(WORKBOOK_R, WORKBOOK_PN) - rels << Relationship.new(CORE_R, CORE_PN) - rels << Relationship.new(APP_R, APP_PN) + rels << Relationship.new(self, WORKBOOK_R, WORKBOOK_PN) + rels << Relationship.new(self, CORE_R, CORE_PN) + rels << Relationship.new(self, APP_R, APP_PN) rels.lock rels end diff --git a/lib/axlsx/rels/relationship.rb b/lib/axlsx/rels/relationship.rb index 385059f1..64356d46 100644 --- a/lib/axlsx/rels/relationship.rb +++ b/lib/axlsx/rels/relationship.rb @@ -3,7 +3,29 @@ module Axlsx # A relationship defines a reference between package parts. # @note Packages automatically manage relationships. class Relationship + + class << self + # Keeps track of all instances of this class. + # @return [Array] + def instances + @instances ||= [] + end + + # Generate and return a unique id. Used for setting {#Id}. + # @return [String] + def next_free_id + @next_free_id_counter ||= 0 + @next_free_id_counter += 1 + "rId#{@next_free_id_counter}" + end + end + # The id of the relationship (eg. "rId123"). Most instances get their own unique id. + # However, some instances need to share the same id – see {#should_use_same_id_as?} + # for details. + # @return [String] + attr_reader :Id + # The location of the relationship target # @return [String] attr_reader :Target @@ -30,14 +52,26 @@ module Axlsx # Target mode must be :external for now. attr_reader :TargetMode - # creates a new relationship + # The source object the relations belongs to (e.g. a hyperlink, drawing, ...). Needed when + # looking up the relationship for a specific object (see {Relationships#for}). + attr_reader :source_obj + + # Initializes a new relationship. + # @param [Object] source_obj see {#source_obj} # @param [String] type The type of the relationship # @param [String] target The target for the relationship # @option [Symbol] :target_mode only accepts :external. - def initialize(type, target, options={}) + def initialize(source_obj, type, target, options={}) + @source_obj = source_obj self.Target=target self.Type=type - self.TargetMode = options.delete(:target_mode) if options[:target_mode] + 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 end # @see Target @@ -50,15 +84,32 @@ module Axlsx # serialize relationship # @param [String] str - # @param [Integer] rId the id for this relationship # @return [String] - def to_xml_string(rId, str = '') - h = self.instance_values - h[:Id] = 'rId' << rId.to_s + def to_xml_string(str = '') + h = self.instance_values.reject{|k, _| k == "source_obj"} str << '' end - + + # Whether this relationship should use the same id as `other`. + # + # 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`, + # `../../foo/bar.xml` etc. are all different but probably mean the same file (this + # is especially an issue for relationships in the context of pivot tables). So lets + # just ignore this attribute for now (except when {#TargetMode} is set to `:External` – + # 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 + end + end end diff --git a/lib/axlsx/rels/relationships.rb b/lib/axlsx/rels/relationships.rb index 521d7689..a9c73f7d 100644 --- a/lib/axlsx/rels/relationships.rb +++ b/lib/axlsx/rels/relationships.rb @@ -11,10 +11,17 @@ require 'axlsx/rels/relationship.rb' super Relationship end + # The relationship instance for the given source object, or nil if none exists. + # @see Relationship#source_obj + # @return [Relationship] + def for(source_obj) + @list.find{ |rel| rel.source_obj == source_obj } + end + def to_xml_string(str = '') str << '' str << '' - each_with_index { |rel, index| rel.to_xml_string(index+1, str) } + each{ |rel| rel.to_xml_string(str) } str << '' end diff --git a/lib/axlsx/workbook/workbook.rb b/lib/axlsx/workbook/workbook.rb index 1397552a..d122f253 100644 --- a/lib/axlsx/workbook/workbook.rb +++ b/lib/axlsx/workbook/workbook.rb @@ -270,14 +270,14 @@ require 'axlsx/workbook/worksheet/selection.rb' def relationships r = Relationships.new @worksheets.each do |sheet| - r << Relationship.new(WORKSHEET_R, WORKSHEET_PN % (r.size+1)) + r << Relationship.new(sheet, WORKSHEET_R, WORKSHEET_PN % (r.size+1)) end pivot_tables.each_with_index do |pivot_table, index| - r << Relationship.new(PIVOT_TABLE_CACHE_DEFINITION_R, PIVOT_TABLE_CACHE_DEFINITION_PN % (index+1)) + r << Relationship.new(pivot_table.cache_definition, PIVOT_TABLE_CACHE_DEFINITION_R, PIVOT_TABLE_CACHE_DEFINITION_PN % (index+1)) end - r << Relationship.new(STYLES_R, STYLES_PN) + r << Relationship.new(self, STYLES_R, STYLES_PN) if use_shared_strings - r << Relationship.new(SHARED_STRINGS_R, SHARED_STRINGS_PN) + r << Relationship.new(self, SHARED_STRINGS_R, SHARED_STRINGS_PN) end r end @@ -336,9 +336,8 @@ require 'axlsx/workbook/worksheet/selection.rb' defined_names.to_xml_string(str) unless pivot_tables.empty? str << '' - pivot_tables.each_with_index do |pivot_table, index| - rId = "rId#{@worksheets.size + index + 1 }" - str << '' + pivot_tables.each do |pivot_table| + str << '' end str << '' end diff --git a/lib/axlsx/workbook/worksheet/comments.rb b/lib/axlsx/workbook/worksheet/comments.rb index 25da9de2..03cce339 100644 --- a/lib/axlsx/workbook/worksheet/comments.rb +++ b/lib/axlsx/workbook/worksheet/comments.rb @@ -56,9 +56,9 @@ module Axlsx # The relationships required by this object # @return [Array] def relationships - [Relationship.new(VML_DRAWING_R, "../#{vml_drawing.pn}"), - Relationship.new(COMMENT_R, "../#{pn}"), - Relationship.new(COMMENT_R_NULL, "NULL")] + [Relationship.new(self, VML_DRAWING_R, "../#{vml_drawing.pn}"), + Relationship.new(self, COMMENT_R, "../#{pn}"), + Relationship.new(self, COMMENT_R_NULL, "NULL")] end # serialize the object diff --git a/lib/axlsx/workbook/worksheet/pivot_table.rb b/lib/axlsx/workbook/worksheet/pivot_table.rb index da87162d..632765f9 100644 --- a/lib/axlsx/workbook/worksheet/pivot_table.rb +++ b/lib/axlsx/workbook/worksheet/pivot_table.rb @@ -135,20 +135,14 @@ module Axlsx @cache_definition ||= PivotTableCacheDefinition.new(self) end - # The worksheet relationships. This is managed automatically by the worksheet + # The relationships for this pivot table. # @return [Relationships] def relationships r = Relationships.new - r << Relationship.new(PIVOT_TABLE_CACHE_DEFINITION_R, "../#{cache_definition.pn}") + r << Relationship.new(cache_definition, PIVOT_TABLE_CACHE_DEFINITION_R, "../#{cache_definition.pn}") r end - # The relation reference id for this table - # @return [String] - def rId - "rId#{index+1}" - end - # Serializes the object # @param [String] str # @return [String] diff --git a/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb b/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb index c1683520..340b94ff 100644 --- a/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +++ b/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb @@ -35,10 +35,11 @@ module Axlsx index + 1 end - # The relation reference id for this table + # The relationship id for this pivot table cache definition. + # @see Relationship#Id # @return [String] def rId - "rId#{index + 1}" + pivot_table.relationships.for(self).Id end # Serializes the object diff --git a/lib/axlsx/workbook/worksheet/pivot_tables.rb b/lib/axlsx/workbook/worksheet/pivot_tables.rb index f5625fc0..912d9f41 100644 --- a/lib/axlsx/workbook/worksheet/pivot_tables.rb +++ b/lib/axlsx/workbook/worksheet/pivot_tables.rb @@ -17,7 +17,7 @@ module Axlsx # returns the relationships required by this collection def relationships return [] if empty? - map{ |pivot_table| Relationship.new(PIVOT_TABLE_R, "../#{pivot_table.pn}") } + map{ |pivot_table| Relationship.new(pivot_table, PIVOT_TABLE_R, "../#{pivot_table.pn}") } end end diff --git a/lib/axlsx/workbook/worksheet/table.rb b/lib/axlsx/workbook/worksheet/table.rb index dce1a40e..94474c57 100644 --- a/lib/axlsx/workbook/worksheet/table.rb +++ b/lib/axlsx/workbook/worksheet/table.rb @@ -47,10 +47,11 @@ module Axlsx "#{TABLE_PN % (index+1)}" end - # The relation reference id for this table + # The relationship id for this table. + # @see Relationship#Id # @return [String] def rId - "rId#{index+1}" + @sheet.relationships.for(self).Id end # The name of the Table. diff --git a/lib/axlsx/workbook/worksheet/tables.rb b/lib/axlsx/workbook/worksheet/tables.rb index 2d9a1f3c..814f995f 100644 --- a/lib/axlsx/workbook/worksheet/tables.rb +++ b/lib/axlsx/workbook/worksheet/tables.rb @@ -17,7 +17,7 @@ module Axlsx # returns the relationships required by this collection def relationships return [] if empty? - map{ |table| Relationship.new(TABLE_R, "../#{table.pn}") } + map{ |table| Relationship.new(table, TABLE_R, "../#{table.pn}") } end def to_xml_string(str = "") diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index 7324181a..ecba9254 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -348,10 +348,11 @@ module Axlsx "#{WORKSHEET_RELS_PN % (index+1)}" end - # The relationship Id of thiw worksheet + # The relationship id of this worksheet. # @return [String] + # @see Relationship#Id def rId - "rId#{index+1}" + @workbook.relationships.for(self).Id end # The index of this worksheet in the owning Workbook's worksheets list. @@ -574,14 +575,6 @@ module Axlsx r end - # identifies the index of an object withing the collections used in generating relationships for the worksheet - # @param [Any] object the object to search for - # @return [Integer] The index of the object - def relationships_index_of(object) - objects = [tables.to_a, worksheet_comments.comments.to_a, hyperlinks.to_a, worksheet_drawing.drawing].flatten.compact || [] - objects.index(object) - end - # Returns the cell or cells defined using excel style A1:B3 references. # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber # @return [Cell, Array] diff --git a/lib/axlsx/workbook/worksheet/worksheet_comments.rb b/lib/axlsx/workbook/worksheet/worksheet_comments.rb index 8c700aa0..f9e4c8cd 100644 --- a/lib/axlsx/workbook/worksheet/worksheet_comments.rb +++ b/lib/axlsx/workbook/worksheet/worksheet_comments.rb @@ -40,10 +40,11 @@ module Axlsx !comments.empty? end - # The index in the worksheet's relationships for the VML drawing that will render the comments - # @return [Integer] - def index - worksheet.relationships.index { |r| r.Type == VML_DRAWING_R } + 1 + # The relationship id of the VML drawing that will render the comments. + # @see Relationship#Id + # @return [String] + def drawing_rId + comments.relationships.find{ |r| r.Type == VML_DRAWING_R }.Id end # Seraalize the object @@ -51,7 +52,7 @@ module Axlsx # @return [String] def to_xml_string(str = '') return unless has_comments? - str << "" + str << "" end end end diff --git a/lib/axlsx/workbook/worksheet/worksheet_drawing.rb b/lib/axlsx/workbook/worksheet/worksheet_drawing.rb index 9deeeec3..08cad1f7 100644 --- a/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +++ b/lib/axlsx/workbook/worksheet/worksheet_drawing.rb @@ -41,24 +41,18 @@ module Axlsx @drawing.is_a? Drawing end - # The relationship required by this object + # The relationship instance for this drawing. # @return [Relationship] def relationship return unless has_drawing? - Relationship.new(DRAWING_R, "../#{drawing.pn}") - end - - # returns the index of the worksheet releationship that defines this drawing. - # @return [Integer] - def index - worksheet.relationships.index{ |r| r.Type == DRAWING_R } +1 + Relationship.new(self, DRAWING_R, "../#{drawing.pn}") end # Serialize the drawing for the worksheet # @param [String] str def to_xml_string(str = '') return unless has_drawing? - str << "" + str << "" end end end diff --git a/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb b/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb index d352ec90..2a241287 100644 --- a/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +++ b/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb @@ -45,18 +45,13 @@ module Axlsx @ref = cell_reference end - # The relationship required by this hyperlink when the taget is :external + # The relationship instance for this hyperlink. + # A relationship is only required if `@target` is `:external`. If not, this method will simply return `nil`. + # @see #target= # @return [Relationship] def relationship return unless @target == :external - Relationship.new HYPERLINK_R, location, :target_mode => :External - end - - # The id of the relationship for this object - # @return [String] - def id - return unless @target == :external - "rId#{(@worksheet.relationships_index_of(self)+1)}" + Relationship.new(self, HYPERLINK_R, location, :target_mode => :External) end # Seralize the object @@ -73,7 +68,7 @@ module Axlsx # r:id should only be specified for external targets. # @return [Hash] def location_or_id - @target == :external ? { :"r:id" => id } : { :location => Axlsx::coder.encode(location) } + @target == :external ? { :"r:id" => relationship.Id } : { :location => Axlsx::coder.encode(location) } end end end diff --git a/test/drawing/tc_drawing.rb b/test/drawing/tc_drawing.rb index f347001f..7014f1b2 100644 --- a/test/drawing/tc_drawing.rb +++ b/test/drawing/tc_drawing.rb @@ -53,11 +53,6 @@ class TestDrawing < Test::Unit::TestCase assert_equal(@ws.drawing.rels_pn, "drawings/_rels/drawing1.xml.rels") end - def test_rId - @ws.add_chart(Axlsx::Pie3DChart) - assert_equal(@ws.drawing.rId, "rId1") - end - def test_index @ws.add_chart(Axlsx::Pie3DChart) assert_equal(@ws.drawing.index, @ws.workbook.drawings.index(@ws.drawing)) diff --git a/test/drawing/tc_graphic_frame.rb b/test/drawing/tc_graphic_frame.rb index ce4b8214..eb638489 100644 --- a/test/drawing/tc_graphic_frame.rb +++ b/test/drawing/tc_graphic_frame.rb @@ -17,14 +17,11 @@ class TestGraphicFrame < Test::Unit::TestCase end def test_rId - assert_equal(@frame.rId, "rId1") - chart = @ws.add_chart Axlsx::Chart - assert_equal(chart.graphic_frame.rId, "rId2") + assert_equal @ws.drawing.relationships.for(@chart).Id, @frame.rId end - def test_rId_with_image_and_chart - image = @ws.add_image :image_src => (File.dirname(__FILE__) + "/../../examples/image1.jpeg"), :start_at => [0,25], :width => 200, :height => 200 - assert_equal(2, image.id) - assert_equal(1, @chart.index+1) + def test_to_xml_has_correct_rId + doc = Nokogiri::XML(@frame.to_xml_string) + assert_equal @frame.rId, doc.xpath("//c:chart", doc.collect_namespaces).first["r:id"] end end diff --git a/test/drawing/tc_hyperlink.rb b/test/drawing/tc_hyperlink.rb index 292f1f96..b3f204b5 100644 --- a/test/drawing/tc_hyperlink.rb +++ b/test/drawing/tc_hyperlink.rb @@ -51,10 +51,6 @@ class TestHyperlink < Test::Unit::TestCase assert_equal(@hyperlink.highlightClick, false ) end - def test_id - assert_equal(@hyperlink.send(:id), 2) - end - def test_history assert_nothing_raised { @hyperlink.history = false } assert_raise(ArgumentError) {@hyperlink.history = "bob"} diff --git a/test/drawing/tc_pic.rb b/test/drawing/tc_pic.rb index a892678d..79a3194d 100644 --- a/test/drawing/tc_pic.rb +++ b/test/drawing/tc_pic.rb @@ -93,4 +93,10 @@ class TestPic < Test::Unit::TestCase assert(errors.empty?, "error free validation") end + def test_to_xml_has_correct_r_id + r_id = @image.anchor.drawing.relationships.for(@image).Id + doc = Nokogiri::XML(@image.anchor.drawing.to_xml_string) + assert_equal r_id, doc.xpath("//a:blip").first["r:embed"] + end + end diff --git a/test/rels/tc_relationship.rb b/test/rels/tc_relationship.rb index 32b9e4a1..add1654f 100644 --- a/test/rels/tc_relationship.rb +++ b/test/rels/tc_relationship.rb @@ -1,26 +1,44 @@ require 'tc_helper.rb' class TestRelationships < Test::Unit::TestCase - def setup + + def test_instances_with_different_attributes_have_unique_ids + rel_1 = Axlsx::Relationship.new(Object.new, Axlsx::WORKSHEET_R, 'target') + rel_2 = Axlsx::Relationship.new(Object.new, Axlsx::COMMENT_R, 'foobar') + assert_not_equal rel_1.Id, rel_2.Id end - - def teardown + + def test_instances_with_same_attributes_share_id + source_obj = Object.new + instance = Axlsx::Relationship.new(source_obj, Axlsx::WORKSHEET_R, 'target') + assert_equal instance.Id, Axlsx::Relationship.new(source_obj, Axlsx::WORKSHEET_R, 'target').Id end - + + def test_target_is_only_considered_for_same_attributes_check_if_target_mode_is_external + source_obj = Object.new + rel_1 = Axlsx::Relationship.new(source_obj, Axlsx::WORKSHEET_R, 'target') + rel_2 = Axlsx::Relationship.new(source_obj, Axlsx::WORKSHEET_R, '../target') + assert_equal rel_1.Id, rel_2.Id + + rel_3 = Axlsx::Relationship.new(source_obj, Axlsx::HYPERLINK_R, 'target', :target_mode => :External) + rel_4 = Axlsx::Relationship.new(source_obj, Axlsx::HYPERLINK_R, '../target', :target_mode => :External) + assert_not_equal rel_3.Id, rel_4.Id + end + def test_type - assert_raise(ArgumentError) { Axlsx::Relationship.new 'type', 'target' } - assert_nothing_raised { Axlsx::Relationship.new Axlsx::WORKSHEET_R, 'target' } - assert_nothing_raised { Axlsx::Relationship.new Axlsx::COMMENT_R, 'target' } + assert_raise(ArgumentError) { Axlsx::Relationship.new nil, 'type', 'target' } + assert_nothing_raised { Axlsx::Relationship.new nil, Axlsx::WORKSHEET_R, 'target' } + assert_nothing_raised { Axlsx::Relationship.new nil, Axlsx::COMMENT_R, 'target' } end def test_target_mode - assert_raise(ArgumentError) { Axlsx::Relationship.new 'type', 'target', :target_mode => "FISH" } - assert_nothing_raised { Axlsx::Relationship.new( Axlsx::WORKSHEET_R, 'target', :target_mode => :External) } + assert_raise(ArgumentError) { Axlsx::Relationship.new nil, 'type', 'target', :target_mode => "FISH" } + assert_nothing_raised { Axlsx::Relationship.new( nil, Axlsx::WORKSHEET_R, 'target', :target_mode => :External) } end def test_ampersand_escaping_in_target - r = Axlsx::Relationship.new(Axlsx::HYPERLINK_R, "http://example.com?foo=1&bar=2", :target_mod => :External) - doc = Nokogiri::XML(r.to_xml_string(1)) + r = Axlsx::Relationship.new(nil, Axlsx::HYPERLINK_R, "http://example.com?foo=1&bar=2", :target_mod => :External) + doc = Nokogiri::XML(r.to_xml_string) assert_equal(doc.xpath("//Relationship[@Target='http://example.com?foo=1&bar=2']").size, 1) end end diff --git a/test/rels/tc_relationships.rb b/test/rels/tc_relationships.rb index 356e4691..fe5a1ce5 100644 --- a/test/rels/tc_relationships.rb +++ b/test/rels/tc_relationships.rb @@ -2,6 +2,17 @@ require 'tc_helper.rb' class TestRelationships < Test::Unit::TestCase + def test_for + source_obj_1, source_obj_2 = Object.new, Object.new + rel_1 = Axlsx::Relationship.new(source_obj_1, Axlsx::WORKSHEET_R, "bar") + rel_2 = Axlsx::Relationship.new(source_obj_2, Axlsx::WORKSHEET_R, "bar") + rels = Axlsx::Relationships.new + rels << rel_1 + rels << rel_2 + assert_equal rel_1, rels.for(source_obj_1) + assert_equal rel_2, rels.for(source_obj_2) + end + def test_valid_document @rels = Axlsx::Relationships.new schema = Nokogiri::XML::Schema(File.open(Axlsx::RELS_XSD)) @@ -12,7 +23,7 @@ class TestRelationships < Test::Unit::TestCase errors << error end - @rels << Axlsx::Relationship.new(Axlsx::WORKSHEET_R, "bar") + @rels << Axlsx::Relationship.new(nil, Axlsx::WORKSHEET_R, "bar") doc = Nokogiri::XML(@rels.to_xml_string) errors = [] schema.validate(doc).each do |error| diff --git a/test/tc_helper.rb b/test/tc_helper.rb index af40a1e4..c09446b7 100644 --- a/test/tc_helper.rb +++ b/test/tc_helper.rb @@ -8,3 +8,7 @@ end require 'test/unit' require "timecop" require "axlsx.rb" + +# Make sure all valid rIds are > 1000 - this should help catching the cases where rId is still +# determined by looking at the index of an object in an array etc. +Axlsx::Relationship.instance_variable_set :@next_free_id_counter, 1000 diff --git a/test/workbook/tc_workbook.rb b/test/workbook/tc_workbook.rb index 591160f4..32b6935a 100644 --- a/test/workbook/tc_workbook.rb +++ b/test/workbook/tc_workbook.rb @@ -116,5 +116,10 @@ class TestWorkbook < Test::Unit::TestCase assert_equal(doc.xpath('//xmlns:workbook/xmlns:definedNames/xmlns:definedName').inner_text, @wb.worksheets[0].auto_filter.defined_name) end - + def test_to_xml_uses_correct_rIds_for_pivotCache + ws = @wb.add_worksheet + pivot_table = ws.add_pivot_table('G5:G6', 'A1:D5') + doc = Nokogiri::XML(@wb.to_xml_string) + assert_equal pivot_table.cache_definition.rId, doc.xpath("//xmlns:pivotCache").first["r:id"] + end end diff --git a/test/workbook/worksheet/tc_pivot_table.rb b/test/workbook/worksheet/tc_pivot_table.rb index d909eeb6..ee90bec2 100644 --- a/test/workbook/worksheet/tc_pivot_table.rb +++ b/test/workbook/worksheet/tc_pivot_table.rb @@ -72,11 +72,6 @@ class TestPivotTable < Test::Unit::TestCase assert_equal(@ws.pivot_tables.first.pn, "pivotTables/pivotTable1.xml") end - def test_rId - @ws.add_pivot_table('G5:G6', 'A1:D5') - assert_equal(@ws.pivot_tables.first.rId, "rId1") - end - def test_index @ws.add_pivot_table('G5:G6', 'A1:D5') assert_equal(@ws.pivot_tables.first.index, @ws.workbook.pivot_tables.index(@ws.pivot_tables.first)) diff --git a/test/workbook/worksheet/tc_pivot_table_cache_definition.rb b/test/workbook/worksheet/tc_pivot_table_cache_definition.rb index 2b4389b7..a38e808a 100644 --- a/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +++ b/test/workbook/worksheet/tc_pivot_table_cache_definition.rb @@ -21,7 +21,7 @@ class TestPivotTableCacheDefinition < Test::Unit::TestCase end def test_rId - assert_equal('rId1', @cache_definition.rId) + assert_equal @pivot_table.relationships.for(@cache_definition).Id, @cache_definition.rId end def test_index diff --git a/test/workbook/worksheet/tc_table.rb b/test/workbook/worksheet/tc_table.rb index de86b886..6f39bb13 100644 --- a/test/workbook/worksheet/tc_table.rb +++ b/test/workbook/worksheet/tc_table.rb @@ -36,8 +36,8 @@ class TestTable < Test::Unit::TestCase end def test_rId - @ws.add_table("A1:D5") - assert_equal(@ws.tables.first.rId, "rId1") + table = @ws.add_table("A1:D5") + assert_equal @ws.relationships.for(table).Id, table.rId end def test_index diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index 980c9e01..00983fec 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -123,9 +123,7 @@ class TestWorksheet < Test::Unit::TestCase end def test_rId - assert_equal(@ws.rId, "rId1") - ws = @ws.workbook.add_worksheet - assert_equal(ws.rId, "rId2") + assert_equal @ws.workbook.relationships.for(@ws).Id, @ws.rId end def test_index @@ -341,16 +339,16 @@ class TestWorksheet < Test::Unit::TestCase def test_to_xml_string_drawing @ws.add_chart Axlsx::Pie3DChart doc = Nokogiri::XML(@ws.to_xml_string) - assert_equal(doc.xpath('//xmlns:worksheet/xmlns:drawing[@r:id="rId1"]').size, 1) + assert_equal @ws.send(:worksheet_drawing).relationship.Id, doc.xpath('//xmlns:worksheet/xmlns:drawing').first["r:id"] end def test_to_xml_string_tables @ws.add_row ["one", "two"] @ws.add_row [1, 2] - @ws.add_table "A1:B2" + table = @ws.add_table "A1:B2" doc = Nokogiri::XML(@ws.to_xml_string) assert_equal(doc.xpath('//xmlns:worksheet/xmlns:tableParts[@count="1"]').size, 1) - assert_equal(doc.xpath('//xmlns:worksheet/xmlns:tableParts/xmlns:tablePart[@r:id="rId1"]').size, 1) + assert_equal table.rId, doc.xpath('//xmlns:worksheet/xmlns:tableParts/xmlns:tablePart').first["r:id"] end def test_to_xml_string diff --git a/test/workbook/worksheet/tc_worksheet_hyperlink.rb b/test/workbook/worksheet/tc_worksheet_hyperlink.rb index 278c5add..748082ac 100644 --- a/test/workbook/worksheet/tc_worksheet_hyperlink.rb +++ b/test/workbook/worksheet/tc_worksheet_hyperlink.rb @@ -32,22 +32,13 @@ class TestWorksheetHyperlink < Test::Unit::TestCase assert_equal(@options[:ref], @a.ref) end - def test_id - @a.target = :external - - assert_equal("rId1", @a.id) - @a.target = :internal - assert_equal(nil, @a.id) - end - - def test_to_xml_string_with_non_external doc = Nokogiri::XML(@ws.to_xml_string) assert_equal(doc.xpath("//xmlns:hyperlink[@ref='#{@a.ref}']").size, 1) assert_equal(doc.xpath("//xmlns:hyperlink[@tooltip='#{@a.tooltip}']").size, 1) assert_equal(doc.xpath("//xmlns:hyperlink[@location='#{@a.location}']").size, 1) assert_equal(doc.xpath("//xmlns:hyperlink[@display='#{@a.display}']").size, 1) - assert_equal(doc.xpath("//xmlns:hyperlink[@r:id='#{@a.id}']").size, 0) + assert_equal(doc.xpath("//xmlns:hyperlink[@r:id]").size, 0) end def test_to_xml_stirng_with_external @@ -57,7 +48,7 @@ class TestWorksheetHyperlink < Test::Unit::TestCase assert_equal(doc.xpath("//xmlns:hyperlink[@tooltip='#{@a.tooltip}']").size, 1) assert_equal(doc.xpath("//xmlns:hyperlink[@display='#{@a.display}']").size, 1) assert_equal(doc.xpath("//xmlns:hyperlink[@location='#{@a.location}']").size, 0) - assert_equal(doc.xpath("//xmlns:hyperlink[@r:id='#{@a.id}']").size, 1) + assert_equal(doc.xpath("//xmlns:hyperlink[@r:id='#{@a.relationship.Id}']").size, 1) end end -- cgit v1.2.3