spreadsheet_architect 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|