summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorRandy Morgan (@morgan_randy) <[email protected]>2012-11-27 15:36:21 -0800
committerRandy Morgan (@morgan_randy) <[email protected]>2012-11-27 15:36:21 -0800
commitc3a36737a56a4f0334b97a897a0d3aa17eded82d (patch)
tree87de5615c5c43d49e9c31f583be14168056f0681 /lib
parent7d3d588a1a34777119fae41749711a734da22974 (diff)
parent036f5883939a91fbc3eb377d968d85500dc3098a (diff)
downloadcaxlsx-c3a36737a56a4f0334b97a897a0d3aa17eded82d.tar.gz
caxlsx-c3a36737a56a4f0334b97a897a0d3aa17eded82d.zip
Merge pull request #148 from alexrothenberg/pivot_table
Create a simple Pivot Table
Diffstat (limited to 'lib')
-rw-r--r--lib/axlsx.rb12
-rw-r--r--lib/axlsx/package.rb13
-rw-r--r--lib/axlsx/rels/relationship.rb1
-rw-r--r--lib/axlsx/util/constants.rb21
-rw-r--r--lib/axlsx/util/validators.rb5
-rw-r--r--lib/axlsx/workbook/workbook.rb28
-rw-r--r--lib/axlsx/workbook/worksheet/pivot_table.rb249
-rw-r--r--lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb63
-rw-r--r--lib/axlsx/workbook/worksheet/pivot_tables.rb24
-rw-r--r--lib/axlsx/workbook/worksheet/worksheet.rb15
10 files changed, 423 insertions, 8 deletions
diff --git a/lib/axlsx.rb b/lib/axlsx.rb
index 20129b95..91391db5 100644
--- a/lib/axlsx.rb
+++ b/lib/axlsx.rb
@@ -104,6 +104,18 @@ module Axlsx
Axlsx::col_ref(c_index).to_s << (r_index+1).to_s
end
+ def self.range_to_a(range)
+ range.match(/^(\w+?\d+)\:(\w+?\d+)$/)
+ start_col, start_row = name_to_indices($1)
+ end_col, end_row = name_to_indices($2)
+ (start_row..end_row).to_a.map do |row_num|
+ (start_col..end_col).to_a.map do |col_num|
+ "#{col_ref(col_num)}#{row_num+1}"
+ end
+ end
+ end
+
+
# performs the increadible feat of changing snake_case to CamelCase
# @param [String] s The snake case string to camelize
# @return [String]
diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb
index e3981d62..df87ed12 100644
--- a/lib/axlsx/package.rb
+++ b/lib/axlsx/package.rb
@@ -192,6 +192,12 @@ module Axlsx
workbook.tables.each do |table|
parts << {:entry => "xl/#{table.pn}", :doc => table.to_xml_string, :schema => SML_XSD}
end
+ workbook.pivot_tables.each do |pivot_table|
+ cache_definition = pivot_table.cache_definition
+ parts << {:entry => "xl/#{pivot_table.rels_pn}", :doc => pivot_table.relationships.to_xml_string, :schema => RELS_XSD}
+ parts << {:entry => "xl/#{pivot_table.pn}", :doc => pivot_table.to_xml_string} #, :schema => SML_XSD}
+ parts << {:entry => "xl/#{cache_definition.pn}", :doc => cache_definition.to_xml_string} #, :schema => SML_XSD}
+ end
workbook.comments.each do|comment|
if comment.size > 0
@@ -255,6 +261,13 @@ module Axlsx
:ContentType => TABLE_CT)
end
+ workbook.pivot_tables.each do |pivot_table|
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.pn}",
+ :ContentType => PIVOT_TABLE_CT)
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.cache_definition.pn}",
+ :ContentType => PIVOT_TABLE_CACHE_DEFINITION_CT)
+ end
+
workbook.comments.each do |comment|
if comment.size > 0
c_types << Axlsx::Override.new(:PartName => "/xl/#{comment.pn}",
diff --git a/lib/axlsx/rels/relationship.rb b/lib/axlsx/rels/relationship.rb
index 04911904..385059f1 100644
--- a/lib/axlsx/rels/relationship.rb
+++ b/lib/axlsx/rels/relationship.rb
@@ -12,6 +12,7 @@ module Axlsx
# @note Supported types are defined as constants in Axlsx:
# @see XML_NS_R
# @see TABLE_R
+ # @see PIVOT_TABLE_R
# @see WORKBOOK_R
# @see WORKSHEET_R
# @see APP_R
diff --git a/lib/axlsx/util/constants.rb b/lib/axlsx/util/constants.rb
index 66434b87..437de6db 100644
--- a/lib/axlsx/util/constants.rb
+++ b/lib/axlsx/util/constants.rb
@@ -51,6 +51,10 @@ module Axlsx
# table rels namespace
TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
+ # pivot table rels namespace
+ PIVOT_TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
+ PIVOT_TABLE_CACHE_DEFINITION_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
+
# workbook rels namespace
WORKBOOK_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
@@ -99,6 +103,12 @@ module Axlsx
# table content type
TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
+ # pivot table content type
+ PIVOT_TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"
+
+ # pivot table cache definition content type
+ PIVOT_TABLE_CACHE_DEFINITION_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"
+
# workbook content type
WORKBOOK_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
@@ -208,6 +218,13 @@ module Axlsx
# drawing part
TABLE_PN = "tables/table%d.xml"
+ # pivot table parts
+ PIVOT_TABLE_PN = "pivotTables/pivotTable%d.xml"
+ PIVOT_TABLE_CACHE_DEFINITION_PN = "pivotCache/pivotCacheDefinition%d.xml"
+
+ # pivot table rels parts
+ PIVOT_TABLE_RELS_PN = "pivotTables/_rels/pivotTable%d.xml.rels"
+
# chart part
CHART_PN = "charts/chart%d.xml"
@@ -320,7 +337,7 @@ module Axlsx
# x1E Information Separator Two
# x1F Information Separator One
#
- # The following are not dealt with.
+ # The following are not dealt with.
# If you have this in your data, expect excel to blow up!
#
# x7F Delete
@@ -365,7 +382,7 @@ module Axlsx
# @see http://www.codetable.net/asciikeycodes
pattern = "[\x0-\x08\x0B\x0C\x0E-\x1F]"
pattern= pattern.respond_to?(:encode) ? pattern.encode('UTF-8') : pattern
-
+
# The regular expression used to remove control characters from worksheets
CONTROL_CHAR_REGEX = Regexp.new(pattern, 'n')
diff --git a/lib/axlsx/util/validators.rb b/lib/axlsx/util/validators.rb
index cc8f0a92..aa8eb1ac 100644
--- a/lib/axlsx/util/validators.rb
+++ b/lib/axlsx/util/validators.rb
@@ -41,6 +41,7 @@ module Axlsx
raise ArgumentError, (ERR_REGEX % [v.inspect, regex.to_s]) unless (v.respond_to?(:to_s) && v.to_s.match(regex))
end
end
+
# Validate that the class of the value provided is either an instance or the class of the allowed types and that any specified additional validation returns true.
class DataTypeValidator
# Perform validation
@@ -229,14 +230,14 @@ module Axlsx
# TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, DRAWING_CT, COMMENT_CT are allowed
# @param [Any] v The value validated
def self.validate_content_type(v)
- RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT], v
+ RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT, PIVOT_TABLE_CT, PIVOT_TABLE_CACHE_DEFINITION_CT], v
end
# Requires that the value is a valid relationship_type
# XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R are allowed
# @param [Any] v The value validated
def self.validate_relationship_type(v)
- RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL], v
+ RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL, PIVOT_TABLE_R, PIVOT_TABLE_CACHE_DEFINITION_R], v
end
# Requires that the value is a valid table element type
diff --git a/lib/axlsx/workbook/workbook.rb b/lib/axlsx/workbook/workbook.rb
index 6c99d6de..c1d304a4 100644
--- a/lib/axlsx/workbook/workbook.rb
+++ b/lib/axlsx/workbook/workbook.rb
@@ -40,6 +40,9 @@ require 'axlsx/workbook/defined_names.rb'
require 'axlsx/workbook/worksheet/table_style_info.rb'
require 'axlsx/workbook/worksheet/table.rb'
require 'axlsx/workbook/worksheet/tables.rb'
+require 'axlsx/workbook/worksheet/pivot_table_cache_definition.rb'
+require 'axlsx/workbook/worksheet/pivot_table.rb'
+require 'axlsx/workbook/worksheet/pivot_tables.rb'
require 'axlsx/workbook/worksheet/data_validation.rb'
require 'axlsx/workbook/worksheet/data_validations.rb'
require 'axlsx/workbook/worksheet/sheet_view.rb'
@@ -121,10 +124,17 @@ require 'axlsx/workbook/worksheet/selection.rb'
# @return [SimpleTypedList]
attr_reader :tables
+ # A colllection of pivot tables associated with this workbook
+ # @note The recommended way to manage drawings is Worksheet#add_table
+ # @see Worksheet#add_table
+ # @see Table
+ # @return [SimpleTypedList]
+ attr_reader :pivot_tables
+
# A collection of defined names for this workbook
# @note The recommended way to manage defined names is Workbook#add_defined_name
- # @see DefinedName
+ # @see DefinedName
# @return [DefinedNames]
def defined_names
@defined_names ||= DefinedNames.new
@@ -170,7 +180,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
# w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
# w
#end
-
+
# Creates a new Workbook
# The recomended way to work with workbooks is via Package#workbook
# @option options [Boolean] date1904. If this is not specified, date1904 is set to false. Office 2011 for Mac defaults to false.
@@ -182,6 +192,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
@images = SimpleTypedList.new Pic
# Are these even used????? Check package serialization parts
@tables = SimpleTypedList.new Table
+ @pivot_tables = SimpleTypedList.new PivotTable
@comments = SimpleTypedList.new Comments
@@ -217,7 +228,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
def use_autowidth=(v=true) Axlsx::validate_boolean v; @use_autowidth = v; end
# inserts a worksheet into this workbook at the position specified.
- # It the index specified is out of range, the worksheet will be added to the end of the
+ # It the index specified is out of range, the worksheet will be added to the end of the
# worksheets collection
# @return [Worksheet]
# @param index The zero based position to insert the newly created worksheet
@@ -259,6 +270,9 @@ require 'axlsx/workbook/worksheet/selection.rb'
@worksheets.each do |sheet|
r << Relationship.new(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))
+ end
r << Relationship.new(STYLES_R, STYLES_PN)
if use_shared_strings
r << Relationship.new(SHARED_STRINGS_R, SHARED_STRINGS_PN)
@@ -299,6 +313,14 @@ require 'axlsx/workbook/worksheet/selection.rb'
end
end
str << '</sheets>'
+ unless pivot_tables.empty?
+ str << '<pivotCaches>'
+ pivot_tables.each_with_index do |pivot_table, index|
+ rId = "rId#{@worksheets.size + index + 1 }"
+ str << '<pivotCache cacheId="' << pivot_table.cache_definition.cache_id.to_s << '" r:id="' << rId << '"/>'
+ end
+ str << '</pivotCaches>'
+ end
defined_names.to_xml_string(str)
str << '</workbook>'
end
diff --git a/lib/axlsx/workbook/worksheet/pivot_table.rb b/lib/axlsx/workbook/worksheet/pivot_table.rb
new file mode 100644
index 00000000..d876f777
--- /dev/null
+++ b/lib/axlsx/workbook/worksheet/pivot_table.rb
@@ -0,0 +1,249 @@
+# encoding: UTF-8
+module Axlsx
+ # Table
+ # @note Worksheet#add_pivot_table is the recommended way to create tables for your worksheets.
+ # @see README for examples
+ class PivotTable
+
+ include Axlsx::OptionsParser
+
+ # Creates a new PivotTable object
+ # @param [String] ref The reference to where the pivot table lives like 'G4:L17'.
+ # @param [String] range The reference to the pivot table data like 'A1:D31'.
+ # @param [Worksheet] sheet The sheet containing the table data.
+ # @option options [Cell, String] name
+ # @option options [TableStyle] style
+ def initialize(ref, range, sheet, options={})
+ @ref = ref
+ self.range = range
+ @sheet = sheet
+ @sheet.workbook.pivot_tables << self
+ @name = "PivotTable#{index+1}"
+ @rows = []
+ @columns = []
+ @data = []
+ @pages = []
+ parse_options options
+ yield self if block_given?
+ end
+
+ # The reference to the table data
+ # @return [String]
+ attr_reader :ref
+
+ # The name of the table.
+ # @return [String]
+ attr_reader :name
+
+ # The name of the sheet.
+ # @return [String]
+ attr_reader :sheet
+
+ # The range where the data for this pivot table lives.
+ # @return [String]
+ attr_reader :range
+
+ def range=(v)
+ DataTypeValidator.validate "#{self.class}.range", [String], v
+ if v.is_a?(String)
+ @range = v
+ end
+ end
+
+ # The rows
+ # @return [Array]
+ attr_reader :rows
+
+ def rows=(v)
+ DataTypeValidator.validate "#{self.class}.rows", [Array], v
+ v.each do |ref|
+ DataTypeValidator.validate "#{self.class}.rows[]", [String], ref
+ end
+ @rows = v
+ end
+
+ # The columns
+ # @return [Array]
+ attr_reader :columns
+
+ def columns=(v)
+ DataTypeValidator.validate "#{self.class}.columns", [Array], v
+ v.each do |ref|
+ DataTypeValidator.validate "#{self.class}.columns[]", [String], ref
+ end
+ @columns = v
+ end
+
+ # The data
+ # @return [Array]
+ attr_reader :data
+
+ def data=(v)
+ DataTypeValidator.validate "#{self.class}.data", [Array], v
+ v.each do |ref|
+ DataTypeValidator.validate "#{self.class}.data[]", [String], ref
+ end
+ @data = v
+ end
+
+ # The pages
+ # @return [String]
+ attr_reader :pages
+
+ def pages=(v)
+ DataTypeValidator.validate "#{self.class}.pages", [Array], v
+ v.each do |ref|
+ DataTypeValidator.validate "#{self.class}.pages[]", [String], ref
+ end
+ @pages = v
+ end
+
+ # The index of this chart in the workbooks charts collection
+ # @return [Integer]
+ def index
+ @sheet.workbook.pivot_tables.index(self)
+ end
+
+ # The part name for this table
+ # @return [String]
+ def pn
+ "#{PIVOT_TABLE_PN % (index+1)}"
+ end
+
+ # The relationship part name of this pivot table
+ # @return [String]
+ def rels_pn
+ "#{PIVOT_TABLE_RELS_PN % (index+1)}"
+ end
+
+ def header_cells_count
+ header_cells.count
+ end
+
+ def cache_definition
+ @cache_definition ||= PivotTableCacheDefinition.new(self)
+ end
+
+ # The worksheet relationships. This is managed automatically by the worksheet
+ # @return [Relationships]
+ def relationships
+ r = Relationships.new
+ r << Relationship.new(PIVOT_TABLE_CACHE_DEFINITION_R, "../#{cache_definition.pn}")
+ 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 = [cache_definition]
+ objects.index(object)
+ end
+
+ # The relation reference id for this table
+ # @return [String]
+ def rId
+ "rId#{index+1}"
+ end
+
+ # Serializes the object
+ # @param [String] str
+ # @return [String]
+ def to_xml_string(str = '')
+ str << '<?xml version="1.0" encoding="UTF-8"?>'
+ str << '<pivotTableDefinition xmlns="' << XML_NS << '" name="' << name << '" cacheId="' << cache_definition.cache_id.to_s << '" dataOnRows="1" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="1" dataCaption="Data" showMultipleLabel="0" showMemberPropertyTips="0" useAutoFormatting="1" indent="0" compact="0" compactData="0" gridDropZones="1" multipleFieldFilters="0">'
+ str << '<location firstDataCol="1" firstDataRow="1" firstHeaderRow="1" ref="' << ref << '"/>'
+ str << '<pivotFields count="' << header_cells_count.to_s << '">'
+ header_cell_values.each do |cell_value|
+ str << pivot_field_for(cell_value)
+ end
+ str << '</pivotFields>'
+ if rows.empty?
+ str << '<rowFields count="1"><field x="-2"/></rowFields>'
+ str << '<rowItems count="2"><i><x/></i> <i i="1"><x v="1"/></i></rowItems>'
+ else
+ str << '<rowFields count="' << rows.size.to_s << '">'
+ rows.each do |row_value|
+ str << '<field x="' << header_index_of(row_value).to_s << '"/>'
+ end
+ str << '</rowFields>'
+ str << '<rowItems count="' << rows.size.to_s << '">'
+ rows.size.times do |i|
+ str << '<i/>'
+ end
+ str << '</rowItems>'
+ end
+ if columns.empty?
+ str << '<colItems count="1"><i/></colItems>'
+ else
+ str << '<colFields count="' << columns.size.to_s << '">'
+ columns.each do |column_value|
+ str << '<field x="' << header_index_of(column_value).to_s << '"/>'
+ end
+ str << '</colFields>'
+ end
+ unless pages.empty?
+ str << '<pageFields count="' << pages.size.to_s << '">'
+ pages.each do |page_value|
+ str << '<pageField fld="' << header_index_of(page_value).to_s << '"/>'
+ end
+ str << '</pageFields>'
+ end
+ unless data.empty?
+ str << '<dataFields count="' << data.size.to_s << '">'
+ data.each do |datum_value|
+ str << '<dataField name="Sum of ' << datum_value << '" ' <<
+ 'fld="' << header_index_of(datum_value).to_s << '" ' <<
+ 'baseField="0" baseItem="0"/>'
+ end
+ str << '</dataFields>'
+ end
+ str << '</pivotTableDefinition>'
+ end
+
+ def header_cell_refs
+ Axlsx::range_to_a(header_range).first
+ end
+
+ def header_cells
+ @sheet[header_range]
+ end
+
+ def header_cell_values
+ header_cells.map(&:value)
+ end
+
+ def header_index_of(value)
+ header_cell_values.index(value)
+ end
+
+ private
+
+ def pivot_field_for(cell_ref)
+ if rows.include? cell_ref
+ '<pivotField axis="axisRow" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
+ '<items count="1"><item t="default"/></items>' <<
+ '</pivotField>'
+ elsif columns.include? cell_ref
+ '<pivotField axis="axisCol" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
+ '<items count="1"><item t="default"/></items>' <<
+ '</pivotField>'
+ elsif pages.include? cell_ref
+ '<pivotField axis="axisCol" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
+ '<items count="1"><item t="default"/></items>' <<
+ '</pivotField>'
+ elsif data.include? cell_ref
+ '<pivotField dataField="1" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
+ '</pivotField>'
+ else
+ '<pivotField compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
+ '</pivotField>'
+ end
+ end
+
+ def header_range
+ range.gsub(/^(\w+?)(\d+)\:(\w+?)\d+$/, '\1\2:\3\2')
+ end
+
+ end
+end
diff --git a/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb b/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb
new file mode 100644
index 00000000..5a6c7442
--- /dev/null
+++ b/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb
@@ -0,0 +1,63 @@
+# encoding: UTF-8
+module Axlsx
+ # Table
+ # @note Worksheet#add_pivot_table is the recommended way to create tables for your worksheets.
+ # @see README for examples
+ class PivotTableCacheDefinition
+
+ include Axlsx::OptionsParser
+
+ # Creates a new PivotTable object
+ # @param [String] pivot_table The pivot table this cache definition is in
+ def initialize(pivot_table)
+ @pivot_table = pivot_table
+ end
+
+ # # The reference to the pivot table data
+ # # @return [PivotTable]
+ attr_reader :pivot_table
+
+ # The index of this chart in the workbooks charts collection
+ # @return [Integer]
+ def index
+ pivot_table.sheet.workbook.pivot_tables.index(pivot_table)
+ end
+
+ # The part name for this table
+ # @return [String]
+ def pn
+ "#{PIVOT_TABLE_CACHE_DEFINITION_PN % (index+1)}"
+ end
+
+ def cache_id
+ index + 1
+ end
+
+ # The relation reference id for this table
+ # @return [String]
+ def rId
+ "rId#{index + 1}"
+ end
+
+ # Serializes the object
+ # @param [String] str
+ # @return [String]
+ def to_xml_string(str = '')
+ str << '<?xml version="1.0" encoding="UTF-8"?>'
+ str << '<pivotCacheDefinition xmlns="' << XML_NS << '" xmlns:r="' << XML_NS_R << '" invalid="1" refreshOnLoad="1" recordCount="0">'
+ str << '<cacheSource type="worksheet">'
+ str << '<worksheetSource ref="' << pivot_table.range << '" sheet="Data Sheet"/>'
+ str << '</cacheSource>'
+ str << '<cacheFields count="' << pivot_table.header_cells_count.to_s << '">'
+ pivot_table.header_cells.each do |cell|
+ str << '<cacheField name="' << cell.value << '" numFmtId="0">'
+ str << '<sharedItems count="0">'
+ str << '</sharedItems>'
+ str << '</cacheField>'
+ end
+ str << '</cacheFields>'
+ str << '</pivotCacheDefinition>'
+ end
+
+ end
+end
diff --git a/lib/axlsx/workbook/worksheet/pivot_tables.rb b/lib/axlsx/workbook/worksheet/pivot_tables.rb
new file mode 100644
index 00000000..f5625fc0
--- /dev/null
+++ b/lib/axlsx/workbook/worksheet/pivot_tables.rb
@@ -0,0 +1,24 @@
+module Axlsx
+
+ # A simple, self serializing class for storing pivot tables
+ class PivotTables < SimpleTypedList
+
+ # creates a new Tables object
+ def initialize(worksheet)
+ raise ArgumentError, "you must provide a worksheet" unless worksheet.is_a?(Worksheet)
+ super PivotTable
+ @worksheet = worksheet
+ end
+
+ # The worksheet that owns this collection of pivot tables
+ # @return [Worksheet]
+ attr_reader :worksheet
+
+ # returns the relationships required by this collection
+ def relationships
+ return [] if empty?
+ map{ |pivot_table| Relationship.new(PIVOT_TABLE_R, "../#{pivot_table.pn}") }
+ end
+ end
+
+end
diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb
index 8a595b2d..4c59e340 100644
--- a/lib/axlsx/workbook/worksheet/worksheet.rb
+++ b/lib/axlsx/workbook/worksheet/worksheet.rb
@@ -79,6 +79,12 @@ module Axlsx
@tables ||= Tables.new self
end
+ # The pivot tables in this worksheet
+ # @return [Array] of Table
+ def pivot_tables
+ @pivot_tables ||= PivotTables.new self
+ end
+
# A typed collection of hyperlinks associated with this worksheet
# @return [WorksheetHyperlinks]
def hyperlinks
@@ -454,6 +460,12 @@ module Axlsx
tables.last
end
+ def add_pivot_table(ref, range, options={})
+ pivot_tables << PivotTable.new(ref, range, self, options)
+ yield pivot_tables.last if block_given?
+ pivot_tables.last
+ end
+
# Shortcut to worsksheet_comments#add_comment
def add_comment(options={})
worksheet_comments.add_comment(options)
@@ -539,7 +551,8 @@ module Axlsx
r + [tables.relationships,
worksheet_comments.relationships,
hyperlinks.relationships,
- worksheet_drawing.relationship].flatten.compact || []
+ worksheet_drawing.relationship,
+ pivot_tables.relationships].flatten.compact || []
r
end