spreadsheet_architect 3.2.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +57 -69
  4. data/lib/spreadsheet_architect.rb +5 -0
  5. data/lib/spreadsheet_architect/axlsx_string_width_patch.rb +23 -0
  6. data/lib/spreadsheet_architect/class_methods/csv.rb +2 -0
  7. data/lib/spreadsheet_architect/class_methods/ods.rb +9 -9
  8. data/lib/spreadsheet_architect/class_methods/xlsx.rb +62 -6
  9. data/lib/spreadsheet_architect/utils.rb +72 -15
  10. data/lib/spreadsheet_architect/utils/xlsx.rb +2 -13
  11. data/lib/spreadsheet_architect/version.rb +1 -1
  12. data/test/dummy_app/app/assets/config/manifest.js +3 -0
  13. data/test/dummy_app/config/application.rb +2 -5
  14. data/test/dummy_app/db/test.sqlite3 +0 -0
  15. data/test/dummy_app/log/test.log +85059 -24302
  16. data/test/dummy_app/tmp/3.0.0.pre/integration/alt_xlsx.xlsx +0 -0
  17. data/test/dummy_app/tmp/3.0.0.pre/integration/csv.csv +6 -0
  18. data/test/dummy_app/tmp/3.0.0.pre/integration/ods.ods +0 -0
  19. data/test/dummy_app/tmp/3.0.0.pre/integration/xlsx.xlsx +0 -0
  20. data/test/dummy_app/tmp/3.0.0.pre/kitchen_sink.ods +0 -0
  21. data/test/dummy_app/tmp/3.0.0.pre/kitchen_sink.xlsx +0 -0
  22. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/data.csv +3 -0
  23. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/data.ods +0 -0
  24. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/data.xlsx +0 -0
  25. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/empty.csv +1 -0
  26. data/test/dummy_app/tmp/{configurations/LegacyPlainRubyObject → 3.0.0.pre/models/ActiveModelObject}/empty.ods +0 -0
  27. data/test/dummy_app/tmp/{configurations → 3.0.0.pre/models}/ActiveModelObject/empty.xlsx +0 -0
  28. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/instances.csv +6 -0
  29. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/instances.ods +0 -0
  30. data/test/dummy_app/tmp/3.0.0.pre/models/ActiveModelObject/instances.xlsx +0 -0
  31. data/test/dummy_app/tmp/3.0.0.pre/models/CustomColumnsMethodPost/data.csv +3 -0
  32. data/test/dummy_app/tmp/3.0.0.pre/models/CustomColumnsMethodPost/data.ods +0 -0
  33. data/test/dummy_app/tmp/3.0.0.pre/models/CustomColumnsMethodPost/data.xlsx +0 -0
  34. data/test/dummy_app/tmp/{configurations/ActiveModelObject → 3.0.0.pre/models/CustomColumnsMethodPost}/empty.csv +0 -0
  35. data/test/dummy_app/tmp/{configurations/Post → 3.0.0.pre/models/CustomColumnsMethodPost}/empty.ods +0 -0
  36. data/test/dummy_app/tmp/{configurations/CustomPost → 3.0.0.pre/models/CustomColumnsMethodPost}/empty.xlsx +0 -0
  37. data/test/dummy_app/tmp/3.0.0.pre/models/CustomColumnsMethodPost/instances.csv +6 -0
  38. data/test/dummy_app/tmp/3.0.0.pre/models/CustomColumnsMethodPost/instances.ods +0 -0
  39. data/test/dummy_app/tmp/3.0.0.pre/models/CustomColumnsMethodPost/instances.xlsx +0 -0
  40. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/data.csv +3 -0
  41. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/data.ods +0 -0
  42. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/data.xlsx +0 -0
  43. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/empty.csv +1 -0
  44. data/test/dummy_app/tmp/{configurations/ActiveModelObject → 3.0.0.pre/models/CustomPost}/empty.ods +0 -0
  45. data/test/dummy_app/tmp/{configurations/LegacyPlainRubyObject → 3.0.0.pre/models/CustomPost}/empty.xlsx +0 -0
  46. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/instances.csv +6 -0
  47. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/instances.ods +0 -0
  48. data/test/dummy_app/tmp/3.0.0.pre/models/CustomPost/instances.xlsx +0 -0
  49. data/test/dummy_app/tmp/3.0.0.pre/models/LegacyPlainRubyObject/data.csv +3 -0
  50. data/test/dummy_app/tmp/3.0.0.pre/models/LegacyPlainRubyObject/data.ods +0 -0
  51. data/test/dummy_app/tmp/3.0.0.pre/models/LegacyPlainRubyObject/data.xlsx +0 -0
  52. data/test/dummy_app/tmp/{configurations/CustomPost → 3.0.0.pre/models/LegacyPlainRubyObject}/empty.csv +0 -0
  53. data/test/dummy_app/tmp/{configurations/SpreadsheetArchitect → 3.0.0.pre/models/LegacyPlainRubyObject}/empty.ods +0 -0
  54. data/test/dummy_app/tmp/3.0.0.pre/models/LegacyPlainRubyObject/empty.xlsx +0 -0
  55. data/test/dummy_app/tmp/3.0.0.pre/models/LegacyPlainRubyObject/instances.csv +6 -0
  56. data/test/dummy_app/tmp/3.0.0.pre/models/LegacyPlainRubyObject/instances.ods +0 -0
  57. data/test/dummy_app/tmp/{configurations → 3.0.0.pre/models}/LegacyPlainRubyObject/instances.xlsx +0 -0
  58. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/data.csv +3 -0
  59. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/data.ods +0 -0
  60. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/data.xlsx +0 -0
  61. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/empty.csv +1 -0
  62. data/test/dummy_app/tmp/{configurations/CustomPost → 3.0.0.pre/models/PlainRubyObject}/empty.ods +0 -0
  63. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/empty.xlsx +0 -0
  64. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/instances.csv +6 -0
  65. data/test/dummy_app/tmp/3.0.0.pre/models/PlainRubyObject/instances.ods +0 -0
  66. data/test/dummy_app/tmp/{configurations → 3.0.0.pre/models}/PlainRubyObject/instances.xlsx +0 -0
  67. data/test/dummy_app/tmp/3.0.0.pre/models/Post/data.csv +3 -0
  68. data/test/dummy_app/tmp/3.0.0.pre/models/Post/data.ods +0 -0
  69. data/test/dummy_app/tmp/3.0.0.pre/models/Post/data.xlsx +0 -0
  70. data/test/dummy_app/tmp/{configurations/LegacyPlainRubyObject → 3.0.0.pre/models/Post}/empty.csv +0 -0
  71. data/test/dummy_app/tmp/{configurations/PlainRubyObject → 3.0.0.pre/models/Post}/empty.ods +0 -0
  72. data/test/dummy_app/tmp/3.0.0.pre/models/Post/empty.xlsx +0 -0
  73. data/test/dummy_app/tmp/3.0.0.pre/models/Post/instances.csv +6 -0
  74. data/test/dummy_app/tmp/3.0.0.pre/models/Post/instances.ods +0 -0
  75. data/test/dummy_app/tmp/3.0.0.pre/models/Post/instances.xlsx +0 -0
  76. data/test/dummy_app/tmp/3.0.0.pre/multi_sheet.ods +0 -0
  77. data/test/dummy_app/tmp/3.0.0.pre/multi_sheet.xlsx +0 -0
  78. data/test/test_helper.rb +8 -8
  79. data/test/unit/exceptions_test.rb +9 -1
  80. data/test/unit/kitchen_sink_test.rb +14 -8
  81. data/test/unit/regressions_test.rb +11 -0
  82. data/test/unit/utils_test.rb +35 -1
  83. data/test/unit/xlsx_freeze_test.rb +44 -0
  84. data/test/unit/xlsx_utils_test.rb +0 -21
  85. metadata +166 -152
  86. data/lib/spreadsheet_architect/monkey_patches/axlsx_column_width.rb +0 -56
  87. data/test/custom_assertions.rb +0 -21
  88. data/test/dummy_app/log/development.log +0 -28
  89. data/test/dummy_app/tmp/configurations/ActiveModelObject/data.csv +0 -3
  90. data/test/dummy_app/tmp/configurations/ActiveModelObject/data.ods +0 -0
  91. data/test/dummy_app/tmp/configurations/ActiveModelObject/data.xlsx +0 -0
  92. data/test/dummy_app/tmp/configurations/ActiveModelObject/instances.csv +0 -6
  93. data/test/dummy_app/tmp/configurations/ActiveModelObject/instances.ods +0 -0
  94. data/test/dummy_app/tmp/configurations/ActiveModelObject/instances.xlsx +0 -0
  95. data/test/dummy_app/tmp/configurations/CustomPost/data.csv +0 -3
  96. data/test/dummy_app/tmp/configurations/CustomPost/data.ods +0 -0
  97. data/test/dummy_app/tmp/configurations/CustomPost/data.xlsx +0 -0
  98. data/test/dummy_app/tmp/configurations/CustomPost/instances.csv +0 -6
  99. data/test/dummy_app/tmp/configurations/CustomPost/instances.ods +0 -0
  100. data/test/dummy_app/tmp/configurations/CustomPost/instances.xlsx +0 -0
  101. data/test/dummy_app/tmp/configurations/LegacyPlainRubyObject/data.csv +0 -3
  102. data/test/dummy_app/tmp/configurations/LegacyPlainRubyObject/data.ods +0 -0
  103. data/test/dummy_app/tmp/configurations/LegacyPlainRubyObject/data.xlsx +0 -0
  104. data/test/dummy_app/tmp/configurations/LegacyPlainRubyObject/instances.csv +0 -6
  105. data/test/dummy_app/tmp/configurations/LegacyPlainRubyObject/instances.ods +0 -0
  106. data/test/dummy_app/tmp/configurations/PlainRubyObject/data.csv +0 -3
  107. data/test/dummy_app/tmp/configurations/PlainRubyObject/data.ods +0 -0
  108. data/test/dummy_app/tmp/configurations/PlainRubyObject/data.xlsx +0 -0
  109. data/test/dummy_app/tmp/configurations/PlainRubyObject/empty.csv +0 -0
  110. data/test/dummy_app/tmp/configurations/PlainRubyObject/empty.xlsx +0 -0
  111. data/test/dummy_app/tmp/configurations/PlainRubyObject/instances.csv +0 -6
  112. data/test/dummy_app/tmp/configurations/PlainRubyObject/instances.ods +0 -0
  113. data/test/dummy_app/tmp/configurations/Post/data.csv +0 -3
  114. data/test/dummy_app/tmp/configurations/Post/data.ods +0 -0
  115. data/test/dummy_app/tmp/configurations/Post/data.xlsx +0 -0
  116. data/test/dummy_app/tmp/configurations/Post/empty.csv +0 -0
  117. data/test/dummy_app/tmp/configurations/Post/empty.xlsx +0 -0
  118. data/test/dummy_app/tmp/configurations/SpreadsheetArchitect/data.csv +0 -3
  119. data/test/dummy_app/tmp/configurations/SpreadsheetArchitect/data.ods +0 -0
  120. data/test/dummy_app/tmp/configurations/SpreadsheetArchitect/data.xlsx +0 -0
  121. data/test/dummy_app/tmp/configurations/SpreadsheetArchitect/empty.csv +0 -0
  122. data/test/dummy_app/tmp/configurations/SpreadsheetArchitect/empty.xlsx +0 -0
  123. data/test/dummy_app/tmp/integration_tests/alt_xlsx.xlsx +0 -0
  124. data/test/dummy_app/tmp/integration_tests/csv.csv +0 -6
  125. data/test/dummy_app/tmp/integration_tests/ods.ods +0 -0
  126. data/test/dummy_app/tmp/integration_tests/xlsx.xlsx +0 -0
  127. data/test/dummy_app/tmp/kitchen_sink.ods +0 -0
  128. data/test/dummy_app/tmp/kitchen_sink.xlsx +0 -0
  129. data/test/dummy_app/tmp/multi_sheet.ods +0 -0
  130. data/test/dummy_app/tmp/multi_sheet.xlsx +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bd5fa4e705cbfedd7acaf5da159b8c3b4de0051e028b4d4d4f413d328fe66fe
