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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e8b306cf08149067ba4f5506c54c88df4e2af706f064b3b2b7cc7f5919991f4
4
- data.tar.gz: 438a64d88598542b9f9ed56552077836089e04b411aa8a4ceabf5279b131a393
3
+ metadata.gz: a6357385eeb33468356dd974f8fda49f92e09b30b9911a825c562509bf5f70dd
4
+ data.tar.gz: a289b95495fd49e8221a89e037168aaae903ca40af2f9167cff60c75c0f28acc
5
5
  SHA512:
6
- metadata.gz: 3728b1539d987a5aa7dfbbb45d0a9637d01c39d5fe3d8a3e19cd7206b9fab4b9ce1458d1f6e534e6ddb79d93d2792c93f940f3a800f2eb657647dd4dfdebceb9
7
- data.tar.gz: fe7d8f59b7663f76804d18e13ece431bdce418252efe51a2e50d28a5d3e5fe1c979b367287155032e82571e09c593bd9be2b40cc461901a4bf5bec728cfcc8e2
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
- - [SheetsV4](#sheetsv4)
13
- - [Installation](#installation)
14
- - [Usage](#usage)
15
- - [Development](#development)
16
- - [Contributing](#contributing)
17
- - [License](#license)
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 yard:audit yard:coverage bundle:audit build]
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
- # YARD
61
+ # yard:build
62
62
 
63
63
  require 'yard'
64
- YARD::Rake::YardocTask.new do |t|
65
- t.files = %w[lib/**/*.rb examples/**/*]
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
- # Yardstick
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
- # Yardstick coverage
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
@@ -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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SheetsV4
4
- VERSION = '0.1.1'
4
+ # The version of this gem
5
+ VERSION = '0.4.0'
5
6
  end
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
- class Error < StandardError; end
7
- # Your code goes here...
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.1.1
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-22 00:00:00.000000000 Z
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: