2011年4月22日金曜日

javascriptでのCIを目指して、phantomjsの環境を作成する4

■概要

前回までで、phantomjsが大体わかったので、jasmine with CoffeeScriptな環境を作成します。

■説明

・CoffeeScriptのコンパイルを楽したい。ここではwatchrを利用してみます。

$ sudo gem install watchr

$ mkdir demo; cd demo
$ mkdir src; mkdir spec
$ vim coffee.watchr

watch(/(src|spec)\/.*\.coffee/) { |md|
  system("coffee -cb #{md[0]}")
}

$ watchr coffee.watchr

(別の端末で)

$ vim src/test.coffee

console.log "a"

$ ls src
test.coffee test.js <= できてます!

・ではjasmineを利用してブラウザからテストを実行します。OOPでは古典的な"カウンター"を作ってみます。
$ wget http://pivotal.github.com/jasmine/downloads/jasmine-standalone-1.0.2.zip
$ unzip *.zip
$ rm *.zip
$ vim spec/CounterSpec.coffee

describe "Counter", ->
  beforeEach ->
    @counter = new Counter()

  describe "#constructor", ->
    it "の初期値は0である", ->
      expect(@counter.value()).toEqual(0)

  describe "#incr", ->
    it "は値を1増加させるはず", ->
      @counter.incr()
      expect(@counter.value()).toEqual(1)

$ vim src/Counter.coffee

class Counter
  constructor: () ->
    @count = 0

  incr: () ->
    @count++

  value: () ->
    @count

$ vim SpecRunner.html (Counter.js/CounterSpec.jsの読み込みを記載する)

ブラウザで http://localhost/demo/SpecRunner.htmlを確認するといい感じです!。
最後にphantomjsからjasmineのSpecRunner.htmlを起動します。
$ vim src/run-jasmine.coffee

if phantom.state.length is 0
  if phantom.args.length isnt 1
    console.log 'Usage: run-jasmine.js URL'
    phantom.exit()
  else
    phantom.state = 'run-jasmine'
    phantom.open phantom.args[0]
else
  window.setInterval ->
    if document.body.querySelector('.finished-at')
      console.log document.body.querySelector('.description').innerText
      for el in document.body.querySelectorAll('div.jasmine_reporter > div.suite.failed')
        console.log ''
        for e in el.querySelectorAll('.description')
          console.log e.innerText
      phantom.exit()
  , 100

$ phantomjs src/run-jasmine.coffee http://localhost/demo/SpecRunner.html
2 specs, 0 failures in 0.039s <= テスト結果が得られてます。いいじゃん!

2011年4月21日木曜日

javascriptでのCIを目指して、phantomjsの環境を作成する3

■概要

前回に引き続きphantomjsのQuickStartを参考にCoffeeScriptを書いてみる。

■説明

・Loading & Rendering

Loadingの説明を見ているといくつかポイントが見えてきます。

・コマンドライン引数に指定されたスクリプトは、複数回呼び出される
※phantom.exit()するまで
・phantom.stateは値が維持されるので、初回呼び出しかどうか判断可能
初回は空文字列
・phantom.openは引数で指定されたページを読み込む
読み込み終了までblockingされる
・phantom.loadStatusはphantom.open後の状態が保持される
"success" もしくは "fail"

という訳で、LoadPage(とRendering)をCoffeeScriptで書いてみます。

$ vim google.coffee

LoadPage = (name, url, action) ->
  if !phantom.state
    phantom.state = name
    phantom.open url
  else
    action()
    phantom.exit()

LoadPage "google", "http://www.google.co.jp", ->
  phantom.render "#{phantom.state}.png"

$ phantomjs google.coffee
$ ls google*
google.coffee google.png

ちゃんと出来ました!なおphantom.viewportSizeの設定をしないとwidth/heightは適当な値になるようです














ちなみにdom treeは読み込み後documentオブジェクトを操作できます。

