emahiro/b.log

Drastically Repeat Yourself !!!!

200 記事継続の振り返りとこれから

この記事がちょうど 200 記事目です。

100 記事に到達してからも、ゆるゆると続けていたらいつの間にか 200 記事をインターネット上に放流してました。

ちなみに、100 記事目当時の投稿は以下

ema-hiro.hatenablog.com

アクセス先ランキング

全エントリのなかで、この振り返りを書いてる時点での流入(アクセス)の多いエントリ TOP5 は以下になります。

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

一番アクセスが多いのは多いのは webpack × html のエントリなのが意外です。世の中の需要がなんとなく推し量れる気がします。
書いた本人がいうのもアレですが、すでに古くなっている内容があるのであまり参考にしないほうがいいと思います。
検索に引っかかってしまうから仕方ないのかもしれませんが...

書いた本人としてイチオシなのは Go を描き始めた当時に書いた Go の template/html の値評価のチートシートのエントリです。
自分向けに書いたつもりが、マークアップエンジニアの方からも「参考にしてる」とフィードバックいただきましたし、これはまだまだ使えるエントリだと思っています。

振り返り

KPT 方式で振り返ってみます。

Keep

  • 継続してアウトプットできていること。
  • 検索エンジンでも引っかかり、それなりに見られるようになってきたこと。
  • ブログ経由で連絡をもらうようになったこと。

継続してアウトプットできていること

100 記事継続した当初の目標の1つであった「無理しない」ということをテーマにゆるゆるとアウトプットを続け、結果として習慣化していることはよかったです。
また、業務の中でも「これブログに書けそうだな」という観点で手順なり、ハマったところなりをメモるようになり、それなりに再現性のあるエントリを書けるようになってきました。

あと継続してるとトンマナのズレとかに気づけるようになってきました。
(まだ、「ですます。」調と「である。」調の混同とか、たまにやりがちですが。)

検索エンジンでも引っかかり、それなりに見られるようになってきたこと

社内の人にも補足されつつあり、無理せず、雑な内容でも書くことを大前提としつつも、変なことを書けないという妙なプレッシャーに悩まされています。
それでも見てもらえるのは嬉しいので、継続するモチベーションになっています。

ブログ経由で連絡をもらうようになったこと

これはまぁその通りですね。
上記とも被りますが「ブログ拝見しました」と言ってもらえるのは素直に嬉しいし、そういうところの話は聞いてみたくなります。

Problem

Problem というほどのものもないですが、300 エントリ目指して改善したいポイントをあげてみます。

  • エントリの題材の幅が狭い。
  • 章立て力が低い。
  • 調べてみた系・ハマった系が多い。
  • 文才がない。

エントリの題材の幅が狭い

業務に関連する技術の備忘録という側面があるのでどうしても Go, GCP に寄りがちでした。
この2つを軸にしておくことは変わりませんが、ちょうどこの三連休で Firebase にも入門してみたので、もう少し題材とする技術要素の幅は広げてみてもいいかもしれないかなと思っています。

章立て力が低い

いい感じの章立てがまだできないので、文章の構造化力が足りていないのかなと思います。

調べてみた系・ハマった系が多い

全然いいし、こういうのをどんどん書いていきたい所存なんですけど、いやそれは README 読めばわかるやん、みたいなこともあるので、単なる調べた系のエントリに閉じないオリジナリティーを出していきたいと思います。

文才がない

ないものはないのでしょうがない。

Try

300 記事に向け「これから」チャレンジしてみたいことを書いてみます。

  • 引き続きそれなりのペースで更新する。
  • 雑に書く、無理をしないことを大前提としつつ、質もそれなりに意識して書く。
  • いい感じに文章を構造化して書く。

まとめ

2017.4 頃に、このはてなブログを本格的に運用し始めたので、3年弱くらいで 200 投稿した計算になります。
始めた当初は 200 個もの拙い文章ををインターネット上に放流するなんて考えてもいませんでした。

質にもそれなりに目を向けつつ、引き続き「無理をしない」「続けることが目標」という意識低めのスタンスは変えずに 300 記事目指します。

ポートフォリオサイトを作成 -> 公開するまでにやったこと

