Skip to content

Commit

Permalink
Add extensions to Google::Apis::SheetsV4 classes (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouball committed Oct 16, 2023
1 parent ed2dc0e commit d8f695c
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 0 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Unofficial helpers for the Google Sheets V4 API
* [Method 2: constructing requests using hashes](#method-2-constructing-requests-using-hashes)
* [Which method should be used?](#which-method-should-be-used)
* [Validating requests](#validating-requests)
* [Google Extensions](#google-extensions)
* [SheetsService Extensions](#sheetsservice-extensions)
* [Spreadsheet Extensions](#spreadsheet-extensions)
* [Sheet Extensions](#sheet-extensions)
* [Working with dates and times](#working-with-dates-and-times)
* [Colors](#colors)
* [Development](#development)
Expand Down Expand Up @@ -274,6 +278,36 @@ request:
SheetsV4.validate_api_object(schema: 'batch_update_spreadsheet_request', object: requests)
```

### Google Extensions

The `SheetsV4::GoogleExtensions` module provides extensions to the `Google::Apis::SheetsV4`
classes to simplify use of the SheetsV4 API.

These extensions are not loaded by default and are not required to use other parts
of this Gem. To enable these extension, you must:

```Ruby
require 'sheets_v4/google_extensions'
```

#### SheetsService Extensions

Functionality is added to `get_spreadsheet` to set the `sheets_service` attribute on
the returned spreadsheet and set the `sheets_service` and `spreadsheet` attributes
on the sheets contained in the spreadsheet.

This can simplify complex spreadsheet updates because you won't have to pass a
sheets_service, spreadsheet, and sheet objects separately.

#### Spreadsheet Extensions

The `sheets_service` attribute is added and is set by `SheetsService#get_spreadsheet`.

#### Sheet Extensions

The `sheets_service` and `spreadsheet` attributes are added. Both are set when the
sheet's spreadsheet is loaded by `SheetsService#get_spreadsheet`.

### Working with dates and times

Google Sheets, similar to other spreadsheet programs, stores dates and date-time
Expand Down
42 changes: 42 additions & 0 deletions lib/sheets_v4/google_extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

module SheetsV4
# The Google extensions are additions directly to Google::Apis::SheetsV4 classes
#
# These additions are optional and provide convenience methods and attributes
# that simplify use of the Google Sheets API.
#
# To use these extensions, require the `sheets_v4/google_extensions` file.
#
# @example
# require 'sheets_v4/google_extensions'
#
module GoogleExtensions; end
end

require_relative 'google_extensions/sheets_service'
require_relative 'google_extensions/spreadsheet'
require_relative 'google_extensions/sheet'

# @private
module Google
module Apis
# Add SheetsV4 extensions to Google::Apis::SheetsV4 classes
module SheetsV4
# Add SheetsV4 extensions to Google::Apis::SheetsV4::SheetsService
class SheetsService
prepend ::SheetsV4::GoogleExtensions::SheetsService
end

# Add SheetsV4 extensions to Google::Apis::SheetsV4::Spreadsheet
class Spreadsheet
prepend ::SheetsV4::GoogleExtensions::Spreadsheet
end

# Add SheetsV4 extensions to Google::Apis::SheetsV4::Sheet
class Sheet
prepend ::SheetsV4::GoogleExtensions::Sheet
end
end
end
end
32 changes: 32 additions & 0 deletions lib/sheets_v4/google_extensions/sheet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (c) 2022 Yahoo
# frozen_string_literal: true

require 'google/apis/sheets_v4'
require 'googleauth'

module SheetsV4
module GoogleExtensions
# The SheetsService class implements handling credentials on top of the
# Google::Apis::SheetsV4::SheetsService class.
#
# @api public
#
module Sheet
# The sheets_service object used to create this sheet
#
# @example
# sheets_service = sheet.sheets_service
#
# @return [Google::Apis::SheetsV4::SheetsService]
attr_reader :sheets_service

# The spreadsheet object that contains this sheet
#
# @example
# spreadsheet = sheet.spreadsheet
#
# @return [Google::Apis::SheetsV4::Spreadsheet]
attr_reader :spreadsheet
end
end
end
99 changes: 99 additions & 0 deletions lib/sheets_v4/google_extensions/sheets_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright (c) 2022 Yahoo
# frozen_string_literal: true

module SheetsV4
module GoogleExtensions
# This module extends the `Google::Apis::SheetsV4::SheetsService` class to add
# attributes to the `Google::Apis::SheetsV4::Spreadsheet` and `Google::Apis::SheetsV4::Sheet`
# classes that reference the `SheetsService` instance used to retrieve them.
#
# Similarly, an attribute is added to the `Google::Apis::SheetsV4::Sheet` class
# that references the `Google::Apis::SheetsV4::Spreadsheet` instance that contains it.
#
# This allows getting the `SheetsService` object from a spreadsheet or sheet object,
# making it unnecessary to pass the `SheetsService` object around with the spreadsheet
# and its sheets.
#
# @example
# require 'sheets_v4/google_extensions'
# sheets_service = SheetsV4::SheetsService.new
# spreadsheet = sheets_service.get_spreadsheet('1nT_q0TrQzC3dLZXuI3K9V5P3mArBVZpVd_vRsOpvcyk')
#
# spreadsheet.sheets_service == sheets_service # => true
# spreadsheet.sheets.each do |sheet|
# sheet.sheets_service == sheets_service # => true
# sheet.spreadsheet == spreadsheet # => true
# end
#
# @api public
#
module SheetsService
# Replace the prepending class's `get_spreadsheet` implementation
#
# When this module is prepended to a class, class's `get_spreadsheet` method
# is replaced wity `new_get_spreadsheet` method from this module. The class's
# original `get_spreadsheet` method is renamed to `original_get_spreadsheet`.
#
# @example
# Google::Apis::SheetsV4::SheetsService.prepend(
# SheetsV4::GoogleExtensions::SheetsService
# )
#
# @return [void]
#
# @private
#
def self.prepended(prepended_to_class)
prepended_to_class.send(:alias_method, :original_get_spreadsheet, :get_spreadsheet)
prepended_to_class.send(:remove_method, :get_spreadsheet)
prepended_to_class.send(:alias_method, :get_spreadsheet, :new_get_spreadsheet)
end

# @!method get_spreadsheet(spreadsheet_id, include_grid_data, ranges, fields, quota_user, options, &block)
#
# @api public
#
# Gets an existing spreadsheet
#
# Creates a spreadsheet object by calling the original
# Google::Apis::SheetsV4::SheetsService#get_spreadsheet method and then does
# the following:
#
# * Sets the `sheets_service` attribute for the returned spreadsheet.
# * Sets the `sheets_service` and `spreadsheet` attributes all the sheets contained in the spreadsheet.
#
# See the documentation for Google::Apis::SheetsV4::SheetsService#get_spreadsheet for
# details on the parameters and return value.
#
# @example Get a spreadsheet object and output new attributes:
# require 'sheets_v4'
# require 'sheets_v4/google_extensions'
#
# sheets_service = SheetsV4::SheetsService.new
# spreadsheet_id = '1nT_q0TrQzC3dLZXuI3K9V5P3mArBVZpVd_vRsOpvcyk'
#
# spreadsheet = sheets_service.get_spreadsheet(spreadsheet_id)
#
# @return [Google::Apis::SheetsV4::Spreadsheet] the spreadsheet whose ID is `spreadsheet_id`

# Replaces the `get_spreadsheet` method implementation in the prepended class
#
# @example
# spreadsheet = sheets_service.new_get_spreadsheet(spreadsheet_id)
#
# @private
#
# @return [Google::Apis::SheetsV4::Spreadsheet]
#
def new_get_spreadsheet(...)
original_get_spreadsheet(...)&.tap do |spreadsheet|
spreadsheet.instance_variable_set(:@sheets_service, self)
spreadsheet.sheets.each do |sheet|
sheet.instance_variable_set(:@sheets_service, self)
sheet.instance_variable_set(:@spreadsheet, spreadsheet)
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/sheets_v4/google_extensions/spreadsheet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) 2022 Yahoo
# frozen_string_literal: true

require 'google/apis/sheets_v4'
require 'googleauth'

module SheetsV4
module GoogleExtensions
# The SheetsService class implements handling credentials on top of the
# Google::Apis::SheetsV4::SheetsService class.
#
# @api public
#
module Spreadsheet
# The sheets_service object used to create this spreadsheet
#
# @example
# sheets_service = spreadsheet.sheets_service
#
# @return [Google::Apis::SheetsV4::SheetsService]
attr_reader :sheets_service
end
end
end
68 changes: 68 additions & 0 deletions spec/sheets_v4/google_extensions/sheets_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

# Copyright (c) 2022 Yahoo

require 'sheets_v4/google_extensions'

RSpec.describe SheetsV4::GoogleExtensions::SheetsService do
let(:sheets_service_class) do
Class.new do
def get_spreadsheet(_spreadsheet_id)
# Empty method because it doesn't matter to the test
end
end.prepend(described_class)
end

let(:spreadsheet_class) do
Class.new do
attr_reader :spreadsheet_id
attr_reader :sheets

def initialize(spreadsheet_id, sheets)
@spreadsheet_id = spreadsheet_id
@sheets = sheets
end
end
end

let(:sheet_class) do
Class.new do
def initialize(id, title)
@properties = Struct.new(:id, :title).new(id, title)
end
end
end

let(:sheet1) { sheet_class.new(1, 'Sheet 1') }
let(:sheet2) { sheet_class.new(2, 'Sheet 2') }
let(:spreadsheet_id) { 'spreadsheet_id' }
let(:spreadsheet) { spreadsheet_class.new(spreadsheet_id, [sheet1, sheet2]) }
let(:sheets_service) { sheets_service_class.new }

context 'when prepended to a class' do
context 'when calling get_spreadsheet' do
before do
allow(sheets_service).to(
receive(:original_get_spreadsheet).with(spreadsheet_id).and_return(spreadsheet)
)
end

it 'should return the spreadsheet returned by calling original_get_spreadsheet ' do
expect(sheets_service.get_spreadsheet(spreadsheet_id)).to eq(spreadsheet)
end

it 'should set the @sheets_service instance variable of the returned spreadsheet' do
spreadsheet = sheets_service.get_spreadsheet(spreadsheet_id)
expect(spreadsheet.instance_variable_get(:@sheets_service)).to eq(sheets_service)
end

it 'should set the @sheets_service and @spreadsheet instance varaibles of the returned sheets' do
spreadsheet = sheets_service.get_spreadsheet(spreadsheet_id)
spreadsheet.sheets.each do |sheet|
expect(sheet.instance_variable_get(:@sheets_service)).to eq(sheets_service)
expect(sheet.instance_variable_get(:@spreadsheet)).to eq(spreadsheet)
end
end
end
end
end

0 comments on commit d8f695c

Please sign in to comment.