workbook 0.4.5.1 → 0.4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/README.md +42 -42
- data/lib/workbook/cell.rb +20 -8
- data/lib/workbook/row.rb +12 -9
- data/lib/workbook/version.rb +1 -1
- data/test/test_cell.rb +13 -6
- data/test/test_sheet.rb +11 -0
- data/workbook.gemspec +1 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d0754f13283c6f5a93dcff9faab6fd3ab404c26
|
4
|
+
data.tar.gz: 5c63815a99a9105a4f0eca1a23d0d3f5e67380cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89e115237295547d0e68b4a9dcaf0e49d8790079d7b92a9e7f3b51b120bafd3cd4cbd73f09a3e326b22acafd85d5f59ed1e7661aa160ff1747acad830787c517
|
7
|
+
data.tar.gz: 28b2cecfcf40c8667fb72d1c3850289a4631e845285ef6b9d3b6eea326383607db38e67c7b1042d12776cc6307493317d6912ffe4436eef9d7d573e00c2afa31
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -5,57 +5,57 @@ Goal of this gem is to make working with workbooks (spreadsheets) as programmer
|
|
5
5
|
* Book
|
6
6
|
* Sheet (one or more)
|
7
7
|
* Table (one or more)
|
8
|
-
|
8
|
+
|
9
9
|
Subsequently a table consists of:
|
10
10
|
|
11
11
|
* Table
|
12
12
|
* Row (one or more)
|
13
13
|
* Cell ( wich has may have a (shared) Format )
|
14
|
-
|
15
|
-
Book, Sheet, Table and Row inherit from the base Array class, and hence walks and quacks 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.
|
14
|
+
|
15
|
+
Book, Sheet, Table and Row inherit from the base Array class, and hence walks and quacks 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.
|
16
16
|
|
17
17
|
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.
|
18
18
|
|
19
19
|
## The Basics
|
20
|
-
|
20
|
+
|
21
21
|
Simply initialize a simple spreadsheet using:
|
22
22
|
|
23
23
|
b = Workbook::Book.new
|
24
|
-
|
24
|
+
|
25
25
|
or
|
26
26
|
|
27
27
|
b = Workbook::Book.open filename
|
28
|
-
|
28
|
+
|
29
29
|
Calling
|
30
30
|
|
31
31
|
s = b.sheet
|
32
32
|
t = s.table
|
33
|
-
|
33
|
+
|
34
34
|
will give you an the first Sheet and Table (if one doesn't exist it is created on the fly).
|
35
35
|
|
36
36
|
You can initialize with simple 2-d array like this:
|
37
37
|
|
38
38
|
b = Workbook::Book.new [['a','b'],[1,2],[3,4],[5,6]]
|
39
39
|
t = s.sheet.table
|
40
|
-
|
40
|
+
|
41
41
|
Subsequently you look up values in the table like this:
|
42
42
|
|
43
|
-
t[1][:b]
|
43
|
+
t[1][:b]
|
44
44
|
# returns <Workbook::Cel @value=2>
|
45
45
|
|
46
46
|
which is equivalent to
|
47
47
|
|
48
|
-
t[1][1]
|
49
|
-
|
48
|
+
t[1][1]
|
49
|
+
|
50
50
|
Of course you'll be able to write a new value back to it. If you just enter a value, formatting of the original cell will be maintained.
|
51
51
|
|
52
52
|
t[1][:b] = 5
|
53
|
-
|
53
|
+
|
54
54
|
Alternatively (more spreadsheet like) you can read cells like this (writing to be supported, not implemented yet)
|
55
|
-
|
55
|
+
|
56
56
|
t['A2']
|
57
|
-
|
58
|
-
If you want to use an existing file as a template (which you can create in Excel to create nice looking templates),
|
57
|
+
|
58
|
+
If you want to use an existing file as a template (which you can create in Excel to create nice looking templates),
|
59
59
|
simply clone the row, and add it back:
|
60
60
|
|
61
61
|
b = Workbook::Book.open("template.xls")
|
@@ -65,16 +65,16 @@ simply clone the row, and add it back:
|
|
65
65
|
# row for the data
|
66
66
|
[1,2,3,4].each do |v|
|
67
67
|
new_row = template_row.clone
|
68
|
-
table << new_row # to use the symbol style header references,
|
69
|
-
# the row first needs to be added back to the
|
68
|
+
table << new_row # to use the symbol style header references,
|
69
|
+
# the row first needs to be added back to the
|
70
70
|
# table
|
71
71
|
new_row[:a] = v
|
72
72
|
end
|
73
73
|
table.delete(template_row) # you don't want the template to show up
|
74
74
|
# in the endresult
|
75
75
|
b.write("result.xls") # write it!
|
76
|
-
|
77
|
-
Another typical use case is exporting a list of ActiveRecord-objects to xls (it is assumed that the headers of the excel-table correspond
|
76
|
+
|
77
|
+
Another typical use case is exporting a list of ActiveRecord-objects to xls (it is assumed that the headers of the excel-table correspond
|
78
78
|
(like "Total order price" and `total_order_price` match) to the headers of the database-table ):
|
79
79
|
|
80
80
|
b = Workbook::Book.open("template.xls")
|
@@ -90,54 +90,54 @@ Another typical use case is exporting a list of ActiveRecord-objects to xls (it
|
|
90
90
|
b.write("recent_orders.xls") # write it!
|
91
91
|
|
92
92
|
|
93
|
-
<!-- Feature *to implement*:
|
94
|
-
|
93
|
+
<!-- Feature *to implement*:
|
94
|
+
|
95
95
|
Feature *to implement*, get a single column:
|
96
96
|
|
97
97
|
t[:b]
|
98
|
-
# returns [<Workbook::Cel @value=2>,<Workbook::Cel @value=4>,<Workbook::Cel @value=6>]
|
99
|
-
|
98
|
+
# returns [<Workbook::Cel @value=2>,<Workbook::Cel @value=4>,<Workbook::Cel @value=6>]
|
99
|
+
|
100
100
|
On my wishlist: In the future I hope to return the cell value directly, without the intermediate Workbook::Cell class in between.
|
101
|
-
|
101
|
+
|
102
102
|
-->
|
103
|
-
|
103
|
+
|
104
104
|
## Utilities
|
105
105
|
|
106
106
|
### Sorting
|
107
107
|
|
108
108
|
Sorting leaves the header alone, if it exists, and doesn't complain about comparing strings with dates with floats (Ever found OpenOffice Calc or Excel complainging about its inability to compare integers and strings? We're talking spreadsheet here). When classes are different the following (default) order is used: Numbers, Strings, Dates and Times, Booleans and Nils (empty values).
|
109
109
|
|
110
|
-
|
111
|
-
|
110
|
+
t.sort
|
111
|
+
|
112
112
|
*To implement*:
|
113
113
|
|
114
|
-
To some extent, sort_by works, it doesn't, however, adhere to the header settings...
|
115
|
-
|
114
|
+
To some extent, sort_by works, it doesn't, however, adhere to the header settings...
|
115
|
+
|
116
116
|
t.sort_by {|r| r[:b]}
|
117
|
-
|
117
|
+
|
118
118
|
### Comparing tables
|
119
|
-
|
119
|
+
|
120
120
|
Simply call on a Workbook::Table
|
121
121
|
|
122
122
|
t1.diff t2
|
123
|
-
|
123
|
+
|
124
124
|
And a new book with a new sheet/table will be returned containing the differences between the two tables.
|
125
|
-
|
125
|
+
|
126
126
|
## Writing
|
127
127
|
|
128
128
|
Currently writing is limited to the following formats. Templating support is still limited.
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
t.(write_)to_json
|
135
|
-
|
129
|
+
|
130
|
+
b.to_xls # returns a spreadsheet workbook
|
131
|
+
b.write_to_xls filename # writes to filename
|
132
|
+
t.(write_)to_csv # returns a csv-string (called on tables)
|
133
|
+
b.(write_)to_html # returns a clean html-page with all tables; unformatted, format-names are used in the classes
|
134
|
+
t.(write_)to_json # returns the values of a table in json
|
135
|
+
|
136
136
|
In case you want to display a formatted table in HTML, some conversion is offered to convert text/background properties to css-entities. Internally the hash storing style elements tries to map to CSS where possible.
|
137
|
-
|
137
|
+
|
138
138
|
## Compatibility
|
139
139
|
|
140
|
-
Workbook should be compatible with ruby 1.8.7, 1.9.3, 2.0.0 (jruby 1.8 mode works as well, but currently there are some issues with jruby 1.9 and encoding). Check [Travis for Workbook's current build status](https://travis-ci.org/murb/workbook) [![Build Status](https://travis-ci.org/murb/workbook.png?branch=master)](https://travis-ci.org/murb/workbook).
|
140
|
+
Workbook should be compatible with ruby 1.8.7, 1.9.3, 2.0.0 (jruby 1.8 mode works as well, but currently there are some issues with jruby 1.9 and encoding). Check [Travis for Workbook's current build status](https://travis-ci.org/murb/workbook) [![Build Status](https://travis-ci.org/murb/workbook.png?branch=master)](https://travis-ci.org/murb/workbook).
|
141
141
|
|
142
142
|
## Alternatives
|
143
143
|
|
data/lib/workbook/cell.rb
CHANGED
@@ -27,6 +27,7 @@ module Workbook
|
|
27
27
|
if valid_value? value
|
28
28
|
self.format = options[:format]
|
29
29
|
@value = value
|
30
|
+
@to_sym = nil
|
30
31
|
else
|
31
32
|
raise ArgumentError, "value should be of a primitive type, e.g. a string, or an integer, not a #{value.class} (is_a? [TrueClass,FalseClass,Date,Time,Numeric,String, NilClass])"
|
32
33
|
end
|
@@ -38,6 +39,7 @@ module Workbook
|
|
38
39
|
def value= value
|
39
40
|
if valid_value? value
|
40
41
|
@value = value
|
42
|
+
@to_sym = nil
|
41
43
|
else
|
42
44
|
raise ArgumentError, "value should be of a primitive type, e.g. a string, or an integer, not a #{value.class} (is_a? [TrueClass,FalseClass,Date,Time,Numeric,String, NilClass])"
|
43
45
|
end
|
@@ -94,13 +96,14 @@ module Workbook
|
|
94
96
|
#
|
95
97
|
# <Workbook::Cell value="yet another value">.to_sym # returns :yet_another_value
|
96
98
|
def to_sym
|
99
|
+
return @to_sym if @to_sym
|
97
100
|
#mb_chars.normalize(:kd).
|
98
101
|
v = nil
|
99
102
|
if value
|
103
|
+
ends_with_exclamationmark = (value[-1] == '!')
|
104
|
+
ends_with_questionmark = (value[-1] == '?')
|
100
105
|
v = value.to_s.downcase
|
101
|
-
v = v.
|
102
|
-
v = v.strip.gsub(/(\.|\?|,|\=)/,'').
|
103
|
-
gsub('$','').
|
106
|
+
v = v.strip.gsub(/[\(\)\.\?\,\!\=\$]/,'').
|
104
107
|
gsub(/\&/,'en').
|
105
108
|
gsub(/\+/,'_plus_').
|
106
109
|
gsub(/\s/, "_").
|
@@ -113,8 +116,8 @@ module Workbook
|
|
113
116
|
gsub('-','')
|
114
117
|
|
115
118
|
accents = {
|
116
|
-
['á','à','â','ä','ã'] => 'a',
|
117
|
-
['Ã','Ä','Â','À','�?'] => 'A',
|
119
|
+
['á','à','â','ä','ã','å'] => 'a',
|
120
|
+
['Ã','Ä','Â','À','�?','Å'] => 'A',
|
118
121
|
['é','è','ê','ë'] => 'e',
|
119
122
|
['Ë','É','È','Ê'] => 'E',
|
120
123
|
['í','ì','î','ï'] => 'i',
|
@@ -123,8 +126,14 @@ module Workbook
|
|
123
126
|
['Õ','Ö','Ô','Ò','Ó'] => 'O',
|
124
127
|
['ú','ù','û','ü'] => 'u',
|
125
128
|
['Ú','Û','Ù','Ü'] => 'U',
|
126
|
-
['ç'] => 'c',
|
127
|
-
['
|
129
|
+
['ç'] => 'c',
|
130
|
+
['Ç'] => 'C',
|
131
|
+
['š', 'ś'] => 's',
|
132
|
+
['Š', 'Ś'] => 'S',
|
133
|
+
['ž','ź'] => 'z',
|
134
|
+
['Ž','Ź'] => 'Z',
|
135
|
+
['ñ'] => 'n',
|
136
|
+
['Ñ'] => 'N'
|
128
137
|
}
|
129
138
|
accents.each do |ac,rep|
|
130
139
|
ac.each do |s|
|
@@ -138,9 +147,12 @@ module Workbook
|
|
138
147
|
encoding_options = {:invalid => :replace, :undef => :replace, :replace => ''}
|
139
148
|
v = v.encode(Encoding.find('ASCII'), encoding_options)
|
140
149
|
end
|
150
|
+
v = "#{v}!" if ends_with_exclamationmark
|
151
|
+
v = "#{v}?" if ends_with_questionmark
|
141
152
|
v = v.downcase.to_sym
|
142
153
|
end
|
143
|
-
v
|
154
|
+
@to_sym = v
|
155
|
+
return @to_sym
|
144
156
|
end
|
145
157
|
|
146
158
|
# Compare
|
data/lib/workbook/row.rb
CHANGED
@@ -124,7 +124,7 @@ module Workbook
|
|
124
124
|
#
|
125
125
|
# @return [Boolean]
|
126
126
|
def header?
|
127
|
-
table != nil and self.object_id ==
|
127
|
+
table != nil and self.object_id == table_header.object_id
|
128
128
|
end
|
129
129
|
|
130
130
|
# Is this the first row in the table
|
@@ -153,25 +153,28 @@ module Workbook
|
|
153
153
|
self.collect{|c| c}
|
154
154
|
end
|
155
155
|
|
156
|
+
def table_header
|
157
|
+
table.header
|
158
|
+
end
|
159
|
+
|
156
160
|
def table_header_keys
|
157
|
-
|
161
|
+
table_header.to_symbols
|
158
162
|
end
|
159
163
|
|
160
164
|
# Returns a hash representation of this row
|
161
165
|
#
|
162
166
|
# @return [Hash]
|
163
167
|
def to_hash
|
164
|
-
#return @hash if @hash
|
165
168
|
keys = table_header_keys
|
166
169
|
values = self
|
167
|
-
|
168
|
-
keys.each_with_index {|k,i|
|
169
|
-
return
|
170
|
+
hash = {}
|
171
|
+
keys.each_with_index {|k,i| hash[k]=values[i]}
|
172
|
+
return hash
|
170
173
|
end
|
171
|
-
|
174
|
+
|
172
175
|
# Returns a hash representation of this row
|
173
176
|
#
|
174
|
-
# it differs from #to_hash as it doesn't contain the Workbook's Workbook::Cell-objects,
|
177
|
+
# it differs from #to_hash as it doesn't contain the Workbook's Workbook::Cell-objects,
|
175
178
|
# but the actual values contained in these cells
|
176
179
|
#
|
177
180
|
# @return [Hash]
|
@@ -181,7 +184,7 @@ module Workbook
|
|
181
184
|
values = self
|
182
185
|
@hash_with_values = {}
|
183
186
|
keys.each_with_index {|k,i| v=values[i]; v=v.value if v; @hash_with_values[k]=v}
|
184
|
-
return @hash_with_values
|
187
|
+
return @hash_with_values
|
185
188
|
end
|
186
189
|
|
187
190
|
# Compares one row wiht another
|
data/lib/workbook/version.rb
CHANGED
data/test/test_cell.rb
CHANGED
@@ -73,12 +73,19 @@ class TestCell < Test::Unit::TestCase
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_to_sym
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
76
|
+
examples = {
|
77
|
+
"A - B" => :a_b,
|
78
|
+
"A-B" => :ab,
|
79
|
+
"A - c (B123)" => :a_c_b123,
|
80
|
+
"A - c (B123)!" => :a_c_b123!,
|
81
|
+
"A-B?" => :ab?,
|
82
|
+
"A-B!" => :ab!,
|
83
|
+
"éåšžÌ?" => :easzi?
|
84
|
+
}
|
85
|
+
examples.each do |k,v|
|
86
|
+
assert_equal(v, Workbook::Cell.new(k).to_sym)
|
87
|
+
end
|
88
|
+
|
82
89
|
|
83
90
|
end
|
84
91
|
|
data/test/test_sheet.rb
CHANGED
@@ -61,6 +61,17 @@ class TestWorkbook < Test::Unit::TestCase
|
|
61
61
|
assert_equal(s, table1.sheet)
|
62
62
|
table1<<Workbook::Row.new([1,2,3,4])
|
63
63
|
assert_equal(false, table1 == table0)
|
64
|
+
end
|
65
|
+
def test_profile_speed
|
66
|
+
w = Workbook::Book.new [["a","b"],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4],[1,2],[3,4]]
|
67
|
+
require 'ruby-prof'
|
68
|
+
RubyProf.start
|
69
|
+
w.sheet.table.each do |row|
|
70
|
+
row[:a].value
|
71
|
+
end
|
72
|
+
result = RubyProf.stop
|
73
|
+
printer = RubyProf::MultiPrinter.new(result)
|
74
|
+
printer.print(:path => ".", :profile => "profile")
|
64
75
|
|
65
76
|
end
|
66
77
|
end
|
data/workbook.gemspec
CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = "Workbook is a datastructure to contain books of tables (an anlogy used in e.g. Excel)"
|
13
13
|
s.description = "Workbook contains workbooks, as in a table, contains rows, contains cells, reads/writes excel, ods and csv and tab separated files, and offers basic diffing and sorting capabilities."
|
14
14
|
s.authors = ["Maarten Brouwers"]
|
15
|
+
s.add_development_dependency 'ruby-prof'
|
15
16
|
s.add_dependency('rubyzip', '0.9.9')
|
16
17
|
s.add_dependency('spreadsheet', '>= 0.7.5')
|
17
18
|
s.add_dependency('fastercsv') if RUBY_VERSION < "1.9"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maarten Brouwers
|
@@ -10,6 +10,20 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2013-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-prof
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rubyzip
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|