リモートワークで昼に冷凍食品食べ続けたのでオススメを紹介する

リモートワークで家で仕事をするようになってから1年以上が経過した。

リモートワーカー昼飯問題

リモートワークをしていると困るのがお昼ご飯。

選択肢としてはいろいろあって、聞いたことがあるものとしては

  • 夕飯の残りを食べる
  • 普通に自分で作る
  • 外で食べる
  • コンビニとかで買う

などがある。

冷凍食品

私は基本的に冷凍食品を食べることが多かった。

栄養が〜!という考えもあると思うけど、個人的には昼食にそもそも栄養バランスなんて期待してないので、他の部分を重要視している。

いろいろメリットがあって

  • 外に出なくていいので楽
  • ほぼ温めるだけでよく、温めてる間に洗濯物を畳んだりできる
  • 安い
  • 最近のものは美味しい

といった具合。

一年も続けると、それなりに種類も食べてきたので、個人的なオススメを紹介したい。


日清具多 辣椒担々麺

https://images-na.ssl-images-amazon.com/images/I/617l3OKqreL.jpg

出典:www.amazon.co.jp

www.nissin.com

これをオススメしたくてこの記事を書いてると言っても過言ではない。

具材のひき肉も結構たくさん入ってて、かなり美味しい。
山椒みたいのが入ってるが、もともとの状態で結構辛いので入れなくてもいいかも。

鍋を用意する必要もなく、電気ケトルとかで300ccのお湯を沸かすだけでいい。

辛いのがだめじゃない人は是非一度買って食べてみてほしい。

セブンイレブンのつけ麺

f:id:mugi1:20180623211549j:plain

チャーシュー盛つけ麺 - セブン-イレブン~近くて便利~

コンビニの冷凍食品もかなりレベルが高くて、最近のはとても美味しい。

特にこのつけ麺が美味しかった。

麺が太麺で結構食べごたえもあるし、つけだれも魚介ベースな感じが美味しい。

これも、つけだれ用のお湯を少し沸かすだけでいいので作るのが楽でいい。

炒飯の極み[えび五目XO醤]

f:id:mugi1:20180623212814p:plain

出典:www.amazon.co.jp

炒飯の極み[えび五目XO醤]|冷凍食品|商品情報|マルハニチロ株式会社

冷凍チャーハンは人によって好みが分かれそうだな〜とは思うけど、個人的にはこれをオススメしたい。

他の冷凍チャーハンはチャーシューだったり、ガツンとした味付けみたいなところで推してるのが多いが、これは珍しくエビがゴロゴロ入ってるタイプ。

(私がエビ好きだっていうのもある)

海鮮感が嫌いじゃない人は食べてもらうといいかも。

ただ、以前Toyama.rbの年末LTでソニックガーデンの木原さんも同じことを言っていた記憶があるが、基本的に冷凍のチャーハンはだいたいうまい。

とりあえずチャーハンがあれば安心感がある。

他のだとこのへんが安定して美味しい。

具材の大きさとか本格っぽさであればコレがかなりすごかった。

が、これは八角の香りが結構強く、人によっては好き嫌いがあるかも。

私は大丈夫だったのでたまに食べてる。

オーマイプレミアム 海の幸のペスカトーレ

f:id:mugi1:20180623220249p:plain

出典:www.amazon.co.jp

www.nippn.co.jp

パスタであればこれがオススメだった。

パッケージを見てもらうとわかるが、具材がすごい。ゴロゴロ入ってる。

そして、これはトレーに入った状態で冷凍されているので、お皿がいらないのもありがたい。洗い物が減る。

味も美味しい。

まとめ

というわけで、

  • 麺(汁あり)
  • つけ麺
  • チャーハン
  • パスタ

の4つを書いてみた。これらは本当によく食べているので、食べたことがなければ是非1回食べてみてほしい。

が、どれだけ美味しかろうとも同じものを食べていると飽きる。

なので、なんかオススメあったら教えてください。(これを言いたかった)

リモートワークしてて気になってきたのでデスク周りのケーブルをスッキリさせた

気がつけばリモートワークを始めてから1年経過していた。

最初にデスク周りは一旦整理したけど、1年もあると色々モノも増えたりしてきて、結構ゴチャゴチャとしてきていた。

特にケーブル周りがひどく、たとえば電源ケーブル等についてはケーブルボックスに収納して見えなくしていたけど、

  • ケーブルボックスから距離があるもの
  • ケーブル自体の長さが中途半端なもの
  • 定期的に抜き差しするためボックスにしまいたくないもの

については適当な状態で、長い間見てみぬフリをしてた。

(以前の状態) f:id:mugi1:20180408151130j:plain

この写真の通り、そのまま垂れ下がってるような感じ。

見えないからいいんじゃね?という考えもできそうだけど、

  • 掃除機をかけるときに邪魔
  • 仕事中に足にケーブルがあたって鬱陶しい
  • なんかイヤ

という問題があるので、気合を入れて整理することにしてみた。

最終的にどうなったか

こうなった

f:id:mugi1:20180408164808j:plain

めっちゃスッキリした。

ポリシー

  • がんばりたくない(コードを1本ずつ固定するとかはやりたくない)
  • ケーブルが増えたり減ったりしても楽に対応したい
  • 雑でいいので、いい感じになってほしい

使ったもの

ネオジム磁石

  • 100均で売ってる

f:id:mugi1:20180408135140j:plain

ステンレスシート

  • ホームセンターに売ってる
  • 1枚700円くらい
  • 買った磁石を持っていって、ちゃんとくっつくものを買う

f:id:mugi1:20180408135058j:plain

ワイヤーネット

  • 100均
  • こちらも磁石がくっつくものを買う
  • ⇣こんな感じのやつ

アイリスオーヤマ メッシュ パネル MPP-6090 ブラック

マスキングテープ

  • 100均
  • なんでもいいけど、磁石より幅があるといい

f:id:mugi1:20180408223200j:plain

普段外さないケーブル類の整理

ワイヤーネットを使って、ケーブルをすべて机の裏に抑えつける。
固定自体は磁石のパワーで雑にやれる。

ステンレスシートを机の裏に貼る

それっぽい場所に適当に貼る

f:id:mugi1:20180408222821j:plain

ワイヤーネットにネオジム磁石を貼り付ける

適当でいい。磁石が強いので勝手にくっつく

f:id:mugi1:20180408154502j:plain

ケーブルをガッと掴んでガッとワイヤーネットに乗せる

ガッとつかんで

f:id:mugi1:20180408222854j:plain

ガッとのせる

f:id:mugi1:20180408222913j:plain

ワイヤーネットごとステンレスシートにガッとくっつける

ネオジム磁石の力によって、適当にやってもいい感じにケーブルごと止まる

f:id:mugi1:20180408222933j:plain

ケーブルが増えたら、ワイヤーネットを引っ張れば簡単に外れるので、 そこにガッとケーブルをねじこんでガッと止めれば終わり。簡単。

取り外すことのあるケーブルの固定

これはお好みでやればいいと思う。

私は磁石が余ったのでやった。

マスキングテープで磁石を挟む

こんなのいくつか作る

f:id:mugi1:20180408223023j:plain

  • マスキングテープ2切れの粘着面で磁石を挟む
    (粘着面同士を貼る。ステンレスシートに磁力でくっつける。)
  • 両端に1個ずつ磁石をいれる
  • どうせ見えないし適当でいい

ステンレスシートにくっつけてケーブルを挟む

こんな感じ。

f:id:mugi1:20180408223047j:plain

簡単に取れるので、ケーブルの取り外しも簡単。

f:id:mugi1:20180408223032j:plain

壁にケーブル止めるやつを作る

これでもまだ磁石が余った。(買いすぎ)

なので、もう何も考えずに、椅子から手がとどく位置の壁にマスキングテープで貼った。

イヤホンとかマイクロUSBケーブルとかがくっついて便利。

f:id:mugi1:20180408231521j:plain

以上です

だいぶスッキリした。

簡単なのでオススメです。

SlackBotをGUIでポチポチしていい感じにするツールを作ってる

年末年始の休みぐらいから、SlackBotをGUIでポチポチするだけでいい感じに作れるようなツールを作ってる。

画面でポチポチ作ると

f:id:mugi1:20180128144304p:plain

こんな感じで動く

f:id:mugi1:20180128144705p:plain

とりあえずある程度動くようになったのでリポジトリ公開しといた

github.com

動機

SlackBotについて考えてみたときに、以下のようなことだけ簡単に実現できて自由に組み合わせることが出来れば、全部じゃなくとも80%くらいのやりたいことは実現できるんじゃね?とある日思った。

  • 実行のトリガー
    • 何らかのキーワードで実行(@bot 次の予定 みたいな)
    • スケジュールして一定時間ごとに実行
    • 外部からのWebRequestで処理を実行
  • トリガーで処理すること
    • どっかにWebRequestを投げて結果をもらう
  • 最後にやること
    • どっかにWebRequestを投げる(投げ捨てる)
    • 何か結果をチャンネルに返す

すでにBotを構築するツールはいっぱいあると思うけど、GUIでポチポチやって簡単に作れたら楽だな〜というのと、なんでもいいのでNuxt.jsを使ってなんか作りたかった、というのがあって、作ってみることにした。

使ったもの

  • Nuxt.js
  • express : APIサーバ兼、Nuxt.jsの動作用
  • mongo : データ保存用。雑にmongooseでアクセスしてる
  • element-ui : 見た目はこいつに任せてる
  • axios : 外部へのWebRequest発行用
  • node-schedule : スケジュール実行用

でだいたい動いてる。

深い意味や理由はまったくないが、全部jsで書いてみようと思ったのでexpressとかを使ってる。

概念的な話

以下のような名前をつけて取り扱ってる

  • Flow : 一連の処理の流れ
  • Trigger : Flowを起動するためのきっかけとなるもの。Flowにつき1つ。
  • Action : Triggerによって実行される一連の処理。Flowにつき任意数。
  • Finisher : Flowの最後にやりたい処理。Flowにつき1つ。

ActionやFinisherは1つまえに実行されたものから何らかのパラメータを受け取って自由に扱う。