f:id:ema_hiro:20200113023005p:plain

Overview

Nuxt + Firebase でポートフォリオサイトを作って公開するまでの過程で行なったことをまとめます。
(まだまだやらないといけないことはありますが、とりあえず最初の一歩でやったことをまとめるところまで)

なお作成したポートフォリオサイトはこちら -> emahiro.dev

やったこと

SNS の icon を手に入れる

自分へのコンタクト方法に SNS アイコンを使うケースが多いと思います。
流石にインターネットからアイコンを拾ってきて、画像として表示するみたいな過去の自分がやっていたような道は通りなくなかったので、アイコンを無償で使える何かがないかを探して、今回は Font Awesome というサービスの無償プランを使いました。

ブランドのアイコン一覧 を軽く眺めたところ、欲しいサービスのアイコンは網羅されてそうだったので、今の所無償プランで問題なさそうです。

今回は無償プランで使える SolidBrands をインストールしました。

Nuxt に Font Awesome を導入する

Nuxt(Vue) への導入方法については こちら を参照してください。Font Awesome 専用のツールのインストールが必要です。

インストール手順は こちら

npm i --save @fortawesome/fontawesome-svg-core @fortawesome/vue-fontawesome @fortawesome/free-solid-svg-icons @fortawesome/free-brands-svg-icons @fortawesome/free-regular-svg-icons

Nuxt での使用手順は こちら

ハマったところ

Font Awesome を使えるようにするには plugins 配下に fontawesome.js というファイルを作成し、Nuxt での導入方法に記載してある Component の設定を追記する必要がありました。

fontawesome.js で Font Awesomeコンポーネントをグローバルに設定したので、使いたいコンポーネントFontAwesomeIcon を指定することで <font-awesome-icon :icon="['fab', 'twitter-square']" /> で Font Awesome のアイコンが使えるようになります。

favicon を変える

Nuxt のアイコンがそのまま使われてなのでとりあえず適当な顔文字に変更しました。

そのうちちゃんとしたやつ作りたいです。
Figma とか使って。

Cloud Functions を使ってはてなブログRSS を取得する

はてなブログRSS を表示させようと考えましたので、Cloud Functions でそのエンドポイントを実装してみます。

Firebase Functions Emulator を使う

公式ドキュメント にローカルのエミュレーターの起動方法について記載してあります。

firebase emulators:start

emulators: Starting emulators: functions, firestore, database, hosting, pubsub
⚠  Your requested "node" version "8" doesn't match your global version "10"
✔  functions: Emulator started at http://localhost:5001
i  firestore: Serving ALL traffic (including WebChannel) on http://localhost:8080
...

起動した状態で local で curl を叩くと結果が返ってきます。

curl -i localhost:5001/emahiro-dev/us-central1/helloWorld

HTTP/1.1 200 OK
x-powered-by: Express
content-type: text/html; charset=utf-8
content-length: 20
etag: W/"14-z3iZXchEt5DVWZKsMncy8Wl4KSQ"
date: Sun, 12 Jan 2020 14:15:14 GMT
connection: close

Hello from Firebase!

Functions の設定の変更

ランタイムを Node 10 に変更し、標準で us-central が指定されてしまう Functions のリージョンを日本リージョンを使用するように設定を変更します。

