CoffeeScriptの文字列への変数埋め込み

なんというかjavascriptの感覚で文字列は+でつなげてしまう。使ったことなかったので調べた。

coffee> a = [1,2,3]
[ 1, 2, 3 ]
coffee> "ary #{a}"
'ary 1,2,3'
coffee> b = {a:'text'}
{ a: 'text' }
coffee> "dic #{b}"
'dic [object Object]'
coffee> "dic #{b.a}"
'dic text'

Node.jsのspawnをつかってcoffescriptでps ax | grep sshを実行させるサンプル

spawnとexecの違いがわからんのでちょっと調べた。

Difference between spawn and exec of Node.js child_process

spawnはストリームをexecはバッファーを返す

で、Node.jsのドキュメントを読んでて spawnをつかった 'ps ax | grep ssh' の例が書いてあったので、これをcoffeescriptで書いた。

ターミナルで普通に動くのでvowsでテストしたら、callbackが返ってくる前に変なオブジェクトが返ってきてなんじゃこりゃ?と。

coffeescriptをjavascriptにコンパイルして読んだら、変なとこにreturnが挿入されてたのであちこちにreturnを入れて勝手にreturnされないようにした。

psax.coffee

util = require('util')
spawn = require('child_process').spawn

psax = (callback) ->
  ps    = spawn('ps', ['ax'])
  grep  = spawn('grep', ['ssh'])

  d = ""

  ps.stdout.on 'data', (data) -> grep.stdin.write(data); return
  ps.stderr.on 'data', (data) -> console.log('ps stderr: ' + data); return
  ps.on 'exit', (code) ->
    console.log('ps process exited with code ' + code) if code isnt 0
    grep.stdin.end(); return

  grep.stdout.on 'data', (data) -> d += data; return
  grep.stderr.on 'data', (data) -> console.log('grep stderr: ' + data); return
  grep.on 'exit', (code) ->
    console.log('grep process exited with code ' + code) if code isnt 0
    return

  grep.stdout.on 'end', ->
    callback(null, d); return

  return

exports.psax = psax

returnを入れて最後に評価した式がかえらなくてしなくていいような記法は用意されてないんだろうか? >>なんかモナドっぽくて素敵じゃないか。

vowsのテスト。宣言(文字列)の書き方のお作法がまだよくわかってない。should beとかshoud haveはわかるんだけど、whenとかAfterとか使い分けってどうなってんの?

そういうお作法が書いてあるサイトないかなぁ。

vows = require('vows')
assert = require('assert')

sp = require('./psax')

psax = sp.psax

vows.describe('Psax').addBatch({
  'A psax': {
    topic: -> psax
    'should be a function': (px) ->
      assert.equal(typeof px, "function")
    'After psax function called': {
      topic: (psax) -> psax(this.callback)
      'callback should return a result': (err,result) ->
        assert.isString(result)
    }
  }
}).export(module)

ProductName テスト駆動JavaScript
Christian Johansen
アスキー・メディアワークス / 3780円 ( 2011-11-25 )


Node.jsでYahoo!日本語形態素解析APIを使う

紆余曲折の末Yahoo! APIに落ち着いた。

http = require('http')
querystring = require('querystring')

qst = querystring.stringify({
    appid: '################',
    sentence: 'もももすももももものうち',
    results: 'ma'
})

options = {
  host: 'jlp.yahooapis.jp',
  port: 80,
  path: '/MAService/V1/parse?' + qst
};

http.get(options,
  (res) ->
    body = ""
    res.on('data', (data) -> body += data)
    res.on('end', -> console.log(body))
  ).on('error',
  (e) -> console.log("Got error: " + e.message)
)

追記12.02.22

結果がXMLで返ってくるのでパースしないといけないが、node.js で libxml を使うにはどのライブラリをつかうべきかというエントリを参考にしてlibxml-to-jsを選択。

http = require('http')
querystring = require('querystring')
parser = require('libxml-to-js')

qst = querystring.stringify({
    appid: '######',
    sentence: 'あたしはプログラムの女の子です',
    results: 'ma'
})

options = {
  host: 'jlp.yahooapis.jp',
  port: 80,
  path: '/MAService/V1/parse?' + qst
};

http.get(options,
  (res) ->
    body = ""
    res.on('data', (data) -> body += data)
    res.on('end', -> parser(body, (err, result) -> console.log(result.ma_result.word_list)))
  ).on('error',
  (e) -> console.log("Got error: " + e.message)
)

結果

{ word: 
   [ { surface: 'あたし', reading: 'あたし', pos: '名詞' },
     { surface: 'は', reading: 'は', pos: '助詞' },
     { surface: 'プログラム', reading: 'ぷろぐらむ', pos: '名詞' },
     { surface: 'の', reading: 'の', pos: '助詞' },
     { surface: '女の子', reading: 'おんなのこ', pos: '名詞' },
     { surface: 'です', reading: 'です', pos: '助動詞' } ] }

CoffeeScriptで自分のメソッドを呼び出す

exports.Test = class Test
  method_a: () -> method_b()
  method_b: () -> console.log("method b")

がmethod_bが見つからないっていうエラーが出た。

coffee> Test = require("./test").Test
[Function: Test]
coffee> t = new Test
{}
coffee> t.method_a()
ReferenceError: method_b is not defined

