認知的不協和を活用して、気が散るのを防ぐ
ここ数週間、職場で試してみて、良かったやつ。
自分はコーディングや文章を書いていると次々と関係のない考えが頭に浮かんでくる。要は気が散りやすい。気がついたら、本来集中しなければいけないことと別のことに本気を出していたりする。
最近はA4サイズのホワイトボードを活用して、これを防いでいる。
- 出版社/メーカー: レイメイ藤井
- メディア: オフィス用品
- この商品を含むブログを見る
まず、ホワイトボードをディスプレイの近くに置く。ディスプレイから目を逸らすとホワイトボードの文字が目に入るくらいの距離感がよい。
ホワイトボードには、「集中したいタスク」と「それをいつまでやるか」を書く。自分の場合は、例えば「~11:30 ○○の△△の実装」と書いて、その後にタスクに取り組む。
気が散って、別のことをしそうになったら(あるいはし始めたら)ホワイトボードをじっと見る。すると、ホワイトボードに書かれた内容と実際の自分の行動が矛盾していることに気づいて、なんとも言えない違和感を感じる。脳は、この違和感を解消しようとするわけだけれども、ホワイトボードに書かれた内容のほうが具体性が高いので、気が散る原因になっている考えを脳は取り下げる(面白いアイディアだったらどこかにメモをしておいて後で見返せばいいかもしれない)。
このように認知的不協和を活用することでひとつのタスクに集中して取り組むことができる。
併せて、ホワイトボードに具体的なタスクを書くことで、近くの席の人に「あの人、ホワイトボードに書いてるのと違うことしてる...」と思われるのは避けたい、といった意識も働いているかもしれない。
この他に、そもそも関係のない考えが浮かんでこないように、こういった考えを出してくる脳のリソースを別のなにかで占有するという手もある。具体的には 論理的思考の放棄の具体的方法 - 登 大遊@筑波大学大学院コンピュータサイエンス専攻の SoftEther VPN 日記 に書かれているような磁石と鉄球のイメージが参考になる。
AWS Lambda で Cloud Firestore を使う
Webpack を利用した複数の Lambda 関数の管理 - kitak blog の続きで色々やっていました。
Cloud Firestore を Lambda から使おうとしたんですが、バンドルファイルを実行したらエラーに。grpc まわりでネイティブモジュールのビルドが必要で、firebase-admin パッケージは Webpack のバンドル対象から除外することにしました。( firebase-sdk-js を使うことも検討したんですが、こちらも結局同じ問題に )
webpack-node-externals
除外には、webpack-node-externalsを使います。これはデフォルトでは、node_modules 以下を全てバンドル対象から外しますが、正規表現で除外対象を絞ることができます。今回の場合、こんなかんじです。
const nodeExternals = require('webpack-node-externals'); module.exports = { mode: 'none', target: 'node', externals: /^firebase-admin/ // ... };
Docker を使ったネイティブモジュールのビルド
ネイティブモジュールのビルドのために Docker を使いました。以下のスクリプトを用意して、postbuild
タスクで実行します(zip を作成する前に実行する)。
Lambda の node バージョンと同じバージョンの node のイメージを使っていますが、本当は Amazon Linux のイメージをベースにした node 入りのイメージを用意したほうがよさそうです。
#!/usr/bin/env node const {promisify} = require('util'); const {exec} = require('child_process'); const execAsync = promisify(exec); (async () => { console.log(`Install native module ...`); await execAsync(`docker run --rm -v "$PWD":/worker -w /worker node:8.10.0 npm i firebase-admin --prefix . --production`, { cwd: './dist/hoge', maxBuffer: 1000*1024, }); })();
今後
- zip のファイルサイズが数十M以上になってしまったので、結局、S3 経由でデプロイする必要がありそう( firebase-admin とそれの依存するパッケージのファイルサイズが大きい... )
- CodeBuild を使って、デプロイのパイプラインを整備する( CodeBuild で Docker が使えたはず )
- Git push or コマンド実行 → CodeBuild で zip の作成 → S3 に zip を配置 → S3 経由で Lambda 関数の更新
- 今回は firebase-admin だけ無視したけれども、webpack-node-externals で node_modules 以下を丸々無視してもいいかもしれない( アプリケーションのコードのみバンドルする )
- 各バンドルファイルに含まれる node_modules 以下のパッケージをリスト化して package.json を動的に生成して、Docker コンテナで install すればよさそう
- GitHub - ubilabs/webpack-node-modules-list: Exports all used node modules of a webpack bundle to a file. を使えばできそう?
昭和天皇物語2を読んだ
読んだ。
- 作者: 能條純一,永福一成,半藤一利
- 出版社/メーカー: 小学館
- 発売日: 2018/03/30
- メディア: コミック
- この商品を含むブログを見る
昭和天皇のお妃選び
下記の藩閥政治の話と絡むのだけど、妃選びが政争の具になるのを避けるために、貞明皇后自ら、積極的に妃を選ぼうとする話が描かれる。具体的には、女学院に乗り込んでいって、よさそうな女学生を探す。
なんとも行動力がある話だけれども、実際にこういうことやってたのかなぁ。この記事を書きながら、貞明皇后の Wikipedia のページを読んでいたのだけど、宮中の古いしきたりを破壊したエピソードが多数なので、やりかねないな、と思った。
藩閥政治から政党政治へ
山縣有朋が皇太子妃を長州の息のかかった人物にしようと画策していて、あー、大正時代でもまだ薩摩と長州のこういう権力争いってやってたんだなぁ、という気持ちになった。
米騒動による寺内正毅の退陣と、その後に政党出身の原敬が首相に選ばれる(元老による推薦)過程が描かれていて、昭和天皇の立太子からお妃選びのあたりがちょうど藩閥政治から政党政治へと移り変わっていく時期なのだな、と結びつけることができた。
東郷平八郎
この作品は東郷平八郎の心理描写が面白い。日露戦争の映画やドラマで無口でクールな描かれ方をされ、亡くなった後は神として祀られる東郷が、授業をする学者の発言にいちいち心を乱されたり、「明治が遠く感じる」と弱音を吐いていて、なんとも人間くさい。
また、2巻の時代に起きていた第一次世界大戦について、「テクノロジーの発達によって大量殺戮が可能になり、かつての戦争(日露戦争)に存在していた人間の尊厳が喪失した」と語っている場面も興味深かった。
Webpack を利用した複数の Lambda 関数の管理
こんなかんじでやってみたらどうでしょ、という話。
AWS Lambda を中心にいわゆるサーバーレスのアプリケーションを構築するには、複数の Lambda 関数を作成することになります。
普通のウェブアプリケーションであれば、ひとつのリポジトリでコードを管理して、起動時に実行環境でアプリケーションのコードをまとめて読み込みますが、Lambda 関数を組み合わせてアプリケーションを構築する場合は、アプリケーションの機能ごとに実行環境が分かれることになり、それに応じてコードも分割しなければいけない難しさがあります(その制約を受け入れるメリットのひとつは、リクエスト数が突然跳ねた場合にスケールすることです)。
各 Lambda 関数は「一つのことだけうまくやる」ことを意識して書きますが、設定や汎用的なロジック、API クライアントなど Lambda 関数に跨った共通のコードをどうやって扱うか、という課題があります。
関数単位や共通のロジックで複数のリポジトリに分けるアプローチは様々な作業が煩雑になる(開発時のリポジトリの行き来、イシューやバージョンの管理、各リポジトリの更新...)ので、なるだけ単一のリポジトリでいきたいです。
ひとつのやり方として、Webpack で各 Lambda 関数の index.js をバンドルファイルとして生成するのがいいんじゃないかと思ってます。
Webpack の設定はこんなかんじです。Webpack のバージョンは 4.5.0 です。
const path = require('path'); module.exports = { mode: 'none', target: 'node', entry: { foo: path.resolve(__dirname, './src/foo.js'), bar: path.resolve(__dirname, './src/bar.js'), }, output: { filename: '[name]/index.js', path: path.resolve(__dirname, 'dist'), libraryTarget: 'commonjs2', } };
Lambda 関数ごとに entry を定義する。
Lambda で動かすために特筆することとして、target オプションを node
にするのと、output の libraryTarget オプションを commonjs2
にしていることです。
共通のロジックは別ファイルに分けて、各 entry のファイルでインポートします。
Webpack のようなモジュールバンドラを使う副次的な効果として、アップロード制限の回避があります。モジュールバンドラ無しで Lambda 関数をデプロイする場合、node_modules も含めて zip を作成することになるので、いくつかパッケージを依存に追加した程度でファイルサイズが膨れ上がり、アップロードできなくなります(S3経由でデプロイする必要がある)。
モジュールバンドラを使えば、node_modules から必要な内容だけ取り出して、ひとまとまりのファイルが生成されます。関数がよほど複雑にならなければ、制限に達することはなさそうです(達した場合は、関数分割をすべきタイミングかもしれません)。
Webpack でビルドしたファイルは、デプロイのために zip にする必要があります。
Webpack には zip を生成するサードパーティのプラグインがありますが、今回は Webpack に過度に依存することを避けるため、あくまで JavaScript のモジュールバンドラとしての役割のみに徹することにします。
postbuild
のタスクで以下のスクリプトを走らせて entry 毎に zip ファイルを生成します。
#!/usr/bin/env node const path = require('path'); const {promisify} = require('util'); const {exec} = require('child_process'); const globby = require('globby'); const execAsync = promisify(exec); (async () => { const paths = await globby(['dist/*'], { onlyDirectories: true, }); // Lambda 関数の数が極端に多い場合は、一定数ずつ作成したほうがよさそう await Promise.all(paths.map((path) => { console.log(`Create zip in ${path}...`); return execAsync(`zip -r Lambda-Deployment.zip * -x *.zip`, { cwd: path, }); })); })();
あとは、生成された zip をウェブのコンソールでアップロードするか、CLI を経由してデプロイします。
API Gateway カスタムオーソライザーを使って、Firebase で認証する
組み合わせただけの話なのですが、個人用メモ。
ちょっと前に「AWSによるサーバーレスアーキテクチャ」を読んだり、手元で色々試してました。本では、認証に Auth0 というサービスを使っているんですが、本が書かれた頃から Auth0 の仕様が大きく変わっています。サンプルを直すのがつらそうだったのと、その章で説明したい内容はあくまでカスタムオーソライザーの設定で、正直、認証サービスはなんでもよさそうだったので、馴染み深い Firebase Auth を代わりに使ってみました。
- 作者: 長尾高弘
- 出版社/メーカー: 翔泳社
- 発売日: 2018/03/14
- メディア: Kindle版
- この商品を含むブログを見る
Firebase の ID トークンの作成と確認
Firebase はクライアントサイドで認証が完結しますが、バックエンドの API サーバーでログインしているユーザーを知りたい場合があります。Firebase ではクライアントで ID トークンの発行をおこない、サーバーでこれを検証することで実現できます。手順は以下のようなかんじです。
各手順で使う Firebase の API は ID トークンを確認する | Firebase の説明がわかりやすいです。1, 2 を簡単にコードで示すと以下のようになります。
firebase.auth().currentUser.getIdToken(true) .then((idToken) => { return fetch('https://XXX.amazonaws.com/dev/get-profile', { mode: 'cors', headers: { 'Authorization': 'Bearer ' + idToken, } }); }).then((response) => { return response.text(); }).then((body) => { console.log(body); }).catch((error) => { console.error(error); });
3 は次で示します。
カスタムオーソライザーに設定する Lambda のコード
トークンの検証とポリシーの生成をおこなう Lambda 関数を用意します。あとはこの Lambda 関数を API Gateway のリクエストの認証に設定すれば終わりです。
const admin = require('firebase-admin'); const serviceAccount = require('./XXX.json'); // Firebase の管理画面からインストールできる鍵ファイル admin.initializeApp({ credential: admin.credential.cert(serviceAccount), databaseURL: 'https://XXX.firebaseio.com' }); const generatePolicy = (principalId, effect, resource) => { const authResponse = {}; authResponse.principalId = principalId; if (effect && resource) { var policyDocument = {}; policyDocument.Version = '2012-10-17'; policyDocument.Statement = []; var statementOne = {}; statementOne.Action = 'execute-api:Invoke'; statementOne.Effect = effect; statementOne.Resource = resource; policyDocument.Statement[0] = statementOne; authResponse.policyDocument = policyDocument; } return authResponse; }; exports.handler = function(event, context, callback){ if (!event.authorizationToken) { callback('Could not find authToken'); return; } const token = event.authorizationToken.split(' ')[1]; // ID トークンの検証 admin.auth().verifyIdToken(token) .then((decodedToken) => { const policy = generatePolicy('user', 'Allow', event.methodArn); callback(null, policy); }).catch((error) => { console.log('Failed idToken verification: ', error); callback('Authorization Failed'); }); };
つらかったこと、いまいちなこと
あんまり本質じゃないですが、CORS まわりでけっこうハマりました。
- カスタムオーソライザーでポリシーの生成にしくじっても(実行時エラーにはならないが、ポリシーの内容に問題がある場合)、API レスポンスが正常(200)で返ってくる
- エンドポイントに紐付いた Lambda 関数は実行されない
- Lambda 関数が実行できなかったらエラーを返すか、CloudWatch Logs にエラーを出力してほしい
- (自分が気づいていないだけでどこかにエラーが出ているかも)
- 「Lambda プロキシ統合の使用」を使うと、API Gateway で「CORS の有効化」をしていても、CORS 関係のヘッダーが付与されない。Lambda 関数でレスポンスを返す際に明示的に指定する必要がある
- CloudWatch Logs が見づらいのと、複数の Lambda 関数で処理を実現している場合のデバッグで、各 Lambda 関数のログを調べるのが大変
- (追記) これは S3 にログを集約して Athena で分析することになりそう
ハリー・ディーン・スタントン主演 ラッキーを観た
渋谷のアップリンクで観た。
主役のラッキーを演じるのは、去年の7月に91歳で亡くなった名優ハリー・ディーン・スタントン。この作品は彼の最後の主演作である。
自分がハリー・ディーン・スタントンを知ったのは、ツイン・ピークスの劇場版だった。数分程度の登場シーンだったが、放心した顔でタバコを吹かしながら、意味深な発言をする演技はあまりに印象的で、自分の頭にベタッとこびりついてしまった。
その後に見たストレイト・ストーリーでも、これまた最後の数分だけの登場にも関わらず、顔で語る圧倒的な演技で惹きつけたのだった。
映画のテーマは最後の主演作らしく「死」についてである。
誰しも、生きている間に少なからず死について考える。「どうなるか分からないから考えるだけ時間の無駄」と思う時点で考えてしまっている。ハイデガー的に、考えることで生を有意義なものに転換することもできる。人生を夏休みに例えると、死について考えることは夏休みの自由研究のようなものではないかと思う。自分なりの答えを一生を通して創り上げていく。
ラッキーは、夏休み最後の日、8月31日に改めて自由研究に向き合うことになった。
彼は生粋の現実主義者であり、自身の生命の終わりを意識したとき、彼の心の中に死についての黒く暗い観念が生まれ、徐々に膨れ上がっていく。彼と一緒にこの宿題に取り組むのは、一癖も二癖もある街の住人達である。彼らと言葉を交わしながら、時にぶつかり合いながら、ラッキーは自分の答えを創り上げていく。
黒く暗い観念を打ち消した先に彼が見たものは何か。名優として生きた人間の生前葬としてふさわしい作品である。
Vue コンポーネントをクライアントサイドでテンプレートエンジンとして使う
forum.vuejs.org に回答したやつ。
コンポーネントを描画した結果の HTML 文字列を取得したいときってあるの?とか思われそうなんですが、たまにあるんですよね(ウィンドウポップアップでプレビューを静的なHTMLで表示する必要があったり、v-html
で表示する内容の一部を差し替える必要があったり )。
やりかたとしては、以下の手順です。
- コンポーネントを登録するオプションオブジェクトを
Vue.extend
に渡してサブコンストラクタを定義する - サブコンストラクタのインスタンスを生成する(このとき、
props
はpropsData
プロパティ経由で指定する。データを変更したい場合はインスタンスのプロパティアクセスで変更する) $mount
メソッドで描画する$el
プロパティで DOM 要素が取得できるので、outerHTML
プロパティにアクセスして、HTML 文字列を取得する
const Foo = Vue.extend({ props: ['name'], template: `<p>Hello, {{name}}</p>` }); const foo = new Foo({ propsData: { name: 'Vue.js' } }); foo.$mount(); const html = foo.$el.outerHTML; console.log(html);
↑はコンパイラを含む Vue のビルドを使わないとエラーになります。ランタイムオンリービルドを使う場合は、.vue
からインポートしたオブジェクトを Vue.extend
に渡してください。