spreadsheet_goodies 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +24 -0
- data/LICENSE.txt +24 -0
- data/README.md +57 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/builds/spreadsheet_goodies-0.0.1.gem +0 -0
- data/lib/spreadsheet_goodies.rb +34 -0
- data/lib/spreadsheet_goodies/excel_workbook_builder.rb +78 -0
- data/lib/spreadsheet_goodies/excel_worksheet_proxy.rb +68 -0
- data/lib/spreadsheet_goodies/excel_worksheet_proxy_row.rb +30 -0
- data/lib/spreadsheet_goodies/google_drive_connector.rb +93 -0
- data/lib/spreadsheet_goodies/google_worksheet_proxy.rb +61 -0
- data/lib/spreadsheet_goodies/google_worksheet_proxy_row.rb +23 -0
- data/lib/spreadsheet_goodies/version.rb +3 -0
- data/spreadsheet_goodies.gemspec +51 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f8a77ef379add514b246cd65ddde8c2263a53b4d
|
4
|
+
data.tar.gz: f0a5cc31a25417c65c3d76b5f64128d561f1ca20
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c8fba74cbcae86da1613a7b6b502e6115784ac6077e44850c18269102639d6152b6245653ab18ff290eba5022b2bd8fbb8ada46ee757370219d36ec2259d262
|
7
|
+
data.tar.gz: d6442b9b72b622f0cadcb51ee67235a3208b8b1910769bd06e277c4692261e5f9eb00423116f948eae229adcc4408964a4375248cc27ebde810b314d6ee17312
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
spreadsheet_goodies
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.3.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org>
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org>
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# SpreadsheetGoodies
|
2
|
+
|
3
|
+
SpreadsheetGoodies is a collection of tools to help work with spreadsheets in
|
4
|
+
Excel and Google Spreadhseets formats. It relies heavily on other gems to make
|
5
|
+
the actual work of reading and writing to spreadsheet documents. It main
|
6
|
+
features are:
|
7
|
+
|
8
|
+
* Read a spreadseet as an array of arrays to allow aceessing its data without
|
9
|
+
using the original document
|
10
|
+
* Access a row's elements using the column titles as keys"
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'spreadsheet_goodies'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install spreadsheet_goodies
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
Read a Google Spreadsheet:
|
31
|
+
```
|
32
|
+
spreadsheet_key = '1UC43X6aZwlWPCnn...'
|
33
|
+
sheet = SpreadsheetGoodies::GoogleWorksheetProxy.new(spreadsheet_key, 'Relação Lojas').read_from_google_drive
|
34
|
+
```
|
35
|
+
|
36
|
+
Read an Excel workbook:
|
37
|
+
```
|
38
|
+
sheet = SpreadsheetGoodies::ExcelWorksheetProxy.new('~/workbook.xlsx')
|
39
|
+
```
|
40
|
+
|
41
|
+
Iterate over every data row (i.e., all but the header row) and print the value
|
42
|
+
of a column titled 'Total':
|
43
|
+
```
|
44
|
+
sheet.data_rows.each do |row|
|
45
|
+
puts "#{row.row_number} -- #{row['Total']}"
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ricardo-jasinski/spreadsheet_goodies.
|
52
|
+
|
53
|
+
|
54
|
+
## License
|
55
|
+
|
56
|
+
The gem is available as open source under the terms of the [Unlicense](http://unlicense.org/UNLICENSE).
|
57
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "spreadsheet_goodies"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
Binary file
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spreadsheet_goodies/version'
|
2
|
+
require 'spreadsheet_goodies/google_worksheet_proxy'
|
3
|
+
require 'spreadsheet_goodies/google_drive_connector'
|
4
|
+
require 'spreadsheet_goodies/excel_worksheet_proxy'
|
5
|
+
require 'roo'
|
6
|
+
|
7
|
+
module SpreadsheetGoodies
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configuration
|
14
|
+
@configuration ||= Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset
|
18
|
+
@configuration = Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configure
|
22
|
+
yield(configuration)
|
23
|
+
end
|
24
|
+
|
25
|
+
class Configuration
|
26
|
+
attr_accessor :service_account_key_json, :login_method,
|
27
|
+
:google_client_id, :client_secret, :refresh_token
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@login_method = :oauth2
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'axlsx'
|
2
|
+
|
3
|
+
# A set of methods to help create and format Excel workbooks
|
4
|
+
class SpreadsheetGoodies::ExcelWorkbookBuilder
|
5
|
+
attr_reader :current_sheet
|
6
|
+
|
7
|
+
def initialize(output_file_pathname)
|
8
|
+
@output_file_pathname = output_file_pathname
|
9
|
+
@axlsx_package = Axlsx::Package.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# The underlying Axlsx::Workbook object
|
13
|
+
def workbook
|
14
|
+
@axlsx_package.workbook
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_worksheet(sheet_name)
|
18
|
+
@current_sheet = @workbook.add_worksheet(name: sheet_name)
|
19
|
+
setup_current_sheet_styles
|
20
|
+
freeze_top_row
|
21
|
+
@current_sheet
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a row to the current sheet using the data row style
|
25
|
+
def add_data_row(row_values)
|
26
|
+
@current_sheet.add_row(row_values, style: @data_row_style)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add a row to the current sheet using the header row style
|
30
|
+
def add_header_row(row_values)
|
31
|
+
@current_sheet.add_row(row_values, style: @header_row_style)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add filter and sorting controls to a sheet.
|
35
|
+
def setup_auto_filter(sheet=nil)
|
36
|
+
worksheet = sheet || @current_sheet
|
37
|
+
top_left_cell_label = worksheet.dimension.first_cell_reference
|
38
|
+
bottom_right_cell_label = worksheet.dimension.last_cell_reference
|
39
|
+
filter_range = "#{top_left_cell_label}:#{bottom_right_cell_label}"
|
40
|
+
worksheet.auto_filter = filter_range
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_to_file
|
44
|
+
@axlsx_package.serialize(@output_file_pathname)
|
45
|
+
end
|
46
|
+
|
47
|
+
def freeze_top_row
|
48
|
+
@current_sheet.sheet_view.pane do |pane|
|
49
|
+
pane.top_left_cell = 'A2'
|
50
|
+
pane.state = :frozen_split
|
51
|
+
pane.y_split = 1
|
52
|
+
pane.active_pane = :bottom_right
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def setup_current_sheet_styles
|
59
|
+
@header_row_style = @current_sheet.styles.add_style(
|
60
|
+
alignment: {horizontal: :center, vertical: :center},
|
61
|
+
font_name: 'Calibri',
|
62
|
+
bg_color: 'FFDDDDDD',
|
63
|
+
b: true,
|
64
|
+
)
|
65
|
+
|
66
|
+
@header_row_sodexo_style = @current_sheet.styles.add_style(
|
67
|
+
alignment: {horizontal: :center, vertical: :center},
|
68
|
+
font_name: 'Calibri',
|
69
|
+
bg_color: '002060',
|
70
|
+
fg_color: 'FFFFFF',
|
71
|
+
b: true,
|
72
|
+
)
|
73
|
+
|
74
|
+
@data_row_style = @current_sheet.styles.add_style(
|
75
|
+
font_name: 'Calibri',
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'excel_worksheet_proxy_row'
|
2
|
+
|
3
|
+
# A cached copy of an Excel worksheet (a single workbook "tab"), stored as an
|
4
|
+
# array of arrays. Individual cells can be accessed by column title, e.g.:
|
5
|
+
# worksheet[0]['A column title']
|
6
|
+
class SpreadsheetGoodies::ExcelWorksheetProxy
|
7
|
+
attr_reader :rows, :workbook, :worksheet
|
8
|
+
|
9
|
+
# @param workbook_file_pathname [String] Full path and filename to Excel workbook document
|
10
|
+
# @param worksheet_title_or_index [String|Integer] Sheet name or index (zero-based)
|
11
|
+
# within the workbook. Optional; if unspecified, the first sheet will be used.
|
12
|
+
# @param num_header_rows [Integer] Number of rows at the top of the sheet that
|
13
|
+
# contain headers or stuff other than data. Optional; if unspecified, assumes
|
14
|
+
# that a single top row contains the header and all rows below are data.
|
15
|
+
def initialize(workbook_file_pathname, worksheet_title_or_index=0, num_header_rows=1)
|
16
|
+
@worksheet_title = worksheet_title_or_index
|
17
|
+
@num_header_rows = num_header_rows
|
18
|
+
|
19
|
+
@workbook = case workbook_file_pathname.to_s
|
20
|
+
when /\.xls[^x]/ then Roo::Excel.new(workbook_file_pathname, file_warning: :ignore)
|
21
|
+
when /\.xlsx/ then Roo::Excelx.new(workbook_file_pathname, file_warning: :ignore)
|
22
|
+
end
|
23
|
+
|
24
|
+
@worksheet = @workbook.sheet(worksheet_title_or_index)
|
25
|
+
|
26
|
+
@header_row = @worksheet.row(num_header_rows)
|
27
|
+
|
28
|
+
# Reads all the worksheet rows, one at a time, using Workshete#row (note: reading
|
29
|
+
# them all at once using Worksheet#parse didn't work because the first row
|
30
|
+
# was missed some times.)
|
31
|
+
rows = (1..@worksheet.last_row).map {|row_number| @worksheet.row(row_number) }
|
32
|
+
|
33
|
+
# Create the rows cache, made of instances of ExcelWorksheetProxyRow
|
34
|
+
@rows = rows.collect.with_index do |row, index|
|
35
|
+
ExcelWorksheetProxyRow.new(@header_row, index+1, *row)
|
36
|
+
end
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](index)
|
42
|
+
@rows[index]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return only the rows that contain data (excludes the header rows)
|
46
|
+
def data_rows
|
47
|
+
@rows[@num_header_rows..-1]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Finds and returns the first row that contains cell_value at the given column
|
51
|
+
def find_row_by_column_value(column_title, cell_value)
|
52
|
+
data_rows.each do |row|
|
53
|
+
return row if row[column_title] == cell_value
|
54
|
+
end
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_row(row_data)
|
60
|
+
last_row_number = @rows.count
|
61
|
+
@rows << ExcelWorksheetProxyRow.new(@header_row, last_row_number+1, *row_data)
|
62
|
+
end
|
63
|
+
|
64
|
+
def spreadsheet
|
65
|
+
@workbook
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class SpreadsheetGoodies::ExcelWorksheetProxy
|
2
|
+
|
3
|
+
# Overload Array#[] to allow reading a cell's contents using its column title
|
4
|
+
# as index, e.g.: row['Column Title']
|
5
|
+
class ::ExcelWorksheetProxyRow < Array
|
6
|
+
attr_reader :header_row, :row_number
|
7
|
+
|
8
|
+
# @param header_row [Array] The header cells of the sheet from were the row was taken
|
9
|
+
# @param sheet_row_number [Integer] The original row number of the row in the
|
10
|
+
# sheet from where the row was taken. Row numebrs follow the spreadshet convention,
|
11
|
+
# i.e., starting from 1.
|
12
|
+
def initialize(header_row, sheet_row_number, *args)
|
13
|
+
@header_row = header_row
|
14
|
+
@row_number = sheet_row_number
|
15
|
+
super(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param locator [Integer|String] The index of a row element. Can be a string
|
19
|
+
# (a column title) or an integer (starting at 0 for the first cell).
|
20
|
+
# @return [String]
|
21
|
+
def [](locator)
|
22
|
+
if locator.is_a?(String)
|
23
|
+
return self[ @header_row.index(locator) ]
|
24
|
+
else
|
25
|
+
return super(locator)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'google_drive'
|
2
|
+
require 'googleauth'
|
3
|
+
|
4
|
+
class SpreadsheetGoodies::GoogleDriveConnector
|
5
|
+
attr_reader :logger, :session
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@logger = Logger.new(STDOUT)
|
9
|
+
@session = log_into_google_drive
|
10
|
+
raise 'Session not found!' unless @session
|
11
|
+
end
|
12
|
+
|
13
|
+
def log_into_google_drive
|
14
|
+
case SpreadsheetGoodies.configuration.login_method
|
15
|
+
when :service_account then log_into_google_drive_via_service_account
|
16
|
+
when :oauth2 then log_into_google_drive_via_oauth2
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_worksheet(spreadsheet_key, worksheet_title=nil)
|
21
|
+
puts "Reading sheet '#{worksheet_title}' from Google Drive..."
|
22
|
+
spreadsheet = @session.spreadsheet_by_key(spreadsheet_key)
|
23
|
+
|
24
|
+
if worksheet_title
|
25
|
+
worksheet = spreadsheet.worksheet_by_title(worksheet_title)
|
26
|
+
if worksheet.nil?
|
27
|
+
raise "Worksheet named '#{worksheet_title}' not found in spreadsheet."
|
28
|
+
end
|
29
|
+
else
|
30
|
+
worksheet = spreadsheet.worksheets.first
|
31
|
+
end
|
32
|
+
|
33
|
+
worksheet
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_worksheet_as_string(spreadsheet_key, worksheet_title)
|
37
|
+
worksheet = read_worksheet(spreadsheet_key, worksheet_title)
|
38
|
+
contents = worksheet.export_as_string.force_encoding('utf-8')
|
39
|
+
puts 'Spreadhseet read successfully.'
|
40
|
+
contents
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Authenticate with Google Drive via OAuth2.
|
46
|
+
# Your OAuth2 ids can be accessed at https://console.developers.google.com/apis/credentials,
|
47
|
+
# logged with your Google account in your custom project. The keys are at
|
48
|
+
# 'API manager' > 'Credentials'. CLIENT_ID and CLIENT_SECRET are available
|
49
|
+
# from the credentials page. The REFRESH_TOKEN must be obtained uncommenting
|
50
|
+
# some rows from the method below and obtaining an access_token via a browser
|
51
|
+
# logged in to your google account.
|
52
|
+
def log_into_google_drive_via_oauth2
|
53
|
+
credentials = Google::Auth::UserRefreshCredentials.new(
|
54
|
+
client_id: SpreadsheetGoodies.configuration.google_client_id,
|
55
|
+
client_secret: SpreadsheetGoodies.configuration.client_secret,
|
56
|
+
scope: [
|
57
|
+
'https://www.googleapis.com/auth/drive',
|
58
|
+
'https://spreadsheets.google.com/feeds/',
|
59
|
+
],
|
60
|
+
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob'
|
61
|
+
)
|
62
|
+
credentials.refresh_token = SpreadsheetGoodies.configuration.refresh_token if SpreadsheetGoodies.configuration.refresh_token
|
63
|
+
|
64
|
+
# Uncomment the rows below to obtain the refresh_token:
|
65
|
+
# print("1. Open this page:\n%s\n\n" % credentials.authorization_uri)
|
66
|
+
# print("2. Enter the authorization code shown in the page: ")
|
67
|
+
# credentials.code = $stdin.gets.chomp
|
68
|
+
# credentials.fetch_access_token!
|
69
|
+
# puts credentials.refresh_token
|
70
|
+
# debugger
|
71
|
+
|
72
|
+
begin
|
73
|
+
credentials.fetch_access_token!
|
74
|
+
rescue Faraday::ConnectionFailed
|
75
|
+
logger.info 'Error logging into Google Drive. Is your internet connection down?'
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
|
79
|
+
session = GoogleDrive::Session.from_credentials(credentials)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Authenticate with Google Drive via a service account.
|
83
|
+
# Your service accounts can be accessed at https://console.developers.google.com/apis/credentials,
|
84
|
+
# logged with your Google account in your custom project.
|
85
|
+
# The service accounts are at 'IAM and administrator' > 'Service accounts'.
|
86
|
+
# The keys are at 'API manager' > 'Credentials'.
|
87
|
+
def log_into_google_drive_via_service_account
|
88
|
+
session = GoogleDrive::Session.from_service_account_key(
|
89
|
+
StringIO.new(SpreadsheetGoodies.configuration.service_account_key_json)
|
90
|
+
)
|
91
|
+
session
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'google_worksheet_proxy_row'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
# A cached copy of a Google Spreadsheets worksheet (i.e., a single workbook "tab"),
|
5
|
+
# stored as an array of arrays. Individual cells can be accessed by column title.
|
6
|
+
# Example:
|
7
|
+
# worksheet[0]['A column title']
|
8
|
+
class SpreadsheetGoodies::GoogleWorksheetProxy
|
9
|
+
attr_reader :rows, :header_row
|
10
|
+
|
11
|
+
# @param worksheet_title_or_index [String] Sheet name; if unspecified, the
|
12
|
+
# first sheet will be used.
|
13
|
+
# @param num_header_rows [Integer] Number of rows at the top of the sheet that
|
14
|
+
# contain headers or stuff other than data. Optional; if unspecified, assumes
|
15
|
+
# that a single top row contains the header and all rows below are data.
|
16
|
+
def initialize(spreadsheet_key, worksheet_title=nil, num_header_rows=1)
|
17
|
+
@spreadsheet_key = spreadsheet_key
|
18
|
+
@worksheet_title = worksheet_title
|
19
|
+
@num_header_rows = num_header_rows
|
20
|
+
end
|
21
|
+
|
22
|
+
# Reads sheet contents and caches it into instance variables to so that it
|
23
|
+
# can be accessed later without accessing Google Drive.
|
24
|
+
def read_from_google_drive
|
25
|
+
worksheet_contents = read_worksheet.export_as_string.force_encoding('utf-8')
|
26
|
+
|
27
|
+
rows = CSV::parse(worksheet_contents)
|
28
|
+
@header_row = rows[@num_header_rows-1]
|
29
|
+
@rows = rows.collect.with_index do |row, index|
|
30
|
+
GoogleWorksheetProxyRow.new(@header_row, index+1, *row)
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](index)
|
37
|
+
@rows[index]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return only the rows that contain data (excludes the header rows)
|
41
|
+
def data_rows
|
42
|
+
@rows[@num_header_rows..-1]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Finds and returns the first row that contains cell_value at the given column
|
46
|
+
def find_row_by_column_value(column_title, cell_value)
|
47
|
+
data_rows.each do |row|
|
48
|
+
return row if row[column_title] == cell_value
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Reads a GoogleDrive::Spreadsheet object from Google Drive. The sheet object
|
55
|
+
# can be used to write data to the online spreadhseet, as we don't yet provide
|
56
|
+
# the helper methods for our users to do it via our public interface.
|
57
|
+
def read_worksheet
|
58
|
+
connector = SpreadsheetGoodies::GoogleDriveConnector.new
|
59
|
+
connector.read_worksheet(@spreadsheet_key, @worksheet_title)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class SpreadsheetGoodies::GoogleWorksheetProxy
|
2
|
+
|
3
|
+
# Sobrecarrega método [] da Array para permitir acessar células passando
|
4
|
+
# o título da coluna como índice. Ex: row['Data formal do pedido']
|
5
|
+
class ::GoogleWorksheetProxyRow < Array
|
6
|
+
attr_reader :header_row, :row_number
|
7
|
+
|
8
|
+
def initialize(header_row, sheet_row_number, *args)
|
9
|
+
@header_row = header_row
|
10
|
+
@row_number = sheet_row_number
|
11
|
+
super(args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](locator)
|
15
|
+
if locator.is_a?(String)
|
16
|
+
return self[ @header_row.index(locator) ]
|
17
|
+
else
|
18
|
+
return super(locator)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'spreadsheet_goodies/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gemspec|
|
7
|
+
gemspec.name = 'spreadsheet_goodies'
|
8
|
+
gemspec.version = SpreadsheetGoodies::VERSION
|
9
|
+
gemspec.authors = ['Ricardo Jasinski']
|
10
|
+
gemspec.email = ['jasinski@solvis.com.br']
|
11
|
+
|
12
|
+
gemspec.summary = "SpreadsheetGoodies is a collection of tools to help work " +
|
13
|
+
"with Excel and Google Drive spreadhseets."
|
14
|
+
gemspec.description = "SpreadsheetGoodies is a collection of tools to help work " +
|
15
|
+
"with Excel and Google Drive spreadhseets. It relies " +
|
16
|
+
"on other gems to do the actual work of reading and writing to " +
|
17
|
+
"spreadsheet documents. It main features are:"
|
18
|
+
" * Read a spreadseet to an array of arrays, to allow accessing its data " +
|
19
|
+
" without using the original document"
|
20
|
+
" * Access a row's elements using the column titles as keys"
|
21
|
+
gemspec.homepage = 'https://github.com/ricardo-jasinski/spreadsheet_goodies'
|
22
|
+
gemspec.license = 'Unlicense'
|
23
|
+
|
24
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
25
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
26
|
+
if gemspec.respond_to?(:metadata)
|
27
|
+
gemspec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
28
|
+
else
|
29
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
30
|
+
'public gem pushes.'
|
31
|
+
end
|
32
|
+
|
33
|
+
gemspec.files = `git ls-files -z`.split("\x0").reject do |f|
|
34
|
+
f.match(%r{^(test|spec|features)/})
|
35
|
+
end
|
36
|
+
gemspec.bindir = 'exe'
|
37
|
+
gemspec.executables = gemspec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
38
|
+
gemspec.require_paths = ['lib']
|
39
|
+
|
40
|
+
gemspec.add_development_dependency 'bundler', '~> 1.14'
|
41
|
+
gemspec.add_development_dependency 'rake', '~> 10.0'
|
42
|
+
gemspec.add_development_dependency 'rspec', '~> 3.0'
|
43
|
+
|
44
|
+
# gemspec.add_dependency 'zip-zip'#, '~> 0'
|
45
|
+
gemspec.add_dependency 'axlsx', '~> 0'
|
46
|
+
gemspec.add_dependency 'google_drive', '~> 0'
|
47
|
+
gemspec.add_dependency 'csv', '~> 0'
|
48
|
+
gemspec.add_dependency 'roo', '~> 0'#, '~> 1.13.2'
|
49
|
+
# gemspec.add_dependency 'roo-xls', '~> 1.2.0'
|
50
|
+
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spreadsheet_goodies
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ricardo Jasinski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: axlsx
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: google_drive
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: csv
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: roo
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: 'SpreadsheetGoodies is a collection of tools to help work with Excel
|
112
|
+
and Google Drive spreadhseets. It relies on other gems to do the actual work of
|
113
|
+
reading and writing to spreadsheet documents. It main features are:'
|
114
|
+
email:
|
115
|
+
- jasinski@solvis.com.br
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- ".rspec"
|
122
|
+
- ".ruby-gemset"
|
123
|
+
- ".ruby-version"
|
124
|
+
- ".travis.yml"
|
125
|
+
- Gemfile
|
126
|
+
- LICENSE
|
127
|
+
- LICENSE.txt
|
128
|
+
- README.md
|
129
|
+
- Rakefile
|
130
|
+
- bin/console
|
131
|
+
- bin/setup
|
132
|
+
- builds/spreadsheet_goodies-0.0.1.gem
|
133
|
+
- lib/spreadsheet_goodies.rb
|
134
|
+
- lib/spreadsheet_goodies/excel_workbook_builder.rb
|
135
|
+
- lib/spreadsheet_goodies/excel_worksheet_proxy.rb
|
136
|
+
- lib/spreadsheet_goodies/excel_worksheet_proxy_row.rb
|
137
|
+
- lib/spreadsheet_goodies/google_drive_connector.rb
|
138
|
+
- lib/spreadsheet_goodies/google_worksheet_proxy.rb
|
139
|
+
- lib/spreadsheet_goodies/google_worksheet_proxy_row.rb
|
140
|
+
- lib/spreadsheet_goodies/version.rb
|
141
|
+
- spreadsheet_goodies.gemspec
|
142
|
+
homepage: https://github.com/ricardo-jasinski/spreadsheet_goodies
|
143
|
+
licenses:
|
144
|
+
- Unlicense
|
145
|
+
metadata:
|
146
|
+
allowed_push_host: https://rubygems.org
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 2.6.6
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: SpreadsheetGoodies is a collection of tools to help work with Excel and Google
|
167
|
+
Drive spreadhseets.
|
168
|
+
test_files: []
|