sheets_v4 0.1.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.markdownlint.yml +25 -0
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +29 -0
- data/README.md +88 -7
- data/Rakefile +19 -6
- data/examples/README.md +48 -0
- data/examples/set_background_color +52 -0
- data/lib/sheets_v4/color.rb +197 -0
- data/lib/sheets_v4/credential_creator.rb +93 -0
- data/lib/sheets_v4/validate_api_object.rb +174 -0
- data/lib/sheets_v4/version.rb +2 -1
- data/lib/sheets_v4.rb +95 -2
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6357385eeb33468356dd974f8fda49f92e09b30b9911a825c562509bf5f70dd
|
4
|
+
data.tar.gz: a289b95495fd49e8221a89e037168aaae903ca40af2f9167cff60c75c0f28acc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59b81fbfa525ee8f5810d1aeeb3d6168dfffe0ffd1a8c19792d8f27548cdb1b2106748a30f499f8619bd83cd73d773c5647f0b7fd57640c990af2eb7e9d5fd7b
|
7
|
+
data.tar.gz: '09ffa389430c102e6e4df4370bc42f2e8769399f78cabbfe61441372ed6bf4a87378a124a3c17fe0c7f53a73255fae7f153cd08df67e93589c5bcbb49360166e'
|
data/.markdownlint.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
default: true
|
2
|
+
|
3
|
+
# Unordered list indentation
|
4
|
+
MD007: { indent: 2 }
|
5
|
+
|
6
|
+
# Line length
|
7
|
+
MD013: { line_length: 90, tables: false, code_blocks: false }
|
8
|
+
|
9
|
+
# Heading duplication is allowed for non-sibling headings
|
10
|
+
MD024: { siblings_only: true }
|
11
|
+
|
12
|
+
# Do not allow the specified trailig punctuation in a header
|
13
|
+
MD026: { punctuation: '.,;:' }
|
14
|
+
|
15
|
+
# Order list items must have a prefix that increases in numerical order
|
16
|
+
MD029: { style: 'ordered' }
|
17
|
+
|
18
|
+
# Lists do not need to be surrounded by blank lines
|
19
|
+
MD032: false
|
20
|
+
|
21
|
+
# Allow raw HTML in Markdown
|
22
|
+
MD033: false
|
23
|
+
|
24
|
+
# Allow emphasis to be used instead of a heading
|
25
|
+
MD036: false
|
data/.rubocop.yml
CHANGED
@@ -23,8 +23,12 @@ Metrics/BlockLength:
|
|
23
23
|
- "spec/**/*_spec.rb"
|
24
24
|
- "*.gemspec"
|
25
25
|
|
26
|
+
Metrics/ModuleLength:
|
27
|
+
CountAsOne: ['hash']
|
28
|
+
|
26
29
|
# When writing minitest tests, it is very hard to limit test class length:
|
27
30
|
Metrics/ClassLength:
|
31
|
+
CountAsOne: ['hash']
|
28
32
|
Exclude:
|
29
33
|
- "test/**/*_test.rb"
|
30
34
|
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,35 @@ Changes for each release are listed in this file.
|
|
4
4
|
|
5
5
|
This project adheres to [Semantic Versioning](https://semver.org/) for its releases.
|
6
6
|
|
7
|
+
## v0.4.0 (2023-09-29)
|
8
|
+
|
9
|
+
[Full Changelog](https://github.com/main-branch/sheets_v4/compare/v0.3.0..v0.4.0)
|
10
|
+
|
11
|
+
Changes since v0.3.0:
|
12
|
+
|
13
|
+
* 843c1b5 Add SheetsV4.sheets_service (#13)
|
14
|
+
* 16a73f5 Refactor ValidateApiObject (#12)
|
15
|
+
* 1525cd3 Add a link in the README to the Sheet V4 API Discover Doc (#11)
|
16
|
+
* 6d5bfb3 Improve color api and documentation (#10)
|
17
|
+
|
18
|
+
## v0.3.0 (2023-09-28)
|
19
|
+
|
20
|
+
[Full Changelog](https://github.com/main-branch/sheets_v4/compare/v0.2.0..v0.3.0)
|
21
|
+
|
22
|
+
Changes since v0.2.0:
|
23
|
+
|
24
|
+
* 0fb4579 Add predefined color objects (#8)
|
25
|
+
|
26
|
+
## v0.2.0 (2023-09-26)
|
27
|
+
|
28
|
+
[Full Changelog](https://github.com/main-branch/sheets_v4/compare/v0.1.1..v0.2.0)
|
29
|
+
|
30
|
+
Changes since v0.1.1:
|
31
|
+
|
32
|
+
* 489ff50 Add SheetsV4.validate_api_object (#6)
|
33
|
+
* b44eba4 List all examples that should be created (#5)
|
34
|
+
* 2edd688 Add links to Google Sheets documentation to the README.md (#4)
|
35
|
+
|
7
36
|
## v0.1.1 (2023-09-22)
|
8
37
|
|
9
38
|
[Full Changelog](https://github.com/main-branch/sheets_v4/compare/v0.1.0..v0.1.1)
|
data/README.md
CHANGED
@@ -9,12 +9,36 @@
|
|
9
9
|
|
10
10
|
Unofficial helpers for the Google Sheets V4 API
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
* [Important Links for Programming Google Sheets](#important-links-for-programming-google-sheets)
|
13
|
+
* [General API Documentation](#general-api-documentation)
|
14
|
+
* [Ruby Implementation of the Sheets API](#ruby-implementation-of-the-sheets-api)
|
15
|
+
* [Other Links](#other-links)
|
16
|
+
* [Installation](#installation)
|
17
|
+
* [Usage](#usage)
|
18
|
+
* [Obtaining an authenticated SheetsService](#obtaining-an-authenticated-sheetsservice)
|
19
|
+
* [Colors](#colors)
|
20
|
+
* [Development](#development)
|
21
|
+
* [Creating a Google API Service Account](#creating-a-google-api-service-account)
|
22
|
+
* [Contributing](#contributing)
|
23
|
+
* [License](#license)
|
24
|
+
|
25
|
+
## Important Links for Programming Google Sheets
|
26
|
+
|
27
|
+
### General API Documentation
|
28
|
+
|
29
|
+
* [Google Sheets API Overview](https://developers.google.com/sheets/api)
|
30
|
+
* [Google Sheets API Reference](https://developers.google.com/sheets/api/reference/rest)
|
31
|
+
* [Batch Update Requests](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request)
|
32
|
+
* [Discovery Document for the Sheets API](https://sheets.googleapis.com/$discovery/rest?version=v4)
|
33
|
+
|
34
|
+
### Ruby Implementation of the Sheets API
|
35
|
+
|
36
|
+
* [SheetsService Class](https://github.com/googleapis/google-api-ruby-client/blob/main/generated/google-apis-sheets_v4/lib/google/apis/sheets_v4/service.rb)
|
37
|
+
* [All Other Sheets Classes](https://github.com/googleapis/google-api-ruby-client/blob/main/generated/google-apis-sheets_v4/lib/google/apis/sheets_v4/classes.rb)
|
38
|
+
|
39
|
+
### Other Links
|
40
|
+
|
41
|
+
* [Apps Script for Sheets](https://developers.google.com/apps-script/guides/sheets)
|
18
42
|
|
19
43
|
## Installation
|
20
44
|
|
@@ -34,6 +58,60 @@ gem install sheets_v4
|
|
34
58
|
|
35
59
|
[Detailed API documenation](https://rubydoc.info/gems/sheets_v4/) is hosted on rubygems.org.
|
36
60
|
|
61
|
+
### Obtaining an authenticated SheetsService
|
62
|
+
|
63
|
+
Typically, to construct an authenticated SheetsService object where the credential
|
64
|
+
is read from a file, the following code is required:
|
65
|
+
|
66
|
+
```Ruby
|
67
|
+
sheets_service = Google::Apis::SheetsV4::SheetsService.new
|
68
|
+
credential = File.read(File.expand_path('~/google-api-credential.json')) do |credential_source|
|
69
|
+
scopes = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
|
70
|
+
options = { json_key_io: credential_source, scope: scopes }
|
71
|
+
Google::Auth::DefaultCredentials.make_creds(options).tap(&:fetch_access_token)
|
72
|
+
end
|
73
|
+
sheets_service.authorization = credential
|
74
|
+
```
|
75
|
+
|
76
|
+
The `SheetsV4.sheets_service` method simplifies this a bit.
|
77
|
+
|
78
|
+
By default, the credential is read from `~/.google-api-credential.json`. In that case,
|
79
|
+
an authenticated SheetsService object can be obtained with one method call:
|
80
|
+
|
81
|
+
```Ruby
|
82
|
+
sheets_service = SheetsV4.sheets_service
|
83
|
+
```
|
84
|
+
|
85
|
+
If the credential is stored elsewhere, pass the credential_source to `SheetsV4.sheets_service`
|
86
|
+
manually. `credential_source` can be a String:
|
87
|
+
|
88
|
+
```Ruby
|
89
|
+
sheets_service = SheetsV4.sheets_service(credential_sourvce: File.read('credential.json'))
|
90
|
+
```
|
91
|
+
|
92
|
+
an IO object:
|
93
|
+
|
94
|
+
```Ruby
|
95
|
+
sheets_service = File.open('credential.json') do |credential_source|
|
96
|
+
SheetsV4.sheets_service(credential_sourvce:)
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
or an already constructed `Google::Auth::*`` object.
|
101
|
+
|
102
|
+
### Colors
|
103
|
+
|
104
|
+
Color objects (with appropriate :red, :green, :blue values) can be retrieved by name
|
105
|
+
using `SheetsV4.color(:black)` or `SheetsV4::Color.black` (these are equivalent).
|
106
|
+
|
107
|
+
Color names can be listed using `SheetsV4.color_names`.
|
108
|
+
|
109
|
+
The color object returned is a Hash as follows:
|
110
|
+
|
111
|
+
```Ruby
|
112
|
+
SheetsV4::Color.black #=> {:red=>0.0, :green=>0.0, :blue=>0.0}
|
113
|
+
```
|
114
|
+
|
37
115
|
## Development
|
38
116
|
|
39
117
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
@@ -46,6 +124,9 @@ release a new version, update the version number in `version.rb`, and then run
|
|
46
124
|
commits and the created tag, and push the `.gem` file to
|
47
125
|
[rubygems.org](https://rubygems.org).
|
48
126
|
|
127
|
+
## Creating a Google API Service Account
|
128
|
+
|
129
|
+
|
49
130
|
## Contributing
|
50
131
|
|
51
132
|
Bug reports and pull requests are welcome on [the main-branch/sheets_v4 GitHub project](https://github.com/main-branch/sheets_v4).
|
@@ -53,4 +134,4 @@ Bug reports and pull requests are welcome on [the main-branch/sheets_v4 GitHub p
|
|
53
134
|
## License
|
54
135
|
|
55
136
|
The gem is available as open source under the terms of the
|
56
|
-
[MIT License](https://opensource.org/licenses/MIT).
|
137
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ desc 'Run the same tasks that the CI build will run'
|
|
4
4
|
if RUBY_PLATFORM == 'java'
|
5
5
|
task default: %w[spec rubocop bundle:audit build]
|
6
6
|
else
|
7
|
-
task default: %w[spec rubocop yard
|
7
|
+
task default: %w[spec rubocop yard bundle:audit build]
|
8
8
|
end
|
9
9
|
|
10
10
|
# Bundler Audit
|
@@ -58,28 +58,41 @@ end
|
|
58
58
|
CLEAN << 'rubocop-report.json'
|
59
59
|
|
60
60
|
unless RUBY_PLATFORM == 'java'
|
61
|
-
#
|
61
|
+
# yard:build
|
62
62
|
|
63
63
|
require 'yard'
|
64
|
-
|
65
|
-
|
64
|
+
|
65
|
+
YARD::Rake::YardocTask.new('yard:build') do |t|
|
66
|
+
t.files = %w[lib/**/*.rb]
|
67
|
+
t.stats_options = ['--list-undoc']
|
66
68
|
end
|
67
69
|
|
68
70
|
CLEAN << '.yardoc'
|
69
71
|
CLEAN << 'doc'
|
70
72
|
|
71
|
-
#
|
73
|
+
# yard:audit
|
72
74
|
|
73
75
|
desc 'Run yardstick to show missing YARD doc elements'
|
74
76
|
task :'yard:audit' do
|
75
77
|
sh "yardstick 'lib/**/*.rb'"
|
76
78
|
end
|
77
79
|
|
78
|
-
#
|
80
|
+
# yard:coverage
|
79
81
|
|
80
82
|
require 'yardstick/rake/verify'
|
81
83
|
|
82
84
|
Yardstick::Rake::Verify.new(:'yard:coverage') do |verify|
|
83
85
|
verify.threshold = 100
|
86
|
+
verify.require_exact_threshold = false
|
87
|
+
end
|
88
|
+
|
89
|
+
task yard: %i[yard:build yard:audit yard:coverage]
|
90
|
+
|
91
|
+
# github-pages:publish
|
92
|
+
|
93
|
+
require 'github_pages_rake_tasks'
|
94
|
+
GithubPagesRakeTasks::PublishTask.new do |task|
|
95
|
+
# task.doc_dir = 'documentation'
|
96
|
+
task.verbose = true
|
84
97
|
end
|
85
98
|
end
|
data/examples/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Google Sheets Examples
|
2
|
+
|
3
|
+
* [ ] Creating a Google API service account [1](https://www.youtube.com/watch?v=sAgWCbGMzTo&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=1)
|
4
|
+
[2](https://www.youtube.com/watch?v=sVURhxyc6jE&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=43)
|
5
|
+
* [ ] Create a SheetsService instance
|
6
|
+
* [X] Set background color
|
7
|
+
* [ ] Creating Google Sheets files [1](https://www.youtube.com/watch?v=JRUxeQ6ZCy0&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=2)
|
8
|
+
* [ ] Writing data to a sheet [1](https://www.youtube.com/watch?v=YF7Ad-7pvks&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=3)
|
9
|
+
* [ ] Reading data from a sheet [1](https://www.youtube.com/watch?v=gkglr8GID5E&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=4)
|
10
|
+
* Reading data
|
11
|
+
* Reading formulas
|
12
|
+
* [ ] Data formatting basics [1](https://www.youtube.com/watch?v=R4EN3iPRris&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=5)
|
13
|
+
* [ ] Creating charts [1](https://www.youtube.com/watch?v=xt3p5I8mNWE&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=6)
|
14
|
+
* [ ] Data validation [1](https://www.youtube.com/watch?v=n_Z2565gu6Y&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=7)
|
15
|
+
* [ ] Cut, copy, and paste [1](https://www.youtube.com/watch?v=r8GWH2E_ehw&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=8)
|
16
|
+
* [ ] Duplicate sheets [1](https://www.youtube.com/watch?v=BgQoPcoOiGY&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=9)
|
17
|
+
* [ ] List sheets in a spreadsheet [1](https://www.youtube.com/watch?v=BgQoPcoOiGY&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=9)
|
18
|
+
* [ ] Set column width and row height [1](https://www.youtube.com/watch?v=H3uMEaPqTVE&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=11)
|
19
|
+
* [ ] Append rows and columns [1](https://www.youtube.com/watch?v=txfiwEjb7sk&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=12)
|
20
|
+
* [ ] Delete rows and columns [1](https://www.youtube.com/watch?v=w1jrCxWx7Tc&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=13)
|
21
|
+
* [ ] Insert rows and columns [1](https://www.youtube.com/watch?v=FL7WSsO5EVs&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=14)
|
22
|
+
* [ ] Move rows and columns [1](https://www.youtube.com/watch?v=YHk3305dkOc&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=15)
|
23
|
+
* [ ] Clear data [1](https://www.youtube.com/watch?v=mvbnhfdDrro&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=16)
|
24
|
+
* [ ] Add and delete sheets [1](https://www.youtube.com/watch?v=X9PVQQVoJFc&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=17)
|
25
|
+
* [ ] Copy sheet from one spreadsheet to another [1](https://www.youtube.com/watch?v=aIEM7Ts4n-c&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=18)
|
26
|
+
* [ ] Add a new sheet to an existing spreadsheet
|
27
|
+
* [ ] Export a sheet to a CSV file [1](https://www.youtube.com/watch?v=Dz22fsWsLhI&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=25)
|
28
|
+
* [ ] Sort sheets [1](https://www.youtube.com/watch?v=qbBZX7uBM1M&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=27)
|
29
|
+
* [ ] Add calculated fields into a pivot table [1](https://www.youtube.com/watch?v=VR8zOz5ATLU&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=32)
|
30
|
+
* [ ] Named ranges [1](https://www.youtube.com/watch?v=LTPdfXS_LHA&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=42)
|
31
|
+
* [ ] Create a pivot table [1](https://www.youtube.com/watch?v=preFnuL7ua0&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=28)
|
32
|
+
* [ ] Calculated pivot fields [1](https://www.youtube.com/watch?v=QLssI4uvjk4&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=33)
|
33
|
+
* [ ] Delete a pivot table
|
34
|
+
* [ ] Add pivot fields [1](https://www.youtube.com/watch?v=VR8zOz5ATLU&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=32)
|
35
|
+
* [ ] Add pivot filters [1](https://www.youtube.com/watch?v=EKikw-eIcbY&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=29)
|
36
|
+
* [ ] Collapse/expand pivot table groups [1](https://www.youtube.com/watch?v=-S9bs5-ZJ5E&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=31)
|
37
|
+
* [ ] Extract pivot table metadata [1](https://www.youtube.com/watch?v=H1SGdqbaL4w&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=30)
|
38
|
+
* [ ] Filter views [1](https://www.youtube.com/watch?v=GyRxsSlx0GU&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=34)
|
39
|
+
* [ ] Locate the last row in a column [1](https://www.youtube.com/watch?v=NWWHleJll28&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=35)
|
40
|
+
* [ ] Autofill [1](https://www.youtube.com/watch?v=guHGNmODdpM&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=36)
|
41
|
+
* [ ] Rename a sheet [1](https://www.youtube.com/watch?v=iuiDUJ4NrQI&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=37)
|
42
|
+
* [ ] Find and replace [1](https://www.youtube.com/watch?v=YaFR0bu5CrY&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=39)
|
43
|
+
* [ ] Add and delete sheets [1](https://www.youtube.com/watch?v=gMD4v8F8vlc&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=38)
|
44
|
+
* [ ] Sum across sheets [1](https://www.youtube.com/watch?v=7QNk-MXkPC4&list=PL3JVwFmb_BnSee8RFaRPZ3nykuMRlaQp1&index=44)
|
45
|
+
* [ ] Freeze rows / columns
|
46
|
+
* [ ] Protected ranges
|
47
|
+
* [ ] Resize a sheet
|
48
|
+
* [ ] Retrying on error
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'sheets_v4'
|
5
|
+
require 'googleauth'
|
6
|
+
|
7
|
+
# Example to show using the SheetsV4 module to set the background color of a cell
|
8
|
+
#
|
9
|
+
# GIVEN the credential file is at ~/.google-api-credential.json
|
10
|
+
# AND the spreadsheet id is 18FAcgotK7nDfLTOTQuGCIjKwxkJMAguhn1OVzpFFgWY
|
11
|
+
# AND the spreadsheet has a sheet whose id is 0
|
12
|
+
# WHEN the script is run
|
13
|
+
# THEN the sheet whose id is 0 will have the background color names starting at A2
|
14
|
+
# AND the background color of the cells in column B will be set to the color matching the color name in column A
|
15
|
+
|
16
|
+
# Build the requests
|
17
|
+
|
18
|
+
def name_rows
|
19
|
+
SheetsV4.color_names.map do |color_name|
|
20
|
+
{ values: [{ user_entered_value: { string_value: color_name.to_s } }] }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def write_names
|
25
|
+
rows = name_rows
|
26
|
+
fields = 'user_entered_value'
|
27
|
+
start = { sheet_id: 0, row_index: 1, column_index: 0 }
|
28
|
+
{ update_cells: { rows:, fields:, start: } }
|
29
|
+
end
|
30
|
+
|
31
|
+
def background_color_rows
|
32
|
+
SheetsV4.color_names.map { |color_name| SheetsV4.color(color_name) }.map do |color|
|
33
|
+
{ values: [{ user_entered_format: { background_color: color } }] }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_background_colors
|
38
|
+
rows = background_color_rows
|
39
|
+
fields = 'user_entered_format'
|
40
|
+
start = { sheet_id: 0, row_index: 1, column_index: 1 }
|
41
|
+
{ update_cells: { rows:, fields:, start: } }
|
42
|
+
end
|
43
|
+
|
44
|
+
def requests = { requests: [write_names, set_background_colors] }
|
45
|
+
|
46
|
+
# SheetsV4.validate_api_object(
|
47
|
+
# schema_name: 'BatchUpdateSpreadsheetRequest', object: request,
|
48
|
+
# logger: Logger.new(STDOUT, level: Logger::ERROR)
|
49
|
+
# )
|
50
|
+
|
51
|
+
spreadsheet_id = '18FAcgotK7nDfLTOTQuGCIjKwxkJMAguhn1OVzpFFgWY'
|
52
|
+
SheetsV4.sheets_service.batch_update_spreadsheet(spreadsheet_id, requests)
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json_schemer'
|
4
|
+
|
5
|
+
module SheetsV4
|
6
|
+
# Returns predefined color objects by name
|
7
|
+
#
|
8
|
+
# A method is implemented for each color name. The list of available colors is
|
9
|
+
# given by `SheetsV4.color_names`.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # Get a color object by name:
|
13
|
+
# SheetsV4::Color.black #=> { "red" => 0.0, "green" => 0.0, "blue" => 0.0 }
|
14
|
+
#
|
15
|
+
# @see SheetsV4.color a method that returns a color object by name
|
16
|
+
# @see SheetsV4.color_names an array of predefined color names
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
#
|
20
|
+
class Color
|
21
|
+
class << self
|
22
|
+
# Return a color object for the given name from SheetsV4.color_names or call super
|
23
|
+
#
|
24
|
+
# @param method_name [#to_sym] the name of the color
|
25
|
+
# @param arguments [Array] ignored
|
26
|
+
# @param block [Proc] ignored
|
27
|
+
# @return [Hash] the color object
|
28
|
+
# @api private
|
29
|
+
def method_missing(method_name, *arguments, &)
|
30
|
+
COLORS[method_name.to_sym] || super
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return true if the given method name is a color name or call super
|
34
|
+
#
|
35
|
+
# @param method_name [#to_sym] the name of the color
|
36
|
+
# @param include_private [Boolean] ignored
|
37
|
+
# @return [Boolean] true if the method name is a color name
|
38
|
+
# @api private
|
39
|
+
def respond_to_missing?(method_name, include_private = false)
|
40
|
+
COLORS.key?(method_name.to_sym) || super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Colors to use in the Google Sheets API
|
45
|
+
COLORS = {
|
46
|
+
# Standard Google Sheets colors
|
47
|
+
#
|
48
|
+
black: { red: 0.00000000000, green: 0.00000000000, blue: 0.00000000000 },
|
49
|
+
dark_gray4: { red: 0.26274509804, green: 0.26274509804, blue: 0.26274509804 },
|
50
|
+
dark_gray3: { red: 0.40000000000, green: 0.40000000000, blue: 0.40000000000 },
|
51
|
+
dark_gray2: { red: 0.60000000000, green: 0.60000000000, blue: 0.60000000000 },
|
52
|
+
dark_gray1: { red: 0.71764705882, green: 0.71764705882, blue: 0.71764705882 },
|
53
|
+
gray: { red: 0.80000000000, green: 0.80000000000, blue: 0.80000000000 },
|
54
|
+
light_gray1: { red: 0.85098039216, green: 0.85098039216, blue: 0.85098039216 },
|
55
|
+
light_gray2: { red: 0.93725490196, green: 0.93725490196, blue: 0.93725490196 },
|
56
|
+
light_gray3: { red: 0.95294117647, green: 0.95294117647, blue: 0.95294117647 },
|
57
|
+
white: { red: 1.00000000000, green: 1.00000000000, blue: 1.00000000000 },
|
58
|
+
red_berry: { red: 0.59607843137, green: 0.00000000000, blue: 0.00000000000 },
|
59
|
+
red: { red: 1.00000000000, green: 0.00000000000, blue: 0.00000000000 },
|
60
|
+
orange: { red: 1.00000000000, green: 0.60000000000, blue: 0.00000000000 },
|
61
|
+
yellow: { red: 1.00000000000, green: 1.00000000000, blue: 0.00000000000 },
|
62
|
+
green: { red: 0.00000000000, green: 1.00000000000, blue: 0.00000000000 },
|
63
|
+
cyan: { red: 0.00000000000, green: 1.00000000000, blue: 1.00000000000 },
|
64
|
+
cornflower_blue: { red: 0.29019607843, green: 0.52549019608, blue: 0.90980392157 },
|
65
|
+
blue: { red: 0.00000000000, green: 0.00000000000, blue: 1.00000000000 },
|
66
|
+
purple: { red: 0.60000000000, green: 0.00000000000, blue: 1.00000000000 },
|
67
|
+
magenta: { red: 1.00000000000, green: 0.00000000000, blue: 1.00000000000 },
|
68
|
+
light_red_berry3: { red: 0.90196078431, green: 0.72156862745, blue: 0.68627450980 },
|
69
|
+
light_red3: { red: 0.95686274510, green: 0.80000000000, blue: 0.80000000000 },
|
70
|
+
light_orange3: { red: 0.98823529412, green: 0.89803921569, blue: 0.80392156863 },
|
71
|
+
light_yellow3: { red: 1.00000000000, green: 0.94901960784, blue: 0.80000000000 },
|
72
|
+
light_green3: { red: 0.85098039216, green: 0.91764705882, blue: 0.82745098039 },
|
73
|
+
light_cyan3: { red: 0.81568627451, green: 0.87843137255, blue: 0.89019607843 },
|
74
|
+
light_cornflower_blue3: { red: 0.78823529412, green: 0.85490196078, blue: 0.97254901961 },
|
75
|
+
light_blue3: { red: 0.81176470588, green: 0.88627450980, blue: 0.95294117647 },
|
76
|
+
light_purple3: { red: 0.85098039216, green: 0.82352941176, blue: 0.91372549020 },
|
77
|
+
light_magenta3: { red: 0.91764705882, green: 0.81960784314, blue: 0.86274509804 },
|
78
|
+
light_red_berry2: { red: 0.86666666667, green: 0.49411764706, blue: 0.41960784314 },
|
79
|
+
light_red2: { red: 0.91764705882, green: 0.60000000000, blue: 0.60000000000 },
|
80
|
+
light_orange2: { red: 0.97647058824, green: 0.79607843137, blue: 0.61176470588 },
|
81
|
+
light_yellow2: { red: 1.00000000000, green: 0.89803921569, blue: 0.60000000000 },
|
82
|
+
light_green2: { red: 0.71372549020, green: 0.84313725490, blue: 0.65882352941 },
|
83
|
+
light_cyan2: { red: 0.63529411765, green: 0.76862745098, blue: 0.78823529412 },
|
84
|
+
light_cornflower_blue2: { red: 0.64313725490, green: 0.76078431373, blue: 0.95686274510 },
|
85
|
+
light_blue2: { red: 0.62352941176, green: 0.77254901961, blue: 0.90980392157 },
|
86
|
+
light_purple2: { red: 0.70588235294, green: 0.65490196078, blue: 0.83921568627 },
|
87
|
+
light_magenta2: { red: 0.83529411765, green: 0.65098039216, blue: 0.74117647059 },
|
88
|
+
light_red_berry1: { red: 0.80000000000, green: 0.25490196078, blue: 0.14509803922 },
|
89
|
+
light_red1: { red: 0.87843137255, green: 0.40000000000, blue: 0.40000000000 },
|
90
|
+
light_orange1: { red: 0.96470588235, green: 0.69803921569, blue: 0.41960784314 },
|
91
|
+
light_yellow1: { red: 1.00000000000, green: 0.85098039216, blue: 0.40000000000 },
|
92
|
+
light_green1: { red: 0.57647058824, green: 0.76862745098, blue: 0.49019607843 },
|
93
|
+
light_cyan1: { red: 0.46274509804, green: 0.64705882353, blue: 0.68627450980 },
|
94
|
+
light_cornflower_blue1: { red: 0.42745098039, green: 0.61960784314, blue: 0.92156862745 },
|
95
|
+
light_blue1: { red: 0.43529411765, green: 0.65882352941, blue: 0.86274509804 },
|
96
|
+
light_purple1: { red: 0.55686274510, green: 0.48627450980, blue: 0.76470588235 },
|
97
|
+
light_magenta1: { red: 0.76078431373, green: 0.48235294118, blue: 0.62745098039 },
|
98
|
+
dark_red_berry1: { red: 0.65098039216, green: 0.10980392157, blue: 0.00000000000 },
|
99
|
+
dark_red1: { red: 0.80000000000, green: 0.00000000000, blue: 0.00000000000 },
|
100
|
+
dark_orange1: { red: 0.90196078431, green: 0.56862745098, blue: 0.21960784314 },
|
101
|
+
dark_yellow1: { red: 0.94509803922, green: 0.76078431373, blue: 0.19607843137 },
|
102
|
+
dark_green1: { red: 0.41568627451, green: 0.65882352941, blue: 0.30980392157 },
|
103
|
+
dark_cyan1: { red: 0.27058823529, green: 0.50588235294, blue: 0.55686274510 },
|
104
|
+
dark_cornflower_blue1: { red: 0.23529411765, green: 0.47058823529, blue: 0.84705882353 },
|
105
|
+
dark_blue1: { red: 0.23921568627, green: 0.52156862745, blue: 0.77647058824 },
|
106
|
+
dark_purple1: { red: 0.40392156863, green: 0.30588235294, blue: 0.65490196078 },
|
107
|
+
dark_magenta1: { red: 0.65098039216, green: 0.30196078431, blue: 0.47450980392 },
|
108
|
+
dark_red_berry2: { red: 0.52156862745, green: 0.12549019608, blue: 0.04705882353 },
|
109
|
+
dark_red2: { red: 0.60000000000, green: 0.00000000000, blue: 0.00000000000 },
|
110
|
+
dark_orange2: { red: 0.70588235294, green: 0.37254901961, blue: 0.02352941176 },
|
111
|
+
dark_yellow2: { red: 0.74901960784, green: 0.56470588235, blue: 0.00000000000 },
|
112
|
+
dark_green2: { red: 0.21960784314, green: 0.46274509804, blue: 0.11372549020 },
|
113
|
+
dark_cyan2: { red: 0.07450980392, green: 0.30980392157, blue: 0.36078431373 },
|
114
|
+
dark_cornflower_blue2: { red: 0.06666666667, green: 0.33333333333, blue: 0.80000000000 },
|
115
|
+
dark_blue2: { red: 0.04313725490, green: 0.32549019608, blue: 0.58039215686 },
|
116
|
+
dark_purple2: { red: 0.20784313725, green: 0.10980392157, blue: 0.45882352941 },
|
117
|
+
dark_magenta2: { red: 0.45490196078, green: 0.10588235294, blue: 0.27843137255 },
|
118
|
+
dark_red_berry3: { red: 0.35686274510, green: 0.05882352941, blue: 0.00000000000 },
|
119
|
+
dark_red3: { red: 0.40000000000, green: 0.00000000000, blue: 0.00000000000 },
|
120
|
+
dark_orange3: { red: 0.47058823529, green: 0.24705882353, blue: 0.01568627451 },
|
121
|
+
dark_yellow3: { red: 0.49803921569, green: 0.37647058824, blue: 0.00000000000 },
|
122
|
+
dark_green3: { red: 0.15294117647, green: 0.30588235294, blue: 0.07450980392 },
|
123
|
+
darn_cyan3: { red: 0.04705882353, green: 0.20392156863, blue: 0.23921568627 },
|
124
|
+
dark_cornflower_blue3: { red: 0.10980392157, green: 0.27058823529, blue: 0.52941176471 },
|
125
|
+
dark_blue3: { red: 0.02745098039, green: 0.21568627451, blue: 0.38823529412 },
|
126
|
+
dark_purple3: { red: 0.12549019608, green: 0.07058823529, blue: 0.30196078431 },
|
127
|
+
dark_magenta3: { red: 0.29803921569, green: 0.06666666667, blue: 0.18823529412 },
|
128
|
+
|
129
|
+
# Yahoo brand colors
|
130
|
+
#
|
131
|
+
grape_jelly: { red: 0.37647058824, green: 0.00392156863, blue: 0.82352941176 },
|
132
|
+
hulk_pants: { red: 0.49411764706, green: 0.12156862745, blue: 1.00000000000 },
|
133
|
+
malbec: { red: 0.22352941176, green: 0.00000000000, blue: 0.49019607843 },
|
134
|
+
tumeric: { red: 1.00000000000, green: 0.65490196078, blue: 0.00000000000 },
|
135
|
+
mulah: { red: 0.10196078431, green: 0.77254901961, blue: 0.40392156863 },
|
136
|
+
dory: { red: 0.05882352941, green: 0.41176470588, blue: 1.00000000000 },
|
137
|
+
malibu: { red: 1.00000000000, green: 0.00000000000, blue: 0.50196078431 },
|
138
|
+
sea_foam: { red: 0.06666666667, green: 0.82745098039, blue: 0.80392156863 },
|
139
|
+
tumeric_tint: { red: 0.98039215686, green: 0.86666666667, blue: 0.69411764706 },
|
140
|
+
mulah_tint: { red: 0.73333333333, green: 0.90196078431, blue: 0.77647058824 },
|
141
|
+
dory_tint: { red: 0.66274509804, green: 0.77254901961, blue: 0.98431372549 },
|
142
|
+
malibu_tint: { red: 0.96862745098, green: 0.68235294118, blue: 0.80000000000 },
|
143
|
+
sea_foam_tint: { red: 0.74901960784, green: 0.92549019608, blue: 0.92156862745 },
|
144
|
+
|
145
|
+
# Yahoo health colors
|
146
|
+
#
|
147
|
+
health_green: { red: 0.00000000000, green: 0.69019607843, blue: 0.31372549020 },
|
148
|
+
health_yellow: { red: 1.00000000000, green: 0.65490196078, blue: 0.00000000000 },
|
149
|
+
health_red: { red: 1.00000000000, green: 0.00000000000, blue: 0.00000000000 },
|
150
|
+
|
151
|
+
# Yahoo Fuji design color palette
|
152
|
+
#
|
153
|
+
fuji_color_watermelon: { red: 1.00000000000, green: 0.32156862745, blue: 0.34117647059 },
|
154
|
+
fuji_color_solo_cup: { red: 0.92156862745, green: 0.05882352941, blue: 0.16078431373 },
|
155
|
+
fuji_color_malibu: { red: 1.00000000000, green: 0.00000000000, blue: 0.50196078431 },
|
156
|
+
fuji_color_barney: { red: 0.80000000000, green: 0.00000000000, blue: 0.54901960784 },
|
157
|
+
fuji_color_mimosa: { red: 1.00000000000, green: 0.82745098039, blue: 0.20000000000 },
|
158
|
+
fuji_color_turmeric: { red: 1.00000000000, green: 0.65490196078, blue: 0.00000000000 },
|
159
|
+
fuji_color_cheetos: { red: 0.99215686275, green: 0.38039215686, blue: 0.00000000000 },
|
160
|
+
fuji_color_carrot_juice: { red: 1.00000000000, green: 0.32156862745, blue: 0.05098039216 },
|
161
|
+
fuji_color_mulah: { red: 0.10196078431, green: 0.77254901961, blue: 0.40392156863 },
|
162
|
+
fuji_color_bonsai: { red: 0.00000000000, green: 0.52941176471, blue: 0.31764705882 },
|
163
|
+
fuji_color_spirulina: { red: 0.00000000000, green: 0.62745098039, blue: 0.62745098039 },
|
164
|
+
fuji_color_sea_foam: { red: 0.06666666667, green: 0.82745098039, blue: 0.80392156863 },
|
165
|
+
fuji_color_peeps: { red: 0.49019607843, green: 0.79607843137, blue: 1.00000000000 },
|
166
|
+
fuji_color_sky: { red: 0.07058823529, green: 0.66274509804, blue: 1.00000000000 },
|
167
|
+
fuji_color_dory: { red: 0.05882352941, green: 0.41176470588, blue: 1.00000000000 },
|
168
|
+
fuji_color_scooter: { red: 0.00000000000, green: 0.38823529412, blue: 0.92156862745 },
|
169
|
+
fuji_color_cobalt: { red: 0.00000000000, green: 0.22745098039, blue: 0.73725490196 },
|
170
|
+
fuji_color_denim: { red: 0.10196078431, green: 0.05098039216, blue: 0.67058823529 },
|
171
|
+
fuji_color_blurple: { red: 0.36470588235, green: 0.36862745098, blue: 1.00000000000 },
|
172
|
+
fuji_color_hendrix: { red: 0.97254901961, green: 0.95686274510, blue: 1.00000000000 },
|
173
|
+
fuji_color_thanos: { red: 0.56470588235, green: 0.48627450980, blue: 1.00000000000 },
|
174
|
+
fuji_color_starfish: { red: 0.46666666667, green: 0.34901960784, blue: 1.00000000000 },
|
175
|
+
fuji_color_hulk_pants: { red: 0.49411764706, green: 0.12156862745, blue: 1.00000000000 },
|
176
|
+
fuji_color_grape_jelly: { red: 0.37647058824, green: 0.00392156863, blue: 0.82352941176 },
|
177
|
+
fuji_color_mulberry: { red: 0.31372549020, green: 0.08235294118, blue: 0.69019607843 },
|
178
|
+
fuji_color_malbec: { red: 0.22352941176, green: 0.00000000000, blue: 0.49019607843 },
|
179
|
+
fuji_grayscale_black: { red: 0.00000000000, green: 0.00000000000, blue: 0.00000000000 },
|
180
|
+
fuji_grayscale_midnight: { red: 0.06274509804, green: 0.08235294118, blue: 0.09411764706 },
|
181
|
+
fuji_grayscale_inkwell: { red: 0.11372549020, green: 0.13333333333, blue: 0.15686274510 },
|
182
|
+
fuji_grayscale_batcave: { red: 0.13725490196, green: 0.16470588235, blue: 0.19215686275 },
|
183
|
+
fuji_grayscale_ramones: { red: 0.17254901961, green: 0.21176470588, blue: 0.24705882353 },
|
184
|
+
fuji_grayscale_charcoal: { red: 0.27450980392, green: 0.30588235294, blue: 0.33725490196 },
|
185
|
+
fuji_grayscale_battleship: { red: 0.35686274510, green: 0.38823529412, blue: 0.41568627451 },
|
186
|
+
fuji_grayscale_dolphin: { red: 0.43137254902, green: 0.46666666667, blue: 0.50196078431 },
|
187
|
+
fuji_grayscale_shark: { red: 0.50980392157, green: 0.54117647059, blue: 0.57647058824 },
|
188
|
+
fuji_grayscale_gandalf: { red: 0.59215686275, green: 0.61960784314, blue: 0.65882352941 },
|
189
|
+
fuji_grayscale_bob: { red: 0.69019607843, green: 0.72549019608, blue: 0.75686274510 },
|
190
|
+
fuji_grayscale_pebble: { red: 0.78039215686, green: 0.80392156863, blue: 0.82352941176 },
|
191
|
+
fuji_grayscale_dirty_seagull: { red: 0.87843137255, green: 0.89411764706, blue: 0.91372549020 },
|
192
|
+
fuji_grayscale_grey_hair: { red: 0.94117647059, green: 0.95294117647, blue: 0.96078431373 },
|
193
|
+
fuji_grayscale_marshmallow: { red: 0.96078431373, green: 0.97254901961, blue: 0.98039215686 },
|
194
|
+
fuji_grayscale_white: { red: 1.00000000000, green: 1.00000000000, blue: 1.00000000000 }
|
195
|
+
}.freeze.each_value(&:freeze)
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json_schemer'
|
4
|
+
|
5
|
+
module SheetsV4
|
6
|
+
# Creates a Google API credential with an access token
|
7
|
+
#
|
8
|
+
class CredentialCreator
|
9
|
+
# Creates a Google API credential with an access token
|
10
|
+
#
|
11
|
+
# This wraps the boiler plate code into one function to make constructing a
|
12
|
+
# credential easy and less error prone.
|
13
|
+
#
|
14
|
+
# @example Constructing a credential from the contents of ~/.credential
|
15
|
+
# credential_source = File.read(File.join(Dir.home, '.credential'))
|
16
|
+
# scope = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
|
17
|
+
# credential = GoogleApisHelpers.credential(credential_source, scope)
|
18
|
+
#
|
19
|
+
# @param [Google::Auth::*, String, IO, nil] credential_source may be one of four things:
|
20
|
+
# (1) a previously created credential that you want to reuse, (2) a credential read
|
21
|
+
# into a string, (3) an IO object with the credential ready to be read, or (4)
|
22
|
+
# if nill, the credential is read from ~/.google-api-credential.json
|
23
|
+
# @param scopes [Object, Array] one or more scopes to access.
|
24
|
+
# @param credential_factory [#make_creds] Used inject the credential_factory for tests
|
25
|
+
#
|
26
|
+
# @return [Object] a credential object with an access token
|
27
|
+
#
|
28
|
+
def self.call(
|
29
|
+
credential_source, scopes, credential_factory = Google::Auth::DefaultCredentials
|
30
|
+
)
|
31
|
+
return credential_source if credential?(credential_source)
|
32
|
+
|
33
|
+
credential_source ||= default_credential_source
|
34
|
+
options = make_creds_options(credential_source, scopes)
|
35
|
+
credential_factory.make_creds(options).tap(&:fetch_access_token)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Reads credential source from `~/.google-api-credential.json`
|
41
|
+
#
|
42
|
+
# @return [String] the credential as a string
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
#
|
46
|
+
private_class_method def self.default_credential_source
|
47
|
+
File.read(File.expand_path('~/.google-api-credential.json'))
|
48
|
+
end
|
49
|
+
|
50
|
+
# Constructs creds_options needed to create a credential
|
51
|
+
#
|
52
|
+
# @param [Google::Auth::*, String, #read] credential_source a credential (which
|
53
|
+
# is an object whose class is in the Google::Auth module), a String containing
|
54
|
+
# the credential, or a IO object with the credential ready to be read.
|
55
|
+
#
|
56
|
+
# @return [Hash] returns the cred_options
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
#
|
60
|
+
private_class_method def self.make_creds_options(credential_source, scopes)
|
61
|
+
{ json_key_io: to_io(credential_source), scope: scopes }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Wraps a credential_source that is a string in a StringIO
|
65
|
+
#
|
66
|
+
# @param [Google::Auth::*, String, #read] credential_source a credential (which
|
67
|
+
# is an object whose class is in the Google::Auth module), a String containing
|
68
|
+
# the credential, or a IO object with the credential ready to be read.
|
69
|
+
#
|
70
|
+
# @return [IO, StringIO] returns a StringIO object is a String was passed in.
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
#
|
74
|
+
private_class_method def self.to_io(credential_source)
|
75
|
+
credential_source.is_a?(String) ? StringIO.new(credential_source) : credential_source
|
76
|
+
end
|
77
|
+
|
78
|
+
# Determines if a credential_source is already a credential
|
79
|
+
#
|
80
|
+
# @param [Google::Auth::*, String, #read] credential_source a credential (which
|
81
|
+
# is an object whose class is in the Google::Auth module), a String containing
|
82
|
+
# the credential, or a IO object with the credential ready to be read.
|
83
|
+
#
|
84
|
+
# @return [Boolean] true if the credential source is an object whose class is in the
|
85
|
+
# Google::Auth module.
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
#
|
89
|
+
private_class_method def self.credential?(credential_source)
|
90
|
+
credential_source.class.name.start_with?('Google::Auth::')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json_schemer'
|
4
|
+
|
5
|
+
module SheetsV4
|
6
|
+
# Validate objects against a Google Sheets API request object schema
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
#
|
10
|
+
class ValidateApiObject
|
11
|
+
# Create a new validator
|
12
|
+
#
|
13
|
+
# By default, a nil logger is used. This means that no messages are logged.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# logger = Logger.new(STDOUT, :level => Logger::INFO)
|
17
|
+
# validator = SheetsV4::ValidateApiObject.new(logger)
|
18
|
+
#
|
19
|
+
# @param logger [Logger] the logger to use
|
20
|
+
#
|
21
|
+
def initialize(logger = Logger.new(nil))
|
22
|
+
@logger = logger
|
23
|
+
end
|
24
|
+
|
25
|
+
# The logger to use internally
|
26
|
+
#
|
27
|
+
# Validation errors are logged at the error level. Other messages are logged
|
28
|
+
# at the debug level.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# logger = Logger.new(STDOUT, :level => Logger::INFO)
|
32
|
+
# validator = SheetsV4::ValidateApiObject.new(logger)
|
33
|
+
# validator.logger == logger # => true
|
34
|
+
# validator.logger.debug { "Debug message" }
|
35
|
+
#
|
36
|
+
# @return [Logger]
|
37
|
+
#
|
38
|
+
attr_reader :logger
|
39
|
+
|
40
|
+
# Validate the object using the JSON schema named schema_name
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# schema_name = 'BatchUpdateSpreadsheetRequest'
|
44
|
+
# object = { 'requests' => [] }
|
45
|
+
# validator = SheetsV4::ValidateApiObject.new
|
46
|
+
# validator.call(schema_name, object)
|
47
|
+
#
|
48
|
+
# @param schema_name [String] the name of the schema to validate against
|
49
|
+
# @param object [Object] the object to validate
|
50
|
+
#
|
51
|
+
# @raise [RuntimeError] if the object does not conform to the schema
|
52
|
+
#
|
53
|
+
# @return [void]
|
54
|
+
#
|
55
|
+
def call(schema_name, object)
|
56
|
+
logger.debug { "Validating #{object} against #{schema_name}" }
|
57
|
+
|
58
|
+
schema = { '$ref' => schema_name }
|
59
|
+
schemer = JSONSchemer.schema(schema, ref_resolver:)
|
60
|
+
errors = schemer.validate(object)
|
61
|
+
raise_error!(schema_name, object, errors) if errors.any?
|
62
|
+
|
63
|
+
logger.debug { "Object #{object} conforms to #{schema_name}" }
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# The resolver to use to resolve JSON schema references
|
69
|
+
# @return [SchemaRefResolver]
|
70
|
+
# @api private
|
71
|
+
def ref_resolver = @ref_resolver ||= SchemaRefResolver.new(logger)
|
72
|
+
|
73
|
+
# Raise an error when the object does not conform to the schema
|
74
|
+
# @return [void]
|
75
|
+
# @raise [RuntimeError]
|
76
|
+
# @api private
|
77
|
+
def raise_error!(schema_name, object, errors)
|
78
|
+
error = errors.first['error']
|
79
|
+
error_message = "Object #{object} does not conform to #{schema_name}: #{error}"
|
80
|
+
logger.error(error_message)
|
81
|
+
raise error_message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Resolve JSON schema references to Google Sheets API schemas
|
86
|
+
#
|
87
|
+
# Uses the Google Discovery API to get the schemas. This is an implementation
|
88
|
+
# detail used to interact with JSONSchemer.
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
#
|
92
|
+
class SchemaRefResolver
|
93
|
+
# Create a new schema resolver
|
94
|
+
#
|
95
|
+
# @param logger [Logger] the logger to use
|
96
|
+
#
|
97
|
+
# @api private
|
98
|
+
#
|
99
|
+
def initialize(logger)
|
100
|
+
@logger = logger
|
101
|
+
end
|
102
|
+
|
103
|
+
# The logger to use internally
|
104
|
+
#
|
105
|
+
# Currently, only info messages are logged.
|
106
|
+
#
|
107
|
+
# @return [Logger]
|
108
|
+
#
|
109
|
+
# @api private
|
110
|
+
#
|
111
|
+
attr_reader :logger
|
112
|
+
|
113
|
+
# Resolve a JSON schema reference
|
114
|
+
#
|
115
|
+
# @param ref [String] the reference to resolve usually in the form "#/definitions/schema_name"
|
116
|
+
#
|
117
|
+
# @return [Hash] the schema object as a hash
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
def call(ref)
|
122
|
+
schema_name = ref.path[1..]
|
123
|
+
logger.debug { "Reading schema #{schema_name}" }
|
124
|
+
schema_object = self.class.api_object_schemas[schema_name]
|
125
|
+
raise "Schema for #{ref} not found" unless schema_object
|
126
|
+
|
127
|
+
schema_object.to_h.tap do |schema|
|
128
|
+
schema['unevaluatedProperties'] = false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# A hash of schemas keyed by the schema name loaded from the Google Discovery API
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# SheetsV4.api_object_schemas #=> { 'PersonSchema' => { 'type' => 'object', ... } ... }
|
136
|
+
#
|
137
|
+
# @return [Hash<String, Object>] a hash of schemas keyed by schema name
|
138
|
+
#
|
139
|
+
def self.api_object_schemas
|
140
|
+
schema_load_semaphore.synchronize { @api_object_schemas ||= load_api_object_schemas }
|
141
|
+
end
|
142
|
+
|
143
|
+
# Validate
|
144
|
+
# A mutex used to synchronize access to the schemas so they are only loaded
|
145
|
+
# once.
|
146
|
+
#
|
147
|
+
@schema_load_semaphore = Thread::Mutex.new
|
148
|
+
|
149
|
+
class << self
|
150
|
+
# A mutex used to synchronize access to the schemas so they are only loaded once
|
151
|
+
#
|
152
|
+
# @return [Thread::Mutex]
|
153
|
+
#
|
154
|
+
# @api private
|
155
|
+
#
|
156
|
+
attr_reader :schema_load_semaphore
|
157
|
+
end
|
158
|
+
|
159
|
+
# Load the schemas from the Google Discovery API
|
160
|
+
#
|
161
|
+
# @return [Hash<String, Object>] a hash of schemas keyed by schema name
|
162
|
+
#
|
163
|
+
# @api private
|
164
|
+
#
|
165
|
+
def self.load_api_object_schemas
|
166
|
+
source = 'https://sheets.googleapis.com/$discovery/rest?version=v4'
|
167
|
+
resp = Net::HTTP.get_response(URI.parse(source))
|
168
|
+
data = resp.body
|
169
|
+
JSON.parse(data)['schemas'].tap do |schemas|
|
170
|
+
schemas.each { |_name, schema| schema['unevaluatedProperties'] = false }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/sheets_v4/version.rb
CHANGED
data/lib/sheets_v4.rb
CHANGED
@@ -1,8 +1,101 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'sheets_v4/version'
|
4
|
+
require_relative 'sheets_v4/color'
|
5
|
+
require_relative 'sheets_v4/credential_creator'
|
6
|
+
require_relative 'sheets_v4/validate_api_object'
|
4
7
|
|
8
|
+
require 'google/apis/sheets_v4'
|
9
|
+
require 'json'
|
10
|
+
require 'logger'
|
11
|
+
require 'net/http'
|
12
|
+
|
13
|
+
# Unofficial helpers for the Google Sheets V4 API
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
#
|
5
17
|
module SheetsV4
|
6
|
-
|
7
|
-
#
|
18
|
+
# Create a new Google::Apis::SheetsV4::SheetsService object
|
19
|
+
#
|
20
|
+
# Simplifies creating and configuring a the credential.
|
21
|
+
#
|
22
|
+
# @example using the crednetial in `~/.google-api-credential`
|
23
|
+
# SheetsV4.sheets_service
|
24
|
+
#
|
25
|
+
# @example using a credential passed in as a string
|
26
|
+
# credential_source = File.read(File.join(Dir.home, '.credential'))
|
27
|
+
# SheetsV4.sheets_service(credential_source:
|
28
|
+
#
|
29
|
+
# @param credential_source [nil, String, IO, Google::Auth::*] may
|
30
|
+
# be either an already constructed credential, the credential read into a String or
|
31
|
+
# an open file with the credential ready to be read. Passing `nil` will result
|
32
|
+
# in the credential being read from `~/.google-api-credential.json`.
|
33
|
+
#
|
34
|
+
# @param scopes [Object, Array] one or more scopes to access.
|
35
|
+
#
|
36
|
+
# @param credential_creator [#credential] Used to inject the credential creator for
|
37
|
+
# testing.
|
38
|
+
#
|
39
|
+
# @return a new SheetsService instance
|
40
|
+
#
|
41
|
+
def self.sheets_service(credential_source: nil, scopes: nil, credential_creator: SheetsV4::CredentialCreator)
|
42
|
+
credential_source ||= File.read(File.expand_path('~/.google-api-credential.json'))
|
43
|
+
scopes ||= [Google::Apis::SheetsV4::AUTH_SPREADSHEETS]
|
44
|
+
|
45
|
+
Google::Apis::SheetsV4::SheetsService.new.tap do |service|
|
46
|
+
service.authorization = credential_creator.call(credential_source, scopes)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Validate the object using the named JSON schema
|
51
|
+
#
|
52
|
+
# The JSON schemas are loaded from the Google Disocvery API. The schemas names are
|
53
|
+
# returned by `SheetsV4.api_object_schemas.keys`.
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# schema_name = 'BatchUpdateSpreadsheetRequest'
|
57
|
+
# object = { 'requests' => [] }
|
58
|
+
# SheetsV4.validate_api_object(schema_name:, object:)
|
59
|
+
#
|
60
|
+
# @param schema_name [String] the name of the schema to validate against
|
61
|
+
# @param object [Object] the object to validate
|
62
|
+
# @param logger [Logger] the logger to use for logging error, info, and debug message
|
63
|
+
#
|
64
|
+
# @raise [RuntimeError] if the object does not conform to the schema
|
65
|
+
#
|
66
|
+
# @return [void]
|
67
|
+
#
|
68
|
+
def self.validate_api_object(schema_name:, object:, logger: Logger.new(nil))
|
69
|
+
ValidateApiObject.new(logger).call(schema_name, object)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Given the name of the color, return a Google Sheets API color object
|
73
|
+
#
|
74
|
+
# Available color names are listed using `SheetsV4.color_names`.
|
75
|
+
#
|
76
|
+
# The object returned is frozen. If you want a color you can change (for instance,
|
77
|
+
# to adjust the `alpha` attribute), you must clone the object returned.
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# SheetsV4::Color.color(:red) #=> { "red": 1.0, "green": 0.0, "blue": 0.0 }
|
81
|
+
#
|
82
|
+
# @param name [#to_sym] the name of the color requested
|
83
|
+
#
|
84
|
+
# @return [Hash] The color requested e.g. `{ "red": 1.0, "green": 0.0, "blue": 0.0 }`
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
#
|
88
|
+
def self.color(name)
|
89
|
+
SheetsV4::Color::COLORS[name.to_sym] || raise("Color #{name} not found")
|
90
|
+
end
|
91
|
+
|
92
|
+
# List the names of the colors available to use in the Google Sheets API
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# SheetsV4::Color.color_names #=> [:black, :white, :red, :green, :blue, :yellow, :magenta, :cyan, ...]
|
96
|
+
#
|
97
|
+
# @return [Array<Symbol>] the names of the colors available
|
98
|
+
# @api public
|
99
|
+
#
|
100
|
+
def self.color_names = SheetsV4::Color::COLORS.keys
|
8
101
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sheets_v4
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Couball
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler-audit
|
@@ -170,6 +170,20 @@ dependencies:
|
|
170
170
|
- - "~>"
|
171
171
|
- !ruby/object:Gem::Version
|
172
172
|
version: '7.0'
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: github_pages_rake_tasks
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - "~>"
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0.1'
|
180
|
+
type: :development
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - "~>"
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0.1'
|
173
187
|
- !ruby/object:Gem::Dependency
|
174
188
|
name: google-apis-sheets_v4
|
175
189
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,6 +212,20 @@ dependencies:
|
|
198
212
|
- - ">="
|
199
213
|
- !ruby/object:Gem::Version
|
200
214
|
version: '0'
|
215
|
+
- !ruby/object:Gem::Dependency
|
216
|
+
name: json_schemer
|
217
|
+
requirement: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - "~>"
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '2.0'
|
222
|
+
type: :runtime
|
223
|
+
prerelease: false
|
224
|
+
version_requirements: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - "~>"
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '2.0'
|
201
229
|
- !ruby/object:Gem::Dependency
|
202
230
|
name: rltk
|
203
231
|
requirement: !ruby/object:Gem::Requirement
|
@@ -219,6 +247,7 @@ executables: []
|
|
219
247
|
extensions: []
|
220
248
|
extra_rdoc_files: []
|
221
249
|
files:
|
250
|
+
- ".markdownlint.yml"
|
222
251
|
- ".rspec"
|
223
252
|
- ".rubocop.yml"
|
224
253
|
- ".yardopts"
|
@@ -227,7 +256,12 @@ files:
|
|
227
256
|
- LICENSE.txt
|
228
257
|
- README.md
|
229
258
|
- Rakefile
|
259
|
+
- examples/README.md
|
260
|
+
- examples/set_background_color
|
230
261
|
- lib/sheets_v4.rb
|
262
|
+
- lib/sheets_v4/color.rb
|
263
|
+
- lib/sheets_v4/credential_creator.rb
|
264
|
+
- lib/sheets_v4/validate_api_object.rb
|
231
265
|
- lib/sheets_v4/version.rb
|
232
266
|
homepage: https://github.com/main-branch/sheets_v4
|
233
267
|
licenses:
|