たとえば上に貼った画像の例ではToyama.rbのイベント情報をチャンネルに返してるが、これはActionで行ったaxiosのレスポンスが次のFinisherに渡されてるので可能になってる。

(ちなみにFinisherではlodash#template形式で雑にテンプレートを書けるようにしてみた)

Slackへの依存をいかに切り出すか

Slack以外のツールにも対応させたいな〜みたいな思いがあったので、Slackに依存する部分とのやりとりはすべて events 経由で間接的に行ってる。

具体的には、Botの動作全体を管理するクラスでは

module.exports = class Bot {
  constructor (client) {
    this.client = client;
  }

  start (configuration) {
    this.client.start(configuration);
  }

  stop () {
    this.client.stop();
  }

  bindMessageTriggerFlow (flow) {
    this.client.on('messageReceived', (data) => {
      if (!data.actionName || data.actionName !== flow.trigger.data.word) return;

      this.executeAllActions(flow, data);
    });
  }


  ...

みたいにして、client がSlackかどうかは関係なく、特定のinterfaceを持ってるかどうかだけ気にするようにしてみた。

client自体は

const EventEmitter = require('events').EventEmitter;

module.exports = class BaseClient extends EventEmitter {
  start (state) {
    throw new Error('not implements error');
  }

  stop () {
    throw new Error('not implements error');
  }

  emitMessageReceived (message = {}) {
    this.emit('messageReceived', message);
  }

  sendMessage () {
    throw new Error('not implements error');
  }
};

みたいなBaseクラスを継承させていて、Chatサービスに依存する実装はサブクラスに丸投げしてる。

定期実行の方法

cron形式で定期実行できるようにしたかったんだけど、その実現に node-schedule を利用させてもらった。

github.com

さくっと動いて便利だった。

気をつけないといけなかったのが、当たり前だけど、Botを止めてもScheduleは止まらない。そのあたりの実装忘れてて、テスト用チャンネルが地獄みたいなことになってた。

f:id:mugi1:20180128152458p:plain

これから作ろうと思ってるもの

  • Herokuのデプロイボタン置く
  • Finisherの概念って実はいらないのでActionに統合する
    • 冷静に考えると最後のActionをFinisherにすればそれでいいので、Finisherなんていらんかったんや、ってこのエントリ書いてて気づいた。
  • ランダムなURLを発行して、それをTriggerにする
  • 認証
    • URLさえ知ってればokみたいになってるので、超簡易的な認証をつけときたい
  • 他のChatツールに対応したい
    • なんとかなると信じてる

まだ途中だけど、作ってる感想としてはNuxt.jsがとても楽でいい感じ。 router書かなくて良いのは最高です。

Nuxt.jsのbuild&startが何をやってるのかをコードから追ってみる

以前、Herokuで簡易的なNuxt.jsのアプリケーションのSSRを確認するエントリーを書いた。

mugi1.hateblo.jp

書いたはいいけど、で...

なんでこれ動いてんの?

デプロイできたやった〜と言いたいところだけど、あまりにも動作がブラックボックスすぎるので、一体何がどうなっているのかを調べてみることにする。

Herokuへのデプロイ

node.jsアプリケーションをHerokuにデプロイする際は、デフォルトではpacakge.jsonに記載のstart scriptが実行される。

(参考: Deploying Node.js Apps on Heroku | Heroku Dev Center)

そしてさらに、デプロイ時には以下の設定を追加する必要がある。

 "heroku-postbuild": "npm run build"

つまりデプロイ時には以下の流れでビルドが実行されていることがわかる。

  1. nuxt build
  2. nuxt start

オフィシャルのガイドによると

  • nuxt build - アプリケーションを Webpack でビルドし、JS と CSS をプロダクション向けにミニファイします
  • nuxt start - プロダクションモードでサーバーを起動します(nuxt build 後に実行してください)

とのこと。

調べること

というわけで、以下を調べてみることにする。

  • next build では何をビルドして何を作っているのか
  • nuxt start では何を起動していて、どこでSSRをやっているのか。

nuxt スクリプト

そもそも nuxt というスクリプトのコードを確認してみる。

nuxt.js/nuxt at dev · nuxt/nuxt.js · GitHub

色々やってるけど、とりあえず

const bin = join(__dirname, 'nuxt-' + cmd)

という箇所があり、cmd の部分に指定したオプションが渡る。

つまり、nuxt-buildnuxt-start といったスクリプトが実行される。

nuxt-build

nuxt.js/nuxt-build at dev · nuxt/nuxt.js · GitHub

オプションのロードなどの後に、以下のようなコードがある。

...

if (options.mode !== 'spa') {
  // Build for SSR app
  builder.build()
    .then(() => debug('Building done'))
    .catch((err) => {
      console.error(err)
      process.exit(1)
    })
} else {
  ...

options.mode !== 'spa' という記述があるが、Nuxt.jsではSSRを使わないアプリケーションの構築もサポートしているので、その場合にビルド方法が変わるものと思われる。

https://ja.nuxtjs.org/guide#シングルページアプリケーション-spa-

デフォルトではSSRは有効なので、どうやら builder.build() がビルドの本体のようだ。

実態は Builderというクラス。コード的にはこのあたり。

https://github.com/nuxt/nuxt.js/blob/dev/lib/builder/builder.js#L107

処理の中で特に重要そうなものだけピックアップすると

  • this.nuxt.ready()
  • this.generateRoutesAndFiles()
  • this.webpackBuild()

かな〜と思われる。

this.nuxt.ready()

実体は Nuxt#ready が該当する。

https://github.com/nuxt/nuxt.js/blob/dev/lib/core/nuxt.js#L51

内部ではさらにModuleContainer#readyRenderer#readyを呼び出しており、デフォルトのものに加えて、任意に追加されたミドルウェアやモジュールをロードしている。

this.generateRoutesAndFiles()

webpackでビルドする前のファイルをテンプレートをもとに生成している。

例えば、pages/ の配下にコンポーネントを突っ込むだけで自動的にルーティングが生成されるのは、このあたりで動的にファイルを取得した上でテンプレートファイルからvue-router向けのjsファイルが出力されている。

pages/ に限らず、Nuxt.jsがいい感じにに解決してくれているものの多くがlib/app 配下にテンプレートとして用意されているっぽいので、生成されるファイルの元となるものが見たい場合はここを参照するとよさそう。

https://github.com/nuxt/nuxt.js/tree/dev/lib/app

this.webpackBuild()

みんな大好きwebpackビルド。

lib/builder/webpackの配下にwebpackビルドで必要なコンフィグファイルがまとまっている。

クライアント側とサーバ側でエントリーファイルが異なるので、client.config.jsserver.config.js が存在しており、それぞれについてビルドが実行される。

最終的な成果物はデフォルトだとプロジェクトルートから見て .nuxt/dist 配下に出力される。

nuxt-start

nuxt.js/nuxt-start at dev · nuxt/nuxt.js · GitHub

Nuxt#listen を実行している。

実行前に .nuxt/dist や、SSRを行う場合には .nuxt/dist/server-bundle.json の存在をチェックしているので、あらかじめ nuxt build が実行されていないと落ちる。

Nuxt#listen

https://github.com/nuxt/nuxt.js/blob/dev/lib/core/nuxt.js#L124

this.renderer.app.listen を実行しており、この app の実体は connectインスタンス。これで実際にNuxt.jsアプリケーションが待ち受け状態になる。

github.com

サーバサイドでのルーティングについては、初期化の過程でRenderer#readyがコールされており、そこからさらに辿ると以下のコードが見つけられる。

// Finally use nuxtMiddleware
this.useMiddleware(this.nuxtMiddleware.bind(this))

useMiddlewareは、connectインスタンスに対してMiddlewareを登録している。

// Use middleware
this.app.use(path, handler)

nuxtMiddlewareについては、内部で renderRoute をコールしており、この中で実際にレンダリングしたHTMLを返却している。

// Call renderToString from the bundleRenderer and generate the HTML (will update the context as well)
let APP = await this.bundleRenderer.renderToString(context)

bundleRendererにはvue-server-renderer#createBundleRenderer で生成されたバンドルレンダラがセットされており、.nuxt/dist/server-bundle.json が元になっている。細かいところまで追えてないが、URLとのマッピングはこのへんで解決しているものと思われる。

というわけで

コードを追ってみることで、事前のビルドで何をしていて、どのあたりでサーバサイドでHTMLを生成しているのかはなんとなくわかった。(ような気がする。)

深く読み込むというよりかは、大体の流れを追うようにコードを見ていったので、もしかすると一部で間違いとかもあるかもしれない。そうだったらスイマセン。

とりあえず、自分の中で完全に闇だった部分が少しわかったのは良かった。

感想としては、コードを見ていると「よくこんなの作ったな...」という気持ちになってきた。OSSコントリビュータに感謝しよう。

parcelでビルド結果から別ファイルを出力するプラグインをつくるまでの道のり

Misoca Advent Calendar 2017 - Qiita : 18日目のエントリーです。


以前、RailsでWebpackerをparcelに置き換えるにはどうしたら?という記事をQiitaに書いた。

qiita.com

この中で、しれっと以下のようなことやっている。

  • ビルド結果からfingerprint付きのファイルを出力したい
  • マッピングが定義されたjsonファイルを吐き出したい
  • というプラグインを作った

実際に作ったプラグインはこれ。

github.com

コード自体は大したことないが、作成する際にドキュメントに書かれている方法だけでは厳しく、parcel自体のコードを読むことでそれっぽいものが作れたという経緯があった。なかなか面白かったので完成までの道のりを記事にしてみる。

欲しかったもの

元の記事にも書いてある内容と同じになりますが、簡単に言うと entry.jsというファイルをビルドした場合に、

  • entry-aUjc10lx83jjXwlechKSaxa3Le9kSjfe.js
  • manifest.json

のようなファイルを吐き出し、manifest.jsonには

{
  "entry.js": "entry-aUjc10lx83jjXwlechKSaxa3Le9kSjfe.js"
}

が記録されている、みたいなことをしたい。

プロダクションで利用する際に、ビルド結果に差分があった場合にキャッシュではなく新しいファイルを参照するように、fingerprintが付与されたファイルを配信したいが、都度scriptタグのsrcを書き換えるのはメンテナンスのコストが高く、別途jsonファイルにマッピングを記録し読み替えるようにしたい、といった具合。

つまり、

  • View上 : 何らかのヘルパー経由でentry.jsを指定
  • 何らかのヘルパー : manifest.json経由で実際に読み込むjsファイルを解決
  • ブラウザ : entry-aUjc10lx83jjXwlechKSaxa3Le9kSjfe.js をロード

といった流れになる。

parcel自体の機能で実現できないか?

そのような機能はまだサポートされていないように見えた。

オフィシャルドキュメントのProductionビルドのあたりを見てみると、htmlからjsをロードした状態とし、htmlをビルド対象とすることでjsのファイル名をユニークなものにしてくれる、いった機能はすでに提供されているっぽい。

SPAを構築する場合などではこれで充分そうだけど、今回の目的とは少し違うようだった。残念。

参考: https://parceljs.org/production.html#set-the-public-url-to-serve-on

自力でなんとかする

ポイントとしては、 「ビルド結果が出力された後の時点で実行したい」といった一点かと思う。

parcelのオフィシャルドキュメントを見ていると、3つの拡張方法が存在するようで、以下のように理解した。

  • Asset
    • ファイルに応じてパース、依存解決、transformなどを行い、変換後コードを生成する
  • Packager
    • 出力ファイルタイプに応じて、Assetで変換したものを取り纏めて実際のファイル出力を行う
  • Plugin
    • parcelビルドを非標準的な方法で自由に拡張する場合に利用する

単純に扱えるファイルの種類を増やしたい場合などはAsset/Packagerを利用するのが正しいアプローチのようだが、今回実現したいことにはファイルタイプは関係なく、純粋に出力結果となるファイルのみを考慮したいので、Pluginを利用するのが正しそう。

プラグインを作る

まずはドキュメントに記載されているプラグインの説明を見てみる。 (https://parceljs.org/plugins.html#plugin-api)

Parcel plugins are very simple. They are simply modules that export a single function, which is called by Parcel automatically during initialization. The function receives as input the Bundler object, and can do configuration such as registering asset types and packagers.

Publish this package on npm using the parcel-plugin- prefix, and it will be automatically detected and loaded as described below.

記載されているサンプルコードも抜粋。

module.exports = function (bundler) {
  bundler.addAssetType('ext', require.resolve('./MyAsset'));
  bundler.addPackager('foo', require.resolve('./MyPackager'));
};

上記を見るからに、単一のプラグインをpackage.jsonに追加するだけで、一気に対応するサポートファイルを追加したりすることができるようだ。

これはこれでとても便利そうですが、Asset/Packageは今回は使わないので、違うアプローチを取る必要がありそう。

では、引数で与えられている bundler が一体何者なのかを追ってみる。

Bundler

実際のコードは以下。

https://github.com/parcel-bundler/parcel/blob/master/src/Bundler.js

Bundlerクラス自体は、CLIから実行した際にインスタンス化されており、外部からはbundleメソッドのみがコールされる。

parcelによるビルド処理本体の起点であり、本体といっても良さそう。

ただ、addAssetType/addPackagerといったメソッドは存在するが、他にそれらしいメソッドは存在せず。どうやってビルド終了時に処理を実行させれば良いのだろう?

そこで、ポイントとなるのはクラス定義となる部分。

class Bundler extends EventEmitter {

nodejsを利用している方ならお馴染みの、EventEmitterを継承している。 (https://nodejs.org/api/events.html)

つまり、プラグインが受け取るbundlerは何らかのタイミングでイベントをemitしてくる(かもしれない)ことがわかる。

emitしている箇所

以下の2つが該当する。

  • this.emit('buildEnd'); - code
  • this.emit('bundled', bundle); - code

ビルド終了時に処理をしたいので、buildEndかな?と思ったけど、buildEndは成功・エラーにかかわらずemitされてしまう模様。

引数のbundleを参照することで引数に指定した出力ディレクトリなども動的に解決することが出来るので、bundledを参照するのが正しそう。

最終的に完成したプラグインのコード

というわけで、上記を元に作ったプラグインが以下。それほど大きくないので全部貼ります。

const path = require('path');
const hasha = require('hasha');
const fs = require('fs');

module.exports = function (bundler) {
  const logger = bundler.logger;

  const readManifestJson = (path) => {
    if (!fs.existsSync(path)) {
      logger.status('✨', 'create manifest file');
      return {};
    };

    logger.status('🖊', 'update manifest file');

    try {
      return JSON.parse(fs.readFileSync(path, 'utf8'));
    } catch(e) {
      logger.error('manifest file is invalid');
      throw e; 
    }
  };

  bundler.on('bundled', (bundle) => {
    const dir = path.dirname(bundle.name);

    logger.status('📦', 'PackageManifestPlugin');
    logger.status('📁', `     dir : ${dir}`);

    const f = bundle.name;
    const hash = hasha.fromFileSync(f, { algorithm: 'sha256' });
    const ext = path.extname(f);
    const basename = path.basename(f, ext);
    const hashFile = path.join(dir, `${basename}-${hash}${ext}`);

    logger.status('✓', `  bundle : ${bundle.name}`);
    logger.status('✓', `        => ${hashFile}`);    

    // create hash included bundle file
    fs.createReadStream(f).pipe(fs.createWriteStream(path.resolve(dir, hashFile)));

    const manifestPath = path.resolve(dir, 'parcel-manifest.json');

    logger.status('📄', `manifest : ${manifestPath}`);

    const manifestValue = readManifestJson(manifestPath);
    manifestValue[path.relative(dir, f)] = path.relative(dir, hashFile);

    fs.writeFileSync(manifestPath, JSON.stringify(manifestValue));
  });
};

parcel-plugin-bundle-manifest/BundleManifestPlugin.js at master · mugi-uno/parcel-plugin-bundle-manifest · GitHub

だいたい以下のような流れで処理してる

  • bundler.on('bundled', ... が処理の起点
  • bundle.name で出力されたファイルへのパスを取得する
  • ファイル内容からhashを作成し、fingerprint付きのファイルをコピーする
  • manifest.jsonファイルを作成する。すでに存在する場合はマージする
  • manifest.jsonファイルを書き込む

終わってみれば大した内容ではない。

あとは、名称を parcel-plugin-xxx といった形でnpmに公開すると、parcelビルド時にpackage.jsonのdependenciesを元に自動的にプラグインが実行されるようになる。

最初は parcel-plugin-manifest とかにしようかと思ったけど、私が一等地っぽい名前を取ってしまうのは如何なものかと思ったので、parcel-plugin-bundle-manifestにした。

www.npmjs.com

作ってみて

EventEmitter周りとかは後々追記される可能性もありそうだけど、ドキュメントだけではなく、コードも見るのが大事だよな〜と改めて感じた。

プラグインを作る過程の中で、基本的な動作とかもなんとなく理解できるようになってくるので、とても勉強になってよかった。

manifestファイルの吐き出しについては、もしかすると将来的にparcel自体の機能としてサポートされるようになるかもしれない。自分としてもそちらのほうが安心なので、ぜひ期待したい。

parcelについて

少しズレるけど、parcel自体についての個人的な所感。(個人的なものです)

まるで設定なしで魔法の如く動いているように理解してしまいそうだけど、実際にはwebpackのloaderなりpluginなりがやってくれていたことを裏側で隠蔽して意識しないで良いようにしてくれているだけなので、フロントエンドのビルド周りで理解しないといけないことが格別減るというわけではないと思う。

(知らないまま使っても良さそうだけど、ハマったときに即死しそう。)

でも、ゼロコンフィグで動かそうというのはとても快適に感じたし、cssファイルをエントリーに取ってビルドできる点などは、webpackのExtractTextPluginにつらみを感じていたので「そうそう、これがやりたかったんだよ!!」という気持ちになった。

これからも出来ることは増えていくだろうし、webpackに取って代わる日が来るのも有り得るかもしれない。

とりあえずどうなっても良いように、webpackへのガッツリ依存している部分を少しずつ外しておくことで、いつか幸せになれる日が来ることを信じている。


さて、明日は弊社代表の@toyoshiがラズパイで頑張った話を書いてくれるようです。

前回のエントリが400近いブックマーク数になるほど読まれていたので、今回も注目ですね!!

Nuxt.jsとHerokuでお手軽にSSRを体験する

これは、Misoca Advent Calendar 2017 の6日目のエントリーです。

SSR

フロントエンドを駆使したアプリケーションを構築すると、 どこかのタイミングでSSR(ServerSideRendering)と向きあうことがある。

最近はクローラが優秀なので、SSRを行わないSPAでもいい感じにインデックスしてくれたりするので、そこまで神経質になることもないことが多いですが、ファーストビューの速度などを突き詰めていくと、やっぱり選択肢としては使える状態にしておきたいところ。

実際にやろうとすると簡単なものではなく、Railsの場合はreact_railsのようなgemに頼ったりすることになったり、頼っても大変だったりするのが現実だと思う。

Next.js & Nuxt.js

といったわけで、Next.js/Nuxt.jsの名前をちらほら見かけるようになったと思う。

オフィシャルのガイドなどをざっと見て「ふむふむ」程度で終わっていたけど、触りだけでも一度試してみることにした。

個人的に、以前はReactがメインだったが、最近ではVueを使うことのほうが多いので、今回はNuxt.jsのほうをやってみることにする。

Nuxt.js

https://ja.nuxtjs.org/guide

Nuxt.js とはユニバーサルな Vue.js アプリケーションを構築するためのフレームワークです。

だそうです。

本気でVueでのSSR可能なSPAを組もうと思うと、以下のような要素が出てくる。

  • Vue
  • vue-router
  • vue-server-renderer
  • vuex
  • babel
  • webpack
  • 場合によってはexpressなど

その上で、アプリケーション自体の構成からルーティングルールを検討した上で、サーバサイド、フロントエンドどちらのレンダリングでも耐えうる形で作っていく必要があり、普通につらい。

Nuxt.jsを使うことで、ざっくりこのあたりを隠蔽してくれるので、とりあえずの出だしとして細かいことを考えずにコードを書き始めることができる。

Nuxt.jsの敷いたレール上に乗るぶんには楽できるけど、カスタマイズしようと思うと細かい知識が必要になるよ、みたいな話なので、考え方としてはRailsWayとかに近いのかなって印象。

やってみよう!

ゴール

  • Heroku上でNuxt.jsで作ったアプリケーションのSSRを確認できる

やらないこと

  • 凝ったカスタマイズは一切しない

インストールする

スターターテンプレートというものが用意されているので、そちらを利用する。

https://ja.nuxtjs.org/guide/installation

$ npm install -g vue-cli 
$ vue init nuxt-community/starter-template nuxt-trial

# 色々聞かれるけどデフォルトで良いのでEnter連打

$ cd nuxt-trial
$ yarn install

試しに動かしてみる。

$ npm run dev

http://localhost:3000/ にアクセスすると、以下のようなページが表示される。

f:id:mugi1:20171203001108p:plain

なんか動いてるようだ。

ちなみにnuxtのコードを見てみると、サーバ自体はconnectを利用してるっぽかった。

このままHerokuに放り込んでみる

インストールされたものはとりあえずあとで見るとして、このままHerokuに放り込んで動くかやってみよう。

FAQにずばりそのままのページが用意されている。

https://ja.nuxtjs.org/faq/heroku-deployment

心を無にして上から叩く。 (herokuのcliはインストールしてあるものとする)

$ heroku create
$ heroku config:set NPM_CONFIG_PRODUCTION=false
$ heroku config:set HOST=0.0.0.0
$ heroku config:set NODE_ENV=production

package.jsonに追記して、Heroku側でよしなにビルドしてくれるようにscriptsを足す。

  ~
  "scripts": {
    ...
    "heroku-postbuild": "npm run build"
  },
  ~

デプロイする

git push heroku master

これでブラウザからHerokuのアプリケーションを見てみると、ローカルと同様に動いていることが確認できる。

簡単。

SSRをやってみよう

では、肝心のSSRがちゃんと動くかを確認してみる。

確認のために以下のようなページを作ってみる。

  • 2ページあって行き来できる
  • ページごとに初期化時に何らかのAPIのデータを取得して表示する

API自体はなんでもいいので、THE COLOR APIというサイトから適当にカラー情報を取得することにする。

http://www.thecolorapi.com/

コンポーネントをつくる

非同期データのためにaxiosを追加しておく。

yarn add axios

色情報を取得するAPIとカラースキーマを取得するAPIの2つがあるので、その2つをページにしてみる。 内容は、ランダムなカラーをもとにデータを表示する超簡易的なもの。

pages/color.vue

<template>
  <section class="container">
    <div>
      <span>color</span>
      <span>|</span>
      <span><nuxt-link to="/scheme">scheme</nuxt-link></span>
    </div>

    <div><img :src="data.image.named" /></div>
  </section>
</template>

<script>
import axios from 'axios'

export default {
  async asyncData () {
    const hex = Math.floor(Math.random() * 16777215).toString(16)
    const { data } = await axios.get(`http://www.thecolorapi.com/id?hex=${hex}`)
    return { data }
  }
}
</script>

pages/schema.vue

<template>
  <section class="container">
    <div>
      <span><nuxt-link to="/color">color</nuxt-link></span>
      <span>|</span>
      <span>scheme</span>
    </div>

    <div><img :src="data.image.named" /></div>
  </section>
</template>

<script>
import axios from 'axios'

export default {
  async asyncData () {
    const hex = Math.floor(Math.random() * 16777215).toString(16)
    const { data } = await axios.get(`http://www.thecolorapi.com/scheme?hex=${hex}`)
    return { data }
  }
}
</script>

この内容でコミットし、改めてHerokuにpushする。

動きを確認してみる

/color

f:id:mugi1:20171203001140p:plain

/scheme

f:id:mugi1:20171203001455p:plain

ちゃんと動いてるっぽい。

(ちなみに画面上で遷移しようとすると、クライアント上でhttpsからhttpのXHRとなるので、Mixed Contentのエラーで落ちる。対処するのが面倒なので、Chromeの場合はURLバー横のアイコンから強制的に許可して回避する。)

Chrome DevToolsのNetworkタブを使って、画面上のリンクで遷移した場合のリクエストを見てみても、初回表示以外はxhrリクエストになっていることがわかる。

f:id:mugi1:20171203001635p:plain

肝心のSSRはということで、ダイレクトアクセスした直後にページのソースコードを見てみる。

f:id:mugi1:20171203002004p:plain

API経由で取得する画像へのURLがファーストビューとなるHTMLに含まれていることが確認できる。

というわけで

これだけであれば、たいした苦労もなく、SSR可能なVue.jsアプリケーションをHerokuで動かすことができた。

使い込んでいくと細かいカスタマイズなどで苦労することはありそうだけど、この簡単さは強力だな〜という印象。

もう少しちゃんとしたアプリケーションを作って検証してみてもいいかなという気持ちになった。

明日は @oosawatechによる「野生化」の話です。

野生化...!?

「プロを目指す人のためのRuby入門」が躊躇なくオススメできる完成度だった

前々から色々とお世話になっている西脇.rbの伊藤淳一さん(@jnchito)が、Rubyの入門書を出版されることになり、ちょうど私が読者のターゲットに近い存在ということで1冊見本誌を頂きました。

というわけで、書評というと生意気ですが、写経も交えつつ読了したので、感想を書いてみます。

ちなみに私自身は

  • プログラマとしては9年くらいやってる
  • Ruby歴自体は2年くらい
  • 業務で使うようになったのはここ1年くらい

といった具合。

実践感の強さ

まず、3章(=かなり序盤)の段階で「例題解くときはテスト自動化をしような」という説明が出てきたときにこれを感じた。

写経できるような入門書はいくつか見てきましたが、初っ端から「テストは自動化して効率よくやっていこうな」というスタンスの本はなかなか無いのでは。

個人的な経験では、通常入門書を写経していくと、「とりあえずコードを書く→動す→なんか落ちる→コードとにらめっこする」みたいなことになることが多かった覚えがある。(そしてイヤになる)

本書の場合、まずはテストを書いてエラーにしてから、実際のコードを書いてテストが通るという流れを踏んでいくので、1つずつ着実に理解していける感がある。

(さらにRubyの入門しながら、しれっとTDDの基礎的な部分も触れられるのでお得感がある)

現実を教えてくれる

読み進めていくと、

  • 実際にはこれは使わないことが多い
  • 幾つか書き方があるけど、こっちが主流
  • こういうのが不具合の原因になりやすい

みたいな、言語仕様・文法だけを学んだだけでは把握できない部分に言及している箇所がかなり多いことに気付く。

実際このあたりは業務で使うときにかなり重要で、学ぶタイミングが少なかったり、単純にググるのが難しい概念だったりもする。

そういったところで都度道筋を教えてくれるのは初心者には嬉しいように感じた。

また、英語で出てくるバックトレースなどについて「英語が苦手な人も辞書を片手に頑張ってね」という記述があるのもなかなか新鮮だった。 わりと入門書では「今は理解しなくていいですが〜」みたいなことを書いてあったり、そもそも触れられないみたいなことも多いですが、ぶっちゃけ理解しなくていいわけがないので、そのあたりの現実突きつけてくれるのは大事。

実際のところ私自身も英語は苦手なので「うっ、すいません..!」って感じだった。

プログラミング初心者向けではなく、Ruby初心者向け

書籍内の冒頭でも触れられていますが、プログラミング自体が初心者という人にはハードルが高めかもしれない。基本的な概念の説明は無いので、「変数...?」みたいな状態だと厳しい。

逆に言うとそのぐらいちゃんとターゲットを絞った本なので、合致した層が手に取ると、かなり参考になる内容が多いと思う。

Rubyを使っていて、「自分はもうRubyバリバリ使えるぜイェーイ!」って言える人以外は、とりあえず買って目を通してみるといいかもしれない。私自身も、もう完全な初心者ではないかな〜と勝手に思っていたけど、普通に知らないことがボロボロ出てきたので、いい感じに振り返れてとても良かった。

写経しやすい

全体を通して、そもそも写経を想定して作られてるなって感じがした。

基本的には各章に1つ練習問題があるので、ざっくりとした理解チェックをするには最低限それをやればいいし、ガッツリ学びたければ、都度記載されてるサンプルコードを全て写経していくこともできる。

ちなみに紙質も違うらしく、開いたときに維持しやすいらしい。

実際維持しやすかった。すごい。

とりあえず理解度が全然違うので、写経しながら読むことをオススメする。

適宜必要な前提知識を教えてくれる

人によっては正規表現の章で、「正規表現知らないならそっち勉強してから戻ってきてね!」と、一度書籍の外に追い出されるという体験をすることになる。

このあたりは「プロを目指す人のための」という名前がピッタリだなという印象だった。実際のところ、根本の部分がフワフワしてると理解できないので、そのあたりをいい加減にしたままRubyでの正規表現の使い方を教えられても困るわけで、「知らないなら知ってから戻ってきてね!」というスタンスはとても潔いし、そうだよな、って感じがした。

トータルでの感想

私の個人的な感情とかは無視しても、かなりの良書だと思った。

Qiita等での伊藤さんの記事で普段お世話になっている人も多いと思うけど、あのわかりやすさがそのまま本になったような感覚。

周りにRubyに精通した人がいて質問できる環境なら良いが、そうじゃない場合には学ぶのが難しい要素というのが実際それなりにあって、本書の内容である程度カバーしてくれるのがとてもいいと思う。

2年前のRuby勉強し始めたころにこの本があれば〜〜〜!!と何度も思った。SIerからRuby使うようなWeb系への転職を考えている人とかには最適の1冊になるのでは。

ターゲットを絞っているぶん、合致するとかなり刺さる内容が多いのかもしれない。

  • 「これからRuby勉強したいんですけど〜」
  • 「それなりに書けるようになったけど、いまひとつ自信ないわ〜」

みたいな人がいたとすれば、今後はこの本をオススメすることになると思う。(私は後者だったので刺さった)

さいごに

見本誌で頂いたという立場を考えると、サクラのような書評になってしまっているように見えるかもしれないけど、内容をじっくり読んだ上で良い内容だと判断したので、躊躇なく書かせて頂きました。

これはもう、「プロを目指す人のためのRails入門」の出版が待ち遠しいですね!