sheets_v4 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60f10846ff0c3b62cb29c487499eb717897a2b28916606de7d3199fe0424435f
4
- data.tar.gz: a6bb345b4379f94dbaa82b266943c144321e3bcf3404db34c1788e766813a766
3
+ metadata.gz: b3ed35f7562643f623e68d6ad41c497f1d92ea7b2d77f4b6f0d5e02586109897
4
+ data.tar.gz: 6687dfc41d70870694a2085e749d803f7062de8d2486866e4e44ea54b1eec95f
5
5
  SHA512:
6
- metadata.gz: 6914986ccca02b55b260b2a39e6777ce5e2cbca03f6fd00053d8ed96a960456831ee008bd175448da343eeaf0bcfc8c67309e489035b9792ec4ce51aba29d917
7
- data.tar.gz: f76c250d9b9bec531ca6185ef3b2b351df6e0a12eccb1d6f862cf6a63d4b81305798630d992d3ec22fb44a94cbd488f3736e85da6ff72302eb7b7cf87017e176
6
+ metadata.gz: 00334f58b9080916f1239671e106292742a00bbd931582577ae8a0664c25ea83f1ecaf5154996a0f28deffe9e8aaf58e4963dd94160095853d7d3454ef9e1b82
7
+ data.tar.gz: a89714057c1aa145ce2b5eaa5a05776b8593c76d6e944b1b2b868b1ddd16a4247bf05ac13b218b74addb0204a3644cb6657e30e2331e725ca68467359cfd66a9
data/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ 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.7.0 (2023-10-08)
8
+
9
+ [Full Changelog](https://github.com/main-branch/sheets_v4/compare/v0.6.0..v0.7.0)
10
+
11
+ Changes since v0.6.0:
12
+
13
+ * 616fe1f Add conversions bewteen Date/DateTime and spreadsheet values (#22)
14
+ * 6f37337 Rename SheetsV4::ValidateApiObjects to SheetsV4::ApiObjectValidation (#21)
15
+ * 0f76992 Rename SheetsV4::ValidateApiObjects::Validate to SheetsV4::ValidateApiObjects::ValidateApiObject (#20)
16
+ * e80040c Rename SheetsV4::CredentialCreator to SheetsV4::CreateCredential (#19)
17
+
7
18
  ## v0.6.0 (2023-10-03)
8
19
 
9
20
  [Full Changelog](https://github.com/main-branch/sheets_v4/compare/v0.5.0..v0.6.0)
data/README.md CHANGED
@@ -25,6 +25,7 @@ Unofficial helpers for the Google Sheets V4 API
25
25
  * [Method 2: constructing requests using hashes](#method-2-constructing-requests-using-hashes)
26
26
  * [Which method should be used?](#which-method-should-be-used)
27
27
  * [Validating requests](#validating-requests)
28
+ * [Working with dates and times](#working-with-dates-and-times)
28
29
  * [Colors](#colors)
29
30
  * [Development](#development)
30
31
  * [Contributing](#contributing)
@@ -125,7 +126,7 @@ sheets_service = File.open('credential.json') do |credential_source|
125
126
  end
126
127
  ```
127
128
 
128
- or an already constructed `Google::Auth::*`` object.
129
+ or an already constructed `Google::Auth::*` object.
129
130
 
130
131
  ### Building a request
131
132
 
@@ -273,6 +274,69 @@ request:
273
274
  SheetsV4.validate_api_object(schema: 'batch_update_spreadsheet_request', object: requests)
274
275
  ```
275
276
 
277
+ ### Working with dates and times
278
+
279
+ Google Sheets, similar to other spreadsheet programs, stores dates and date-time
280
+ values as numbers. This system makes it easier to perform calculations with
281
+ dates and times.
282
+
283
+ This gem provides two sets of equavalent conversion methods. The first set is defined
284
+ as class methods on the `SheetsV4` class.
285
+
286
+ * `SheetsV4.date_to_gs(date)` returns a numeric cell value
287
+ * `SheetsV4.gs_to_date(cell_value)` returns a Date object
288
+ * `SheetsV4.datetime_to_gs(datetime)` returns a numeric cell value
289
+ * `SheetsV4.gs_to_datetime(cell_value)` returns a DateTime object
290
+
291
+ In order to convert to and from spreadsheet values, the spreadsheet timezone must
292
+ be known. A spreadsheet's timezone is found in the Google Sheets spreadsheet object's
293
+ properties:
294
+
295
+ ```Ruby
296
+ SheetsV4.default_spreadsheet_tz = spreadsheet.properties.time_zone
297
+ ```
298
+
299
+ If a time zone is not set using `SheetsV4.default_spreadsheet_tz`, a RuntimeError
300
+ will be raised when any of the above methods are used.
301
+
302
+ Here is an example of how the timezone can change the values fetched from the
303
+ spreadsheet:
304
+
305
+ ```Ruby
306
+ cell_value = 44333.191666666666
307
+
308
+ SheetsV4.default_spreadsheet_tz = 'America/New_York'
309
+ datetime = SheetsV4.gs_to_datetime(cell_value) #=> Mon, 17 May 2021 04:36:00 -0400
310
+ datetime.utc #=> 2021-05-17 08:36:00 UTC
311
+
312
+ SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
313
+ datetime = SheetsV4.gs_to_datetime(cell_value) #=> Mon, 17 May 2021 04:36:00 -0700
314
+ datetime.utc #=> 2021-05-17 11:36:00 UTC
315
+ ```
316
+
317
+ Valid time zone names are those listed in one of these two sources:
318
+
319
+ * `ActiveSupport::TimeZone.all.map { |tz| tz.tzinfo.name }`
320
+ * `ActiveSupport::TimeZone.all.map(&:name)`
321
+
322
+ The `SheetsV4` methods works well if the spreadsheet timezone is constant through
323
+ the run of the program. If this is not the case -- for instance when working with
324
+ multiple spreadsheets whose timezones may be different -- then use
325
+ `SheetsV4::ConvertDatesAndTimes`.
326
+
327
+ Each instance of `SheetsV4::ConvertDatesAndTimes` has it's own spreadsheet timezone
328
+ used in the conversions. Instance methods for this class are the same as the
329
+ date conversion methods on the SheetsV4 class.
330
+
331
+ Example:
332
+
333
+ ```Ruby
334
+ cell_value = 44333.191666666666
335
+ converter = SheetsV4::ConvertDatesAndTimes.new('America/Los_Angeles')
336
+ datetime = SheetsV4.gs_to_datetime(cell_value) #=> Mon, 17 May 2021 04:36:00 -0700
337
+ datetime.utc #=> 2021-05-17 11:36:00 UTC
338
+ ```
339
+
276
340
  ### Colors
277
341
 
278
342
  Color objects (with appropriate :red, :green, :blue values) can be retrieved by name
data/examples/README.md CHANGED
@@ -46,3 +46,4 @@
46
46
  * [ ] Protected ranges
47
47
  * [ ] Resize a sheet
48
48
  * [ ] Retrying on error
49
+ * [ ] Set a custom datetime or decimal format for a range [1](https://developers.google.com/sheets/api/samples/formatting#set_a_custom_datetime_or_decimal_format_for_a_range)
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SheetsV4
4
- module ValidateApiObjects
4
+ module ApiObjectValidation
5
5
  # Load the Google Discovery API description for the Sheets V4 API
6
6
  #
7
7
  # @example
8
8
  # logger = Logger.new(STDOUT, :level => Logger::ERROR)
9
- # schemas = SheetsV4::ValidateApiObjects::LoadSchemas.new(logger:).call
9
+ # schemas = SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call
10
10
  #
11
11
  # @api private
12
12
  #
@@ -18,7 +18,7 @@ module SheetsV4
18
18
  # The schemas are only loaded once and cached.
19
19
  #
20
20
  # @example
21
- # schema_loader = SheetsV4::ValidateApiObjects::LoadSchemas.new
21
+ # schema_loader = SheetsV4::ApiObjectValidation::LoadSchemas.new
22
22
  #
23
23
  # @param logger [Logger] the logger to use
24
24
  #
@@ -30,8 +30,8 @@ module SheetsV4
30
30
  #
31
31
  # @example
32
32
  # logger = Logger.new(STDOUT, :level => Logger::INFO)
33
- # validator = SheetsV4::ValidateApiObjects::LoadSchemas.new(logger)
34
- # validator.logger == logger # => true
33
+ # schema_loader = SheetsV4::ApiObjectValidation::LoadSchemas.new(logger)
34
+ # schema_loader.logger == logger # => true
35
35
  #
36
36
  # @return [Logger]
37
37
  #
@@ -84,7 +84,7 @@ module SheetsV4
84
84
  # Log an error and raise a RuntimeError based on the HTTP response code
85
85
  # @param http_response [Net::HTTPResponse] the HTTP response
86
86
  # @return [void]
87
- # @raises [RuntimeError]
87
+ # @raise [RuntimeError]
88
88
  # @api private
89
89
  def raise_error(http_response)
90
90
  message = "HTTP Error '#{http_response.code}' loading schemas from '#{http_response.uri}'"
@@ -146,7 +146,7 @@ module SheetsV4
146
146
  # @return [void]
147
147
  # @api private
148
148
  def post_process_schemas(schemas)
149
- SheetsV4::ValidateApiObjects::TraverseObjectTree.call(
149
+ SheetsV4::ApiObjectValidation::TraverseObjectTree.call(
150
150
  object: schemas, visitor: ->(path:, object:) { schema_visitor(path:, object:) }
151
151
  )
152
152
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SheetsV4
4
- module ValidateApiObjects
4
+ module ApiObjectValidation
5
5
  # Resolve a JSON schema reference to a Google Sheets API schema
6
6
  #
7
7
  # This class uses the Google Discovery API to get the schemas. Any schema reference
@@ -9,14 +9,14 @@ module SheetsV4
9
9
  # name in the Google Discovery API and returning the schema object (as a Hash).
10
10
  #
11
11
  # This means that `{ "$ref": "cell_data" }` is resolved by returning
12
- # `SheetsV4::ValidateApiObjects::LoadSchemas.new(logger:).call['cell_data']`.
12
+ # `SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call['cell_data']`.
13
13
  #
14
- # An RuntimeError is raised if `SheetsV4::ValidateApiObjects::LoadSchemas.new.call`
14
+ # An RuntimeError is raised if `SheetsV4::ApiObjectValidation::LoadSchemas.new.call`
15
15
  # does not have a key matching the schema name.
16
16
  #
17
17
  # @example
18
18
  # logger = Logger.new(STDOUT, level: Logger::INFO)
19
- # ref_resolver = SheetsV4::ValidateApiObjects::ResolveSchemaRef.new(logger:)
19
+ # ref_resolver = SheetsV4::ApiObjectValidation::ResolveSchemaRef.new(logger:)
20
20
  # people_schema = { 'type' => 'array', 'items' => { '$ref' => 'person' } }
21
21
  # json_validator = JSONSchemer.schema(people_schema, ref_resolver:)
22
22
  # people_json = [{ 'name' => { 'first' => 'John', 'last' => 'Doe' } }]
@@ -53,7 +53,7 @@ module SheetsV4
53
53
 
54
54
  # Resolve a JSON schema reference
55
55
  #
56
- # @param ref [URI] the reference to resolve usually in the form "json-schemer://schema/{name}"
56
+ # @param ref [URI] the reference to resolve usually in the form "json-schemer://schema/[name]"
57
57
  #
58
58
  # @return [Hash] the schema object as a hash
59
59
  #
@@ -62,7 +62,7 @@ module SheetsV4
62
62
  def call(ref)
63
63
  schema_name = ref.path[1..]
64
64
  logger.debug { "Reading schema #{schema_name}" }
65
- schemas = SheetsV4::ValidateApiObjects::LoadSchemas.new(logger:).call
65
+ schemas = SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call
66
66
  schemas[schema_name].tap do |schema_object|
67
67
  raise "Schema for #{ref} not found" unless schema_object
68
68
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SheetsV4
4
- module ValidateApiObjects
4
+ module ApiObjectValidation
5
5
  # Visit all objects in arbitrarily nested object tree of hashes and/or arrays
6
6
  #
7
7
  # @api public
@@ -16,7 +16,7 @@ module SheetsV4
16
16
  #
17
17
  # ```Ruby
18
18
  # visitor = -> (path:, object:) { puts "path: #{path}, object: #{obj}" }
19
- # SheetsV4::ValidateApiObjects::TraverseObjectTree.call(object:, visitor:)
19
+ # SheetsV4::ApiObjectValidation::TraverseObjectTree.call(object:, visitor:)
20
20
  # ```
21
21
  #
22
22
  # @example Given a simple object (not very exciting)
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/inflector'
5
+ require 'json_schemer'
6
+
7
+ module SheetsV4
8
+ module ApiObjectValidation
9
+ # Validate objects against a Google Sheets API request object schema
10
+ #
11
+ # @api public
12
+ #
13
+ class ValidateApiObject
14
+ # Create a new api object validator
15
+ #
16
+ # By default, a nil logger is used. This means that no messages are logged.
17
+ #
18
+ # @example
19
+ # validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
20
+ #
21
+ # @param logger [Logger] the logger to use
22
+ #
23
+ def initialize(logger: Logger.new(nil))
24
+ @logger = logger
25
+ end
26
+
27
+ # The logger to use internally
28
+ #
29
+ # Validation errors are logged at the error level. Other messages are logged
30
+ # at the debug level.
31
+ #
32
+ # @example
33
+ # logger = Logger.new(STDOUT, :level => Logger::INFO)
34
+ # validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger)
35
+ # validator.logger == logger # => true
36
+ # validator.logger.debug { "Debug message" }
37
+ #
38
+ # @return [Logger]
39
+ #
40
+ attr_reader :logger
41
+
42
+ # Validate the object using the JSON schema named schema_name
43
+ #
44
+ # @example
45
+ # schema_name = 'batch_update_spreadsheet_request'
46
+ # object = { 'requests' => [] }
47
+ # validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
48
+ # validator.call(schema_name:, object:)
49
+ #
50
+ # @param schema_name [String] the name of the schema to validate against
51
+ # @param object [Object] the object to validate
52
+ #
53
+ # @raise [RuntimeError] if the object does not conform to the schema
54
+ #
55
+ # @return [void]
56
+ #
57
+ def call(schema_name:, object:)
58
+ logger.debug { "Validating #{object} against #{schema_name}" }
59
+
60
+ schema = { '$ref' => schema_name }
61
+ schemer = JSONSchemer.schema(schema, ref_resolver:)
62
+ errors = schemer.validate(object)
63
+ raise_error!(schema_name, object, errors) if errors.any?
64
+
65
+ logger.debug { "Object #{object} conforms to #{schema_name}" }
66
+ end
67
+
68
+ private
69
+
70
+ # The resolver to use to resolve JSON schema references
71
+ # @return [ResolveSchemaRef]
72
+ # @api private
73
+ def ref_resolver = @ref_resolver ||= SheetsV4::ApiObjectValidation::ResolveSchemaRef.new(logger:)
74
+
75
+ # Raise an error when the object does not conform to the schema
76
+ # @return [void]
77
+ # @raise [RuntimeError]
78
+ # @api private
79
+ def raise_error!(schema_name, object, errors)
80
+ error = errors.first['error']
81
+ error_message = "Object #{object} does not conform to #{schema_name}: #{error}"
82
+ logger.error(error_message)
83
+ raise error_message
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SheetsV4
4
+ # Validate API Objects against the Google Discovery API
5
+ #
6
+ # @example
7
+ # logger = Logger.new(STDOUT, :level => Logger::ERROR)
8
+ # schema_name = 'batch_update_spreadsheet_request'
9
+ # object = { 'requests' => [] }
10
+ # SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger:).call(schema_name:, object:)
11
+ #
12
+ # @api public
13
+ #
14
+ module ApiObjectValidation; end
15
+ end
16
+
17
+ require_relative 'api_object_validation/load_schemas'
18
+ require_relative 'api_object_validation/resolve_schema_ref'
19
+ require_relative 'api_object_validation/traverse_object_tree'
20
+ require_relative 'api_object_validation/validate_api_object'
@@ -23,7 +23,6 @@ module SheetsV4
23
23
  #
24
24
  # @param method_name [#to_sym] the name of the color
25
25
  # @param arguments [Array] ignored
26
- # @param block [Proc] ignored
27
26
  # @return [Hash] the color object
28
27
  # @api private
29
28
  def method_missing(method_name, *arguments, &)
@@ -0,0 +1,243 @@
1
+ # Copyright (c) 2023 Yahoo
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'active_support'
6
+ require 'active_support/values/time_zone'
7
+ require 'active_support/core_ext/numeric/time'
8
+ require 'active_support/core_ext/string/zones'
9
+
10
+ module SheetsV4
11
+ # Convert between Ruby Date and DateTime objects and Google Sheets values
12
+ #
13
+ # Google Sheets uses decimal values to represent dates and times. These
14
+ # conversion routines allows converting from Ruby Date and DateTime objects
15
+ # and values that Google Sheets recognize.
16
+ #
17
+ # DateTime objects passed to `datetime_to_gs` or `date_to_gs` are converted to
18
+ # the time zone of the spreadsheet given in the initializer. DateTime objects
19
+ # returned by `gs_to_datetime` are always in the spreadsheet's time zone.
20
+ #
21
+ # Valid time zone names are those listed in one of these two sources:
22
+ # * `ActiveSupport::TimeZone.all.map { |tz| tz.tzinfo.name }`
23
+ # * `ActiveSupport::TimeZone.all.map(&:name)`
24
+ #
25
+ # @example
26
+ # tz = spreadsheet.properties.time_zone #=> e.g. 'America/Los_Angeles'
27
+ # converter = SheetsV4::ConvertDatesAndTimes.new(tz)
28
+ # date = Date.parse('1967-03-15')
29
+ # converter.date_to_gs(date) #=> 24546
30
+ # converter.gs_to_date(24546) #=> #<Date: 1967-03-15 ((2439565j,0s,0n),+0s,2299161j)>
31
+ #
32
+ # date_time = DateTime.parse('2021-05-17 11:36:00 UTC')
33
+ # converter.datetime_to_gs(date_time) #=> 44333.191666666666
34
+ # converter.gs_to_datetime(44333.191666666666)
35
+ # #=> #<DateTime: 2021-05-17T11:36:00+00:00 ((2459352j,41760s,0n),+0s,2299161j)>
36
+ #
37
+ # @api public
38
+ #
39
+ class ConvertDatesAndTimes
40
+ # The time zone passed into the initializer
41
+ #
42
+ # @example
43
+ # time_zone = 'UTC'
44
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
45
+ # converter.spreadsheet_tz
46
+ # #=> #<ActiveSupport::TimeZone:0x00007fe39000f908 @name="America/Los_Angeles", ...>
47
+ #
48
+ # @return [ActiveSupport::TimeZone] the time zone
49
+ #
50
+ attr_reader :spreadsheet_tz
51
+
52
+ # Initialize the conversion routines for a spreadsheet
53
+ #
54
+ # @example
55
+ # time_zone = 'America/Los_Angeles'
56
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
57
+ #
58
+ # @param spreadsheet_tz [String] the time zone set in the spreadsheet properties
59
+ #
60
+ # @raise [RuntimeError] if the time zone is not valid
61
+ #
62
+ def initialize(spreadsheet_tz)
63
+ @spreadsheet_tz = ActiveSupport::TimeZone.new(spreadsheet_tz)
64
+ raise "Invalid time zone '#{spreadsheet_tz}'" unless @spreadsheet_tz
65
+ end
66
+
67
+ # Convert a Ruby DateTime object to a Google Sheets date time value
68
+ #
69
+ # @example
70
+ # time_zone = 'America/Los_Angeles'
71
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
72
+ # date_time = DateTime.parse('2021-05-17 11:36:00 UTC')
73
+ # converter.datetime_to_gs(date_time) #=> 44333.191666666666
74
+ #
75
+ # @param datetime [DateTime, nil] the date and time to convert
76
+ #
77
+ # @return [Float, String] the value to store in a Google Sheets cell
78
+ # Returns a Float if datetime is not nil; otherwise, returns an empty string.
79
+ #
80
+ def datetime_to_gs(datetime)
81
+ return '' unless datetime
82
+
83
+ time = datetime.to_time.in_time_zone(spreadsheet_tz)
84
+ unix_to_gs_epoch(replace_time_zone(time, 'UTC').to_i)
85
+ end
86
+
87
+ # Convert a Google Sheets date time value to a DateTime object
88
+ #
89
+ # Time is rounded to the nearest second. The DateTime object returned is in
90
+ # the spreadsheet's time zone given in the initiaizer.
91
+ #
92
+ # @example
93
+ # time_zone = 'America/Los_Angeles'
94
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
95
+ # gs_value = 44333.191666666666
96
+ # converter.gs_to_datetime(gs_value) #=> #<DateTime: 2021-05-17T04:35:59-07:00 ...>
97
+ #
98
+ # @param gs_datetime [Float, "", nil] the value from the Google Sheets cell
99
+ #
100
+ # @return [DateTime, nil] the value represented by gs_datetime
101
+ # Returns a DateTime object if a Float was given; otherwise, returns nil if an
102
+ # empty string or nil was given.
103
+ #
104
+ def gs_to_datetime(gs_datetime)
105
+ return nil if gs_datetime.nil? || gs_datetime == ''
106
+
107
+ raise 'gs_datetime is a string' if gs_datetime.is_a?(String)
108
+
109
+ unix_epoch_datetime = gs_to_unix_epoch(gs_datetime.to_f)
110
+ time = Time.at_without_coercion(unix_epoch_datetime, in: 'UTC')
111
+ replace_time_zone(time, spreadsheet_tz).to_datetime
112
+ end
113
+
114
+ # Convert a Ruby Date object to a Google Sheets date value
115
+ #
116
+ # The Google Sheets date value is a float.
117
+ #
118
+ # @example with a Date object
119
+ # time_zone = 'America/Los_Angeles'
120
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
121
+ # date = Date.parse('2021-05-17')
122
+ # converter.date_to_gs(date) #=> 44333
123
+ #
124
+ # @example with a DateTime object
125
+ # time_zone = 'America/Los_Angeles'
126
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
127
+ # date_time = DateTime.parse('2021-05-17 11:36:00 UTC')
128
+ # converter.date_to_gs(date_time) #=> 44333
129
+ #
130
+ # @param date [DateTime, Date, nil] the date to convert
131
+ #
132
+ # @return [Float, String] the value to sstore in a Google Sheets cell
133
+ # Returns a Float if date is not nil; otherwise, returns an empty string
134
+ #
135
+ def date_to_gs(date)
136
+ return datetime_to_gs(date).to_i if date.is_a?(DateTime)
137
+
138
+ return (date - gs_epoch_start_date).to_i if date.is_a?(Date)
139
+
140
+ ''
141
+ end
142
+
143
+ # Convert a Google Sheets date value to a Ruby Date object
144
+ #
145
+ # @example with a Date value
146
+ # time_zone = 'America/Los_Angeles'
147
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
148
+ # gs_value = 44333
149
+ # converter.gs_to_date(gs_value) #=> #<Date: 2021-05-17 ...>
150
+ #
151
+ # @example with a Date and Time value
152
+ # time_zone = 'America/Los_Angeles'
153
+ # converter = SheetsV4::ConvertDatesAndTimes.new(time_zone)
154
+ # gs_value = 44333.191666666666
155
+ # converter.gs_to_date(gs_value) #=> #<Date: 2021-05-17 ...>
156
+ #
157
+ # @param gs_date [Float, "", nil] the value from the Google Sheets cell
158
+ #
159
+ # @return [Date, nil] the value represented by gs_date
160
+ # Returns a Date object if a Float was given; otherwise, returns nil if an
161
+ # empty string or nil was given.
162
+ #
163
+ def gs_to_date(gs_date)
164
+ return nil if gs_date.nil? || gs_date == ''
165
+
166
+ raise 'gs_date is a string' if gs_date.is_a?(String)
167
+
168
+ (gs_epoch_start_date + gs_date.to_i)
169
+ end
170
+
171
+ private
172
+
173
+ # The Google Sheets epoch start Date to use when calculating dates
174
+ # @return [Date] the 'zero' Date
175
+ # @api private
176
+ def gs_epoch_start_date
177
+ @gs_epoch_start_date ||= Date.parse('1899-12-30')
178
+ end
179
+
180
+ # The number of seconds in a day
181
+ #
182
+ # @return [Integer] number of seconds in a day
183
+ #
184
+ SECONDS_PER_DAY = 86_400
185
+
186
+ # The number of seconds between the Google Sheets Epoch and Unix Epoch
187
+ #
188
+ # Effectively the number of seconds between 1899-12-30 00:00:00 UTC and
189
+ # 1970-01-01 00:00:00 UTC.
190
+ #
191
+ # @return [Integer] the number of seconds
192
+ #
193
+ SECONDS_BETWEEN_GS_AND_UNIX_EPOCHS = 25_569 * 86_400
194
+
195
+ # Convert a gs_datetime to unix time
196
+ #
197
+ # @param gs_datetime [Float] time relative to the Google Sheets Epoch
198
+ #
199
+ # gs_datetime is a float representing the number of days since the start of
200
+ # the Google Sheets epoch (1899-12-30 00:00:00 UTC).
201
+ #
202
+ # @return [Integer] the same datetime in Unix Time rounded to the nearest second
203
+ #
204
+ # Unix time is an integer representing the number of seconds since the start of
205
+ # the Unix Epoch (1970-01-01 00:00:00 UTC). The number returned is rounded
206
+ # to the nearest second.
207
+ #
208
+ # @api private
209
+ #
210
+ def gs_to_unix_epoch(gs_datetime)
211
+ (gs_datetime * SECONDS_PER_DAY).round - SECONDS_BETWEEN_GS_AND_UNIX_EPOCHS
212
+ end
213
+
214
+ # Convert a unix time to gs_datetime
215
+ #
216
+ # @param unix_time [Integer] seconds since the Unix epoch 1970-01-01 00:00:00 UTC
217
+ #
218
+ # @return [Float] days since the Google Sheets epoch 1899-12-30 00:00:00 UTC
219
+ #
220
+ # @api private
221
+ #
222
+ def unix_to_gs_epoch(unix_time)
223
+ (unix_time + SECONDS_BETWEEN_GS_AND_UNIX_EPOCHS).to_f / SECONDS_PER_DAY
224
+ end
225
+
226
+ # Given a time, change the time zone without impacting the displayed date/time
227
+ #
228
+ # @example
229
+ # replace_time_zone(Time.parse('2021-05-21 11:40 UTC'), 'America/Los_Angeles')
230
+ # #=> '2021-05-21 11:40 -0700'
231
+ #
232
+ # @param time [Time] the time object to adjust
233
+ # @param time_zone_name [String] the desired time zone
234
+ #
235
+ # @return [Time] the resulting time object
236
+ #
237
+ # @api private
238
+ #
239
+ def replace_time_zone(time, time_zone_name)
240
+ time.asctime.in_time_zone(time_zone_name)
241
+ end
242
+ end
243
+ end
@@ -5,7 +5,7 @@ require 'json_schemer'
5
5
  module SheetsV4
6
6
  # Creates a Google API credential with an access token
7
7
  #
8
- class CredentialCreator
8
+ class CreateCredential
9
9
  # Creates a Google API credential with an access token
10
10
  #
11
11
  # This wraps the boiler plate code into one function to make constructing a
@@ -14,7 +14,7 @@ module SheetsV4
14
14
  # @example Constructing a credential from the contents of ~/.credential
15
15
  # credential_source = File.read(File.join(Dir.home, '.credential'))
16
16
  # scope = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
17
- # credential = GoogleApisHelpers.credential(credential_source, scope)
17
+ # credential = SheetsV4::CreateCredential.call(credential_source, scope)
18
18
  #
19
19
  # @param [Google::Auth::*, String, IO, nil] credential_source may be one of four things:
20
20
  # (1) a previously created credential that you want to reuse, (2) a credential read
@@ -1,21 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support'
3
4
  require 'active_support/inflector'
4
5
  require 'json_schemer'
5
6
 
6
7
  module SheetsV4
7
- module ValidateApiObjects
8
+ module ApiObjectValidation
8
9
  # Validate objects against a Google Sheets API request object schema
9
10
  #
10
11
  # @api public
11
12
  #
12
- class Validate
13
+ class ValidateApiObject
13
14
  # Create a new validator
14
15
  #
15
16
  # By default, a nil logger is used. This means that no messages are logged.
16
17
  #
17
18
  # @example
18
- # validator = SheetsV4::ValidateApiObjects::Validator.new
19
+ # validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
19
20
  #
20
21
  # @param logger [Logger] the logger to use
21
22
  #
@@ -30,7 +31,7 @@ module SheetsV4
30
31
  #
31
32
  # @example
32
33
  # logger = Logger.new(STDOUT, :level => Logger::INFO)
33
- # validator = SheetsV4::ValidateApiObjects::Validator.new(logger)
34
+ # validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger)
34
35
  # validator.logger == logger # => true
35
36
  # validator.logger.debug { "Debug message" }
36
37
  #
@@ -43,7 +44,7 @@ module SheetsV4
43
44
  # @example
44
45
  # schema_name = 'batch_update_spreadsheet_request'
45
46
  # object = { 'requests' => [] }
46
- # validator = SheetsV4::ValidateApiObjects::Validator.new
47
+ # validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
47
48
  # validator.call(schema_name:, object:)
48
49
  #
49
50
  # @param schema_name [String] the name of the schema to validate against
@@ -69,7 +70,7 @@ module SheetsV4
69
70
  # The resolver to use to resolve JSON schema references
70
71
  # @return [ResolveSchemaRef]
71
72
  # @api private
72
- def ref_resolver = @ref_resolver ||= SheetsV4::ValidateApiObjects::ResolveSchemaRef.new(logger:)
73
+ def ref_resolver = @ref_resolver ||= SheetsV4::ApiObjectValidation::ResolveSchemaRef.new(logger:)
73
74
 
74
75
  # Raise an error when the object does not conform to the schema
75
76
  # @return [void]
@@ -7,14 +7,14 @@ module SheetsV4
7
7
  # logger = Logger.new(STDOUT, :level => Logger::ERROR)
8
8
  # schema_name = 'batch_update_spreadsheet_request'
9
9
  # object = { 'requests' => [] }
10
- # SheetsV4::ValidateApiObjects::Validator.new(logger:).call(schema_name:, object:)
10
+ # SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger:).call(schema_name:, object:)
11
11
  #
12
12
  # @api public
13
13
  #
14
- module ValidateApiObjects; end
14
+ module ApiObjectValidation; end
15
15
  end
16
16
 
17
- require_relative 'validate_api_objects/load_schemas'
18
- require_relative 'validate_api_objects/resolve_schema_ref'
19
- require_relative 'validate_api_objects/traverse_object_tree'
20
- require_relative 'validate_api_objects/validate'
17
+ require_relative 'api_object_validation/load_schemas'
18
+ require_relative 'api_object_validation/resolve_schema_ref'
19
+ require_relative 'api_object_validation/traverse_object_tree'
20
+ require_relative 'api_object_validation/validate_api_object'
@@ -2,5 +2,5 @@
2
2
 
3
3
  module SheetsV4
4
4
  # The version of this gem
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0'
6
6
  end
data/lib/sheets_v4.rb CHANGED
@@ -2,9 +2,14 @@
2
2
 
3
3
  require_relative 'sheets_v4/version'
4
4
  require_relative 'sheets_v4/color'
5
- require_relative 'sheets_v4/credential_creator'
6
- require_relative 'sheets_v4/validate_api_objects'
5
+ require_relative 'sheets_v4/convert_dates_and_times'
6
+ require_relative 'sheets_v4/create_credential'
7
+ require_relative 'sheets_v4/api_object_validation'
7
8
 
9
+ require 'active_support'
10
+ require 'active_support/values/time_zone'
11
+ require 'active_support/core_ext/numeric/time'
12
+ require 'active_support/core_ext/string/zones'
8
13
  require 'google/apis/sheets_v4'
9
14
  require 'json'
10
15
  require 'logger'
@@ -15,102 +20,221 @@ require 'net/http'
15
20
  # @api public
16
21
  #
17
22
  module SheetsV4
18
- # Create a new Google::Apis::SheetsV4::SheetsService object
19
- #
20
- # Simplifies creating and configuring a the credential.
21
- #
22
- # @example using the credential 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.expand_path('~/.google-api-credential.json'))
27
- # SheetsV4.sheets_service(credential_source:)
28
- #
29
- # @example using a credential passed in as an IO
30
- # credential_source = File.open(File.expand_path('~/.google-api-credential.json'))
31
- # SheetsV4.sheets_service(credential_source:)
32
- #
33
- # @param credential_source [nil, String, IO, Google::Auth::*] may
34
- # be either an already constructed credential, the credential read into a String or
35
- # an open file with the credential ready to be read. Passing `nil` will result
36
- # in the credential being read from `~/.google-api-credential.json`.
37
- #
38
- # @param scopes [Object, Array] one or more scopes to access.
39
- #
40
- # @param credential_creator [#credential] Used to inject the credential creator for
41
- # testing.
42
- #
43
- # @return a new SheetsService instance
44
- #
45
- def self.sheets_service(credential_source: nil, scopes: nil, credential_creator: SheetsV4::CredentialCreator)
46
- credential_source ||= File.read(File.expand_path('~/.google-api-credential.json'))
47
- scopes ||= [Google::Apis::SheetsV4::AUTH_SPREADSHEETS]
48
-
49
- Google::Apis::SheetsV4::SheetsService.new.tap do |service|
50
- service.authorization = credential_creator.call(credential_source, scopes)
23
+ class << self
24
+ # Create a new Google::Apis::SheetsV4::SheetsService object
25
+ #
26
+ # Simplifies creating and configuring a the credential.
27
+ #
28
+ # @example using the credential in `~/.google-api-credential`
29
+ # SheetsV4.sheets_service
30
+ #
31
+ # @example using a credential passed in as a string
32
+ # credential_source = File.read(File.expand_path('~/.google-api-credential.json'))
33
+ # SheetsV4.sheets_service(credential_source:)
34
+ #
35
+ # @example using a credential passed in as an IO
36
+ # credential_source = File.open(File.expand_path('~/.google-api-credential.json'))
37
+ # SheetsV4.sheets_service(credential_source:)
38
+ #
39
+ # @param credential_source [nil, String, IO, Google::Auth::*] may
40
+ # be either an already constructed credential, the credential read into a String or
41
+ # an open file with the credential ready to be read. Passing `nil` will result
42
+ # in the credential being read from `~/.google-api-credential.json`.
43
+ #
44
+ # @param scopes [Object, Array] one or more scopes to access.
45
+ #
46
+ # @param credential_creator [#credential] Used to inject the credential creator for
47
+ # testing.
48
+ #
49
+ # @return a new SheetsService instance
50
+ #
51
+ def sheets_service(credential_source: nil, scopes: nil, credential_creator: SheetsV4::CreateCredential)
52
+ credential_source ||= File.read(File.expand_path('~/.google-api-credential.json'))
53
+ scopes ||= [Google::Apis::SheetsV4::AUTH_SPREADSHEETS]
54
+
55
+ Google::Apis::SheetsV4::SheetsService.new.tap do |service|
56
+ service.authorization = credential_creator.call(credential_source, scopes)
57
+ end
51
58
  end
52
- end
53
59
 
54
- # Validate the object using the named JSON schema
55
- #
56
- # The JSON schemas are loaded from the Google Disocvery API. The schemas names are
57
- # returned by `SheetsV4.api_object_schema_names`.
58
- #
59
- # @example
60
- # schema_name = 'batch_update_spreadsheet_request'
61
- # object = { 'requests' => [] }
62
- # SheetsV4.validate_api_object(schema_name:, object:)
63
- #
64
- # @param schema_name [String] the name of the schema to validate against
65
- # @param object [Object] the object to validate
66
- # @param logger [Logger] the logger to use for logging error, info, and debug message
67
- #
68
- # @raise [RuntimeError] if the object does not conform to the schema
69
- #
70
- # @return [void]
71
- #
72
- def self.validate_api_object(schema_name:, object:, logger: Logger.new(nil))
73
- SheetsV4::ValidateApiObjects::Validate.new(logger:).call(schema_name:, object:)
74
- end
60
+ # Validate the object using the named JSON schema
61
+ #
62
+ # The JSON schemas are loaded from the Google Disocvery API. The schemas names are
63
+ # returned by `SheetsV4.api_object_schema_names`.
64
+ #
65
+ # @example
66
+ # schema_name = 'batch_update_spreadsheet_request'
67
+ # object = { 'requests' => [] }
68
+ # SheetsV4.validate_api_object(schema_name:, object:)
69
+ #
70
+ # @param schema_name [String] the name of the schema to validate against
71
+ # @param object [Object] the object to validate
72
+ # @param logger [Logger] the logger to use for logging error, info, and debug message
73
+ #
74
+ # @raise [RuntimeError] if the object does not conform to the schema
75
+ #
76
+ # @return [void]
77
+ #
78
+ def validate_api_object(schema_name:, object:, logger: Logger.new(nil))
79
+ SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger:).call(schema_name:, object:)
80
+ end
75
81
 
76
- # List the names of the schemas available to use in the Google Sheets API
77
- #
78
- # @example List the name of the schemas available
79
- # SheetsV4.api_object_schema_names #=> ["add_banding_request", "add_banding_response", ...]
80
- #
81
- # @return [Array<String>] the names of the schemas available
82
- #
83
- def self.api_object_schema_names(logger: Logger.new(nil))
84
- SheetsV4::ValidateApiObjects::LoadSchemas.new(logger:).call.keys.sort
85
- end
82
+ # List the names of the schemas available to use in the Google Sheets API
83
+ #
84
+ # @example List the name of the schemas available
85
+ # SheetsV4.api_object_schema_names #=> ["add_banding_request", "add_banding_response", ...]
86
+ #
87
+ # @return [Array<String>] the names of the schemas available
88
+ #
89
+ def api_object_schema_names(logger: Logger.new(nil))
90
+ SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call.keys.sort
91
+ end
86
92
 
87
- # Given the name of the color, return a Google Sheets API color object
88
- #
89
- # Available color names are listed using `SheetsV4.color_names`.
90
- #
91
- # The object returned is frozen. If you want a color you can change (for instance,
92
- # to adjust the `alpha` attribute), you must clone the object returned.
93
- #
94
- # @example
95
- # SheetsV4::Color.color(:red) #=> { "red": 1.0, "green": 0.0, "blue": 0.0 }
96
- #
97
- # @param name [#to_sym] the name of the color requested
98
- #
99
- # @return [Hash] The color requested e.g. `{ "red": 1.0, "green": 0.0, "blue": 0.0 }`
100
- #
101
- # @api public
102
- #
103
- def self.color(name)
104
- SheetsV4::Color::COLORS[name.to_sym] || raise("Color #{name} not found")
105
- end
93
+ # Given the name of the color, return a Google Sheets API color object
94
+ #
95
+ # Available color names are listed using `SheetsV4.color_names`.
96
+ #
97
+ # The object returned is frozen. If you want a color you can change (for instance,
98
+ # to adjust the `alpha` attribute), you must clone the object returned.
99
+ #
100
+ # @example
101
+ # SheetsV4::Color.color(:red) #=> { "red": 1.0, "green": 0.0, "blue": 0.0 }
102
+ #
103
+ # @param name [#to_sym] the name of the color requested
104
+ #
105
+ # @return [Hash] The color requested e.g. `{ "red": 1.0, "green": 0.0, "blue": 0.0 }`
106
+ #
107
+ def color(name)
108
+ SheetsV4::Color::COLORS[name.to_sym] || raise("Color #{name} not found")
109
+ end
110
+
111
+ # List the names of the colors available to use in the Google Sheets API
112
+ #
113
+ # @example
114
+ # SheetsV4::Color.color_names #=> [:black, :white, :red, :green, :blue, :yellow, :magenta, :cyan, ...]
115
+ #
116
+ # @return [Array<Symbol>] the names of the colors available
117
+ #
118
+ def color_names = SheetsV4::Color::COLORS.keys
119
+
120
+ # @!attribute [rw] default_spreadsheet_tz
121
+ #
122
+ # Set the default time zone for date and time conversions
123
+ #
124
+ # Valid time zone names are those listed in one of these two sources:
125
+ # * `ActiveSupport::TimeZone.all.map { |tz| tz.tzinfo.name }`
126
+ # * `ActiveSupport::TimeZone.all.map(&:name)`
127
+ #
128
+ # If you want to set the timezone to the time zone of the system's local time,
129
+ # you could use the [timezone_local gem](https://rubygems.org/gems/timezone_local).
130
+ #
131
+ # @example
132
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
133
+ #
134
+ # @example Set the time zone to the system's local time
135
+ # require 'timezone_local'
136
+ # SheetsV4.default_spreadsheet_tz = TimeZone::Local.get.name
137
+ #
138
+ # @return [void]
139
+ #
140
+ def default_spreadsheet_tz
141
+ @default_spreadsheet_tz || raise('default_spreadsheet_tz not set')
142
+ end
143
+
144
+ def default_spreadsheet_tz=(time_zone)
145
+ raise "Invalid time zone '#{time_zone}'" unless ActiveSupport::TimeZone.new(time_zone)
106
146
 
107
- # List the names of the colors available to use in the Google Sheets API
108
- #
109
- # @example
110
- # SheetsV4::Color.color_names #=> [:black, :white, :red, :green, :blue, :yellow, :magenta, :cyan, ...]
111
- #
112
- # @return [Array<Symbol>] the names of the colors available
113
- # @api public
114
- #
115
- def self.color_names = SheetsV4::Color::COLORS.keys
147
+ @default_date_and_time_converter = nil unless @default_spreadsheet_tz == time_zone
148
+ @default_spreadsheet_tz = time_zone
149
+ end
150
+
151
+ # The default converter for dates and times
152
+ # @return [SheetsV4::ConvertDatesAndTimes]
153
+ # @api private
154
+ def default_date_and_time_converter
155
+ @default_date_and_time_converter ||= SheetsV4::ConvertDatesAndTimes.new(default_spreadsheet_tz)
156
+ end
157
+
158
+ # Convert a Ruby Date object to a Google Sheet date value
159
+ #
160
+ # Uses the time zone given by `SheetsV4.default_spreadsheet_tz`.
161
+ #
162
+ # @example with a Date object
163
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
164
+ # date = Date.parse('2021-05-17')
165
+ # SheetsV4.date_to_gs(date) #=> 44333
166
+ #
167
+ # @example with a DateTime object
168
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
169
+ # date_time = DateTime.parse('2021-05-17 11:36:00 UTC')
170
+ # SheetsV4.date_to_gs(date_time) #=> 44333
171
+ #
172
+ # @param date [DateTime, Date, nil] the date to convert
173
+ #
174
+ # @return [Float, String] the value to sstore in a Google Sheet cell
175
+ # Returns a Float if date is not nil; otherwise, returns an empty string
176
+ #
177
+ def date_to_gs(date)
178
+ default_date_and_time_converter.date_to_gs(date)
179
+ end
180
+
181
+ # Convert a Google Sheets date value to a Ruby Date object
182
+ #
183
+ # @example with a date value
184
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
185
+ # gs_value = 44333
186
+ # SheetsV4.gs_to_date(gs_value) #=> #<Date: 2021-05-17 ...>
187
+ #
188
+ # @example with a date and time value
189
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
190
+ # gs_value = 44333.191666666666
191
+ # SheetsV4.gs_to_date(gs_value) #=> #<Date: 2021-05-17 ...>
192
+ #
193
+ # @param gs_value [Float, "", nil] the value from the Google Sheets cell
194
+ #
195
+ # @return [Date, nil] the value represented by gs_date
196
+ #
197
+ # Returns a Date object if a Float was given; otherwise, returns nil if an
198
+ # empty string or nil was given.
199
+ #
200
+ def gs_to_date(gs_value)
201
+ default_date_and_time_converter.gs_to_date(gs_value)
202
+ end
203
+
204
+ # Convert a Ruby DateTime object to a Google Sheets value
205
+ #
206
+ # @example
207
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
208
+ # date_time = DateTime.parse('2021-05-17 11:36:00 UTC')
209
+ # SheetsV4.datetime_to_gs(date_time) #=> 44333.191666666666
210
+ #
211
+ # @param datetime [DateTime, nil] the date and time to convert
212
+ #
213
+ # @return [Float, String] the value to store in a Google Sheet cell
214
+ # Returns a Float if datetime is not nil; otherwise, returns an empty string.
215
+ #
216
+ def datetime_to_gs(datetime)
217
+ default_date_and_time_converter.datetime_to_gs(datetime)
218
+ end
219
+
220
+ # Convert a Google Sheets date time value to a DateTime object
221
+ #
222
+ # Time is rounded to the nearest second. The DateTime object returned is in
223
+ # the time zone given by `SheetsV4.default_spreadsheet_tz`.
224
+ #
225
+ # @example
226
+ # SheetsV4.default_spreadsheet_tz = 'America/Los_Angeles'
227
+ # gs_value = 44333.191666666666
228
+ # SheetsV4.gs_to_datetime(gs_value) #=> #<DateTime: 2021-05-17T04:35:59-07:00 ...>
229
+ #
230
+ # @param gs_value [Float, "", nil] the value from the Google Sheets cell
231
+ #
232
+ # @return [DateTime, nil] the value represented by gs_datetime
233
+ # Returns a DateTime object if a Float was given; otherwise, returns nil if an
234
+ # empty string or nil was given.
235
+ #
236
+ def gs_to_datetime(gs_value)
237
+ default_date_and_time_converter.gs_to_datetime(gs_value)
238
+ end
239
+ end
116
240
  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.6.0
4
+ version: 0.7.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-10-04 00:00:00.000000000 Z
11
+ date: 2023-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler-audit
@@ -260,13 +260,16 @@ files:
260
260
  - examples/set_background_color
261
261
  - examples/set_background_color2
262
262
  - lib/sheets_v4.rb
263
+ - lib/sheets_v4/api_object_validation.rb
264
+ - lib/sheets_v4/api_object_validation/load_schemas.rb
265
+ - lib/sheets_v4/api_object_validation/resolve_schema_ref.rb
266
+ - lib/sheets_v4/api_object_validation/traverse_object_tree.rb
267
+ - lib/sheets_v4/api_object_validation/validate_api_object.rb
263
268
  - lib/sheets_v4/color.rb
264
- - lib/sheets_v4/credential_creator.rb
269
+ - lib/sheets_v4/convert_dates_and_times.rb
270
+ - lib/sheets_v4/create_credential.rb
265
271
  - lib/sheets_v4/validate_api_objects.rb
266
- - lib/sheets_v4/validate_api_objects/load_schemas.rb
267
- - lib/sheets_v4/validate_api_objects/resolve_schema_ref.rb
268
- - lib/sheets_v4/validate_api_objects/traverse_object_tree.rb
269
- - lib/sheets_v4/validate_api_objects/validate.rb
272
+ - lib/sheets_v4/validate_api_objects/validate_api_object.rb
270
273
  - lib/sheets_v4/version.rb
271
274
  homepage: https://github.com/main-branch/sheets_v4
272
275
  licenses: