workbook 0.8.0 → 0.8.2

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +21 -0
  3. data/.gitignore +4 -1
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +4 -4
  6. data/CHANGELOG.md +16 -0
  7. data/Gemfile +4 -2
  8. data/README.md +9 -7
  9. data/Rakefile +9 -7
  10. data/lib/workbook/book.rb +74 -61
  11. data/lib/workbook/cell.rb +59 -12
  12. data/lib/workbook/column.rb +33 -29
  13. data/lib/workbook/format.rb +24 -23
  14. data/lib/workbook/generatetypes.rb +6 -5
  15. data/lib/workbook/modules/cache.rb +8 -7
  16. data/lib/workbook/modules/cell.rb +78 -99
  17. data/lib/workbook/modules/diff_sort.rb +93 -82
  18. data/lib/workbook/modules/raw_objects_storage.rb +7 -7
  19. data/lib/workbook/modules/type_parser.rb +31 -21
  20. data/lib/workbook/nil_value.rb +5 -9
  21. data/lib/workbook/readers/csv_reader.rb +7 -9
  22. data/lib/workbook/readers/ods_reader.rb +49 -49
  23. data/lib/workbook/readers/txt_reader.rb +7 -7
  24. data/lib/workbook/readers/xls_reader.rb +22 -32
  25. data/lib/workbook/readers/xls_shared.rb +107 -116
  26. data/lib/workbook/readers/xlsx_reader.rb +47 -46
  27. data/lib/workbook/row.rb +100 -83
  28. data/lib/workbook/sheet.rb +48 -37
  29. data/lib/workbook/table.rb +97 -71
  30. data/lib/workbook/template.rb +13 -14
  31. data/lib/workbook/types/date.rb +2 -1
  32. data/lib/workbook/types/false.rb +1 -1
  33. data/lib/workbook/types/false_class.rb +2 -1
  34. data/lib/workbook/types/nil.rb +1 -1
  35. data/lib/workbook/types/nil_class.rb +3 -2
  36. data/lib/workbook/types/numeric.rb +3 -2
  37. data/lib/workbook/types/string.rb +3 -2
  38. data/lib/workbook/types/time.rb +3 -2
  39. data/lib/workbook/types/true.rb +1 -1
  40. data/lib/workbook/types/true_class.rb +3 -2
  41. data/lib/workbook/version.rb +3 -2
  42. data/lib/workbook/writers/csv_table_writer.rb +11 -12
  43. data/lib/workbook/writers/html_writer.rb +35 -37
  44. data/lib/workbook/writers/json_table_writer.rb +9 -10
  45. data/lib/workbook/writers/xls_writer.rb +31 -35
  46. data/lib/workbook/writers/xlsx_writer.rb +46 -28
  47. data/lib/workbook.rb +17 -14
  48. data/test/helper.rb +8 -5
  49. data/test/test_book.rb +43 -38
  50. data/test/test_column.rb +29 -25
  51. data/test/test_format.rb +53 -55
  52. data/test/test_functional.rb +9 -8
  53. data/test/test_modules_cache.rb +20 -17
  54. data/test/test_modules_cell.rb +49 -46
  55. data/test/test_modules_table_diff_sort.rb +56 -63
  56. data/test/test_modules_type_parser.rb +63 -31
  57. data/test/test_readers_csv_reader.rb +50 -42
  58. data/test/test_readers_ods_reader.rb +30 -31
  59. data/test/test_readers_txt_reader.rb +23 -23
  60. data/test/test_readers_xls_reader.rb +22 -23
  61. data/test/test_readers_xls_shared.rb +5 -4
  62. data/test/test_readers_xlsx_reader.rb +46 -37
  63. data/test/test_row.rb +107 -109
  64. data/test/test_sheet.rb +43 -35
  65. data/test/test_table.rb +84 -60
  66. data/test/test_template.rb +18 -15
  67. data/test/test_types_date.rb +7 -7
  68. data/test/test_writers_csv_writer.rb +24 -0
  69. data/test/test_writers_html_writer.rb +44 -41
  70. data/test/test_writers_json_writer.rb +11 -9
  71. data/test/test_writers_xls_writer.rb +52 -35
  72. data/test/test_writers_xlsx_writer.rb +64 -34
  73. data/workbook.gemspec +26 -27
  74. metadata +93 -27
