トライアンドエラーを回すときのコツ

自分が意識していることは次のとおりです。

  • まずは(半)手動で回せるようにする
  • 手動で回すのが面倒になってきたら自動化を考え始める
    • 原則として必要になるまでは自動化は考えない方向で
    • いきなり自動化すると見えなくなってくるものもある
  • 自動化の際はそのコストを考えて、手で回す方が総じてコストが安いならば、単純作業を我慢して手で回す
    • たとえば 1回 につき 30分 かかる試行が 10回 程度で完結する見込みならば、そのために数時間かけて自動化するのは総コスト高になる
    • ただし、自動化の過程で気づきや経験が得られるというメリットがある
    • 自動化が再利用できるタイプのものならば意義はある
  • 小さく部分に分けて回せるようにする
    • 1回 で 30分 の試行を回すならば、うまく部分を切り取って 5分 で 6回 回し、統合時に 30分 で回せるようにする

Heroku でデプロイ時にシェルスクリプトを実行するためには .profile を作って書く方法もある(問題点あり)

結論

標題通りです。

Procfilerelease タグで書いてもうまくいかないときもあります。これはずばり「リリースフェーズ中のファイルシステムの変更はアプリの Dyno formation にデプロイされない」ということが理由です*1

この場合は .procfile に書くという逃げ道があります。

問題点

.profile なので、たとえば $ heroku run bash でシェルに入るときに毎回実行されてしまう。したがって、内容は冪等になるものであり、かつ、時間がかからないものにしないといけない。

「あなたが今やっていることはちょっと変なことだよ」ということの証拠だと思うので、とりあえずこじんまりとまとめるべき。

補足

  • 実際問題として何に困ってこの方法にたどり着いたかというと、PostgreSQL に接続するために証明書がいるのですが、それは「ファイルのパス指定」でしか対象を指定できないことです*2
  • .profile の実行と Procfilerelease: の実行順序については、Procfile の方が先です*3
    • このため、いろいろと辛みがあることが存在する

ドキュメント

devcenter.heroku.com

*1:期待されているリリースフェイズでの挙動は、アセットのアップロードやデータベースの構築など、外部のリソースだけに関わるものでしょう

*2:生テキストなら環境変数などが使えた

*3:したがって、.profile で生成したファイルを用いて Procfile で何かをすることはできない

Ruby で Google::Apis::DriveV3 を用いて Google Drive のファイルを操作する方法

前提条件

  • サービスアカウントを用いて認証を行うとします

使う gem

  • google-api-client
    • google-apis-sheets_v4 でもいいですが、名前が適切な方が誰からも分かりやすいので google-api-client がよいと思います

注意事項

  • 当該ドライブ(フォルダ等)に、サービスアカウントによるアクセス権限を予め付与しておくこと*1
  • 認証コードでは require 'google/apis/drive_v3' を明示的に書くこと*2

具体的方法

認証

いつものやりかたです。スプレッドシートなどと同じです。

以下は概念的なコードですです。

require 'google/apis/drive_v3' # 重要

def create_api
  authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
    json_key_io: File.open('/path/to/credentials.json'),

    scope: %w[
      https://www.googleapis.com/auth/drive
      https://www.googleapis.com/auth/drive.file
    ]
  )
  authorizer.fetch_access_token!

  # https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/DriveV3/DriveService.html
  api = Google::Apis::DriveV3::DriveService.new
  api.authorization = authorizer

  api
end

特定のフォルダ直下のファイルやフォルダを取得する

ここが最大のハマりポイントです。

結論から書くとdrive_id を親として、その親の中のファイルやフォルダを『検索する』」方法により取得します。

api = create_api
listed_files = api.list_files(q: "'ドライブID' in parents")

これで Google::Apis::DriveV3::FileList のオブジェクトが得られます。その他の実行時オプションは都合に応じて用います。ただし、corpora を指定してしまうとなぜか drive_id が正しくても認証できません*3

ここさえできれば後は大丈夫でしょう。

listed_files の個々の中身を調べる

file_objects = listed_files.files

ファイルの mime_type を調べる

上記の file_objects の各要素である file_object には mime_type が定義されています。この mime_type により、そのオブジェクトがフォルダなのかファイルなのかを判別できます*4

mime_type には以下のような種類があります。この値で分岐を書けば、フォルダの中のフォルダを掘るようなコードはすぐ書けるでしょう*5

  • application/vnd.google-apps.folder
  • application/vnd.google-apps.spreadsheet
  • application/vnd.google-apps.document
  • image/png
  • image/jpeg

ファイルを作成(アップロード)する

以下のように書きます。オプションは他にもあるので後述のドキュメントを見ます。

api.create_file(
   {
     name: "file.txt",
     parents: [upload_folder_id], # 配列であることに注意
   },
   upload_source: '/path/to/sample.txt' 
 )

ファイルをダウンロードする

api.get_file(file_id, download_dest: '/tmp/foobar')

重要ドキュメント

以下のドキュメントは超重要です。これらを見れば全て書いてあります。

Google::Apis::DriveV3::DriveService

これが create_api で作られるインスタンスです。これが全てのベースです。

googleapis.dev

Google::Apis::DriveV3::FileList

list_files したときの戻り値のインスタンスが属するクラスです。

中身は次項の Class: Google::Apis::DriveV3::FileList のインスタンスが詰め込まれた配列になっています。

googleapis.dev

Google::Apis::DriveV3::File

Class: Google::Apis::DriveV3::FileList の中に詰め込まれている配列の各要素です。

たとえば以下のようなオブジェクトです。ここまで取れればあとはやりたい放題です。

   #<Google::Apis::DriveV3::File:0x00007f957321c630
    @id="1234567890",
    @kind="drive#file",
    @mime_type="image/jpeg",
    @name="my_photo.jpg">,

ファイル検索クエリ書式

ファイルを検索する際のクエリの書き方の説明です。これを見ないとファイル検索(絞り込み)が満足にできないので、一読しておいたほうがいいです。

developers.google.com

感想と結論

  • 上記のことがわかっていればだいたいのことはできる
  • しかし、コードがごちゃごちゃしてしまいそうだ
  • ラッパー gem を使いたくなるのも分かる。
    • が、だいぶメンテされていないようだし、共有フォルダ・共有ファイルを扱うにはこっちしかないっぽいと思うので、頑張ってこっちで書いたほうがいいと思う
  • ここまで面倒だと GAS でやったほうが楽なことが多いし、実際そうしている
    • GAS はドキュメントも多いし Clasp を使えばまあまあ楽

*1:忘れやすい

*2:Rails だとしても

*3:ここが最大のハマりポイント。サービスアカウントだからかも

*4:#kind というのがあるんですが、これは全て drive#file なので騙されてはいけません

*5:先ほど書いた、q: "'ドライブID' in parents" の「ドライブID」を「オブジェクトID」に変更すればいい

Powered by はてなブログ