diff options
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | cd | 1 | ||||
| -rw-r--r-- | examples/underline.rb | 13 | ||||
| -rw-r--r-- | exclusive.rb | 32 | ||||
| -rw-r--r-- | lib/axlsx/doc_props/core.rb | 7 | ||||
| -rw-r--r-- | lib/axlsx/drawing/axes.rb | 10 | ||||
| -rw-r--r-- | lib/axlsx/drawing/bar_3D_chart.rb | 4 | ||||
| -rw-r--r-- | lib/axlsx/drawing/chart.rb | 18 | ||||
| -rw-r--r-- | lib/axlsx/package.rb | 14 | ||||
| -rw-r--r-- | lib/axlsx/util/validators.rb | 7 | ||||
| -rw-r--r-- | lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb | 2 | ||||
| -rw-r--r-- | lib/axlsx/workbook/worksheet/worksheet.rb | 9 | ||||
| -rw-r--r-- | test/doc_props/tc_core.rb | 7 | ||||
| -rw-r--r-- | test/drawing/tc_axes.rb | 8 | ||||
| -rw-r--r-- | test/drawing/tc_bar_3D_chart.rb | 6 | ||||
| -rw-r--r-- | test/drawing/tc_chart.rb | 14 | ||||
| -rw-r--r-- | test/tc_package.rb | 6 | ||||
| -rw-r--r-- | test/workbook/worksheet/tc_conditional_formatting.rb | 6 | ||||
| -rw-r--r-- | test/workbook/worksheet/tc_worksheet.rb | 7 |
19 files changed, 158 insertions, 21 deletions
@@ -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. @@ -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/doc_props/core.rb b/lib/axlsx/doc_props/core.rb index d71300e0..a74031f2 100644 --- a/lib/axlsx/doc_props/core.rb +++ b/lib/axlsx/doc_props/core.rb @@ -8,14 +8,19 @@ module Axlsx # Creates a new Core object. # @option options [String] creator + # @option options [Time] created def initialize(options={}) @creator = options[:creator] || 'axlsx' + @created = options[:created] end # The author of the document. By default this is 'axlsx' # @return [String] attr_accessor :creator + # Creation time of the document. If nil, the current time will be used. + attr_accessor :created + # serializes the core.xml document # @return [String] def to_xml_string(str = '') @@ -24,7 +29,7 @@ module Axlsx str << 'xmlns:dcmitype="' << CORE_NS_DCMIT << '" xmlns:dcterms="' << CORE_NS_DCT << '" ' str << 'xmlns:xsi="' << CORE_NS_XSI << '">' str << '<dc:creator>' << self.creator << '</dc:creator>' - str << '<dcterms:created xsi:type="dcterms:W3CDTF">' << Time.now.strftime('%Y-%m-%dT%H:%M:%S') << 'Z</dcterms:created>' + str << '<dcterms:created xsi:type="dcterms:W3CDTF">' << (created || Time.now).strftime('%Y-%m-%dT%H:%M:%S') << 'Z</dcterms:created>' str << '<cp:revision>0</cp:revision>' str << '</cp:coreProperties>' end diff --git a/lib/axlsx/drawing/axes.rb b/lib/axlsx/drawing/axes.rb index c3a8dd85..bc40e532 100644 --- a/lib/axlsx/drawing/axes.rb +++ b/lib/axlsx/drawing/axes.rb @@ -5,9 +5,11 @@ module Axlsx class Axes # @param [Hash] options options used to generate axis each key - # should be an axis name like :val_axix and its value should be the - # class of the axis type to construct. + # should be an axis name like :val_axis and its value should be the + # class of the axis type to construct. The :cat_axis, if there is one, + # must come first (we assume a Ruby 1.9+ Hash or an OrderedHash). def initialize(options={}) + raise(ArgumentError, "CatAxis must come first") if options.keys.include?(:cat_axis) && options.keys.first != :cat_axis options.each do |name, axis_class| add_axis(name, axis_class) end @@ -28,7 +30,9 @@ module Axlsx # serialized. Otherwise, each axis is serialized in full. def to_xml_string(str = '', options = {}) if options[:ids] - axes.inject(str) { |string, axis| string << '<c:axId val="' << axis[1].id.to_s << '"/>' } + # CatAxis must come first in the XML (for Microsoft Excel at least) + sorted = axes.sort_by { |name, axis| axis.kind_of?(CatAxis) ? 0 : 1 } + sorted.inject(str) { |string, axis| string << '<c:axId val="' << axis[1].id.to_s << '"/>' } else axes.each { |axis| axis[1].to_xml_string(str) } end diff --git a/lib/axlsx/drawing/bar_3D_chart.rb b/lib/axlsx/drawing/bar_3D_chart.rb index 3315ad80..755f334c 100644 --- a/lib/axlsx/drawing/bar_3D_chart.rb +++ b/lib/axlsx/drawing/bar_3D_chart.rb @@ -142,10 +142,10 @@ module Axlsx end # A hash of axes used by this chart. Bar charts have a value and - # category axes specified via axex[:val_axes] and axes[:cat_axis] + # category axes specified via axes[:val_axes] and axes[:cat_axis] # @return [Axes] def axes - @axes ||= Axes.new(:val_axis => ValAxis, :cat_axis => CatAxis) + @axes ||= Axes.new(:cat_axis => CatAxis, :val_axis => ValAxis) end end end diff --git a/lib/axlsx/drawing/chart.rb b/lib/axlsx/drawing/chart.rb index 79019a0a..1da3a253 100644 --- a/lib/axlsx/drawing/chart.rb +++ b/lib/axlsx/drawing/chart.rb @@ -20,6 +20,7 @@ module Axlsx @graphic_frame.anchor.drawing.worksheet.workbook.charts << self @series = SimpleTypedList.new Series @show_legend = true + @display_blanks_as = :gap @series_type = Series @title = Title.new parse_options options @@ -70,6 +71,15 @@ module Axlsx # @return [Boolean] attr_reader :show_legend + # How to display blank values + # Options are + # * gap: Display nothing + # * span: Not sure what this does + # * zero: Display as if the value were zero, not blank + # @return [Symbol] + # 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] def relationship @@ -105,6 +115,12 @@ module Axlsx # @return [Boolean] def show_legend=(v) Axlsx::validate_boolean(v); @show_legend = v; end + # How to display blank values + # @see display_blanks_as + # @param [Symbol] v + # @return [Symbol] + def display_blanks_as=(v) Axlsx::validate_display_blanks_as(v); @display_blanks_as = v; end + # The style for the chart. # see ECMA Part 1 ยง21.2.2.196 # @param [Integer] v must be between 1 and 48 @@ -157,7 +173,7 @@ module Axlsx str << '</c:legend>' end str << '<c:plotVisOnly val="1"/>' - str << '<c:dispBlanksAs val="zero"/>' + str << '<c:dispBlanksAs val="' << display_blanks_as.to_s << '"/>' str << '<c:showDLblsOverMax val="1"/>' str << '</c:chart>' str << '<c:printSettings>' diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb index df87ed12..37620b48 100644 --- a/lib/axlsx/package.rb +++ b/lib/axlsx/package.rb @@ -17,12 +17,14 @@ module Axlsx # # @param [Hash] options A hash that you can use to specify the author and workbook for this package. # @option options [String] :author The author of the document + # @option options [Time] :created_at Timestamp in the document properties (defaults to current time). # @option options [Boolean] :use_shared_strings This is passed to the workbook to specify that shared strings should be used when serializing the package. # @example Package.new :author => 'you!', :workbook => Workbook.new def initialize(options={}) @workbook = nil @core, @app = Core.new, App.new @core.creator = options[:author] || @core.creator + @core.created = options[:created_at] parse_options options yield self if block_given? end @@ -35,12 +37,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 +44,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/util/validators.rb b/lib/axlsx/util/validators.rb index 739a4de2..a161f0d9 100644 --- a/lib/axlsx/util/validators.rb +++ b/lib/axlsx/util/validators.rb @@ -290,4 +290,11 @@ module Axlsx def self.validate_split_state_type(v) RestrictionValidator.validate :split_state_type, [:frozen, :frozen_split, :split], v end + + # Requires that the value is a valid "display blanks as" type. + # valid types must be one of gap, span, zero + # @param [Any] v The value validated + def self.validate_display_blanks_as(v) + RestrictionValidator.validate :display_blanks_as, [:gap, :span, :zero], v + end end 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/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/doc_props/tc_core.rb b/test/doc_props/tc_core.rb index cfff4d59..0eddfed7 100644 --- a/test/doc_props/tc_core.rb +++ b/test/doc_props/tc_core.rb @@ -23,6 +23,13 @@ class TestCore < Test::Unit::TestCase assert_equal(@doc.xpath('//dcterms:created').text, @time, "dcterms:created incorrect") end + def test_created_as_option + time = Time.utc(2013, 1, 1, 12, 00) + c = Axlsx::Core.new :created => time + doc = Nokogiri::XML(c.to_xml_string) + assert_equal(doc.xpath('//dcterms:created').text, time.xmlschema, "dcterms:created incorrect") + end + def test_populates_default_name assert_equal(@doc.xpath('//dc:creator').text, "axlsx", "Default name not populated") end diff --git a/test/drawing/tc_axes.rb b/test/drawing/tc_axes.rb new file mode 100644 index 00000000..e3c26936 --- /dev/null +++ b/test/drawing/tc_axes.rb @@ -0,0 +1,8 @@ +require 'tc_helper.rb' + +class TestAxes < Test::Unit::TestCase + def test_constructor_requires_cat_axis_first + assert_raise(ArgumentError) { Axlsx::Axes.new(:val_axis => Axlsx::ValAxis, :cat_axis => Axlsx::CatAxis) } + assert_nothing_raised { Axlsx::Axes.new(:cat_axis => Axlsx::CatAxis, :val_axis => Axlsx::ValAxis) } + end +end
\ No newline at end of file diff --git a/test/drawing/tc_bar_3D_chart.rb b/test/drawing/tc_bar_3D_chart.rb index 3e1ff342..0cae7af6 100644 --- a/test/drawing/tc_bar_3D_chart.rb +++ b/test/drawing/tc_bar_3D_chart.rb @@ -62,4 +62,10 @@ class TestBar3DChart < Test::Unit::TestCase assert(errors.empty?, "error free validation") end + def test_to_xml_string_has_axes_in_correct_order + str = @chart.to_xml_string + cat_axis_position = str.index(@chart.axes[:cat_axis].id.to_s) + val_axis_position = str.index(@chart.axes[:val_axis].id.to_s) + assert(cat_axis_position < val_axis_position, "cat_axis must occur earlier than val_axis in the XML") + end end diff --git a/test/drawing/tc_chart.rb b/test/drawing/tc_chart.rb index 03e4fd6f..751d2ae4 100644 --- a/test/drawing/tc_chart.rb +++ b/test/drawing/tc_chart.rb @@ -45,6 +45,14 @@ class TestChart < Test::Unit::TestCase assert_equal(false, @chart.vary_colors) end + def test_display_blanks_as + assert_equal(:gap, @chart.display_blanks_as, "default is not :gap") + assert_raise(ArgumentError, "did not validate possible values") { @chart.display_blanks_as = :hole } + assert_nothing_raised { @chart.display_blanks_as = :zero } + assert_nothing_raised { @chart.display_blanks_as = :span } + assert_equal(:span, @chart.display_blanks_as) + end + def test_start_at @chart.start_at 15, 25 assert_equal(@chart.graphic_frame.anchor.from.col, 15) @@ -94,4 +102,10 @@ class TestChart < Test::Unit::TestCase assert(errors.empty?, "error free validation") end + def test_to_xml_string_for_display_blanks_as + schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD)) + @chart.display_blanks_as = :span + doc = Nokogiri::XML(@chart.to_xml_string) + assert_equal("span", doc.xpath("//c:dispBlanksAs").attr("val").value, "did not use the display_blanks_as configuration") + end end diff --git a/test/tc_package.rb b/test/tc_package.rb index 5fd411e0..df096a31 100644 --- a/test/tc_package.rb +++ b/test/tc_package.rb @@ -105,6 +105,12 @@ class TestPackage < Test::Unit::TestCase assert(Axlsx::Package.new.workbook.worksheets.size == 0, 'Workbook should not have sheets by default') end + def test_created_at_is_propagated_to_core + time = Time.utc(2013, 1, 1, 12, 0) + p = Axlsx::Package.new :created_at => time + assert_equal(time, p.core.created) + end + def test_serialization assert_nothing_raised do begin 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 diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index b05a1c56..980c9e01 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 } + assert_equal(:foo, cols[1][1]) + end + def test_row_style @ws.add_row [1,2,3,4] @ws.add_row [1,2,3,4] |