diff --git a/functions/package.json b/functions/package.json
index 02a2f48..61f7b66 100644
--- a/functions/package.json
+++ b/functions/package.json
@@ -10,7 +10,7 @@
     "logs": "firebase functions:log"
   },
   "engines": {
-    "node": "8"
+    "node": "10"
   },
   "main": "lib/index.js",
   "dependencies": {
diff --git a/functions/src/index.ts b/functions/src/index.ts
index e6e0bb8..fa15f33 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -3,6 +3,10 @@ import * as functions from "firebase-functions";
 // // Start writing Firebase Functions
 // // https://firebase.google.com/docs/functions/typescript

-export const helloWorld = functions.https.onRequest((request, response) => {
-  response.send("Hello from Firebase!");
-});
+const jp = "asia-northeast1";
+
+export const helloWorld = functions
+  .region(jp) // 追加
+  .https.onRequest((request, response) => {
+    response.send("Hello from Firebase!");
+  });

新しい Functions を定義する

functions/index.ts に新しい関数を定義したら npm run build をします。
( functions/package.json 内に定義されてる build スクリプトの中身は tsc ですが )

Functions の動作確認には上述した Firebase Functions のエミュレーターを使用します。

まとめ

一旦自分が Nuxt + Firebase でポートフォリオサイトを作成するところまでやったことをまとめました。

今後、機能やコンテンツを増やしたときにはその内容を公開していきたいと思います。

See Also

Docs

Entries

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

Nuxt のアプリで HTML の head タグを編集する

Overview

Nuxt のアプリケーションにおいて HTML の head タグを編集する方法を記載します。

config ファイルで head タグを編集する

nuxt.config.jshead property を更新します。

デフォルトで生成される設定ファイルは以下です。

{
  head: {
    title: process.env.npm_package_name || '', 
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
    ],
  },
}

Bootstrap-Vue のレスポンシブ設定をしてみる

レスポンシブ設定をするために Bootstrap-VueResponsive meta tag に記載されてる内容に更新します。

