workbook 0.8.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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