workbook 0.8.1 → 0.9.0

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