roo 1.11.2 → 1.12.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.
- data/CHANGELOG +26 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +26 -0
- data/README.markdown +82 -62
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/roo_soap_server.rb +1 -1
- data/lib/roo.rb +28 -10
- data/lib/roo/{generic_spreadsheet.rb → base.rb} +127 -159
- data/lib/roo/csv.rb +28 -30
- data/lib/roo/excel.rb +38 -48
- data/lib/roo/excel2003xml.rb +24 -36
- data/lib/roo/excelx.rb +59 -61
- data/lib/roo/google.rb +36 -59
- data/lib/roo/openoffice.rb +41 -65
- data/lib/roo/roo_rails_helper.rb +2 -1
- data/lib/roo/spreadsheet.rb +17 -8
- data/roo.gemspec +20 -8
- data/spec/fixtures/vcr_cassettes/google_drive.yml +165 -0
- data/spec/fixtures/vcr_cassettes/google_drive_access_token.yml +73 -0
- data/spec/lib/roo/base_spec.rb +7 -0
- data/spec/lib/roo/csv_spec.rb +54 -0
- data/spec/lib/roo/excel_spec.rb +17 -0
- data/spec/lib/roo/excelx_spec.rb +13 -0
- data/spec/lib/roo/google_spec.rb +36 -0
- data/spec/lib/roo/libreoffice_spec.rb +19 -0
- data/spec/lib/roo/openoffice_spec.rb +21 -0
- data/spec/lib/roo/spreadsheet_spec.rb +13 -1
- data/spec/spec_helper.rb +7 -0
- data/test/all_ss.rb +1 -1
- data/test/files/1900_base.xlsx +0 -0
- data/test/files/1904_base.xlsx +0 -0
- data/test/rm_sub_test.rb +1 -1
- data/test/test_generic_spreadsheet.rb +68 -66
- data/test/test_helper.rb +3 -84
- data/test/test_roo.rb +85 -708
- data/website/index.html +16 -16
- data/website/index.txt +42 -42
- metadata +44 -7
- data/TODO +0 -2
data/CHANGELOG
CHANGED
@@ -1,4 +1,29 @@
|
|
1
|
-
== 1.
|
1
|
+
== 1.12.0 (unreleased)
|
2
|
+
|
3
|
+
* 3 deprecations
|
4
|
+
* Rename Openoffice -> OpenOffice, Libreoffice -> LibreOffice, Csv -> CSV, and redirect the old names to the new constants
|
5
|
+
* Enable Roo::Excel, Excel2003XML, Excelx, OpenOffice to accept an options hash, and deprecate the old method argument based approach to supplying them options
|
6
|
+
* Roo's roo_rails_helper, aka the `spreadsheet` html-generating view method is currently deprecated with no replacement. If you find it helpful, tell http://github.com/Empact or extract it yourself.
|
7
|
+
|
8
|
+
* 9 enhancements
|
9
|
+
* Add Roo::Excelx#load_xml so that people can customize to their data, e.g. https://github.com/Empact/roo/pull/23
|
10
|
+
* Enable passing csv_options to Roo::CSV, which are passed through to the underlying CSV call.
|
11
|
+
* Enable passing options through from Roo::Spreadsheet to any Roo type.
|
12
|
+
* Enable passing an :extension option to Roo::Spreadsheet.new, which will override the extension detected on in the path https://github.com/Empact/roo/pull/15
|
13
|
+
* Switch from google-spreadsheet-ruby to google_drive for Roo::Google access https://github.com/Empact/roo/pull/40
|
14
|
+
* Make all the classes consistent in that #read_cells is only effective if the sheet has not been read.
|
15
|
+
* Roo::Google supports login via oauth :access_token. https://github.com/Empact/roo/pull/61
|
16
|
+
* Roo::Excel now exposes its Spreadsheet workbook via #workbook
|
17
|
+
* Pull #load_xml down into Roo::Base, and use it in Excel2003XML and OpenOffice.
|
18
|
+
|
19
|
+
* 2 changes
|
20
|
+
* #formula? now returns truthy or falsey, rather than true/false.
|
21
|
+
* Base#longest_sheet was moved to Excel, as it only worked under Excel
|
22
|
+
|
23
|
+
* 1 bugfix
|
24
|
+
* Fix that Roo::CSV#parse(headers: true) would blow up. https://github.com/Empact/roo/pull/37
|
25
|
+
|
26
|
+
== 1.11.2 2013-04-10
|
2
27
|
|
3
28
|
* 4 bugfixes
|
4
29
|
* Fix that Roo::Spreadsheet.open wasn't tolerant to case differences.
|
data/Gemfile
CHANGED
@@ -3,9 +3,9 @@ source 'http://rubygems.org'
|
|
3
3
|
gem 'spreadsheet', '> 0.6.4'
|
4
4
|
gem 'nokogiri'
|
5
5
|
gem 'rubyzip'
|
6
|
-
# gem 'google-spreadsheet-ruby'
|
7
6
|
|
8
7
|
group :development do
|
8
|
+
gem 'google_drive'
|
9
9
|
gem 'jeweler'
|
10
10
|
end
|
11
11
|
|
@@ -14,4 +14,5 @@ group :test do
|
|
14
14
|
gem 'webmock'
|
15
15
|
gem 'shoulda'
|
16
16
|
gem 'rspec'
|
17
|
+
gem 'vcr'
|
17
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -4,14 +4,37 @@ GEM
|
|
4
4
|
addressable (2.3.2)
|
5
5
|
crack (0.3.1)
|
6
6
|
diff-lcs (1.2.1)
|
7
|
+
faraday (0.8.7)
|
8
|
+
multipart-post (~> 1.1)
|
7
9
|
git (1.2.5)
|
10
|
+
google_drive (0.3.6)
|
11
|
+
nokogiri (>= 1.4.4, != 1.5.2, != 1.5.1)
|
12
|
+
oauth (>= 0.3.6)
|
13
|
+
oauth2 (>= 0.5.0)
|
14
|
+
httpauth (0.2.0)
|
8
15
|
jeweler (1.8.3)
|
9
16
|
bundler (~> 1.0)
|
10
17
|
git (>= 1.2.5)
|
11
18
|
rake
|
12
19
|
rdoc
|
13
20
|
json (1.7.7)
|
21
|
+
json (1.7.7-java)
|
22
|
+
jwt (0.1.8)
|
23
|
+
multi_json (>= 1.5)
|
24
|
+
multi_json (1.7.3)
|
25
|
+
multi_xml (0.5.3)
|
26
|
+
multipart-post (1.2.0)
|
14
27
|
nokogiri (1.5.6)
|
28
|
+
nokogiri (1.5.6-java)
|
29
|
+
oauth (0.4.7)
|
30
|
+
oauth2 (0.9.1)
|
31
|
+
faraday (~> 0.8)
|
32
|
+
httpauth (~> 0.1)
|
33
|
+
jwt (~> 0.1.4)
|
34
|
+
multi_json (~> 1.0)
|
35
|
+
multi_xml (~> 0.5)
|
36
|
+
rack (~> 1.2)
|
37
|
+
rack (1.5.2)
|
15
38
|
rake (0.9.2.2)
|
16
39
|
rdoc (3.12.2)
|
17
40
|
json (~> 1.4)
|
@@ -32,6 +55,7 @@ GEM
|
|
32
55
|
shoulda-matchers (1.0.0)
|
33
56
|
spreadsheet (0.8.2)
|
34
57
|
ruby-ole (>= 1.0)
|
58
|
+
vcr (2.5.0)
|
35
59
|
webmock (1.9.0)
|
36
60
|
addressable (>= 2.2.7)
|
37
61
|
crack (>= 0.1.7)
|
@@ -41,10 +65,12 @@ PLATFORMS
|
|
41
65
|
ruby
|
42
66
|
|
43
67
|
DEPENDENCIES
|
68
|
+
google_drive
|
44
69
|
jeweler
|
45
70
|
nokogiri
|
46
71
|
rspec
|
47
72
|
rubyzip
|
48
73
|
shoulda
|
49
74
|
spreadsheet (> 0.6.4)
|
75
|
+
vcr
|
50
76
|
webmock
|
data/README.markdown
CHANGED
@@ -2,108 +2,128 @@
|
|
2
2
|
|
3
3
|
Roo implements read access for all spreadsheet types and read/write access for
|
4
4
|
Google spreadsheets. It can handle
|
5
|
-
*
|
5
|
+
* OpenOffice
|
6
6
|
* Excel
|
7
7
|
* Google spreadsheets
|
8
8
|
* Excelx
|
9
|
-
*
|
9
|
+
* LibreOffice
|
10
10
|
* CSV
|
11
11
|
|
12
|
+
## Notes
|
13
|
+
|
14
|
+
### XLS
|
15
|
+
|
16
|
+
There is no support for formulas in Roo for .xls files - you can get the result
|
17
|
+
of a formula but not the formula itself.
|
18
|
+
|
19
|
+
### Google Spreadsheet
|
20
|
+
|
12
21
|
Using Roo to access Google spreadsheets requires you install the 'google-spreadsheet-ruby' gem separately.
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
## License
|
24
|
+
|
25
|
+
While Roo is licensed under the MIT / Expat license, please note that the 'spreadsheet' gem [is released under](https://github.com/zdavatz/spreadsheet/blob/master/LICENSE.txt) the GPLv3 license.
|
17
26
|
|
18
27
|
## Usage:
|
19
28
|
|
20
|
-
|
29
|
+
require 'roo'
|
30
|
+
|
31
|
+
s = Roo::OpenOffice.new("myspreadsheet.ods") # loads an OpenOffice Spreadsheet
|
32
|
+
s = Roo::Excel.new("myspreadsheet.xls") # loads an Excel Spreadsheet
|
33
|
+
s = Roo::Google.new("myspreadsheetkey_at_google") # loads a Google Spreadsheet
|
34
|
+
s = Roo::Excelx.new("myspreadsheet.xlsx") # loads an Excel Spreadsheet for Excel .xlsx files
|
35
|
+
s = Roo::CSV.new("mycsv.csv") # loads a CSV file
|
21
36
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
# You can use CSV to load TSV files, or files of a certain encoding by passing
|
38
|
+
# in options under the :csv_options key
|
39
|
+
s = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"}) # TSV
|
40
|
+
s = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1}) # csv with explicit encoding
|
26
41
|
|
27
|
-
|
42
|
+
s.default_sheet = s.sheets.first # first sheet in the spreadsheet file will be used
|
28
43
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
44
|
+
# s.sheets is an array which holds the names of the sheets within
|
45
|
+
# a spreadsheet.
|
46
|
+
# you can also write
|
47
|
+
# s.default_sheet = s.sheets[3] or
|
48
|
+
# s.default_sheet = 'Sheet 3'
|
34
49
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
50
|
+
s.cell(1,1) # returns the content of the first row/first cell in the sheet
|
51
|
+
s.cell('A',1) # same cell
|
52
|
+
s.cell(1,'A') # same cell
|
53
|
+
s.cell(1,'A',s.sheets[0]) # same cell
|
39
54
|
|
40
|
-
|
41
|
-
|
55
|
+
# almost all methods have an optional argument 'sheet'.
|
56
|
+
# If this parameter is omitted, the default_sheet will be used.
|
42
57
|
|
43
|
-
|
58
|
+
s.info # prints infos about the spreadsheet file
|
44
59
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
60
|
+
s.first_row # the number of the first row
|
61
|
+
s.last_row # the number of the last row
|
62
|
+
s.first_column # the number of the first column
|
63
|
+
s.last_column # the number of the last column
|
49
64
|
|
50
|
-
|
65
|
+
# limited font information is available
|
51
66
|
|
52
|
-
|
53
|
-
|
54
|
-
|
67
|
+
s.font(1,1).bold?
|
68
|
+
s.font(1,1).italic?
|
69
|
+
s.font(1,1).underline?
|
55
70
|
|
56
71
|
|
57
72
|
see http://roo.rubyforge.org for a more complete tutorial
|
58
73
|
|
59
74
|
# Fork Changelog / New Features
|
60
75
|
|
61
|
-
|
76
|
+
# Spreadsheet.open can accept both files and paths
|
77
|
+
|
78
|
+
xls = Roo::Spreadsheet.open('./new_prices.xls')
|
79
|
+
|
80
|
+
# If the File.path or provided path string does not have an extension, you can optionally
|
81
|
+
# provide one as a string or symbol
|
62
82
|
|
63
|
-
|
83
|
+
xls = Roo::Spreadsheet.open('./rails_temp_upload', extension: :xls)
|
64
84
|
|
65
|
-
|
85
|
+
# no more setting xls.default_sheet, just use this
|
66
86
|
|
67
|
-
|
68
|
-
|
87
|
+
xls.sheet('Info').row(1)
|
88
|
+
xls.sheet(0).row(1)
|
69
89
|
|
70
|
-
|
71
|
-
|
90
|
+
# excel likes to create random "Data01" sheets for macros
|
91
|
+
# use this to find the sheet with the most data to parse
|
72
92
|
|
73
|
-
|
93
|
+
xls.longest_sheet
|
74
94
|
|
75
|
-
|
95
|
+
# this excel file has multiple worksheets, let's iterate through each of them and process
|
76
96
|
|
77
|
-
|
78
|
-
|
79
|
-
|
97
|
+
xls.each_with_pagename do |name, sheet|
|
98
|
+
p sheet.row(1)
|
99
|
+
end
|
80
100
|
|
81
|
-
|
101
|
+
# pull out a hash of exclusive column data (get rid of useless columns and save memory)
|
82
102
|
|
83
|
-
|
84
|
-
|
103
|
+
xls.each(:id => 'UPC',:qty => 'ATS') {|hash| arr << hash}
|
104
|
+
#=> hash will appear like {:upc=>727880013358, :qty => 12}
|
85
105
|
|
86
|
-
|
106
|
+
# NOTE: .parse does the same as .each, except it returns an array (similar to each vs. map)
|
87
107
|
|
88
|
-
|
89
|
-
|
90
|
-
|
108
|
+
# not sure exactly what a column will be named? try a wildcard search with the character *
|
109
|
+
# regex characters are allowed ('^price\s')
|
110
|
+
# case insensitive
|
91
111
|
|
92
|
-
|
112
|
+
xls.parse(:id => 'UPC*SKU',:qty => 'ATS*\sATP\s*QTY$')
|
93
113
|
|
94
|
-
|
95
|
-
|
114
|
+
# if you need to locate the header row and assign the header names themselves,
|
115
|
+
# use the :header_search option
|
96
116
|
|
97
|
-
|
98
|
-
|
99
|
-
|
117
|
+
xls.parse(:header_search => ['UPC*SKU','ATS*\sATP\s*QTY$'])
|
118
|
+
#=> each element will appear in this fashion:
|
119
|
+
#=> {"UPC" => 123456789012, "STYLE" => "987B0", "COLOR" => "blue", "QTY" => 78}
|
100
120
|
|
101
|
-
|
121
|
+
# want to strip out annoying unicode characters and surrounding white space?
|
102
122
|
|
103
|
-
|
123
|
+
xls.parse(:clean => true)
|
104
124
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
125
|
+
# another bonus feature is a patch to prevent the Spreadsheet gem from parsing
|
126
|
+
# thousands and thousands of blank lines. i got fed up after watching my computer
|
127
|
+
# nearly catch fire for 4 hours for a spreadsheet with only 200 ACTUAL lines
|
128
|
+
# - located in lib/roo/worksheet.rb
|
109
129
|
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ Jeweler::Tasks.new do |gem|
|
|
4
4
|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
5
5
|
gem.name = "roo"
|
6
6
|
gem.summary = "Roo can access the contents of various spreadsheet files."
|
7
|
-
gem.description = "Roo can access the contents of various spreadsheet files. It can handle\n*
|
7
|
+
gem.description = "Roo can access the contents of various spreadsheet files. It can handle\n* OpenOffice\n* Excel\n* Google spreadsheets\n* Excelx\n* LibreOffice\n* CSV"
|
8
8
|
gem.email = "ruby.ruby.ruby.roo@gmail.com"
|
9
9
|
gem.homepage = "http://github.com/Empact/roo"
|
10
10
|
gem.authors = ['Thomas Preymesser', 'Hugh McGowan', 'Ben Woosley']
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.12.0
|
data/examples/roo_soap_server.rb
CHANGED
@@ -5,7 +5,7 @@ NS = "spreadsheetserver" # name of your service = namespace
|
|
5
5
|
class Server2 < SOAP::RPC::StandaloneServer
|
6
6
|
|
7
7
|
def on_init
|
8
|
-
spreadsheet =
|
8
|
+
spreadsheet = OpenOffice.new("./Ferien-de.ods")
|
9
9
|
add_method(spreadsheet, 'cell', 'row', 'col')
|
10
10
|
add_method(spreadsheet, 'officeversion')
|
11
11
|
add_method(spreadsheet, 'first_row')
|
data/lib/roo.rb
CHANGED
@@ -1,16 +1,34 @@
|
|
1
1
|
module Roo
|
2
2
|
|
3
|
-
VERSION = '1.
|
3
|
+
VERSION = '1.12.0'
|
4
4
|
|
5
|
-
|
5
|
+
def self.const_missing(const_name)
|
6
|
+
case const_name
|
7
|
+
when :Libreoffice
|
8
|
+
warn "`Roo::Libreoffice` has been deprecated. Use `Roo::LibreOffice` instead."
|
9
|
+
LibreOffice
|
10
|
+
when :Openoffice
|
11
|
+
warn "`Roo::Openoffice` has been deprecated. Use `Roo::OpenOffice` instead."
|
12
|
+
OpenOffice
|
13
|
+
when :Csv
|
14
|
+
warn "`Roo::Csv` has been deprecated. Use `Roo::CSV` instead."
|
15
|
+
CSV
|
16
|
+
when :GenericSpreadsheet
|
17
|
+
warn "`Roo::GenericSpreadsheet` has been deprecated. Use `Roo::Base` instead."
|
18
|
+
Base
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
6
23
|
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :Excel, 'roo/excel'
|
10
|
-
autoload :Excelx, 'roo/excelx'
|
11
|
-
autoload :Google, 'roo/google'
|
12
|
-
autoload :Csv, 'roo/csv'
|
24
|
+
autoload :Spreadsheet, 'roo/spreadsheet'
|
25
|
+
autoload :Base, 'roo/base'
|
13
26
|
|
14
|
-
autoload :
|
15
|
-
autoload :
|
27
|
+
autoload :OpenOffice, 'roo/openoffice'
|
28
|
+
autoload :LibreOffice, 'roo/openoffice'
|
29
|
+
autoload :Excel, 'roo/excel'
|
30
|
+
autoload :Excelx, 'roo/excelx'
|
31
|
+
autoload :Excel2003XML, 'roo/excel2003xml'
|
32
|
+
autoload :Google, 'roo/google'
|
33
|
+
autoload :CSV, 'roo/csv'
|
16
34
|
end
|
@@ -5,7 +5,7 @@ require 'stringio'
|
|
5
5
|
require 'zip/zipfilesystem'
|
6
6
|
|
7
7
|
# Base class for all other types of spreadsheets
|
8
|
-
class Roo::
|
8
|
+
class Roo::Base
|
9
9
|
include Enumerable
|
10
10
|
|
11
11
|
TEMP_PREFIX = "oo_"
|
@@ -18,7 +18,7 @@ class Roo::GenericSpreadsheet
|
|
18
18
|
protected
|
19
19
|
|
20
20
|
def self.split_coordinate(str)
|
21
|
-
letter,number = Roo::
|
21
|
+
letter,number = Roo::Base.split_coord(str)
|
22
22
|
x = letter_to_number(letter)
|
23
23
|
y = number
|
24
24
|
return y, x
|
@@ -37,9 +37,12 @@ class Roo::GenericSpreadsheet
|
|
37
37
|
|
38
38
|
public
|
39
39
|
|
40
|
-
def initialize(filename,
|
41
|
-
@
|
42
|
-
@
|
40
|
+
def initialize(filename, options={}, file_warning=:error, tmpdir=nil)
|
41
|
+
@filename = filename
|
42
|
+
@options = options
|
43
|
+
|
44
|
+
@cell = {}
|
45
|
+
@cell_type = {}
|
43
46
|
@cells_read = {}
|
44
47
|
|
45
48
|
@first_row = {}
|
@@ -47,12 +50,8 @@ class Roo::GenericSpreadsheet
|
|
47
50
|
@first_column = {}
|
48
51
|
@last_column = {}
|
49
52
|
|
50
|
-
@
|
51
|
-
@style_defaults = Hash.new { |h,k| h[k] = [] }
|
52
|
-
@style_definitions = {}
|
53
|
-
|
53
|
+
@header_line = 1
|
54
54
|
@default_sheet = self.sheets.first
|
55
|
-
@formula = {}
|
56
55
|
@header_line = 1
|
57
56
|
end
|
58
57
|
|
@@ -67,18 +66,18 @@ class Roo::GenericSpreadsheet
|
|
67
66
|
|
68
67
|
# first non-empty column as a letter
|
69
68
|
def first_column_as_letter(sheet=nil)
|
70
|
-
Roo::
|
69
|
+
Roo::Base.number_to_letter(first_column(sheet))
|
71
70
|
end
|
72
71
|
|
73
72
|
# last non-empty column as a letter
|
74
73
|
def last_column_as_letter(sheet=nil)
|
75
|
-
Roo::
|
74
|
+
Roo::Base.number_to_letter(last_column(sheet))
|
76
75
|
end
|
77
76
|
|
78
77
|
# returns the number of the first non-empty row
|
79
78
|
def first_row(sheet=nil)
|
80
79
|
sheet ||= @default_sheet
|
81
|
-
read_cells(sheet)
|
80
|
+
read_cells(sheet)
|
82
81
|
if @first_row[sheet]
|
83
82
|
return @first_row[sheet]
|
84
83
|
end
|
@@ -96,7 +95,7 @@ class Roo::GenericSpreadsheet
|
|
96
95
|
# returns the number of the last non-empty row
|
97
96
|
def last_row(sheet=nil)
|
98
97
|
sheet ||= @default_sheet
|
99
|
-
read_cells(sheet)
|
98
|
+
read_cells(sheet)
|
100
99
|
if @last_row[sheet]
|
101
100
|
return @last_row[sheet]
|
102
101
|
end
|
@@ -114,7 +113,7 @@ class Roo::GenericSpreadsheet
|
|
114
113
|
# returns the number of the first non-empty column
|
115
114
|
def first_column(sheet=nil)
|
116
115
|
sheet ||= @default_sheet
|
117
|
-
read_cells(sheet)
|
116
|
+
read_cells(sheet)
|
118
117
|
if @first_column[sheet]
|
119
118
|
return @first_column[sheet]
|
120
119
|
end
|
@@ -132,7 +131,7 @@ class Roo::GenericSpreadsheet
|
|
132
131
|
# returns the number of the last non-empty column
|
133
132
|
def last_column(sheet=nil)
|
134
133
|
sheet ||= @default_sheet
|
135
|
-
read_cells(sheet)
|
134
|
+
read_cells(sheet)
|
136
135
|
if @last_column[sheet]
|
137
136
|
return @last_column[sheet]
|
138
137
|
end
|
@@ -166,7 +165,7 @@ class Roo::GenericSpreadsheet
|
|
166
165
|
result << " col: #{col} \n"
|
167
166
|
result << " celltype: #{self.celltype(row,col,sheet)} \n"
|
168
167
|
if self.celltype(row,col,sheet) == :time
|
169
|
-
result << " value: #{Roo::
|
168
|
+
result << " value: #{Roo::Base.integer_to_timestring( self.cell(row,col,sheet))} \n"
|
170
169
|
else
|
171
170
|
result << " value: #{self.cell(row,col,sheet)} \n"
|
172
171
|
end
|
@@ -255,7 +254,7 @@ class Roo::GenericSpreadsheet
|
|
255
254
|
# row numbers are 1,2,3,... like in the spreadsheet
|
256
255
|
def row(rownumber,sheet=nil)
|
257
256
|
sheet ||= @default_sheet
|
258
|
-
read_cells(sheet)
|
257
|
+
read_cells(sheet)
|
259
258
|
first_column(sheet).upto(last_column(sheet)).map do |col|
|
260
259
|
cell(rownumber,col,sheet)
|
261
260
|
end
|
@@ -268,7 +267,7 @@ class Roo::GenericSpreadsheet
|
|
268
267
|
columnnumber = Roo::Excel.letter_to_number(columnnumber)
|
269
268
|
end
|
270
269
|
sheet ||= @default_sheet
|
271
|
-
read_cells(sheet)
|
270
|
+
read_cells(sheet)
|
272
271
|
first_row(sheet).upto(last_row(sheet)).map do |row|
|
273
272
|
cell(row,columnnumber,sheet)
|
274
273
|
end
|
@@ -278,7 +277,7 @@ class Roo::GenericSpreadsheet
|
|
278
277
|
# (this will not be saved back to the spreadsheet file!)
|
279
278
|
def set(row,col,value,sheet=nil) #:nodoc:
|
280
279
|
sheet ||= @default_sheet
|
281
|
-
read_cells(sheet)
|
280
|
+
read_cells(sheet)
|
282
281
|
row, col = normalize(row,col)
|
283
282
|
cell_type = case value
|
284
283
|
when Fixnum then :float
|
@@ -293,21 +292,15 @@ class Roo::GenericSpreadsheet
|
|
293
292
|
|
294
293
|
# reopens and read a spreadsheet document
|
295
294
|
def reload
|
296
|
-
# von Abfrage der Klasse direkt auf .to_s == '..' umgestellt
|
297
295
|
ds = @default_sheet
|
298
|
-
|
299
|
-
initialize(@spreadsheetkey,@user,@password)
|
300
|
-
else
|
301
|
-
initialize(@filename)
|
302
|
-
end
|
296
|
+
reinitialize
|
303
297
|
self.default_sheet = ds
|
304
|
-
#@first_row = @last_row = @first_column = @last_column = nil
|
305
298
|
end
|
306
299
|
|
307
300
|
# true if cell is empty
|
308
301
|
def empty?(row, col, sheet=nil)
|
309
302
|
sheet ||= @default_sheet
|
310
|
-
read_cells(sheet)
|
303
|
+
read_cells(sheet)
|
311
304
|
row,col = normalize(row,col)
|
312
305
|
contents = cell(row, col, sheet)
|
313
306
|
!contents || (celltype(row, col, sheet) == :string && contents.empty?) \
|
@@ -329,8 +322,8 @@ class Roo::GenericSpreadsheet
|
|
329
322
|
else
|
330
323
|
result << " First row: #{first_row}\n"
|
331
324
|
result << " Last row: #{last_row}\n"
|
332
|
-
result << " First column: #{Roo::
|
333
|
-
result << " Last column: #{Roo::
|
325
|
+
result << " First column: #{Roo::Base.number_to_letter(first_column)}\n"
|
326
|
+
result << " Last column: #{Roo::Base.number_to_letter(last_column)}"
|
334
327
|
end
|
335
328
|
result << "\n" if sheet != sheets.last
|
336
329
|
n += 1
|
@@ -370,7 +363,7 @@ class Roo::GenericSpreadsheet
|
|
370
363
|
# #aa42 => #cell('aa',42)
|
371
364
|
# #aa42('Sheet1') => #cell('aa',42,'Sheet1')
|
372
365
|
if m =~ /^([a-z]+)(\d)$/
|
373
|
-
col = Roo::
|
366
|
+
col = Roo::Base.letter_to_number($1)
|
374
367
|
row = $2.to_i
|
375
368
|
if args.empty?
|
376
369
|
cell(row,col)
|
@@ -382,148 +375,120 @@ class Roo::GenericSpreadsheet
|
|
382
375
|
end
|
383
376
|
end
|
384
377
|
|
385
|
-
|
386
|
-
#
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
theformulas = Array.new
|
391
|
-
sheet ||= @default_sheet
|
392
|
-
read_cells(sheet) unless @cells_read[sheet]
|
393
|
-
return theformulas unless first_row(sheet) # if there is no first row then
|
394
|
-
# there can't be formulas
|
395
|
-
first_row(sheet).upto(last_row(sheet)) {|row|
|
396
|
-
first_column(sheet).upto(last_column(sheet)) {|col|
|
397
|
-
if formula?(row,col,sheet)
|
398
|
-
theformulas << [row, col, formula(row,col,sheet)]
|
399
|
-
end
|
400
|
-
}
|
401
|
-
}
|
402
|
-
theformulas
|
378
|
+
# access different worksheets by calling spreadsheet.sheet(1)
|
379
|
+
# or spreadsheet.sheet('SHEETNAME')
|
380
|
+
def sheet(index,name=false)
|
381
|
+
@default_sheet = String === index ? index : self.sheets[index]
|
382
|
+
name ? [@default_sheet,self] : self
|
403
383
|
end
|
404
|
-
=end
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
# FestivalBobcats fork changes begin here
|
409
384
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
def sheet(index,name=false)
|
415
|
-
@default_sheet = String === index ? index : self.sheets[index]
|
416
|
-
name ? [@default_sheet,self] : self
|
385
|
+
# iterate through all worksheets of a document
|
386
|
+
def each_with_pagename
|
387
|
+
self.sheets.each do |s|
|
388
|
+
yield sheet(s,true)
|
417
389
|
end
|
390
|
+
end
|
418
391
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
end
|
392
|
+
# by passing in headers as options, this method returns
|
393
|
+
# specific columns from your header assignment
|
394
|
+
# for example:
|
395
|
+
# xls.sheet('New Prices').parse(:upc => 'UPC', :price => 'Price') would return:
|
396
|
+
# [{:upc => 123456789012, :price => 35.42},..]
|
425
397
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
# xls.sheet('New Prices').parse(:upc => 'UPC', :price => 'Price') would return:
|
430
|
-
# [{:upc => 123456789012, :price => 35.42},..]
|
398
|
+
# the queries are matched with regex, so regex options can be passed in
|
399
|
+
# such as :price => '^(Cost|Price)'
|
400
|
+
# case insensitive by default
|
431
401
|
|
432
|
-
# the queries are matched with regex, so regex options can be passed in
|
433
|
-
# such as :price => '^(Cost|Price)'
|
434
|
-
# case insensitive by default
|
435
402
|
|
403
|
+
# by using the :header_search option, you can query for headers
|
404
|
+
# and return a hash of every row with the keys set to the header result
|
405
|
+
# for example:
|
406
|
+
# xls.sheet('New Prices').parse(:header_search => ['UPC*SKU','^Price*\sCost\s'])
|
436
407
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
# xls.sheet('New Prices').parse(:header_search => ['UPC*SKU','^Price*\sCost\s'])
|
408
|
+
# that example searches for a column titled either UPC or SKU and another
|
409
|
+
# column titled either Price or Cost (regex characters allowed)
|
410
|
+
# * is the wildcard character
|
441
411
|
|
442
|
-
|
443
|
-
|
444
|
-
# * is the wildcard character
|
412
|
+
# you can also pass in a :clean => true option to strip the sheet of
|
413
|
+
# odd unicode characters and white spaces around columns
|
445
414
|
|
446
|
-
|
447
|
-
|
415
|
+
def each(options={})
|
416
|
+
if options.empty?
|
417
|
+
1.upto(last_row) do |line|
|
418
|
+
yield row(line)
|
419
|
+
end
|
420
|
+
else
|
421
|
+
if options[:clean]
|
422
|
+
options.delete(:clean)
|
423
|
+
@cleaned ||= {}
|
424
|
+
@cleaned[@default_sheet] || clean_sheet(@default_sheet)
|
425
|
+
end
|
448
426
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
427
|
+
if options[:header_search]
|
428
|
+
@headers = nil
|
429
|
+
@header_line = row_with(options[:header_search])
|
430
|
+
elsif [:first_row,true].include?(options[:headers])
|
431
|
+
@headers = []
|
432
|
+
row(first_row).each_with_index {|x,i| @headers << [x,i + 1]}
|
454
433
|
else
|
455
|
-
|
456
|
-
|
457
|
-
@cleaned ||= {}
|
458
|
-
@cleaned[@default_sheet] || clean_sheet(@default_sheet)
|
459
|
-
end
|
460
|
-
|
461
|
-
if options[:header_search]
|
462
|
-
@headers = nil
|
463
|
-
@header_line = row_with(options[:header_search])
|
464
|
-
elsif [:first_row,true].include?(options[:headers])
|
465
|
-
@headers = []
|
466
|
-
row(first_row).each_with_index {|x,i| @headers << [x,i + 1]}
|
467
|
-
else
|
468
|
-
set_headers(options)
|
469
|
-
end
|
434
|
+
set_headers(options)
|
435
|
+
end
|
470
436
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
437
|
+
headers = @headers ||
|
438
|
+
Hash[(first_column..last_column).map do |col|
|
439
|
+
[cell(@header_line,col), col]
|
440
|
+
end]
|
475
441
|
|
476
|
-
|
477
|
-
|
478
|
-
end
|
442
|
+
@header_line.upto(last_row) do |line|
|
443
|
+
yield(Hash[headers.map {|k,v| [k,cell(line,v)]}])
|
479
444
|
end
|
480
445
|
end
|
446
|
+
end
|
481
447
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
end
|
448
|
+
def parse(options={})
|
449
|
+
ary = []
|
450
|
+
if block_given?
|
451
|
+
each(options) {|row| ary << yield(row)}
|
452
|
+
else
|
453
|
+
each(options) {|row| ary << row}
|
454
|
+
end
|
455
|
+
ary
|
456
|
+
end
|
457
|
+
|
458
|
+
def row_with(query,return_headers=false)
|
459
|
+
query.map! {|x| Array(x.split('*'))}
|
460
|
+
line_no = 0
|
461
|
+
each do |row|
|
462
|
+
line_no += 1
|
463
|
+
# makes sure headers is the first part of wildcard search for priority
|
464
|
+
# ex. if UPC and SKU exist for UPC*SKU search, UPC takes the cake
|
465
|
+
headers = query.map do |q|
|
466
|
+
q.map {|i| row.grep(/#{i}/i)[0]}.compact[0]
|
467
|
+
end.compact
|
468
|
+
|
469
|
+
if headers.length == query.length
|
470
|
+
@header_line = line_no
|
471
|
+
return return_headers ? headers : line_no
|
472
|
+
elsif line_no > 100
|
473
|
+
raise "Couldn't find header row."
|
509
474
|
end
|
510
475
|
end
|
511
|
-
|
512
|
-
# this method lets you find the worksheet with the most data
|
513
|
-
def longest_sheet
|
514
|
-
sheet(@workbook.worksheets.inject {|m,o|
|
515
|
-
o.row_count > m.row_count ? o : m
|
516
|
-
}.name)
|
517
|
-
end
|
476
|
+
end
|
518
477
|
|
519
478
|
protected
|
520
479
|
|
480
|
+
def load_xml(path)
|
481
|
+
File.open(path) do |file|
|
482
|
+
Nokogiri::XML(file)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
521
486
|
def file_type_check(filename, ext, name, warning_level, packed=nil)
|
522
487
|
new_expression = {
|
523
|
-
'.ods' => 'Roo::
|
488
|
+
'.ods' => 'Roo::OpenOffice.new',
|
524
489
|
'.xls' => 'Roo::Excel.new',
|
525
490
|
'.xlsx' => 'Roo::Excelx.new',
|
526
|
-
'.csv' => 'Roo::
|
491
|
+
'.csv' => 'Roo::CSV.new',
|
527
492
|
'.xml' => 'Roo::Excel2003XML.new',
|
528
493
|
}
|
529
494
|
if packed == :zip
|
@@ -574,6 +539,10 @@ class Roo::GenericSpreadsheet
|
|
574
539
|
|
575
540
|
private
|
576
541
|
|
542
|
+
def reinitialize
|
543
|
+
initialize(@filename)
|
544
|
+
end
|
545
|
+
|
577
546
|
def make_tmpdir(tmp_root = nil)
|
578
547
|
Dir.mktmpdir(TEMP_PREFIX, tmp_root || ENV['ROO_TMP']) do |tmpdir|
|
579
548
|
yield tmpdir
|
@@ -581,7 +550,7 @@ class Roo::GenericSpreadsheet
|
|
581
550
|
end
|
582
551
|
|
583
552
|
def clean_sheet(sheet)
|
584
|
-
read_cells(sheet)
|
553
|
+
read_cells(sheet)
|
585
554
|
@cell[sheet].each_pair do |coord,value|
|
586
555
|
if String === value
|
587
556
|
@cell[sheet][coord] = sanitize_value(value)
|
@@ -627,7 +596,7 @@ class Roo::GenericSpreadsheet
|
|
627
596
|
end
|
628
597
|
end
|
629
598
|
if col.class == String
|
630
|
-
col = Roo::
|
599
|
+
col = Roo::Base.letter_to_number(col)
|
631
600
|
end
|
632
601
|
return row,col
|
633
602
|
end
|
@@ -636,21 +605,20 @@ class Roo::GenericSpreadsheet
|
|
636
605
|
filename.start_with?("http://", "https://")
|
637
606
|
end
|
638
607
|
|
639
|
-
def
|
608
|
+
def download_uri(uri, tmpdir)
|
640
609
|
require 'open-uri'
|
610
|
+
tempfilename = File.join(tmpdir, File.basename(uri))
|
641
611
|
response = ''
|
642
612
|
begin
|
643
|
-
open(
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
end
|
649
|
-
}
|
613
|
+
File.open(tempfilename,"wb") do |file|
|
614
|
+
open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") { |net|
|
615
|
+
file.write(net.read)
|
616
|
+
}
|
617
|
+
end
|
650
618
|
rescue OpenURI::HTTPError
|
651
619
|
raise "could not open #{uri}"
|
652
620
|
end
|
653
|
-
|
621
|
+
tempfilename
|
654
622
|
end
|
655
623
|
|
656
624
|
def open_from_stream(stream, tmpdir)
|
@@ -790,7 +758,7 @@ class Roo::GenericSpreadsheet
|
|
790
758
|
when :date, :datetime
|
791
759
|
onecell.to_s
|
792
760
|
when :time
|
793
|
-
Roo::
|
761
|
+
Roo::Base.integer_to_timestring(onecell)
|
794
762
|
else
|
795
763
|
raise "unhandled celltype #{celltype(row,col,sheet)}"
|
796
764
|
end || ""
|