0

WEB+DB PressのReactの記事が良かったのでcoffeeで書きなおした

昨日か今日ぐらいの発売のWEB+DB Press vol.86のp123~のnaoya氏のReactの記事が良いらしいからみんなで勉強しましょう、って増井先生が言ってたので、昨日夕方数人で読んでて家帰ってから実際にコード書いてみた。

JavaScript苦手なのでcoffee-scriptで写経した。

https://github.com/shokai/react-coffee-study

observer modelかつデータの流れを1方向にするのをFluxアーキテクチャと呼ぶのとか、DOM切り貼り職人のつらみとVirtualDOMの差分更新とかそういう概念の話は知ってたけど、なぜかなかなかReactそのものを使う機会が無かった。

Reactの感想

触ってみた感じではBackbone+Marionetteよりとっつきやすい。
WEB+DB Pressの記事の前半のtodoリストはみたままcoffeeで書いて、それですぐ馴染んできたので後半のmarkdown previewはなんとなく適当に書いたらちゃんと動いた。



coffeeでreact(coffee内にJSX)

最終的にcjsxというcoffee内にJSXを書ける拡張を使って、
Gruntからbrowserify+coffee-reactifyでsourceMap付きでJSX変換済みのjsを吐き出すようにした。
NODE_ENV=production付けるとさらにuglifyjsで圧縮する。
リポジトリ内のGruntfile.coffee見るとわかる。


ReactにはJSXというHTMLのようなテンプレートエンジンが埋め込まれたJavaScriptが使われていて、ふつうのJSの中にこういうのを書くと

こういうJSに変換される。

つまりHTMLっぽいJSXというマークアップを書く→JSに変換→VirtualDOMが構築され→適当なタイミングで本物DOMが差分更新される、らしい。

このJS内のJSXの変換は、WEB+DB Pressの記事では最初JSXTransformer.jsでクライアントサイドでやらせてて、後半はbrowserifyでサーバー側であらかじめ変換する方法を取っていた。


coffeeでjsx使うにはcoffee-react npmの中にあるcjsxコマンドを使うんだけど、この後にさらにbrowserify通すとsourcemapが崩れてデバッグ不能になる。
多段階の変換するとsourcemapズレたりどっか飛んでいってしまってよくないので、cjsxの変換もbrowserifyにやらせる。


browserify

ReactはjQueryと共存できない→jQueryないと俺はajaxできない→ajax機能だけのjQueryをカスタムビルドするのもまあアリかもしれないけど、ここはbrowserifyでnodeのライブラリをブラウザに持ってくれば解決、あとeventemitterとかも持ってこれるし便利!

というわけでbrowserifyというJSを静的解析してrequire先のファイルを全部1つのjsファイルに固めてくれるツールを使う。

debugオプション付ければsourceMapもちゃんと付くので、cjsxでのエラーもその先のnpmのファイル名や行番号も見える。

全部リポジトリ内のGruntfile.coffeeに書いた。


0

AndroidタブレットでWeb MIDI API

前:Node.jsとMIDIコントローラでHueを調光する

音楽やる気はないけどMIDIデバイスは入出力デバイスとして面白い。スライダーとかツマミとか色々あるし、MIDIで制御できるドラム叩きロボットとかもある。
とりあえず電子工作なしで買ってきたデバイス接続するだけでPCでもスマホでもWebブラウザから使えるというのは面白い。


Web MIDI API

Web MIDI APIはブラウザでMIDIデバイスが使えるHTML5のAPIで、現在はGoogle Chromeだけが対応している。
chrome://flags でWebMIDIを有効にし、MIDIデバイスを接続してからChromeを再起動する必要がある。
Chrome起動後にMIDIデバイスつなげても認識しないので注意。


試した

http://shokai.github.io/WebMidiAPIStudy/


Android版Chromeも対応しているので、USBホストケーブルでNexus7に接続して試してみた。
流れてきたデータを全部表示するだけだけど、Nodeのmidi npmで試した時とほぼ同じ手順でMIDIデバイスに接続できる。流れてくるデータもnode版はArrayでWebMIDIはUInt8Arrayなだけなので、MIDIデバイス用のラッパーライブラリ作るとしたらブラウザ・Nodeの両対応は簡単そう。



Mac版Chromeでも動く。Android版では取得できなかったデバイスのmanifacturerとnameプロパティがちゃんと取れた。


実装

https://github.com/shokai/WebMidiAPIStudyにある。

requestMIDIAccessはPromiseで使う
return if typeof navigator.requestMIDIAccess isnt 'function'

navigator.requestMIDIAccess()
.then (midi) ->
return new Promise (resolve, reject) ->
console.log "#{midi.inputs.size} input devices"
console.log "#{midi.outputs.size} output devices"
it = midi.inputs.values() # inputデバイスのリストがiteratorでくる
loop
input = it.next()
break if input.done
return resolve input.value # デバイス発見した
reject "input device not found"

