excel_utils 1.1.2 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea8c9b8aeb5945a6d01f5ea29578bf34c48d28f8a572cb6d5f3857757fb48ae9
4
- data.tar.gz: f1fc9f86e3a3fe784184cd942739e3ea961f018ea313006222568785988b6f34
3
+ metadata.gz: 83f4fd8fec5396120145aefdaa76246f93310b40a6a80d9d799223546e8de9d2
4
+ data.tar.gz: 45cce54a288cd0e462f60341d0fc4f8884eea4c640db9f92850a50f35e1f32bf
5
5
  SHA512:
6
- metadata.gz: 0d7ef606b270f1af8ae88e91e12b44c657e693c7653c3df2d5b951d71758339749be6d78ee95a2c04f1e6895544cb653d8f84bae8ba619757bfc2b1bbb945dbe
7
- data.tar.gz: 6eea2444670d88df36d2d4fdcca88ba4ced1ac9bfbc8890b8e2ab6b4c6b90d42a0413725599ea99e5bfc93f480bbd931cdc8516e725b04ed2c0a3c355ca00815
6
+ metadata.gz: 20d1dfdf88228997f8c999348541dd269ef2bd9510bb5480663a3aa9c1cef9528ffe1287c198355b55347299d0d89ba6e6946e4950bf11b9b398d66f59ecebdc
7
+ data.tar.gz: 7e0d52d3569e343780230ba2efff28c69f449502de5dab3931d1caa426e3748c4131d72cabe003b28d4eac70602ed5be1c031470028069dcc8d0c6ddffe11544
@@ -19,9 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_runtime_dependency 'inflecto', '~> 0.0'
22
- spec.add_runtime_dependency 'roo', '~> 2.7'
23
- spec.add_runtime_dependency 'roo-xls', '~> 1.1'
22
+ spec.add_runtime_dependency 'roo', '~> 2.8'
23
+ spec.add_runtime_dependency 'roo-xls', '~> 1.2'
24
24
  spec.add_runtime_dependency 'write_xlsx', '~> 0.85'
25
+ spec.add_runtime_dependency 'nesquikcsv', '~> 0.1'
25
26
 
26
27
  spec.add_development_dependency 'rake', '~> 12.0'
27
28
  spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
@@ -4,16 +4,26 @@ require 'roo'
4
4
  require 'roo-xls'
5
5
  require 'write_xlsx'
6
6
  require 'inflecto'
7
+ require 'nesquikcsv'
7
8
 
8
9
  require_relative 'excel_utils/version'
9
- require_relative 'excel_utils/workbook'
10
- require_relative 'excel_utils/sheet'
10
+ require_relative 'excel_utils/workbooks/csv'
11
+ require_relative 'excel_utils/workbooks/excel'
12
+ require_relative 'excel_utils/sheets/base'
13
+ require_relative 'excel_utils/sheets/csv'
14
+ require_relative 'excel_utils/sheets/excel'
15
+ require_relative 'excel_utils/sheets/excel_stream'
11
16
  require_relative 'excel_utils/writer'
12
17
 
13
18
  module ExcelUtils
14
-
19
+
15
20
  def self.read(filename, **options)
16
- Workbook.new filename, **options
21
+ extension = options.fetch(:extension, File.extname(filename)[1..-1])
22
+ if extension == 'csv'
23
+ Workbooks::CSV.new(filename, **options)
24
+ else
25
+ Workbooks::Excel.new(filename, **options)
26
+ end
17
27
  end
18
28
 
19
29
  def self.write(filename, data)
