sheets_v4 0.1.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|