Hatena::ブログ(Diary)

mizchi log

@mizchiの雑記帳

2011-08-17

nodejsでTwitterのUserStreamを取得


さすがに自分で書くの面倒だったので探してきた。のだけど、動くサンプルがほとんどなくて苦労した。
ライブラリのバージョンがしょっちゅう変わるせいだと思われるので、これもいつまで動くかわかりません。

coffee-script が必要です。

$ git clone git://github.com/cesare/twitter-userstream.git
$ cd twitter-userstream
$ cake build
$ npm install . -g
$ npm link twitter-userstream

npmに登録されてないのでローカルに落とす
(さすがに他人のリポジトリを勝手にnpm publishするのはね…)


例のごとくアクセストークンは自分でとってきてください
# us.coffee(ほとんどサンプルのまま)

#!/usr/bin/env coffee
TwitterUserstream = require('twitter-userstream').TwitterUserstream
sys = require 'sys'
util = require 'util'

tokens =
  consumerKey:       ""
  consumerSecret:    ""
  accessToken:       ""
  accessTokenSecret: ""

us = new TwitterUserstream tokens, (us) ->
  show = (title, data) ->
    sys.puts "--- " + title + " ---"
    sys.puts util.inspect(data, false, null)
    sys.puts ""

  us.on "friends", (data) ->
    sys.puts "--- friends ---"
    sys.puts "Total " + data.friends.length + " friends"
    sys.puts ""

  us.on "tweet", (tweet) -> show "tweet", tweet
  us.on "follow", (data) -> show "follow", data
  us.on "delete", (data) -> show "delete", data

us.start()


ハッシュタグなどのストリーム取るのはTwitterNode使ったほうがいいです。
no title

2011-07-05

海外で話題になってたnodejsのチュートリアルとか実装サンプルを適当にまとめた


まあdeliciousのタグ眺めてただけなんですけど…
Recent nodejs Bookmarks on Delicious

趣味と偏見混じってます

チュートリアルとか

The Node Beginner Book » A comprehensive Node.js tutorial
よさげなチュートリアル。

Mastering Node
まだ書きかけっぽい。htmlの他にepub、pdfも。

javascript - How do I get started with NodeJS - Stack Overflow
Stack Overflow。チュートリアルまとめ。

周辺

Writing Node.js Native Extensions | Cloudkick, manage servers better
C++で拡張モジュール書くやり方。非同期メソッドの作り方も。

Node Tuts - Node.js Free screencast tutorials
nginxによるnodejsのための設定。動画

実装例

cmarin/MongoDB-Node-Express-Blog - GitHub
名前のとおりです。

--verbose: NodeJS + WebSockets = Stoopid Easy Comet Chat
名前のry

Experimenting with Node.js - Jeff Kreeftmeijer
node-websocket-serverによるwebsocketサンプル

ライブラリ(ちょうど話題になってたもの)

jadell/box2dnode - GitHub
サーバーサイドのbox2d(jsによる物理エンジン)。socket.ioと組み合わせてアクションゲームとかいけそう。

node-http-proxy: Reverse proxy for Node.js - The Changelog - Open Source moves fast. Keep up.
nodeによるリバースプロキシ。websocket使う際に振り分けてやるとよい。

Fab.js
WAF? 何かのラッパなんだろうがちゃんと理解してない。

CoffeeScript

CoffeeKup
coffeescriptによるHTMLテンプレート


はてブのタグで購読してたけど、deliciousのタグもいいですね

2011-06-29

coffeescriptとstep.js でどれぐらい非同期を同期的に簡潔に書けるか?


なんか日本語がおかしいですが…
nodejs/expressの習作として、簡単なマイクロブログ作ってたんですが
MongoDBのORMとしてMongooseを使ってて、DBの呼び出しってNodeJSでは基本的に非同期なので
たしかにnodeの設計思想からしてそうすべきだとは思うんですが、単純にコードとしての見栄えが悪くなってました

で、js直には触らず、全部 coffeescript で書いてるんですが
たとえば、expressとmongooseに突っ込むところを組み合わせると、こうなります

require 'coffee-script'
mongoose = require 'mongoose'
express = require 'express'
OAuth = require('oauth').OAuth