@@ -0,0 +1,41 @@
1
+ module ExcelUtils
2
+ module Sheets
3
+ class Base
4
+
5
+ include Enumerable
6
+
7
+ attr_reader :name, :normalize_column_names
8
+
9
+ def initialize(name:, normalize_column_names: false)
10
+ @name = name
11
+ @normalize_column_names = normalize_column_names
12
+ end
13
+
14
+ def column_names
15
+ @column_names ||= normalize_column_names ? normalize_columns(first_row) : first_row
16
+ end
17
+
18
+ def each
19
+ if column_names.any?
20
+ each_row do |row|
21
+ break if empty_row? row
22
+ yield Hash[column_names.zip(row)]
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def normalize_columns(names)
30
+ names.map do |name|
31
+ Inflecto.underscore(name.strip.gsub(' ', '_')).to_sym
32
+ end
33
+ end
34
+
35
+ def empty_row?(row)
36
+ row.all? { |cell| cell.to_s.strip.empty? }
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ module ExcelUtils
2
+ module Sheets
3
+ class CSV < Base
4
+
5
+ def initialize(filename:, **options)
6
+ super(**options)
7
+ @filename = filename
8
+ end
9
+
10
+ private
11
+
12
+ attr_reader :filename
13
+
14
+ def first_row
15
+ NesquikCSV.open(filename) { |csv| csv.readline } || []
16
+ end
17
+
18
+ def each_row
19
+ first = true
20
+ NesquikCSV.foreach(filename) do |row|
21
+ yield row unless first
22
+ first = false
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ module ExcelUtils
2
+ module Sheets
3
+ class Excel < Base
4
+
5
+ def initialize(spreadsheet:, **options)
6
+ super(**options)
7
+ @spreadsheet = spreadsheet
8
+ end
9
+
10
+ private
11
+
12
+ attr_reader :spreadsheet
13
+
14
+ def first_row
15
+ with_sheet do |sheet|
16
+ sheet.first_row ? sheet.row(sheet.first_row) : []
17
+ end
18
+ end
19
+
20
+ def each_row
21
+ with_sheet do |sheet|
22
+ (sheet.first_row + 1).upto(sheet.last_row) do |i|
23
+ yield sheet.row(i)
24
+ end
25
+ end
26
+ end
27
+
28
+ def with_sheet
29
+ yield spreadsheet.sheet(name)
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module ExcelUtils
2
+ module Sheets
3
+ class ExcelStream < Base
4
+
5
+ def initialize(spreadsheet:, **options)
6
+ super(**options)
7
+ @spreadsheet = spreadsheet
8
+ end
9
+
10
+ private
11
+
12
+ attr_reader :spreadsheet
13
+
14
+ def first_row
15
+ row = sheet.each_row_streaming(pad_cells: true, max_rows: 0).first || []
16
+ normalize_row row
17
+ end
18
+
19
+ def each_row
20
+ sheet.each_row_streaming(pad_cells: true, offset: 1) do |row|
21
+ yield normalize_row(row)
22
+ end
23
+ end
24
+
25
+ def normalize_row(row)
26
+ row.map { |cell| cell ? cell.value : cell }
27
+ end
28
+
29
+ def sheet
30
+ spreadsheet.sheet name
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module ExcelUtils
2
- VERSION = '1.1.2'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -0,0 +1,36 @@
1
+ module ExcelUtils
2
+ module Workbooks
3
+ class CSV
4
+
5
+ SHEET_NAME = 'default'.freeze
6
+
7
+ attr_reader :filename, :normalize_column_names
8
+
9
+ def initialize(filename, normalize_column_names: false)
10
+ @filename = filename
11
+ @normalize_column_names = normalize_column_names
12
+
13
+ @sheet = Sheets::CSV.new name: SHEET_NAME,
14
+ normalize_column_names: normalize_column_names,
15
+ filename: filename
16
+ end
17
+
18
+ def sheets
19
+ [sheet]
20
+ end
21
+
22
+ def [](sheet_name)
23
+ sheet_name == SHEET_NAME ? sheet : nil
24
+ end
25
+
26
+ def to_h
27
+ {SHEET_NAME => sheet.to_a}
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :sheet
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ module ExcelUtils
2
+ module Workbooks
3
+ class Excel
4
+
5
+ attr_reader :filename, :normalize_column_names
6
+
7
+ def initialize(filename, normalize_column_names: false, extension: nil)
8
+ @filename = filename
9
+ @normalize_column_names = normalize_column_names
10
+ @spreadsheet = Roo::Spreadsheet.open filename, extension: extension
11
+ end
12
+
13
+ def sheets
14
+ @sheets ||= spreadsheet.sheets.map do |name|
15
+ sheet_class.new name: name,
16
+ normalize_column_names: normalize_column_names,
17
+ spreadsheet: spreadsheet
18
+ end
19
+ end
20
+
21
+ def [](sheet_name)
22
+ sheets.detect { |sheet| sheet.name == sheet_name }
23
+ end
24
+
25
+ def to_h
26
+ sheets.each_with_object({}) do |sheet, hash|
27
+ hash[sheet.name] = sheet.to_a
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :spreadsheet
34
+
35
+ def sheet_class
36
+ @sheet_class ||= spreadsheet.respond_to?(:each_row_streaming) ? Sheets::ExcelStream : Sheets::Excel
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -3,4 +3,14 @@ require 'minitest/autorun'
3
3
  require 'minitest/colorin'
