Google Apps Script (webapp) でログを記録する
Google Apps Script とログ
Google Apps Script (GAS) で、ログを書く時に長年 Logger.log
を使っていた。
スクリプトを実行し、メニューの 表示
→ ログ
でログを表示できる。
ただし、このログは自分でスクリプトを実行した時のログが表示できるだけで、例えば webapp としてデプロイしている時のログを見ることはできなかった。
Stackdriver Logging の有効化
実行中のログを見るためには Stackdriver Logging というものを利用する。
メニューの 表示
→ Stackdriver Logging
をクリックする。
紐付けができていない場合には以下のダイアログが表示されるので、 Google Cloud Platform でプロジェクトを作ってあげる必要がある。
https://console.cloud.google.com/ にアクセスし、左上のプロジェクト名をクリックし、新しいプロジェクトをクリックする。
適当にプロジェクト名を入力して、作成をクリックする。
するとプロジェクトが作成されるので、プロジェクト番号
をメモする。
GAS のエディタに戻り、メニューから リソース
→ Google Platform プロジェクト
をクリックする。
プロジェクトを紐付けるダイアログが表示されるので、プロジェクト番号を入力して プロジェクトを設定
をクリックする。
OAuth 同意画面を設定する必要があると表示されるので、 こちら
をクリックする。
User Type は 外部
しか選択できないので、外部を選択し、作成 をクリックする。
OAuth 同意画面の作成画面で アプリケーション名 だけ入れて 保存 をクリックする
GAS の先程のダイアログに戻って、もう一度 プロジェクトを設定
をクリックすると、今度は紐付けできる。
ログを出力する
ログを出力するときは Logger
ではなく console
を使う。
こんな感じのスクリプトを webapp として公開して、ブラウザでアクセスしてみる。
const doGet = (e) => { console.log(e); return ContentService.createTextOutput('ok'); }
ログを確認する
メニューの 表示
→ Stackdriver Logging
をクリックする。JSON がいい感じに展開されているのがわかる。
console.log
はログレベルが DEBUG
になる。
console.warn
や console.info
、console.error
なんかが使える。
おまけ
clasp を使わずに GAS を作ると、 Google Apps Script のログに「展開」というボタンが表示されて、そこからログが見られるような気がする。
clasp を使うとこれが表示されない。謎。
参考 URL
Node-RED Google Assistant Bridge 使ってみた
はじめに
我が家では Node-RED と Node-RED Alexa Home Skill Bridge を使って、家の家電を操作している。 これの Google Home 版である Node-RED Google Assistant Bridge が、公開されたので設定してみた。
基本的には 更新ドキュメント の通りにしている。
Node-RED Google Assistant Bridge の設定
Node-RED Alexa Home Skill Bridge にアカウントを登録する。 その後、適当にデバイスを登録する。 ここでは「リビングの電気」というデバイスを登録している。
日本語使ったら「絵文字を使うのは関心しないよ」みたいなことを言われるけど無視する。
Google Group に参加する
Google Assistant で使っている Google アカウントで以下のグループに参加する。
https://groups.google.com/forum/#!forum/node-red-google-home-bridge
アカウントをリンクする
Node-RED Google Assistant Bridge に自分のアカウントを教えて上げる必要があるので、 Google Home アプリでアカウントを認証する。
アプリを開いたら左上の「+」ボタンをタップし、「デバイスのセットアップ」をタップする。
次に「セットアップ済みのデバイスのリンク」をタップする。
次に「NR-GAB」をタップする。
*[test]
て書いてあるけど大丈夫か…?
Node-RED Alexa Home Skill Bridge の画面が開くので、 Node-RED Alexa Home Skill Bridge のアカウントで認証する。
トップページに「リビングの電気」が追加された。
Node-RED に node-red-google-home-bridge のインストール
まずはノードをインストールする。
我が家では Node-RED を IBM Cloud で動かしており、GUI からノードをインストールすることができないので、 package.json
に追記する。
$ npm install node-red-contrib-googlehome
おまけ: IBM Cloud にデプロイする。
$ bx login $ bx target -o 組織名 -s dev $ bx cf push
Node-RED 側の設定
google home ノードを適当に置き、ダブルクリックする。
「新規に google-home-conf を追加...」になっていることを確認し、鉛筆アイコンをクリックする。
Node-RED Google Assistant Bridge の ID とパスワードを渡すと、Device の一覧が取得できる。
適当にノードを配置する。
google home ノードに接続している switch の中身は以下のような感じにした。
設定完了
この状態で「オッケーグーグル、リビングの電気をオフ」と言うと、リビングの電気が消えるようになった。 Alexa でも Google Home でも家電が操作できるようになってライフチェンジングになった!
Ruboty で Redash のデータを通知する ruboty-redash 作った
Ruboty で Redash のデータを通知したい
Redash で数値を可視化している環境の場合、その最新の値を Slack で通知したい。 Ruboty を使って bot を運用しているので、 Ruboty プラグインとして Redash の値を取ってくるものを作った。
ruboty-redash
使い方
Ruboty にプラグインを追加して、以下の2つの環境変数を追加する。
REDASH_ROOT
… Ruboty に URL のルート (例:https://app.redash.io/
)REDASH_USER_APIKEY
… Redash のユーザの API キー
あとはチャット上で以下のようなコマンドを実行する。
@ruboty redash show <クエリID> <ERB メッセージフォーマット>
<クエリ ID>
は、Redash のクエリの画面を開いた時に URL に表示される ID のこと。
<ERB メッセージフォーマット>
は、メッセージのフォーマットを ERB で指定する。
例えば
@ruboty redash show 11 昨日の生産性スコアは <%= data['rows'][-1]['productivity_pulse'] %> です。
のようになる。
この data
のデータ構造の調べ方が少しややこしいので、以下で解説する。
data
のデータ構造の調べ方
Redash で該当のクエリのページを Google Chrome を表示する。
Developer Tools の Network タブを開いて query_results
でフィルタする。
その返ってきた JSON の Preview で query_result
配下の data
フィールドが data
変数に格納されるという仕組み。
query_result
の値を見ていることからわかるように、最後の実行結果を取得するという仕組みになっている。
そのため、一度も実行されていないクエリは ruboty-redash で取得することはできない。
また、クエリの実行は Redash 側でスケジュール化しておいて常に実行されるようにしておかないと値は最新化されない。
まとめ
Ruboty で Redash の値を通知できるような仕組みを作った。 本文中の「生産性スコア」が気になった方は Redash で作る「じぶんダッシュボード」 というタイトルで発表した - mallowlabsの備忘録 の記事も参考にして欲しい。
Global Media Controls で Alexa SPA を操作する Chrome 拡張を作った
モチベーション
音楽を聴くときは Amazon Echo のマルチルームオーディオを使っている。 いろんな部屋で同じ曲が流れるのはいい経験だが、曲名を確認したり、曲をスキップしたいときに、声を出さないといけない。 そこで https://alexa.amazon.co.jp/spa/index.html#player (以下 Alexa SPA) を使うと、Web ブラウザから曲名を確認したり、曲をスキップできて便利なので、合わせて使っている。
最近 Chrome 79 で YouTube Music を聴いてたら、Chrome のツールバーに音楽のツールバーが表示されるようになったことに気づいた。
調べたら Global Media Controls という言うらしい。Chrome 79 時点では実験的な実装とされている。 これを Alexa SPA でも使いたいと思ったので、 Chrome 拡張で実装してみた
alexa-spa-global-media-controls
Chrome ストアには上げていないので、試してみたい場合はクローンして、Chrome に読み込んで欲しい。
拡張をインストールした状態で https://alexa.amazon.co.jp/spa/index.html#player にアクセスして、Alexa で音楽を流せばツールバーに以下のように表示される。
実装の話
日本語のドキュメントが余りなかったのと、細かいところで結構ハマったので、ハマりどころをまとめる。
Chrome 79.0.3945.130
時点の話なので、今後改善する可能性は高い。
ツールバーに表示されない
基本的には MediaSession API を以下のように使うだけのはずである。
navigator.mediaSession.metadata = new MediaMetadata({ title: '曲名', artist: 'アーティスト名', album: 'アルバム名', artwork: [ { src:' アルバムアートの URL' } ] });
しかし、これを呼び出してもツールバーにボタンが表示されない。
調べてみると実際のタブで video
タグや audio
タグで実際に音声が再生されている必要があるらしい。
しかも、 5秒以上の音声ファイルを再生している必要があるらしい。
Alexa SPA では実際の音は Amazon Echo から流れるため、タブで音楽を再生することはできない。
そこで、無音の ogg ファイルを用意して、 DOM の状態を見て、再生したり停止するように実装した。
アルバム名の表示位置が変
MediaSession API に素直に album 属性を渡すと、ドメインの横に表示される。
YouTube Music はどうしてるのか見てみたら artist
属性にアルバム名を含めるようにしていた。
なんか釈然としないが、似たような感じで実装した。
navigator.mediaSession.metadata = new MediaMetadata({ title: '曲名 - アルバム名', artist: 'アーティスト名', artwork: [ { src:' アルバムアートの URL' } ] });
アルバムアートが表示されない
アルバムアートの URL に画像の URL を渡しているにも関わらず、画像が表示されない。
どうやら sizes
属性を指定しなければならないらしい。
navigator.mediaSession.metadata = new MediaMetadata({ title: '曲名 - アルバム名', artist: 'アーティスト名', artwork: [ { sizes: '412x412', src:' アルバムアートの URL' } ] });
artwork
には type
も指定できるが、こっちは不要らしい。
まとめ
Global Media Controls 個人的にはかなりアリなので、普及して欲しい。
参考 URL
Redash で作る「じぶんダッシュボード」 というタイトルで発表した
スライド
概要
Misoca 冬のLT大会 - connpass で私のダッシュボード事情について話してきた。
- Heroku と Google Spreadsheets を使うことで運用することで、維持費無料でダッシュボードを作る方法
- 運動量や睡眠、読書量や生産性とを可視化する方法
みたいな話を話せて楽しかった。
Misoca のコードレビューで教えてもらった RSpec マッチャまとめ
この記事は Misoca+弥生 Advent Calendar 2019 の7日目の記事です。
はじめに
Misoca に入社して1年とちょっとが経ちました。
Misoca は Ruby がメインの会社です。
私のキャリアはずっと Java だったので、特に RSpec の知識が貧弱で、RSpec は「expect
って書くのは知ってる…」ぐらいの知識でした。
RSpec のマッチャは数も多く、いったいどこから勉強したらよいのか…というままテストコードを書いたため、コードレビューでよりよい書き方を教えてもらいつつやってきました。
そこで、同じ指摘をもらわないように自分用にまとめつつ、同じような境遇の人に少しでも役に立つように、Misoca のコードレビューで教えてもらった RSpec の書き方を紹介します。
注意事項として、以下で書き換え前と後のコードが出てきますが、どちらが良い悪いという意図はありません。 実際、指摘をもらっても書き換えることもあれば、メリットがあればそのままにすることもありました。
be_xxx
target
が xxx? というメソッドを持っている場合、 be_xxx というマッチャが使えます。
例えば
expect(target.persisted?).to be_truthy
という example があったら
expect(target).to be_persisted
と書くことができます。
and
and
マッチャをつなげることで expect(target)
を何度も書かなくてすみます。
例えば
expect(target).to be_cool expect(target).to be_beautiful
という example があったら
expect(target).to be_cool .and be_beautiful
と書くことできます。
have_attributes
have_attributes
マッチャを使うことで複数の属性を持つことを一気にチェックできます。
例えば
expect(target.name).to eq('mallowlabs') expect(target.company).to eq('Misoca Inc.') expect(target.joined_at).to eq('2018-10-01')
という example があったら
expect(target).to have_attributes( name: 'mallowlabs', company: 'Misoca Inc.', joined_at: '2018-10-01' )
と書くことができます。
all
all
マッチャを使うことでコレクションの要素を一気にチェックできます。
targets.each do |target| expect(target).to be_ok end
という example があったら
expect(targets).to all(be_ok)
と書くことできます。
satisfy
satisfy
マッチャを使うことでブロックをマッチャに変換して all
マッチャに渡すことができます。
例えば
targets.each do |target| expect(target).not_to be_cool end
という example があったら
expect(targets).to all(satisfy { |t| !t.cool? })
と書くことができます。
contain_exactly
contain_exactly
マッチャを使うことでコレクションの中に要素がすべて含まれているかを確認できます。
コレクションの長さを確認しつつ、要素が意図したものが入っているかのチェックする際に
array = ['mallowlabs'] expect(array.size).to eq(1) expect(array.first).to eq('mallowlabs')
という example を書いていました。これは
array = ['mallowlabs'] expect(array).to contain_exactly('mallowlabs')
のように書けます。
他にも
いろんなマッチャを教えてもらいましたが、シンプルに説明できなかったので割愛します。
まとめ
洗練されていないコードを書いても dis られることなく、建設的な提案をしてくれる Misoca の開発メンバーのみんなに感謝です。
明日は id:ryotaway が「BrainF***処理系を作ってみます」という記事を買いてくれるそうです。楽しみですね。
RSpec の contain_exactly マッチャ と match_array マッチャ
contain_exactly マッチャ
`contain_exactly` matcher - Built in matchers - RSpec Expectations - RSpec - Relish
以下のようにコレクションに対して、順序は問わないがすべてが含まれているかをチェックすることができる。
expect([1, 2, 3]).to contain_exactly(2, 3, 1) # pass expect([:a, :c, :b]).to contain_exactly(:a, :c) # fail
match_array マッチャ
ほぼ同じようなマッチャとして match_array というのもある。 これは引数に配列を取るので注意する。
expect([1, 2, 3]).to match_array [2, 3, 1] # pass expect([:a, :c, :b]).to match_array [:a, :c] # fail
ソースコードを読む
これらのマッチャの動作を理解するために、まずは contain_exactly
のソースコードを読んでみた。
def match(_expected, _actual) return false unless convert_actual_to_an_array match_when_sorted? || (extra_items.empty? && missing_items.empty?) end def match_when_sorted? values_match?(safe_sort(expected), safe_sort(actual)) end
引数を配列に変換して、ソートしたあとで、それぞれマッチするかをチェックしている。
一方 match_array
は以下のような感じ。
def match_array(items) contain_exactly(*items) end
contain_exactly
呼んでた。
ということで contain_exactly
と match_array
は引数に配列を取るかどうか以外は同じ挙動をすることがわかった。
まとめ
RSpec は奥が深い。