google_docs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e938ee8f78128013178af8e98317c6e549d020b3
4
+ data.tar.gz: 5c9cb8b317f216569b5fff1186005cbf2367d0ee
5
+ SHA512:
6
+ metadata.gz: e58009e7812d392b788e08a6ca49da6af122286750bd03c54d63bac7c20f1b98bbad281fb1988ca5bf1908974e887fae688843f42d8005e7419bff5668d78257
7
+ data.tar.gz: 4f502985f94c912c7b26de553afd5dfce7436bb18cefbb6792b7f2df4bd5dad601e350333f94335d90d2f289f0cffe0ec577ffa64fe3c0ea544806aa2350482f
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at ifuelen@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+ gem 'google-api-client'
5
+ gem 'pry-nav', group: [:development, :test]
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ google_docs (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.5.0)
10
+ public_suffix (~> 2.0, >= 2.0.2)
11
+ coderay (1.1.2)
12
+ diff-lcs (1.3)
13
+ faraday (0.11.0)
14
+ multipart-post (>= 1.2, < 3)
15
+ google-api-client (0.9.28)
16
+ addressable (~> 2.3)
17
+ googleauth (~> 0.5)
18
+ httpclient (~> 2.7)
19
+ hurley (~> 0.1)
20
+ memoist (~> 0.11)
21
+ mime-types (>= 1.6)
22
+ representable (~> 2.3.0)
23
+ retriable (~> 2.0)
24
+ googleauth (0.5.1)
25
+ faraday (~> 0.9)
26
+ jwt (~> 1.4)
27
+ logging (~> 2.0)
28
+ memoist (~> 0.12)
29
+ multi_json (~> 1.11)
30
+ os (~> 0.9)
31
+ signet (~> 0.7)
32
+ httpclient (2.8.3)
33
+ hurley (0.2)
34
+ jwt (1.5.6)
35
+ little-plugger (1.1.4)
36
+ logging (2.1.0)
37
+ little-plugger (~> 1.1)
38
+ multi_json (~> 1.10)
39
+ memoist (0.15.0)
40
+ method_source (0.8.2)
41
+ mime-types (3.1)
42
+ mime-types-data (~> 3.2015)
43
+ mime-types-data (3.2016.0521)
44
+ multi_json (1.12.1)
45
+ multipart-post (2.0.0)
46
+ os (0.9.6)
47
+ pry (0.10.4)
48
+ coderay (~> 1.1.0)
49
+ method_source (~> 0.8.1)
50
+ slop (~> 3.4)
51
+ pry-nav (0.2.4)
52
+ pry (>= 0.9.10, < 0.11.0)
53
+ public_suffix (2.0.5)
54
+ rake (10.5.0)
55
+ representable (2.3.0)
56
+ uber (~> 0.0.7)
57
+ retriable (2.1.0)
58
+ rspec (3.5.0)
59
+ rspec-core (~> 3.5.0)
60
+ rspec-expectations (~> 3.5.0)
61
+ rspec-mocks (~> 3.5.0)
62
+ rspec-core (3.5.4)
63
+ rspec-support (~> 3.5.0)
64
+ rspec-expectations (3.5.0)
65
+ diff-lcs (>= 1.2.0, < 2.0)
66
+ rspec-support (~> 3.5.0)
67
+ rspec-mocks (3.5.0)
68
+ diff-lcs (>= 1.2.0, < 2.0)
69
+ rspec-support (~> 3.5.0)
70
+ rspec-support (3.5.0)
71
+ signet (0.7.3)
72
+ addressable (~> 2.3)
73
+ faraday (~> 0.9)
74
+ jwt (~> 1.5)
75
+ multi_json (~> 1.10)
76
+ slop (3.6.0)
77
+ uber (0.0.15)
78
+
79
+ PLATFORMS
80
+ ruby
81
+
82
+ DEPENDENCIES
83
+ bundler (~> 1.14)
84
+ google-api-client
85
+ google_docs!
86
+ pry-nav
87
+ rake (~> 10.0)
88
+ rspec (~> 3.5)
89
+
90
+ BUNDLED WITH
91
+ 1.16.0.pre.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 JetRuby Agency
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,257 @@
1
+ # Google Docs
2
+
3
+ `google_docs` is a wrapper around [google-api-ruby-client](https://github.com/google/google-api-ruby-client).
4
+ It allows you to get information about the spreadsheet and format it.
5
+ Currently, gem consists of two classes `GoogleDocs::SpreadSheet` and `GoogleDocs::Sheet` and uses `Sheets API V4`.
6
+
7
+ Main features:
8
+ - Set up columns width
9
+ - Update grid properties of the sheet (freeze columns/rows)
10
+ - Update and format cells
11
+ - Merge/unmerge cells in the given range
12
+ - Export google sheet documents as PDF
13
+ - Read information from certain sheet
14
+
15
+ ### Pre requirement
16
+ In order to use our gem your application should have configured [Google OmniAuth](https://github.com/google/google-api-ruby-client#authorization) already. Also you should configure [Google Sheet API](https://developers.google.com/sheets/api/quickstart/ruby)
17
+
18
+ ### Installation
19
+ You can add it to your Gemfile with:
20
+ ```sh
21
+ gem 'google_docs'
22
+ ```
23
+ Then run `bundle install`
24
+
25
+ ### Getting started
26
+ Initialize new object of `GoogleDocs::SpreadSheet` class
27
+ ```sh
28
+ spreadsheet = GoogleDocs::SpreadSheet.new('some_google_access_token', 'some_spreadsheet_id')
29
+ ```
30
+ After successfull intialization we can call the following methods:
31
+ * `spreadsheet.properties` returns properties of the spreadsheet
32
+ * `spreadsheet.sheets` get all sheets of the spreadsheet.
33
+
34
+ If you want to get sheet directly by key:
35
+ ```sh
36
+ sheet = GoogleDocs::Sheet.new(spreadsheet_id: required, sheet_id: required, google_access_token: required)
37
+ ```
38
+ By default, after object initialization there are no modification request to be executed.
39
+ You should prepare requests and only then call `sheet.apply_changes!` to send requests.
40
+ To add requests you can run following methods:
41
+
42
+ * `sheet.setup_columns_width(data)` - build request to set up columns width. Argument `data` should have the following format:
43
+ ```
44
+ {
45
+ 'A' => integer,
46
+ 'D' => integer,
47
+ ...
48
+ }
49
+ ```
50
+ The keys of hash are the columns letters and values of hash - width in pixels for these names in sheet respectively.
51
+ For instance, suppose, you have the following sheet: ![sheet_without_width](/samples/google_sheet_without_width.png)
52
+ and our data are the following:
53
+ ```
54
+ {
55
+ 'A' => 50,
56
+ 'B' => 150,
57
+ 'C' => 150,
58
+ 'D' => 100,
59
+ 'E' => 50,
60
+ 'F' => 100,
61
+ 'G' => 700
62
+ }
63
+ ```
64
+ After our method have been executed we receive:
65
+ ![sheet_with_width](/samples/google_sheet_set_width.png)
66
+
67
+
68
+ * `sheet.update_grid_properties(props)` - build request to update properties of the sheet, i.e. to freeze rows and columns while user is scrolling document. Argument `props` must have the following format:
69
+
70
+ ```
71
+ {
72
+ row_count: integer,
73
+ column_count: integer,
74
+ frozen_row_count: integer,
75
+ frozen_column_count: integer,
76
+ hide_gridlines: boolean
77
+ }
78
+ ```
79
+
80
+ For instance, suppose, you have the following data:
81
+ ```
82
+ props = {
83
+ row_count: 7,
84
+ column_count: 7,
85
+ frozen_row_count: 2,
86
+ frozen_column_count: 1,
87
+ hide_gridlines: true
88
+ }
89
+ ```
90
+ After our method have been executed we receive:
91
+ ![update_grid_properties](/samples/google_sheet_update_grid_properties.png)
92
+
93
+
94
+
95
+ * `sheet.update_cells(data)` - build request to update all cells in a range with new data.
96
+ Argument `data` is an array with rows. Each row is an array of cells. Cell can be a String, Number, Boolean or Hash.
97
+ When the value of cell starts from '=' it will be sent as formula.
98
+ When cell is a Hash it can contain additional formatting options.
99
+ Schema of the `data` attribute:
100
+ ```
101
+ [
102
+ [
103
+ {
104
+ value: 'any cell value'
105
+ borders: {
106
+ left, right, top, bottom: {
107
+ style: :dotted, :dashed, :solid, :double
108
+ width: 1..3
109
+ color: '#RRGGBB'
110
+ }
111
+ }
112
+ background_color: '#RRGGBB'
113
+ horizontal_alignment: :left, :center, :right
114
+ vertical_alignment: :top, :middle, :bottom
115
+ hyperlink_display_type: :linked, :plain_text
116
+ wrap_strategy: :overflow_cell, :legacy_wrap, :clip, :wrap
117
+ text_direction: :left_to_right, :right_to_left
118
+ number_format: {
119
+ type: :text, :number, :percent, :currency, :date, :time, :datetime, :scientific
120
+ pattern: "#,##0.0000"
121
+ }
122
+ text_format: {
123
+ foreground_color: '#RRGGBB'
124
+ font_family: 'Terminus'
125
+ font_size: integer
126
+ bold: boolean
127
+ italic: boolean
128
+ strikethrough: boolean
129
+ underline: boolean
130
+ }
131
+ },
132
+ 'string cell',
133
+ number_cell
134
+ ],
135
+ # new row
136
+ [ {cell 1}, {cell 2}, ... {cell n} ]
137
+ ]
138
+ ```
139
+
140
+ For instance, data could look like:
141
+
142
+ ```
143
+ data = [
144
+ [{ value: 'books', background_color: '#f1c232', text_format: { bold: true }, horizontal_alignment: :left,
145
+ borders: { bottom: { style: :solid, width: 3, color: '#RRGGBB' },
146
+ right: { style: :solid, width: 3, color: '#RRGGBB' } } }
147
+ ],
148
+ [
149
+ { value: 'id', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :right },
150
+ { value: 'author', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :center },
151
+ { value: 'title', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :center },
152
+ { value: 'genre', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :center },
153
+ { value: 'price', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :center },
154
+ { value: 'publish_date', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :center },
155
+ { value: 'description', background_color: '#b7b7b7', text_format: { bold: true }, horizontal_alignment: :center }
156
+ ],
157
+ [
158
+ { value: 'bk101', text_format: { foreground_color: '#0143DF' }, horizontal_alignment: :right },
159
+ { value: 'Gambardella, Matthew', text_format: { italic: true }, horizontal_alignment: :center },
160
+ { value: "XML Developer's Guide", text_format: { underline: true }, number_format: { type: :text }, horizontal_alignment: :center },
161
+ { value: 'Computer', text_format: { bold: true }, horizontal_alignment: :center },
162
+ { value: 44.95, number_format: { type: :currency }, text_format: { strikethrough: true, foreground_color: '#FF0000' }, horizontal_alignment: :center },
163
+ { value: '10/01/2000', number_format: { type: :date}, horizontal_alignment: :center },
164
+ { value: 'An in-depth look at creating applications', text_format: { font_family: 'Courier New' }, horizontal_alignment: :left },
165
+ ],
166
+ [
167
+ { value: 'bk102', text_format: { foreground_color: '#0143DF' }, horizontal_alignment: :right },
168
+ { value: 'Ralls, Kim', text_format: { italic: true }, horizontal_alignment: :center },
169
+ { value: "Midnight Rain", text_format: { underline: true }, number_format: { type: :text }, horizontal_alignment: :center },
170
+ { value: 'Fantasy', text_format: { bold: true }, horizontal_alignment: :center },
171
+ { value: 10.99, number_format: { type: :currency }, horizontal_alignment: :center },
172
+ { value: '16/12/2000', number_format: { type: :date}, horizontal_alignment: :center },
173
+ { value: 'A former architect battles corporate zombies, an evil sorceress', text_format: { font_family: 'Courier New' }, horizontal_alignment: :left },
174
+ ]
175
+ ]
176
+ ```
177
+ After our method have been executed we receive:
178
+ ![update_cells](/samples/google_sheet_update_cells.png)
179
+
180
+
181
+ * `sheet.merge_cells(props)` - build request to merge cells in the given range. Index count starts from 0. Argument `props` has the following format:
182
+
183
+ ```
184
+ {
185
+ type: :merge_all, :merge_columns, :merge_rows
186
+ range: {
187
+ start_row_index: integer,
188
+ end_row_index: integer,
189
+ start_column_index: integer,
190
+ end_column_index: integer
191
+ }
192
+ }
193
+ ```
194
+ For instance,
195
+ ```
196
+ props = {
197
+ type: :merge_rows,
198
+ range: {
199
+ start_row_index: 0,
200
+ end_row_index: 1,
201
+ start_column_index: 0,
202
+ end_column_index: 7
203
+ }
204
+ }
205
+ ```
206
+ After our method have been executed we receive:
207
+ ![merge_cells](/samples/google_sheet_merge_cells.png)
208
+
209
+ * `sheet.unmerge_cells(range)` - build request to unmerge cells in the given range, where argument `range` has the following format:
210
+
211
+ ```
212
+ {
213
+ start_row_index: integer,
214
+ end_row_index: integer,
215
+ start_column_index: integer,
216
+ end_column_index: integer
217
+ }
218
+ ```
219
+ * `sheet.apply_changes!` - execute all reguests.
220
+
221
+
222
+ * `sheet.get_rows_values` - read information from each row in the sheet. Output example:
223
+ ```
224
+ [
225
+ ["books"],
226
+ ["id", "author", "title", "genre", "price", "publish_date", "description"],
227
+ ["bk101", "Gambardella, Matthew", "XML Developer's Guide", "Computer", "44.95", "2000-10-01", "An in-depth look at creating applications"],
228
+ ["bk102", "Ralls, Kim", "Midnight Rain", "Fantasy", "5.95", "2000-12-16", "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world"],
229
+ ["bk103", "Corets, Eva", "Maeve Ascendant", "Fantasy", "5.95", "2000-11-17", "After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society"],
230
+ ["bk104", "Thurman, Paula", "Splish Splash", "Romance", "4.95", "2000-11-02", "A deep sea diver finds true love twenty thousand leagues beneath the sea"],
231
+ ["bk105", "Kress, Peter", "Paradox Lost", "Science Fiction", "6.95", "2000-11-02", "After an inadvertant trip through a Heisenberg Uncertainty Device, James Salway discovers the problems of being quantum"]
232
+ ]
233
+ ```
234
+
235
+ * `sheet.download_pdf { |file| model.update(attachment: file) }` - export google sheet document as PDF
236
+
237
+ ## Contributing
238
+
239
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jetruby/google_docs-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
240
+
241
+ ## License
242
+
243
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
244
+
245
+ ## Code of Conduct
246
+
247
+ Everyone interacting in the ApolloUploadServer project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/jetruby/google_docs-ruby/blob/master/CODE_OF_CONDUCT.md).
248
+
249
+ ## About JetRuby
250
+ ApolloUploadServer is maintained and founded by JetRuby Agency.
251
+
252
+ We love open source software!
253
+ See [our projects][portfolio] or
254
+ [contact us][contact] to design, develop, and grow your product.
255
+
256
+ [portfolio]: http://jetruby.com/portfolio/
257
+ [contact]: http://jetruby.com/#contactUs
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ task default: :spec
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'google_docs'
5
+ require 'irb'
6
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../lib/google_docs/version', __FILE__)
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'google_docs'
5
+ spec.version = GoogleDocs::VERSION
6
+ spec.authors = ['JetRuby']
7
+ spec.email = ['engineering@jetruby.com']
8
+
9
+ spec.summary = 'Google Docs'
10
+ spec.description = 'A simple gem to create styles for google document'
11
+ spec.homepage = 'http://jetruby.com'
12
+ spec.license = 'MIT'
13
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
14
+ f.match(%r{^(test|spec|features)/})
15
+ end
16
+ spec.bindir = 'exe'
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler', '~> 1.14'
21
+ spec.add_development_dependency 'rake', '~> 10.0'
22
+ spec.add_development_dependency 'rspec', '~> 3.5'
23
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'google_docs/version'
2
+ require 'google/apis/sheets_v4'
3
+ require_relative 'google_docs/sheet'
4
+ require_relative 'google_docs/spread_sheet'
5
+
6
+ module GoogleDocs
7
+ SERVICE = Google::Apis::SheetsV4
8
+ end
@@ -0,0 +1,277 @@
1
+ require 'tempfile'
2
+ require_relative '../string_utils'
3
+
4
+ module GoogleDocs
5
+ class Sheet
6
+ LETTER_LIST = ('A'..'ZZZ').to_a.freeze
7
+ # Set class instance variables
8
+ # @param spreadsheet_id [String] id of spreadsheet
9
+ # @param sheet_id [String] id of sheet
10
+ # @param google_access_token [String]
11
+ # @param service [Google::Apis::SheetsV4::SheetsService]
12
+ # param google_access_token [String] google access token
13
+ # @note if you have `service` you don't need `google_access_token`
14
+ def initialize(spreadsheet_id:, sheet_id:, google_access_token: nil, service: nil)
15
+ if service.nil?
16
+ @service = SERVICE::SheetsService.new
17
+ @service.authorization = google_access_token
18
+ else
19
+ @service = service
20
+ end
21
+
22
+ @spreadsheet = @service.get_spreadsheet(spreadsheet_id)
23
+ @sheet = @spreadsheet.sheets.find { |sheet| sheet.properties.sheet_id == sheet_id.to_i }
24
+ @requests = []
25
+
26
+ raise ArgumentError, 'Invalid sheet id' if @sheet.nil?
27
+ end
28
+
29
+ # return all information from each row in sheet
30
+ def get_rows_values
31
+ last_column_letter = LETTER_LIST[@sheet.properties.grid_properties.column_count - 1]
32
+ last_row_number = @sheet.properties.grid_properties.row_count
33
+ @service.get_spreadsheet_values(
34
+ @spreadsheet.spreadsheet_id, "#{@sheet.properties.title}!A1:#{last_column_letter}#{last_row_number}"
35
+ ).values
36
+ end
37
+
38
+ # create pdf file of sheet's content
39
+ # @yield [file] Gives file to the block
40
+ def download_pdf
41
+ file = Tempfile.new(['sheet', '.pdf'])
42
+ file.binmode
43
+ file.write(pdf_request)
44
+ yield(file)
45
+ file.close
46
+ end
47
+
48
+ def pdf_request
49
+ @service.http(:get, "https://docs.google.com/spreadsheets/d/#{@spreadsheet.spreadsheet_id}/export?exportFormat=pdf&gid=#{@sheet.properties.sheet_id}")
50
+ end
51
+
52
+ # @param data [Hash] keys are the columns names and values indicate desirable columns width in sheet
53
+ # {
54
+ # {
55
+ # 'A' => 'integer value',
56
+ # 'D' => 'integer value',
57
+ # ...
58
+ # }
59
+ # }
60
+ # @note 'integer value' is positive pixel size for Google::Apis::SheetsV4::DimensionProperties
61
+ def setup_columns_width(data)
62
+ data.each do |key, value|
63
+ append_request(
64
+ update_dimension_properties: SERVICE::UpdateDimensionPropertiesRequest.new(
65
+ range: SERVICE::DimensionRange.new(
66
+ sheet_id: @sheet.properties.sheet_id,
67
+ dimension: 'COLUMNS',
68
+ start_index: LETTER_LIST.index(key),
69
+ end_index: LETTER_LIST.index(key) + 1
70
+ ),
71
+ properties: SERVICE::DimensionProperties.new(pixel_size: value),
72
+ fields: 'pixelSize'
73
+ )
74
+ )
75
+ end
76
+ true
77
+ end
78
+
79
+ # @param props [Hash]
80
+ # @example of props value
81
+ # https://developers.google.com/sheets/reference/rest/v4/spreadsheets#GridProperties
82
+ # {
83
+ # row_count: integer
84
+ # column_count: integer
85
+ # frozen_row_count: integer
86
+ # frozen_column_count: integer
87
+ # hide_gridlines: bool
88
+ # }
89
+ def update_grid_properties(props)
90
+ append_request(
91
+ update_sheet_properties: SERVICE::UpdateSheetPropertiesRequest.new(
92
+ fields: props.keys.map { |field| "gridProperties.#{StringUtils.camelize(field.to_s)}" }.join(','),
93
+ properties: SERVICE::SheetProperties.new(
94
+ grid_properties: SERVICE::GridProperties.new(props),
95
+ sheet_id: @sheet.properties.sheet_id
96
+ )
97
+ )
98
+ )
99
+ end
100
+
101
+ # @param data [Array<Array>]
102
+ # @example of data value
103
+ # [
104
+ # [ 'text', 'text', ...], # first row
105
+ # [], # second row (empty)
106
+ # [ 'text', { value: 'text', ...props }] # third row
107
+ # ...
108
+ # ]
109
+ def update_cells(data)
110
+ append_request(
111
+ update_cells: SERVICE::UpdateCellsRequest.new(
112
+ rows: format_rows_from(data),
113
+ fields: '*',
114
+ range: grid_range
115
+ )
116
+ )
117
+ end
118
+
119
+ # @param props [Hash]
120
+ # @example of props value
121
+ # {
122
+ # type: :merge_all, :merge_columns, :merge_rows
123
+ # range: {
124
+ # start_row_index: integer
125
+ # end_row_index: integer
126
+ # start_column_index: integer
127
+ # end_column_index: integer
128
+ # }
129
+ # }
130
+ def merge_cells(props)
131
+ append_request(
132
+ merge_cells: SERVICE::MergeCellsRequest.new(
133
+ merge_type: props[:type],
134
+ range: grid_range(props[:range])
135
+ )
136
+ )
137
+ end
138
+
139
+ # @param range [Hash]
140
+ # @example of props value
141
+ # {
142
+ # start_row_index: integer
143
+ # end_row_index: integer
144
+ # start_column_index: integer
145
+ # end_column_index: integer
146
+ # }
147
+ def unmerge_cells(range)
148
+ append_request(
149
+ unmerge_cells: SERVICE::UnmergeCellsRequest.new(
150
+ range: grid_range(range)
151
+ )
152
+ )
153
+ end
154
+
155
+ def apply_changes!
156
+ @service.batch_update_spreadsheet(
157
+ @spreadsheet.spreadsheet_id, SERVICE::BatchUpdateSpreadsheetRequest.new(requests: @requests)
158
+ )
159
+ @requests = []
160
+ end
161
+
162
+ private
163
+
164
+ def append_request(request_params)
165
+ @requests << SERVICE::Request.new(request_params)
166
+ end
167
+
168
+ def grid_range(range = {})
169
+ SERVICE::GridRange.new(range.merge(sheet_id: @sheet.properties.sheet_id))
170
+ end
171
+
172
+ def format_rows_from(values)
173
+ values.map do |row_cells|
174
+ row_data = row_cells.map do |cell|
175
+ if cell.is_a? Hash
176
+ SERVICE::CellData.new(
177
+ user_entered_value: extended_value_for(cell[:value]),
178
+ user_entered_format: format_cell_with(cell.except(:value))
179
+ )
180
+ else
181
+ SERVICE::CellData.new(user_entered_value: extended_value_for(cell))
182
+ end
183
+ end
184
+ SERVICE::RowData.new(values: row_data)
185
+ end
186
+ end
187
+
188
+ def detect_type_for(value)
189
+ case value
190
+ when String
191
+ value.start_with?('=') ? :formula_value : :string_value
192
+ when Integer, Float
193
+ :number_value
194
+ when TrueClass, FalseClass
195
+ :bool_value
196
+ else
197
+ :string_value
198
+ end
199
+ end
200
+
201
+ def extended_value_for(value)
202
+ SERVICE::ExtendedValue.new(detect_type_for(value || value.to_s) => (value || value.to_s))
203
+ end
204
+
205
+ #
206
+ # {
207
+ # borders: {
208
+ # left, right, top, bottom: {
209
+ # style: :dotted, :dashed, :solid, :double
210
+ # width: 1..3
211
+ # color: '#RRGGBB'
212
+ # }
213
+ # }
214
+ # background_color: '#RRGGBB'
215
+ # horizontal_alignment: :left, :center, :right
216
+ # vertical_alignment: :top, :middle, :bottom
217
+ # hyperlink_display_type: :linked, :plain_text
218
+ # wrap_strategy: :overflow_cell, :legacy_wrap, :clip, :wrap
219
+ # text_direction: :left_to_right, :right_to_left
220
+ # number_format: {
221
+ # type: :text, :number, :percent, :currency, :date, :time, :datetime, :scientific
222
+ # pattern: "#,##0.0000"
223
+ # }
224
+ # text_format: {
225
+ # foreground_color: '#RRGGBB'
226
+ # font_family: 'Terminus'
227
+ # font_size: integer
228
+ # bold: bool
229
+ # italic: bool
230
+ # strikethrough: bool
231
+ # underline: bool
232
+ # }
233
+ # }
234
+
235
+ def format_cell_with(params)
236
+ params = params.dup
237
+ params[:background_color] &&= format_color params[:background_color]
238
+ params[:borders] &&= format_borders params[:borders]
239
+ params[:text_format] &&= format_text params[:text_format]
240
+ params[:number_format] &&= format_number params[:number_format]
241
+ SERVICE::CellFormat.new(params)
242
+ end
243
+
244
+ def format_text(props)
245
+ props = props.dup
246
+ props[:foreground_color] &&= format_color(props[:foreground_color])
247
+ SERVICE::TextFormat.new(props)
248
+ end
249
+
250
+ def format_number(props)
251
+ SERVICE::NumberFormat.new(props)
252
+ end
253
+
254
+ def format_color(hex)
255
+ red, green, blue = hex_to_rgb(hex)
256
+ SERVICE::Color.new(red: red, blue: blue, green: green, alpha: 1.0)
257
+ end
258
+
259
+ def format_borders(borders)
260
+ SERVICE::Borders.new(borders.map { |position, props| [position, format_border(props)] }.to_h)
261
+ end
262
+
263
+ def format_border(props)
264
+ SERVICE::Border.new(
265
+ style: props[:style],
266
+ width: props[:width],
267
+ color: format_color(props[:color])
268
+ )
269
+ end
270
+
271
+ # '#RRGGBB' => [0..1, 0..1, 0..1]
272
+
273
+ def hex_to_rgb(hex)
274
+ hex.scan(/[^#]{2}/).map { |color| (color.to_i(16).to_f / 255) }
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,19 @@
1
+ module GoogleDocs
2
+ class SpreadSheet
3
+ def initialize(google_access_token, spreadsheet_id)
4
+ @service = SERVICE::SheetsService.new
5
+ @service.authorization = google_access_token
6
+ @spreadsheet = @service.get_spreadsheet(spreadsheet_id)
7
+ end
8
+
9
+ def sheets
10
+ @spreadsheet.sheets.map do |sheet|
11
+ Sheet.new(spreadsheet_id: @spreadsheet.spreadsheet_id, sheet_id: sheet.properties.sheet_id, service: @service)
12
+ end
13
+ end
14
+
15
+ def properties
16
+ @spreadsheet.properties
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module GoogleDocs
2
+ VERSION = '0.0.1'.freeze
3
+ end
@@ -0,0 +1,10 @@
1
+ module StringUtils
2
+ def self.camelize(string, uppercase_first_letter = true)
3
+ string = if uppercase_first_letter
4
+ string.sub(/^[a-z\d]*/) { $&.capitalize }
5
+ else
6
+ string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
7
+ end
8
+ string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }.gsub('/', '::')
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google_docs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - JetRuby
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-29 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.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.5'
55
+ description: A simple gem to create styles for google document
56
+ email:
57
+ - engineering@jetruby.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - CODE_OF_CONDUCT.md
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - google_docs.gemspec
71
+ - lib/google_docs.rb
72
+ - lib/google_docs/sheet.rb
73
+ - lib/google_docs/spread_sheet.rb
74
+ - lib/google_docs/version.rb
75
+ - lib/string_utils.rb
76
+ - samples/google_sheet_merge_cells.png
77
+ - samples/google_sheet_set_width.png
78
+ - samples/google_sheet_update_cells.png
79
+ - samples/google_sheet_update_grid_properties.png
80
+ - samples/google_sheet_without_width.png
81
+ homepage: http://jetruby.com
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.6.11
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Google Docs
105
+ test_files: []