@@ -1,6 +1,7 @@
1
- # -*- encoding : utf-8 -*-
2
1
  # frozen_string_literal: true
3
- require 'workbook/modules/raw_objects_storage'
2
+ # frozen_string_literal: true
3
+
4
+ require "workbook/modules/raw_objects_storage"
4
5
 
5
6
  module Workbook
6
7
  # Format is an object used for maintinaing a cell's formatting. It can belong to many cells. It maintains a relation to the raw template's equivalent, to preserve attributes Workbook cannot modify/access.
@@ -24,25 +25,25 @@ module Workbook
24
25
  #
25
26
  # @param [Workbook::Format, Hash] options (e.g. :background, :color, :background_color, :font_weight (integer or css-type labels)
26
27
  # @return [String] the name of the format, default: nil
27
- def initialize options={}, name=nil
28
+ def initialize options = {}, name = nil
28
29
  if options.is_a? String
29
30
  name = options
30
31
  else
31
- options.each {|k,v| self[k]=v}
32
+ options.each { |k, v| self[k] = v }
32
33
  end
33
34
  self.name = name
34
35
  end
35
36
 
36
37
  # Does the current format feature a background *color*? (not black or white or transparant).
37
- def has_background_color? color=:any
38
+ def has_background_color? color = :any
38
39
  bg_color = flattened[:background_color] ? flattened[:background_color].to_s.downcase : nil
39
40
 
40
- if color != :any and bg_color
41
- return bg_color == color.to_s.downcase
41
+ if (color != :any) && bg_color
42
+ bg_color == color.to_s.downcase
42
43
  elsif bg_color
43
- return !(flattened[:background_color].downcase=='#ffffff' or flattened[:background_color]=='#000000')
44
+ !((flattened[:background_color].downcase == "#ffffff") || (flattened[:background_color] == "#000000"))
44
45
  else
45
- return false
46
+ false
46
47
  end
47
48
  end
48
49
 
@@ -50,9 +51,9 @@ module Workbook
50
51
  # @return String very basic CSS styling string
51
52
  def to_css
52
53
  css_parts = []
53
- background = [flattened[:background_color].to_s,flattened[:background].to_s].join(" ").strip
54
- css_parts.push("background: #{background}") if background and background != ""
55
- css_parts.push("color: #{flattened[:color].to_s}") if flattened[:color]
54
+ background = [flattened[:background_color].to_s, flattened[:background].to_s].join(" ").strip
55
+ css_parts.push("background: #{background}") if background && (background != "")
56
+ css_parts.push("color: #{flattened[:color]}") if flattened[:color]
56
57
  css_parts.join("; ")
57
58
  end
58
59
 
@@ -60,22 +61,22 @@ module Workbook
60
61
  # @param [Workbook::Format] other_format
61
62
  # @return [Workbook::Format] a new resulting Workbook::Format
62
63
  def merge(other_format)
63
- self.remove_all_raws!
64
- self.merge_hash(other_format)
64
+ remove_all_raws!
65
+ merge_hash(other_format)
65
66
  end
66
67
 
67
68
  # Applies the formatting options of self with another, removes as a consequence the reference to the raw object's equivalent.
68
69
  # @param [Workbook::Format] other_format
69
70
  # @return [Workbook::Format] self
70
71
  def merge!(other_format)
71
- self.remove_all_raws!
72
- self.merge_hash!(other_format)
72
+ remove_all_raws!
73
+ merge_hash!(other_format)
73
74
  end
74
75
 
75
76
  # returns an array of all formats this style is inheriting from (including itself)
76
77
  # @return [Array<Workbook::Format>] an array of Workbook::Formats
77
78
  def formats
78
- formats=[]
79
+ formats = []
79
80
  f = self