4
- data.tar.gz: 6c58102ad00821be0af9cf78672af4b410278c111de88319346562fee49d7f18
3
+ metadata.gz: b0e103cc275bf05dabe10a86cb7f74dcc4a412daa2328e607ca70d7af22ea40b
4
+ data.tar.gz: 5799934cf9f9e177ca49ed33d749d01e535143cc18bf6990082b834fe6d9a6d5
5
5
  SHA512:
6
- metadata.gz: 25382f69d7516e34a2078cd2e50763743dabbdd3a25cd1a877242f1d5578212824284f50b2238bc20b80f5f734665ff622b2ef6ba7994aa8b844c41c23baddb3
7
- data.tar.gz: bb80ae2bc0b7f05d9acda89577904595bd1f54d065c5b029100eff69d3b34fa1d6423d8e5ce572a9693fdf1442d8bb8d9336d96801f31ead5193182a740525fd
6
+ metadata.gz: cee7b3f8dfc727d63c1f84f06f94c1bf3eaf46283b15b698724a5d0f57d3ef39728838b5295376df6e112bbb3141579c6ebc4189e8af85bd82558abe0cbd2546
7
+ data.tar.gz: 13c7e7db2193bb37be8911592e5eab725700e047dc8437c455adcad0ae8574f3187ac2d40412b82e706e90a286ec38b15be248c013b0fc5ed9db1d0c0b943e98
@@ -1,6 +1,28 @@
1
1
  CHANGELOG