4
4
  require 'pry-nav'
5
5
 
6
- require 'excel_utils'
6
+ require 'excel_utils'
7
+
8
+ RESOURCES_PATH = File.expand_path '../resources', __FILE__
9
+
10
+ class Minitest::Test
11
+
12
+ def resource_path(relative_path)
13
+ File.join RESOURCES_PATH, relative_path
14
+ end
15
+
16
+ end
@@ -2,110 +2,151 @@ require 'minitest_helper'
2
2
 
3
3
  describe ExcelUtils, 'Read' do
4
4
 
5
- def expected_rows(sheet)
6
- rows_by_sheet[sheet.name].map { |r| Hash[columns_by_sheet[sheet.name].zip(r)] }
7
- end
5
+ Dir.glob(File.join(RESOURCES_PATH, 'basic.*')).each do |filename|
8
6
 
9
- let :rows_by_sheet do
10
- {
11
- 'Sheet1' => [
12
- [1.0, 'some text'],
13
- [2.0, 1.35],
14
- [3.0, Date.parse('2019-08-17')],
15
- [4.0, nil]
16
- ],
17
- 'Sheet2' => [
18
- [123.0, 'Text 1'],
19
- [456.0, 'Text 2']
20
- ],
21
- 'Sheet3' => []
22
- }
23
- end
7
+ [true, false].each do |normalize_column_names|
8
+
9
+ describe File.basename(filename), "Workbook (normalize_column_names: #{normalize_column_names})" do
10
+
11
+ let(:workbook) { ExcelUtils.read filename, normalize_column_names: normalize_column_names }
12
+
13
+ let(:sheet) { workbook.sheets.first }
14
+
15
+ let(:csv?) { File.extname(filename) == '.csv' }
16
+
17
+ let(:expected_sheet_name) { csv? ? 'default' : 'Sheet1' }
18
+
19
+ let(:column_a) { normalize_column_names ? :column_a : 'Column A' }
20
+
21
+ let(:column_b) { normalize_column_names ? :column_b : 'Column B' }
24
22
 
25
- ['xls', 'xlsx'].each do |extension|
23
+ let(:expected_columns) { [column_a, column_b] }
26
24
 
27
- describe extension do
28
-
29
- let(:filename) { File.expand_path "../sample.#{extension}", __FILE__ }
25
+ let :expected_rows do
26
+ if csv?
27
+ [
28
+ {column_a => '1', column_b => 'some text'},
29
+ {column_a => '2', column_b => '1,35'},
30
+ {column_a => '3', column_b => '17/08/2019'},
31
+ {column_a => '4', column_b => nil}
32
+ ]
33
+ else
34
+ [
35
+ {column_a => 1, column_b => 'some text'},
36
+ {column_a => 2, column_b => 1.35},
37
+ {column_a => 3, column_b => Date.parse('2019-08-17')},
38
+ {column_a => 4, column_b => nil}
39
+ ]
40
+ end
41
+ end
30
42
 