80
81
  formats << f
81
82
  while f.parent
@@ -88,23 +89,23 @@ module Workbook
88
89
  # returns an array of all format-names this style is inheriting from (and this style)
89
90
  # @return [Array<String>] an array of Workbook::Formats
90
91
  def all_names
91
- formats.collect{|a| a.name}
92
+ formats.collect { |a| a.name }
92
93
  end
93
94
 
94
95
  # Applies the formatting options of self with its parents until no parent can be found
95
96
  # @return [Workbook::Format] new Workbook::Format that is the result of merging current style with all its parent's styles.
96
97
  def flattened
97
- ff=Workbook::Format.new()
98
- formats.each{|a| ff.merge!(a) }
99
- return ff
98
+ ff = Workbook::Format.new
99
+ formats.each { |a| ff.merge!(a) }
100
+ ff
100
101
  end
101
102
 
102
103
  # Formatting is sometimes the only way to detect the cells' type.
103
104
  def derived_type
104
105
  if self[:numberformat]
105
- if self[:numberformat].to_s.match("h")
106
+ if self[:numberformat].to_s.match?("h")
106
107
  :time
107
- elsif self[:numberformat].to_s.match(/y/i)
108
+ elsif /y/i.match?(self[:numberformat].to_s)
108
109
  :date
109
110
  end
110
111
  end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
- ["Numeric","String","Time","Date","TrueClass","FalseClass","NilClass"].each do |type|
3
- f = File.open(File.join(File.dirname(__FILE__),"types","#{type}.rb"),'w+')
2
+
3
+ ["Numeric", "String", "Time", "Date", "TrueClass", "FalseClass", "NilClass"].each do |type|
4
+ f = File.open(File.join(File.dirname(__FILE__), "types", "#{type}.rb"), "w+")
4
5
  puts f.inspect
5
- doc="require 'workbook/cell'
6
+ doc = "require 'workbook/cell'
6
7
 
7
8
  module Workbook
8
9
  module Types
@@ -11,5 +12,5 @@ module Workbook
11
12
  end
12
13
  end
13
14
  end"
14
- f.write(doc)
15
- end
15
+ f.write(doc)
16
+ end
@@ -1,5 +1,6 @@
1
- # -*- encoding : utf-8 -*-
2
1
  # frozen_string_literal: true
2
+ # frozen_string_literal: true
3
+
3
4
  module Workbook
4
5
  module Modules
5
6
  # Adds simple caching
@@ -31,12 +32,12 @@ module Workbook
31
32
 
32
33
  # Check if currently stored key is available and still valid
33
34
  # @return [Boolean]
34
- def valid_cache_key?(key, expires=nil)
35
+ def valid_cache_key?(key, expires = nil)
35
36
  cache_valid_from
36
- rv = (@cache[key] and (@cache[key][:inserted_at] > cache_valid_from) and (expires.nil? or @cache[key][:inserted_at] < expires)) ? true : false
37
- rv
37
+ @cache[key] && (@cache[key][:inserted_at] > cache_valid_from) && (expires.nil? || (@cache[key][:inserted_at] < expires))
38
38
  end
39
- def fetch_cache(key, expires=nil)
39
+
40
+ def fetch_cache(key, expires = nil)
40
41
  @cache ||= {}
41
42
  if valid_cache_key?(key, expires)
42
43
  return @cache[key][:value]
@@ -46,8 +47,8 @@ module Workbook
46
47
  inserted_at: Time.now
47
48
  }
48
49
  end
49
- return @cache[key][:value]
50
+ @cache[key][:value]
50
51
  end
51
52
  end
52
53
  end
53
- end
54
+ end
@@ -1,8 +1,9 @@
1
- # -*- encoding : utf-8 -*-
2
1
  # frozen_string_literal: true
3
- require 'workbook/modules/type_parser'
4
- require 'workbook/nil_value'
5
- require 'date'
2
+ # frozen_string_literal: true
3
+
4
+ require "workbook/modules/type_parser"
5
+ require "workbook/nil_value"
6
+ require "date"
6
7
 
