Google Apps Scriptを正確なスケジュールで実行する
課題
- GASで「毎日 HH:MM に実行」といった感じのタスクスケジューリングをしたい
- トリガー「時間主導型/日タイマー」だと「午前8時〜9時」のような、ざっくりとした指定しかできない
- 「時間主導型/特定の日時」は
YYYY-MM-DD HH:MM
で指定ができ、指定した時間に正確に実行してくれるが、日時のピンポイント指定のみで条件指定ができないため、1実行につき1件のトリガーを作成しなければならず、運用するのは非現実的
ソリューション
「特定の日時」トリガーを動的に生成するタスクを「日タイマー」トリガーで実行する
サンプルコード
- 下の例では
createTriggers()
で、土日祝を除く毎日、12:00、13:00、14:00にmain()
を実行するためのトリガーを生成する - 生成された
main()
のトリガーは実行が完了した後も残り続けるため、ScriptApp.deleteTrigger()
で掃除する- 削除する際に、
createTriggers()
のトリガーを一緒に削除しないよう、trigger.getHandlerFunction() === 'main’
のものに絞って削除を行う
- 削除する際に、
createTriggers()
を実行するためのトリガーを「時間主導型/日タイマー/午前10時〜11時」で作成する
function isHoliday(date) { if (date.getDay() === 0 || date.getDay() === 6) { return true } // 日本の祝日カレンダーに終日予定があれば祝日とする var calendar = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com') var events = calendar.getEventsForDay(date) return events.length > 0 } function createTriggers() { console.log('createTriggers') var now = new Date() // 残っているトリガーを掃除する var triggers = ScriptApp.getProjectTriggers() if (Array.isArray(triggers)) { triggers.forEach(function(trigger) { // mainのトリガーのみを削除する if(trigger.getHandlerFunction() === 'main') { ScriptApp.deleteTrigger(trigger); } }) } // 土日祝はスケジュールしない if (isHoliday(now)) { console.log('there are no schedules today') return } var hours = [12, 13, 14] hours.forEach(function(hour) { var date = new Date() date.setHours(hour) date.setMinutes(0) if (now.valueOf() < date.valueOf()) { // main() のトリガーを指定した日時で作成 ScriptApp.newTrigger("main").timeBased().at(date).create() } }) } function main() { console.log('hello.') }
実行結果
createTriggers()
が日タイマーで実行された結果、11:00、12:00、13:00 のトリガーが生成されている。
(なぜか並び順は滅茶苦茶だが...)
各トリガーの実行結果。
秒はまちまちだが、時/分までは指定した時刻に実行されているのがわかる。
(GASで console.log()
した内容はStackdriver Loggingで確認できる)
まとめ
- GASを正確に定期実行したい場合、デフォルトで用意されている時間主導型トリガーのタイマーではなく、
ScriptApp.newTrigger()
を使用する - 生成したトリガーは実行された後も残るので、次のトリガー生成のタイミングにリセットが必要
iPhone X ファーストインプレ
iPhone X、11/3(金)に入手することができた。
Apple表参道にピックアップに行ったが、3日の18時すぎだったので受け取りはスムーズに完了した。
忘れないうちにインプレを記しておく。
気づいた点
- ホームボタンないのは1日で慣れる
- 3日経つと「最初からホームボタンなどいらなかったのでは?」という気持ち
- Control Center、右上から引っ張り出さないとならないのは少し遠く感じる
- Face IDはコツさえつかめば十分Touch IDを置き換えられる
- キッチンやお風呂で指が濡れてても使える
- 真っ暗な場所でも動作する
- ただし、横になっているときは失敗しやすい。要因の一つとして、横になっているときは顔と画面の距離が近い。気持ち遠くで構えることで成功率を高められる
- Face ID認証後、ロック画面でスワイプしないとロック解除されないのが不便。通知を見逃さないためには仕方ないと思うが、Touch IDのときのように認証完了後ダイレクトにホーム画面に遷移するオプションがほしい
- 対策としては、Face IDの認証が成功することを見込んで、スリープ解除したらすぐにスワイプしておく
- OLEDはなんといっても黒
- ディスプレイサイズ
- 8 Plusに比べて広くなった感じはないが、縦の情報量が増えたのでスクロールを要する画面は快適になった
- 上部の欠けは使ってるとそれほど気にならない
- カメラ
- ずっしり重い
- 8 Plusよりは軽いが、比較的コンパクトなフットプリントのおかげかずっしり重く感じる
- 4.7インチのiPhoneを使っていた人は重く感じるかも
- ワイヤレス充電
- オフィシャルのレザーケースを着けているが、PanasonicやAnkerのQi充電器で問題なく充電を行えた
- シルバーを選んだが、クロムメッキされたステンレスフレームは初代〜3GSや初期のiPod Touchを思い起こさせる
まとめ
- Plus系からのアップグレードだと、機能追加とコンパクトさを同時に手に入れられて満足度が高い。
- 操作系に対する懸念はあったが、3日で慣れた。ただし、従来のものと全く別の操作を要求されることもある(Control Centerなど)ので、プライベートがX、仕事用は8などの複数台持ちをすると少しストレスがかかりそう
- 黒が黒であることのありがたみ。テレビもそのうち有機ELにしたい
teenage engineering OP-1
シーケンサを作っていた間は、影響をモロに受けてしまいそうなので我慢していたOP-1を、シーケンサの制作が一段落ついたこともあって買った。
触ってみるとシーケンサにあたるものはおまけ程度で、メインはシンセサイザー or ドラムキット(それに、FX/EQ)と、4トラックのレコーダー。
初見だと何をしていいのか戸惑うUIだが、OP-1の設計思想が頭に入ると腑に落ちる。
各インストゥルメント、エフェクトのビジュアライズが個別に用意されていて楽しい。そういうところに発揮できるユーモアと、それを実装できる余裕は持っていたいなと思う。
シャーシは切削されたアルミでそこそこ重量はあるので、見た目ほど持ち運びに適した感じではないのだけど、コンピュータの電源を入れずに音を作って遊べるので、旅行に持っていって移動中や宿でのんびりするときに弄るのがよさそうだ。
Reactでパターンシーケンサを作った
以前、シンセサイザーを作ったが、
楽器が弾けないのでブラウザに自動演奏してもらうことにした。
Flowtype
シーケンスや音源のプリセットにflowtypeのアノテーションを使ったが、内部で生まれては消えるようなデータならわざわざclassを定義するまでもないし、Flowtypeなら必要な部分にだけ適用することができるので、導入したことで効率的に開発を進めることができた。
Material-UI
本当はGrommetのようなメリハリの効いたのが使いたかったんだけど、コンポーネントのバリエーションが足りなくて今回もMaterial-UIを使った。
とはいえ、Material-UIにも楽器を作る上で必要なコンポーネントが全て揃っているわけではないので、楽器のためのUIコンポーネントライブラリを作ってみるのもアリかもしれない。
音を鳴らすしくみ
肝心の音を鳴らすしくみについては
リファクタリングしながら書いていこうと思う。
追記: 2017/10/27
記事公開されました
Kubernetesのsecretsをファイルから設定する
Google Container Engine から接続する | Cloud SQL for MySQL | Google Cloud Platform
GKEからCloud SQL Proxyを使うにあたり、公式ドキュメントの方法で接続用のユーザアカウントをk8sのsecretsに登録していたのだが、件数が多い場合にコマンドで都度設定するのは億劫だなと思っていたら、公式に方法が記載されていた。
下記のような形式でyamlを書いて $ kubectl create -f ./secret.yaml
で作成。
apiVersion: v1 kind: Secret metadata: name: [SECRET_NAME] type: Opaque data: username: [BASE64_ENCODED_USER_NAME] password: [BASE64_ENCODED_PASSWORD]
一点気をつけたいのが、
Each item must be base64 encoded:
とあるように、値はBase64エンコードされている必要がある。
おまけ: 設定した値をyamlにエクスポートする
(secretsに限らないが)get
に -o yaml
オプションを指定すれば設定した値をファイルに書き出しておける。
$ kubectl get secrets [SECRET_NAME] -o yaml > path_to.yaml
Container ComponentをTypeScriptで書く
react-reduxのContainer ComponentをTypeScriptで書き直した。
嫌いじゃないが、もう少しシンプルにならないものか。
途中でAtomからVS Codeに切り替えたが、
特にカスタマイズをしなくてもTypeScriptに対応していたり、
IntelliSenseがpropsやstateに対して効いてくれるのは助かる。
しばらく使ってみようと思う。
gistdcbb4d372e5fde5018a3c2a717fec90c
参考
Typesafe Container Components with React-Redux’s Connect and TypeScript
はじめてのReact VR
Reactのコンポーネントの中にthree.jsのsceneを配置したら面白いんじゃないか…
というようなことを考えていたらReact VRとやらが登場していたので少し触ってみた。
DEMO nishaya/my-first-react-vr
componentの切り出しは、通常のReactと同様
export default class MyText extends React.Component { render() { return ( <Text> {this.props.children} </Text> ) } }
と定義したものを、importして使う
import MyText from './components/my_text.js' ... render() { return ( <View> <MyText First </MyText>
Reactをある程度知っていれば、
Componentの定義やstateの更新による再描画など基本的な書き方は同じなので、
Reactでwebアプリケーションを組む感覚でVRのシーンが組めそうだと感じた。
ちなみに、webブラウザ上での実行時の描画にはThree.jsが使われている。