axlsx 1.2.3 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.yardopts +3 -2
  2. data/CHANGELOG.md +34 -1
  3. data/README.md +26 -37
  4. data/Rakefile +1 -1
  5. data/examples/auto_filter.rb +16 -0
  6. data/examples/auto_filter.xlsx +0 -0
  7. data/examples/example.rb +3 -2
  8. data/examples/example.xlsx +0 -0
  9. data/examples/example_streamed.xlsx +0 -0
  10. data/examples/no-use_autowidth.xlsx +0 -0
  11. data/examples/shared_strings_example.xlsx +0 -0
  12. data/examples/skydrive/real_example.rb +6 -6
  13. data/examples/sprk2012/Screen Shot 2012-09-11 at 10.42.06 PM.png +0 -0
  14. data/examples/sprk2012/Screen Shot 2012-09-11 at 11.07.48 PM.png +0 -0
  15. data/examples/sprk2012/Screen Shot 2012-09-11 at 8.31.50 PM.png +0 -0
  16. data/examples/sprk2012/Screen Shot 2012-09-11 at 9.23.27 PM.png +0 -0
  17. data/examples/sprk2012/Screen Shot 2012-09-11 at 9.32.06 PM.png +0 -0
  18. data/examples/sprk2012/Screen Shot 2012-09-11 at 9.33.35 PM.png +0 -0
  19. data/examples/sprk2012/Screen Shot 2012-09-11 at 9.46.44 PM.png +0 -0
  20. data/examples/sprk2012/Screen Shot 2012-09-12 at 5.07.23 PM.png +0 -0
  21. data/examples/sprk2012/basics.rb +1 -0
  22. data/examples/sprk2012/basics.xlsx +0 -0
  23. data/examples/sprk2012/gravatar.jpeg +0 -0
  24. data/examples/sprk2012/hair_club.jpg +0 -0
  25. data/examples/sprk2012/images.rb +7 -12
  26. data/examples/sprk2012/images.xlsx +0 -0
  27. data/examples/sprk2012/line_chart.rb +56 -0
  28. data/examples/sprk2012/line_chart.xlsx +0 -0
  29. data/examples/sprk2012/sprk2012.key +0 -0
  30. data/examples/sprk2012/styles.rb +13 -12
  31. data/examples/sprk2012/styles.xlsx +0 -0
  32. data/examples/styles.rb +62 -0
  33. data/examples/styles.xlsx +0 -0
  34. data/lib/axlsx.rb +8 -1
  35. data/lib/axlsx/stylesheet/styles.rb +4 -0
  36. data/lib/axlsx/util/constants.rb +90 -5
  37. data/lib/axlsx/util/validators.rb +26 -8
  38. data/lib/axlsx/version.rb +2 -2
  39. data/lib/axlsx/workbook/workbook.rb +4 -1
  40. data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +77 -0
  41. data/lib/axlsx/workbook/worksheet/auto_filter/filter_column.rb +102 -0
  42. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +253 -0
  43. data/lib/axlsx/workbook/worksheet/cell.rb +9 -4
  44. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +1 -1
  45. data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +47 -0
  46. data/lib/axlsx/workbook/worksheet/sheet_calc_pr.rb +49 -0
  47. data/lib/axlsx/workbook/worksheet/sheet_pr.rb +87 -4
  48. data/lib/axlsx/workbook/worksheet/table.rb +8 -1
  49. data/lib/axlsx/workbook/worksheet/table_style_info.rb +68 -0
  50. data/lib/axlsx/workbook/worksheet/worksheet.rb +18 -3
  51. data/test/stylesheet/tc_styles.rb +13 -0
  52. data/test/util/tc_validators.rb +8 -1
  53. data/test/workbook/worksheet/auto_filter/tc_auto_filter.rb +38 -0
  54. data/test/workbook/worksheet/auto_filter/tc_filter_column.rb +76 -0
  55. data/test/workbook/worksheet/auto_filter/tc_filters.rb +50 -0
  56. data/test/workbook/worksheet/tc_cell.rb +5 -0
  57. data/test/workbook/worksheet/tc_page_set_up_pr.rb +15 -0
  58. data/test/workbook/worksheet/tc_sheet_calc_pr.rb +18 -0
  59. data/test/workbook/worksheet/tc_sheet_pr.rb +27 -0
  60. data/test/workbook/worksheet/{table/tc_table.rb → tc_table.rb} +6 -1
  61. data/test/workbook/worksheet/tc_table_style_info.rb +53 -0
  62. data/test/workbook/worksheet/tc_worksheet.rb +17 -3
  63. metadata +45 -7
  64. data/examples/~$extractive.xlsx +0 -0
  65. data/lib/axlsx/workbook/worksheet/auto_filter.rb +0 -35
