マクロの利用例

http://f.hatena.ne.jp/images/fotolife/a/amrita2/20070711/20070711144132.png

これは、Amrita2+Gettextによって日本語化したLoginEngineのユーザ情報修正画面です。この上半分の部分テンプレートでは、マクロを活用してAmrita2化してみました。

元のrhtmlは次のようになっています。

<div class="user_edit">
  <table>
    <%= form_input changeable(user, "firstname"), "First Name", "firstname" %>
    <%= form_input changeable(user, "lastname"), "Last Name","lastname" %>
    <%= form_input changeable(user, "login"), "Login ID", "login", :size => 30 %>
    <%= form_input changeable(user, "email"), "Email", "email" %>
    <% if submit %>
      <%= form_input :submit_button, (user.new_record? ? 'Signup' : 'Change Settings'), :class => 'two_columns' %>
    <% end %>
  </table>
</div>

form_inputは、LoginEngine独自のhelperメソッドで、<tr><td>... を生成するものです。これと似た機能ををAmrita2のマクロとして作成してみました。

changeableも同じくLoginEngine独自のメソッドです。いくつかの条件によって text_field とread_only_fieldを切り替える処理ですが、この部分はこの例では省略しています。(常にtext_fieldになる)

class TwoColumns < Amrita2::Macro::Base
  TemplateText = <<-END
      <<table<
        <<tr class="two_columns":rows<
          <<td class="prompt"<
              <<label:title>>
          <<td class="value" :contents>>
  END

  def macro_data(element)
    rows = element.search("tr").collect do |c|
      title, contents = *c.search("td")
      {
        :title => title.contents,
        :contents => Amrita2::SanitizedString[contents.children.to_s],
      }
    end
    {
      :rows => rows,
    }
  end
end

このマクロは2カラムのテーブルを作成し、1つ目のカラムには<label>...</label>を埋めこむものです。

これを利用すると、上記のテンプレートは次のようになります。

<%(BeforeCompile)
  use_macro(TwoColumns)
%>

<<div class="user_edit"<
  <<two_columns<
    <<<-------------------------------------------------------------
      ||| First Name: | <%= text_field "user", "firstname" %>      |
    <<<-------------------------------------------------------------
      ||| Last Name:  | <%= text_field "user", "lastname" %>       |
    <<<-------------------------------------------------------------
      ||| Login ID:   | <%= text_field "user", "login" %>          |
    <<<-------------------------------------------------------------
      ||| Email:      | <%= text_field "user", "email" %>          |
  << ?[submit] <
    <%= submit_button 'user', (user.new_record? ? _('Signup') : _('Change Settings')), :class => 'two_columns' %>

区切られた表の部分は、<tr><td>を生成する略記法です。これによって生成されたxmlが一回分解されて、上記のマクロで再度テーブルに組み込まれます。

これを日本語化して表示すると次のようになります。

<div class = "user_edit">
  <table>
    <tr class = "two_columns">
      <td class = "prompt"><label></label></td>
      <td class = "value"> <input id="user_firstname" name="user[firstname]" size="30" type="text" value="中島" /> </td>
    </tr>
    <tr class = "two_columns">
      <td class = "prompt"><label>名前</label></td>
      <td class = "value"> <input id="user_lastname" name="user[lastname]" size="30" type="text" value="拓" /> </td>
    </tr>
    ....

「姓」や「名前」の部分が<label>...</label>に挟まれていることに注目してください。

マクロの作成は、はっきり言って難易度が高いですが、これを一回作ってしまうと、表の略記法と組み合わせることで、以下のようなメリットがあります。

  1. 個々のテンプレートを最終表示結果に近い直感的な形で記述できる
  2. マクロの展開方法もAmXMLのテンプレートになっているので、CSSとマクロ内テンプレートを調整することで、最終的なHTMLの調整を行なうことができる
  3. <table>によるレイアウトから<div>等によるレイアウトへの変更もマクロ変更のみで可能
  4. アプリ側(ビュー)の記述範囲とマクロの記述範囲を入れ子にできる(上記の<label>の付加はマクロの担当、<label>の中(セルの中身)はアプリ側の記述)

