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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +25 -12
  4. data/lib/spreadsheet_architect.rb +1 -1
  5. data/lib/spreadsheet_architect/class_methods/xlsx.rb +28 -0
  6. data/lib/spreadsheet_architect/exceptions.rb +20 -14
  7. data/lib/spreadsheet_architect/monkey_patches/axlsx_column_width.rb +3 -0
  8. data/lib/spreadsheet_architect/utils.rb +31 -16
  9. data/lib/spreadsheet_architect/utils/xlsx.rb +26 -0
  10. data/lib/spreadsheet_architect/version.rb +1 -1
  11. data/test/dummy_app/app/models/custom_columns_method_post.rb +29 -0
  12. data/test/dummy_app/log/test.log +42144 -61
  13. data/test/dummy_app/tmp/axlsx-master/integration/alt_xlsx.xlsx +0 -0
  14. data/test/dummy_app/tmp/axlsx-master/integration/ods.ods +0 -0
  15. data/test/dummy_app/tmp/axlsx-master/integration/xlsx.xlsx +0 -0
  16. data/test/dummy_app/tmp/axlsx-master/kitchen_sink.ods +0 -0
  17. data/test/dummy_app/tmp/axlsx-master/kitchen_sink.xlsx +0 -0
  18. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/data.csv +1 -1
  19. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/data.ods +0 -0
  20. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/data.xlsx +0 -0
  21. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/empty.csv +1 -0
  22. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/empty.ods +0 -0
  23. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/empty.xlsx +0 -0
  24. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/instances.csv +5 -5
  25. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/instances.ods +0 -0
  26. data/test/dummy_app/tmp/axlsx-master/models/ActiveModelObject/instances.xlsx +0 -0
  27. data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/data.csv +3 -0
  28. data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/data.ods +0 -0
  29. data/test/dummy_app/tmp/axlsx-master/models/{SpreadsheetArchitect → CustomColumnsMethodPost}/data.xlsx +0 -0
  30. data/test/dummy_app/tmp/axlsx-master/models/{SpreadsheetArchitect → CustomColumnsMethodPost}/empty.csv +0 -0
  31. data/test/dummy_app/tmp/axlsx-master/models/{SpreadsheetArchitect → CustomColumnsMethodPost}/empty.ods +0 -0
  32. data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/empty.xlsx +0 -0
  33. data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/instances.csv +6 -0
  34. data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/instances.ods +0 -0
  35. data/test/dummy_app/tmp/axlsx-master/models/CustomColumnsMethodPost/instances.xlsx +0 -0
  36. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/data.csv +1 -1
  37. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/data.ods +0 -0
  38. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/data.xlsx +0 -0
  39. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/empty.csv +1 -0
  40. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/empty.ods +0 -0
  41. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/empty.xlsx +0 -0
  42. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/instances.csv +5 -5
  43. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/instances.ods +0 -0
  44. data/test/dummy_app/tmp/axlsx-master/models/CustomPost/instances.xlsx +0 -0
  45. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/data.csv +1 -1
  46. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/data.ods +0 -0
  47. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/data.xlsx +0 -0
  48. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/empty.ods +0 -0
  49. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/empty.xlsx +0 -0
  50. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/instances.csv +5 -5
  51. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/instances.ods +0 -0
  52. data/test/dummy_app/tmp/axlsx-master/models/LegacyPlainRubyObject/instances.xlsx +0 -0
  53. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/data.csv +1 -1
  54. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/data.ods +0 -0
  55. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/data.xlsx +0 -0
  56. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/empty.ods +0 -0
  57. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/empty.xlsx +0 -0
  58. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/instances.csv +5 -5
  59. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/instances.ods +0 -0
  60. data/test/dummy_app/tmp/axlsx-master/models/PlainRubyObject/instances.xlsx +0 -0
  61. data/test/dummy_app/tmp/axlsx-master/models/Post/data.csv +1 -1
  62. data/test/dummy_app/tmp/axlsx-master/models/Post/data.ods +0 -0
  63. data/test/dummy_app/tmp/axlsx-master/models/Post/data.xlsx +0 -0
  64. data/test/dummy_app/tmp/axlsx-master/models/Post/empty.csv +1 -0
  65. data/test/dummy_app/tmp/axlsx-master/models/Post/empty.ods +0 -0
  66. data/test/dummy_app/tmp/axlsx-master/models/Post/empty.xlsx +0 -0
  67. data/test/dummy_app/tmp/axlsx-master/models/Post/instances.csv +6 -0
  68. data/test/dummy_app/tmp/axlsx-master/models/Post/instances.ods +0 -0
  69. data/test/dummy_app/tmp/axlsx-master/models/Post/instances.xlsx +0 -0
  70. data/test/dummy_app/tmp/axlsx-master/multi_sheet.ods +0 -0
  71. data/test/dummy_app/tmp/axlsx-master/multi_sheet.xlsx +0 -0
  72. data/test/models/all_models_test.rb +23 -18
  73. data/test/test_helper.rb +5 -1
  74. data/test/unit/exceptions_test.rb +22 -7
  75. data/test/unit/kitchen_sink_test.rb +15 -0
  76. data/test/unit/utils_test.rb +2 -2
  77. metadata +28 -14
  78. data/test/dummy_app/tmp/axlsx-master/models/SpreadsheetArchitect/data.csv +0 -3
  79. data/test/dummy_app/tmp/axlsx-master/models/SpreadsheetArchitect/data.ods +0 -0
  80. 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: 9373288a5139e4300488dc26c66aa5783ddca415a5a01c9b509b7e19a4716055