@@ -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 << '<tableStyleInfo showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0" name="TableStyleMedium9" />'
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]
@@ -491,15 +497,24 @@ module Axlsx
491
497
  # This intentionally does not use nokogiri for performance reasons
492
498
  # @return [String]
493
499
  def to_xml_string
500
+ auto_filter.apply if auto_filter.range
494
501
  str = '<?xml version="1.0" encoding="UTF-8"?>'
495
502
  str << worksheet_node
496
503
  serializable_parts.each do |item|
497
504
  item.to_xml_string(str) if item
498
505
  end
499
506
  str << '</worksheet>'
500
- str.gsub(/[[:cntrl:]]/,'')
507
+ sanitize(str)
501
508
  end
502
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
+
503
518
  # The worksheet relationships. This is managed automatically by the worksheet
504
519
  # @return [Relationships]
505
520
  def relationships
@@ -556,7 +571,7 @@ module Axlsx
556
571
 
557
572
 
558
573
  private
559
-
574
+
560
575
  def validate_sheet_name(name)
561
576
  DataTypeValidator.validate "Worksheet.name", String, name
562
577
  raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if name.size > 31
@@ -569,7 +584,7 @@ module Axlsx
569
584
 
570
585
  def serializable_parts
571
586
  [sheet_pr, dimension, sheet_view, column_info,
572
- sheet_data, @sheet_protection, protected_ranges,
587
+ sheet_data, sheet_calc_pr, @sheet_protection, protected_ranges,
573
588
  auto_filter, merged_cells, conditional_formattings,
574
589
  data_validations, hyperlinks, print_options, page_margins,
575
590
  page_setup, worksheet_drawing, worksheet_comments,
@@ -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",
@@ -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
- end
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
+
@@ -275,6 +275,11 @@ class TestCell < Test::Unit::TestCase
275
275
  assert_equal(sz, @c.row.worksheet.workbook.styles.fonts.first.sz)
276
276
  end
277
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
+
278
283
  def test_font_size_with_custom_sz
279
284
  @c.style = @c.row.worksheet.workbook.styles.add_style :sz => 52
280
285
  sz = @c.send(:font_size)
@@ -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
@@ -0,0 +1,18 @@
1
+ require 'tc_helper'
2
+
3
+ class TestSheetCalcPr < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @sheet_calc_pr = Axlsx::SheetCalcPr.new(:full_calc_on_load => false)
7
+ end
8
+
9
+ def test_full_calc_on_load
10
+ assert_equal false, @sheet_calc_pr.full_calc_on_load
11
+ assert Axlsx::SheetCalcPr.new.full_calc_on_load
12
+ end
13
+
14
+ def test_to_xml_string
15
+ doc = Nokogiri::XML(@sheet_calc_pr.to_xml_string)
16
+ assert_equal 1, doc.xpath('//sheetCalcPr[@fullCalcOnLoad="false"]').size
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ require 'tc_helper.rb'
2
+
3
+
4
+ class TestSheetPr < Test::Unit::TestCase
5
+
6
+ def setup
7
+ worksheet = Axlsx::Package.new.workbook.add_worksheet
8
+ @options = {
9
+ :sync_horizontal => false,
10
+ :sync_vertical => false,
11
+ :transtion_evaluation => true,
12
+ :transition_entry => true,
13
+ :published => false,
14
+ :filter_mode => true,
15
+ :enable_format_conditions_calculation => false,
16
+ :code_name => '007',
17
+ :sync_ref => 'foo'
18
+ }
19
+ @sheet_pr = Axlsx::SheetPr.new(worksheet, @options)
20
+ end
21
+
22
+ def test_initialization
23
+ @options.each do |key, value|
24
+ assert_equal value, @sheet_pr.send(key)
25
+ end
26
+ end
27
+ end
@@ -15,6 +15,12 @@ class TestTable < Test::Unit::TestCase
15
15
 
16
16
  end
17
17
 
18
+ def test_table_style_info
19
+ table = @ws.add_table('A1:D5', :name => 'foo', :style_info => { :show_row_stripes => true, :name => "TableStyleMedium25" })
20
+ assert_equal('TableStyleMedium25', table.table_style_info.name)
21
+ assert_equal(true, table.table_style_info.show_row_stripes)
22
+ end
23
+
18
24
  def test_add_table
19
25
  name = "test"
20
26
  table = @ws.add_table("A1:D5", :name => name)
@@ -22,7 +28,6 @@ class TestTable < Test::Unit::TestCase
22
28
  assert_equal(@ws.workbook.tables.last, table, "must be added to workbook table collection")
23
29
  assert_equal(@ws.tables.last, table, "must be added to worksheet table collection")
24
30
  assert_equal(table.name, name, "options for name are applied")
25
-
26
31
  end
27
32
 
28
33
  def test_pn