spreadsheet_architect 3.3.0 → 4.2.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.
@@ -0,0 +1,23 @@
1
+ if Axlsx::VERSION.to_f < 3.1
2
+
3
+ Axlsx::Cell.class_eval do
4
+ private
5
+
6
+ def string_width(string, font_size)
7
+ font_scale = font_size / 10.0
8
+ (string.to_s.size + 3) * font_scale
9
+ end
10
+ end
11
+
12
+ if defined?(Axlsx::RichTextRun)
13
+ Axlsx::RichTextRun.class_eval do
14
+ private
15
+
16
+ def string_width(string, font_size)
17
+ font_scale = font_size / 10.0
18
+ string.to_s.size * font_scale
19
+ end
20
+ end
21
+ end
22
+
23
+ end
@@ -2,6 +2,7 @@ require 'csv'
2
2
 
3
3
  module SpreadsheetArchitect
4
4
  module ClassMethods
5
+
5
6
  def to_csv(opts={})
6
7
  opts = SpreadsheetArchitect::Utils.get_options(opts, self)
7
8
  options = SpreadsheetArchitect::Utils.get_cell_data(opts, self)
@@ -18,5 +19,6 @@ module SpreadsheetArchitect
18
19
  end
19
20
  end
20
21
  end
22
+
21
23
  end
22
24
  end
@@ -2,6 +2,7 @@ require 'rodf'
2
2
 
3
3
  module SpreadsheetArchitect
4
4
  module ClassMethods
5
+
5
6
  def to_ods(opts={})
6
7
  return to_rodf_spreadsheet(opts).bytes
7
8
  end
@@ -1,12 +1,11 @@
1
1
  require 'axlsx'
2
2
  require 'axlsx_styler'
3
3
 
4
- require 'spreadsheet_architect/monkey_patches/axlsx_column_width'
4
+ require 'spreadsheet_architect/axlsx_string_width_patch'
5
5
 
6
6
  module SpreadsheetArchitect
7
- XLSX_COLUMN_TYPES = [:string, :integer, :float, :boolean].freeze
8
-
9
7
  module ClassMethods
8
+
10
9
  def to_xlsx(opts={})
11
10
  return to_axlsx_package(opts).to_stream.read
12
11
  end
@@ -18,7 +17,7 @@ module SpreadsheetArchitect
18
17
  if options[:column_types] && !(options[:column_types].compact.collect(&:to_sym) - SpreadsheetArchitect::XLSX_COLUMN_TYPES).empty?
19
18
  raise SpreadsheetArchitect::Exceptions::ArgumentError.new("Invalid column type. Valid XLSX values are #{SpreadsheetArchitect::XLSX_COLUMN_TYPES}")
20
19
  end
21
-
20
+
22
21
  header_style = SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(options[:header_style])
23
22
  row_style = SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(options[:row_style])
24
23
 
@@ -48,10 +47,10 @@ module SpreadsheetArchitect
48
47
 
49
48
  if options[:conditional_row_styles]
50
49
  conditional_styles_for_row = SpreadsheetArchitect::Utils::XLSX.conditional_styles_for_row(options[:conditional_row_styles], row_index, header_row)
51
-
50
+
52
51
  unless conditional_styles_for_row.empty?
53
52
  sheet.add_style(
54
- "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
53
+ "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
55
54
  SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(conditional_styles_for_row)
56
55
  )
57
56
  end
@@ -108,11 +107,13 @@ module SpreadsheetArchitect
108
107
  sheet.add_row row_data, style: styles, types: types
109
108
 
110
109
  if options[:conditional_row_styles]
110
+ options[:conditional_row_styles] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:conditional_row_styles])
111
+
111
112
  conditional_styles_for_row = SpreadsheetArchitect::Utils::XLSX.conditional_styles_for_row(options[:conditional_row_styles], row_index, row_data)
112
-
113
+
113
114
  unless conditional_styles_for_row.empty?
114
115
  sheet.add_style(
115
- "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
116
+ "#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES.first}#{row_index+1}:#{SpreadsheetArchitect::Utils::XLSX::COL_NAMES[max_row_length-1]}#{row_index+1}",
116
117
  SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(conditional_styles_for_row)
117
118
  )
118
119
  end
@@ -128,6 +129,8 @@ module SpreadsheetArchitect
128
129
  end
129
130
 
130
131
  if options[:borders]
132
+ options[:borders] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:borders])
133
+
131
134
  options[:borders].each do |x|
132
135
  if x[:range].is_a?(Hash)
133
136
  x[:range] = SpreadsheetArchitect::Utils::XLSX.range_hash_to_str(x[:range], max_row_length, num_rows)