小一時間悩んだ挙句@が必要なことに気づいた

exports.Test = class Test
  method_a: () -> @method_b()
  method_b: () -> console.log("method b")

CoffeeScriptのswitch文で(Rubyのように)多値を取る方法が分からない

Rubyのようにこんな感じに書きたいのだけど

switch Math.floor(Math.random()*100)
  when 0..30  then console.log('small')
  when 31..60 then console.log('middle')
  else             console.log('large')

そうすると、

Error: In test.coffee, Parse error on line 2: Unexpected '..'

と怒られるのです。

スマイルプリキュアオーバーフロー

最近見かけたsofで役に立ったもの

CoffeeScriptでループさせるには

while文かloop文。個人的にはloopのほうが好みかな

ExpressのdynamicHelpersの 使いどころ

Node.jsのnextTickを理解する

これはかなり良かった。理解が深まった。

スマイルプリキュア

5人も覚えられない

恋する非同期プログラム

去年、恋するプログラムをJavascriptで書いていこうとしたんだけど、Canvasとかわからなくて4章くらいで放置してた。

その間にHTML Canvas読んだり、Node.jsもわかってきたので再開しようかなと。vowsも使ってみたいし。

で、このやつをCoffeeScriptに書きなおした

class Responder
 constructor: (@name) ->
 response: (input) -> input + 'ってなに?'

class Unmo
 constructor: (@name) ->
   @responder = new Responder('what')
   @responder_name = @responder.name
 dialogue: (input) -> @responder.response(input)

u = new Unmo('proto')
console.log(u.dialogue('マックロシーベルト'))

動かしてみる

$ coffee test.coffee 
マックロシーベルトってなに?

ProductName 恋するプログラム―Rubyでつくる人工無脳
秋山 智俊
毎日コミュニケーションズ / ?円 ( 2005-04 )


vowsはCoffeeScriptでテストができる

CoffeeScriptでテストをできるライブラリを探していたらvowsで出来ることを知った。

vows = require 'vows'
assert = require 'assert'

vows
  .describe('Division by zero')
  .addBatch
    'when dividing a number by zero':
      topic: -> 42/ 0

      'we get Infinity': (topic) ->
        assert.equal topic, Infinity

    'but when dividing zero by zero':
      topic: -> 0 / 0

      'we get a value which':
        'is not a number': (topic) ->
          assert.isNaN topic

        'is not equal to itself': (topic) -> 
          assert.notEqual topic, topic

  .export(module)

これをvows --specすると

vows

かなり良い感じですね。

ExpressのスケルトンをCoffeeScriptに変換する

sof見てたら「xxx.jsっていうjavascriptをCoffeeScriptにしたいんだけど」「js2coffee使え」っていう問答があったので、使ってみた。

$ npm install -g js2coffee
$ express testapp
$ cd testapp
$  js2coffee app.js

で、無事に変換された

app = module.exports = express.createServer()
app.configure ->
  app.set "views", __dirname + "/views"
  app.set "view engine", "jade"
  app.use express.bodyParser()
  app.use express.methodOverride()
  app.use app.router
  app.use express.static(__dirname + "/public")

app.configure "development", ->
  app.use express.errorHandler(
    dumpExceptions: true
    showStack: true
  )

app.configure "production", ->
  app.use express.errorHandler()

app.get "/", routes.index
app.listen 3000
console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env

となると、srcフォルダを掘ってそこに.coffeeを入れて、libにコンパイルしたjavascriptを生成するようなCakefieを書けばいいような気がするが、nodeunitのテスト入れたいばあいどうすればいいのかな?

perlみたいに

$ cake
$ cake test

ってやれればいいんだが。

CoffeeScript自体のソースコードを読むのがいい気がしているが、他にベストプラクティス的なものはあるんだろうか?

クライアントサイドのMVCを考える本

Spineっていうクライアント側でMVCを実現するWebフレームワークが面白かったので、そのライブラリの作者が書いたJavascript Web Applicationsっていう本を読んでいる。

ProductName JavaScript Web Applications
Alex Maccaw
Oreilly & Associates Inc / 3020円 ( 2011-08-30 )


先週末の温泉旅行と、今日の出張の新幹線の行き帰りで7章まで読んだので感想をちょっと書いておくが、サイ本に対してフクロウ本って言われるようになるのかなぁって感じの内容だと思うがどうなんだろう。

手を動かしながら学ぶという本ではなくて、クライアント側でMVCを実現するにはどうしたらよいかという方法論を学ぶ本なので、写経して覚えるというより、自分の中の知識を統合して一つ上のレベルに昇格する感じ。

  • Spineを一通り使えるようになっていること
  • JGPの内容を理解していること
  • Javascriptパターンを理解していること

は必須な感じ。副題がjQuery developer's Guide to Moving State to the Clientと書いてあるんだけどjQueryの知識はあんまり必要でなくて、ググッて理解できるか、該当するソースコードを読めればOKだと思います。それよりもなぜそういう実装にしないといけないのか?っていうことを考えることを求められますね。

ProductName JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
Douglas Crockford
オライリージャパン / 1890円 ( 2008-12-22 )


ProductName JavaScriptパターン ―優れたアプリケーションのための作法
Stoyan Stefanov
オライリージャパン / 2940円 ( 2011-02-16 )