diff --git a/nuxt.config.js b/nuxt.config.js
index 53fe780..fe3be60 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -8,7 +8,7 @@ module.exports = {
     title: process.env.npm_package_name || '',
     meta: [
       { charset: 'utf-8' },
-      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
+      { name: 'viewport', content: 'width=device-width, initial-scale=1, shrink-to-fit=no' },
       { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
     ],
     link: [

See Also

Nuxt のアプリを Firebase Hosting を使って公開する

f:id:ema_hiro:20200112095704p:plain

Overview

Nuxt と Firebase Hosting を使ってサイトを公開したのでその記録について記載します。

Install Nuxt

公式のインストール手順 に則って進めます。

npx create-nuxt-app emahiro.dev

今回自分は以下のような設定にしました。

create-nuxt-app v2.12.0
✨  Generating Nuxt.js project in emahiro.dev
? Project name emahiro.dev
? Project description emahiro's portfolio site
? Author name emahiro
? Choose the package manager Npm
? Choose UI framework Bootstrap Vue
? Choose custom server framework Micro
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose test framework Jest
? Choose rendering mode Single Page App

ただ、裏側に Firebase を使っているので必要なかったものもありました。

Build

npm run build
npm run start

# start server at localhost:3000

これで .nuxt 配下にビルドされたアプリケーションが作成され、 localhost:3000 で接続できるようになります。

Nust のアプリを Firebase Hosting で公開する

Firebase の通常設定の流れのまま firebase deploy を行なった場合、デプロイが完了しても Firebase Hosting のアプリのテンプレートが反映されて Nuxt のアプリが反映されませんでした。

原因は単純で、Firebase の通常の設定フローでぽちぽち進めていった場合に、Firebase Hosting で見る先は $FB_PROJECT_ROOT/pulic/index.html になり、 Nuxt の成果物とはディレクトリ( dist )が異なります。 そのため、Nuxt のビルドした成果物を Firebase Hosting で見れるようにする必要があります。

Nuxt の成果物を Firebase Hosting で参照するには Firebase Hosting の参照先を設定時の public から Nuxt の成果物が配置される dist に変更します。

diff --git a/firebase.json b/firebase.json
index 7b0091d..5a0db41 100644
--- a/firebase.json
+++ b/firebase.json
@@ -13,7 +13,7 @@
     ]
   },
   "hosting": {
-    "public": "public",
+    "public": "dist",
     "ignore": [
       "firebase.json",
       "**/.*",

これで再度 firebase deploy を実行すると Nuxt のページが Firebase Hosting で参照されるようになりました。

Firebase をやり始めたぞ

f:id:ema_hiro:20200112003332p:plain

Overview

Firebase を始めるに当たって、Firebase のセットアップからカスタムドメインの設定を行い、簡単な hello world を表示したところまでやったことを記載します。

Get Started Firebase

アカウント登録

Firebase にて「コンソールに移動」を選択します。

コンソールに移動するだけでアカウントは無料プラン( Sparkプラン )に登録されてました。

どのプランがいいのか

Firebase つよつよな令和のシステムアーキテクトより無料でも使えるけど、機能制限を受けるのでさっさと Blaze (従量課金プラン) にしなさいとアドバイスをもらったので従量課金プランに変更しました。

課金制限を設定する

とはいえ従量課金制で運用するのは怖いので課金アラートを設定します。

f:id:ema_hiro:20200112002349p:plain

プロジェクト名の右の「歯車」マークから「使用量と制限」を選択します。

使用量と制限 > 詳細と設定 > 予算とアラート > 「最初の予算を作成」を選択。

予算の範囲を指定できるので

  1. Firebase のどのリソースの予算を制限するのか指定します。
  2. 予算の金額を指定します。
  3. 閾値を設定します。

僕は予算制限をとりあえず ¥1000 に設定して以下のような閾値にしました。

f:id:ema_hiro:20200112002558p:plain

とりあえず実値で閾値を設定しましたが、予測値を指定することも可能です。

最終的には以下のように課金アラート一覧に表示されます。

f:id:ema_hiro:20200112012603p:plain

ドメインと紐付ける

あらかじめ取得しておいたドメインと紐付けます。 僕は Google Domainemahiro.dev というドメインを取得していたのでこちらを使います。

ドメインとの紐付けドキュメントは こちら

ドメインと紐づけるには Firebase Hosting を利用します。

Firebase の設定

Firebase Hosting にアクセスすると、まず最初に設定を始める必要があるので ドキュメント に則って Firebase の設定を進めます。

npm install -g firebase-tools
firebase login
# firebase に登録してる gmail を選択する。
cd $MYPROJECT_ROOT
firebase init

Firestore のロケーションを設定しておらずにエラーになる

プロジェクトにおいて Firebase のどのリソースを使うのかを設定する時に「全て」を選択したために、Cloud FireStore のロケーションを設定されておらずセットアップがコケることがありました。

firebase init

# 略

=== Firestore Setup

Error: Cloud resource location is not set for this project but the operation you are attempting to perform in Cloud Firestore requires it. Please see this documentation for more details: https://firebase.google.com/docs/projects/locations

これを解消するために Cloud Firestore のスタートページでロケーションを設定します。

Filestore のロケーション設定手順

  1. Firestore にアクセスします

f:id:ema_hiro:20200112004042p:plain

  1. ロケーションを選択します。日本は asia-northeast1 or 2 なので、僕は今回は asia-northeast1 (東京リージョン) を選択しました。

f:id:ema_hiro:20200112004150p:plain

これで再度 setup を実行し、最終的に以下のようなファイル群が生成されます。

.firebaserc
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
functions/
public/
storage.rules

Firebase Hosting へのデプロイ

Firebase のセットアップが完了したので Firebase Hosting へのデプロイを行います。

firebase deploy

ドメインを紐付ける

Firebase のセットアップという横道に逸れましたが、設定が完了して、deploy -> firebase のデフォルト URL でアプリケーションが表示できるところまで確認したので、emahiro.dev の紐付け作業を行います。

Firebase Hosting でドメインを追加する

Firebase Hosting のドメインでカスタムドメインを追加します。

f:id:ema_hiro:20200112004416p:plain

ドメインを追加すると IP アドレスが割り振られます。「表示」から確認できます。

f:id:ema_hiro:20200112005550p:plain

Google Domains でカスタムリソースに設定する

Google Domains の DNS > カスタムリソースレコードの項目で、上記の Firebase にカスタムドメインを追加した時に割り振られた IP アドレスを追加します。

f:id:ema_hiro:20200112005655p:plain

カスタムリソースに追加すると Firebase のドメインの欄に追加したドメインの反映が始まり「保留中」表示に変更になります。
正常に反映されると「接続されています」表示になります。

f:id:ema_hiro:20200112005750p:plain

これで紐付けは完了で、emahiro.dev でアクセスが可能になりました。

その他

Firebase の API キー丸見え問題

デプロイして喜んでたそばから Firebase つよつよおじさんが /__/firebase/init.js というファイルにアクセスしてきて API Key 丸々外部に公開されてることを教えてくれました( いじめられました

なるほどこの辺が公開されちゃってるわけなので、Firestore のセキュリティルール等を完全に理解した上で Firebase を使わないと 簡単におもちゃにされる 脆弱なアプリケーションになってしまうわけですね。

ちなみのこのエンドポイントはアプリケーション側でアクセスしないように制御できるようなものでもないとのこと。
ここで公開されてる環境変数を使うことも普通にあるらしい。ふむふむ。

まとめ

Firebase こと始めができました。
サクサク設定を進めて、簡単に公開までできてしまったので、ほんとすごいツールが出てきたな (小並) といった感じでした。
(前々から聞いてはいましたけど)

See Also

関数を引数に指定して CallBack のように振る舞わせる

Overview

関数を引数に指定してすることで、呼び出し先の関数内にて、特定の条件で指定した関数を実行する (Callback のように振る舞わせる) 実装方法をやってみたのでその記録を記載する。

Log

github.com

Usecase

R/W の競合を発生させないために sync.Mutex をフィールドに持っている struct に対してロック中に別の関数を実行させたいケースなどを考えたい。

例えば、上記のお試し実装にあるような 一度の呼び出しでオンメモリキャッシュが存在すればキャッシュを返し、なければ API の呼び出しを待ってキャッシュに書き込む、みたいなことをしたいときに、引数に関数を当てておいて呼び出し元ではロックをかけず、呼び出し先でかけてるロックを利用する、みたいなケースが考えられると思う。

この他にどんなときに使えるのか、ということはこのエントリ執筆時点では考えていないが、こういういった関数を渡して、呼び出し先でゴニョるみたいな実装方法は覚えていて損はないなと思った。

Boostnote をやめる

概要

Boostnote で作成した cson 形式のノートファイルを markdown 形式に変換する手順です。

モチベーション

お気に入りで使っていたノートアプリである Boostnote のアプリがリニューアル( Boost Note )され、有料アプリになってしまっていたので、これを機会に BoostNote をやめることにしました。
もともと Dropbox 連携だったりが出来て無料である程度データを同期することが出来ていたんですが、リニューアル & 有料化に伴い、今後今まで使っていた Boostnote の方は今後新機能の開発も止まりそうなので、これを機に乗り換えようと思いました。

ただ、これ!と言うノートアプリがあるわけではないので、しばらくは md ファイルを Dropbox に配置したまま、エディタアプリで開く、と言う手段を取ることにしました。

ただ、今まで書いたノートが全て無駄になる、といったことは避けたかったので、cson 形式のファイルを md 形式に変更して退避させないといけないのでその方法を調べたと言う話です。

やり方

weaming/boostnote2md.py を使う

weaming/boostnote2md.py を使うだけでほぼ終わりです。

手順

上記の gist コピペで使います。

# boost_note.json が存在するディレクトリが Boostnote のワーキングディレクトリです。
cd BOOSTNOTE_WORKDIR

echo "" > boostnote2md.py
# weaming/boostnote2md.py をコピペ

python3 boostnote2md.py

output/{ $TagName }/{ $Title }.md
...

同階層に output ディレクトリが作成されてそこに { $Title }.md と言う形式で md ファイルが出力されます。

以上です。

2019 - 振り返り

晦日なので KPT で今年の振り返りをします。

ちなみに年始の目標はこちら。

ema-hiro.hatenablog.com

Keep

登壇

これが今年は一番大きな出来事だったかなと思います。

社内勉強会、そして Go Conference と言う大きなカンファレンスで登壇しました。
20代のうちに経験できてよかったです。

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

登壇については、自分が使ってる技術を深く理解すると言う意味でもいい機会になりました。

アウトプットの継続

今年も引き続きブログへのアウトプットを継続できたことはよかったです。
社内でも見てる人が増えてきて、おかしなことを書けなくなってきたのが地味にプレッシャーですが、「見ました」と言ってもらえるのは書いてきてよかったなと思える瞬間でもあります。

自分のブログ経由で連絡もらったりといったことも増えてきたので、今後も継続していこうかなと思います。

また、地味に3年くらい続けていて、来年中には200投稿達成しそうですので次は300投稿を目指していきたいと思います。

投資始めた

投資というか資産運用ですね。

楽天証券に口座開いてちまちま始めました。
手堅いものしか今は買ってないので来年はもう少しポートフォリオの種類を増やしたいと思っています。

副業始めた

Lovegraph と言う会社で副業エンジニアとして働き始めました。

lovegraph.me

本業ではずっと Go に触れてますが、こちらでは古き良き(?) Ruby on Rails を使って開発してます。

自分は1つのものにしかコミットできないタイプだと思っていたのですが、自分なりにペースを作ると案外できるものだと思いました。

いいものに投資する

こんなにガジェットに投資したの初めてでは?と言うくらい買いました。

※ Switch は去年の PayPay 祭り(騒動) で買ったやつですが。

今年買ったものはどれも買ってよかったと言えるものなので、来年もそう言うものに出会いたいなと思いました。

筋トレ始めた

肩こりとそれに伴う頭痛が酷すぎたので、いつも登場する同僚氏とは別の同僚に唆されて始めたんですが、いつの間にか週1でちゃんと行うルーティンになってました。
実際肩こりも緩和したし、体力も戻ってきたのでいい効果しかありませんでした。

来年も継続したいです。

Problem

本を読めなかった

書籍に結構課金しましたが、興味がある部分や必要なところだけしか読まなかったりで、一冊ちゃんと読みきったと言える本は少なかったです。

そんなかでも個人的に今年一番良かった本はこちら。

一読の価値のある本だと思います。

新しい技術にほとんど触れなかった

年始に立てた目標に入れてなかったとはいえ、いくつか興味のあるトピックはあったのに結局触ることがなかったです。
来年は何かしら触れたいと思っています。

今のところの目標はこの辺を予定しています。

  • Firebase
  • Actions on Google
    • 年末にGoogle nest mini を手に入れたのもありますし、まず何ができるのか知るところから...。

料理のレパートリーは増えなかった

来年頑張る....頑張りたい....。

ものは捨てたが同じくらい増えた

プラマイゼロ....。

Try

  • 引き続きアウトプットを継続する。
  • 新しい技術要素に触れる。
  • 本を読む。
    • ジャンルは特に決めずに興味があるもの + いまある積読は読破する。
  • 30歳になるのでキャリアのことを真面目に考える。
    • もともと決めてた節目なので、次の10年どうしたいのかを考える機会を作って、意思決定する。
  • お金の勉強をする。
  • 筋トレ続ける。

ログを出力する車輪の再発明をしてみた

Go の 標準の log パッケージ の実装をベースに自前で管理のログ出力部分を実装してみました。

やったこと

Go の標準の log パッケージをベースにして自前でログを実装する機会があったので、そもそも log パッケージ内の実装を読んで見ようと思ったのがきっかけです。
以下のような感じで HTTP のリクエストログを表示するくんを実装しました。

2019/12/21 18:18:19 GET /
2019/12/21 18:18:20 GET /

コードは以下に置いてます。
(goimports かけ忘れましたw)

github.com

log パッケージの実装の詳細

ポイントを絞って車輪の再発明のときに参考にしたところを記載します。

感想

車輪の再発明がてら本家のパッケージがどう実装してるか見るのはとても参考になります。

node のバージョン管理に n を使い始めた

内容

理由

  • 年末だし、開発周りに環境をアップデートしていたこと
  • node-brew で管理すると、バージョンを切り替えるときに nodebrew を打つのがめんどくさい
  • 割と仕事でもそこそこ頻繁に node のバージョンを切り替えることが多いので、打ち込むコマンドは少ない方がいい
    • n って1文字じゃん!!!最高!!!!

nodebew を捨てる

  1. nodebrew のPATHを通してる箇所を削除
  2. ~/.nodebrew を削除
  3. usr/local/bin 配下の nodebrew を削除

n を入れる

https://github.com/tj/n/blob/master/README.md の通り。
これだけだと n 経由で特定バージョンの node を入れるときに /usr/local/n 配下に書き込み権限がないので、README.md に記載してる権限付与の作業が必要。

$ n 10.16.0

  installing : node-v10.16.0
       mkdir : /usr/local/n/versions/node/10.16.0
       fetch : https://nodejs.org/dist/v10.16.0/node-v10.16.0-darwin-x64.tar.gz

便利!

[追記]

初めて n を入れてから n {$VERSION} で指定したバージョンの node を入れようとすると権限エラーが起きることがある。

 n 9.11.2

  installing : node-v9.11.2
       mkdir : /usr/local/n/versions/node/9.11.2
mkdir: /usr/local/n/versions/node/9.11.2: Permission denied

  Error: sudo required (or change ownership, or define N_PREFIX)

N_PREFIX を指定すると解決する。以下を bash_profile や .zshrc に追加する。

export N_PREFIX="$HOME/.n"
export PATH="$PATH:$N_PREFIX/bin"

Go で n 番目の要素を削除する

大した話じゃないんですけど、忘れてたので備忘録です。

strs := []string{"a", "b", "c", "d", "e"}

// "c" を削除する。
idx := 2

strs = append(strs[:idx], strs[idx+1:]...)
fmt.Println(strs)

ref: https://play.golang.org/p/3QP4OXw2X0u

Go の httptest で立てたサーバーにアクセスする

テストなどで実際にサーバーを立てずに、HTTP のリクエストをシミュレートしたいときに httptest を使いますが、この httptest で立てたダミーサーバーそのものアクセスする方法はないかを調べてみました。

Motivation

あるテストをメンテしていた時に httptest.NewServer で作成したダミーのサーバーに対してリクエストを送っているのですが、ダミーサーバーにうまく送信できずにテストが落ちる、と言うことを繰り返してました。

結果としては、httptest.NewServer に router を差し込んで、アプリケーションで定義してる routing をシミュレートできていなかったことが原因でした。
このとき、テスト対象のアプリの routing に依存しない独自の Hnalder を定義した httptest.NewServer で立てたサーバーに向けて、 HTTP のリクエストを送る方法を知りたいないと思ったのがこのエントリを書こうと思った動機です。

サンプルコード

httptest.Server を立てます。

httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("hello in test server."))
}))