元ネタでは、helperメソッドを駆使してややトリッキーな方法によって似たようなことをやっていますが、この方式の方が意図が明確で、保守性も高いと思います。

自動Gettext

Amrita2にGettextと連動する機能を実装しました。これを利用すると、ビューを最小限の手間で国際化することができます。

サンプルとして LoginEngineをAmrita2化+日本語化していますが、たとえば、homeというビューは次のようになります。

元のrhtmlのファイルは次のようなものです。

<div title="<%= title_helper %>" class="memo">
  <h3>Welcome</h3>
  <p>You are now logged into the system, <%= @fullname %>...</p>
  <p>Since you are here it's safe to assume the application never called store_location, otherwise you would have been redirected somewhere else after a successful login.</p>

  <%= link_to '&#171; logout', :action => 'logout' %>
</div>

これをAmrita2のAmXML形式に書き直すと、こうなります。

<<div class="form":|Attr[:title=>:title_helper]<
  <<h3<
    Welcome
  <<p<
    <% $_ = { :name => @fullname } %>
    You are now logged into the system,  %{name} ...
  <<p<
    Since you are here it's safe to assume the application never called store_location,
    otherwise you would have been redirected somewhere else after a successful login.

  <%= link_to '&#171; ' + _('logout'), :action => 'logout' %>

Rakefileに次のようなタスクを追加して、これを実行します。

task :updatepo do
  $: << 'vendor/plugins/amrita2/lib'
  require 'gettext/utils'
  require 'amrita2/gettext'
  require 'amrita2/macro'
  GetText.update_pofiles("login_engine", #テキストドメイン名(init_gettextで使用した名前)
                         Dir.glob("{app,config,components,lib}/**/*.{rb,rhtml,a2html}"),  #ターゲットとなるファイル
                         "logine_engine 1.0.0"  #アプリケーションのバージョン
                         )
end

