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.
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