axlsx 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +3 -2
- data/CHANGELOG.md +34 -1
- data/README.md +36 -30
- data/Rakefile +1 -1
- data/examples/auto_filter.rb +16 -0
- data/examples/auto_filter.xlsx +0 -0
- data/examples/basic_charts.rb +4 -0
- data/examples/example.rb +41 -13
- data/examples/example.xlsx +0 -0
- data/examples/example_streamed.xlsx +0 -0
- data/examples/extractive.xlsx +0 -0
- data/examples/hyperlinks.rb +23 -0
- data/examples/hyperlinks.xlsx +0 -0
- data/examples/no-use_autowidth.xlsx +0 -0
- data/examples/page_setup.rb +11 -0
- data/examples/page_setup.xlsx +0 -0
- data/examples/shared_strings_example.xlsx +0 -0
- data/examples/skydrive/axlsx.csv +1 -0
- data/examples/skydrive/axlsx.xlsx +0 -0
- data/examples/skydrive/real_example.rb +8 -8
- data/examples/sprk2012/Screen Shot 2012-09-11 at 10.42.06 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-11 at 11.07.48 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-11 at 8.31.50 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-11 at 9.23.27 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-11 at 9.32.06 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-11 at 9.33.35 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-11 at 9.46.44 PM.png +0 -0
- data/examples/sprk2012/Screen Shot 2012-09-12 at 5.07.23 PM.png +0 -0
- data/examples/sprk2012/basics.rb +11 -0
- data/examples/sprk2012/basics.xlsx +0 -0
- data/examples/sprk2012/gravatar.jpeg +0 -0
- data/examples/sprk2012/hair_club.jpg +0 -0
- data/examples/sprk2012/images.rb +9 -0
- data/examples/sprk2012/images.xlsx +0 -0
- data/examples/sprk2012/line_chart.rb +56 -0
- data/examples/sprk2012/line_chart.xlsx +0 -0
- data/examples/sprk2012/sprk2012.key +0 -0
- data/examples/sprk2012/styles.rb +20 -0
- data/examples/sprk2012/styles.xlsx +0 -0
- data/examples/styles.rb +62 -0
- data/examples/styles.xlsx +0 -0
- data/lib/axlsx/doc_props/app.rb +3 -3
- data/lib/axlsx/drawing/axis.rb +1 -1
- data/lib/axlsx/drawing/view_3D.rb +1 -1
- data/lib/axlsx/package.rb +0 -1
- data/lib/axlsx/stylesheet/styles.rb +10 -1
- data/lib/axlsx/util/constants.rb +105 -4
- data/lib/axlsx/util/simple_typed_list.rb +3 -1
- data/lib/axlsx/util/validators.rb +26 -8
- data/lib/axlsx/version.rb +2 -2
- data/lib/axlsx/workbook/workbook.rb +6 -1
- data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +77 -0
- data/lib/axlsx/workbook/worksheet/auto_filter/filter_column.rb +102 -0
- data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +253 -0
- data/lib/axlsx/workbook/worksheet/cell.rb +10 -5
- data/lib/axlsx/workbook/worksheet/date_time_converter.rb +1 -1
- data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +47 -0
- data/lib/axlsx/workbook/worksheet/page_setup.rb +135 -7
- data/lib/axlsx/workbook/worksheet/sheet_calc_pr.rb +49 -0
- data/lib/axlsx/workbook/worksheet/sheet_pr.rb +87 -4
- data/lib/axlsx/workbook/worksheet/table.rb +8 -1
- data/lib/axlsx/workbook/worksheet/table_style_info.rb +68 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +45 -7
- data/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +101 -0
- data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +38 -0
- data/lib/axlsx.rb +8 -1
- data/test/stylesheet/tc_styles.rb +13 -0
- data/test/tc_package.rb +1 -0
- data/test/util/tc_validators.rb +8 -1
- data/test/workbook/worksheet/auto_filter/tc_auto_filter.rb +38 -0
- data/test/workbook/worksheet/auto_filter/tc_filter_column.rb +76 -0
- data/test/workbook/worksheet/auto_filter/tc_filters.rb +50 -0
- data/test/workbook/worksheet/tc_cell.rb +23 -1
- data/test/workbook/worksheet/tc_page_set_up_pr.rb +15 -0
- data/test/workbook/worksheet/tc_page_setup.rb +6 -1
- data/test/workbook/worksheet/tc_sheet_calc_pr.rb +18 -0
- data/test/workbook/worksheet/tc_sheet_pr.rb +27 -0
- data/test/workbook/worksheet/{table/tc_table.rb → tc_table.rb} +6 -1
- data/test/workbook/worksheet/tc_table_style_info.rb +53 -0
- data/test/workbook/worksheet/tc_worksheet.rb +17 -3
- data/test/workbook/worksheet/tc_worksheet_hyperlink.rb +64 -0
- metadata +59 -6
- data/lib/axlsx/workbook/worksheet/auto_filter.rb +0 -34
|
@@ -28,6 +28,7 @@ module Axlsx
|
|
|
28
28
|
@sheet = sheet
|
|
29
29
|
@style = nil
|
|
30
30
|
@sheet.workbook.tables << self
|
|
31
|
+
@table_style_info = TableStyleInfo.new(options[:style_info]) if options[:style_info]
|
|
31
32
|
@name = "Table#{index+1}"
|
|
32
33
|
options.each do |o|
|
|
33
34
|
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
|
@@ -62,6 +63,12 @@ module Axlsx
|
|
|
62
63
|
@name = v
|
|
63
64
|
end
|
|
64
65
|
end
|
|
66
|
+
|
|
67
|
+
# TableStyleInfo for the table.
|
|
68
|
+
# initialization can be fed via the :style_info option
|
|
69
|
+
def table_style_info
|
|
70
|
+
@table_style_info ||= TableStyleInfo.new
|
|
71
|
+
end
|
|
65
72
|
|
|
66
73
|
# Serializes the object
|
|
67
74
|
# @param [String] str
|
|
@@ -77,7 +84,7 @@ module Axlsx
|
|
|
77
84
|
end
|
|
78
85
|
str << '</tableColumns>'
|
|
79
86
|
#TODO implement tableStyleInfo
|
|
80
|
-
str
|
|
87
|
+
table_style_info.to_xml_string(str) # '<tableStyleInfo showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0" name="TableStyleMedium9" />'
|
|
81
88
|
str << '</table>'
|
|
82
89
|
end
|
|
83
90
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
|
|
3
|
+
# The table style info class manages style attributes for defined tables in
|
|
4
|
+
# a worksheet
|
|
5
|
+
class TableStyleInfo
|
|
6
|
+
|
|
7
|
+
# boolean attributes for this object
|
|
8
|
+
BOOLEAN_ATTRIBUTES = %w(show_first_column show_last_column show_row_stripes show_column_stripes)
|
|
9
|
+
|
|
10
|
+
# creates a new TableStyleInfo instance
|
|
11
|
+
# @param [Hash] options
|
|
12
|
+
# @option [Boolean] show_first_column indicates if the first column should
|
|
13
|
+
# be shown
|
|
14
|
+
# @option [Boolean] show_last_column indicates if the last column should
|
|
15
|
+
# be shown
|
|
16
|
+
# @option [Boolean] show_column_stripes indicates if column stripes should
|
|
17
|
+
# be shown
|
|
18
|
+
# @option [Boolean] show_row_stripes indicates if row stripes should be shown
|
|
19
|
+
# @option [String] name The name of the style to apply to your table.
|
|
20
|
+
# Only predefined styles are currently supported.
|
|
21
|
+
# @see Annex G. (normative) Predefined SpreadsheetML Style Definitions in part 1 of the specification.
|
|
22
|
+
def initialize(options = {})
|
|
23
|
+
initialize_defaults
|
|
24
|
+
@name = 'TableStyleMedium9'
|
|
25
|
+
options.each do |k, v|
|
|
26
|
+
send("#{k}=", v) if respond_to? "#{k}="
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Dynamically create accessors for boolean attriubtes
|
|
31
|
+
BOOLEAN_ATTRIBUTES.each do |attr|
|
|
32
|
+
class_eval %{
|
|
33
|
+
# The #{attr} attribute reader
|
|
34
|
+
# @return [Boolean]
|
|
35
|
+
attr_reader :#{attr}
|
|
36
|
+
|
|
37
|
+
# The #{attr} writer
|
|
38
|
+
# @param [Boolean] value The value to assign to #{attr}
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
def #{attr}=(value)
|
|
41
|
+
Axlsx::validate_boolean(value)
|
|
42
|
+
@#{attr} = value
|
|
43
|
+
end
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Initialize all the values to false as Excel requires them to
|
|
48
|
+
# explicitly be disabled or all will show.
|
|
49
|
+
def initialize_defaults
|
|
50
|
+
BOOLEAN_ATTRIBUTES.each do |attr|
|
|
51
|
+
self.send("#{attr}=", 0)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The name of the table style.
|
|
56
|
+
attr_accessor :name
|
|
57
|
+
|
|
58
|
+
# seralizes this object to an xml string
|
|
59
|
+
# @param [String] str the string to contact this objects serialization to.
|
|
60
|
+
def to_xml_string(str = '')
|
|
61
|
+
str << '<tableStyleInfo '
|
|
62
|
+
instance_values.each do |key, value|
|
|
63
|
+
str << Axlsx::camel(key, false) << "='#{value}' "
|
|
64
|
+
end
|
|
65
|
+
str << '/>'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -42,6 +42,12 @@ module Axlsx
|
|
|
42
42
|
def name
|
|
43
43
|
@name ||= "Sheet" + (index+1).to_s
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
# The sheet calculation properties
|
|
47
|
+
# @return [SheetCalcPr]
|
|
48
|
+
def sheet_calc_pr
|
|
49
|
+
@sheet_calc_pr ||= SheetCalcPr.new
|
|
50
|
+
end
|
|
45
51
|
|
|
46
52
|
# The sheet protection object for this workbook
|
|
47
53
|
# @return [SheetProtection]
|
|
@@ -70,7 +76,13 @@ module Axlsx
|
|
|
70
76
|
def tables
|
|
71
77
|
@tables ||= Tables.new self
|
|
72
78
|
end
|
|
73
|
-
|
|
79
|
+
|
|
80
|
+
# A typed collection of hyperlinks associated with this worksheet
|
|
81
|
+
# @return [WorksheetHyperlinks]
|
|
82
|
+
def hyperlinks
|
|
83
|
+
@hyperlinks ||= WorksheetHyperlinks.new self
|
|
84
|
+
end
|
|
85
|
+
|
|
74
86
|
# The a shortcut to the worksheet_comments list of comments
|
|
75
87
|
# @return [Array|SimpleTypedList]
|
|
76
88
|
def comments
|
|
@@ -391,6 +403,14 @@ module Axlsx
|
|
|
391
403
|
data_validations << dv
|
|
392
404
|
end
|
|
393
405
|
|
|
406
|
+
# Adds a new hyperlink to the worksheet
|
|
407
|
+
# @param [Hash] options for the hyperlink
|
|
408
|
+
# @see WorksheetHyperlink for a list of options
|
|
409
|
+
# @return [WorksheetHyperlink]
|
|
410
|
+
def add_hyperlink(options={})
|
|
411
|
+
hyperlinks.add(options)
|
|
412
|
+
end
|
|
413
|
+
|
|
394
414
|
# Adds a chart to this worksheets drawing. This is the recommended way to create charts for your worksheet. This method wraps the complexity of dealing with ooxml drawing, anchors, markers graphic frames chart objects and all the other dirty details.
|
|
395
415
|
# @param [Class] chart_type
|
|
396
416
|
# @option options [Array] start_at
|
|
@@ -477,25 +497,43 @@ module Axlsx
|
|
|
477
497
|
# This intentionally does not use nokogiri for performance reasons
|
|
478
498
|
# @return [String]
|
|
479
499
|
def to_xml_string
|
|
500
|
+
auto_filter.apply if auto_filter.range
|
|
480
501
|
str = '<?xml version="1.0" encoding="UTF-8"?>'
|
|
481
502
|
str << worksheet_node
|
|
482
503
|
serializable_parts.each do |item|
|
|
483
504
|
item.to_xml_string(str) if item
|
|
484
505
|
end
|
|
485
506
|
str << '</worksheet>'
|
|
486
|
-
str
|
|
507
|
+
sanitize(str)
|
|
487
508
|
end
|
|
488
509
|
|
|
510
|
+
# returns the provided string with all invalid control charaters
|
|
511
|
+
# removed.
|
|
512
|
+
# @param [String] str The sting to process
|
|
513
|
+
# @return [String]
|
|
514
|
+
def sanitize(str)
|
|
515
|
+
str.gsub(CONTROL_CHAR_REGEX, '')
|
|
516
|
+
end
|
|
517
|
+
|
|
489
518
|
# The worksheet relationships. This is managed automatically by the worksheet
|
|
490
519
|
# @return [Relationships]
|
|
491
520
|
def relationships
|
|
492
521
|
r = Relationships.new
|
|
493
|
-
r + [tables.relationships,
|
|
494
|
-
worksheet_comments.relationships,
|
|
522
|
+
r + [tables.relationships,
|
|
523
|
+
worksheet_comments.relationships,
|
|
524
|
+
hyperlinks.relationships,
|
|
495
525
|
worksheet_drawing.relationship].flatten.compact || []
|
|
496
526
|
r
|
|
497
527
|
end
|
|
498
528
|
|
|
529
|
+
# identifies the index of an object withing the collections used in generating relationships for the worksheet
|
|
530
|
+
# @param [Any] object the object to search for
|
|
531
|
+
# @return [Integer] The index of the object
|
|
532
|
+
def relationships_index_of(object)
|
|
533
|
+
objects = [tables.to_a, worksheet_comments.comments.to_a, hyperlinks.to_a, worksheet_drawing.drawing].flatten.compact || []
|
|
534
|
+
objects.index(object)
|
|
535
|
+
end
|
|
536
|
+
|
|
499
537
|
# Returns the cell or cells defined using excel style A1:B3 references.
|
|
500
538
|
# @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
|
|
501
539
|
# @return [Cell, Array]
|
|
@@ -533,7 +571,7 @@ module Axlsx
|
|
|
533
571
|
|
|
534
572
|
|
|
535
573
|
private
|
|
536
|
-
|
|
574
|
+
|
|
537
575
|
def validate_sheet_name(name)
|
|
538
576
|
DataTypeValidator.validate "Worksheet.name", String, name
|
|
539
577
|
raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if name.size > 31
|
|
@@ -546,9 +584,9 @@ module Axlsx
|
|
|
546
584
|
|
|
547
585
|
def serializable_parts
|
|
548
586
|
[sheet_pr, dimension, sheet_view, column_info,
|
|
549
|
-
sheet_data, @sheet_protection, protected_ranges,
|
|
587
|
+
sheet_data, sheet_calc_pr, @sheet_protection, protected_ranges,
|
|
550
588
|
auto_filter, merged_cells, conditional_formattings,
|
|
551
|
-
data_validations, print_options, page_margins,
|
|
589
|
+
data_validations, hyperlinks, print_options, page_margins,
|
|
552
590
|
page_setup, worksheet_drawing, worksheet_comments,
|
|
553
591
|
tables]
|
|
554
592
|
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
|
|
3
|
+
# A worksheet hyperlink object. Note that this is not the same as a drawing hyperlink object.
|
|
4
|
+
class WorksheetHyperlink
|
|
5
|
+
|
|
6
|
+
# Creates a new hyperlink object.
|
|
7
|
+
# @note the preferred way to add hyperlinks to your worksheet is the Worksheet#add_hyperlink method
|
|
8
|
+
# @param [Worksheet] worksheet the Worksheet that owns this hyperlink
|
|
9
|
+
# @param [Hash] options options to use when creating this hyperlink
|
|
10
|
+
# @option [String] display Display string, if different from string in string table. This is a property on the hyperlink object, but does not need to appear in the spreadsheet application UI.
|
|
11
|
+
# @option [String] location Location within target. If target is a workbook (or this workbook) this shall refer to a sheet and cell or a defined name. Can also be an HTML anchor if target is HTML file.
|
|
12
|
+
# @option [String] tooltip The tip to display when the user positions the mouse cursor over this hyperlink
|
|
13
|
+
# @option [Symbol] target This is :external by default. If you set it to anything else, the location is interpreted to be the current workbook.
|
|
14
|
+
# @option [String|Cell] ref The location of this hyperlink in the worksheet
|
|
15
|
+
def initialize(worksheet, options={})
|
|
16
|
+
DataTypeValidator.validate "Hyperlink.worksheet", [Worksheet], worksheet
|
|
17
|
+
@worksheet = worksheet
|
|
18
|
+
@target = :external
|
|
19
|
+
options.each do |o|
|
|
20
|
+
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
|
21
|
+
end
|
|
22
|
+
yield self if block_given?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# String attributes for this object
|
|
26
|
+
STRING_ATTRIBUTES = %w(display location tooltip)
|
|
27
|
+
|
|
28
|
+
#Cell location of hyperlink on worksheet.
|
|
29
|
+
# @return [String]
|
|
30
|
+
attr_reader :ref
|
|
31
|
+
|
|
32
|
+
# Sets the target for this hyperlink. Anything other than :external instructs the library to treat the location as an in-workbook reference.
|
|
33
|
+
# @param [Symbol] target
|
|
34
|
+
def target=(target)
|
|
35
|
+
@target = target
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Sets the cell location of this hyperlink in the worksheet
|
|
39
|
+
# @param [String|Cell] cell_reference The string reference or cell that defines where this hyperlink shows in the worksheet.
|
|
40
|
+
def ref=(cell_reference)
|
|
41
|
+
cell_reference = cell_reference.r if cell_reference.is_a?(Cell)
|
|
42
|
+
|
|
43
|
+
Axlsx::validate_string cell_reference
|
|
44
|
+
@ref = cell_reference
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Dynamically create string attribute accessors
|
|
48
|
+
STRING_ATTRIBUTES.each do |attr|
|
|
49
|
+
class_eval %{
|
|
50
|
+
# The #{attr} attribute reader
|
|
51
|
+
# @return [String]
|
|
52
|
+
attr_reader :#{attr}
|
|
53
|
+
|
|
54
|
+
# The #{attr} writer
|
|
55
|
+
# @param [String] value The value to assign to #{attr}
|
|
56
|
+
# @return [String]
|
|
57
|
+
def #{attr}=(value)
|
|
58
|
+
Axlsx::validate_string(value)
|
|
59
|
+
@#{attr}= value
|
|
60
|
+
end
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# The relationship required by this hyperlink when the taget is :external
|
|
65
|
+
# @return [Relationship]
|
|
66
|
+
def relationship
|
|
67
|
+
return unless @target == :external
|
|
68
|
+
Relationship.new HYPERLINK_R, location, :target_mode => :External
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The id of the relationship for this object
|
|
72
|
+
# @return [String]
|
|
73
|
+
def id
|
|
74
|
+
return unless @target == :external
|
|
75
|
+
"rId#{(@worksheet.relationships_index_of(self)+1)}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Seralize the object
|
|
79
|
+
# @param [String] str
|
|
80
|
+
# @return [String]
|
|
81
|
+
def to_xml_string(str='')
|
|
82
|
+
str << '<hyperlink '
|
|
83
|
+
serialization_values.map { |key, value| str << key.to_s << '="' << value.to_s << '" ' }
|
|
84
|
+
str << '/>'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The values to be used in serialization based on the target.
|
|
88
|
+
# location should only be specified for non-external targets.
|
|
89
|
+
# r:id should only be specified for external targets.
|
|
90
|
+
# @return [Hash]
|
|
91
|
+
def serialization_values
|
|
92
|
+
h = instance_values.reject { |key, value| !%w(display ref tooltip).include?(key) }
|
|
93
|
+
if @target == :external
|
|
94
|
+
h['r:id'] = id
|
|
95
|
+
else
|
|
96
|
+
h['location'] = location
|
|
97
|
+
end
|
|
98
|
+
h
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Axlsx
|
|
2
|
+
|
|
3
|
+
#A collection of hyperlink objects for a worksheet
|
|
4
|
+
class WorksheetHyperlinks < SimpleTypedList
|
|
5
|
+
|
|
6
|
+
# Creates a new Hyperlinks collection
|
|
7
|
+
# @param [Worksheet] worksheet the worksheet that owns these hyperlinks
|
|
8
|
+
def initialize(worksheet)
|
|
9
|
+
DataTypeValidator.validate "Hyperlinks.worksheet", [Worksheet], worksheet
|
|
10
|
+
@worksheet = worksheet
|
|
11
|
+
super WorksheetHyperlink
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Creates and adds a new hyperlink based on the options provided
|
|
15
|
+
# @see WorksheetHyperlink#initialize
|
|
16
|
+
# @return [WorksheetHyperlink]
|
|
17
|
+
def add(options)
|
|
18
|
+
@list << WorksheetHyperlink.new(@worksheet, options)
|
|
19
|
+
@list.last
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The relationships required by this collection's hyperlinks
|
|
23
|
+
# @return Array
|
|
24
|
+
def relationships
|
|
25
|
+
return [] if empty?
|
|
26
|
+
map { |hyperlink| hyperlink.relationship }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# seralize the collection of hyperlinks
|
|
30
|
+
# @return [String]
|
|
31
|
+
def to_xml_string(str='')
|
|
32
|
+
return if empty?
|
|
33
|
+
str << '<hyperlinks>'
|
|
34
|
+
@list.each { |hyperlink| hyperlink.to_xml_string(str) }
|
|
35
|
+
str << '</hyperlinks>'
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/axlsx.rb
CHANGED
|
@@ -37,7 +37,12 @@ if !Object.respond_to?(:instance_values)
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
# xlsx generation with charts, images, automated column width, customizable styles
|
|
40
|
+
# xlsx generation with charts, images, automated column width, customizable styles
|
|
41
|
+
# and full schema validation. Axlsx excels at helping you generate beautiful
|
|
42
|
+
# Office Open XML Spreadsheet documents without having to understand the entire
|
|
43
|
+
# ECMA specification. Check out the README for some examples of how easy it is.
|
|
44
|
+
# Best of all, you can validate your xlsx file before serialization so you know
|
|
45
|
+
# for sure that anything generated is going to load on your client's machine.
|
|
41
46
|
module Axlsx
|
|
42
47
|
|
|
43
48
|
# determines the cell range for the items provided
|
|
@@ -65,6 +70,7 @@ module Axlsx
|
|
|
65
70
|
# returns the x, y position of a cell
|
|
66
71
|
def self.name_to_indices(name)
|
|
67
72
|
raise ArgumentError, 'invalid cell name' unless name.size > 1
|
|
73
|
+
# capitalization?!?
|
|
68
74
|
v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |val, c|
|
|
69
75
|
val[:i] += ((c.bytes.first - 64) * val[:base]); val[:base] *= 26; val
|
|
70
76
|
end
|
|
@@ -95,6 +101,7 @@ module Axlsx
|
|
|
95
101
|
# @param [String] s The snake case string to camelize
|
|
96
102
|
# @return [String]
|
|
97
103
|
def self.camel(s="", all_caps = true)
|
|
104
|
+
s = s.to_s
|
|
98
105
|
s = s.capitalize if all_caps
|
|
99
106
|
s.gsub(/_(.)/){ $1.upcase }
|
|
100
107
|
end
|
|
@@ -105,6 +105,19 @@ class TestStyles < Test::Unit::TestCase
|
|
|
105
105
|
assert(@styles.parse_alignment_options(:alignment => {}).is_a?(Axlsx::CellAlignment))
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
+
def test_parse_font_using_defaults
|
|
109
|
+
original = @styles.fonts.first
|
|
110
|
+
@styles.add_style :b => 1, :sz => 99
|
|
111
|
+
created = @styles.fonts.last
|
|
112
|
+
original_attributes = original.instance_values
|
|
113
|
+
assert_equal(1, created.b)
|
|
114
|
+
assert_equal(99, created.sz)
|
|
115
|
+
copied = original_attributes.reject{ |key, value| %w(b sz).include? key }
|
|
116
|
+
copied.each do |key, value|
|
|
117
|
+
assert_equal(created.instance_values[key], value)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
108
121
|
def test_parse_font_options
|
|
109
122
|
options = {
|
|
110
123
|
:fg_color => "FF050505",
|
data/test/tc_package.rb
CHANGED
|
@@ -7,6 +7,7 @@ class TestPackage < Test::Unit::TestCase
|
|
|
7
7
|
ws = @package.workbook.add_worksheet
|
|
8
8
|
ws.add_row ['Can', 'we', 'build it?']
|
|
9
9
|
ws.add_row ['Yes!', 'We', 'can!']
|
|
10
|
+
ws.add_hyperlink :ref => ws.rows.first.cells.last, :location => 'https://github.com/randym'
|
|
10
11
|
ws.workbook.add_defined_name("#{ws.name}!A1:C2", :name => '_xlnm.Print_Titles', :hidden => true)
|
|
11
12
|
ws.protect_range('A1:C1')
|
|
12
13
|
ws.protect_range(ws.rows.last.cells)
|
data/test/util/tc_validators.rb
CHANGED
|
@@ -158,4 +158,11 @@ class TestValidators < Test::Unit::TestCase
|
|
|
158
158
|
assert_raise(ArgumentError) { Axlsx.validate_split_state_type 'frozen_split' }
|
|
159
159
|
assert_raise(ArgumentError) { Axlsx.validate_split_state_type 0 }
|
|
160
160
|
end
|
|
161
|
-
|
|
161
|
+
|
|
162
|
+
def test_range_validation
|
|
163
|
+
# exclusive
|
|
164
|
+
assert_raise(ArgumentError) { Axlsx::RangeValidator.validate('foo', 1, 10, 10, false) }
|
|
165
|
+
# inclusive by default
|
|
166
|
+
assert_nothing_raised { Axlsx::RangeValidator.validate('foo', 1, 10, 10) }
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'tc_helper.rb'
|
|
2
|
+
|
|
3
|
+
class TestAutoFilter < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
ws = Axlsx::Package.new.workbook.add_worksheet
|
|
7
|
+
3.times { |index| ws.add_row [1*index,2*index,3*index] }
|
|
8
|
+
@auto_filter = ws.auto_filter
|
|
9
|
+
@auto_filter.range = 'A1:C3'
|
|
10
|
+
@auto_filter.add_column 0, :filters, :filter_items => [1]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_defined_name
|
|
14
|
+
assert_equal("'Sheet1'!$A$1:$C$3", @auto_filter.defined_name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_to_xml_string
|
|
18
|
+
doc = Nokogiri::XML(@auto_filter.to_xml_string)
|
|
19
|
+
assert(doc.xpath("autoFilter[@ref='#{@auto_filter.range}']"))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_columns
|
|
23
|
+
assert @auto_filter.columns.is_a?(Axlsx::SimpleTypedList)
|
|
24
|
+
assert_equal @auto_filter.columns.allowed_types, [Axlsx::FilterColumn]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_add_column
|
|
28
|
+
@auto_filter.add_column(0, :filters) do |column|
|
|
29
|
+
assert column.is_a? FilterColumn
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_applya
|
|
34
|
+
assert_equal nil, @auto_filter.worksheet.rows.last.hidden
|
|
35
|
+
@auto_filter.apply
|
|
36
|
+
assert_equal true, @auto_filter.worksheet.rows.last.hidden
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'tc_helper.rb'
|
|
2
|
+
|
|
3
|
+
class TestFilterColumn < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@filter_column = Axlsx::FilterColumn.new(0, :filters, :filter_items => [200])
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_initialize_col_id
|
|
11
|
+
assert_raise ArgumentError do
|
|
12
|
+
Axlsx::FilterColumn.new(0, :bobs_house_of_filter)
|
|
13
|
+
end
|
|
14
|
+
assert_raise ArgumentError do
|
|
15
|
+
Axlsx::FilterColumn.new(:penut, :filters)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_initailize_filter_type
|
|
20
|
+
assert @filter_column.filter.is_a?(Axlsx::Filters)
|
|
21
|
+
assert_equal 1, @filter_column.filter.filter_items.size
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_initialize_filter_type_filters_with_options
|
|
25
|
+
assert_equal 200, @filter_column.filter.filter_items.first.val
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_initialize_with_block
|
|
29
|
+
filter_column = Axlsx::FilterColumn.new(0, :filters) do |filters|
|
|
30
|
+
filters.filter_items = [700, 100, 5]
|
|
31
|
+
end
|
|
32
|
+
assert_equal 3, filter_column.filter.filter_items.size
|
|
33
|
+
assert_equal 700, filter_column.filter.filter_items.first.val
|
|
34
|
+
assert_equal 5, filter_column.filter.filter_items.last.val
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_default_show_button
|
|
38
|
+
assert_equal true, @filter_column.show_button
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_default_hidden_button
|
|
42
|
+
assert_equal false, @filter_column.hidden_button
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_show_button
|
|
46
|
+
assert_raise ArgumentError do
|
|
47
|
+
@filter_column.show_button = :foo
|
|
48
|
+
end
|
|
49
|
+
assert_nothing_raised { @filter_column.show_button = false }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_hidden_button
|
|
53
|
+
assert_raise ArgumentError do
|
|
54
|
+
@filter_column.hidden_button = :hoge
|
|
55
|
+
end
|
|
56
|
+
assert_nothing_raised { @filter_column.hidden_button = true }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_col_id=
|
|
60
|
+
assert_raise ArgumentError do
|
|
61
|
+
@filter_column.col_id = :bar
|
|
62
|
+
end
|
|
63
|
+
assert_nothing_raised { @filter_column.col_id = 7 }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_to_xml_string
|
|
67
|
+
doc = Nokogiri::XML(@filter_column.to_xml_string)
|
|
68
|
+
assert doc.xpath("//filterColumn[@colId=#{@filter_column.col_id}]")
|
|
69
|
+
assert doc.xpath("//filterColumn[@hiddenButton=#{@filter_column.hidden_button}]")
|
|
70
|
+
assert doc.xpath("//filterColumn[@showButton=#{@filter_column.show_button}]")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
assert doc.xpath("//filterColumn/filters")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'tc_helper.rb'
|
|
2
|
+
|
|
3
|
+
class TestFilters < Test::Unit::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@filters = Axlsx::Filters.new(:filter_items => [1, 'a'],
|
|
6
|
+
:date_group_items =>[ { :date_time_grouping => :year, :year => 2011, :month => 11, :day => 11, :hour => 0, :minute => 0, :second => 0 } ] ,
|
|
7
|
+
:blank => true)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_blank
|
|
11
|
+
assert_equal true, @filters.blank
|
|
12
|
+
assert_raise(ArgumentError) { @filters.blank = :only_if_you_want_it }
|
|
13
|
+
@filters.blank = true
|
|
14
|
+
assert_equal true, @filters.blank
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_calendar_type
|
|
18
|
+
assert_raise(ArgumentError) { @filters.calendar_type = 'monkey calendar' }
|
|
19
|
+
@filters.calendar_type = 'japan'
|
|
20
|
+
assert_equal('japan', @filters.calendar_type)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_filters_items
|
|
24
|
+
assert @filters.filter_items.is_a?(Array)
|
|
25
|
+
assert_equal 2, @filters.filter_items.size
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_date_group_items
|
|
29
|
+
assert @filters.date_group_items.is_a?(Array)
|
|
30
|
+
assert_equal 1, @filters.date_group_items.size
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_apply_is_false_for_matching_values
|
|
34
|
+
keeper = Object.new
|
|
35
|
+
def keeper.value; 'a'; end
|
|
36
|
+
assert_equal false, @filters.apply(keeper)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_apply_is_true_for_non_matching_values
|
|
40
|
+
hidden = Object.new
|
|
41
|
+
def hidden.value; 'b'; end
|
|
42
|
+
assert_equal true, @filters.apply(hidden)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_to_xml_string
|
|
46
|
+
doc = Nokogiri::XML(@filters.to_xml_string)
|
|
47
|
+
assert_equal(1, doc.xpath('//filters[@blank="true"]').size)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
@@ -60,7 +60,7 @@ class TestCell < Test::Unit::TestCase
|
|
|
60
60
|
assert_raise(ArgumentError, "type must be :string, :integer, :float, :date, :time, :boolean") { @c.type = :array }
|
|
61
61
|
assert_nothing_raised("type can be changed") { @c.type = :string }
|
|
62
62
|
assert_equal(@c.value, "1.0", "changing type casts the value")
|
|
63
|
-
|
|
63
|
+
assert_equal(:float, @row.add_cell(1.0/10**7).type, 'properly identify exponential floats as float type')
|
|
64
64
|
assert_equal(@row.add_cell(Time.now).type, :time, 'time should be time')
|
|
65
65
|
assert_equal(@row.add_cell(Date.today).type, :date, 'date should be date')
|
|
66
66
|
assert_equal(@row.add_cell(true).type, :boolean, 'boolean should be boolean')
|
|
@@ -88,6 +88,7 @@ class TestCell < Test::Unit::TestCase
|
|
|
88
88
|
assert_equal(@c.send(:cell_type_from_value, -1), :integer)
|
|
89
89
|
assert_equal(@c.send(:cell_type_from_value, true), :boolean)
|
|
90
90
|
assert_equal(@c.send(:cell_type_from_value, false), :boolean)
|
|
91
|
+
assert_equal(@c.send(:cell_type_from_value, 1.0/10**6), :float)
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
def test_cast_value
|
|
@@ -268,6 +269,27 @@ class TestCell < Test::Unit::TestCase
|
|
|
268
269
|
|
|
269
270
|
end
|
|
270
271
|
|
|
272
|
+
def test_font_size_with_custom_style_and_no_sz
|
|
273
|
+
@c.style = @c.row.worksheet.workbook.styles.add_style :bg_color => 'FF00FF'
|
|
274
|
+
sz = @c.send(:font_size)
|
|
275
|
+
assert_equal(sz, @c.row.worksheet.workbook.styles.fonts.first.sz)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def test_font_size_with_bolding
|
|
279
|
+
@c.style = @c.row.worksheet.workbook.styles.add_style :b => true
|
|
280
|
+
assert_equal(@c.row.worksheet.workbook.styles.fonts.first.sz * 1.5, @c.send(:font_size))
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def test_font_size_with_custom_sz
|
|
284
|
+
@c.style = @c.row.worksheet.workbook.styles.add_style :sz => 52
|
|
285
|
+
sz = @c.send(:font_size)
|
|
286
|
+
assert_equal(sz, 52)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def test_cell_with_sz
|
|
290
|
+
@c.sz = 25
|
|
291
|
+
assert_equal(25, @c.send(:font_size))
|
|
292
|
+
end
|
|
271
293
|
def test_to_xml
|
|
272
294
|
# TODO This could use some much more stringent testing related to the xml content generated!
|
|
273
295
|
@ws.add_row [Time.now, Date.today, true, 1, 1.0, "text", "=sum(A1:A2)"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'tc_helper.rb'
|
|
2
|
+
|
|
3
|
+
class TestPageSetUpPr < Test::Unit::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@page_setup_pr = Axlsx::PageSetUpPr.new(:fit_to_page => true, :auto_page_breaks => true)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def test_fit_to_page
|
|
9
|
+
assert_equal true, @page_setup_pr.fit_to_page
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_auto_page_breaks
|
|
13
|
+
assert_equal true, @page_setup_pr.auto_page_breaks
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -44,6 +44,11 @@ class TestPageSetup < Test::Unit::TestCase
|
|
|
44
44
|
assert_equal(50, @ps.scale)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def test_paper_size
|
|
48
|
+
assert_raise(ArgumentError) { @ps.paper_size = 119 }
|
|
49
|
+
assert_nothing_raised { @ps.paper_size = 10 }
|
|
50
|
+
end
|
|
51
|
+
|
|
47
52
|
def test_set_some_values
|
|
48
53
|
@ps.set(:fit_to_width => 2, :orientation => :portrait)
|
|
49
54
|
assert_equal(2, @ps.fit_to_width)
|
|
@@ -58,7 +63,7 @@ class TestPageSetup < Test::Unit::TestCase
|
|
|
58
63
|
assert(@ps.fit_to_width == nil && @ps.fit_to_height == nil)
|
|
59
64
|
assert(@ps.fit_to_page? == false)
|
|
60
65
|
end
|
|
61
|
-
|
|
66
|
+
|
|
62
67
|
def test_with_height_fit_to_page?
|
|
63
68
|
assert(@ps.fit_to_width == nil && @ps.fit_to_height == nil)
|
|
64
69
|
@ps.set(:fit_to_height => 1)
|