@@ -140,6 +143,8 @@ module SpreadsheetArchitect
140
143
  end
141
144
 
142
145
  if options[:column_styles]
146
+ options[:column_styles] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:column_styles])
147
+
143
148
  options[:column_styles].each do |x|
144
149
  start_row = (options[:headers] ? options[:headers].count : 0) + 1
145
150
 
@@ -171,6 +176,8 @@ module SpreadsheetArchitect
171
176
  end
172
177
 
173
178
  if options[:range_styles]
179
+ options[:range_styles] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:range_styles])
180
+
174
181
  options[:range_styles].each do |x|
175
182
  styles = SpreadsheetArchitect::Utils::XLSX.convert_styles_to_axlsx(x[:styles])
176
183
 
@@ -185,6 +192,8 @@ module SpreadsheetArchitect
185
192
  end
186
193
 
187
194
  if options[:merges]
195
+ options[:merges] = SpreadsheetArchitect::Utils.hash_array_symbolize_keys(options[:merges])
196
+
188
197
  options[:merges].each do |x|
189
198
  if x[:range].is_a?(Hash)
190
199
  x[:range] = SpreadsheetArchitect::Utils::XLSX.range_hash_to_str(x[:range], max_row_length, num_rows)
@@ -195,9 +204,48 @@ module SpreadsheetArchitect
195
204
  sheet.merge_cells x[:range]
196
205
  end
197
206
  end
207
+
208
+ if options[:freeze_headers]
209
+ sheet.sheet_view.pane do |pane|
210
+ pane.state = :frozen
211
+ pane.y_split = options[:headers].count
212
+ end
213
+
214
+ elsif options[:freeze]
215
+ options[:freeze] = SpreadsheetArchitect::Utils.symbolize_keys(options[:freeze])
216
+
217
+ sheet.sheet_view.pane do |pane|
218
+ pane.state = :frozen
219
+
220
+ ### Currently not working
221
+ #if options[:freeze][:active_pane]
222
+ # Axlsx.validate_pane_type(options[:freeze][:active_pane])
223
+ # pane.active_pane = options[:freeze][:active_pane]
224
+ #else
225
+ # pane.active_pane = :bottom_right
226
+ #end
227
+
228
+ if !options[:freeze][:rows]
229
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new("The :rows key must be specified in the :freeze option hash")
230
+ elsif options[:freeze][:rows].is_a?(Range)
231
+ pane.y_split = options[:freeze][:rows].count
232
+ else
233
+ pane.y_split = 1
234
+ end
235
+
236
+ if options[:freeze][:columns] && options[:freeze][:columns] != :all
237
+ if options[:freeze][:columns].is_a?(Range)
238
+ pane.x_split = options[:freeze][:columns].count
239
+ else
240
+ pane.x_split = 1
241
+ end
242
+ end
243
+ end
244
+ end
198
245
  end
199
246
 
200
247
  return package
201
248
  end
249
+
202
250
  end
203
251
  end
@@ -7,9 +7,12 @@ module SpreadsheetArchitect
7
7
  data = options[:data]
8
8
  end
9
9
 
10
- if !options[:data] && options[:headers] == true
10
+ if options[:headers] == true
11
11
  headers = []
12
- needs_headers = true
12
+
13
+ if !options[:data]
14
+ needs_headers = true
15
+ end
13
16
  elsif options[:headers].is_a?(Array)
14
17
  headers = options[:headers]
15
18
  else
@@ -109,20 +112,22 @@ module SpreadsheetArchitect
109
112
  def self.get_options(options, klass)
110
113
  verify_option_types(options)
111
114
 
112
- if defined?(klass::SPREADSHEET_OPTIONS)
113
- if klass::SPREADSHEET_OPTIONS.is_a?(Hash)
114
- options = SpreadsheetArchitect.default_options.merge(
115
- klass::SPREADSHEET_OPTIONS.merge(options)
116
- )
115
+ if !options[:skip_defaults]
116
+ if defined?(klass::SPREADSHEET_OPTIONS)
117
+ if klass::SPREADSHEET_OPTIONS.is_a?(Hash)
118
+ defaults = SpreadsheetArchitect.default_options.merge(klass::SPREADSHEET_OPTIONS)
119
+ else
120
+ raise SpreadsheetArchitect::Exceptions::OptionTypeError.new("#{klass}::SPREADSHEET_OPTIONS constant")
121
+ end
117
122
  else
118
- raise SpreadsheetArchitect::Exceptions::OptionTypeError.new("#{klass}::SPREADSHEET_OPTIONS constant")
123
+ defaults = SpreadsheetArchitect.default_options
119
124
  end