NewServer の引数に router を指定してないのでこの状態で httptest.NewRequst でリクエストを生成してサーバーにアクセスすることはできません。

ではどうやってこの生成したサーバーにアクセスするのかを godoc と実際のコードを追いながら調べたところ httptest.Serverhttp.Server 型の Config と言う Field が存在しており、これが httptest.NewServer して立てたサーバーの本体のようです。
(このフィールドの用途はサーバーを立てた後に構成を変更するためのものらしいので、実際の用途とは少し違う使い方をすることになりそうです。)

実際に httptest.NewServer で立てたサーバーにアクセスするコードは以下のようになります。

func TestMain(m *testing.M) {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Printf("request in test server. req: %+v", r)
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("hello in test server."))
    }))

    r := httptest.NewRequest(http.MethodGet, "/", nil)
    w := httptest.NewRecorder()
    ts.Config.Handler.ServeHTTP(w, r)
}

実際にテストを実行してみます。
ちなみに TestMain にした意図はテストをしたいわけではなくて、テストでサーバーを起動してアクセスすることが目的だからです。

go test -v . 
request in test server. req: &{Method:GET URL:/ Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[] Body:{} GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:example.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:192.0.2.1:1234 RequestURI:/ TLS:<nil> Cancel:<nil> Response:<nil> ctx:<nil>}
ok      github.com/emahiro/ilhttptest   0.550s

