はじめに
みなさん、こんちは torihaziです。
最近、Ruby で Google API(google drive APIとgoogle sheet api)を触っているのですが
あんまりドキュメントなくて困ったのでこれを機にまとめてみようかと
追記 まだまとまっていないです。1章ではなぜ drive.authorizationには何を設定できるのか
そこを詳しく突き詰めるとこまでと list_filesが実際には何をしているかというところまでです。
- はじめに
- 使用するgemというか大元
- やっていき
- 探求の旅
- まず Google::Apis::RequestOptions.default.dup
- list_filesが何をしているのか
- まずはmake_simple_command
- verify_universe_domain! 何してるか。
- fullpath
- Templateって何。
- command
- HttpCommand
- ただいま、list_files
- execute_or_queue_command
- batch_command
- command.execute
- prepare!
- opencensus_begin_span
- do_retry
- execute_once
- apply_request_options(request_header)
- 結論
使用するgemというか大元
The client gems are named according to the pattern google-apis-<servicename>_<serviceversion>. For example, the client for the Google Drive V3 API is google-apis-drive_v3.
gemに命名規則があるそうなので調べてみましょう。

右上の Search に "drive"と入力してenter


左下に "google-apis-drive_v3"とかありますね。多分これです。
それか gemの一覧とかで調べられないのかな?
ruby でほしいgemがあるか調べたい! - Torihaji's Growth Diary
ということで "google-apis-drive_v3"を入れる。
入れました。
Installing faraday-follow_redirects 0.4.0 Installing google-cloud-env 2.3.1 Installing representable 3.2.0 Installing addressable 2.8.7 Fetching signet 0.21.0 Installing signet 0.21.0 Fetching googleauth 1.16.0 Installing googleauth 1.16.0 Fetching google-apis-core 1.0.2 Installing google-apis-core 1.0.2 Fetching google-apis-drive_v3 0.73.0 Installing google-apis-drive_v3 0.73.0
色々依存で入った。
- faraday-follow_redirects 0.4.0 HTTPリクエストライブラリ「Faraday」の拡張 リダイレクト(301/302など)を自動で追従
- google-cloud-env 2.3.1 Google Cloud環境の検出と設定 実行環境(GCP、ローカルなど)の判定と環境変数の取得
- representable 3.2.0 オブジェクトのシリアライズ/デシリアライズ JSON/XMLなどの変換を定義可能にする
- addressable 2.8.7 URI/URLの処理 パース、正規化、エンコード/デコード
- signet 0.21.0 OAuth署名の実装 Google APIの認証で使用
- googleauth 1.16.0 Google API認証 OAuth 2.0、サービスアカウント、アプリケーション認証情報の処理
- google-apis-core 1.0.2 Google APIクライアントの共通基盤 HTTP通信、エラーハンドリング、リトライなどの共通機能
- google-apis-drive_v3 0.73.0 Google Drive API v3のクライアント ファイルの取得・作成・更新・削除など
やっていき
では使っていきましょう。
それぞれがなんで必要かは使い慣れてからで。
さぁ rails cで色々触っていきましょう。
irb との違いは 以下。irbはrubyの純粋な対話型実行環境、rails cはRailsの機能を使える対話型実行環境
で以下とあるので使ってみましょう。
Then, to use it, require the file and instantiate the service. For example to use the Drive API:
app(dev)> client = Google::Apis::DriveV3::DriveService.new => #<Google::Apis::DriveV3::DriveService:0x0000ffffa1f83670
はいできました。
authorizationが何か知りませんが一旦やってみましょう。
files = client.list_files(q: "title contains 'finances'") app(dev)* files.items.each do |file| app(dev)* puts file.title app(dev)> end Sending HTTP get https://www.googleapis.com/drive/v3/files?q=title%20contains%20%27finances%27 403
実際にはHTTPリクエストを飛ばす上で、クエリを構築して送ってるらしい。
HTTPステータスコード403 Forbiddenは、リクエストされたページにアクセスする権限がないために、サーバーがリクエストを拒否した状態
{
"error": {
"code": 403,
"message": "Method doesn't allow unregistered callers (callers without established identity). Please use API Key or other form of API consumer identity to call this API.",
"errors": [
{
"message": "Method doesn't allow unregistered callers (callers without established identity). Please use API Key or other form of API consumer identity to call this API.",
"domain": "global",
"reason": "forbidden"
}
],
"status": "PERMISSION_DENIED"
}
}
claudeに聞いてみると
Google Drive APIへのリクエストに認証情報(APIキーまたはOAuthトークン)が含まれていない
まぁそうかと。
ではその認証情報をどうセットするのか。
drive.authorization = ... # See Googleauth or Signet libraries
これっぽい。
何をしているのか。元をのぞいてみる。
探求の旅
def initialize
super(DEFAULT_ENDPOINT_TEMPLATE, 'drive/v3/',
client_name: 'google-apis-drive_v3',
client_version: Google::Apis::DriveV3::GEM_VERSION)
@batch_path = 'batch/drive/v3'
end
近くにこんな記述が。
# @return [String]
# API key. Your API key identifies your project and provides you with API access,
# quota, and reports. Required unless you provide an OAuth 2.0 token.
attr_accessor :key
なるほど。API keyを与えるか OAuth 2.0 tokenを与える必要があるそうな。
今回はAPI keyを使わないので OAuth 2.0 tokenの路線で行く。
もし keyを使うなら
client = Google::Apis::DriveV3::DriveService.new client.key = your_api_key
みたいな感じにしたら使えるとみた。
はい、でこのtokenはどうやってやるのかな。
docに書いてない。わかりやすいとこにはないな。
drive.authorization = ... # See Googleauth or Signet libraries
とあるから多分これなんだろうけど。
書いておいてほしいな。
usage みたいな感じでデカデカと。
初学者にはきつい。
ということでこのauthorizationがなんなのか、掘ってみる。
先ほどと同じく画面右上の searchでauthorizationとすると