すると、*.a2html形式のファイルに対して、Amrita2で追加したパーサが起動して、poファイルができます。これをja/*.potにコピーして、日本語を入れていきます。

potファイルから上記のビューに関連する所だけ抜き出すと

#: app/views/user/home.a2html:-
msgid "Welcome"
msgstr "ようこそ"

#: app/views/user/home.a2html:-
msgid ""
"Since you are here it's safe to assume the application never called "
"store_location,\n"
"    otherwise you would have been redirected somewhere else after a "
"successful login."
msgstr ""
"この画面が表示されるのはアプリケーションが store_location を呼んでないからで"
"す。呼んでいれば、ログインに成功した後には、他の画面にリダイレクトされるで"
"しょう"

#: app/views/user/home.a2html:-
msgid "You are now logged into the system,  %{name} ..."
msgstr "%{name}さん、あなたはこのシステムにログインしています。"

#: app/views/user/home.a2html:-
msgid "logout"
msgstr "ログアウト"

後は、以下のページにあるように通常通りの作業をすればOKです。

結果、次のように表示されます。

<div title="UserController home" class="form">
  <h3>ようこそ</h3>
  <p>  中島 拓さん、あなたはこのシステムにログインしています。</p>
  <p>この画面が表示されるのはアプリケーションが 
     store_location を呼んでないからです。呼んでいれば、
     ログインに成功した後には、他の画面にリダイレクト
     されるでしょう</p> 
  <a href="/user/logout"><<ログアウト</a> 
</div>

ポイントは、以下の通りです。

  1. ビュー内の文字列は全て自動的にGettext対応の文字列として抜き出す
  2. 可変部分の埋めこみもほぼ自動的
  3. erb形式との混在が可能

順番に説明していくと

  <<h3<
    Welcome

"Welcome"という文字列が自動的にpoファイルに抽出されています。

  <<p<
    <% $_ = { :name => @fullname } %>
    You are now logged into the system,  %{name} ...

<% ... %>内には、自由にRubyの処理を記述できます。各文字列は、内部的には以下のようなRubyソースにコンパイルされますから、この中で $_ にハッシュを設定することで、可変部の埋め込みが行われます。

    stream.concat _("You are now logged into the system,  %{name} ...") % $_

これを次のように書くこともできます。

  <<p:|ToHash[:name=>:fullname]<
    You are now logged into the system,  %{name} ...

なお、最初からAmrita2のテンプレートを想定して、コントローラ側でデータ構造をうまく設定すれば、このような処理( $_ への値の設定)は不要になります。

<%=....%>の中に翻訳すべき文字列がある場合には、一般のrhtmlファイルと同じように、文字列を_(...)で囲みます。

  <%= link_to '&#171; ' + _('logout'), :action => 'logout' %>

ビュー内の静的な文字列が特別な手間をかけることなく全て自動的に国際化されるので、アプリケーションによっては効果が大きいのではないかと思います。

はてぶのプライベート率を調べてみた

ちょっと気になることがあったので、はてなブックマーク人気エントリーになったエントリーのブックマーク数の、公開/非公開の割合を調べてみた。

スクリプトはこのエントリーの末尾に添付するけど、まずは実行結果。

表示項目は

  • URL(6/1から6/3の人気エントリー)
  • そのページに含まれるエントリー全体の公開/非公開のブックマーク計
  • 全体での非公開の割合(private ratio)
  • 非公開率の上位5つと下位5つのエントリー
http://b.hatena.ne.jp/hotentry?mode=daily&date=20070601
public total = 8912
private total = 2123
private ratio = 19.24
34.69 disneycom.jp(ディズニー混む.jp) (64/34)
32.94 データグリッド実現用JavaScriptライブラリ各種:phpspot開発日誌 (57/28)
29.97 Full Auto | 電子レンジだけで作るおいしい食べ物 (201/86)
27.91 ITmedia Biz.ID:GTD黒帯へのチェックリスト【あなたは何級?】 (124/48)
26.90  窓の杜 - 【特集】「Thunderbird」v2.0対応拡張機能特集 第1回  (125/46)
......
9.09 たまごまごごはん - マンガやゲーム作品の題名に4文字が多いワケ (60/6)
8.96 NC-15 - 似非メンヘラの戯言 (61/6)
7.69 PC相手に独り言、4割の女性が経験 (60/5)
6.85 私的録音録画小委員会、CD売上減と私的複製の関係めぐり議論は平行線 (68/5)
3.90 ニコニコ動画 開発者ブログ(新着情報)ニコニコ宣言とは。 (ようするに版) (74/3)

http://b.hatena.ne.jp/hotentry?mode=daily&date=20070602
public total = 7323
private total = 1681
private ratio = 18.67
29.55 TechCrunch Japanese アーカイブParallels 3.0〜Macを買う理由がまた一つ (31/13)
27.91 ITmedia Biz.ID:GTD黒帯へのチェックリスト【あなたは何級?】 (124/48)
27.88 萌え理論Blog - ショートカットキーまとめ (269/104)
26.98 Photoshopチュートリアル ミニチュア模型を撮影したような写真に加工する - DesignWalker (249/92)
26.90  窓の杜 - 【特集】「Thunderbird」v2.0対応拡張機能特集 第1回  (125/46)
......
8.93 痛いニュース(ノ∀`):人に近い、しなやかな動きの「子供型ロボット」開発 (51/5)
8.14 機動戦士ガンダム00 [ダブルオー] (79/7)
7.95 Google Gearsがすごいと思うなら、いい加減RSSを全文配信にしてくれ(管理人日記) - むぅもぉ.jp (81/7)
6.15 IT戦記 - そろそろサイボウズ・ラボについて一言いっとくか (61/4)
5.06 人に近いしなやかな動き 子ども型ロボット開発 (75/4)

http://b.hatena.ne.jp/hotentry?mode=daily&date=20070603
public total = 6743
private total = 1629
private ratio = 19.46
29.38 日刊スレッドガイド : お奨めの掃除道具 (226/94)
27.88 萌え理論Blog - ショートカットキーまとめ (269/104)
26.98 Photoshopチュートリアル ミニチュア模型を撮影したような写真に加工する - DesignWalker (249/92)
25.57 NC-15 - 頭のおかしい人事に対抗する方法 (326/112)
25.16 一流の研究者の集中力(2)超シングルタスクのすすめ | Lifehacking.jp (238/80)
......
7.14 はろはて。 - 垢BANされた人専用一括addスクリプト (26/2)
6.67 dankogaiを生理的に受け付けない理由 (42/3)
6.38 livedoor ニュース - 声優志望少女ワイセツ事件、声優ファンが事務所前で抗議 (44/3)
5.13 レジデント初期研修用資料: 「人類は衰退しました」感想 (37/2)
3.03 考えるための書評集 ZARD 坂井泉水の栄光と凋落。 (32/1)

だいたい、平均で20%くらいが非公開で、割合が高いエントリーは30%くらいが非公開になるようだ。

明確な傾向は見えないけど、しいて言えば、非公開率が高いエントリーではLifehack系のネタが目立ち、低い方では、2ちゃんねる系というか個人ニュース系というか、そちらのエントリーが多いような気がする。

実行したスクリプトはこれ。

#!/usr/bin/env ruby -Ku

require 'open-uri'
require 'rubygems'
require 'hpricot'

class PrivateBMChecker
  attr_reader :bmcounts, :pub_total, :priv_total

  def initialize
    @urls = []
  end

  def process(f)
    process_file(f)

    @bmcounts = @urls.collect do |url|
      doc = Hpricot(open("http://b.hatena.ne.jp/#{url}").read)
      {
        :url => url,
        :title => (doc/'span.title').text,
        :public_cnt => (doc/'span.public-count').text.to_i,
        :private_cnt => (doc/'span.private-count').text[1..-1].to_i
      }
    end

    @bmcounts.each do |b|
      b[:priv_ratio] = b[:private_cnt] * 100.0 / (b[:public_cnt] + b[:private_cnt])
    end    
    
    @pub_total = @priv_total = 0
    @bmcounts.each do |b|
      @pub_total += b[:public_cnt]
      @priv_total += b[:private_cnt]
    end
  end

  def process_file(f)
    doc = Hpricot(f.read)
    entries = doc/'div.entry-footer'
    entries.each do |e|
      process_entry(e)
    end
  end

  def process_entry(e)
    (e/'strong a').each do |a|
      @urls << a[:href]
    end
  end
end

class TextReporter
  def report(c)
    header(c)
    detail(c)
  end

  def header(c)
    puts "public total = %d" % c.pub_total
    puts "private total = %d" % c.priv_total
    puts "private ratio = %4.2f" % (c.priv_total*100.0/(c.pub_total + c.priv_total))
  end

  def detail(c)
    c.bmcounts.sort_by { |b| b[:priv_ratio] }.reverse.each do |b|
      puts "%4.2f %s (%d/%d)" % [b[:priv_ratio], b[:title], b[:public_cnt], b[:private_cnt]]
    end
  end
end

ARGV.each do |a|
  checker = PrivateBMChecker.new
  puts a
  checker.process(open(a))
  TextReporter.new.report(checker)
end

実行コマンド

ruby -Ku hatebu.rb 'http://b.hatena.ne.jp/hotentry?mode=daily&date=20070601' 'http://b.hatena.ne.jp/hotentry?mode=daily&date=20070602' 'http://b.hatena.ne.jp/hotentry?mode=daily&date=20070603' | tee log

日別の人気エントリー以外でも、はてなブックマークのURLを与えればだいたい動くと思う。

MP3Tunesで日本語変換トラブル

MP3tunes.comを使用していてトラブルに遭遇し、なんとか復旧できたので、その顛末をメモしておく。

サービス概略

MP3tunes.comは、mp3等の音楽ファイルを保存してくれるサービスで、驚いたことにフリーのアカウントでも容量無制限で保存してくれる。フリーでの制限は実質的には「一つのファイルの容量が10MB」ということのみ。

本当にそれ以外に制限無いのかが疑問で試してみたのだけど、私の場合は、2200曲、8.5Gは(後述する日本語関連の問題を除き)問題なく保存し、ダウンロードできている。一ファイルの容量制限は、時間にするとだいたい10分くらいが上限になるようだ。JAZZやクラシックでは、この制限で中抜けになっているものが目立つが、ロックやポップスでは、ほとんど問題ない。

サービス内容は、oboe sync という専用クライアントをダウンロードして、これを利用して、アップロードダウンロードすることが基本。それ以外に、WEBページ上のプレイヤーで聞けたり、モバイル用にビットレートを下げて配信してくれるサービスもある(ただこれはひどい音でちょっと聞く気にはならない)。

WEBページ上では、日本語のアーチスト名やアルバムは全部文字化けしてしまうが、曲の中身自体は保存されている模様で、何度かダウンロードのテストをした時には問題なく、一時保存用の「ロッカー」として使うなら問題ないと判断した。

トラブル概略

最初は機能を確認するだけのつもりで使いはじめたが、上記のように文字化け以外は問題なさそうなので、手元のマシンをクリーンインストールする時に、リッピングした音楽ファイルをここに保存することにした。

ところが、テストの時は問題なかったのに、クリーンインストールしてからoboe syncを立ち上げると、途中で内部エラーを表示して異常終了してしまった。(メッセージ等は記録してない)

全部、手持ちのCDからリッピングしたものなので、それをやり直せばよいのだが、さすがに2000曲やり直すのかと思うと、ちょっと落ちこんだ。

そこで、速攻でRubyスクリプトを書いて試行錯誤してみたら、mp3ファイルはなんとかダウンロードできた。

これをiTunesに登録してみたら、一部のid3タグが壊れているみたいで曲名が無いものや化けているものがあるが、8割以上はライブラリとして復旧できた。曲名が無いファイルも聞く分には何も問題ない。

それから、そのダウンロードしたmp3ファイルを調べてみて、問題についておよその目星はついたが、完全には現象、対策等を究明する所まではいってない。

MP3Tunesのサポートには「日本語で問題が起きて、自力復旧したからいいけど、情報が欲しかったらメールください」と連絡したけど、返事はなかった。

トラブル詳細

(網羅的な調査はしてないので、目安として考えてください)

  • リッピングしたソフトは、最初wmaで、途中からiTunes(バージョン等は覚えていない)
  • 壊れているのはアーチスト、アルバム名、曲名のどれかに日本語を含むもののみ
  • 壊れかたにはいろいろある
  • アルバム丸ごと全曲ダメになっている場合と、曲単位でおかしくなっている場合がある
  • iTunes7.1, wma9, winamp5.35 で調査した所、以下のパターンがある
  1. wma9では再生できず、他では文字化けするが再生できる
  2. wma9ではタグ情報(曲名等)が表示されないが再生は可能で、他では再生もタグ表示も正常
  3. どのプレイヤーでも再生は可能だが、タグ情報は一切表示されない

cygwin ruby 1.85 + http://ruby-mp3info.rubyforge.org/ で読ませてみた所、wma9で読めないファイルでは、下記のようなエラーが発生する。

/usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:209:in `iconv': invalid encoding ("
ISO-8859-1", "UNICODE") (Iconv::InvalidEncoding)
        from /usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:209:in `decode_tag'
        from /usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:280:in `add_value_to_t
ag2'
        from /usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:231:in `read_id3v2_3_f
rames'
        from /usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:220:in `loop'
        from /usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:220:in `read_id3v2_3_f
rames'
        from /usr/lib/ruby/site_ruby/1.8/mp3info/id3v2.rb:135:in `from_io'
        from /usr/lib/ruby/site_ruby/1.8/mp3info.rb:412:in `parse_tags'
        from /usr/lib/ruby/site_ruby/1.8/mp3info.rb:171:in `initialize'
        from /usr/lib/ruby/site_ruby/1.8/mp3info.rb:271:in `new'
        from /usr/lib/ruby/site_ruby/1.8/mp3info.rb:271:in `open'
        from check_mp3.rb:7
        from check_mp3.rb:4:in `glob'
        from check_mp3.rb:4

推測

アップロードする前には、たぶんどのプレイヤーでも全部のファイルを再生、表示できてたと思われる。チェックはしてないけど、特にトラブルは無かった。だから、MP3Tunesが、アップロードしたファイルを保存する時かそれを読みこむ時に、一部のidタグを(意図せずに)壊していることは間違いないと思う。

そして壊しているのは、日本語が含まれる文字列なので、日本語のコード変換に関連していることは推測できる。

たくさんある日本語の文字列の中の一部でしか問題が発生しないことや、壊したファイルを読めるソフトと読めないソフトがあることから考えると、このあたりに関係ありそう。

Windows 機種依存文字等の日本語関係の文字コード変換を Iconv モジュールで行うには、対策を施してある iconv ライブラリを用いる必要があります。具体的には以下のライブラリを使う事になるでしょう。

ただし、たとえば、YMOの「ジャム」というタイトルが表示されてないので、JIS X 0213で追加された文字を含まない曲でも問題が起こっているようで、そこはよくわからない。

各ファイルを徹底的に調査すれば、パターンや原因が見つかるかもしれないけど、そこまでやる時間はありません。

ダウンロードスクリプト

oboe sync がダウンしてしまうアカウントに対しても、下記のスクリプトで全曲ダウンロードはできた。

ブラウザから1曲をダウンロードしてみて、それを回線トレースして、繰り返し処理するスクリプトにしたもの。やっつけ仕事なので、中身はかなりいいかげん。

基本的には、Rubyがわかる方に限り自己責任でご利用ください。

require 'open-uri'
require 'net/http'

EMAIL="tnaka@dc4.so-net.ne.jp"
PASSWORD="*********"
Net::HTTP.version_1_2   
$CNT = 0
OUTDIR="mp3"

def get_path
  $CNT += 1
  path = "#{OUTDIR}/%04d.mp3" % $CNT
end

class MP3Tunes
  attr_reader :sessid
  def login(mail, pass)
    Net::HTTP.start('shop.mp3tunes.com', 80)  do |http|
      response = http.post('/loginform.php',
                           "frmEmail=#{mail}&frmPassword=#{pass}")
      p response
      case response
      when Net::HTTPSuccess 
      when Net::HTTPRedirection 
        if  response.header["set-cookie"].to_s =~ /PHPSESSID=([0-9a-f]*)/
          @sessid = $1
        end
      end
      p @sessid
    end
  end

  def get_albums
    ret = []
    open("http://www.mp3tunes.com/locker/cb/index_albums?loadflag=yes", 
         "Cookie"=> "PHPSESSID=#{@sessid}")  do |f|
      f.each_line do |l|
        #puts l
        if l =~ /FilterDelay\('(\d+)'\)/
          ret << $1
        end
      end
    end
    ret
  end

  def get_playlist(album)
    ret = []
    open("http://www.mp3tunes.com/locker/mp3tunes_playlist.m3u?albums=#{album}", 
         "Cookie"=> "PHPSESSID=#{@sessid}")  do |f|
      f.each_line do |l|
        next unless l =~ /^http/
        ret << l
      end
    end
    ret
  end
end

if ARGV[0] == "test"
  require 'test/unit'

  class TestMP3Tunes < Test::Unit::TestCase

    def setup
      @m = MP3Tunes.new
    end

    def test_login_fail
      @m.login(EMAIL, "xxx")
      assert_nil(@m.sessid)
    end

    def test_login
      @m.login(EMAIL, PASSWORD)
      assert_not_nil(@m.sessid)
    end

    def test_albums
      @m.login(EMAIL, PASSWORD)
      a = @m.get_albums
      assert_not_equal(0, a.size)
    end

    def test_playlist
      @m.login(EMAIL, PASSWORD)
      a = @m.get_albums
      p a
      assert_not_equal(0, a.size)
      l = @m.get_playlist(a[0])
      assert_not_equal(0, l.size)
    end
  end
  puts "end"
else
  m = MP3Tunes.new
  m.login(EMAIL, PASSWORD)
  m.get_albums.each_with_index do |album, n|
    next if n % 2 == 0
    puts "album #{album}"
    begin
      m.get_playlist(album).each do |song|
        path = get_path
        puts "song #{song}"
        next if FileTest::exists?(path)
        begin
          open(song,
               "Cookie"=> "PHPSESSID=#{m.sessid}")  do |f|
            File::open(path, "wb") do |of|
              of.write f.read
            end
          end
        rescue
          p $!
        end
      end
    rescue
      p $!
    end
  end
end

使用ソフトメモ

使用しているWindowsマシンを初期化することになりそうなので、使用しているソフトをメモ。

Firefox extentions

あと、coLinuxを使っているが、これを機に、vmplayerに乗り換える予定。

w-zero3 設定覚え書き

ちょっと前に、W-ZERO3 (es)を使いはじめたので、設定の覚書。

全般的感想

全くの主観的感想です。

  • デカイ(10年前の携帯のよう)
  • よく落ちる、よくフリーズする
  • 画面、キーボードは予想よりはよかった
  • 携帯というよりPDA
  • PDAとして好きなようにいじるには良い
  • いろいろ苦労するが苦労すればたいていのことはできる

w-zero3 + PocketPuTTY + w3m = 最強のモバイルインターネット

主な利用法は、移動中に軽くネットにアクセスすることだけど、PocketPuTTYw3mの組合せがなかなか良かった。

自由にできるUNIX系サーバが必要なので、誰にでもできるパターンではないけど、環境が揃う人、特にPuTTYを使っている人にはオススメです。

メリットは

  • カーソルキーだけで(片手持ちのままで)操作できる
  • 一番いいのはPHS接続の時でもサクサク動くこと(これが本当に気持ちいい)
  • URLの登録等の細かい設定は、PCから行なえる
  • フルキーボードを開けば、w3mの機能はほぼ全部使える
  • 非常時には、w3mを抜けてシェル上でメンテナンスも可能

デメリットは、サーバのアカウントが必要なことと、動くまでの設定が面倒なこと。その手順は以下の通り。

  1. w-zero3専用のアカウントを作成する
  2. WindowsマシンでPuTTY付属のputtykeygenでパスフレーズ無しで秘密鍵生成
  3. 生成した*.PPKファイルをw-zero3に転送
  4. puttykeygenの画面上に表示される公開鍵を、サーバの専用アカウントの。~/.ssh/authorized_keysにコピペ
  5. 専用アカウントの.bash_profileの最後に "w3m -B"を追加
  6. WindowsマシンのPuTTYから専用アカウントにログインして、w3mを起動。よく見るサイトをブックマークに登録しておく
  7. w3mのキーマップ変更(下記)
  8. w-zero3上で、PocketPuTTYを起動して、ユーザ名と秘密鍵ファイル(3で転送したもの)を設定し、その設定情報を保存
  9. ランチャーにPocketPuTTYを登録し、起動パラメータに "-load (設定情報名)"を追加

かなり面倒くさいけど、ここまで設定すると、ランチャーからダイレクトに w3m のブックマークが表示される。

あとは、w3mのキーマップを変更する。その手順は

  1. /usr/share/doc/w3m/examples/keymap.default (debianの場合) を ~/.w3m/keymapにコピー
  2. 下記を変更する
keymap  UP      PREV_PAGE
keymap  DOWN    NEXT_PAGE
keymap  RIGHT   NEXT_LINK
keymap  LEFT    BACK

↑↓でページ単位の移動、→で次のリンクへ、←で前の画面へという設定。

これで、カーソルキーと真ん中のボタンだけで、たいていのサイトを閲覧できる。

問題点は、見たいリンクを行き過ぎた時に、簡単にカーソルを戻す方法が無いことだけど、これは↑↓という操作で、ページの先頭にカーソルが戻るので、それで→を必要なだけ押せばOK。

フルキーボードを開いている時には、カーソルキーは使わずにhjklのvi的な操作で普通に操作できる。

これで、今話題のYahoo Pipes自分用のニュースフィードを作成してはてなrss経由で読んでいる。

はてなrssは、r.hatena.ne.jp/xxxx/mobile で、w3mからでも携帯用の画面を表示できる。

gmailも、w3mからgmail.comにアクセスすると、「JavaScriptをサポートしてないブラウザはこちらへどうぞ」という案内が表示されるので、そのURLを表示して、w3mのブックマークに登録しておく。

まだ試してないけど、F's Garage:W-ZERO3向けのRSSリーダーα版なんてものもある。

あとは、keymapでテンキーに機能を割り付けることができればもっといいけど、これは今の所うまくいってない。

スケジュール関連

スケジュールは、主としてGoogle Calendarに入力して、それをw-zero3と同期して使用している。

スケジューラはたくさんあるけど、OffisnailDateが、週単位、月単位のビューにも予定の内容が表示されていて良かった。それから、予定の入力も使いやすそう。
Google Calendarとの同期は、こちらで紹介されているGooSyncというサービスもあるけど、これは、無料アカウントだと一ヶ月分しか転送されないので、POSyncにした。

下の三つは、スケジュール、仕事、カレンダーをtoday画面(初期画面)に表示するもの。

システム関連、ユーティリティ

WILL - YTaskMgr
画面回転ボタンに割り付けて使用
JUcrab(メニューボタンユーティリティー)
ランチャー
Runner_es
JUcrabには常用するものを少数で、残りはこちらから起動
Battery Monitor
バッテリー残量表示
GSFinder+
定番ファイラー
EasyDial
簡単ダイアリングツール

Windows Mobileでは、×ボタンを押してもアプリは終了せずバックグラウンドで動いている。しかし、タスクバーが無くて、今、何が動いてどうなっているのか把握しずらい。

YTaskMgr は、シンプルで軽いタスクマネージャー兼ランチャー兼便利ツール集。この機械自体の弱点をうまく補っていると思う。

w-zero3は、ボタンがたくさんあって、これをどう割り付けるかによって、使い勝手がかなり違う。今も試行錯誤中だが、現状は以下の通り

  • 画面回転キー:YTaskMgr
  • ソフトキー1,2 :JUcrab
  • IEキー長押し: (初期画面の表示)
  • メールキー長押し: コンテキストメニューの表示

何かしたい時は、IEキー長押しで初期画面に戻し、ソフトキーからJUcrab経由でRunner_esを起動して、そこから使用するアプリを起動する。慣れれば結構直感的に操作できる。よく使うアプリやユーティリティ的なものは、JUcrabに直接登録しておく。

その他アプリ

2++
2chブラウザ
PocketLadio
インターネットラジオ
simpleHTTPntp
時刻同期
手書きメーラー
手書きのメモをそのままメール送信できるソフト
手書きメモソフト comono HandyMemo for W-ZERO3
手書きメモ
TOMBO Project
母艦連動、暗号化可能なテキストビューワー
KOTETU’s Hatena - MiniEdit
付箋紙のようにも使える小さいエディタ
NextTrain for PocketPC
時刻表表示ツール、データ作成は時刻表変換ツール
NoteM
ボイスレコーダー

w-zero3をアイディアメモとしても活用したいと思っているけど、今はいろいろ試している段階。手書きメーラーというソフトが、起動してメモを書き殴ってそのまま自分宛に送信できるので、なかなか良さそう。

PocketLadio(+TCPMP)は、ビットレートが48bps以下の局を選べば、PHSでも結構実用的に聞ける感じ。これと2++で電車内の暇つぶしには苦労しないと思う。

TOMBOは母艦にもWin32版を入れて、パスワード管理をこれで一元化することにした。