workbook 0.1.6.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +8 -6
- data/lib/workbook/book.rb +14 -6
- data/lib/workbook/readers/xlsx_reader.rb +2 -0
- data/lib/workbook/row.rb +25 -3
- data/lib/workbook/table.rb +7 -4
- data/lib/workbook/writers/xls_writer.rb +20 -22
- data/readme.markdown +9 -1
- data/test/test_modules_table_diff_sort.rb +1 -1
- data/test/test_readers_csv_reader.rb +1 -1
- data/test/test_readers_xls_reader.rb +1 -1
- data/test/test_writers_xls_writer.rb +10 -0
- data/workbook.gemspec +3 -3
- metadata +6 -7
data/Gemfile.lock
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
workbook (0.
|
4
|
+
workbook (0.2.0)
|
5
5
|
fastercsv
|
6
6
|
rchardet (~> 1.3)
|
7
|
-
|
7
|
+
rubyXL
|
8
|
+
spreadsheet (>= 0.7.5)
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: http://rubygems.org/
|
11
12
|
specs:
|
12
|
-
fastercsv (1.5.
|
13
|
-
rchardet (1.3)
|
14
|
-
ruby-ole (1.2.11.
|
15
|
-
|
13
|
+
fastercsv (1.5.5)
|
14
|
+
rchardet (1.3.1)
|
15
|
+
ruby-ole (1.2.11.6)
|
16
|
+
rubyXL (1.2.10)
|
17
|
+
spreadsheet (0.7.5)
|
16
18
|
ruby-ole (>= 1.0)
|
17
19
|
|
18
20
|
PLATFORMS
|
data/lib/workbook/book.rb
CHANGED
@@ -6,6 +6,9 @@ require 'workbook/readers/txt_reader'
|
|
6
6
|
require 'rchardet'
|
7
7
|
|
8
8
|
module Workbook
|
9
|
+
# The Book class is the container of sheets. It can be inialized by either the standard initalizer or the open method. The
|
10
|
+
# Book class can also keep a reference to a template class, storing shared formatting options.
|
11
|
+
#
|
9
12
|
class Book < Array
|
10
13
|
include Workbook::Writers::XlsWriter
|
11
14
|
include Workbook::Readers::XlsReader
|
@@ -15,7 +18,6 @@ module Workbook
|
|
15
18
|
|
16
19
|
attr_accessor :title
|
17
20
|
attr_accessor :template
|
18
|
-
attr_accessor :default_rewrite_header
|
19
21
|
|
20
22
|
# @param [Workbook::Sheet, Array] Create a new workbook based on an existing sheet, or initialize a sheet based on the array
|
21
23
|
# @return [Workbook::Book]
|
@@ -53,13 +55,16 @@ module Workbook
|
|
53
55
|
first
|
54
56
|
end
|
55
57
|
|
58
|
+
# @return [Boolean] returns true if the first sheet has contents
|
56
59
|
def has_contents?
|
57
60
|
sheet.has_contents?
|
58
61
|
end
|
59
62
|
|
60
63
|
# Loads an external file into an existing worbook
|
64
|
+
#
|
61
65
|
# @param [String] a string with a reference to the file to be opened
|
62
66
|
# @param [String] an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
|
67
|
+
# @return [Workbook::Book] A new instance, based on the filename
|
63
68
|
def open filename, ext=nil
|
64
69
|
ext = file_extension(filename) unless ext
|
65
70
|
if ['txt','csv','xml'].include?(ext)
|
@@ -70,8 +75,10 @@ module Workbook
|
|
70
75
|
end
|
71
76
|
|
72
77
|
# Open the file in binary, read-only mode, do not read it, but pas it throug to the extension determined loaded
|
78
|
+
#
|
73
79
|
# @param [String] a string with a reference to the file to be opened
|
74
80
|
# @param [String] an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
|
81
|
+
# @return [Workbook::Book] A new instance, based on the filename
|
75
82
|
def open_binary filename, ext=nil
|
76
83
|
ext = file_extension(filename) unless ext
|
77
84
|
f = File.open(filename,'rb')
|
@@ -79,6 +86,7 @@ module Workbook
|
|
79
86
|
end
|
80
87
|
|
81
88
|
# Open the file in non-binary, read-only mode, read it and parse it to UTF-8
|
89
|
+
#
|
82
90
|
# @param [String] a string with a reference to the file to be opened
|
83
91
|
# @param [String] an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
|
84
92
|
def open_text filename, ext=nil
|
@@ -91,12 +99,16 @@ module Workbook
|
|
91
99
|
end
|
92
100
|
|
93
101
|
# @param [String] The full filename, or path
|
102
|
+
#
|
94
103
|
# @return [String] The file extension
|
95
104
|
def file_extension(filename)
|
96
105
|
File.extname(filename).gsub('.','').downcase if filename
|
97
106
|
end
|
98
107
|
|
99
108
|
# Create an instance from a file, using open.
|
109
|
+
#
|
110
|
+
# @param [String] the filename of the document
|
111
|
+
# @param [String] (not required) enforce a certain extension, the parser is based on the extension of the file
|
100
112
|
# @return [Workbook::Book] A new instance, based on the filename
|
101
113
|
def self.open filename, ext=nil
|
102
114
|
wb = self.new
|
@@ -114,11 +126,7 @@ module Workbook
|
|
114
126
|
def sort
|
115
127
|
raise Exception("Books can't be sorted")
|
116
128
|
end
|
117
|
-
|
118
|
-
def default_rewrite_header?
|
119
|
-
return true if default_rewrite_header.nil?
|
120
|
-
default_rewrite_header
|
121
|
-
end
|
129
|
+
|
122
130
|
|
123
131
|
end
|
124
132
|
end
|
@@ -62,6 +62,7 @@ module RubyXL
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
# end monkey patch submitted
|
65
|
+
|
65
66
|
module RubyXL
|
66
67
|
class Workbook
|
67
68
|
def num_fmts_by_id
|
@@ -106,6 +107,7 @@ module RubyXL
|
|
106
107
|
end
|
107
108
|
end
|
108
109
|
end
|
110
|
+
|
109
111
|
# other monkey patch
|
110
112
|
module RubyXL
|
111
113
|
class Cell
|
data/lib/workbook/row.rb
CHANGED
@@ -2,11 +2,14 @@ module Workbook
|
|
2
2
|
class Row < Array
|
3
3
|
alias_method :compare_without_header, :<=>
|
4
4
|
|
5
|
-
#used in compares
|
5
|
+
# The placeholder attribute is used in compares (corresponds to newly created or removed lines (depending which side you're on)
|
6
6
|
attr_accessor :placeholder
|
7
7
|
attr_accessor :format
|
8
8
|
|
9
9
|
|
10
|
+
# @param [Workbook::Row, Array<Workbook::Cell>, Array] list of cells to initialize the row with, default is empty
|
11
|
+
# @param [Workbook::Table] a row normally belongs to a table, reference it here
|
12
|
+
# @param [Hash], option hash. Supprted options: parse_cells_on_batch_creation (parse cell values during row-initalization, default: false), cell_parse_options (default {}, see Workbook::Modules::TypeParser)
|
10
13
|
def initialize cells=[], table=nil, options={}
|
11
14
|
options=options ? {:parse_cells_on_batch_creation=>false,:cell_parse_options=>{}}.merge(options) : {}
|
12
15
|
cells = [] if cells==nil
|
@@ -30,7 +33,9 @@ module Workbook
|
|
30
33
|
@table
|
31
34
|
end
|
32
35
|
|
33
|
-
#
|
36
|
+
# Set reference to the table this row belongs to (and adds the row to this table)
|
37
|
+
#
|
38
|
+
# @param [Workbook::Table]
|
34
39
|
def table= t
|
35
40
|
raise ArgumentError, "table should be a Workbook::Table (you passed a #{t.class})" unless t.is_a?(Workbook::Table) or t == nil
|
36
41
|
if t
|
@@ -39,6 +44,14 @@ module Workbook
|
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
47
|
+
# Overrides normal Array's []-function with support for symbols that identify a column based on the header-values
|
48
|
+
#
|
49
|
+
# @example Lookup using fixnum or header value encoded as symbol
|
50
|
+
# row[1] #=> <Cell value="a">
|
51
|
+
# row[:a] #=> <Cell value="a">
|
52
|
+
#
|
53
|
+
# @param [Fixnum, Symbol]
|
54
|
+
# @return [Workbook::Cell, nil]
|
42
55
|
def [](index_or_hash)
|
43
56
|
if index_or_hash.is_a? Fixnum
|
44
57
|
return to_a[index_or_hash]
|
@@ -52,7 +65,11 @@ module Workbook
|
|
52
65
|
end
|
53
66
|
end
|
54
67
|
|
55
|
-
#
|
68
|
+
# Returns an array of cells allows you to find cells by a given color, normally a string containing a hex
|
69
|
+
#
|
70
|
+
# @param [String] default :any colour, can be a CSS-style hex-string
|
71
|
+
# @param [Hash] options. Option :hash_keys (default true) returns row as an array of symbols
|
72
|
+
# @return [Array<Symbol>, Workbook::Row<Workbook::Cell>]
|
56
73
|
def find_cells_by_background_color color=:any, options={}
|
57
74
|
options = {:hash_keys=>true}.merge(options)
|
58
75
|
cells = self.collect {|c| c if c.format.has_background_color?(color) }.compact
|
@@ -60,14 +77,19 @@ module Workbook
|
|
60
77
|
options[:hash_keys] ? r.to_symbols : r
|
61
78
|
end
|
62
79
|
|
80
|
+
# Returns true when the row belongs to a table and it is the header row (typically the first row)
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
63
83
|
def header?
|
64
84
|
table != nil and self.object_id == table.header.object_id
|
65
85
|
end
|
66
86
|
|
87
|
+
# @return [Array<Symbol>] returns row as an array of symbols
|
67
88
|
def to_symbols
|
68
89
|
collect{|c| c.to_sym}
|
69
90
|
end
|
70
91
|
|
92
|
+
# @return [Array<Workbook::Cell>] returns row as an array of symbols
|
71
93
|
def to_a
|
72
94
|
self.collect{|c| c}
|
73
95
|
end
|
data/lib/workbook/table.rb
CHANGED
@@ -21,9 +21,12 @@ module Workbook
|
|
21
21
|
end
|
22
22
|
self.sheet = sheet
|
23
23
|
# Column data is considered as a 'row' with 'cells' that contain 'formatting'
|
24
|
-
|
25
24
|
end
|
26
25
|
|
26
|
+
# Returns the header of this table (typically the first row, but can be a different row).
|
27
|
+
# The header row is also used for finding values in a aribrary row.
|
28
|
+
#
|
29
|
+
# @return [Workbook::Row] The header
|
27
30
|
def header
|
28
31
|
if @header == false
|
29
32
|
false
|
@@ -34,9 +37,9 @@ module Workbook
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
37
|
-
#
|
38
|
-
def new_row
|
39
|
-
r = Workbook::Row.new(
|
40
|
+
# Generates a new row, with optionally predefined cell-values, that is already connected to this table.
|
41
|
+
def new_row cell_values=[]
|
42
|
+
r = Workbook::Row.new(cell_values,self)
|
40
43
|
return r
|
41
44
|
end
|
42
45
|
|
@@ -70,34 +70,28 @@ module Workbook
|
|
70
70
|
:cyan=>'#00FFFF',
|
71
71
|
:border=>'#FFFFFF',
|
72
72
|
:text=>'#000000',
|
73
|
-
:lime=>'#00f94c'
|
73
|
+
:lime=>'#00f94c'
|
74
|
+
}
|
74
75
|
|
76
|
+
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an XlS
|
77
|
+
#
|
78
|
+
# @params [Hash] A hash with options (unused so far)
|
79
|
+
# @return [Spreadsheet] A Spreadsheet object, ready for writing or more lower level operations
|
75
80
|
def to_xls options={}
|
76
|
-
options = {:rewrite_header=>default_rewrite_header?}.merge options
|
77
81
|
book = init_spreadsheet_template
|
78
82
|
self.each_with_index do |s,si|
|
79
83
|
xls_sheet = book.worksheet si
|
80
84
|
xls_sheet = book.create_worksheet if xls_sheet == nil
|
81
85
|
s.table.each_with_index do |r, ri|
|
82
|
-
|
83
|
-
|
84
|
-
if
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
write_row = true
|
89
|
-
end
|
90
|
-
if write_row
|
91
|
-
xls_sheet.row(ri).height= r.format[:height] if r.format
|
92
|
-
r.each_with_index do |c, ci|
|
93
|
-
if c
|
94
|
-
if r.header?
|
95
|
-
xls_sheet.columns[ci] ||= Spreadsheet::Column.new(ci,nil)
|
96
|
-
xls_sheet.columns[ci].width= c.format[:width]
|
97
|
-
end
|
98
|
-
xls_sheet.row(ri)[ci] = c.value
|
99
|
-
xls_sheet.row(ri).set_format(ci, format_to_xls_format(c.format))
|
86
|
+
xls_sheet.row(ri).height= r.format[:height] if r.format
|
87
|
+
r.each_with_index do |c, ci|
|
88
|
+
if c
|
89
|
+
if r.header?
|
90
|
+
xls_sheet.columns[ci] ||= Spreadsheet::Column.new(ci,nil)
|
91
|
+
xls_sheet.columns[ci].width= c.format[:width]
|
100
92
|
end
|
93
|
+
xls_sheet.row(ri)[ci] = c.value
|
94
|
+
xls_sheet.row(ri).set_format(ci, format_to_xls_format(c.format))
|
101
95
|
end
|
102
96
|
end
|
103
97
|
end
|
@@ -138,8 +132,12 @@ module Workbook
|
|
138
132
|
numberformat.gsub('%Y','yyyy').gsub('%A','dddd').gsub('%B','mmmm').gsub('%a','ddd').gsub('%b','mmm').gsub('%y','yy').gsub('%d','dd').gsub('%m','mm').gsub('%y','y').gsub('%y','%%y').gsub('%e','d')
|
139
133
|
end
|
140
134
|
|
141
|
-
|
142
|
-
|
135
|
+
# Write the current workbook to Microsoft Excel format (using the spreadsheet gem)
|
136
|
+
#
|
137
|
+
# @param [String] the filename
|
138
|
+
# @param [Hash] options, see #to_xls
|
139
|
+
|
140
|
+
def write_to_xls filename="#{title}.xls", options={}
|
143
141
|
if to_xls(options).write(filename)
|
144
142
|
return filename
|
145
143
|
end
|
data/readme.markdown
CHANGED
@@ -96,4 +96,12 @@ The [ruby toolbox lists plenty of alternatives](https://www.ruby-toolbox.com/sea
|
|
96
96
|
|
97
97
|
## License
|
98
98
|
|
99
|
-
MIT
|
99
|
+
This code MIT (but see below) (c) murb / Maarten Brouwers, 2011-2013
|
100
|
+
|
101
|
+
Workbook uses the following gems:
|
102
|
+
|
103
|
+
* [Spreadsheet](https://github.com/zdavatz/spreadsheet) (Copyright (c) 2010 ywesee GmbH (mhatakeyama@ywesee.com, zdavatz@ywesee.com); GPL3 (License required for closed implementations))
|
104
|
+
* [FasterCSV](http://fastercsv.rubyforge.org/) (Copyright (c) James Edward Gray II; GPL2 & Ruby License)
|
105
|
+
* [rchardet](http://rubyforge.org/projects/rchardet) (Copyright (c) JMHodges; LGPL)
|
106
|
+
* [ruby-ole](http://code.google.com/p/ruby-ole/) (Copyright (c) 2007-2010 Charles Lowe; MIT)
|
107
|
+
* [RubyXL](https://github.com/gilt/rubyXL) (Copyright (c) 2011 Vivek Bhagwat; MIT-Licensed)
|
@@ -99,7 +99,7 @@ module Modules
|
|
99
99
|
diff_result = tself.diff tother
|
100
100
|
assert_equal('a',diff_result.sheet.table.header[0].value)
|
101
101
|
assert_equal("a,b,c,d\n1,2,3,4\n3,2,3,4\n3,3 (was: 2),3,4\n4,2,3,4\n(was: 5),(was: 2),(was: 3),(was: 4)\n",diff_result.sheet.table.to_csv)
|
102
|
-
diff_result.write_to_xls
|
102
|
+
diff_result.write_to_xls
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -31,7 +31,7 @@ module Readers
|
|
31
31
|
assert_equal(Date.new(2001,1,1),w.sheet.table[1][:a].value)
|
32
32
|
assert_equal(DateTime.new(2011,2,12,12,23),w.sheet.table[4][:c].value)
|
33
33
|
assert_equal("6/12 ovk getekend terugontv.+>acq ter tekening. 13/12 ovk getekend terugontv.+>Fred ter tekening.", w.sheet.table[5][:b].value)
|
34
|
-
assert_equal(
|
34
|
+
assert_equal(DateTime.new(Time.now.year,6,12),w.sheet.table[5][:c].value)
|
35
35
|
end
|
36
36
|
def test_excel_standardized_open
|
37
37
|
w = Workbook::Book.new
|
@@ -44,7 +44,7 @@ module Readers
|
|
44
44
|
w = Workbook::Book.new
|
45
45
|
w.open("test/artifacts/txt_in_xls.xls")
|
46
46
|
assert_equal([:naam,:nummer,:ilt,:corporate_key,:naam_medewerker, nil, nil, :telefoon, :openingsdatum],w.sheet.table.header.to_symbols)
|
47
|
-
assert_equal(["dddd",2222,"i9000","asd","Anita",nil,"Betera",
|
47
|
+
assert_equal(["dddd",2222,"i9000","asd","Anita",nil,"Betera","012-3456789",Date.new(2011,10,5)],w.sheet.table[1].collect{|a| a.value})
|
48
48
|
end
|
49
49
|
def test_zip_in_xls_open
|
50
50
|
w = Workbook::Book.new
|
@@ -20,6 +20,16 @@ module Writers
|
|
20
20
|
b = Workbook::Book.open filename
|
21
21
|
assert_equal(3.85546875,b.sheet.table.first[:a].format[:width])
|
22
22
|
end
|
23
|
+
def test_cloning_roundtrip
|
24
|
+
b = Workbook::Book.open('test/artifacts/book_with_tabs_and_colours.xls')
|
25
|
+
b.sheet.table << b.sheet.table[2]
|
26
|
+
assert_equal(90588,b.sheet.table[5][:b].value)
|
27
|
+
assert_equal("#FFFF00",b.sheet.table[5][:c].format[:background_color])
|
28
|
+
filename = b.write_to_xls
|
29
|
+
b = Workbook::Book.open filename
|
30
|
+
assert_equal(90588,b.sheet.table[5][:b].value)
|
31
|
+
assert_equal("#FFFF00",b.sheet.table[5][:c].format[:background_color])
|
32
|
+
end
|
23
33
|
|
24
34
|
def test_init_spreadsheet_template
|
25
35
|
b = Workbook::Book.new
|
data/workbook.gemspec
CHANGED
@@ -5,12 +5,12 @@ require "workbook"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'workbook'
|
7
7
|
s.rubyforge_project = 'workbook'
|
8
|
-
s.version = '0.
|
9
|
-
s.date = '
|
8
|
+
s.version = '0.2.0'
|
9
|
+
s.date = '2013-01-10'
|
10
10
|
s.summary = "Workbook is a datastructure to contain books of tables (an anlogy used in e.g. Excel)"
|
11
11
|
s.description = "Workbook contains workbooks, as in a table, contains rows, contains cells, reads/writes excels and csv's and tab separated, and offers basic diffing and sorting capabilities."
|
12
12
|
s.authors = ["Maarten Brouwers"]
|
13
|
-
s.add_dependency('spreadsheet', '>= 0.
|
13
|
+
s.add_dependency('spreadsheet', '>= 0.7.5')
|
14
14
|
s.add_dependency('fastercsv')
|
15
15
|
s.add_dependency("rchardet", "~> 1.3")
|
16
16
|
s.add_dependency('rubyXL')
|
metadata
CHANGED
@@ -4,10 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
|
-
- 6
|
9
7
|
- 2
|
10
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Maarten Brouwers
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date:
|
17
|
+
date: 2013-01-10 00:00:00 +01:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -27,9 +26,9 @@ dependencies:
|
|
27
26
|
- !ruby/object:Gem::Version
|
28
27
|
segments:
|
29
28
|
- 0
|
30
|
-
-
|
31
|
-
-
|
32
|
-
version: 0.
|
29
|
+
- 7
|
30
|
+
- 5
|
31
|
+
version: 0.7.5
|
33
32
|
type: :runtime
|
34
33
|
version_requirements: *id001
|
35
34
|
- !ruby/object:Gem::Dependency
|