ラベル javascript の投稿を表示しています。 すべての投稿を表示
ラベル javascript の投稿を表示しています。 すべての投稿を表示

2011年7月28日木曜日

backbone.jsがいつのまにかpjax対応していた

■概要

年初にbackbone.jsの調査をしていた頃は、ajaxで画面遷移を行うには、fragmentを利用する事しかできませんでした。

しかし0.5以降では、Backbone.Routerを利用する事によって、pjax(HTML5のpushStateを利用した話題のあれ)な画面遷移が出来るになっています。

pjaxについては、こちらをご参照ください。
またgithubのファイルブラウザはpjaxの良例として有名なのでチェックしてみて下さい。


■何処がpjaxなのか?

まずは下記手順を実施後、http://localhost:3000/を閲覧してみましょう。chrome等html5対応ブラウザで確認して下さい。


次にhelpをクリックしてみましょう。

注目すべきなのは、
①URLが変わっている
②画面下部のレンダリング日時が変わっていない。

次にaboutをクリックしてみましょう。helpの時と同じです。

ブラウザの「戻る」ボタンをクリックしてみましょう。
About => Help => Indexに遷移します。「進む」ボタンもいい感じに遷移します。

次にHelpが画面に表示されている状態で、「F5」を押下してみましょう。
レンダリング日時が変わったはずです。


■説明

ajaxは非同期に(高速に)画面の一部を書き換える技術ですが、ユーザーの利便性の観点から、ブラウザの戻る、進むを利用可能にする必要があります。これまでのajaxでは、#(フラグメント)を利用して実現する事が多かったのですが、あまり良い解決策では有りませんでした。

pjaxでは、HTML5のpushStateを利用する事により、ブラウザのURLを切り替えつつ、画面の一部のみ切り替える事ができます。無論、URLは通常のURLですので、「通常のHTTPリクエスト」として処理するように実装する事が可能です。下記のIndexControllerでは、Ajax通信かどうかでレイアウトの適用を変更しています。
これができたら何が嬉しいのか?!それは読者への宿題にしておきます(^.^;

ちなみに、IE(≒pushStateをサポートしていないブラウザ)でみたら fragment での遷移に自動的に切り替わってました。。

■体験手順

・まずは前回の記事を参考にプロジェクトを生成する。

$ ./gen.sh pjax

・移動して体験に必要なファイルを用意する

$ cd pjax

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>pjax2</title>
    <meta charset="utf-8"/>
    <%= stylesheet_link_tag :all %>
    <%= javascript_include_tag :defaults %>
    <%= javascript_include_tag :backbone %>
    <script><%= yield :javascripts %></script>
    <%= csrf_meta_tag %>
  </head>
  <body>
    <%= content_for?(:content) ? yield(:content) : yield %>
  </body>
</html>

app/views/layouts/index.html.erb

<%= content_for :javascripts do %>
$(function() {
  var Workspace = Backbone.Router.extend({
    routes: {
      "":      "index",
      "help":  "help",
      "about": "about"
    },
    index: function() {
      $("#app").load("/");
    },
    help: function() {
      $("#app").load("/help");
    },
    about: function() {
      $("#app").load("/about");
    }
  });
  var ws = new Workspace();
  Backbone.history.start({pushState: true});

  $("#indexButton").click(function() {
    ws.navigate("", true);
  });
  $("#helpButton").click(function() {
    ws.navigate("help", true);
  });
  $("#aboutButton").click(function() {
    ws.navigate("about", true);
  });
});
<% end %>

<%= content_for :content do %>
  <div id="app"></div>
  <hr/>
  <input type="button" name="index" value="index" id="indexButton" />
  <input type="button" name="help"  value="help"  id="helpButton" />
  <input type="button" name="about" value="about" id="aboutButton" />
  最終レンダリング日時:<%= I18n.l Time.now %>
<% end %>

<%= render :file => "layouts/application" %>

app/views/index/index.html.erb

<h1>Index</h1>

app/views/index/help.html.erb

<h1>help</h1>

app/views/index/about.html.erb

<h1>about</h1>

app/controllers/index_controller.rb

class IndexController < ApplicationController
  layout "index"
  def index
    render :layout => !request.xhr?
  end
  def help
    render :layout => !request.xhr?
  end
  def about
    render :layout => !request.xhr?
  end
end

config/routes.rb

最後に
root :to      => "index#index"
match "help"  => "index#help"
match "about" => "index#about"

public/index.html <= 削除

サーバ起動!
$ rails server

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年3月24日木曜日

javascriptでの関数内関数について

javascriptでの関数内関数について、基礎的な事が良くわかってなかったのでメモしておきます。
ruby on railsのalias_method_chain的な動きをさせたくて

function foo() {
  console.debug("foo");
}

var fooWithoutBar = foo;

foo = function() {
  console.debug("before foo");
  fooWithoutBar();
  console.debug("after foo");
}

foo();

と書きましたが、これは問題なく動きます。ところが

(function() {
  function foo() {
    console.debug("foo");
  }

  var fooWithoutBar = foo;

  foo = function() {
    console.debug("before foo");
    fooWithoutBar();
    console.debug("after foo");
  }
}());

foo(); // error

は動きません。関数内関数は、本当に関数内関数として動きます!rubyとは違いますな。。

8行目の代入がグローバルスコープ?とか考えてしまいましたが
・変数fooはローカルスコープで宣言されている(2行目)
・8行目で変数を探す際、ローカルスコープから探し始める
・ローカルスコープで発見!
・その変数に新しい関数オブジェクトを代入
という訳で、上記の即時関数の外ではfooは見えません。

ちなみに

  var fooWithoutBar = foo;
  ↓
  fooWithoutBar = foo;

とすると、即時関数外でもfooWithoutBar()は呼び出せます。代入時点ではfooWithoutBarという変数は存在しないのでグローバルスコープの変数となる為と思われます。

2011年1月29日土曜日

CoffeeScriptをWindowsで使う

■概要

CoffeeScript面白いのですが、*nix系でしか利用できない。と思ってましたが、*.coffee -> *.jsのコンパイルだけならWindowsでも可能な方法を発見したのでご報告します!

■方法

http://jashkenas.github.com/coffee-script/ からcoffee-scriptをダウンロード。
・解凍して、extras/coffee-script.jsを取り出す。
・coffee-script.jsと同じフォルダに、下記の様な感じでcoffee-scriptコンパイラを利用するスクリプトを作成する
  ※色々処理はしょってるので要注意!

// compiler.js
var fs = WScript.CreateObject("Scripting.FileSystemObject"); 

//coffeescriptを読み込んで評価
var path = WScript.ScriptFullName.replace(WScript.ScriptName,"");
eval(fs.OpenTextFile(path + "coffee-script.js", 1).ReadAll()); 

//
// ここからメインです。
//
var args = WScript.Arguments;
if (args.length == 0) {
  WScript.Echo("error: please Drag And Drop *.coffee file!");
} else {
  for (var i = 0; i < args.length; i++) {
    var src      = fs.OpenTextFile(args.Item(i), 1).ReadAll();
    var compiled = CoffeeScript.compile(src);
    var file     = args.Item(i).replace(".coffee", ".js");
    fs.OpenTextFile(file, 2, true).Write(compiled);
  }
}

・*.coffeeなファイルをドラッグ & ドロップすると、*.coffeeと同じフォルダに*.jsが出来上がります!

■参考

・JScriptでimport スマートにできないかなぁ http://d.hatena.ne.jp/TakamiChie/20080403/1207227691

■2011/01/29 12:45

書き込み時に新規作成フラグtrueがついていませんでしたので修正しました。