LoadPage "google", "http://www.google.co.jp", ->
  console.log document.getElementById("prm").innerText

$ phantomjs google.coffee
世界中から寄せられた日本へのメッセージが日本語で見られるサイトを開設しました。


■参照

phantomjsのQuickStart

■雑感

次回は本当にCIができたらな~と思いつつ(ゴールは近いはず)

2011年4月20日水曜日

javascriptでのCIを目指して、phantomjsの環境を作成する2

■概要
前回 phantomjsのインストールに成功したので、色々触って見ます。

・ちなみにphantomjsってCoffeeScriptいけるんですね!

$ phantomjs

Usage: phantomjs [options] script.[js|coffee]
  [script argument [script argument ...]]

・次からはphantomjsのQuickStartを、CoffeeScriptに書き写しながら進めます。

■説明

・まずはhello world

$ vim hello.coffee

console.log "hello, world!"
phantom.exit()

$ phantomjs hello.coffee
hello, world!

・次に同期sleep

$ vim delay.coffee

for t in [10..1]
  console.log t
  phantom.sleep 1000
console.log 'BLAST OFF'
phantom.exit()

$ phantomjs delay.coffee
10
9
...
1
BLAST OFF

・ちなみに非同期sleepは

$ vim adelay.coffee

fibs = [0, 1]

ticker = window.setInterval ->
  console.log fibs[fibs.length - 1]
  fibs.push fibs[fibs.length - 1] + fibs[fibs.length - 2]
  if fibs.length > 10
    window.clearInterval ticker
    phantom.exit()
, 300

$ phantomjs adelay.coffee
1
1
2
...
21
34
functionオブジェクトが最後の引数ではない場合ここにカンマ何ですね。。

・コマンドライン引数は

$ vim arguments.coffee

if phantom.args.length is 0
  console.log 'Try to pass some args when invoking this script!'
else
  phantom.args.forEach (arg, i) -> console.log "#{i}: #{arg}"
phantom.exit()

$ phantomjs arguments.coffee
Try to pass some args when invoking this script!

$ phantomjs arguments.coffee a b c
0: a
1: b
2: c

次回は、感じのページオープンとdom tree周り経由でCIにつなげたいと思う今日この頃

■参考

phantomjsのQuickStart

■雑感

CoffeeScriptの構文は、あまり違和感が無いな~
CoffeeScriptを習得するには、js -> coffeeの翻訳が一番?

■更新履歴

2011/04/20 http://tech.kayac.com/ を参考にさせて頂き、setIntervalの記載方法を変更しました。

2011年4月13日水曜日

javascriptでのCIを目指して、phantomjsの環境を作成する1

■概要
qunit-tapとproveを使ってJSの単体テストのCIをする方法に感化されて、まずは環境づくりに挑戦してみます。

■インストール手順

centos 5.5に環境を作るまでの手順です。

○phantomjsをインストール

http://code.google.com/p/phantomjs/wiki/BuildInstructionsのコメントとかも参考にしながら進めました。

# vim /etc/yum.repos.d/atrpms.repo

[atrpms]
name= CentOS-$releasever - ATrpms
baseurl=http://dl.atrpms.net/el$releasever-$basearch/atrpms/testing/
gpgcheck=1
gpgkey=http://ATrpms.net/RPM-GPG-KEY.atrpms
enabled=1

# rpm --import http://packages.atrpms.net/RPM-GPG-KEY.atrpms
# yum install qt47-devel qt47-webkit qt47-webkit-devel

# git clone git://github.com/ariya/phantomjs.git && cd phantomjs
# qmake-qt47
# make
# cp bin/phantomjs /usr/local/bin (PATHの通ってる場所にコピー)

○phantomjsの確認

http://code.google.com/p/phantomjs/を参考に起動を確認しようと思いました

# vim test.js

if (phantom.state.length === 0) {
    phantom.state = 'pizza';
    phantom.open('http://www.google.com/m/local?site=local&q=pizza+in+new+york');
} else {
    var list = document.querySelectorAll('div.bf');
    for (var i in list) {
        console.log(list[i].innerText);
    }
    phantom.exit();
}