2
2
  ---------
3
3
 
4
+ - **Unreleased**
5
+ - Nothing yet
6
+ - **4.1.0**
7
+ - Raise ArgumentError when invalid option names are given
8
+ - **4.0.1**
9
+ - Fix bug with `headers: false` where a blank header row is still added
10
+ - Fix Bug for older version of `caxlsx` v2.0.2
11
+ - **4.0.0**
12
+ - Switch to the `caxlsx` gem (Community Axlsx) from the legacy unmaintained `axlsx` gem. Axlsx has had a long history of being poorly maintained so this community gem improves the situation.
13
+ - Require Ruby 2.3+
14
+ - Ensure all options using Hash are automatically converted to symbol only hashes
15
+ - Add XLSX option `:freeze` to freeze custom sections of your spreadsheet
16
+ - Add XLSX option `:freeze_headers` to freeze the headers of your spreadsheet
17
+ - Remove old Axlsx patch for column width
18
+ - Backport new code for `string_width` calculations to Axlsx 3.0.1 and below.
19
+ - **3.3.1**
20
+ - [Issue #30](https://github.com/westonganger/spreadsheet_architect/issues/30) - Fix duplicate constant warning for XLSX_COLUMN_TYPES
21
+ - **3.3.0**
22
+ - Fix `:borders` option, was broken in v3.2.1
23
+ - Fix bug when passing `false` to `:headers` option
24
+ - Raise error when unsupported column type is passed
25
+ - Remove claimed support for `:currency` and `:percent` for ODS spreadsheets as they were not working. PR Wanted.
4
26
  - **3.2.1** - April 10, 2019
5
27
  - Fix bug when using `column_style` option with `include_header: true` & letter based column numbering
6
28
  - **3.2.0** - September 14, 2018
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Spreadsheet Architect
2
2
 
3
3
  <a href="https://badge.fury.io/rb/spreadsheet_architect" target="_blank"><img height="21" style='border:0px;height:21px;' border='0' src="https://badge.fury.io/rb/spreadsheet_architect.svg" alt="Gem Version"></a>
4
- <a href='https://travis-ci.org/westonganger/spreadsheet_architect' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://api.travis-ci.org/westonganger/spreadsheet_architect.svg?branch=master' border='0' alt='Build Status' /></a>
4
+ <a href='https://travis-ci.com/westonganger/spreadsheet_architect' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://api.travis-ci.org/westonganger/spreadsheet_architect.svg?branch=master' border='0' alt='Build Status' /></a>
5
5
  <a href='https://rubygems.org/gems/spreadsheet_architect' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://ruby-gem-downloads-badge.herokuapp.com/spreadsheet_architect?label=rubygems&type=total&total_label=downloads&color=brightgreen' border='0' alt='RubyGems Downloads' /></a>
6
- <a href='https://ko-fi.com/A5071NK' target='_blank'><img height='22' style='border:0px;height:22px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=a' border='0' alt='Buy Me a Coffee' /></a>
6
+ <a href='https://ko-fi.com/A5071NK' target='_blank'><img height='22' style='border:0px;height:22px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=a' border='0' alt='Buy Me a Coffee' /></a>
7
7
 
8
8
  Spreadsheet Architect is a library that allows you to create XLSX, ODS, or CSV spreadsheets super easily from ActiveRecord relations, plain Ruby objects, or tabular data.
9
9
 
10
10
  Key Features:
11
11
 
12
12
  - Dead simple custom spreadsheets with custom data
13
- - Data Sources: ActiveRecord relations, array of plain Ruby object instances, or tabular 2D Array Data
13
+ - Data Sources: Tabular Data from an Array, ActiveRecord relations, or array of plain Ruby object instances
14
14
  - Easily style and customize spreadsheets
15
15
  - Create multi sheet spreadsheets
16
16
  - Setting Class/Model or Project specific defaults
@@ -22,10 +22,12 @@ Key Features:
22
22
  gem 'spreadsheet_architect'
23
23
  ```
24
24
 
25
- # General Usage
25
+ # General Usage
26
26
 
27
27
  ### Tabular (Array) Data
28
28
 
29
+ The simplest and preffered usage is to simply create the data array yourself.
30
+
29
31
  ```ruby
30
32
  headers = ['Col 1','Col 2','Col 3']
31
33
  data = [[1,2,3], [4,5,6], [7,8,9]]
@@ -34,50 +36,25 @@ SpreadsheetArchitect.to_ods(headers: headers, data: data)
34
36
  SpreadsheetArchitect.to_csv(headers: headers, data: data)
35
37
  ```
36
38
 
37
- ### Rails relation or an array of plain Ruby object instances
38
-
39
- ```ruby
40
- posts = Post.order(name: :asc).where(published: true)
41
- # OR
42
- posts = 10.times.map{|i| Post.new(number: i)}
39
+ Using this style will allow you to utilize any custom performance optimizations during your data generation process. This will come in handy when the spreadsheets get large and things start to get slow. One of my favourites for Rails is [light_record](https://github.com/Paxa/light_record)
43
40
 
44
- SpreadsheetArchitect.to_xlsx(instances: posts)
45
- SpreadsheetArchitect.to_ods(instances: posts)
46
- SpreadsheetArchitect.to_csv(instances: posts)
47
- ```
41
+ ### Rails Relations or an Array of plain Ruby object instances
48
42
 
49
- **(Optional)** If you would like to add the methods `to_xlsx`, `to_ods`, `to_csv`, `to_axlsx_package`, `to_rodf_spreadsheet` to some class, you can simply include the SpreadsheetArchitect module to whichever classes you choose. A good default strategy is to simply add it to the ApplicationRecord or another parent class to have it available on all appropriate classes. For example:
43
+ If you would like to add the methods `to_xlsx`, `to_ods`, `to_csv`, `to_axlsx_package`, `to_rodf_spreadsheet` to some class, you can simply include the SpreadsheetArchitect module to whichever classes you choose. For example:
50
44
 
51
45
  ```ruby
52
- class ApplicationRecord < ActiveRecord::Base
46
+ class Post < ApplicationRecord
53
47
  include SpreadsheetArchitect
54
48
  end
55
49
  ```
56
50
 
57
- Then use it on the class or ActiveRecord relations of the class
58
-
59
- ```ruby
60
- posts = Post.order(name: :asc).where(published: true)
61
- posts.to_xlsx
62
- posts.to_ods
63
- posts.to_csv
64
-
65
- # Plain Ruby Objects
66
- posts_array = 10.times.map{|i| Post.new(number: i)}
67
- Post.to_xlsx(instances: posts_array)
68
- Post.to_ods(instances: posts_array)
69
- Post.to_csv(instances: posts_array)
70
- ```
71
-
72
- # Usage with Instances / ActiveRecord Relations
73
-
74
- When NOT using the `:data` option, ie. on an AR Relation or using the `:instances` option, Spreadsheet Architect requires an instance method defined on the class to generate the data. It looks for the `spreadsheet_columns` method on the class. If you are using on an ActiveRecord model and that method is not defined, it would fallback to the models `column_names` method (not recommended). If using the `:data` option this is ignored.
51
+ When using on an AR Relation or using the `:instances` option, SpreadsheetArchitect requires an instance method to be defined on the class to generate the data. By default it looks for the `spreadsheet_columns` method on the class. If you are using on an ActiveRecord model and that method is not defined, it would fallback to the models `column_names` method. If using the `:data` option this is completely ignored.
75
52
 
76
53
  ```ruby
77
54
  class Post
55
+ include SpreadsheetArchitect
78
56
 
79
57
  def spreadsheet_columns
80
-
81
58
  ### Column format is: [Header, Cell Data / Method (if symbol) to Call on each Instance, (optional) Cell Type]
82
59
  [
83
60
  ['Title', :title],
@@ -89,15 +66,30 @@ class Post
89
66
  ['Rating', :rating],
90
67
  ['Category/Tags', "#{category.name} - #{tags.collect(&:name).join(', ')}"]
91
68
  ]
69
+ end
70
+
92
71
  end
72
+ ```
73
+
74
+ Then use it on the class or ActiveRecord relations of the class
93
75
 
94
- Post.to_xlsx(instances: posts)
76
+ ```ruby
77
+ posts = Post.order(name: :asc).where(published: true)
78
+ posts.to_xlsx
79
+ posts.to_ods
80
+ posts.to_csv
81
+
82
+ # Plain Ruby Objects
83
+ posts_array = 10.times.map{|i| Post.new(number: i)}
84
+ Post.to_xlsx(instances: posts_array)
85
+ Post.to_ods(instances: posts_array)
86
+ Post.to_csv(instances: posts_array)
95
87
  ```
96
88
 
97
- If you want to use a different method name then `spreadsheet_columns` you can pass a method name as a Symbol or String to the `spreadsheet_columns` option. Feel free to utilize the model-wide/project-wide defaults features if desired necessary.
89
+ If you want to use a different method name then `spreadsheet_columns` you can pass a method name to the `:spreadsheet_columns` option.
98
90
 
99
91
  ```ruby
100
- Post.to_xlsx(instances: posts, spreadsheet_columns: :my_special_columns)
92
+ Post.to_xlsx(instances: posts, spreadsheet_columns: :my_special_method)
101
93
  ```
102
94
 
103
95
  Alternatively, you can pass a Proc/lambda to the `spreadsheet_columns` option. For those purists that really dont want to define any extra `spreadsheet_columns` instance method on your model, this option can help you work with that methodology.
@@ -119,10 +111,19 @@ Post.to_xlsx(instances: posts, spreadsheet_columns: Proc.new{|instance|
119
111
 
120
112
  # Sending & Saving Spreadsheets
121
113
 
122
- ### Method 1: Send Data via Rails Controller
114
+ ### Method 1: Save to a file manually
123
115
 
124
116
  ```ruby
117
+ file_data = SpreadsheetArchitect.to_xlsx(headers: headers, data: data)
125
118
 
119
+ File.open('path/to/file.xlsx', 'w+b') do |f|
120
+ f.write file_data
121
+ end
122
+ ```
123
+
124
+ ### Method 2: Send Data via Rails Controller
125
+
126
+ ```ruby
126
127
  class PostsController < ActionController::Base
127
128
  respond_to :html, :xlsx, :ods, :csv
128
129
 
@@ -176,44 +177,27 @@ class PostsController < ActionController::Base
176
177
  end
177
178
  ```
178
179
 
179
- ### Method 2: Save to a file manually
180
+ # Multi Sheet Spreadsheets
180
181
 
181
- ```ruby
182
- ### Ex. with ActiveRecord relation
183
- file_data = Post.order(published_at: :asc).to_xlsx
184
- File.open('path/to/file.xlsx', 'w+b') do |f|
185
- f.write file_data
186
- end
182
+ ### XLSX
187
183
 
188
- file_data = Post.order(published_at: :asc).to_ods
189
- File.open('path/to/file.ods', 'w+b') do |f|
190
- f.write file_data
191
- end
192
-
193
- file_data = Post.order(published_at: :asc).to_csv
194
- File.open('path/to/file.csv', 'w+b') do |f|
195
- f.write file_data
196
- end
197
- ```
198
-
199
- # Multi Sheet XLSX Spreadsheets
200
184
  ```ruby
201
185
  axlsx_package = SpreadsheetArchitect.to_axlsx_package({headers: headers, data: data})
202
- axlsx_package = SpreadsheetArchitect.to_axlsx_package({headers: headers, data: data}, package)
186
+ axlsx_package = SpreadsheetArchitect.to_axlsx_package({headers: headers, data: data}, axlsx_package)
203
187
 
204
- File.open('path/to/file.xlsx', 'w+b') do |f|
188
+ File.open('path/to/multi_sheet_file.xlsx', 'w+b') do |f|
205
189
  f.write axlsx_package.to_stream.read
206
190
  end
207
191
  ```
208
192
 
209
193
  See this file for more details: https://github.com/westonganger/spreadsheet_architect/blob/master/test/spreadsheet_architect/multi_sheet_test.rb
210
194
 
211
- ### Multi Sheet ODS Spreadsheets
195
+ ### ODS
212
196
  ```ruby
213
197
  ods_spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet({headers: headers, data: data})
214
- ods_spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet({headers: headers, data: data}, spreadsheet)
198
+ ods_spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet({headers: headers, data: data}, ods_spreadsheet)
215
199
 
216
- File.open('path/to/file.ods', 'w+b') do |f|
200
+ File.open('path/to/multi_sheet_file.ods', 'w+b') do |f|
217
201
  f.write ods_spreadsheet
218
202
  end
219
203
  ```
@@ -231,15 +215,17 @@ See this file for more details: https://github.com/westonganger/spreadsheet_arch
231
215
  |**spreadsheet_columns**<br>*Proc/Symbol/String*| Use this option to override or define the spreadsheet columns. Normally, if this option is not specified and are using the instances option/ActiveRecord relation, it uses the classes custom `spreadsheet_columns` method or any custom defaults defined.<br>If neither of those and is an ActiveRecord model, then it will falls back to the models `self.column_names` | Cannot be used with the `:data` option.<br><br>If a Proc value is passed it will be evaluated on the instance object.<br><br>If a Symbol or String value is passed then it will search the instance for a method name that matches and call it. |
232
216
  |**headers**<br>*Array / 2D Array*| |Data for the header row cells. If using on a class/relation, this defaults to the ones provided via `spreadsheet_columns`. Pass `false` to skip the header row. |
233
217
  |**sheet_name**<br>*String*|`Sheet1`||
234
- |**header_style**<br>*Hash*|`{background_color: "AAAAAA", color: "FFFFFF", align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false}`|See all available style options [here](https://github.com/westonganger/spreadsheet_architect/blob/master/docs/axlsx_styles_reference.md)|
235
- |**row_style**<br>*Hash*|`{background_color: nil, color: "000000", align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false, format_code: nil}`|Styles for non-header rows. See all available style options [here](https://github.com/westonganger/spreadsheet_architect/blob/master/docs/axlsx_styles_reference.md)|
218
+ |**header_style**<br>*Hash*|`{background_color: "AAAAAA", color: "FFFFFF", align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false}`|See all available style options [here](https://github.com/westonganger/spreadsheet_architect/blob/master/docs/axlsx_style_reference.md)|
219
+ |**row_style**<br>*Hash*|`{background_color: nil, color: "000000", align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false, format_code: nil}`|Styles for non-header rows. See all available style options [here](https://github.com/westonganger/spreadsheet_architect/blob/master/docs/axlsx_style_reference.md)|
236
220
  |**column_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb)|
237
221
  |**range_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb)|
238
222
  |**conditional_row_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb). The if/unless proc will called with the following args: `row_index`, `row_data`|
239
223
  |**merges**<br>*Array*||Merge cells. [See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb). Warning merges cannot overlap eachother, if you attempt to do so Excel will claim your spreadsheet is corrupt and refuse to open your spreadsheet.|
240
224
  |**borders**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb)|
241
- |**column_types**<br>*Array*||Valid types for XLSX are :string, :integer, :float, :boolean, nil = auto determine.|
225
+ |**column_types**<br>*Array*||Valid types for XLSX are :string, :integer, :float, :date, :time, :boolean, nil = auto determine.|
242
226
  |**column_widths**<br>*Array*||Sometimes you may want explicit column widths. Use nil if you want a column to autofit again.|
227
+ |**freeze_headers**<br>*Boolean*||Make all header rows frozen/fixed so they do not scroll.|
228
+ |**freeze**<br>* Hash*|`{rows: (1..4), columns: :all}`|Make all specified rows and columns frozen/fixed so they do not scroll.|
243
229
 
244
230
  ## `to_axlsx_spreadsheet(options={}, axlsx_package_to_join=nil)`
245
231
  Same options as `to_xlsx`
@@ -255,7 +241,7 @@ Same options as `to_xlsx`
255
241
  |**sheet_name**<br>*String*|`Sheet1`||
256
242
  |**header_style**<br>*Hash*|`{background_color: "AAAAAA", color: "FFFFFF", align: :center, font_size: 10, bold: true}`|Note: Currently ODS only supports these options|
257
243
  |**row_style**<br>*Hash*|`{background_color: nil, color: "000000", align: :left, font_size: 10, bold: false}`|Styles for non-header rows. Currently ODS only supports these options|
258
- |**column_types**<br>*Array*||Valid types for ODS are :string, :float :percent, :currency, :date, :time,, nil = auto determine. Due to [RODF Issue #19](https://github.com/thiagoarrais/rodf/issues/19), :date/:time will be converted to :string |
244
+ |**column_types**<br>*Array*||Valid types for ODS are :string, :float, :date, :time, :boolean, nil = auto determine. Due to [RODF Issue #19](https://github.com/thiagoarrais/rodf/issues/19), :date/:time will be converted to :string |
259
245
 
260
246
  ## `to_rodf_spreadsheet(options={}, spreadsheet_to_join=nil)`
261
247
  Same options as `to_ods`
@@ -273,7 +259,9 @@ Same options as `to_ods`
273
259
  # Change class-wide default method options
274
260
 
275
261
  ```ruby
276
- class Post
262
+ class Post < ApplicationRecord
263
+ include SpreadsheetArchitect
264
+
277
265
  def spreadsheet_columns
278
266
  [:name, :content]
279
267
  end
@@ -335,7 +323,7 @@ We use the `appraisal` gem for testing multiple versions of `axlsx`. Please use
335
323
  1. `bundle exec appraisal install`
336
324
  2. `bundle exec appraisal rake test`
337
325
 
338
- At this time the spreadsheets generated by the test suite are manually inspected. After running the tests, the test output can be viewed at `test/dummy_app/tmp/#{alxsx_version}/*`
326
+ At this time the spreadsheets generated by the test suite are manually inspected. After running the tests, the test output can be viewed at `tmp/#{alxsx_version}/*`
339
327
 
340
328
  # Credits
341
329
 
@@ -10,6 +10,7 @@ require 'spreadsheet_architect/class_methods/ods'
10
10
  require 'spreadsheet_architect/class_methods/xlsx'
11
11
 
12
12
  module SpreadsheetArchitect
13
+
13
14
  def self.included(base)
14
15
  base.send(:extend, ClassMethods)
15
16
  end
@@ -33,4 +34,8 @@ module SpreadsheetArchitect
33
34
  def self.default_options
34
35
  @default_options
35
36
  end
37
+
38
+ XLSX_COLUMN_TYPES = [:string, :integer, :float, :date, :time, :boolean].freeze
39
+ ODS_COLUMN_TYPES = [:string, :float, :date, :time, :boolean].freeze
40
+
36
41
  end
@@ -0,0 +1,23 @@
1
+ if Axlsx::VERSION.to_f < 3.1
2
+
3
+ Axlsx::Cell.class_eval do
4
+ private
5
+
6
+ def string_width(string, font_size)
7
+ font_scale = font_size / 10.0
8
+ (string.to_s.size + 3) * font_scale
9
+ end
10
+ end
11
+
12
+ if defined?(Axlsx::RichTextRun)
13
+ Axlsx::RichTextRun.class_eval do
14
+ private
15
+
16
+ def string_width(string, font_size)
17
+ font_scale = font_size / 10.0
18
+ string.to_s.size * font_scale
19
+ end
20
+ end
21
+ end
22
+
23
+ end
@@ -2,6 +2,7 @@ require 'csv'
2
2
 
3
3
  module SpreadsheetArchitect
4
4
  module ClassMethods
5
+
5
6
  def to_csv(opts={})
6
7
  opts = SpreadsheetArchitect::Utils.get_options(opts, self)
7
8
  options = SpreadsheetArchitect::Utils.get_cell_data(opts, self)
@@ -18,5 +19,6 @@ module SpreadsheetArchitect
18
19
  end
19
20
  end
20
21
  end
22
+
21
23
  end
22
24
  end
@@ -2,6 +2,7 @@ require 'rodf'
2
2
 
3
3
  module SpreadsheetArchitect
4
4
  module ClassMethods
5
+
5
6
  def to_ods(opts={})
6
7
  return to_rodf_spreadsheet(opts).bytes
7
8
  end
@@ -10,6 +11,10 @@ module SpreadsheetArchitect
10
11
  opts = SpreadsheetArchitect::Utils.get_options(opts, self)
11
12
  options = SpreadsheetArchitect::Utils.get_cell_data(opts, self)
12
13
 
14
+ if options[:column_types] && !(options[:column_types].compact.collect(&:to_sym) - SpreadsheetArchitect::ODS_COLUMN_TYPES).empty?
15
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new("Invalid column type. Valid ODS values are #{SpreadsheetArchitect::ODS_COLUMN_TYPES}")
16
+ end
17
+
13
18
  if !spreadsheet
14
19
  spreadsheet = RODF::Spreadsheet.new
15
20
  end
@@ -48,21 +53,16 @@ module SpreadsheetArchitect
48
53
  options[:data].each_with_index do |row_data, index|
49
54
  row do
50
55
  row_data.each_with_index do |val, i|
51
- if options[:types]
52
- type = options[:types][i]
56
+ if options[:column_types]
57
+ type = options[:column_types][i]
53
58
  end
54
59
 
55
- if type
56
- if [:date, :time].include?(type)
57
- type = :string
58
- val = val.to_s
59
- end
60
- elsif val.respond_to?(:strftime)
60
+ if (type && [:date, :time].include?(type)) || val.respond_to?(:strftime)
61
61
  type = :string
62
62
  val = val.to_s
63
63
  end
64
64
 
65
- cell val, style: :row_style, type: (options[:types][i] if options[:types])
65
+ cell val, style: :row_style, type: type
66
66
  end
67
67
  end
68
68
  end
@@ -1,10 +1,11 @@
1
1
  require 'axlsx'
2
2
  require 'axlsx_styler'
3
3
 
4
- require 'spreadsheet_architect/monkey_patches/axlsx_column_width'
4
+ require 'spreadsheet_architect/axlsx_string_width_patch'
5
5
 
6
6
  module SpreadsheetArchitect
7
7
  module ClassMethods
8
+
8
9
  def to_xlsx(opts={})
9
10
  return to_axlsx_package(opts).to_stream.read
10
11
  end
@@ -12,7 +13,11 @@ module SpreadsheetArchitect
12
13
  def to_axlsx_package(opts={}, package=nil)
13
14
  opts = SpreadsheetArchitect::Utils.get_options(opts, self)
14
15
  options = SpreadsheetArchitect::Utils.get_cell_data(opts, self)
15
-
16
+
17
+ if options[:column_types] && !(options[:column_types].compact.collect(&:to_sym) - SpreadsheetArchitect::XLSX_COLUMN_TYPES).empty?
18
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new("Invalid column type. Valid XLSX values are #{SpreadsheetArchitect::XLSX_COLUMN_TYPES}")
19
+ end
20
+
16
21
  header_style = SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(options[:header_style])
17
22
  row_style = SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(options[:row_style])
18
23
 
@@ -42,10 +47,10 @@ module SpreadsheetArchitect
42
47
 
43
48
  if options[:conditional_row_styles]
44
49
  conditional_styles_for_row = SpreadsheetArchitect::Utils::XLSX.conditional_styles_for_row(options[:conditional_row_styles], row_index, header_row)
45
-
50
+
46
51
  unless conditional_styles_for_row.empty?
47
52
  sheet.add_style(
48
- "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
53
+ "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
49
54
  SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(conditional_styles_for_row)
50
55
  )
51
56
  end
@@ -102,11 +107,13 @@ module SpreadsheetArchitect
102
107
  sheet.add_row row_data, style: styles, types: types
103
108
 
104
109
  if options[:conditional_row_styles]
110
+ options[:conditional_row_styles] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:conditional_row_styles])
111
+
105
112
  conditional_styles_for_row = SpreadsheetArchitect::Utils::XLSX.conditional_styles_for_row(options[:conditional_row_styles], row_index, row_data)
