roo 1.11.2 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 || ""
|