workbook 0.8.0 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +21 -0
- data/.gitignore +4 -1
- data/.ruby-version +1 -1
- data/.travis.yml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +4 -2
- data/README.md +9 -7
- data/Rakefile +9 -7
- data/lib/workbook/book.rb +74 -61
- data/lib/workbook/cell.rb +59 -12
- data/lib/workbook/column.rb +33 -29
- data/lib/workbook/format.rb +24 -23
- data/lib/workbook/generatetypes.rb +6 -5
- data/lib/workbook/modules/cache.rb +8 -7
- data/lib/workbook/modules/cell.rb +78 -99
- data/lib/workbook/modules/diff_sort.rb +93 -82
- data/lib/workbook/modules/raw_objects_storage.rb +7 -7
- data/lib/workbook/modules/type_parser.rb +31 -21
- data/lib/workbook/nil_value.rb +5 -9
- data/lib/workbook/readers/csv_reader.rb +7 -9
- data/lib/workbook/readers/ods_reader.rb +49 -49
- data/lib/workbook/readers/txt_reader.rb +7 -7
- data/lib/workbook/readers/xls_reader.rb +22 -32
- data/lib/workbook/readers/xls_shared.rb +107 -116
- data/lib/workbook/readers/xlsx_reader.rb +47 -46
- data/lib/workbook/row.rb +100 -83
- data/lib/workbook/sheet.rb +48 -37
- data/lib/workbook/table.rb +97 -71
- data/lib/workbook/template.rb +13 -14
- data/lib/workbook/types/date.rb +2 -1
- data/lib/workbook/types/false.rb +1 -1
- data/lib/workbook/types/false_class.rb +2 -1
- data/lib/workbook/types/nil.rb +1 -1
- data/lib/workbook/types/nil_class.rb +3 -2
- data/lib/workbook/types/numeric.rb +3 -2
- data/lib/workbook/types/string.rb +3 -2
- data/lib/workbook/types/time.rb +3 -2
- data/lib/workbook/types/true.rb +1 -1
- data/lib/workbook/types/true_class.rb +3 -2
- data/lib/workbook/version.rb +3 -2
- data/lib/workbook/writers/csv_table_writer.rb +11 -12
- data/lib/workbook/writers/html_writer.rb +35 -37
- data/lib/workbook/writers/json_table_writer.rb +9 -10
- data/lib/workbook/writers/xls_writer.rb +31 -35
- data/lib/workbook/writers/xlsx_writer.rb +46 -28
- data/lib/workbook.rb +17 -14
- data/test/helper.rb +8 -5
- data/test/test_book.rb +43 -38
- data/test/test_column.rb +29 -25
- data/test/test_format.rb +53 -55
- data/test/test_functional.rb +9 -8
- data/test/test_modules_cache.rb +20 -17
- data/test/test_modules_cell.rb +49 -46
- data/test/test_modules_table_diff_sort.rb +56 -63
- data/test/test_modules_type_parser.rb +63 -31
- data/test/test_readers_csv_reader.rb +50 -42
- data/test/test_readers_ods_reader.rb +30 -31
- data/test/test_readers_txt_reader.rb +23 -23
- data/test/test_readers_xls_reader.rb +22 -23
- data/test/test_readers_xls_shared.rb +5 -4
- data/test/test_readers_xlsx_reader.rb +46 -37
- data/test/test_row.rb +107 -109
- data/test/test_sheet.rb +43 -35
- data/test/test_table.rb +84 -60
- data/test/test_template.rb +18 -15
- data/test/test_types_date.rb +7 -7
- data/test/test_writers_csv_writer.rb +24 -0
- data/test/test_writers_html_writer.rb +44 -41
- data/test/test_writers_json_writer.rb +11 -9
- data/test/test_writers_xls_writer.rb +52 -35
- data/test/test_writers_xlsx_writer.rb +64 -34
- data/workbook.gemspec +26 -27
- metadata +93 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19f7a4905a129fba73781bb8ef9f672573419b5b754508bc3cc28aff968d5fb8
|
4
|
+
data.tar.gz: 7bd07336aaee461b7ff487c683b427ba29d717bee23731d80c1e74d1a1f7cd1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d20a391aee1d3338f08ecddb104cca25411626787bec16a2c0a25e125efa6bbee91f79350a919afcca7d6443fac62ca34d2f2da20985e1eae3b59c73e6f6b7f
|
7
|
+
data.tar.gz: 93d1ffe829d5fb3f591b9ef5244c013f4268d982f8e148893439b7788d8422f99e1449577fd708e753cb26db9029db1a08a1e46e8db0d12860e23db4124a8a82
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
version: "2"
|
2
|
+
plugins:
|
3
|
+
rubocop:
|
4
|
+
enabled: true
|
5
|
+
channel: rubocop-1-23-0
|
6
|
+
brakeman:
|
7
|
+
enabled: false
|
8
|
+
exclude_patterns:
|
9
|
+
- "config/"
|
10
|
+
- "db/"
|
11
|
+
- "dist/"
|
12
|
+
- "features/"
|
13
|
+
- "**/node_modules/"
|
14
|
+
- "script/"
|
15
|
+
- "**/spec/"
|
16
|
+
- "**/test/"
|
17
|
+
- "**/tests/"
|
18
|
+
- "Tests/"
|
19
|
+
- "**/vendor/"
|
20
|
+
- "**/*_test.go"
|
21
|
+
- "**/*.d.ts"
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
3.2.1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
### 0.9.0 (in progress)
|
4
|
+
|
5
|
+
* Dropped inheritance from Array and borrowed from the Enumerable module instead (this might break some existing implementations; please create pull request if you miss Array like functionality)
|
6
|
+
|
7
|
+
### 0.8.1
|
8
|
+
|
9
|
+
* Adopted [Standard](https://github.com/testdouble/standard) for code formatting
|
10
|
+
|
11
|
+
### 0.8.0
|
12
|
+
|
13
|
+
* Dropped support for ruby < 2.3.0
|
14
|
+
* Added support for 2.6.0
|
15
|
+
* Stripping strings before calling to_sym (possibly breaking, as symbols can be used to address columns)
|
16
|
+
* Started maintaining a changelog
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# Workbook
|
2
|
-
[![Code Climate](https://codeclimate.com/github/murb/workbook.
|
2
|
+
[![Code Climate](https://codeclimate.com/github/murb/workbook.svg)](https://codeclimate.com/github/murb/workbook)
|
3
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/058655e705a3f36896e0/test_coverage)](https://codeclimate.com/github/murb/workbook/test_coverage)
|
4
|
+
[![Build Status](https://travis-ci.org/murb/workbook.svg?branch=master)](https://travis-ci.org/murb/workbook)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/workbook.svg)](http://badge.fury.io/rb/workbook)
|
3
6
|
|
4
|
-
Goal of this gem is to make working with workbooks (spreadsheets) as programmer friendly as possible. Not reinventing a totally new DSL or all kinds of new methodnames, but just borrowing from known concepts such as hashes and arrays (much like (Faster)CSV does)). Workbook is a gem that mimicks a typical spreadsheet, a bundle of sheets, bundled in a *workbook*. A sheet may contain one or more tables (which might the multi table sheets of Apple Numbers or Excel ranges). Basically:
|
7
|
+
Goal of this gem is to make working with workbooks (spreadsheets) as programmer friendly as possible. Not reinventing a totally new DSL or all kinds of new methodnames, but just borrowing from known concepts such as hashes and arrays (much like (Faster)CSV does)). Workbook is a gem that mimicks a typical spreadsheet, a bundle of sheets, bundled in a *workbook*. A sheet may contain one or more tables (which might be the multi table sheets of Apple Numbers or Excel ranges). Basically:
|
5
8
|
|
6
9
|
* Book
|
7
10
|
* Sheet (one or more)
|
@@ -13,7 +16,7 @@ Subsequently a table consists of:
|
|
13
16
|
* Row (one or more)
|
14
17
|
* Cell ( wich has may have a (shared) Format )
|
15
18
|
|
16
|
-
Book, Sheet, Table and Row inherit from the
|
19
|
+
Book, Sheet, Table and Row inherit much of the behaviours from the Array and Hash classes, it tries to walk and quack as such. The row is extended with hashlike lookups (`row[:id]`) and writers (`row[:id]=`). Values are converted to ruby native types, and optional parsers can be added to improve recognition.
|
17
20
|
|
18
21
|
In addition to offering you this plain structure it allows for importing .xls, .csv, .xlsx, .txt files (more to come), writing .xls, and .csv (more to come) and includes several utilities to easily create an overview of the differences between two tables and output basic cell-styling properties as css.
|
19
22
|
|
@@ -32,7 +35,7 @@ Calling
|
|
32
35
|
s = b.sheet
|
33
36
|
t = s.table
|
34
37
|
|
35
|
-
will give you
|
38
|
+
will give you the first Sheet and Table (if one doesn't exist it is created on the fly).
|
36
39
|
|
37
40
|
You can initialize with simple 2-d array like this:
|
38
41
|
|
@@ -129,8 +132,7 @@ In case you want to display a formatted table in HTML, some conversion is offere
|
|
129
132
|
|
130
133
|
## Compatibility
|
131
134
|
|
132
|
-
Workbook is automatically tested
|
133
|
-
Check [Travis for Workbook's current build status](https://travis-ci.org/murb/workbook) [![Build Status](https://travis-ci.org/murb/workbook.svg?branch=master)](https://travis-ci.org/murb/workbook).
|
135
|
+
Workbook is automatically tested. Check [Travis for Workbook's current build status of the supported ruby versions](https://travis-ci.org/murb/workbook) [![Build Status](https://travis-ci.org/murb/workbook.svg?branch=master)](https://travis-ci.org/murb/workbook).
|
134
136
|
|
135
137
|
## Future
|
136
138
|
|
@@ -153,4 +155,4 @@ Workbook uses the following gems:
|
|
153
155
|
* [FasterCSV](http://fastercsv.rubyforge.org/) Used for reading CSV (comma separated text) and TXT (tab separated text) files (Copyright © James Edward Gray II; GPL2 & Ruby License)
|
154
156
|
* [rchardet](http://rubyforge.org/projects/rchardet) Used for detecting encoding in CSV and TXT importers (Copyright © JMHodges; LGPL)
|
155
157
|
* [axslx](https://github.com/randym/axlsx) Used for writing the newer .xlsx files (with formatting) (Copyright © 2011, 2012 Randy Morgan, MIT License)
|
156
|
-
* [Nokogiri](http://nokogiri.org/) Used for reading ODS documents (Copyright © 2008 - 2012 Aaron Patterson, Mike Dalessio, Charles Nutter, Sergio Arbeo, Patrick Mahoney, Yoko Harada; MIT Licensed)
|
158
|
+
* [Nokogiri](http://nokogiri.org/) Used for reading ODS documents (Copyright © 2008 - 2012 Aaron Patterson, Mike Dalessio, Charles Nutter, Sergio Arbeo, Patrick Mahoney, Yoko Harada; MIT Licensed)
|
data/Rakefile
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rake"
|
4
|
+
require "rake/testtask"
|
5
|
+
require "bundler"
|
4
6
|
require "bundler/gem_tasks"
|
5
7
|
|
6
|
-
#Bundler::GemHelper.install_tasks
|
8
|
+
# Bundler::GemHelper.install_tasks
|
7
9
|
|
8
|
-
task :
|
10
|
+
task default: [:test]
|
9
11
|
|
10
12
|
Rake::TestTask.new do |t|
|
11
13
|
t.libs << "test"
|
12
|
-
t.test_files = FileList[
|
14
|
+
t.test_files = FileList["test/test*.rb", "test/writers/test*.rb"]
|
13
15
|
t.verbose = false
|
14
|
-
end
|
16
|
+
end
|
data/lib/workbook/book.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
1
|
# frozen_string_literal: true
|
3
|
-
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "forwardable"
|
5
|
+
require "open-uri"
|
6
|
+
require "workbook/writers/xls_writer"
|
7
|
+
require "workbook/writers/xlsx_writer"
|
8
|
+
require "workbook/writers/html_writer"
|
9
|
+
require "workbook/readers/xls_reader"
|
10
|
+
require "workbook/readers/xls_shared"
|
11
|
+
require "workbook/readers/xlsx_reader"
|
12
|
+
require "workbook/readers/ods_reader"
|
13
|
+
require "workbook/readers/csv_reader"
|
14
|
+
require "workbook/readers/txt_reader"
|
15
|
+
require "workbook/modules/diff_sort"
|
15
16
|
|
16
17
|
module Workbook
|
17
18
|
# The Book class is the container of sheets. It can be inialized by either the standard initalizer or the open method. The
|
@@ -33,8 +34,9 @@ module Workbook
|
|
33
34
|
"CDF V2 Document, No summary info"
|
34
35
|
]
|
35
36
|
|
36
|
-
class Book
|
37
|
-
|
37
|
+
class Book
|
38
|
+
include Enumerable
|
39
|
+
extend Forwardable
|
38
40
|
|
39
41
|
include Workbook::Readers::XlsShared
|
40
42
|
include Workbook::Writers::XlsWriter
|
@@ -47,14 +49,13 @@ module Workbook
|
|
47
49
|
include Workbook::Readers::TxtReader
|
48
50
|
include Workbook::Modules::BookDiffSort
|
49
51
|
|
52
|
+
delegate [:last, :pop, :delete_at, :each, :[]] => :@sheets
|
53
|
+
|
50
54
|
# @param [Workbook::Sheet, Array] sheet create a new workbook based on an existing sheet, or initialize a sheet based on the array
|
51
55
|
# @return [Workbook::Book]
|
52
|
-
def initialize sheet=nil
|
53
|
-
|
54
|
-
|
55
|
-
elsif sheet
|
56
|
-
self.push Workbook::Sheet.new(sheet, self, {})
|
57
|
-
end
|
56
|
+
def initialize sheet = nil
|
57
|
+
@sheets = []
|
58
|
+
push sheet if sheet
|
58
59
|
end
|
59
60
|
|
60
61
|
# @return [Workbook::Template] returns the template describing how the document should be/is formatted
|
@@ -72,37 +73,51 @@ module Workbook
|
|
72
73
|
#
|
73
74
|
# @return [String] the title of the workbook
|
74
75
|
def title
|
75
|
-
|
76
|
+
defined?(@title) && !@title.nil? ? @title : "untitled document"
|
76
77
|
end
|
77
78
|
|
78
|
-
|
79
|
-
@title = t
|
80
|
-
end
|
79
|
+
attr_writer :title
|
81
80
|
|
82
81
|
# Push (like in array) a sheet to the workbook (parameter is optional, default is a new sheet)
|
83
82
|
#
|
84
|
-
# @param [Workbook::Sheet] sheet
|
85
|
-
def push sheet=Workbook::Sheet.new
|
86
|
-
|
87
|
-
sheet.book=
|
83
|
+
# @param [Workbook::Sheet, Array[Array]] sheet
|
84
|
+
def push sheet = Workbook::Sheet.new
|
85
|
+
sheet = Workbook::Sheet.new(sheet) unless sheet.is_a? Workbook::Sheet
|
86
|
+
sheet.book = self
|
87
|
+
|
88
|
+
@sheets.push(sheet)
|
89
|
+
sheet
|
88
90
|
end
|
89
91
|
|
90
|
-
#
|
92
|
+
# Inserts a new Table at the specified index
|
91
93
|
#
|
92
|
-
# @param [
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
94
|
+
# @param [Integer] index of the table
|
95
|
+
# @param [Workbook::Table, Array<Array>] table The new first table of this sheet
|
96
|
+
#
|
97
|
+
# @return [Workbook::Table]
|
98
|
+
def []= index, value
|
99
|
+
table_to_insert = value.is_a?(Workbook::Sheet) ? value : Workbook::Sheet.new
|
100
|
+
table_to_insert.book = self
|
101
|
+
@sheets[index] = table_to_insert
|
97
102
|
end
|
98
103
|
|
104
|
+
# returns the index of an item
|
105
|
+
def index item
|
106
|
+
@sheets.index item
|
107
|
+
end
|
108
|
+
|
109
|
+
# << (like in array) a sheet to the workbook (parameter is optional, default is a new sheet)
|
110
|
+
#
|
111
|
+
# @param [Workbook::Sheet, Array[Array]] sheet
|
112
|
+
def << sheet = Workbook::Sheet.new
|
113
|
+
push sheet
|
114
|
+
end
|
99
115
|
|
100
116
|
# Sheet returns the first sheet of a workbook, or an empty one.
|
101
117
|
#
|
102
118
|
# @return [Workbook::Sheet] The first sheet, and creates an empty one if one doesn't exists
|
103
119
|
def sheet
|
104
|
-
|
105
|
-
first
|
120
|
+
first || push
|
106
121
|
end
|
107
122
|
|
108
123
|
# If the first sheet has any contents
|
@@ -117,9 +132,9 @@ module Workbook
|
|
117
132
|
# @param [String] filename a string with a reference to the file to be opened
|
118
133
|
# @param [String] extension an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
|
119
134
|
# @return [Workbook::Book] A new instance, based on the filename
|
120
|
-
def import filename, extension=nil, options={}
|
121
|
-
extension
|
122
|
-
if [
|
135
|
+
def import filename, extension = nil, options = {}
|
136
|
+
extension ||= file_extension(filename)
|
137
|
+
if ["txt", "csv", "xml"].include?(extension)
|
123
138
|
open_text filename, extension, options
|
124
139
|
else
|
125
140
|
open_binary filename, extension, options
|
@@ -131,9 +146,9 @@ module Workbook
|
|
131
146
|
# @param [String] filename a string with a reference to the file to be opened
|
132
147
|
# @param [String] extension an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
|
133
148
|
# @return [Workbook::Book] A new instance, based on the filename
|
134
|
-
def open_binary filename, extension=nil, options={}
|
135
|
-
extension
|
136
|
-
f = open(filename)
|
149
|
+
def open_binary filename, extension = nil, options = {}
|
150
|
+
extension ||= file_extension(filename)
|
151
|
+
f = File.open(filename)
|
137
152
|
send("load_#{extension}".to_sym, f, options)
|
138
153
|
end
|
139
154
|
|
@@ -141,9 +156,9 @@ module Workbook
|
|
141
156
|
#
|
142
157
|
# @param [String] filename a string with a reference to the file to be opened
|
143
158
|
# @param [String] extension an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
|
144
|
-
def open_text filename, extension=nil, options={}
|
145
|
-
extension
|
146
|
-
t = text_to_utf8(open(filename).read)
|
159
|
+
def open_text filename, extension = nil, options = {}
|
160
|
+
extension ||= file_extension(filename)
|
161
|
+
t = text_to_utf8(File.open(filename).read)
|
147
162
|
send("load_#{extension}".to_sym, t, options)
|
148
163
|
end
|
149
164
|
|
@@ -151,21 +166,20 @@ module Workbook
|
|
151
166
|
#
|
152
167
|
# @param [String] filename a string with a reference to the file to be written to
|
153
168
|
# @param [Hash] options depends on the writer chosen by the file's filetype
|
154
|
-
def write filename, options={}
|
169
|
+
def write filename, options = {}
|
155
170
|
extension = file_extension(filename)
|
156
171
|
send("write_to_#{extension}".to_sym, filename, options)
|
157
172
|
end
|
158
173
|
|
159
|
-
|
160
174
|
# Helper method to convert text in a file to UTF-8
|
161
175
|
#
|
162
176
|
# @param [String] text a string to convert
|
163
177
|
def text_to_utf8 text
|
164
|
-
unless text.valid_encoding?
|
178
|
+
unless text.valid_encoding? && (text.encoding == "UTF-8")
|
165
179
|
# TODO: had some ruby 1.9 problems with rchardet ... but ideally it or a similar functionality will be reintroduced
|
166
180
|
source_encoding = text.valid_encoding? ? text.encoding : "US-ASCII"
|
167
|
-
text = text.encode(
|
168
|
-
text = text.
|
181
|
+
text = text.encode("UTF-8", source_encoding, invalid: :replace, undef: :replace, replace: "")
|
182
|
+
text = text.delete("\u0000") # TODO: this cleanup of nil values isn't supposed to be needed...
|
169
183
|
end
|
170
184
|
text
|
171
185
|
end
|
@@ -174,16 +188,16 @@ module Workbook
|
|
174
188
|
#
|
175
189
|
# @return [String] The file extension
|
176
190
|
def file_extension(filename)
|
177
|
-
ext = File.extname(filename).
|
191
|
+
ext = File.extname(filename).delete(".").downcase if filename
|
178
192
|
# for remote files which has asset id after extension
|
179
|
-
ext.split(
|
193
|
+
ext.split("?")[0]
|
180
194
|
end
|
181
195
|
|
182
196
|
# Load the CSV data contained in the given StringIO or String object
|
183
197
|
#
|
184
198
|
# @param [StringIO] stringio_or_string StringIO stream or String object, with data in CSV format
|
185
199
|
# @param [Symbol] filetype (currently only :csv or :txt), indicating the format of the first parameter
|
186
|
-
def read(stringio_or_string, filetype, options={})
|
200
|
+
def read(stringio_or_string, filetype, options = {})
|
187
201
|
raise ArgumentError.new("The filetype parameter should be either :csv or :txt") unless [:csv, :txt].include?(filetype)
|
188
202
|
t = stringio_or_string.respond_to?(:read) ? stringio_or_string.read : stringio_or_string.to_s
|
189
203
|
t = text_to_utf8(t)
|
@@ -195,7 +209,7 @@ module Workbook
|
|
195
209
|
# @param [Integer] index the index of the sheet
|
196
210
|
def create_or_open_sheet_at index
|
197
211
|
s = self[index]
|
198
|
-
s = self[index] = Workbook::Sheet.new if s
|
212
|
+
s = self[index] = Workbook::Sheet.new if s.nil?
|
199
213
|
s.book = self
|
200
214
|
s
|
201
215
|
end
|
@@ -206,10 +220,10 @@ module Workbook
|
|
206
220
|
# @param [String] filename of the document
|
207
221
|
# @param [String] extension of the document (not required). The parser used is based on the extension of the file, this option allows you to override the default.
|
208
222
|
# @return [Workbook::Book] A new instance, based on the filename
|
209
|
-
def open filename, extension=nil
|
210
|
-
wb =
|
223
|
+
def open filename, extension = nil
|
224
|
+
wb = new
|
211
225
|
wb.import filename, extension
|
212
|
-
|
226
|
+
wb
|
213
227
|
end
|
214
228
|
|
215
229
|
# Create an instance from the given stream or string, which should be in CSV or TXT format
|
@@ -217,12 +231,11 @@ module Workbook
|
|
217
231
|
# @param [StringIO] stringio_or_string StringIO stream or String object, with data in CSV or TXT format
|
218
232
|
# @param [Symbol] filetype (currently only :csv or :txt), indicating the format of the first parameter
|
219
233
|
# @return [Workbook::Book] A new instance
|
220
|
-
def read stringio_or_string, filetype, options={}
|
221
|
-
wb =
|
234
|
+
def read stringio_or_string, filetype, options = {}
|
235
|
+
wb = new
|
222
236
|
wb.read(stringio_or_string, filetype, options)
|
223
237
|
wb
|
224
238
|
end
|
225
|
-
|
226
239
|
end
|
227
240
|
end
|
228
241
|
end
|
data/lib/workbook/cell.rb
CHANGED
@@ -1,18 +1,65 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
1
|
# frozen_string_literal: true
|
3
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "workbook/modules/cell"
|
4
5
|
|
5
6
|
module Workbook
|
6
7
|
class Cell
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
include Workbook::Modules::Cell
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# returns a symbol representation of a string.
|
13
|
+
# @param [String] value to convert
|
14
|
+
# @example
|
15
|
+
#
|
16
|
+
# <Workbook::Cell value="yet another value">.to_sym # returns :yet_another_value
|
17
|
+
def value_to_sym value
|
18
|
+
v = nil
|
19
|
+
cell_type ||= ::Workbook::Modules::Cell::CLASS_CELLTYPE_MAPPING[value.class.to_s]
|
20
|
+
value_to_s = value.to_s.strip.downcase
|
21
|
+
unless value.nil? || value_to_s == ""
|
22
|
+
if cell_type == :integer
|
23
|
+
v = "num#{value}".to_sym
|
24
|
+
elsif cell_type == :float
|
25
|
+
v = "num#{value}".sub(".", "_").to_sym
|
26
|
+
else
|
27
|
+
v = value_to_s.strip
|
28
|
+
ends_with_exclamationmark = (v[-1] == "!")
|
29
|
+
ends_with_questionmark = (v[-1] == "?")
|
30
|
+
|
31
|
+
v = _replace_possibly_problematic_characters_from_string(v)
|
32
|
+
|
33
|
+
v = v.encode(Encoding.find("ASCII"), invalid: :replace, undef: :replace, replace: "")
|
34
|
+
|
35
|
+
v = "#{v}!" if ends_with_exclamationmark
|
36
|
+
v = "#{v}?" if ends_with_questionmark
|
37
|
+
v = v.downcase.to_sym
|
38
|
+
end
|
39
|
+
end
|
40
|
+
v
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def _replace_possibly_problematic_characters_from_string(string)
|
46
|
+
Workbook::Modules::Cell::CHARACTER_REPACEMENTS.each do |ac, rep|
|
47
|
+
ac.each do |s|
|
48
|
+
string = string.gsub(s, rep)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
string
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] value a valid value
|
57
|
+
# @param [Hash] options a reference to :format (Workbook::Format) can be specified
|
58
|
+
def initialize value = nil, options = {}
|
59
|
+
self.format = options[:format] if options[:format]
|
60
|
+
self.row = options[:row]
|
61
|
+
self.value = value
|
62
|
+
@to_sym = nil
|
63
|
+
end
|
17
64
|
end
|
18
65
|
end
|
data/lib/workbook/column.rb
CHANGED
@@ -1,33 +1,39 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
1
|
# frozen_string_literal: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
3
4
|
module Workbook
|
5
|
+
# Column helps us to store general properties of a column, and lets us easily perform operations on values within a column, it also exposes a read only Enumerable API to access the cells in the column.
|
4
6
|
|
5
|
-
# Column helps us to store general properties of a column, and lets us easily perform operations on values within a column
|
6
7
|
class Column
|
7
|
-
|
8
|
+
include Enumerable
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
delegate [:first, :last, :each, :count, :include?, :index, :to_csv, :length, :empty?] => :cells
|
8
12
|
|
9
|
-
|
13
|
+
attr_accessor :limit, :width # character limit
|
14
|
+
|
15
|
+
def initialize(table = nil, options = {})
|
10
16
|
self.table = table
|
11
|
-
options.each{ |k,v|
|
17
|
+
options.each { |k, v| public_send("#{k}=", v) }
|
12
18
|
end
|
13
19
|
|
14
20
|
# Returns column type, either :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :date, :binary, :boolean
|
15
21
|
def column_type
|
16
22
|
return @column_type if defined?(@column_type)
|
17
|
-
ind =
|
23
|
+
ind = index
|
18
24
|
table[1..500].each do |row|
|
19
|
-
if row[ind]
|
25
|
+
if row[ind]&.cell_type
|
20
26
|
cel_column_type = row[ind].cell_type
|
21
|
-
if !defined?(@column_type)
|
27
|
+
if !defined?(@column_type) || @column_type.nil?
|
22
28
|
@column_type = cel_column_type
|
23
|
-
elsif cel_column_type == @column_type
|
29
|
+
elsif (cel_column_type == @column_type) || (cel_column_type == :nil)
|
24
30
|
else
|
25
31
|
@column_type = :string
|
26
32
|
break
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
30
|
-
|
36
|
+
@column_type
|
31
37
|
end
|
32
38
|
|
33
39
|
# Returns index of the column within the table's columns-set
|
@@ -39,14 +45,12 @@ module Workbook
|
|
39
45
|
# Set the table this column belongs to
|
40
46
|
# @param [Workbook::Table] table this column belongs to
|
41
47
|
def table= table
|
42
|
-
raise(ArgumentError, "value should be nil or Workbook::Table") unless [NilClass,Workbook::Table].include? table.class
|
48
|
+
raise(ArgumentError, "value should be nil or Workbook::Table") unless [NilClass, Workbook::Table].include? table.class
|
43
49
|
@table = table
|
44
50
|
end
|
45
51
|
|
46
52
|
# @return [Workbook::Table]
|
47
|
-
|
48
|
-
@table
|
49
|
-
end
|
53
|
+
attr_reader :table
|
50
54
|
|
51
55
|
def column_type= column_type
|
52
56
|
if [:primary_key, :string, :text, :integer, :float, :decimal, :datetime, :date, :binary, :boolean].include? column_type
|
@@ -57,25 +61,26 @@ module Workbook
|
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
64
|
+
def cells
|
65
|
+
table_header_object_id = table.header.object_id
|
66
|
+
table.map{|r| r[index] unless r.object_id == table_header_object_id }.compact
|
67
|
+
end
|
68
|
+
|
60
69
|
def head_value
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
return "!noheader!"
|
65
|
-
end
|
70
|
+
table.header[index].value
|
71
|
+
rescue
|
72
|
+
"!noheader!"
|
66
73
|
end
|
67
74
|
|
68
75
|
def inspect
|
69
76
|
"<Workbook::Column index=#{index}, header=#{head_value}>"
|
70
77
|
end
|
71
78
|
|
72
|
-
#default cell
|
73
|
-
|
74
|
-
return @default
|
75
|
-
end
|
79
|
+
# default cell
|
80
|
+
attr_reader :default
|
76
81
|
|
77
82
|
def default= value
|
78
|
-
@default = value if value.
|
83
|
+
@default = value if value.instance_of?(Cell)
|
79
84
|
@default = Cell.new(value)
|
80
85
|
end
|
81
86
|
|
@@ -84,13 +89,12 @@ module Workbook
|
|
84
89
|
# @param [String] string that typically identifies a column
|
85
90
|
# @return [Integer]
|
86
91
|
def alpha_index_to_number_index string
|
87
|
-
string.upcase!
|
88
92
|
sum = 0
|
89
|
-
string.chars.each_with_index do |
|
90
|
-
sum = sum * 26 + char.
|
93
|
+
string.upcase.chars.each_with_index do |char, char_index|
|
94
|
+
sum = sum * 26 + char.unpack1("U") - 64
|
91
95
|
end
|
92
|
-
|
96
|
+
sum - 1
|
93
97
|
end
|
94
98
|
end
|
95
99
|
end
|
96
|
-
end
|
100
|
+
end
|