7
8
  module Workbook
8
9
  module Modules
@@ -10,50 +11,51 @@ module Workbook
10
11
  include Workbook::Modules::TypeParser
11
12
 
12
13
  CHARACTER_REPACEMENTS = {
13
- [/[\(\)\.\?\,\!\=\$\:]/,] => '',
14
- [/\&/] => 'amp',
15
- [/\+/] => '_plus_',
16
- [/\s/,'/_','/',"\\"] => '_',
17
- ['–_','-_','+_','-'] => '',
18
- ['__']=>'_',
19
- ['>']=>'gt',
20
- ['<']=>'lt',
21
- ['á','à','â','ä','ã','å'] => 'a',
22
- ['Ã','Ä','Â','À','�?','Å'] => 'A',
23
- ['é','è','ê','ë'] => 'e',
24
- ['Ë','É','È','Ê'] => 'E',
25
- ['í','ì','î','ï'] => 'i',
26
- ['�?','Î','Ì','�?'] => 'I',
27
- ['ó','ò','ô','ö','õ'] => 'o',
28
- ['Õ','Ö','Ô','Ò','Ó'] => 'O',
29
- ['ú','ù','û','ü'] => 'u',
30
- ['Ú','Û','Ù','Ü'] => 'U',
31
- ['ç'] => 'c',
32
- ['Ç'] => 'C',
33
- ['š', 'ś'] => 's',
34
- ['Š', 'Ś'] => 'S',
35
- ['ž','ź'] => 'z',
36
- ['Ž','Ź'] => 'Z',
37
- ['ñ'] => 'n',
38
- ['Ñ'] => 'N',
39
- ['#'] => 'hash',
40
- ['*'] => 'asterisk'
14
+ [/[().?,!=$:]/] => "",
15
+ [/&/] => "amp",
16
+ [/\+/] => "_plus_",
17
+ [/\s/, "/_", "/", "\\"] => "_",
18
+ ["–_", "-_", "+_", "-"] => "",
19
+ ["__"] => "_",
20
+ [">"] => "gt",
21
+ ["<"] => "lt",
22
+ ["á", "à", "â", "ä", "ã", "å"] => "a",
23
+ ["Ã", "Ä", "Â", "À", "�?", "Å"] => "A",
24
+ ["é", "è", "ê", "ë"] => "e",
25
+ ["Ë", "É", "È", "Ê"] => "E",
26
+ ["í", "ì", "î", "ï"] => "i",
27
+ ["�?", "Î", "Ì", "�?"] => "I",
28
+ ["ó", "ò", "ô", "ö", "õ"] => "o",
29
+ ["Õ", "Ö", "Ô", "Ò", "Ó"] => "O",
30
+ ["ú", "ù", "û", "ü"] => "u",
31
+ ["Ú", "Û", "Ù", "Ü"] => "U",
32
+ ["ç"] => "c",
33
+ ["Ç"] => "C",
34
+ ["š", "ś"] => "s",
35
+ ["Š", "Ś"] => "S",
36
+ ["ž", "ź"] => "z",
37
+ ["Ž", "Ź"] => "Z",
38
+ ["ñ"] => "n",
39
+ ["Ñ"] => "N",
40
+ ["#"] => "hash",
41
+ ["*"] => "asterisk"
41
42
  }
42
43
  CLASS_CELLTYPE_MAPPING = {
43
- 'Numeric' => :integer,
44
- 'Integer' => :integer,
45
- 'Fixnum' => :integer,
46
- 'Float' => :float,
47
- 'String' => :string,
48
- 'Symbol' => :string,
49
- 'Time' => :time,
50
- 'Date' => :date,
51
- 'DateTime' => :datetime,
52
- 'ActiveSupport::TimeWithZone' => :datetime,
53
- 'TrueClass' => :boolean,
54
- 'FalseClass' => :boolean,
55
- 'NilClass' => :nil,
56
- 'Workbook::NilValue' => :nil
44
+ "BigDecimal" => :decimal,
45
+ "Numeric" => :integer,
46
+ "Integer" => :integer,
47
+ "Fixnum" => :integer,
48
+ "Float" => :float,
49
+ "String" => :string,
50
+ "Symbol" => :string,
51
+ "Time" => :time,
52
+ "Date" => :date,
53
+ "DateTime" => :datetime,
54
+ "ActiveSupport::TimeWithZone" => :datetime,
55
+ "TrueClass" => :boolean,
56
+ "FalseClass" => :boolean,
57
+ "NilClass" => :nil,
58
+ "Workbook::NilValue" => :nil
57
59
  }