31
- describe 'Original column names' do
43
+ it 'filename' do
44
+ workbook.filename.must_equal filename
45
+ end
46
+
47
+ it 'normalize_column_names' do
48
+ workbook.normalize_column_names.must_equal normalize_column_names
49
+ end
32
50
 
33
- let(:workbook) { ExcelUtils.read filename }
34
-
35
- let :columns_by_sheet do
36
- {
37
- 'Sheet1' => ['Column A', 'Column B'],
38
- 'Sheet2' => ['ID', 'Value'],
39
- 'Sheet3' => []
40
- }
51
+ it 'sheets' do
52
+ workbook.sheets.count.must_equal 1
53
+ workbook[workbook.sheets.first.name].must_equal sheet
41
54
  end
42
55
 
43
56
  it 'to_h' do
44
- workbook.to_h.must_equal 'Sheet1' => expected_rows(workbook['Sheet1']),
45
- 'Sheet2' => expected_rows(workbook['Sheet2']),
46
- 'Sheet3' => []
57
+ workbook.to_h.must_equal workbook.sheets.first.name => sheet.to_a
47
58
  end
48
59
 
49
- ['Sheet1', 'Sheet2', 'Sheet3'].each do |sheet_name|
60
+ describe 'Sheet' do
50
61
 
51
- describe sheet_name do
62
+ it 'name' do
63
+ sheet.name.must_equal expected_sheet_name
64
+ end
52
65
 
53
- let(:sheet) { workbook[sheet_name] }
66
+ it 'normalize_column_names' do
67
+ sheet.normalize_column_names.must_equal workbook.normalize_column_names
68
+ end
54
69
 
55
- it 'Column names' do
56
- sheet.column_names.must_equal columns_by_sheet[sheet_name]
57
- end
70
+ it 'column_names' do
71
+ sheet.column_names.must_equal expected_columns
72
+ end
58
73
 
59
- it 'Rows' do
60
- sheet.to_a.must_equal expected_rows(sheet)
61
- end
74
+ it 'count' do
75
+ sheet.count.must_equal 4
76
+ end
62
77
 
78
+ it 'to_a' do
79
+ sheet.to_a.must_equal expected_rows
63
80
  end
64
81
 
65
82
  end
66
83
 
67
84
  end
68
85
 
69
- describe 'Normalized column names' do
86
+ end
70
87
 
71
- let(:workbook) { ExcelUtils.read filename, normalize_column_names: true }
72
-
73
- let :columns_by_sheet do
74
- {
75
- 'Sheet1' => [:column_a, :column_b],
76
- 'Sheet2' => [:id, :value],
77
- 'Sheet3' => []
78
- }
79
- end
88
+ end
80
89
 
81
- ['Sheet1', 'Sheet2', 'Sheet3'].each do |sheet_name|
90
+ it 'empty.csv' do
91
+ workbook = ExcelUtils.read resource_path('empty.csv')
82
92
 
83
- describe sheet_name do
93
+ workbook.sheets.map(&:name).must_equal ['default']
84
94
 
85
- let(:sheet) { workbook[sheet_name] }
95
+ workbook['default'].column_names.must_equal []
96
+ workbook['default'].to_a.must_equal []
86
97
 
87
- it 'Column names' do
88
- sheet.column_names.must_equal columns_by_sheet[sheet_name]
89
- end
98
+ workbook.to_h.must_equal 'default' => []
99
+ end
90
100
 
91
- it 'Rows' do
92
- sheet.to_a.must_equal expected_rows(sheet)
93
- end
101
+ it 'only_headers.csv' do
102
+ workbook = ExcelUtils.read resource_path('only_headers.csv')
94
103
 
95
- end
104
+ workbook.sheets.map(&:name).must_equal ['default']
96
105
 
97
- end
106
+ workbook['default'].column_names.must_equal ['ID', 'Value']
107
+ workbook['default'].to_a.must_equal []
98
108
 