ちゃんと作成したサーバーにアクセスできてますね。

ユースケース

正直これを書いた後に、じゃあ実際どう言うケースでこのテストサーバーにアクセスできることが嬉しくなるのだろう?っと考えて「これだ!」と言うことは思いつきませんでした...。

以下のようなミドルウェアを実装して

func mw() func(http.Handler) http.Handler {
    return func(h http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // middleware で何かしらの処理をする
            fmt.Printf("This is in middleware\n")
        })
    }
}

サーバーを作る時に事前に差し込んでおくとか考えましたけど、その場合には

h := mw()(http.NewServeMux())
r := httptest.NewRequest()
w := httptest.NewRecorder()
h.ServeHTTP(w,r)

とした方が便利ですしコード少なくて済みます。

テストサーバーに直でアクセスできる方が嬉しいケースがまだ足りませんが、httptest で立てたサーバーへのアクセス方法を知ることはできました。

今回書いたコードは以下にあげてます。

github.com

Go で意図的に競合状態を発生させる

Summary

  • Go では concrrent map writes のような競合状態の可能性がある実装があるときに排他ロックをかける。
  • 競合状態を回避するためのサンプルとしてロックをかける実装はたくさんインターネット上に情報が出てくるが、そもそも意図的に競合状態を作り出すサンプルがなかったので書いてみた。
  • Go のテストで競合状態をチェックするには race オプションを使用する

