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