iPhone等のスマホ向けレイアウトを簡単に作れるjQueryMobileを試してみた

jQueryMobileを触ってみたのでメモ。

jQueryMobileはスマートフォン、タブレットに特化したJavascript Framework。

準備

downloadページからjQueryMobile本体とcssをダウンロードしてくる。
http://jquerymobile.com/download/

ホスティングもしているみたいだけど、今回はjQueryMobileで提供されるデザインテーマで利用される画像パスがアプリ側と合わなかった為zipをダウンロードした。
ダウンロードしたファイルを展開するとjs,css,imagesが入っている。
cssはjQueryMobileが要素の属性を判断してレイアウトを整えるのに使われる。
これらと合わせてjQuery本体も読み込む。
よってロードする順番は以下のようになる。

<link href="/stylesheets/jquery.mobile-1.0a2.min.css" media="screen" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script src="/javascripts/jquery.mobile-1.0a2.min.js" type="text/javascript"></script>

以上で準備は完了。

要素の記述

実際にjQueryMobileを活用してスマホ向けviewを構築していく。
まずメインのコンテンツとなる部分を以下の属性をつけて記述する。

<div data-role="page" data-theme="d">
</div>

data-role="page"はメインのコンテンツであることを示す属性。
data-theme="d"は適用するデザインテーマ*1の種類を指定する為の属性。

次にヘッダ要素の記述。

<div data-role="header">
  header
</div>

ここでもヘッダを表すdata-role="header"を指定することでヘッダレイアウトが適用される。

リスト要素の記述。

<ul data-role="listview" data-theme="c" data-inset="true">
  <li>
    <a href="/vocabulary/new">add word</a>
  </li>
  <li>
    <a href="/dashboard/index">dashboard</a>
  </li>
  <li>
    <a href="/mobile/list">list</a>
  </li>
  <li>
    <a href="/dashboard/examination">examination</a>
  </li>
</ul>