58
60
  # Note that these types are sorted by 'importance'
59
61
 
@@ -78,7 +80,7 @@ module Workbook
78
80
  end
79
81
 
80
82
  def row= r
81
- @row= r
83
+ @row = r
82
84
  end
83
85
 
84
86
  # Change the current value
@@ -107,18 +109,25 @@ module Workbook
107
109
  @value
108
110
  end
109
111
 
112
+ # Returns the column object for the cell
113
+ #
114
+ # @return [Workbook::Column] the column the cell belongs to
115
+ def column
116
+ table.columns[index]
117
+ end
118
+
110
119
  # Returns the sheet its at.
111
120
  #
112
121
  # @return [Workbook::Table]
113
122
  def table
114
- row.table if row
123
+ row&.table
115
124
  end
116
125
 
117
126
  # Quick assessor to the book's template, if it exists
118
127
  #
119
128
  # @return [Workbook::Template]
120
129
  def template
121
- row.template if row
130
+ row&.template
122
131
  end
123
132
 
124
133
  # Change the current format
@@ -129,7 +138,7 @@ module Workbook
129
138
  @workbook_format = f
130
139
  elsif f.is_a? Hash
131
140
  @workbook_format = Workbook::Format.new(f)
132
- elsif f.class == NilClass
141
+ elsif f.instance_of?(NilClass)
133
142
  @workbook_format = Workbook::Format.new
134
143
  end
135
144
  end
@@ -139,7 +148,7 @@ module Workbook
139
148
  # @return [Workbook::Format] the current format
140
149
  def format
141
150
  # return @workbook_format if @workbook_format
142
- if row and template and row.header? and !defined?(@workbook_format)
151
+ if row && template && row.header? && !defined?(@workbook_format)
143
152
  @workbook_format = template.create_or_find_format_by(:header)
144
153
  else
145
154
  @workbook_format ||= Workbook::Format.new
@@ -153,9 +162,9 @@ module Workbook
153
162
  # @return [Boolean]
154
163
  def ==(other)
155
164
  if other.is_a? Cell
156
- other.value == self.value
165
+ other.value == value
157
166
  else
158
- other == self.value
167
+ other == value
159
168
  end
160
169
  end
161
170
 
@@ -166,7 +175,7 @@ module Workbook
166
175
  end
167
176
 
168
177
  def nil_or_empty?
169
- value.nil? || value.to_s == ""
178
+ value.nil? || value.strip.to_s == ""
170
179
  end
171
180
 
172
181
  def value_to_s
@@ -179,46 +188,24 @@ module Workbook
179
188
  #
180
189
  # <Workbook::Cell value="yet another value">.to_sym # returns :yet_another_value
181
190
  def to_sym
182
- return @to_sym if @to_sym
183
- v = nil
184
- unless nil_or_empty?
185
- if cell_type == :integer
186
- v = "num#{value}".to_sym
187
- elsif cell_type == :float
188
- v = "num#{value}".sub(".","_").to_sym
189
- else
190
- v = value_to_s.strip
191
- ends_with_exclamationmark = (v[-1] == '!')
192
- ends_with_questionmark = (v[-1] == '?')
193
-
194
- v = _replace_possibly_problematic_characters_from_string(v)
195
-
196
- v = v.encode(Encoding.find('ASCII'), {:invalid => :replace, :undef => :replace, :replace => ''})
197
-
198
- v = "#{v}!" if ends_with_exclamationmark
199
- v = "#{v}?" if ends_with_questionmark
200
- v = v.downcase.to_sym
201
- end
202
- end
203
- @to_sym = v
204
- return @to_sym
191
+ @to_sym ||= ::Workbook::Cell.value_to_sym(value)
205
192
  end
