2015年の振り返り
新年早々いろいろあって振り返ってる余裕が無かったので、それっぽいのをハリボテで。
2015年に書いた記事
当ブログ
8記事!少ない!!
Qiita
2記事!少ない!!
2015年に発表したトーク(public)
www.slideshare.net
www.slideshare.net
www.slideshare.net
www.slideshare.net
4回!ま、まあ...
振り返り
JavaScript一色だなぁ...
ブラウザサイドからNodeJSまで。 ただ、使っているツールや関心の範囲はこの一年でだいぶかわってきたのかなと思う。
フロントエンドツールの開拓、WAFのアーキテクチャ設計、それに関連する各種ライブラリの選定等など、外部モジュールを触るのが多い一年だった。 MVCよくわからんっていう時期から、FluxやReduxを通じてアプリのアーキテクチャを考えていく中で、 単一のライブラリに頼るのも、複数を組み合わせるのもそれなりに模索が必要だと感じているのが年末だった。
2016年の抱負
ツールは揃ってきているけど、いま欲しいものが現状ない。 そういう「何がほしいのか」という部分を自分が適切に理解し、それに即したライブラリを模索・制作していきたいなとは思っている。
年が明けてから、FluxやReduxによって手放そうとしたMVC,DDDを取り戻す方向性を考えている。 技術的にModelとはどういうものか、というより、Model的な考え方をFlux上に載せつつ、効率的に開発できないかと考えている最近。
僕はもともとMVCについての理解が浅かったので、改めて勉強しようかなと思う。
余談
新年早々部屋が半分水浸しになって大変大変つらかったけど、この先は楽しい一年にしていこうとおもったのでした
#gotandajs でトークしてきました
ブログを書くまでがgotandajsだ!ってことで。
gotandajs
五反田や沖縄で JavaScript の勉強する(conpassから引用)会で、AngularJSにFluxとRiotJSを導入したというトークをしてきました。
発表資料
発表について
不足してたなっていう状況を幾つか補足。
- モバイル中心のウェブアプリ(ゲーム)
- JavaScriptを触る人は基本的に僕一人
- 実際にFluxを導入し始めて発表内容の形に持ってくるまで3,4ヶ月くらいかけてる
injector
をRiotJSから参照できるようにするのってどうなの?- 僕一人だから許容してたけど複数人で触るとリスクのほうが高そうなので、ぶっちゃけあんまオススメしない
全体を通して
- 思っていたよりガチで濃ゆい人が集まっていた -- flux, angular, riotを知ってる人が思いの外多くいた -- 他の人のトークも結構濃ゆい話題だった
- Mithrilやreduxといった最近気になっているけど本気で触る余裕がない~なモノとかの話題もあって嬉しい -- トレンドをみんな抑えてる感じで、初回なのにレベル高い
懇親会ネタ
ぱっと思い出せたネタを書き出してみる
- みんな結構、FluxとかReduxとかの設計面で悩んでいる
- Fluxやってると全体でstateを管理したいけど、Reduxみたいに全部管理するのはちょっと
- 程よくComponentとStoreで分散させたい
- ルーターとかどうしてる?
- ReactRouterとか学習コスト高すぎ
- redux-routerは良さそう?
- 最近のライブラリだと何を選ぶ?
- 僕なら初期開発の楽さでAngular
- Vueはすべてがそこそこ良いので安定
- Reactはなんかいろいろめんどくさい、が思想はいい
- Mithrilは
msx-loader
が壊れてて... - などなど...
- テストどうしてる?
- reduxはサンプルをベースに
- fluxとかでロジックはテストし易い
- Reactはテストしやすい...んだろうけど...「「...」」
- Viewのテストのコストが高すぎる
- E2Eでテストしたほうがカバーする範囲広くてリファクタリングとかしやすそう
- 「何」をテストしたいのか? 「ユーザに届ける価値」「どうやってテストすればいいんだー!」
- DIは料理番組
- 下ごしらえ済みの素材を受け取って料理にするイメージ
- だからinjectorをRiotで受け取るのどーなん?って思った
- 静岡アツい
- 次回はG社で
設計の話からビルドシステムの話、他の言語やコミュニティの話、かなり幅広く盛り上がっていた印象
感想
とても楽しい会だった。
なんかみんな似たようなことで悩んでいたり、試そうと思っていたことを試したっていうFBもらえたり、なかなかいい知見を得られたと思う。 コンポーネントのあり方とか、Fluxの設計とかはまだまだ議論の余地もあるだろうし、こういう機会があるのはありがたい。
新しいライブラリも短いサイクルでバンバンでてくるようなJavaScript界隈においてそれぞれが試して意見を交換できる会は貴重だ。
みんなのレベル感も結構近いのでは?と思うくらい懇親会では盛り上がっていたし、本当に楽しかった。
というわけで、大変満足でした。次回も期待します。
お疲れ様でした!
MINILA AIR US67 を購入したのでMac用にカスタマイズしたメモ
商品
Majestouch MINILA Air US67キー 茶軸
設定
caps lock
⇔ctrl
- 鉄板
- [修飾キー設定]
cmd
⇔option
- [修飾キー設定]
caps lock
→cmd
- caps lockなんて要らん
- windows時代の
ctrl
+v|c
の癖が抜けなくてな...
- [ショートカット] 入力ソース/前の入力ソースを選択
cmd
+ESC
- 左手だけで切り替えたいけど大きくブラインドタッチの状態から手を動かしたくないので
所感など
ctrl
+alt
+fn
で接続設定出来るみたいなことが書いてあったけど、結局connect
ボタン使わないと初期設定できなかったESC
⇔~
するかも- キーの差し替えできるの便利
- 押し心地と触り心地が以前使ってたMajestouchと若干違くて新鮮な心地よさがある
指定期間でDOMを出し分けてくれるangular-periodを公開しました
angular-periodとは
AngularJS のDirectiveで、期間を指定するとその 期間前、期間中、期間後でDOMの表示を切り替えてるようにするものです。
例えば、なんかの応募とかで、以下の様なHTMLをテンプレートエンジンやJavaScriptなどを使って出し分けることがある時に便利です。
angularJSを使った一番お手軽なケースとしてはこんなかんじでしょうか。
angular.module("campaign", []) .controller('CampaignCtrl", function () { this.now = new Date().getTime(); this.start = new Date('2015-08-01T00:00:00').getTime(); this.end = new Date('2015-08-31T23:59:59').getTime(); });
<div class="campaign" ng-controller="Campaign as campaign"> <!-- 期間前の表示 --> <div class="form is-previous" ng-if="campaign.now < campaign.start"> <p>○月☓日から応募できます</p> <!-- 期間中の表示 --> <form class="form" ng-if="start <= campaign.now && campaign.now <= campaign.end"> <button type="submit">応募する!</button> </form> <!-- 期間後の表示 --> <div class="form is-after" ng-if"campaign.end < campaign.now"> <p>応募は締め切りました</p> </div> </div>
angular-periodを使えば、以下のように書けます。
angular.module('campaign', ['angularPeriod']);
<div class="campaign" ng-period ng-period-start="'2015-08-01T00:00:00'" ng-period-end ="'2015-08-31T23:59:59'"> <!-- 期間前の表示 --> <div class="form is-previous" ng-period-when="previous"> <p>○月☓日から応募できます</p> <!-- 期間中の表示 --> <form class="form" ng-period-when="during> <button type="submit">応募する!</button> </form> <!-- 期間後の表示 --> <div class="form is-after" ng-period-when="after"> <p>応募は締め切りました</p> </div> </div>
開始と終了を渡すと、その期間の前、中、後のHTMLを選択してDOM Treeに組み込みます。 最も単純なケースではコントローラーすら不要なので、ロジックやテストを大幅に削減できます。
また、angular-periodはリアルタイムな反映及び、setTimeoutの上限値である32bit intを超える値もサポートできるようにしているので、長期にわたる期間であってもある程度は許容されます。
インストール
npm, bowerともに公開しているので必要な方を利用してください。
npm install angular-period
bower install angular-period
日付の指定とパースについて
利用可能な値
上記には文字フォーマットを用いた例を記載しました。 ただ、実際にプログラムする上で文字列だけでは様々な不便なことがあるでしょう。
ngPeriodは Date
オブジェクトに変換可能な値をサポートしています。
具体的には、
- Date
のパース可能な文字列
- Date
オブジェクト
- momentjsなどのDateオブジェクトに渡せるオブジェクト
先ほどの例で、例えばmomentjsを使う場合は以下の様に書けます。
angular.module('campaign', ['angularPeriod']) .controller('CampaignCtrl", function () { this.start = moment('2015-08-01 00:00:00'); this.end = moment('2015-08-31 23:59:59'); });
<body ng-controller="Campaign as campaign"> <div class="campaign" ng-period ng-period-start="campaign.start'" ng-period-end ="campaign.end"> <!-- 期間前の表示 --> <div class="form is-previous" ng-period-when="previous"> <p>○月☓日から応募できます</p> <!-- 期間中の表示 --> <form class="form" ng-period-when="during> <button type="submit">応募する!</button> </form> <!-- 期間後の表示 --> <div class="form is-after" ng-period-when="after"> <p>応募は締め切りました</p> </div> </div> </body>
至ってシンプル。
パースできない値
Dateオブジェクトでパース可能な値はブラウザによって異なります。
パースできない値を渡すと、Dateは Invalid Date
というオブジェクトを返すので、
それをそのままDateとして扱おうとすると、Nanだったり不適切な値になりえます。
困るケースでは、ChromeとSafariで利用可能なDateのフォーマットが異なるという点です。
Chromeは YYYY-MM-DD HH:mm:ss
という値をサポートしていますが、SafariではInvalid Date
が返ってきます。
これは失念すると非常に気づきにくくリスキーで、常にChromeとSafariでの動作確認を要求されるため大変不便です。
angular-periodでは、Invalid Date
になるような値を渡された場合その時点でErrorオブジェクトをthrowするようにはしています。
JavaScriptのDateオブジェクトは非常に複雑かつブラウザ間で挙動が違うので、それを吸収するよりは、すでに実装され安定的に運用されている他のモジュール(momentjsのような)を利用することを推奨します。
まとめ
というわけで結構便利なモジュールを書いたつもりで居るのでどうぞお試しあれ。
node-hariko にクリパラメータによってモックデータを割り振る機能を入れた
欲しかった機能をいれたかったので、 hariko を v1.1.0 に更新しました。
更新内容としては、複数のリクエストをExamplesに設定している時に、リクエストパラメータがマッチするやつを優先的にレスポンスさせるような機能を追加しました。
multipe example
API-Blueprintでは一つのアクションに複数のレスポンスを記載できます。
# GET /messages{?page} + Request /messages (application/json) + Response 200 (application/json) パラメータが無いときは `page=1` としてデータを返す + Body [ /* message arrays */ ] + Request /messages?page=2 (application/json) + Response 200 (application/json) + Body [ /* message arrays */ ] + Request /messages?page=9999 (application/json) + Response 200 (application/json) 存在しないページをしたら空の配列(`[]`)を返す + Body []
モックサーバのレスポンスとの兼ね合い
複数のExampleを定義をした時、リクエストとレスポンスは一つのActionの中に配列で格納されるんだけど、drakov ではキーのマッチングで優先順位を決めてマッチングしてたけど、そんなことよりキーと値が一致したらそいつを優先的に返して欲しい。
上記の例だと
|GET /message|ひとつめ| |GET /message?page=1|ふたつめ| |GET /message?page=2|ふたつめ| |GET /message?page=9999|ふたつめ|
みたいに帰ってきて、9999にリクエストしたら空のレスポンスが帰ってこない!!みたいになる。 いくら、json-outputの機能があるとはいえ、毎回毎回書き換えるのはだるくて仕方ないし、ドキュメント上で明示しておきたい。
クエリパラメータのでマッチングして完全一致したやつを優先的に出力する
node-hariko/resource_spec.js at master · rymizuki/node-hariko · GitHub
こんな感じで。 クエリパラメータの値がMarkdown上で定義されて居て、かつ一致するものがあればそれをレスポンス、 一致するものが無かったら先頭で定義しているものをレスポンスという形にした。
|GET /message|ひとつめ| |GET /message?page=1|ひとつめ| |GET /message?page=2|ふたつめ| |GET /message?page=9999|みっつめ|
を返してくれるようにした。
まとめ
- リクエストとレスポンスを密に連携してよりデバッグしやすい環境を作りました!
todo
- warningsをいまちゃんと出力してないので教えてあげてほしい。
- できれば、コードを示して「どこでwarningsでているのか」をもっとわかりやすくしたい
Introduction to Hariko API Server with API-Blueprint
はい、harikoの紹介です。
harikoとは
harikoとはNodeJS製のAPI MockServerであり、APIドキュメンテーションツールであるAPI Blueprintのエコシステムです。
使い方
npmからインストールして下さい。
npm install -g hariko
harikoはAPI-Blueprintのドキュメントをベースにレスポンスを返すAPI Serverを立ち上げるので、 まずはAPI-Blueprintの仕様に沿ったmarkdownが必要です。
# GET /api/message + Response 200 (text/plain) hello world
適当に、 docs/api/message.md
とかに保存します。
harikoのサーバを立ち上げます。 CLIから以下のように打ち込んで下さい。
hariko -f 'docs/api/*.md'
[INFO] Running Hariko Server ... http://localhost:3000
とshellがレスポンスしたらサーバの実行完了です。
ブラウザとかから http://localhost/api/message を叩いてみてください。
hello world
と表示されるはずです。
harikoの機能たち
詳細はREADME.md とか見て下さいねって感じなんですけど、便利!!!と思って作った機能を幾つか紹介しませう。
markdownファイルの一部除外
drakovになくて地味に不便だったので入れた。 APIサーバに載せたくない一部ファイルを任意で除外するやつ。
glob形式で読み込む奴はブラックリストで弾ける用にしてほしい気持ち。
hariko -f 'docs/**/*.md'\ --exclude 'docs/metadata.md'\ --exclude 'docs/overview.md'
watch
ファイルを監視して、更新したらサーバを再読み込みします。 これは gaze をそのまま使ってます。gruntやgulpやらで使ってる奴。
gruntやらgulpやらのplugin書くときにいちいちreload書くの面倒でしょ?って思ったので。
hariko -f 'docs/**/*.md' -w
proxy
API-Blueprintに定義されていないパスにリクエストが飛んできたら、 指定したサーバにプロキシするオプション。
stubcellの全部のAPIリソースを一斉に移管するのが辛かったので、逐次移行できるようにしたいが為に用意したの。
application → hariko server → stubcell
hariko -f 'docs/**/*.md' --proxy 'http://localhost:8100'
output
これ!!!まじべんりだから!!!べんりだから!!!! 指定したディレクトリにレスポンスデータを吐き出すオプション。
stubcellやeasymockみたいにJSONファイルを弄れば次のレスポンスから結果が変わるという、手動アプリケーションのための仕組み。 リロードしたら全部リセットされるので注意ね♪
hariko -f 'docs/**/*.md' -o 'api/'
verbose
ログレベルをverboseに設定します。 リクエストのデータとか、その辺のデバッグに使うといいかも?
ログ周りは自前だけどそれなりに気を配って書いた気持ち。
hariko -f 'docs/**/*.md' -v
harikoを作った背景
もともと、いまいる会社でSPA (Single Page Application) の開発で、UIファーストを実践していたのですが、どうにもAPIのドキュメンテーションが不足し、サーバとフロントエンドでやりとりの齟齬や過去のリソースについての確認があったり、まあ、ドキュメント欲しいよねって感じでした。(主に僕が
過去に easymock を使っていたのですが、ドキュメントの記法が独特で、メンテナンスもしばらくされていなかったので次のプロジェクトからは、 stubcell を使うようになりました。
どちらのツールも、JSONファイルをレスポンスとして返却してくれるツールだったんですが、 stubcellはJSON5に対応しているし、レスポンス内容を細かく設定できるしで好んで使っていました。
でもさすがに、JSON5だからといってjsonファイルにコメントアウトでドキュメンテーションするのは読みやすいとはいえない状況...
その当時(大体2015年の2月あたり?)からAPI-Blueprintの存在は知っていて、でもドキュメントMarkdownとは言いつつも学習コスト高そうだなぁ、エコシステムもどうなんだろうなぁと思っていたのでした。
時は経ち、「やっぱりドキュメント欲しいね」ってなって、改めてAPI-Blueprintを検証したのでした。
改めて調べてみるとエコシステムはなかなか充実しており、aglio なんかは美しいドキュメントを生成してくれて素晴らしいなぁと思っていたので、ついでに drakov というAPI Mock Serverがあるそうなのでそれも一緒にインストしました。
やーべんりべんり、と思っていたのはつかの間。 drakovを使っていた上で幾つか不便な点が出てきました。
- mdファイルを監視してサーバをリロードしてほしい
- mdファイルを一部除外したい
- 検証用にうん万行のJSONデータを返すようにしたい
- 存在しないAPIはstubcellで立てたAPIにproxyしてほしい
stubcellやらeasymockやらでやっていたファイルを書き換えたら次のリクエストで反映されたJSONが帰ってくるっていう状況は実に効率的だった...
という要望が積み重なってわっと作ったのが hariko というわけです。
AngularJS x Webpack を使った構成の紹介
AngularJSとWebpackを上手いこと連携させてメンテナンスしやそうな構成を試行錯誤したので、 現状の記録と整理を兼ねて記事にしてみる。
結構いいかんじなアプローチなのではないかと思いつつ、まだ詰め切れていないところも多いので今後ブラッシュアップを重ねていきたい。
関連技術
- AngularJS
- AngularUIRouter
- Webpack
- CoffeeScript
要点
- 役割でディレクトリを分ける
- 汎用化出来そうな仕組みはアプリケーションの外に置く
- DDDを意識してビジネスロジックを構築する
- Webpackのローダーを活用する
- Webpackのrequireをそこそこに活用する
- $injectorを活用する
- Angular UI-Routerの設定をもとにViewのディレクトリを作る
役割でディレクトリを分ける
AngularJSだと controller
, service
, config
, directive
のように機能でディレクトリを分けるのがスタンダードのようだけど、メンテナンスをしているとあっちいったりこっちいったりで結構面倒臭かったり、考えをごっちゃにしがちであまり使い勝手がよくなかった。
そんなわけでJSの役割を幾つかに別けて、それごとにディレクトリを切るようにしてみた。
app/config
- アプリケーションの設定
app/domains
app/views
- 見た目に関する部分、
controller
とtemplate
を内包する - view-modelとdomainを
controller
で連携させる
- 見た目に関する部分、
- 汎用モジュール
汎用化出来そうな仕組みはアプリケーションの外に置く
モーダルやローディングなど汎用的に使え、アプリケーションに依存するものを設定に切り出せるようなモジュールは基本的に app
(アプリケーションの本体)とは別のディレクトリにおく。
外出ししやすくなるし、管理を別けれるので一度FIXすれば後は気にしなくていい。
ディレクティブやView周りのproviderはだいたい汎用化出来そうなので、適当なプリフィクスを振って、 xx-modal
みたいな感じでディレクトリを切ってそこに置くようにしている。
DDDを意識してビジネスロジックを組む
DDDというか、意識していることは以下。
- 単一責務の原則
- 一つのクラスが持つ役割はひとつ
- 複数持つようなら分ける
- Aggregateパターン
- 複数のユーザストーリーを一つのクラスに集約することでViewから参照するクラスを制限する
- しかし責務は分かれてるようにしているので相互影響は最小。最悪クラスの差し替えも想定してる
- Repositoryパターン
いままであまりJavaScriptでOOPを意識してこなかったのだけど、単一責務の原則をしっかりまもって、I/Fをある程度制約かければ結構取り回しが効いて良さそうな感じはする。 ただ、どのくらいの規模までハンドルできるかはちょっと見えてないので、今後の発展を見たい。
Webpackのローダーを活用する
CoffeeScriptのコンパイルはもちろんだが、何より嬉しいのはHTMLをJavaScriptの文字列として展開できること。
これによって、 grunt-ng-template
のようなライブラリは不要になったし、遷移のたびにXHRを投げる必要もなくなった。
UI-Routerと連携しつつ、templateファイルを require
することで文字列をそのままわたせる。
ついでに、viewsのディレクトリの中で controller
とtemplate
を並べせることができて、「controller
とtemplate
はセットで編集したいのにディレクトリの移動面倒!」「ディレクトリ構造違くてわけわかんない」みたいな問題から開放された。
HTMLだけでなく、JSONやYAMLのローダーを使うことで設定などの読み込みも容易になるのでなにかと便利になる。
Webpackのrequireをそこそこに活用する
webpackの拡張requireはあんまり使っていない。 index化したり拡張子の省略が主だけど、これが思いの外使い勝手が良かった。
Webpackのextensions
オプションに拡張子を登録しておけば、以下の様に拡張子を省略してかいた場合、 app.(js|coffee)
あるいは app/index.(js|coffee)
を探しにいってくれる。
var app = require("app");
設定的には、extensions: ["", "coffee"]
とか登録しとくとCoffeeScriptも読んでくれるので便利。
index
ファイルはファイルの機能によって何を書くかをわけている。
View系
exportするのはController。ユーザストーリーの入り口的な意味合いを持たせている。 DDDのAggregate的な扱いをしている雰囲気。雰囲気なのであまり厳密にやってない。
例としては、以下の様な感じ。
- ユーザのサマリを出すページは
user/index.js
- ユーザの所持品を出すページは
user/friend.js
- ヘルプのページは
help/index.js
モジュール系
angularの拡張としてproviderやfactoryを提供する場合。 ドメイン系はだいたいproviderとして提供しているし、汎用的なクラスだったりするとfactoryを提供する。
index
ファイルでproviderやfactoryを定義し、別のファイルにクラスの定義をしている。
例として、ユーザクラスと、ユーザが持ってる友達のクラスを書くとしたら以下の様な構成にする。
+- domains/ +- user/ +- index.js +- user.js +- friend.js
$injectorを活用する
domainsなんかはクラスをたくさん作って、providerでインスタンス化している。
愚直にかくと以下のような書き方になる。
angular.module(module.exports = "user-domain", []).provider ()-> User = require "./user" return { '$get': -> new User() }
requireで呼び出すことはできるけど、例えば $resource
や $q
を使いたいときが結構ある。
呼び出すにしても $injector
呼ばなければならない。
$get
でDIして、コンストラクタに渡す?でも無駄なプロパティは抱えたくない。
そこで、$injector.invoke
を採用した。
こいつを使うと、以下のようにかける。
index.js:
angular.module(module.exports = "user-domain", []).provider () -> return { '$get': ($injector) -> new ($injector.invoke(require "./user")) }
user.js:
module.exports = ($resource) -> class User # Userクラスの定義
DIもクラス側に書けてメンテナンスがしやすくなった。
さらに、$injector.instantiate
を使って、パラメータのバリデーションをしつつインスタンスを生成するようにする。
index.js:
angular.module(module.exports = "user-domain", []).provider () -> return { '$get': ($injector) -> $injector.instantiate($injector.invoke(require "./user"), {}) }
$injector.instantiate
を使うとコンストラクタに渡る値が存在しないときにErrorを投げてくれるので、「うっかり引数変え忘れた!」とかに気付ける。
若干鬱陶しいという気はしつつ、たくさんコードを書いているとどうしても漏れがでてくるのでエラーを投げてくれるのはありがたい。
Angular UI-Routerの設定をもとにViewのディレクトリを作る
state
の構造と views
のディレクトリ構造が合致するのが望ましい。
こうするとファイルをソースを見なくても探しにいけるし、なによりファイル名を決めるときに考えることが少ない。
templateとcontrollerを同じディレクトリにおいているので考える手間が一つになる。
$stateProvider .state "app", abstruct: true controller: require "app/views/app/index.coffee" template: require "app/views/app/index.html" .state "app.user", url: "^/user/" controller: require "app/views/app/user/index.coffee" template: require "app/views/app/user/index.html" .state "app.user.friend", url: "^/user/friend" controller: require "app/views/app/user/friend.coffee" template: require "app/views/app/user/friend.html"
ディレクトリ構成
というわけで、大体以下の様な感じにディレクトリが出来上がる。
+- scripts +- xx-modal/ +- xx-loading/ +- xx-preload/ +- app/ +- config | +- router.coffee | +- resource.coffee | +- xx-modal.coffee +- domains/ | +- user/ | | +- user.coffee | | +- index.coffee | +- friend/ +- views/ +- app/ | +- contents/ | | +- header/ | | +- index.coffee | | +- index.html | |+- index.coffee | +- index.html +- index.coffee
まとめ
AngularJS と Webpackを連携させる事例を紹介しました。 組み合わせて色々便利で管理も楽になっていいよ!