99
- end
109
+ workbook.to_h.must_equal 'default' => []
110
+ end
100
111
 
101
- end
112
+ it 'custom_extension.tmp' do
113
+ workbook = ExcelUtils.read resource_path('custom_extension.tmp'), extension: 'xlsx'
102
114
 
115
+ expected_rows = [
116
+ {'ID' => 1, 'Value' => 'Text 1'},
117
+ {'ID' => 2, 'Value' => 'Text 2'}
118
+ ]
119
+
120
+ workbook.sheets.map(&:name).must_equal ['Sheet1']
121
+
122
+ workbook['Sheet1'].column_names.must_equal ['ID', 'Value']
123
+ workbook['Sheet1'].to_a.must_equal expected_rows
124
+
125
+ workbook.to_h.must_equal 'Sheet1' => expected_rows
103
126
  end
104
127
 
105
- it 'Force extension' do
106
- filename = File.expand_path "../sample.tmp", __FILE__
107
- workbook = ExcelUtils.read filename, extension: 'xlsx'
108
- workbook.sheets.count.must_equal 3
128
+ it 'multiple.xlsx' do
129
+ workbook = ExcelUtils.read resource_path('multiple.xlsx'), normalize_column_names: true
130
+
131
+ workbook.sheets.map(&:name).must_equal ['Sheet1', 'Sheet2', 'Sheet3']
132
+
133
+ expected_rows_sheet_1 = [
134
+ {id: 1, value: 'Text 1'},
135
+ {id: 2, value: 'Text 2'}
136
+ ]
137
+
138
+ workbook['Sheet1'].column_names.must_equal [:id, :value]
139
+ workbook['Sheet1'].to_a.must_equal expected_rows_sheet_1
140
+
141
+ workbook['Sheet2'].column_names.must_equal [:id, :value]
142
+ workbook['Sheet2'].to_a.must_equal []
143
+
144
+ workbook['Sheet3'].column_names.must_equal []
145
+ workbook['Sheet3'].to_a.must_equal []
146
+
147
+ workbook.to_h.must_equal 'Sheet1' => expected_rows_sheet_1,
148
+ 'Sheet2' => [],
149
+ 'Sheet3' => []
109
150
  end
110
151
 
111
152
  end
@@ -0,0 +1,5 @@
1
+ Column A,Column B
2
+ 1,some text
3
+ 2,"1,35"
4
+ 3,17/08/2019
5
+ 4,
Binary file
Binary file
Binary file
File without changes
@@ -0,0 +1 @@
1
+ ID,Value
@@ -34,7 +34,7 @@ describe ExcelUtils do
34
34
  sheet.each_with_index do |row, i|
35
35
  columns = row.keys | data[sheet.name][i].keys
36
36
  columns.each do |column|
37
- row[column].must_equal data[sheet.name][i][column]
37
+ row[column].must_equal data[sheet.name][i][column]
38
38
  end
39
39
  end
40
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: excel_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Naiman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2020-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inflecto
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.7'
33
+ version: '2.8'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.7'
40
+ version: '2.8'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: roo-xls
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.1'
47
+ version: '1.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.1'
54
+ version: '1.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: write_xlsx
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.85'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nesquikcsv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -188,16 +202,26 @@ files:
188
202
  - Rakefile
189
203
  - excel_utils.gemspec
190
204
  - lib/excel_utils.rb
191
- - lib/excel_utils/sheet.rb
205
+ - lib/excel_utils/sheets/base.rb
206
+ - lib/excel_utils/sheets/csv.rb
207
+ - lib/excel_utils/sheets/excel.rb
208
+ - lib/excel_utils/sheets/excel_stream.rb
192
209
  - lib/excel_utils/version.rb
193
- - lib/excel_utils/workbook.rb
210
+ - lib/excel_utils/workbooks/csv.rb
211
+ - lib/excel_utils/workbooks/excel.rb
194
212
  - lib/excel_utils/writer.rb
