東京Node学園祭2日目に参加しました
昨日に引き続き、今日もNode学園祭です。
今日はハンズオン中心のセッションでした。
Authentication Tutorial with Auth0
認証プラットフォームを提供するAuth0社のセッションで、実際にAuth0を触ったりReactを使ってAPIを作ってみたりしました。
このサービスのことは今まで知りませんでしたが、設定画面で登録するだけで、ソーシャル認証もメールのVerificationもすぐにできました。
基本的な機能は無料で使えるので、大規模サービスだけでなく、小さなサービスをすぐ作りたい場合にも役立ちそうです。
ハンズオンではReactのチュートリアルを使って自分のAPIを作ってもみたのですが、こちらはなぜかAPIの画面からログインができない状態で時間切れに。
とはいえAPIの仕組みを学ぶのは予想以上に楽しかったです。
LINE Bot Handson
Node.jsを使ってLINEのBotを作るハンズオンでした。
最近になってNode.js向けのSDKが公開されたおかげで、基礎編の部分(オウム返しBotの作成)はとても簡単に作ることができました。
講師の@n0bisuke氏が内容をQiitaにまとめてくださっているのですが、とてもわかりやすいのでおすすめです。
その後応用編として、様々な機能をもたせたBotの制作に挑戦。
参加された方は、単語をWikipediaで検索してくれるBot・すてきな画像を拾ってくれるBotなど、短時間でいろいろなBotを作っていました。
私はというと、ハンズオン終了後も数時間うなりながらBotと格闘。 最終的にこんなのができました。
「映画」という単語を入れてメッセージを送ると、Cinefilという映画サイトの最新記事をアイキャッチ画像とリンクつきで教えてくれます。 これについてはまたブログかQiitaで書くつもりです。とりあえず無事完成してよかった。
速く良く書けることを目指して
今日のハンズオンで驚いたのが、時間内にBotを完成させた人たちの速さ。
自分が作ったようなものを、だいたい30分ほどであっさり完成させていました。
確かに、大事なのは何をどうやって作るかを考えることであって、実際に手を動かすのはコードの文法さえわかっていればいくらでも速くなるはず。
文法で悩んでいるうちはまだまだだなと痛感しました。
まずは今日結局できなかったNodeSchoolのコンテンツに、近いうちに取り組みたいと思っています。
東京Node学園祭1日目に参加しました
この冬から会社でフロントエンド担当になる予定なので、どんな空気感なのか知りたいと思って参加しました。
一応TypeScriptやReactを触ったことはあるものの、業務でやったわけではないうえしばらくブランクがあるので、周りとのレベルの差をひしひしと感じました。私は甘かったのだ...
自分がもっとNode.jsに詳しければきっと面白かっただろうな、と思う発表もいくつか。もっと精進します。
面白かった発表
もう資料が公開されてました。速いですね。
まず、QiitaのJorge Bucanan氏による「Make You a React」。
https://goo.gl/UKjXiC
JSXファイル作成からVirtual DOMのパースまでを自分で実装してみようという内容でした。
Virtual DOMって何なのかという理解があいまいなまま今までReactを使っていたので、とても学びになる発表でした。
サンプルコードもわかりやすかったです。学園祭が終わったら自分でも書いてみるつもりです。
そして、メルカリのきりん氏による「いまさら聞けないCORS対応」。
この辺りは前に業務で扱った経験があるので、当時の苦労を思い出してしみじみしました。
先輩から「公式ドキュメントを読め」と何度言われたことか...
CORSやPreflightの意味など、あいまいになりがちな知識がわかりやすくまとまっていました。あとでまた見返します。
明日は
NodeSchoolでJSの勘を取り戻したいです。書くの大事。
正規表現クロスワードが面白すぎるのでぜひ紹介したい
名前のとおり、こういうやつ。
縦横に文字を入れていき、すべての行・列で正規表現が満たされるようにできれば正解。
正解した盤面を横に読んでいくと、文章になっている問題もある。
1マスだけの初級問題から、9✕9マスくらいの鬼問題までたくさん揃っており、やりごたえも抜群。
解き方としては、入る文字が限られている行・列から攻めていくのがよさそう。
例えば上の問題なら、一番左の列にはFIA
のどれかしか入りえない。また、真ん中の行の最初の文字は必ずUOIT
のどれか
になる。これらを合わせると、真ん中の行の左端のマスが決まる。
あとは正規表現の知識とあなたの粘り強さしだい。
正規表現って一見よく使いそうだけど、大半の用途はURLのバリデーションや簡単な分岐表現が多くて、いざがっつり必要になったときにけっこう焦ることが多い気がする。
こういうので遊んでおくと、後々になって大きなものが蓄積していくんだろうなあ。
ReactのSyntheticEventとは何なのか
Reactでイベントを作るとき、引数にe
を渡す例がよくある。
例えば公式ドキュメントにもこんなコードが載ってる。
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); }
このe
はSyntheticEvent
を表すものらしいが、どんなものなのかいまいちつかめないのでいろいろ調べてみた。
特にこのページがわかりやすく、役に立った。
通常のイベントとの違い
Reactを使わず、通常のJSでイベントを実装するときは、イベントの種類によって様々なインターフェイスがある。
例えばクリックイベントを作るならMouseEvent
を使い、キーボード入力をイベントリスナーとするならKeyBoardEvent
を使う。
イベントインターフェイスの種類によって、独自のプロパティが実装されており、ブラウザごとの対応状況も異なる。そのため、ネイティブのJSでイベントをクロスブラウザ対応させるのはなかなか大変らしい。
ReactのSynthetic Event
一方Reactの場合、全てのイベントをSyntheticEvent
として扱う。
Synthetic Event
を使えば、各ブラウザのネイティブイベントを全てラップしており、何も意識しなくてもクロスブラウザ対応ができる。
そして冒頭に出てきたe
は、イベントが発生した際にそれをSyntheticEvent
としてイベントハンドラに渡す役割がある。
単なる引数にすぎないので名前は何でもいい。event
にしている人もいた。
SyntheticEventのプロパティ
SyntheticEvent
に用意されているプロパティは以下のような感じ。
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() DOMEventTarget target number timeStamp string type
どのネイティブイベントにもあてはまりそうな、一般性の高いものが選ばれている。
ネイティブイベント特有のプロパティを使うには
個々のネイティブイベント特有のプロパティを使いたい場合も問題ない。
SyntheticEvent
が、イベントの名前によって何のイベントをラップしているか識別し、それに合わせてプロパティを使えるようになる。
例えば、公式が作ったコードを少し修正して、以下のようなコードを書いてみた。
もともとのコードは、ボタンをクリックするとオン・オフが切り替わるというだけのものだったが、
今回の修正により、shiftキーを押しながらクリックしないと切り替わらないようにしてみた。
See the Pen Toggle - ShiftKey ver. by Udomomo (@Udomomo) on CodePen.
修正したのは以下の部分。
handleClick
関数に引数e
を渡し、e.shiftKey
で条件分岐させてみた。
handleClick(e) { // eを追加 if (e.shiftKey) { // e.shiftKeyによる条件分岐を追加 this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } }
なぜこれが動くかというと、render
関数の中でonClick
という名前でイベントを定義しているため。
これにより、SyntheticEvent
が「自分がラップしているネイティブイベントはonMouseEvent
だ」と認識し、ネイティブJSのMouseEvent
オブジェクトのプロパティであるShiftKey
を使えるようになったのだ。
イベントの名前とプロパティの対応関係は、公式ドキュメントに載っている。
SyntheticEventのPoolingについて
公式ドキュメントには、「The SyntheticEvent is pooled」とある。
これは、SyntheticEventオブジェクト自体が再利用されるということ。
SyntheticEvent
のコールバック関数の実行が終わると、プロパティの値が全てnull
にリセットされる。
すなわち、あるSyntheticEvent
の中でプロパティに値を設定した後、それを非同期で呼び出す別の処理で使ったり、値を取っておいて後で別の関数の中で使おうとしても、null
しか返ってこない。
event.persist()
を使えば、非同期処理でもプロパティの値を参照できるようになるらしいが、Reactのパフォーマンスが落ちてしまうようだ。
diffコマンドで全行違うと言われたので調べたら改行コードのせいだった
自分が作ったHTMLのコードが、非エンジニア向けに配られるExcel資料に載せられることになった。そのExcelファイルを見ると、一つのセルの中にコードがそのままコピペされて入っていた。
Excelファイルを作った人が「これでいいか確認して」と言ってきたので、そのコードをエディタにコピペして元ファイルとdiff
コマンドで比べてみたら、なんと全行違うとのこと。
目で見るとどこも違っているように見えないけど...と思っていろいろ調べたら、違っているのは改行コードだとわかった。
改行コードの種類
改行コードには、CR
(carriage return)とLF
(line field)の2種類がある。タイプライター時代の名残りらしい。
それを引きずった結果、OSごとに異なる改行コードが使われるようになってしまった。
改行コードを確認する
実際に改行コードを確認してみる。
HTMLファイル
エディタを使えばなんとか確認できることが多いはず。
自分はSublime Text 3 を使っているが、改行コードを画面右下に表示するための設定方法があった。
Windows
と出たので、元のHTMLファイルはどうやらCR+LF
らしい。確かに元ファイルは前のPC(Windows)で作った覚えがある。
Excelファイル
Excelにはcode()
という関数がある。これは、使われている文字のASCIIコードを返してくれる。
これを使い、セル内改行に何が使われているのか確かめることができる。ASCIIコードと改行コードの対応は以下の通り。
- 10:
LF
- 13:
CR
さて、Excelのセル内改行は、WindowsだとLF
、MacだとCR
らしい。Excel独自の仕様はやっかいだ。
最近PCをMacBookに変えたので、受け取ったExcelファイルでcode()
関数を使うと、確かに13
と出た。Excelファイルを作った人はWindowsを使っているので、元はLF
だったのがCR
に変換されたのだろうか。
どのみち、CR+LF
ではないらしい。
地味に怖い改行コード
今回のようなケースは、エディタなどを使って改行コードを全て置換すればなんとかなる。
ただ、怖いのは複数OSのユーザを対象としたシステムやソフトウェアを作る場合。プログラムの中で思わぬ変換や誤認識が起きて、気づきにくいバグの元になりうる。
以前「WindowsとMacは全然違うよー」と冗談めかして言っていた先輩の顔が思い浮かんだ。苦労したんだろうなぁ。
Sublime Text3で書いたファイルをVagrant仮想環境に同期させる
うちのPython環境はVagrantで作ったゲストOSの中にある。
一方でエディタはSublime Text3を使っている。flake8で補完が効いてらくらく。
なので、ホストOSでSublime Text3を使って書いたファイルを、手間なくゲストOSにも同期させたいと思っていた。
Sublime Text3にはSFTPパッケージがあるので、最初はそれを使おうと思っていたが、それよりもVagrantで共有フォルダの設定をしてしまった方が楽だと気づいた。SFTPパッケージは$30かかるし。
rsyncを使う
ファイルの共有を楽にするためには、vagrantのrsync
機能を使うのがおすすめ。前提としてVagrantのバージョンは1.5以上が必要。
これを使うと、 vagrant rsync
コマンドを打つだけで同期ができる。
さらに、 vagrant rsync-auto
とすれば、共有フォルダを監視してくれて、ファイル更新のたびに自動で同期される。
ちなみに、vagrant rsync
が有効なのは、ホストOS→ゲストOSという流れでファイル共有をするときのみ。
その逆をしたいときは、Vagrantの外部プラグインを使う必要がある。例えばvagrant-rsync-back
などがある。
共有フォルダの設定をする
Vagrantfileの中に設定を書き加えればよい。
このとき、rsync
を使うために、オプションとしてtype: "rsync"
とする必要がある。
設定の例はこんな感じ。
Vagrant.configure("2") do |config| # other config here config.vm.synced_folder "/Users/Udomomo/python_project", "/home/vagrant/python_project/", type: "rsync", owner: "vagrant", group: "vagrant" end
【17/10/15 追記】
上記だけでは足りなかった。以下のようにしなければならない。
config.vm.synced_folder "/Users/Udomomo/python_project", "/home/vagrant/python_project/", type: "rsync", owner: "vagrant", group: "vagrant" rsync__exclude: [".git", ".gitignore", "tmp", "log", "cache"], rsync__chown: false
rsync__exclude
は、ゲストOS上で同期の対象外とするファイルやディレクトリを指定する。.git
や.gitignore
、log
、cache
などを設定するとよい。
これが必要なのは、rsyncによる同期をするとホストOS上にないファイルは全て削除されてしまうから。ゲストOS側でgitレポジトリを作った後に同期をしたら、レポジトリが消えてしまうという痛い目に遭った。
また、rsync__chown
は、rsyncによってゲストOS側のファイルのオーナーやグループ名などが変わらないようにするための設定。v1.6.3から追加されたとのこと。
【追記終】
後はVagrantを再起動させれば、設定が完了する。
vagrant rsync-auto
としておけば、ホストOSのUsers/Udomomo/python_project
配下で更新されたファイルが、ゲストOSの/home/vagrant/python_project/
配下に自動的に同期される。
ちなみにこの設定だが、複数個書くこともできる。新しいプロジェクトが始まるごとにVagrantfileに書き足し、不要なものを消せばよい。
共有フォルダを一つしか設定できないと、毎回そこに行ってファイルを取ってこないといけないので、複数個設定できるのは本当に助かる...。
TypeScriptの型推論について学んだ
型推論とは
最近TypeScriptを使って開発していたけれど、大きな勘違いをしていた。
強い型付けをする言語ということなので、変数を定義するときに毎回型を宣言しなければいけないと思っていたのだ。
例えばこんな感じ。
let x: number = 3;
人間から見れば、この number
はノイズでしかない。3
は数値だと、誰が見てもわかるはずだ。
入力が手間なうえ、可読性を下げてしまう。
この場合は以下のような記述で問題ない。
let x = 3;
TypeScriptは3
が数値であることを知っており、xを数値型とみなしてくれる。これを型推論という。
「最も共通する型」による型推論
この機会にもう少し公式ドキュメントを読んで深掘ってみる。
例えば以下の場合は、Arrayであることは間違いないが、その要素の型は何と推論されるだろうか?
let x = [0, 1, null];
TypeScriptは、提供される候補の型から「最も共通する型」を選ぶ。これは、他の全ての候補と互換性がある、すなわち他の候補がその型からの派生した型であるという意味だ。
上の場合、Arrayの中にはnumberとnullの2つの型があるが、TypeScriptでは、nullは全ての型の部分型(subtype)とみなされる。
このため、上の変数xの型はnumber[]
と推論される。
ちなみに、この方法で型が選べない場合は、推論の結果が空のオブジェクト{}
になってしまうので、最初に示したように自分で型を書いてやる必要がある。
「文脈上の型」による型推論
さきほどの共通する型による型推論は、平たく言うと右辺をもとに左辺の型を判定するという流れだった。
しかし、その逆で左辺から右辺の型を判定することもある。
window.onmousedown = function(mouseEvent) { console.log(mouseEvent.button); //<- Error };
上の場合、mouseEvent
の型は定義されていないはずだが、なぜエラーが起きるのだろうか?
TypeScriptは以下のように推論するようだ。
・window.onmousedown
は、mouseEvent型の引数を取る関数であることを知っている
・このため、右辺の引数 mouseEvent
は、 mouseEvent
型であると推論される
・しかし、 mouseEvent
型に button
というプロパティはない。そのためエラーとなる
developer.mozilla.org
このとき、function(mouseEvent: any) {...}
のように、引数mouseEvent
の型を明示していれば、推論が上書きされてエラーは生じない。
人間が快適にコードを書けるよう、影でいろいろな苦労があるんだなあとしみじみ実感した。