競合状態チェックの実装

https://github.com/emahiro/il/pull/6 に記載した。
一応並行処理のため、毎回確実に発生するわけではないが数回に一度は concurrent map writes が発生する。
そのため go test . -race で競合状態のチェックをかけるとテストが落ちるようになっている。

コード上は同期的な処理に見えても、マイクロサービス内で飛び交うリクエストなど並行に処理がされるケースに置いて concurrent mas writes が発生しうる場合には test で並行に動かしてみて競合状態 (race conditons)をチェックすることが出来ると良いし、こう言うケースに置いて単体テストで競合状態をシュッと作り出せるととってもスマートだなと思う。

LINE Developer Day 2019 に参加して来た

linedevday.linecorp.com

今年も参加してきたので備忘録として感想をつらつらまとめます。 スライドは [こちら https://speakerdeck.com/line_devday2019]

感想

まず会場がすごい。グランドニッコーを二日間使うってどういうことだ。
僕は技術的なカンファレンスでここにきたことがあるのは5年前くらいの Unite Tokyo (Unity のカンファレンス) 以来だった。
このペースならそのうち日比谷公園ジャックとかしそうな勢いを感じた。
(天候が安定しない日本においては野外の選択肢は難しいが、今のような時期なら晴れも多くワンチャンあるかも。でもきっと寒い笑)

しかしお台場は遠いので朝苦手勢からすると、1日目のキーノートに間に合うか前日から不安だった(結果間に合った。)

セッション全体の感想については自分が参加したものから受けた印象に限るが、1日目の2日目で毛色が異なる構成で二日間参加したけどどちらも興味深い話をたくさん聞けた。

1日目は技術的な内容が非常に盛りだくさんだった。 去年までの新製品や新サービス発表のような内容とは異なり、LINEの技術そのものや、長年蓄積してきた技術的な課題に立ち向かう現場の話、さらにはリアルな世界でプロダクトを作る上で必ず考えないといけないもの(セキュリティなど)に関するセッションも多く、ある意味ハレの面だけでないところにも目を向けたセッションも多かった。

2日目は技術的な内容もありつつ、LINEで働くことやプロダクトマネジメントに寄った内容もあり、技術だけでなく「ものづくり」全般に関わる内容が多かったように思う。
1日目に技術的な話をモリモリ聞いていて、ちょっと食傷気味だったところに毛色の違う話が多かったので、聞いて楽しく、脳みそもリフレッシュした状態で聞くことができた。

会場全体も色々工夫してあった。

  • 休憩スペースの机の並びが「LINE」になっていたり
  • ガチャがあってガチャ結果でノベルティの内容が変わっていたり(僕は2等で卓上加湿器をもらった)

などなど。
あ、お弁当は二日ともとてもお高いものだったので美味しかった。

ざっとまとめるとこんな感じ。
LINE のカンファレンスは当たり前のように英語セッションがあったり、普通に英語や韓国語が飛び交っていたりして、グローバル企業なんだなぁということをつくづく感じる。

また来年も遊びにこようと思えたイベントでした。楽しかったー。

リポジトリのオーナーを移行しました

報告

以下のリポジトリのオーナーを移行しました。

App Engine の 2nd Gen 移行のために業務で作っていたものです。

もう一つ作ってるツールがあるのでそちらもできたらこういう風に使おうと思って作りました、みたいな内容のエントリを書こうかなと思います。