4
- data.tar.gz: c8c58c68932e5fc5e36c3f73c3b5d106e90261a64bdbf046fa133e940c2fbb94
3
+ metadata.gz: c8e7dd80b1f4347e578f1a4ab5a8224ee78f03d8f5c6cfac34882099fb6307f3
4
+ data.tar.gz: 90a5a75b3efe90265123b140373080fe7b780e51b7f6cbf9d6afc30adf9f656d
5
5
  SHA512:
6
- metadata.gz: ef1f1ac7cd4187c9dae74efcc001099cc190a39a11f7f15341c0649dfac21c13347f8f537274a664e6365229d31a32b58dc60786ea3f7127fdfd18828ba91175
7
- data.tar.gz: 84b8abd050f5924b25ba15012dde4cd5500ad85de2fa16149c0d0eda0e3fa85ef49370a7d3d5f6a2f3c7ede3f176c6ca23fae02e811bf18711b3b6c3e2fbc09f
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, if `spreadsheet_columns` is passed as an option, this instance method does not need to be defined on the class. If defined on the class then naturally this will override it.
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>*Array*| If using the instances option or on a ActiveRecord relation, this defaults to the classes custom `spreadsheet_columns` method or any custom defaults defined.<br>If none of those then falls back to `self.column_names` for ActiveRecord models. | Cannot be used with the `:data` option.<br><br>Use this option to override or define the spreadsheet columns. |
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/spreadsheet_architect/kitchen_sink_test.rb)|
229
- |**range_styles**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/spreadsheet_architect/kitchen_sink_test.rb)|
230
- |**merges**<br>*Array*||Merge cells. [See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/spreadsheet_architect/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.|
231
- |**borders**<br>*Array*||[See this example for usage](https://github.com/westonganger/spreadsheet_architect/blob/master/test/spreadsheet_architect/kitchen_sink_test.rb)|
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`. For more details
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>*Array*| If using the instances option or on a ActiveRecord relation, this defaults to the classes custom `spreadsheet_columns` method or any custom defaults defined.<br>If none of those then falls back to `self.column_names` for ActiveRecord models. | Cannot be used with the `:data` option.<br><br>Use this option to override or define the spreadsheet columns. |
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>*Array*| If using the instances option or on a ActiveRecord relation, this defaults to the classes custom `spreadsheet_columns` method or any custom defaults defined.<br>If none of those then falls back to `self.column_names` for ActiveRecord models. | Cannot be used with the `:data` option.<br><br>Use this option to override or define the spreadsheet columns. |
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/spreadsheet_architect/kitchen_sink_test.rb
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::InvalidTypeError.new("SpreadsheetArchitect.default_options")
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 InvalidRangeError < StandardError
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 InvalidTypeError < StandardError
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 < StandardError
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 < StandardError
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 < StandardError
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 < StandardError
50
- def initialize(klass)
51
- super("The instance method `spreadsheet_columns` is not defined on #{klass}")
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
@@ -15,6 +15,9 @@ if RUBY_VERSION.to_f >= 2
15
15
  @custom_width = true
16
16
  @best_fit = true
17
17
  @width = v + 5
18
+ else
19
+ @custom_width = true
20
+ @width = v
18
21
  end
19
22
  end
20
23
  end
@@ -32,43 +32,57 @@ module SpreadsheetArchitect
32
32
  end
33
33
  end
34
34
 
35
- if !options[:spreadsheet_columns] && klass != SpreadsheetArchitect && !klass.instance_methods.include?(:spreadsheet_columns)
36
- if is_ar_model?(klass)
37
- the_column_names = klass.column_names
38
- headers = the_column_names.map{|x| str_titleize(x)} if needs_headers
39
- columns = the_column_names.map{|x| x.to_sym}
40
- else
41
- raise SpreadsheetArchitect::Exceptions::SpreadsheetColumnsNotDefinedError.new(klass)
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.instance_eval(col.to_s) : col}
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 !options[:spreadsheet_columns]
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.instance_eval(x[1].to_s) : x[1])
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.instance_eval(x.to_s) : x)
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::InvalidTypeError.new("#{klass}::SPREADSHEET_OPTIONS constant")
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::InvalidTypeError.new(":#{option_name} option")
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={})
@@ -1,3 +1,3 @@
1
1
  module SpreadsheetArchitect
2
- VERSION = "3.0.0"
2
+ VERSION = "3.1.0"
3
3
  end
@@ -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