spreadsheet_architect 3.0.0 → 3.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +25 -12
- data/lib/spreadsheet_architect.rb +1 -1
- data/lib/spreadsheet_architect/class_methods/xlsx.rb +28 -0
- data/lib/spreadsheet_architect/exceptions.rb +20 -14
- data/lib/spreadsheet_architect/monkey_patches/axlsx_column_width.rb +3 -0
- data/lib/spreadsheet_architect/utils.rb +31 -16
- data/lib/spreadsheet_architect/utils/xlsx.rb +26 -0
- data/lib/spreadsheet_architect/version.rb +1 -1
- data/test/dummy_app/app/models/custom_columns_method_post.rb +29 -0
- data/test/dummy_app/log/test.log +42144 -61
- data/test/dummy_app/tmp/axlsx-master/integration/alt_xlsx.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/integration/ods.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/integration/xlsx.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/kitchen_sink.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/kitchen_sink.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/data.csv +1 -1
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/data.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/empty.csv +1 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/empty.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/empty.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/instances.csv +5 -5
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/instances.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/instances.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/data.csv +3 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/{SpreadsheetArchitect → CustomColumnsMethodPost}/data.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/{SpreadsheetArchitect → CustomColumnsMethodPost}/empty.csv +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/{SpreadsheetArchitect → CustomColumnsMethodPost}/empty.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/empty.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/instances.csv +6 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/instances.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/instances.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/data.csv +1 -1
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/data.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/empty.csv +1 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/empty.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/empty.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/instances.csv +5 -5
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/instances.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/CustomPost/instances.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/data.csv +1 -1
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/data.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/empty.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/empty.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/instances.csv +5 -5
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/instances.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/instances.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/data.csv +1 -1
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/data.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/empty.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/empty.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/instances.csv +5 -5
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/instances.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/instances.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/data.csv +1 -1
- data/test/dummy_app/tmp/axlsx-master/models/Post/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/data.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/empty.csv +1 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/empty.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/empty.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/instances.csv +6 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/instances.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/Post/instances.xlsx +0 -0
- data/test/dummy_app/tmp/axlsx-master/multi_sheet.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/multi_sheet.xlsx +0 -0
- data/test/models/all_models_test.rb +23 -18
- data/test/test_helper.rb +5 -1
- data/test/unit/exceptions_test.rb +22 -7
- data/test/unit/kitchen_sink_test.rb +15 -0
- data/test/unit/utils_test.rb +2 -2
- metadata +28 -14
- data/test/dummy_app/tmp/axlsx-master/models/SpreadsheetArchitect/data.csv +0 -3
- data/test/dummy_app/tmp/axlsx-master/models/SpreadsheetArchitect/data.ods +0 -0
- data/test/dummy_app/tmp/axlsx-master/models/SpreadsheetArchitect/empty.xlsx +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8e7dd80b1f4347e578f1a4ab5a8224ee78f03d8f5c6cfac34882099fb6307f3
|
|
4
|
+
data.tar.gz: 90a5a75b3efe90265123b140373080fe7b780e51b7f6cbf9d6afc30adf9f656d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 519fc04923d74447c67d4f6717f2314da4ac248a2f3d03ab324894d8f273a82dacee97a8acef8c3d39006d70aca1c638c6fce461cb694fe1ca82c602291dbb68
|
|
7
|
+
data.tar.gz: f86599479cc858dbde7161a27d922967ce87f93936993137398905b4711784253b59ceff7f5cf6d2c8490e3bb7c9199c09243afa284b2b0b7116495b1c490cf2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
CHANGELOG
|
|
2
2
|
---------
|
|
3
3
|
|
|
4
|
+
- **3.1.0** - UNRELEASED
|
|
5
|
+
- Add new option `:conditional_row_styles` to `to_xlsx`.
|
|
6
|
+
- Add ability to pass an alternative method name as a Symbol/String to the `:spreadsheet_columns` option.
|
|
7
|
+
- Replace all usage of the legacy method `instance_eval` with the proper method `send`.
|
|
8
|
+
- [#23](https://github.com/westonganger/spreadsheet_architect/issues/23#issuecomment-412803761) - Fix bug where custom `columns_widths` in xlsx spreadsheets might not get set correctly.
|
|
9
|
+
- All exceptions now inherit from the appropriate ruby core exception classes
|
|
10
|
+
- `SpreadsheetArchitect::Exceptions::InvalidOptionError` renamed to `SpreadsheetArchitect::Exceptions::OptionTypeError`
|
|
4
11
|
- **3.0.0** - July 6, 2018
|
|
5
12
|
- [#16](https://github.com/westonganger/spreadsheet_architect/issues/16) - Add ability to pass :instances option to SpreadsheetArchitect class methods
|
|
6
13
|
- [#16](https://github.com/westonganger/spreadsheet_architect/issues/16) - Remove Plain Ruby syntax `Post.to_xlsx(instances: posts_array)` in favor of `SpreadsheetArchitect.to_xlsx(instance: posts_array)`. However, it may still work at this time if configured correctly.
|
data/README.md
CHANGED
|
@@ -90,9 +90,17 @@ class Post
|
|
|
90
90
|
['Category/Tags', "#{category.name} - #{tags.collect(&:name).join(', ')}"]
|
|
91
91
|
]
|
|
92
92
|
end
|
|
93
|
+
|
|
94
|
+
Post.to_xlsx(instances: posts)
|
|
95
|
+
```
|
|
96
|
+
|
|
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.
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
Post.to_xlsx(instances: posts, spreadsheet_columns: :my_special_columns)
|
|
93
101
|
```
|
|
94
102
|
|
|
95
|
-
Alternatively,
|
|
103
|
+
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.
|
|
96
104
|
|
|
97
105
|
```ruby
|
|
98
106
|
Post.to_xlsx(instances: posts, spreadsheet_columns: Proc.new{|instance|
|
|
@@ -220,20 +228,21 @@ See this file for more details: https://github.com/westonganger/spreadsheet_arch
|
|
|
220
228
|
|---|---|---|
|
|
221
229
|
|**data**<br>*2D Array*| |Cannot be used with the `:instances` option.<br><br>Tabular data for the non-header row cells. |
|
|
222
230
|
|**instances**<br>*Array*| |Cannot be used with the `:data` option.<br><br>Array of class/model instances to be used as row data. Cannot be used with :data option|
|
|
223
|
-
|**spreadsheet_columns**<br>*
|
|
231
|
+
|**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. |
|
|
224
232
|
|**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. |
|
|
225
233
|
|**sheet_name**<br>*String*|`Sheet1`||
|
|
226
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)|
|
|
227
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)|
|
|
228
|
-
|**column_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/
|
|
229
|
-
|**range_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/
|
|
230
|
-
|**
|
|
231
|
-
|**
|
|
236
|
+
|**column_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb)|
|
|
237
|
+
|**range_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb)|
|
|
238
|
+
|**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
|
+
|**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
|
+
|**borders**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb)|
|
|
232
241
|
|**column_types**<br>*Array*||Valid types for XLSX are :string, :integer, :float, :boolean, nil = auto determine.|
|
|
233
242
|
|**column_widths**<br>*Array*||Sometimes you may want explicit column widths. Use nil if you want a column to autofit again.|
|
|
234
243
|
|
|
235
244
|
## `to_axlsx_spreadsheet(options={}, axlsx_package_to_join=nil)`
|
|
236
|
-
Same options as `to_xlsx
|
|
245
|
+
Same options as `to_xlsx`
|
|
237
246
|
|
|
238
247
|
## `to_ods(options={})`
|
|
239
248
|
|
|
@@ -241,7 +250,7 @@ Same options as `to_xlsx`. For more details
|
|
|
241
250
|
|---|---|---|
|
|
242
251
|
|**data**<br>*2D Array*| |Cannot be used with the `:instances` option.<br><br>Tabular data for the non-header row cells. |
|
|
243
252
|
|**instances**<br>*Array*| |Cannot be used with the `:data` option.<br><br>Array of class/model instances to be used as row data. Cannot be used with :data option|
|
|
244
|
-
|**spreadsheet_columns**<br>*
|
|
253
|
+
|**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. |
|
|
245
254
|
|**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. |
|
|
246
255
|
|**sheet_name**<br>*String*|`Sheet1`||
|
|
247
256
|
|**header_style**<br>*Hash*|`{background_color: "AAAAAA", color: "FFFFFF", align: :center, font_size: 10, bold: true}`|Note: Currently ODS only supports these options|
|
|
@@ -257,7 +266,7 @@ Same options as `to_ods`
|
|
|
257
266
|
|---|---|---|
|
|
258
267
|
|**data**<br>*2D Array*| |Cannot be used with the `:instances` option.<br><br>Tabular data for the non-header row cells. |
|
|
259
268
|
|**instances**<br>*Array*| |Cannot be used with the `:data` option.<br><br>Array of class/model instances to be used as row data. Cannot be used with :data option|
|
|
260
|
-
|**spreadsheet_columns**<br>*
|
|
269
|
+
|**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. |
|
|
261
270
|
|**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. |
|
|
262
271
|
|
|
263
272
|
|
|
@@ -265,8 +274,6 @@ Same options as `to_ods`
|
|
|
265
274
|
|
|
266
275
|
```ruby
|
|
267
276
|
class Post
|
|
268
|
-
include SpreadsheetArchitect
|
|
269
|
-
|
|
270
277
|
def spreadsheet_columns
|
|
271
278
|
[:name, :content]
|
|
272
279
|
end
|
|
@@ -276,11 +283,13 @@ class Post
|
|
|
276
283
|
['My Post Report'],
|
|
277
284
|
self.column_names.map{|x| x.titleize}
|
|
278
285
|
],
|
|
286
|
+
spreadsheet_columns: :spreadsheet_columns,
|
|
279
287
|
header_style: {background_color: 'AAAAAA', color: 'FFFFFF', align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
|
|
280
288
|
row_style: {background_color: nil, color: '000000', align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
|
|
281
289
|
sheet_name: self.name,
|
|
282
290
|
column_styles: [],
|
|
283
291
|
range_styles: [],
|
|
292
|
+
conditional_row_styles: [],
|
|
284
293
|
merges: [],
|
|
285
294
|
borders: [],
|
|
286
295
|
column_types: [],
|
|
@@ -295,11 +304,13 @@ end
|
|
|
295
304
|
|
|
296
305
|
SpreadsheetArchitect.default_options = {
|
|
297
306
|
headers: true,
|
|
307
|
+
spreadsheet_columns: :spreadsheet_columns,
|
|
298
308
|
header_style: {background_color: 'AAAAAA', color: 'FFFFFF', align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
|
|
299
309
|
row_style: {background_color: nil, color: '000000', align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
|
|
300
310
|
sheet_name: 'My Project Export',
|
|
301
311
|
column_styles: [],
|
|
302
312
|
range_styles: [],
|
|
313
|
+
conditional_row_styles: [],
|
|
303
314
|
merges: [],
|
|
304
315
|
borders: [],
|
|
305
316
|
column_types: [],
|
|
@@ -307,7 +318,7 @@ SpreadsheetArchitect.default_options = {
|
|
|
307
318
|
```
|
|
308
319
|
|
|
309
320
|
# Kitchen Sink Examples with Styling for XLSX and ODS
|
|
310
|
-
See this example: https://github.com/westonganger/spreadsheet_architect/blob/master/test/
|
|
321
|
+
See this example: https://github.com/westonganger/spreadsheet_architect/blob/master/test/unit/kitchen_sink_test.rb
|
|
311
322
|
|
|
312
323
|
# Axlsx Style Reference
|
|
313
324
|
|
|
@@ -324,6 +335,8 @@ We use the `appraisal` gem for testing multiple versions of `axlsx`. Please use
|
|
|
324
335
|
1. `bundle exec appraisal install`
|
|
325
336
|
2. `bundle exec appraisal rake test`
|
|
326
337
|
|
|
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}/*`
|
|
339
|
+
|
|
327
340
|
# Credits
|
|
328
341
|
|
|
329
342
|
Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
|
|
@@ -26,7 +26,7 @@ module SpreadsheetArchitect
|
|
|
26
26
|
if val.is_a?(Hash)
|
|
27
27
|
@default_options = val
|
|
28
28
|
else
|
|
29
|
-
raise SpreadsheetArchitect::Exceptions::
|
|
29
|
+
raise SpreadsheetArchitect::Exceptions::OptionTypeError.new("SpreadsheetArchitect.default_options")
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -20,6 +20,8 @@ module SpreadsheetArchitect
|
|
|
20
20
|
package = Axlsx::Package.new
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
row_index = -1
|
|
24
|
+
|
|
23
25
|
package.workbook.add_worksheet(name: options[:sheet_name]) do |sheet|
|
|
24
26
|
max_row_length = options[:data].empty? ? 0 : options[:data].max_by{|x| x.length}.length
|
|
25
27
|
|
|
@@ -27,6 +29,8 @@ module SpreadsheetArchitect
|
|
|
27
29
|
header_style_index = package.workbook.styles.add_style(header_style)
|
|
28
30
|
|
|
29
31
|
options[:headers].each do |header_row|
|
|
32
|
+
row_index += 1
|
|
33
|
+
|
|
30
34
|
missing = max_row_length - header_row.count
|
|
31
35
|
if missing > 0
|
|
32
36
|
missing.times do
|
|
@@ -35,6 +39,17 @@ module SpreadsheetArchitect
|
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
sheet.add_row header_row, style: header_style_index
|
|
42
|
+
|
|
43
|
+
if options[:conditional_row_styles]
|
|
44
|
+
conditional_styles_for_row = SpreadsheetArchitect::Utils::XLSX.conditional_styles_for_row(options[:conditional_row_styles], row_index, header_row)
|
|
45
|
+
|
|
46
|
+
unless conditional_styles_for_row.empty?
|
|
47
|
+
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}",
|
|
49
|
+
SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(conditional_styles_for_row)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
38
53
|
end
|
|
39
54
|
end
|
|
40
55
|
|
|
@@ -45,6 +60,8 @@ module SpreadsheetArchitect
|
|
|
45
60
|
row_style_index = package.workbook.styles.add_style(row_style)
|
|
46
61
|
|
|
47
62
|
options[:data].each do |row_data|
|
|
63
|
+
row_index += 1
|
|
64
|
+
|
|
48
65
|
missing = max_row_length - row_data.count
|
|
49
66
|
if missing > 0
|
|
50
67
|
missing.times do
|
|
@@ -66,6 +83,17 @@ module SpreadsheetArchitect
|
|
|
66
83
|
end
|
|
67
84
|
|
|
68
85
|
sheet.add_row row_data, style: row_style_index, types: types
|
|
86
|
+
|
|
87
|
+
if options[:conditional_row_styles]
|
|
88
|
+
conditional_styles_for_row = SpreadsheetArchitect::Utils::XLSX.conditional_styles_for_row(options[:conditional_row_styles], row_index, row_data)
|
|
89
|
+
|
|
90
|
+
unless conditional_styles_for_row.empty?
|
|
91
|
+
sheet.add_style(
|
|
92
|
+
"#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
|
|
93
|
+
SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(conditional_styles_for_row)
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
69
97
|
end
|
|
70
98
|
|
|
71
99
|
options[:data].first.each_with_index do |x,i|
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
module SpreadsheetArchitect
|
|
2
2
|
module Exceptions
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class OptionTypeError < TypeError
|
|
5
|
+
def initialize(which)
|
|
6
|
+
super("Invalid data type for the #{which} option")
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class ArgumentError < ::ArgumentError
|
|
11
|
+
def initialize(msg)
|
|
12
|
+
super(msg)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class InvalidRangeError < ArgumentError
|
|
5
17
|
def initialize(type, range)
|
|
6
18
|
case type
|
|
7
19
|
when :columns, :rows
|
|
@@ -16,39 +28,33 @@ module SpreadsheetArchitect
|
|
|
16
28
|
end
|
|
17
29
|
end
|
|
18
30
|
|
|
19
|
-
class
|
|
20
|
-
def initialize(which)
|
|
21
|
-
super("Invalid data type for the #{which}")
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
class InvalidColumnError < StandardError
|
|
31
|
+
class InvalidColumnError < ArgumentError
|
|
26
32
|
def initialize(col)
|
|
27
33
|
super("Invalid Column `#{col}` given for column_types options")
|
|
28
34
|
end
|
|
29
35
|
end
|
|
30
36
|
|
|
31
|
-
class InvalidRangeStylesOptionError <
|
|
37
|
+
class InvalidRangeStylesOptionError < ArgumentError
|
|
32
38
|
def initialize(type, opt)
|
|
33
39
|
super("Invalid or missing :#{type} option for `#{opt}`. :#{type} can be an integer, range, or :all")
|
|
34
40
|
end
|
|
35
41
|
end
|
|
36
42
|
|
|
37
|
-
class NoDataError <
|
|
43
|
+
class NoDataError < ArgumentError
|
|
38
44
|
def initialize
|
|
39
45
|
super("Missing :data or :instances option")
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
|
|
43
|
-
class MultipleDataSourcesError <
|
|
49
|
+
class MultipleDataSourcesError < ArgumentError
|
|
44
50
|
def initialize
|
|
45
51
|
super("Both :data and :instances options cannot be combined, please choose one.")
|
|
46
52
|
end
|
|
47
53
|
end
|
|
48
54
|
|
|
49
|
-
class SpreadsheetColumnsNotDefinedError <
|
|
50
|
-
def initialize(klass)
|
|
51
|
-
super("The instance method `
|
|
55
|
+
class SpreadsheetColumnsNotDefinedError < ArgumentError
|
|
56
|
+
def initialize(klass, method_name='spreadsheet_columns')
|
|
57
|
+
super("The instance method `#{method_name}` is not defined on #{klass}")
|
|
52
58
|
end
|
|
53
59
|
end
|
|
54
60
|
end
|
|
@@ -32,43 +32,57 @@ module SpreadsheetArchitect
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
if
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
if options[:spreadsheet_columns]
|
|
36
|
+
if [String, Symbol].any?{|x| options[:spreadsheet_columns].is_a?(x)}
|
|
37
|
+
cols_method_name = options[:spreadsheet_columns]
|
|
38
|
+
|
|
39
|
+
if klass != SpreadsheetArchitect && !klass.instance_methods.include?(cols_method_name)
|
|
40
|
+
raise SpreadsheetArchitect::Exceptions::SpreadsheetColumnsNotDefinedError.new(klass, cols_method_name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
else
|
|
44
|
+
if klass != SpreadsheetArchitect && !klass.instance_methods.include?(:spreadsheet_columns)
|
|
45
|
+
if is_ar_model?(klass)
|
|
46
|
+
the_column_names = klass.column_names
|
|
47
|
+
headers = the_column_names.map{|x| str_titleize(x)} if needs_headers
|
|
48
|
+
columns = the_column_names.map{|x| x.to_sym}
|
|
49
|
+
else
|
|
50
|
+
raise SpreadsheetArchitect::Exceptions::SpreadsheetColumnsNotDefinedError.new(klass)
|
|
51
|
+
end
|
|
42
52
|
end
|
|
43
53
|
end
|
|
44
54
|
|
|
45
55
|
data = []
|
|
46
56
|
options[:instances].each do |instance|
|
|
47
57
|
if columns
|
|
48
|
-
data.push columns.map{|col| col.is_a?(Symbol) ? instance.
|
|
58
|
+
data.push columns.map{|col| col.is_a?(Symbol) ? instance.send(col) : col}
|
|
49
59
|
else
|
|
50
60
|
row_data = []
|
|
51
61
|
|
|
52
|
-
if
|
|
62
|
+
if options[:spreadsheet_columns]
|
|
63
|
+
if cols_method_name
|
|
64
|
+
instance_cols = instance.send(cols_method_name)
|
|
65
|
+
else
|
|
66
|
+
instance_cols = options[:spreadsheet_columns].call(instance)
|
|
67
|
+
end
|
|
68
|
+
else
|
|
53
69
|
if klass == SpreadsheetArchitect && !instance.respond_to?(:spreadsheet_columns)
|
|
54
70
|
raise SpreadsheetArchitect::Exceptions::SpreadsheetColumnsNotDefinedError.new(instance.class)
|
|
55
71
|
else
|
|
56
72
|
instance_cols = instance.spreadsheet_columns
|
|
57
73
|
end
|
|
58
|
-
else
|
|
59
|
-
instance_cols = options[:spreadsheet_columns].call(instance)
|
|
60
74
|
end
|
|
61
75
|
|
|
62
76
|
instance_cols.each_with_index do |x,i|
|
|
63
77
|
if x.is_a?(Array)
|
|
64
78
|
headers.push(x[0].to_s) if needs_headers
|
|
65
|
-
row_data.push(x[1].is_a?(Symbol) ? instance.
|
|
79
|
+
row_data.push(x[1].is_a?(Symbol) ? instance.send(x[1]) : x[1])
|
|
66
80
|
if needs_column_types
|
|
67
81
|
column_types[i] = x[2]
|
|
68
82
|
end
|
|
69
83
|
else
|
|
70
84
|
headers.push(str_titleize(x.to_s)) if needs_headers
|
|
71
|
-
row_data.push(x.is_a?(Symbol) ? instance.
|
|
85
|
+
row_data.push(x.is_a?(Symbol) ? instance.send(x) : x)
|
|
72
86
|
end
|
|
73
87
|
end
|
|
74
88
|
|
|
@@ -101,7 +115,7 @@ module SpreadsheetArchitect
|
|
|
101
115
|
klass::SPREADSHEET_OPTIONS.merge(options)
|
|
102
116
|
)
|
|
103
117
|
else
|
|
104
|
-
raise SpreadsheetArchitect::Exceptions::
|
|
118
|
+
raise SpreadsheetArchitect::Exceptions::OptionTypeError.new("#{klass}::SPREADSHEET_OPTIONS constant")
|
|
105
119
|
end
|
|
106
120
|
else
|
|
107
121
|
options = SpreadsheetArchitect.default_options.merge(options)
|
|
@@ -188,13 +202,13 @@ module SpreadsheetArchitect
|
|
|
188
202
|
end
|
|
189
203
|
|
|
190
204
|
if invalid
|
|
191
|
-
raise SpreadsheetArchitect::Exceptions::
|
|
205
|
+
raise SpreadsheetArchitect::Exceptions::OptionTypeError.new(":#{option_name} option")
|
|
192
206
|
end
|
|
193
207
|
end
|
|
194
208
|
end
|
|
195
209
|
|
|
196
210
|
def self.verify_option_types(options)
|
|
197
|
-
check_option_type(options, :spreadsheet_columns, Proc)
|
|
211
|
+
check_option_type(options, :spreadsheet_columns, [Proc, Symbol, String])
|
|
198
212
|
check_option_type(options, :data, Array)
|
|
199
213
|
check_option_type(options, :instances, Array)
|
|
200
214
|
check_option_type(options, :headers, [TrueClass, Array])
|
|
@@ -202,6 +216,7 @@ module SpreadsheetArchitect
|
|
|
202
216
|
check_option_type(options, :row_style, Hash)
|
|
203
217
|
check_option_type(options, :column_styles, Array)
|
|
204
218
|
check_option_type(options, :range_styles, Array)
|
|
219
|
+
check_option_type(options, :conditional_row_styles, Array)
|
|
205
220
|
check_option_type(options, :merges, Array)
|
|
206
221
|
check_option_type(options, :borders, Array)
|
|
207
222
|
check_option_type(options, :column_widths, Array)
|
|
@@ -148,6 +148,32 @@ module SpreadsheetArchitect
|
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
def self.conditional_styles_for_row(conditional_row_styles, row_index, row_data)
|
|
152
|
+
merged_conditional_styles = {}
|
|
153
|
+
|
|
154
|
+
conditional_row_styles.each do |x|
|
|
155
|
+
if x[:if] && x[:unless]
|
|
156
|
+
raise SpreadsheetArchitect::Exceptions::ArgumentError.new('Cannot pass both :if and :unless within the same :conditonal_row_styles entry')
|
|
157
|
+
elsif !x[:if] && !x[:unless]
|
|
158
|
+
raise SpreadsheetArchitect::Exceptions::ArgumentError.new('Must pass either :if or :unless within the each :conditonal_row_styles entry')
|
|
159
|
+
elsif !x[:styles]
|
|
160
|
+
raise SpreadsheetArchitect::Exceptions::ArgumentError.new('Must pass the :styles option within a :conditonal_row_styles entry')
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
conditions_met = (x[:if] || x[:unless]).call(row_data, row_index)
|
|
164
|
+
|
|
165
|
+
if x[:unless]
|
|
166
|
+
conditions_met = !conditions_met
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
if conditions_met
|
|
170
|
+
merged_conditional_styles.merge!(x[:styles])
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
return merged_conditional_styles
|
|
175
|
+
end
|
|
176
|
+
|
|
151
177
|
private
|
|
152
178
|
|
|
153
179
|
def self.symbolize_keys(hash={})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class CustomColumnsMethodPost < ActiveRecord::Base
|
|
2
|
+
self.table_name = :posts
|
|
3
|
+
|
|
4
|
+
include SpreadsheetArchitect
|
|
5
|
+
|
|
6
|
+
def my_custom_spreadsheet_columns
|
|
7
|
+
[
|
|
8
|
+
:name,
|
|
9
|
+
['The Content', :content],
|
|
10
|
+
:created_at,
|
|
11
|
+
['Created At Date', created_at.to_date],
|
|
12
|
+
[:asd, 'tadaaa']
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
SPREADSHEET_OPTIONS = {
|
|
17
|
+
headers: true,
|
|
18
|
+
spreadsheet_columns: :my_custom_spreadsheet_columns,
|
|
19
|
+
header_style: {background_color: 'AAAAAA', color: 'FFFFFF', align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
|
|
20
|
+
row_style: {background_color: nil, color: '000000', align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
|
|
21
|
+
sheet_name: 'My Project Export',
|
|
22
|
+
column_styles: [],
|
|
23
|
+
range_styles: [],
|
|
24
|
+
merges: [],
|
|
25
|
+
borders: [],
|
|
26
|
+
column_types: []
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
end
|