106
-
113
+
107
114
  unless conditional_styles_for_row.empty?
108
115
  sheet.add_style(
109
- "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
116
+ "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
110
117
  SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(conditional_styles_for_row)
111
118
  )
112
119
  end
@@ -122,16 +129,22 @@ module SpreadsheetArchitect
122
129
  end
123
130
 
124
131
  if options[:borders]
132
+ options[:borders] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:borders])
133
+
125
134
  options[:borders].each do |x|
126
135
  if x[:range].is_a?(Hash)
127
136
  x[:range] = SpreadsheetArchitect::Utils::XLSX.range_hash_to_str(x[:range], max_row_length, num_rows)
128
137
  else
129
138
  SpreadsheetArchitect::Utils::XLSX.verify_range(x[:range], num_rows)
130
139
  end
140
+
141
+ sheet.add_border x[:range], (x[:border_styles] || x[:styles])
131
142
  end
132
143
  end
133
144
 
134
145
  if options[:column_styles]
146
+ options[:column_styles] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:column_styles])
147
+
135
148
  options[:column_styles].each do |x|
136
149
  start_row = (options[:headers] ? options[:headers].count : 0) + 1
137
150
 
@@ -163,6 +176,8 @@ module SpreadsheetArchitect
163
176
  end
164
177
 