195
213
  - spec/coverage_helper.rb
196
214
  - spec/minitest_helper.rb
197
215
  - spec/read_spec.rb
198
- - spec/sample.tmp
199
- - spec/sample.xls
200
- - spec/sample.xlsx
216
+ - spec/resources/basic.csv
217
+ - spec/resources/basic.ods
218
+ - spec/resources/basic.xls
219
+ - spec/resources/basic.xlsm
220
+ - spec/resources/basic.xlsx
221
+ - spec/resources/custom_extension.tmp
222
+ - spec/resources/empty.csv
223
+ - spec/resources/multiple.xlsx
224
+ - spec/resources/only_headers.csv
201
225
  - spec/write_spec.rb
202
226
  homepage: https://github.com/gabynaiman/excel_utils
203
227
  licenses:
@@ -226,7 +250,13 @@ test_files:
226
250
  - spec/coverage_helper.rb
227
251
  - spec/minitest_helper.rb
228
252
  - spec/read_spec.rb
229
- - spec/sample.tmp
230
- - spec/sample.xls
231
- - spec/sample.xlsx
253
+ - spec/resources/basic.csv
254
+ - spec/resources/basic.ods
255
+ - spec/resources/basic.xls
256
+ - spec/resources/basic.xlsm
257
+ - spec/resources/basic.xlsx
258
+ - spec/resources/custom_extension.tmp
259
+ - spec/resources/empty.csv
260
+ - spec/resources/multiple.xlsx
261
+ - spec/resources/only_headers.csv
232
262
  - spec/write_spec.rb
@@ -1,54 +0,0 @@
1
- module ExcelUtils
2
- class Sheet
3
-
4
- include Enumerable
5
-
6
- attr_reader :name, :normalize_column_names
7
-
8
- def initialize(name, spreadsheet, normalize_column_names: false)
9
- @name = name
10
- @spreadsheet = spreadsheet
11
- @normalize_column_names = normalize_column_names
12
- end
13
-
14
- def column_names
15
- @column_names ||= begin
16
- if sheet.first_row
17
- first_row = sheet.row sheet.first_row
18
- normalize_column_names ? first_row.map { |name| normalize_column name } : first_row
19
- else
20
- []
21
- end
22
- end
23
- end
24
-
25
- def each(&block)
26
- rows.each(&block)
27
- end
28
-
29
- private
30
-
31
- attr_reader :spreadsheet
32
-
33
- def sheet
34
- spreadsheet.sheet name
35
- end
36
-
37
- def rows
38
- @rows ||= begin
39
- if sheet.first_row
40
- sheet.to_a[1..-1].map do |row|
41
- Hash[column_names.zip(row)]
42
- end
43
- else
44
- []
45
- end
46
- end
47
- end
48
-
49
- def normalize_column(name)
50
- Inflecto.underscore(name.strip.gsub(' ', '_')).to_sym
51
- end
52
-
53
- end
54
- end
@@ -1,33 +0,0 @@
1
- module ExcelUtils
2
- class Workbook
3
-
4
- attr_reader :filename, :normalize_column_names
5
-
6
- def initialize(filename, normalize_column_names: false, extension: nil)
7
- @filename = filename
8
- @normalize_column_names = normalize_column_names
9
- @spreadsheet = Roo::Spreadsheet.open filename, extension: extension
10
- end
11
-
12
- def sheets
13
- @sheets ||= spreadsheet.sheets.map do |name|
14
- Sheet.new name, spreadsheet, normalize_column_names: normalize_column_names
15
- end
16
- end
17
-
18
- def [](sheet_name)
19
- sheets.detect { |sheet| sheet.name == sheet_name }
20
- end
21
-
22
- def to_h
23
- sheets.each_with_object({}) do |sheet, hash|
24
- hash[sheet.name] = sheet.to_a
25
- end
26
- end
27
-
28
- private
29
-
30
- attr_reader :spreadsheet
31
-
32
- end
33
- end
Binary file
Binary file