# phantomjs test.js
2011-04-13T00:00:32 [WARNING] phantomjs: cannot connect to X server


うーん問題あるみたいですねぇ。

○xvfbの準備

どうもxvfbをインストールして仮想画面を立ち上げないとphantomjsは使えない感じなのでインストール&起動します。

# yum install xorg-x11-server-Xvfb xorg-x11-fonts*
$ Xvfb :2 -screen 0 800x600x24 2> /dev/null &
$ export DISPLAY=:2.0
$ phantomjs test.js

Adrienne's Pizza Bar Restaurant
54 Stone Street, New York, NY
(212) 248-3838 -

John's Pizzeria
278 Bleecker St, New York, New York
(212) 243-1680 -

...


おーなんだか動いてますね!

○/dev/null 指定しないと、何か変なエラーメッセージが出てるんですけど..

FreeFontPath: FPE "built-ins" refcount is 2, should be 1; fixing.
Could not init font path element unix/:7100, removing from list!

=> fontの設定関係の問題っぽいのですが、どうも解決策がわかりません。。とりあえずほっています

○Xvfbどうやって止めたらいいんだ?

$ kill -9 `cat /tmp/.X2-lock`; rm -f /tmp/.X2-lock

こんな感じかな?

2011年4月6日水曜日

indexを作成すると本当に更新性能は下がるのか?

■概要
RDBMSの参照性能を向上させる為に、indexを作成する事は常識だと思います。
ところが「indexを張りすぎると更新性能が悪くなる」と言われており、理屈的にも理解できます(データおよびindexを更新する必要があるから)。
しかし実際どの程度劣化するのか知りたいので、実験してみたいと思います。

■前提
・さくらVPS 512Mコース
・CentOS 5.x
・mysql 5.0.77
・利用テーブル(下記の様なテーブルで順次indexを作成しなおしテストした)
drop table test_table;
create table test_table (
  id integer auto_increment not null
, v1 varchar(30) not null
, v2 varchar(30) not null
, v3 varchar(30) not null
, v4 varchar(30) not null
, v5 varchar(30) not null
, primary key(id)
);
--create index v1_index on test_table(v1);
--create index v2_index on test_table(v2);
--create index v3_index on test_table(v3);
--create index v4_index on test_table(v4);
--create index v5_index on test_table(v5);
・テストデータ 10万件insert (下記の様なrubyスクリプトを作成し出来上がったinsert.sql文ファイルを実行した)
def random_string(len)
  (0...len).map{ ('a'..'z').to_a[rand(26)] }.join
end

open("insert.sql", "wb") do |f|
  f.write("truncate table test_table;\r\n")
  f.write("set @time:=now();\r\n")
  (0...100000).each do |i|
    v1 = random_string(30)
    v2 = random_string(30)
    v3 = random_string(30)
    v4 = random_string(30)
    v5 = random_string(30)
    vals = "'" << [v1, v2, v3, v4, v5].join("','") << "'"
    f.write("insert into test_table (v1, v2, v3, v4, v5) values (#{vals});\r\n")
    puts i
  end
  f.write("commit;\r\n")
  f.write("select timediff(now(), @time);\r\n")
end

$ mysql -u xxx < insert.sql

■結果

index数 1回目 2回目 3回目 4回目 5回目 平均処理時間(秒)
0
15
20
14
10
14
12.6
1
19
18
16
15
14
16.4
2
15
14
18
18
15
16.0
3
18
14
17
20
21
18.0
4
23
18
18
19
18
19.2
5
19
22
19
24
20
20.8



■結論

確かに下がるには、下がるようだ。。という訳でセオリー通りindexの張りすぎには注意が必要のようです。

ただ全体的にy = ax + bの一次関数likeな上昇なので、"+b"がどこに消費されるのかが気になるところです(sql文のparse?)