120
- else
121
- options = SpreadsheetArchitect.default_options.merge(options)
125
+
126
+ options = defaults.merge(options)
122
127
  end
123
128
 
124
129
  if !options[:headers]
125
- options[:header_style] = false
130
+ options.delete(:header_style)
126
131
  end
127
132
 
128
133
  if !options[:sheet_name]
@@ -137,6 +142,14 @@ module SpreadsheetArchitect
137
142
  end
138
143
  end
139
144
 
145
+ if options[:freeze]
146
+ if options[:freeze_headers]
147
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new('Cannot use both :freeze and :freeze_headers options at the same time')
148
+ elsif options[:freeze].is_a?(Hash) && !options[:freeze][:rows]
149
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new('Must provide a :rows key when passing a hash to the :freeze option')
150
+ end
151
+ end
152
+
140
153
  return options
141
154
  end
142
155
 
@@ -208,23 +221,22 @@ module SpreadsheetArchitect
208
221
  end
209
222
 
210
223
  def self.verify_option_types(options)
211
- check_option_type(options, :spreadsheet_columns, [Proc, Symbol, String])
212
- check_option_type(options, :data, Array)
213
- check_option_type(options, :instances, Array)
214
- check_option_type(options, :headers, [TrueClass, FalseClass, Array])
215
- check_option_type(options, :header_style, Hash)
216
- check_option_type(options, :row_style, Hash)
217
- check_option_type(options, :column_styles, Array)
218
- check_option_type(options, :range_styles, Array)
219
- check_option_type(options, :conditional_row_styles, Array)
220
- check_option_type(options, :merges, Array)
221
- check_option_type(options, :borders, Array)
222
- check_option_type(options, :column_widths, Array)
223
- check_option_type(options, :column_types, Array)
224
+ options = self.symbolize_keys(options, shallow: true)
225
+
226
+ bad_keys = options.keys - ALLOWED_OPTIONS.keys
227
+
228
+ if bad_keys.any?
229
+ raise SpreadsheetArchitect::Exceptions::ArgumentError.new("Invalid options provided: #{bad_keys}")
230
+ end
231
+
232
+ ALLOWED_OPTIONS.each do |key, allowed_types|
233
+ check_option_type(options, key, allowed_types)
234
+ end
224
235
  end
225
236
 
226
- def self.stringify_keys(hash={})
237
+ def self.stringify_keys(hash)
227
238
  new_hash = {}
239
+
228
240
  hash.each do |k,v|
229
241
  if v.is_a?(Hash)
230
242
  new_hash[k.to_s] = self.stringify_keys(v)
@@ -232,7 +244,54 @@ module SpreadsheetArchitect
232
244
  new_hash[k.to_s] = v
233
245
  end
234
246
  end
247
+
235
248
  return new_hash
236
249
  end
250
+
251
+ def self.symbolize_keys(hash, shallow: false)
252
+ new_hash = {}
253
+
254
+ hash.each do |k,v|
255
+ if v.is_a?(Hash)
256
+ new_hash[k.to_sym] = shallow ? v : self.symbolize_keys(v)
257
+ else
258
+ new_hash[k.to_sym] = v
259
+ end
260
+ end
261
+
262
+ return new_hash
263
+ end
264
+
265
+ def self.hash_array_symbolize_keys(array)
266
+ new_array = []
267
+
268
+ array.each_with_index do |x,i|
269
+ new_array[i] = x.is_a?(Hash) ? self.symbolize_keys(x) : x
270
+ end
271
+
272
+ return new_array
273
+ end
274
+
275
+ ALLOWED_OPTIONS = {
276
+ borders: Array,
277
+ column_styles: Array,
278
+ conditional_row_styles: Array,
279
+ column_widths: Array,
280
+ column_types: Array,
281
+ data: Array,
282
+ freeze_headers: [TrueClass, FalseClass],
283
+ freeze: Hash,
284
+ headers: [TrueClass, FalseClass, Array],
285
+ header_style: Hash,
286
+ instances: Array,
287
+ merges: Array,
288
+ range_styles: Array,
289
+ skip_defaults: [TrueClass, FalseClass],
290
+ row_style: Hash,
291
+ sheet_name: String,
292
+ spreadsheet_columns: [Proc, Symbol, String],
293
+ }.freeze
294
+
295
+
237
296
  end
238
297
  end
@@ -25,8 +25,29 @@ module SpreadsheetArchitect
25
25
  end
26
26
 
27
27
  def self.convert_styles_to_axlsx(styles={})