それだと
# @!attribute [rw] authorization
# @return [Signet::OAuth2::Client]
# OAuth2 credentials
def authorization=(authorization)
request_options.authorization = authorization
end
def authorization
request_options.authorization
end
これっぽい??
DriveはGoogle::Apis::Core::BaseServiceを継承していたし。
いやだとしても ???だな。
とりあえずrequest_options.authorizationに入れている。
ちなみにそれは親元のBaseServiceの
self.request_options = Google::Apis::RequestOptions.default.dup
を入れているらしい。
これも合わせて流し読み
1個ずつ倒していこう。
まず Google::Apis::RequestOptions.default.dup
# Request options
class RequestOptions
# @!attribute [rw] authorization
# @return [Signet::OAuth2::Client, #apply(Hash)] OAuth2 credentials.
# @!attribute [rw] retries
# @return [Integer] Number of times to retry requests on server error.
# @!attribute [rw] max_elapsed_time
# @return [Integer] Total time in seconds that requests are allowed to keep being retried.
# @!attribute [rw] base_interval
# @return [Float] The initial interval in seconds between tries.
# @!attribute [rw] max_interval
# @return [Integer] The maximum interval in seconds that any individual retry can reach.
# @!attribute [rw] multiplier
# @return [Numeric] Each successive interval grows by this factor. A multipler of 1.5 means the next interval
# will be 1.5x the current interval.
# @!attribute [rw] header
# @return [Hash<String,String>] Additional HTTP headers to include in requests.
# @!attribute [rw] normalize_unicode
# @return [Boolean] True if unicode strings should be normalized in path parameters.
# @!attribute [rw] skip_serialization
# @return [Boolean] True if body object should be treated as raw text instead of an object.
# @!attribute [rw] skip_deserialization
# @return [Boolean] True if response should be returned in raw form instead of deserialized.
# @!attribute [rw] api_format_version
# @return [Integer] Version of the error format to request/expect.
# @!attribute [rw] use_opencensus
# @return [Boolean] Whether OpenCensus spans should be generated for requests. Default is true.
# @!attribute [rw] quota_project
# @return [String] Project ID to charge quota, or `nil` to default to the credentials-specified project.
# @!attribute [rw] query
# @return [Hash<String,String>] Additional HTTP URL query parameters to include in requests.
# @!attribute [rw] add_invocation_id_header
# @return [Boolean] True if the header gccl-invocation-id need to be set
# @!attribute [rw] upload_chunk_size
# @return [Integer] The chunk size of storage upload. The default value is 100 MB.
# Get the default options
# @return [Google::Apis::RequestOptions]
def self.default
@options ||= RequestOptions.new
end
def merge(options)
return self if options.nil?
new_options = dup
members.each do |opt|
opt = opt.to_sym
new_options[opt] = options[opt] unless options[opt].nil?
end
new_options
end
end
dupは元のオブジェクトのコピーだから、良い。
とにかく、RequestOptionsとかいうものを作っているっぽい。
その中にいた
# @!attribute [rw] authorization
# @return [Signet::OAuth2::Client, #apply(Hash)] OAuth2 credentials.
そもそもこの書き方はなんだ
YARDというらしい。
これはこれでまた記事が書けそう
書き方が二つあって、こっちがdirectiveという書き方。属性とか実際のメソッドに与える引数とか書く時に使うものとして理解。
claudeった。
authorizationという読み書き可能な属性があり、それはOAuth2認証情報を保持するもので、Signet::OAuth2::Clientオブジェクトか、apply(Hash)メソッドを持つオブジェクトを返せるし、 authorizationに設定もできる
らしい。
あ、でもBaseServiceの方はSignet::OAuth2::Clientオブジェクト だけだな。
まぁいっか。
でこいつを入れて、どのようにhttpリクエスト時に使うんだろう。
とりあえずauthorizationというものを keyを設定しない限りは使う必要がありそうという事は覚えておこう。
今の結論
authorization: Signet::OAuth2::Client or apply(Hash)というメソッドがあるオブジェクト
で次だ。
さっき試しに使った list_filesが実際は何をしているのか・
これを突き詰めていけば、さっきのauthorizationの正体とかなぜ必要なのかもわかるはず。
list_filesが何をしているのか
さてまた検索する