.then (input) ->
console.log "Input Device found: #{input.manufacturer} - #{input.name}"
input.onmidimessage = (msg) ->
console.log msg.data # UInt8Arrayでデータが読める

.catch (err) ->
console.error err


気づいたこと


充電できない

AndroidではMIDIデバイスを使っているとUSBポートが埋まるので充電できない。Qiでなら充電できるかと思ったけどNexus5とNexus7ではMIDIと同時に充電は無理だった。


複数アプリから1つのMIDIデバイスを使える

Macでは複数のブラウザウィンドウで同時に1つのMIDIデバイスからのデータが読めた。またブラウザと同時にNode.jsからも使えた。
Macでは /System/Library/Frameworks/CoreMIDI.framework/MIDIServer が仲介しているっぽい。


参考になった

0

slackでふぁぼったのをhubotで通知する

hubot-slackアダプタ v3の中で使われているnode-slack-clientを見ていたら、slackで発言に星を付けた時に通知が来ていたのでそれを別のroomに流すようにしてみた。

slackには自分のstarを見る画面はあっても他人のstarを見れる画面がない。API使ってstars.listが取れるけど、starまとめ的なページを作るにはユーザの数だけ定期的にクロールしなきゃだめなのかー・・・と思ってたけど、hubotにサーバープッシュされて来ていた。



starsというroomをあらかじめ作って、hubotを/inviteしておく必要ある
https://gist.github.com/shokai/e52dd7fdd5d2592878b0

# Description:
# notify "star_added" event for slack.com
#
# Author:
# @shokai

debug = require('debug')('hubot:slack-star')

module.exports = (robot) ->

robot.adapter.client?.on? 'raw_message', (msg) ->
return unless msg.type is 'star_added'
debug msg
return unless msg.item.message.permalink
user = robot.adapter.client.getUserByID msg.user
text = ":star: @#{user.name} added star #{msg.item.message.permalink}"
debug text
robot.send {room: 'stars'}, text
raw_messageイベントは、node-slack-clientがサーバーからwebsocketでJSONを受信してparseしてすぐemitされる。
これは通常のSlackのAPI(HTTPで使うやつ)ではなくhubot用のwebsocketので、一方通行に受信してるだけなので、こっちからslackに送信できるのはbotの発言命令だけのようだ。hubotにstarつけさせたり、発言を削除したりはできない。

getUserByIDはnode-slack-client内部でcacheされてるので呼び出しまくっても問題ない。

もしstar通知が多くて邪魔になってきたら、3人以上starした発言、とかで通知するroom作ったりしてみようかな

0

Herokuが寝ないようにする

herokuは1時間アクセスが無いとDynoが寝る

たとえsocket.ioをつなぎっぱなしにして頻繁に通信していても寝てしまうのでこうして20分おきに自分で自分にHEADリクエストを送って寝ないようにしてる

request = require 'request'

module.exports = (app) ->

return unless /^https?:\/\/.+/.test process.env.HEROKU_URL

setInterval ->
console.log 'ping'
url = "#{process.env.HEROKU_URL}?time=#{Date.now()}"
request.head url, (err, res) ->
if !err and res.statusCode is 200
console.log 'pong'
, 60 * 1000 * 20 # 20 min


環境変数HEROKU_URLに自分自身のURLを設定して使う

% heroku config:set HEROKU_URL=https://(APP_NAME).herokuapp.com

0

hubotに付いているcoffeeが古くてPromiseが動かない

起こった問題


hubotにごはんを選んでもらうをPromiseで書きなおして、Herokuで動かしているhubotの中で使ったら

[TypeError: Object http://ja.wikipedia.org/wiki/Category:料理 has no method ‘then’]


というエラーがでてNodeプロセスが死んで困っていた。ローカルでは動いている。

hubot-gohan/gohan.coffee at be90c1c25219c66295b1c1a10be507c9731cbfaf · shokai/hubot-gohan

  getGohan: ->
debug 'getting Gohan..'
@getPagesCached "#{@baseUrl}/wiki/Category:料理" ## ここでエラー
.then (pages) =>
return new Promise (resolve) =>
categories = _.filter pages, (page) -> /^\/wiki\/Category:/.test page.link
category = _.sample categories
resolve category
.then (category) =>
@getPagesCached "#{@baseUrl}#{category.link}"
.then (pages) =>
return new Promise (resolve) =>
pages = _.filter pages, (page) ->
!(/^\/wiki\/Category:/.test page.link) and
/^\/wiki\/.+/.test(page.link) and
page.title?
debug "got #{pages.length} pages"
gohan = _.sample pages
resolve {url: "#{@baseUrl}#{gohan.link}", title: gohan.title}


解決方法

hubot –createでhubotのテンプレを作ると付属しているcoffee-script npmが1.6.3なので動かない。

新しいcoffee (1.8.x)を入れるとちゃんとPromiseが動くようになる。
% hubot --create my-hubot
% cd my-hubot/
% npm install coffee-script -save