28
- styles = {} unless styles.is_a?(Hash)
29
- styles = self.symbolize_keys(styles)
28
+ if [nil, false, true].include?(styles)
29
+ return {}
30
+ end
31
+
32
+ styles = SpreadsheetArchitect::Utils.symbolize_keys(styles)
33
+
34
+ ### BOOLEAN VALUES
35
+ if styles[:b].nil? && styles.has_key?(:bold)
36
+ styles[:b] = !!styles.delete(:bold)
37
+ end
38
+
39
+ if styles[:i].nil? && styles.has_key?(:italic)
40
+ styles[:i] = !!styles.delete(:italic)
41
+ end
42
+
43
+ if styles[:u].nil? && styles.has_key?(:underline)
44
+ styles[:u] = !!styles.delete(:underline)
45
+ end
46
+
47
+ ### OTHER VALUES
48
+ if styles[:sz].nil? && !styles[:font_size].nil?
49
+ styles[:sz] = styles.delete(:font_size)
50
+ end
30
51
 
31
52
  if styles[:fg_color].nil? && styles[:color] && styles[:color].respond_to?(:sub) && !styles[:color].empty?
32
53
  styles[:fg_color] = styles.delete(:color).sub('#','')
@@ -36,7 +57,7 @@ module SpreadsheetArchitect
36
57
  styles[:bg_color] = styles.delete(:background_color).sub('#','')
37
58
  end
38
59
 
39
- if styles[:alignment].nil? && styles[:align]
60
+ if styles[:alignment].nil? && [:align, :valign, :wrap_text].any?{|k| styles.has_key?(k) }
40
61
  if styles[:align].is_a?(Hash)
41
62
  styles[:alignment] = {
42
63
  horizontal: styles[:align][:horizontal],
@@ -44,34 +65,22 @@ module SpreadsheetArchitect
44
65
  wrap_text: styles[:align][:wrap_text]
45
66
  }
46
67
  else
47
- styles[:alignment] = {horizontal: (styles.delete(:align) || nil) }
68
+ styles[:alignment] = {horizontal: styles.delete(:align), vertical: styles.delete(:valign), wrap_text: styles.delete(:wrap_text) }.compact
48
69
  end
49
-
50
- styles.delete(:align)
51
- end
52
-
53
- if styles[:b].nil?
54
- styles[:b] = styles.delete(:bold) || nil
55
- end
56
-
57
- if styles[:sz].nil?
58
- styles[:sz] = styles.delete(:font_size) || nil
59
- end
60
-
61
- if styles[:i].nil?
62
- styles[:i] = styles.delete(:italic) || nil
63
- end
64
-
65
- if styles[:u].nil?
66
- styles[:u] = styles.delete(:underline) || nil
67
70
  end
68
71
 
72
+ ### COMMENT SEEMS WRONG, TO BE RECONFIRMED
69
73
  ### If `:u` is false instead of nil, it may be incorrectly rendered as true in Excel
70
- if styles[:u] == false
71
- styles[:u] = nil
74
+ #if styles[:u] == false
75
+ # styles[:u] = nil
76
+ #end
77
+
78
+ ### ENSURE CLEANUP OF ALL ALIAS KEYS
79
+ [:bold, :font_size, :italic, :underline, :align, :valign, :wrap_text, :color, :background_color].each do |k|
80
+ styles.delete(k)
72
81
  end
73
82
 
74
- styles.delete_if{|k,v| v.nil?}
83
+ return styles
75
84
  end
76
85
 
77
86
  def self.range_hash_to_str(hash, num_columns, num_rows)
@@ -176,18 +185,6 @@ module SpreadsheetArchitect
176
185
 
177
186
  private
178
187
 
179
- def self.symbolize_keys(hash={})
180
- new_hash = {}
181
- hash.each do |k, v|
182
- if v.is_a?(Hash)
183
- new_hash[k.to_sym] = self.symbolize_keys(v)
184
- else
185
- new_hash[k.to_sym] = v
186
- end
187
- end
188
- new_hash
189
- end
190
-
191
188
  ### Limit of 16384 columns as per Excel limitations
192
189
  COL_NAMES = Array('A'..'XFD').freeze
193
190
 
@@ -1,3 +1,3 @@
1
1
  module SpreadsheetArchitect
2
- VERSION = "3.3.0"
2
+ VERSION = "4.2.0"
3
3
  end
@@ -15,8 +15,11 @@ class SpreadsheetsController < ApplicationController
15
15
  end
16
16
 
17
17
  def alt_xlsx
18
- @posts = Post.all
19
- respond_with @posts
18
+ if Rails::VERSION::MAJOR >= 5
19
+ @posts = Post.all
20
+
21
+ respond_with @posts
22
+ end
20
23
  end
21
24
 
22
25
  def test_xlsx