ここまでくると属性については大体予想できるでしょうが、
data-role="listview"でリスト構造を取ることを指定する。
data-theme="c"でリストのデザインテーマ*2を適用。
data-inset="true"を指定することによって一括りのデータセットとされ、全体が角丸レイアウトされる模様。(詳細未確認

途中に上記以外の要素が入っているが、以上をレンダリングさせると以下のようなレイアウトになる。

リンクについて

jQueryMobileにおいてリンク(aタグ)は全てAjaxによるリクエストとなる。*3
これによりページ遷移なしにiOSのネイティブアプリの様なページ遷移を行うことができる。
イメージについてはjQueryMobileデモを参照。
リンク先のページについては特にAjaxリクエストであることを意識する必要はない。
従来通りのHTMLで、jQueryMobileのレイアウトを記述したHTMLを返すだけで良い。
例:

<!DOCTYPE html>
<html>
<head>
  <title>title</title>
  <link href="/stylesheets/jquery.mobile-1.0a2.min.css" media="screen" rel="stylesheet" type="text/css" />
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
  <script src="/javascripts/jquery.mobile-1.0a2.min.js" type="text/javascript"></script>
</head>
<body>

<div data-role="page" data-theme="d">
  <div data-role="header">
    <h1>Word List</h1>
  </div>

  <div style="margin:0 3px;">
    <ul data-role="listview" data-theme="c" data-inset="true">
      <li>
        <a href="/vocabulary/show/1">word</a>
      </li>
      <li>
        <a href="/vocabulary/show/2">duplicate</a>
      </li>
      <li>
        <a href="/vocabulary/show/3">disrupt</a>
      </li>
      <li>
        <a href="/vocabulary/show/4">rupt</a>
      </li>
      <li>
        <a href="/vocabulary/show/5">annual</a>
      </li>
      <li>
        <a href="/vocabulary/show/6">fundamental</a>
      </li>
      <li>
        <a href="/vocabulary/show/7">ensure</a>
      </li>
      <li>
        <a href="/vocabulary/show/8">add</a>
      </li>
    </ul>
  </div>
</div>
</body>
</html>

jQueryMobileはこのレスポンスをロードして以下の様にレイアウトを整える。

この様に自動でbackボタンも付けられる。これを利用してページ遷移することなく元のページに戻ることができる。

まとめ

jQueryMobileはスマートフォン向けのページを素早く簡単に構築することができます。
自前で実装すると面倒な無遷移のページ移動が簡単に実装できます。
またデザインもjQueryMobile側で吸収してくれているので大量のcssを書かずに済みます。
これ以外にもたくさん機能が提供されているので非常に有用なフレームワークであると思います。
特にイベント関連の充実は本当に素晴らしいです。

これでデザインテーマも簡単に記述できるようになるともっと良いものになると思いました。

*1:http://jquerymobile.com/demos/1.0a2/#docs/api/themes.html

*2:http://jquerymobile.com/demos/1.0a2/#docs/lists/lists-themes.html

*3:Ajaxとならないように指定も可能

Rails3でAjaxでformのcallbackを指定するには

Rails3からの変更点でハマったのでメモ。

Rails2ではformをAjaxで送信するにはform_remote_tagを利用していた。
これがRails3では以下のように:remote => trueを指定することで実現する。

<%= form_tag(url_for(:action => 'create'), :remote => true, :id => "result_form") do %>
<% end %>

今は主にjQueryを使って開発している為、デフォルトで扱うjavascriptjQueryに変更する。
Gemfileに以下記述

gem 'jquery-rails'

bundle installして関連ファイルをgenerate

$ bundle install
$ rails g jquery:install

jquery.jsとrails.jsを読み込むように設定
jquery.jsはgoogleから読み込み

#app/views/layouts/application.html.erb等に記述
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
  <%= javascript_include_tag 'rails' %>

formからのリクエストを受け取るcallbackをJavascriptファイルに設定

$(function($){
    $('#result_form')
        .bind("ajax:complete", function(){
           //ここに具体的な処理を記述
        });
});

他のステータスについても記述可能
それぞれ引数を受け取ることもできる。

$(function($){
    $('#result_form')
        .bind("ajax:loading", function(xhr){
           //ここに具体的な処理を記述
        })
        .bind("ajax:success", function(data, status, xhr){
           //ここに具体的な処理を記述
        })
        .bind("ajax:complete", function(xhr){
           //ここに具体的な処理を記述
        })
        .bind("ajax:failure", function(data, status, xhr){
           //ここに具体的な処理を記述
        });
});

処理を無名関数に記述できるのでRails2でのようにviewにゴチャっと書く必要がなくなり、より明確になったと思う。

MongoDBのReplica Setsについての概要

MongoDBによるレプリケーションの一種、Replica Setsについての概要

MongoDBでは各種RDBMSで採用されているMaster/Slaveでのレプリケーション方式の他にReplica Setsという仕組みを利用することができる。

これは複数のDBプロセスをクラスタリングすることで冗長性を確保する仕組みで、
従来のレプリケーションと違って自動でのFailOverを実現している。

具体的にはPrimaryであるメンバが各種クエリを受付、Read,Writeを行う。
書き込まれた内容はSecondaryにミラーリングされる。
下記図を参照。

実際にReplica Setsを構築する為の手順を以下に示します。

複数のmongodプロセスを立ち上げる。
その為のデータディレクトリを作成。

$ mkdir -p ./data/repltest1
$ mkdir -p ./data/repltest2
$ mkdir -p ./data/repltest3

mongodプロセスの立ち上げ

$ mongod --replSet repltest --port 27017 --dbpath ./data/repltest1 --rest
$ mongod --replSet repltest --port 27018 --dbpath ./data/repltest2 --rest
$ mongod --replSet repltest --port 27019 --dbpath ./data/repltest3 --rest

--restオプションを付与するとHTTP Admin UIでの詳細表示が可能となる。

Replica Setsの設定

$ mongo localhost:27017

以下mongoコンソールで入力
クラスタを定義
_idにはmongod立ち上げ時の--replSetの値をセットする。

config = {_id: 'repltest', members: [
   {_id: 0, host: 'localhost:27017'},
   {_id: 1, host: 'localhost:27018'},
   {_id: 2, host: 'localhost:27019'}]
}

定義したクラスタをrs.initiate()に流しこむ。

> rs.initiate(config);
{
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}

状態を確認

> rs.status();
{
        "set" : "repltest",
        "date" : "Tue Oct 05 2010 11:56:23 GMT+0900 (JST)",
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "HOSTNAME.local:27017",
                        "health" : 1,
                        "state" : 1,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27018",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 14,
                        "lastHeartbeat" : "Tue Oct 05 2010 11:56:21 GMT+0900 (JST)"
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27019",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 14,
                        "lastHeartbeat" : "Tue Oct 05 2010 11:56:21 GMT+0900 (JST)"
                }
        ],
        "ok" : 1
}