206
193
 
207
194
  # Compare
208
195
  #
209
196
  # @param [Workbook::Cell] other cell to compare against (based on value), can compare different value-types using #compare_on_class
210
- # @return [Fixnum] -1, 0, 1
197
+ # @return [Integer] -1, 0, 1
211
198
  def <=> other
212
199
  rv = nil
213
200
  begin
214
- rv = self.value <=> other.value
201
+ rv = value <=> other.value
215
202
  rescue NoMethodError
216
203
  rv = compare_on_class other
217
204
  end
218
- if rv == nil
205
+ if rv.nil?
219
206
  rv = compare_on_class other
220
207
  end
221
- return rv
208
+ rv
222
209
  end
223
210
 
224
211
  # Compare on class level
@@ -227,7 +214,7 @@ module Workbook
227
214
  def compare_on_class other
228
215
  other_value = nil
229
216
  other_value = other.value if other
230
- self_value = importance_of_class self.value
217
+ self_value = importance_of_class value
231
218
  other_value = importance_of_class other_value
232
219
  self_value <=> other_value
233
220
  end
@@ -243,14 +230,14 @@ module Workbook
243
230
  #
244
231
  # @return [Boolean] index of the cell
245
232
  def format?
246
- format and format.keys.count > 0
233
+ format && (format.keys.count > 0)
247
234
  end
248
235
 
249
236
  # Returns the index of the cell within the row, returns nil if no row is present
250
237
  #
251
238
  # @return [Integer, NilClass] index of the cell
252
239
  def index
253
- row.index self if row
240
+ row&.index self
254
241
  end
255
242
 
256
243
  # Returns the key (a Symbol) of the cell, based on its table's header
@@ -263,6 +250,7 @@ module Workbook
263
250
  def inspect
264
251
  txt = "<Workbook::Cell @value=#{value}"
265
252
  txt += " @format=#{format}" if format?
253
+ txt += " @cell_type=#{cell_type}"
266
254
  txt += ">"
267
255
  txt
268
256
  end
@@ -270,9 +258,9 @@ module Workbook
270
258
  # convert value to string, and in case of a Date or Time value, apply formatting
271
259
  # @return [String]
272
260
  def to_s
273
- if (self.is_a? Date or self.is_a? Time) and format[:number_format]
261
+ if (is_a?(Date) || is_a?(Time)) && format[:number_format]
274
262
  value.strftime(format[:number_format])
275
- elsif (self.class == Workbook::Cell)
263
+ elsif instance_of?(Workbook::Cell)
276
264
  value.to_s
277
265
  else
278
266
  super
@@ -282,26 +270,17 @@ module Workbook
282
270
  def colspan= c
283
271
  @colspan = c
284
272
  end
273
+
285
274
  def rowspan= r
286
275
  @rowspan = r
287
276
  end
288
277
 
289
278
  def colspan
290
- @colspan.to_i if defined?(@colspan) and @colspan.to_i > 1
291
- end
292
- def rowspan
293
- @rowspan.to_i if defined?(@rowspan) and @rowspan.to_i > 1
279
+ @colspan.to_i if defined?(@colspan) && (@colspan.to_i > 1)
294
280
  end
295
281
 
296
- private
297
-
298
- def _replace_possibly_problematic_characters_from_string(string)
299
- Workbook::Modules::Cell::CHARACTER_REPACEMENTS.each do |ac,rep|
300
- ac.each do |s|
301
- string = string.gsub(s, rep)
302
- end
303
- end
304
- string
282
+ def rowspan
283
+ @rowspan.to_i if defined?(@rowspan) && (@rowspan.to_i > 1)
305
284
  end
306
285
  end
307
286
  end