165
178
  if options[:range_styles]
179
+ options[:range_styles] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:range_styles])
180
+
166
181
  options[:range_styles].each do |x|
167
182
  styles = SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(x[:styles])
168
183
 
@@ -177,6 +192,8 @@ module SpreadsheetArchitect
177
192
  end
178
193
 
179
194
  if options[:merges]
195
+ options[:merges] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:merges])
196
+
180
197
  options[:merges].each do |x|
181
198
  if x[:range].is_a?(Hash)
182
199
  x[:range] = SpreadsheetArchitect::Utils::XLSX.range_hash_to_str(x[:range], max_row_length, num_rows)
@@ -187,9 +204,48 @@ module SpreadsheetArchitect
187
204
  sheet.merge_cells x[:range]
188
205
  end
189
206
  end
207
+
208
+ if options[:freeze_headers]
209
+ sheet.sheet_view.pane do |pane|
210
+ pane.state = :frozen
211
+ pane.y_split = options[:headers].count
212
+ end
213
+
214
+ elsif options[:freeze]
215
+ options[:freeze] = SpreadsheetArchitect::Utils.symbolize_keys(options[:freeze])
216
+
217
+ sheet.sheet_view.pane do |pane|
218
+ pane.state = :frozen
219
+
220
+ ### Currently not working
221
+ #if options[:freeze][:active_pane]
222
+ # Axlsx.validate_pane_type(options[:freeze][:active_pane])
223
+ # pane.active_pane = options[:freeze][:active_pane]
224
+ #else
225
+ # pane.active_pane = :bottom_right
226
+ #end
227
+
228
+ if !options[:freeze][:rows]
229
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new("The :rows key must be specified in the :freeze option hash")
230
+ elsif options[:freeze][:rows].is_a?(Range)
231
+ pane.y_split = options[:freeze][:rows].count
232
+ else
233
+ pane.y_split = 1
234
+ end
235
+
236
+ if options[:freeze][:columns] && options[:freeze][:columns] != :all
237
+ if options[:freeze][:columns].is_a?(Range)
238
+ pane.x_split = options[:freeze][:columns].count
239
+ else
240
+ pane.x_split = 1
241
+ end
242
+ end
243
+ end
244
+ end
190
245
  end
191
246
 
192
247
  return package
193
248
  end
249
+
194
250
  end
195
251
  end