詳細はHTTP Admin UIで確認できる。
ブラウザでhttp://localhost:28017を開く。
(Admin UIはデフォルトではmongodでのportに1000を加えたものが割り当てられる)

Replica Setsの詳細は下記URLより確認。
http://localhost:28017/_replSet

Secondaryの生存確認は2秒間隔で実施される。


この状態でPrimaryをctrl + c等で停止してみる。
すると下記の状態に遷移する。

従来のSecondaryがPrimaryに昇格しているのがわかる。

再び停止していたプロセスを立ち上げるとSecondaryとしてデータの同期が行われる。


このようにPrimaryが落ちた場合でも自動で他のプロセスが昇格することでauto failoverを実現している。

参考:MongoDB ReplicaSets



* mkdir のオプションが大文字だったのを修正

高専カンファレンスのLTで発表しました

高専カンファレンス2010秋 東京のLTで発表しました。

今回はテーマとして物事を正しく捉えて向き合い、そのために必要なことについて経験を元にお話しました。
そのスライドと動画です。

動画は53分あたりから。

第6回ジオメディアサミットで発表しました

2010/9/20 パシフィコ横浜で開催されたジオメディアサミットで発表しました。

当日のスライドと動画です。
ustして頂いたTechWaveさん、ありがとうございます。

当日はエンターテイメントセッションに割り当てられていたのですが、
空気を読まずに半分技術の話をしました。

何か質問、ご指摘等ありましたらお願いします。

Rails MySQLでintegerカラムに潜む罠

Railsでinteger型のカラムにinsertする際にハマったのでメモ。

以下の様なテーブルがあったとする。

  user_id, integer
  point, integer

これに対してActiveRecordを用いてintegerのmax値を超えたデータをinsertする。

  PointList.create(:user_id => 2, :point => 2247483647
  => #

と一見正常に見えるがデータをロードしてみるとintegerのmax値に丸められてしまっている。

  PointList.all
  [#]

この問題はpointカラムにvalidates_uniqueness_ofを設定している場合でもvalidate時点ではintegerを超えた値でも、
既に登録されているデータと値が異なる場合valid?がtrueとなる。

この為重複したデータがMySQL内に存在することになってしまう。

Ruby on Rails 2.3.8とMySQL5.1.48で確認しました。
SQLiteではこの問題は発生しませんでした。

Rails3のinstallからRSpec2のgenerateまで

Rails3 RCが出ようとしているところでRails3のinstallからRSpec2のgenerateまでの手順メモ。

Rails3のinstall
rvm上でinstallした

$ gem install rails3b
$ gem install rails --pre

$ rails -v
Rails 3.0.0.beta3

Railsプロジェクトを作成

$ rails todo

但し今後はrails new app_nameになる模様。

bundlerで作成するプロジェクトの依存関係を解消する。
依存関係はrails rootのGemfileに記述
今回はsqlite3-rubyrspec

gem 'sqlite3-ruby', :require => 'sqlite3'

group :test do
  gem "rspec", "2.0.0.beta.11"
  gem "rspec-rails", "2.0.0.beta.11"
end

依存gemをinstall

$ bundle install

RSpecをアプリにinstall

$ rails g rspec:install

Rails 2.xまでのscript/*はrailsコマンドに置き換えられました。
script/server → rails server or rails s
script/generate → rails generate or rails g
など。
これらは以下のコマンドで確認できる。

$ rails コマンド -h

モデルの作成
rails g rspec:installしておけばspecも含めて一気に作ってくれる。
普段はTest::Unitのファイルが作成される。

$ rails g model todolist description:string todo_priority:integer
      invoke  active_record
      create    db/migrate/20100609134110_create_todolists.rb
      create    app/models/todolist.rb
      invoke    rspec
      create      spec/models/todolist_spec.rb

rails g rspec:modelというのもあるが、これはspecファイルのみ作成される。
これまでのscript/generate rspec_modelと同じ様な動きと思ったら違っていた。
今回のTest::Unitを置き換える形でgeneraterが走るのはRSpecの使われ方を考えると、正しい方向にシフトしたっていうことなのか。

Specの実行

$ spec -c spec/

RSpec2の新しいAPIもろもろはこれから調べる。