# 中略
# OAuht認証のところだけ抜粋
app.get '/oauth_verify',(req, res)->
  oauth_token = req.query.oauth_token
  oauth_verifier = req.query.oauth_verifier
  if oauth_token and oauth_verifier and req.session.oauth
    oauth.getOAuthAccessToken oauth_token, null, oauth_verifier,
      (error, oauth_access_token, oauth_access_token_secret, results) ->
        req.session.regenerate ()->
          req.session.name = results.screen_name
          req.session.twitter_id = results.user_id
          User.findOne { id:results.user_id } , (err,user)->
            if not user
              item = new User()
              item.name = results.screen_name
              item.id = results.user_id
              item.twitter =
                token: oauth_access_token
                token_secret: oauth_access_token_secret
              item.save (err)-> console.log 'add new user:'+JSON.stringify(item)
            console.log "[login] #{results.screen_name}" if user
            res.redirect '/'
  else
    oauth.getOAuthRequestToken (error, oauth_token, oauth_token_secret, results)->
      req.session.oauth =
        oauth_token: oauth_token
        oauth_token_secret: oauth_token_secret
        request_token_results: results
      res.redirect 'https://api.twitter.com/oauth/authorize?oauth_token=' + oauth_token

node.js+socket.io+oauth+SessionWebSocketでログイン付きチャットを作るメモ - すぎゃーんメモを参考に、coffeescriptで書きました
コード全体は express/coffeescript ― Gist に置いておきます


どうでしょう、expressとmongooseのコールバックで奥へ奥へと… まあ悪かないんですが、プログラマたるもの、単純にネストが深いコードは警戒してしまうわけです
まあ、元々簡単に書けてしまう部分でもないんですが、だからこそシンプルにしておきたい、ですよね

っていうので、そういえば非同期を上手く記述するライブラリがあるよなーと、色々触ってみたんですが、
一番シンプルそうだった step.jsってのに落ち着きました。

step.js


creationix/step - GitHub
インストールは npm で npm install step

やってることは単純で、コールバック関数に this を代入してやると、次の関数呼び出しではそこから処理が開始されます。
公式のサンプルコードはこんな感じです

Step(
  function readSelf() {
    fs.readFile(__filename, this);
  },
  function capitalize(err, text) {
    if (err) throw err;
    return text.toUpperCase();
  },
  function showIt(err, newText) {
    if (err) throw err;
    console.log(newText);
  }
);


便利なstep.jsなんですがcoffeeescriptと組み合わせるときはちょっと工夫がいります
coffeescriptは、関数の中では「最後に評価されたものをreturnする」って仕様になってて、
これはこれでメソッドチェーンとか書きやすくはなるんですが、step.jsではvoid以外でreturnされると、そこで挙動が変わってしまいます
だから、明示的に空のreturnを書いてやる必要があります。ちょっとカッコ悪いですね。

という感じで、OAuthのコードを描き直してやるとこうなります

app.get '/oauth_verify',(req, res)->
  oauth_token = req.query.oauth_token
  oauth_verifier = req.query.oauth_verifier

  if oauth_token and oauth_verifier and req.session.oauth
    _atoken = _asec = ""
    _res = {}

    step ()->
      oauth.getOAuthAccessToken oauth_token, null, oauth_verifier,@
      return
    , (e, oauth_access_token, oauth_access_token_secret, results) ->
      _atoken = oauth_access_token
      _asec = oauth_access_token_secret
      _res = results
      req.session.regenerate @
      return
    , ()->
      User.findOne { id:_res.user_id } ,@
      return
    , (e,user)->
      console.log user
      if not user
        item = new User()
        item.name = _res.screen_name
        item.id = _res.user_id
        item.twitter =
          token: _atoken
          token_secret: _asec
        item.save (e)-> console.log 'add new user:'+JSON.stringify(item)

      req.session.name = _res.screen_name
      req.session.twitter_id = _res.user_id
      console.log "[login] #{_res.screen_name}"
      res.redirect '/'
      return
  else
    oauth.getOAuthRequestToken (error, oauth_token, oauth_token_secret, results)->
      req.session.oauth =
        oauth_token: oauth_token
        oauth_token_secret: oauth_token_secret
        request_token_results: results
      res.redirect 'https://api.twitter.com/oauth/authorize?oauth_token=' + oauth_token

ネストは減りましたが、縦に長くなってしまいました。
あと、step.jsで実行されるそれぞれの名前空間は、step関数が呼び出された時点に依存していて、関数の中の名前空間は、そのクロージャで閉じているので、
関数から関数へ値を渡したいときは、バッファーとなる変数を用意しとく必要がありました。_atoken,_asecret,_res がそれです


結論として

  • ちょっとネストが深くなるのは許容する
  • ネストが3段超えた辺りでstep.js使うのを検討する


っていう風にすると可読性が高くなるんじゃないかなって思いました。まる。


他の非同期ライブラリとかは、InfoQ: 仮想パネル: JavaScriptで非同期プログラミングを乗り切る方法が、それぞれの設計思想とかまとまって良かったです。