正体はこれ。
def list_files(corpora: nil, corpus: nil, drive_id: nil, include_items_from_all_drives: nil, include_labels: nil, include_permissions_for_view: nil, include_team_drive_items: nil, order_by: nil, page_size: nil, page_token: nil, q: nil, spaces: nil, supports_all_drives: nil, supports_team_drives: nil, team_drive_id: nil, fields: nil, quota_user: nil, options: nil, &block)
command = make_simple_command(:get, 'files', options)
command.response_representation = Google::Apis::DriveV3::FileList::Representation
command.response_class = Google::Apis::DriveV3::FileList
command.query['corpora'] = corpora unless corpora.nil?
command.query['corpus'] = corpus unless corpus.nil?
command.query['driveId'] = drive_id unless drive_id.nil?
command.query['includeItemsFromAllDrives'] = include_items_from_all_drives unless include_items_from_all_drives.nil?
command.query['includeLabels'] = include_labels unless include_labels.nil?
command.query['includePermissionsForView'] = include_permissions_for_view unless include_permissions_for_view.nil?
command.query['includeTeamDriveItems'] = include_team_drive_items unless include_team_drive_items.nil?
command.query['orderBy'] = order_by unless order_by.nil?
command.query['pageSize'] = page_size unless page_size.nil?
command.query['pageToken'] = page_token unless page_token.nil?
command.query['q'] = q unless q.nil?
command.query['spaces'] = spaces unless spaces.nil?
command.query['supportsAllDrives'] = supports_all_drives unless supports_all_drives.nil?
command.query['supportsTeamDrives'] = supports_team_drives unless supports_team_drives.nil?
command.query['teamDriveId'] = team_drive_id unless team_drive_id.nil?
command.query['fields'] = fields unless fields.nil?
command.query['quotaUser'] = quota_user unless quota_user.nil?
execute_or_queue_command(command, &block)
end
なんか色々指定できるそうな。値設定したらそれをqueryに添えてやるらしい。
queryはその名の通り、?hoge=1&huga=1みたいに設定していく感じなのかな?
command = make_simple_command(:get, 'files', options) ~~~ execute_or_queue_command(command, &block)
これが何してるかだ。
まずはmake_simple_command

これが実態。
# Create a new command.
#
# @param [symbol] method
# HTTP method (:get, :post, :delete, etc...)
# @param [String] path
# Additional path, appended to API base path
# @param [Hash, Google::Apis::RequestOptions] options
# Request-specific options
# @return [Google::Apis::Core::DownloadCommand]
def make_simple_command(method, path, options)
verify_universe_domain!
full_path =
if path.start_with? "/"
path[1..-1]
else
base_path + path
end
template = Addressable::Template.new(root_url + full_path)
command = ApiCommand.new(method, template, client_version: client_version)
command.options = request_options.merge(options)
apply_command_defaults(command)
command
end
verify_universe_domain! 何してるか。
検索の画像載せるのがめんどくなってきた。
# Verify that the universe domain setting matches the universe domain
# in the credentials, if present.
#
# @raise [Google::Apis::UniverseDomainError] if there is a mismatch
def verify_universe_domain!
auth = authorization
auth_universe_domain = auth.universe_domain if auth.respond_to? :universe_domain
if auth_universe_domain && auth_universe_domain != universe_domain
raise UniverseDomainError,
"Universe domain is #{universe_domain} but credentials are in #{auth_universe_domain}"
end
true
end
authorizationはさっきのrequest_optionsで入れたやつ。
そこから そのauthに universe_domainメソッドがあったらなんかチェックして例外を投げて、
それ以外はtrueを返す。
まぁ多分そんな気にしなくて良いと思う。とりあえずtrue返すっていう事で次。
fullpath
full_path =
if path.start_with? "/"
path[1..-1]
else
base_path + path
end
pathっていうのは command = make_simple_command(:get, 'files', options)
なので 'files'。
という事でここでいうfull_pathは base_path + 'files'
base_pathがなんかはわからないけど、このgemはGoogle 公式が提供しているREST APIへの
アクセスをより簡素にしてくれてると思うから
Method: files.list | Google Drive | Google for Developers
結論、ここに飛ばしてくれてるはず。
なのでbase_pathは "https://www.googleapis.com/drive/v3" だと思う。
これに今回の 'files'をつけたら、それっぽくなるし。
Templateって何。
template = Addressable::Template.new(root_url + full_path)
これはなんだ。
Addressableというものをリポジトリ内でも検索してもなし。
外部のgemか?
なんかURLを構築してるのかな?
gemがあった。URL管理を便利にしてくれるやつっぽい。
次。
command
command = ApiCommand.new(method, template, client_version: client_version)
methodは httpメソッドがシンボルで渡される。templateは url、versoinは多分固定の文字列とか数値。
なので ApiComandが何してるかだけ気にすれば良い。
本体。
# @param [symbol] method
# HTTP method
# @param [String,Addressable::URI, Addressable::Template] url
# HTTP URL or template
# @param [String, #read] body
# Request body
def initialize(method, url, body: nil, client_version: nil)
super(method, url, body: body)
self.client_version = client_version || Core::VERSION
end
これをさらにsuperして親のものを使うと。
これ作った人よくこんな抽象化できるな。
すごいなー。
でこのsuperは HttpCommandなのでそれをみにいく。
module Google
module Apis
module Core
# Command for executing most basic API request with JSON requests/responses
class ApiCommand < HttpCommand
HttpCommand
# @param [symbol] method
# HTTP method
# @param [String,Addressable::URI, Addressable::Template] url
# HTTP URL or template
# @param [String, #read] body
# Request body
def initialize(method, url, body: nil)
self.options = Google::Apis::RequestOptions.default.dup
self.url = url
self.url = Addressable::Template.new(url) if url.is_a?(String)
self.method = method
self.header = Hash.new
self.body = body
self.query = {}
self.params = {}
@opencensus_span = nil
if OPENCENSUS_AVAILABLE
logger.warn 'OpenCensus support is now deprecated. ' +
'Please refer https://github.com/googleapis/google-api-ruby-client#tracing for migrating to use OpenTelemetry.'
end
end
なんかよくわからないけど色々入れてるみたい。
とりあえずこれがmake_simple_commandで帰ってくると。
でlist_filesに戻ろう。なんかqueryにhashで色々入れられるらしいね。
ただいま、list_files
def list_files(corpora: nil, corpus: nil, drive_id: nil, include_items_from_all_drives: nil, include_labels: nil, include_permissions_for_view: nil, include_team_drive_items: nil, order_by: nil, page_size: nil, page_token: nil, q: nil, spaces: nil, supports_all_drives: nil, supports_team_drives: nil, team_drive_id: nil, fields: nil, quota_user: nil, options: nil, &block)
command = make_simple_command(:get, 'files', options)
command.response_representation = Google::Apis::DriveV3::FileList::Representation
command.response_class = Google::Apis::DriveV3::FileList
command.query['corpora'] = corpora unless corpora.nil?
command.query['corpus'] = corpus unless corpus.nil?
command.query['driveId'] = drive_id unless drive_id.nil?
command.query['includeItemsFromAllDrives'] = include_items_from_all_drives unless include_items_from_all_drives.nil?
command.query['includeLabels'] = include_labels unless include_labels.nil?
command.query['includePermissionsForView'] = include_permissions_for_view unless include_permissions_for_view.nil?
command.query['includeTeamDriveItems'] = include_team_drive_items unless include_team_drive_items.nil?
command.query['orderBy'] = order_by unless order_by.nil?
command.query['pageSize'] = page_size unless page_size.nil?
command.query['pageToken'] = page_token unless page_token.nil?
command.query['q'] = q unless q.nil?
command.query['spaces'] = spaces unless spaces.nil?
command.query['supportsAllDrives'] = supports_all_drives unless supports_all_drives.nil?
command.query['supportsTeamDrives'] = supports_team_drives unless supports_team_drives.nil?
command.query['teamDriveId'] = team_drive_id unless team_drive_id.nil?
command.query['fields'] = fields unless fields.nil?
command.query['quotaUser'] = quota_user unless quota_user.nil?
execute_or_queue_command(command, &block)
end
で途中のcommandに設定するqueryは無視できるので、ここから次の相手。
execute_or_queue_command
execute_or_queue_command(command, &block)は何してるのか。
# Execute the request. If a batch is in progress, the request is added to the batch instead.
#
# @param [Google::Apis::Core::HttpCommand] command
# Command to execute
# @return [Object] response object if command executed and no callback supplied
# @yield [result, err] Result & error if block supplied
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
# @raise [Google::Apis::AuthorizationError] Authorization is required
def execute_or_queue_command(command, &callback)
batch_command = current_batch
if batch_command
fail "Can not combine services in a batch" if Thread.current[:google_api_batch_service] != self
batch_command.add(command, &callback)
nil
else
command.execute(client, &callback)
end
end
ようやくゴールが見えてきた気がする。
試しにさっきのlist_filesの例外を捕捉してなんのクラスだったかみてみよう。
app(dev)* begin app(dev)* files = client.list_files(q: "hoge") app(dev)* rescue => e app(dev)* pp e.class app(dev)* pp e.message app(dev)> end Google::Apis::ClientError "forbidden: Method doesn't allow unregistered callers (callers without established identity). Please use API Key or other form of API consumer identity to call this API."
およ、AuthorizationErrorじゃなかった。
まぁそもそもこのclientでどこにアクセスしたいのっていうのもわかりっこないからあれだけど
まぁいっか。先に進もう、いずれわかるでしょ。
batch_command
多分batchだったらみたいな感じかな。この場合は今回関係ないと思うから skip
command.execute
これが本体。
command.execute(client, &callback)
このコマンドというのはさっきのHttpCommandだから。
# Execute the command, retrying as necessary
#
# @param [Faraday::Connection] client
# Faraday connection
# @yield [result, err] Result or error if block supplied
# @return [Object]
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
# @raise [Google::Apis::AuthorizationError] Authorization is required
def execute(client, &block)
prepare!
opencensus_begin_span
do_retry :execute_once, client, &block
ensure
opencensus_end_span
@http_res = nil
release!
end
clientというはFaradayとかいうrubyのhttpクライアント。日本語あっているか知らないけど。
prepare!
# Prepare the request (e.g. calculate headers, add query params, serialize data, etc) before sending
#
# @private
# @return [void]
def prepare!
normalize_unicode = true
if options
header.update(options.header) if options.header
query.update(options.query) if options.query
normalize_unicode = options.normalize_unicode
end
self.url = url.expand(params, nil, normalize_unicode) if url.is_a?(Addressable::Template)
url.query_values = normalize_query_values(query).merge(url.query_values || {})
if allow_form_encoding?
@form_encoded = true
self.body = Addressable::URI.form_encode(url.query_values(Array))
self.header['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
else
@form_encoded = false
end
self.body = '' if self.body.nil? && [:post, :put, :patch].include?(method)
if defined?(::Google::Apis::Core::CompositeIO) && body.is_a?(::Google::Apis::Core::CompositeIO)
header["Content-Length"] ||= body.size.to_s
end
end
リクエストをこれまでの情報を元にセットするっぽい。
opencensus_begin_span
何してるかわからん。
do_retry
まだ認証情報セットしてるようには見えないな。
見逃したか??
def do_retry func, client
begin
Retriable.retriable tries: options.retries + 1,
max_elapsed_time: options.max_elapsed_time,
base_interval: options.base_interval,
max_interval: options.max_interval,
multiplier: options.multiplier,
on: RETRIABLE_ERRORS do |try|
# This 2nd level retriable only catches auth errors, and supports 1 retry, which allows
# auth to be re-attempted without having to retry all sorts of other failures like
# NotFound, etc
auth_tries = (try == 1 && authorization_refreshable? ? 2 : 1)
Retriable.retriable tries: auth_tries,
on: [Google::Apis::AuthorizationError, Signet::AuthorizationError, Signet::RemoteServerError, Signet::UnexpectedStatusError],
on_retry: proc { |*| refresh_authorization } do
send(func, client).tap do |result|
if block_given?
yield result, nil
end
end
end
end
rescue => e
if block_given?
yield nil, e
else
raise e
end
end
end
何してるの??
retryはしてそう。
けど やっているのは execute_onceだからそれをみることにする。
execute_once
# Execute the command once.
#
# @private
# @param [Faraday::Connection] client
# Faraday connection
# @return [Object]
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
# @raise [Google::Apis::AuthorizationError] Authorization is required
def execute_once(client)
body.rewind if body.respond_to?(:rewind)
begin
logger.debug { sprintf('Sending HTTP %s %s', method, url) }
request_header = header.dup
apply_request_options(request_header)
@http_res = client.run_request(method, url.to_s, body, request_header)
logger.debug { @http_res.status }
logger.debug { safe_single_line_representation @http_res }
response = process_response(@http_res.status.to_i, @http_res.headers, @http_res.body)
success(response)
rescue => e
logger.debug { sprintf('Caught error %s', e) }
error(e, rethrow: true)
end
end
あ、なんか見覚えあるlogger
logger.debug { sprintf('Sending HTTP %s %s', method, url) }
これで最初のログが出るのか。
app(dev)* begin app(dev)* files = client.list_files(q: "hoge") app(dev)* rescue => e app(dev)* pp e.class app(dev)* pp e.message app(dev)> end Sending HTTP get https://www.googleapis.com/drive/v3/files?q=hoge <= これ。 403
次。
apply_request_options(request_header)
これが何してるか。
# Update the request with any specified options.
# @param [Hash] req_header
# HTTP headers
# @return [void]
def apply_request_options(req_header)
if options.authorization.respond_to?(:apply!)
options.authorization.apply!(req_header)
elsif options.authorization.is_a?(String)
req_header['Authorization'] = sprintf('Bearer %s', options.authorization)
end
req_header.update(header)
end
あ、authorizationあったーーーーーーーー。
なるほど、ここで applyメソッドがあるかみてるな。
authorizationを見て、applyがあれば そのapply!メソッドを使って、多分 headerの値を書き換えて、
文字列であれば、AuthorizatoinヘッダーにBearerトークンを付与している。
あーだからこのままaccess_tokenとか付与してもいけるのか。
いけるのか??知らんけど。
とりあえずauthorizationに設定できるものは 2種類そう。
string(多分、token直渡し or apply!メソッドあるやつ)
あれでもSignetだっけ、あれはどこ行った??あれも apply!メソッdがあるのかな?
ていうかgoogle-apis-drive_v4 apiはhttpリクエスト自体にFaradayを使っているんだ。
Faraday使ったことないから詳しくは知らないけど。
なんかはてぶろが重くなってきたから次章へ。
結論
今回理解したのは
client = Google::Apis::DriveV3::DriveService.new files = client.list_files(q: "title contains 'finances'")
とかやっているけど裏でやっているのは
httpリクエストに クエリパラメータつけたりしてるだけ。
そのリクエストはFaradayを使っている。
drive.authorizationで指定できるのは 2つ
文字列なら AuthorizationヘッダでBearerトークン
apply!メソッドがあるオブジェクトを渡したらそれ専用の処理をする。
では次へ。