Pages

2011年12月25日日曜日

iTunesはXMLで出来ている (1) DTD読み込み編

iTunesのライブラリ管理は、xmlファイルで行われています。なんとか、加工してみたいと思い、XMLの勉強をし直してみた。
# この記事は、ちょっと(いつもの通り)中途半端です。申し訳ない。

いまさらXML

XMLと一言でいっても、中は結構複雑、その世界は4つの技術で構成されている。
  • 構造記述言語 XML
  • 変換言語 XSLT
  • リンク言語 XPointer
  • 問い合わせ言語 XPath
iTunes管理ファイルを加工する手続きを上記の4つの技術を使って説明すると以下のようになる。
XMLで記述されたファイルiTunes.xmlを、XSLTで目的のhtmlに変換する。
XSLは、必要な個所をXPATHを使って探し出しXPointerが取り出す。

iTunes Music Library.xmlの構造

ライブラリを管理しているファイルitunes Music Library.xmlは、私のMac OSXの場合、以下の場所にある。実際には、ライブラリを移動している人もいますから、検索したほうが良い。

~/Music/iTunes/iTunes Music Library.xml
# windowsではどこなのか知りません。:-p

各情報は、dictタグでくくられている。
各情報の要素は、Keyタグとその値の組み合わせで記載。
トラック情報の要素は、名前、
内容を、先頭のほうを覗いてみると
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Major Version</key><integer>1</integer>
  <key>Minor Version</key><integer>1</integer>
  <key>Date</key><date>2011-11-19T23:48:40Z</date>
  <key>Application Version</key><string>10.5.1</string>
  <key>Features</key><integer>5</integer>
  <key>Show Content Ratings</key><true/>
  <key>Music Folder</key><string>file://localhost/Users/baker/Music/iTunes/iTunes%20Media/</string>
  <key>Library Persistent ID</key><string>BB0A0B413A2028D8</string>
  <key>Tracks</key>
  <dict>
    <key>3708</key>
    <dict>
      <key>Track ID</key><integer>3708</integer>
      <key>Name</key><string>Time Difference</string>
      <key>Artist</key><string>Hiromi's Sonicbloom</string>
      <key>Composer</key><string>上原 ひろみ</string>
(以下略)
となっている。
DTD (Document type Definition 文書型宣言)として、http://www.apple.com/DTDs/PropertyList-1.0.dtd ファイルが指定されている。DTDは、このファイルが所属している文書の構造を定義しているファイル。構造とは、どんな要素から成り立っているか、特別に定義しているタグ(要素)はなにか、属性の書き方はどうなっているかとか、もろもろ。XMLは、共通する書き方があり、ほとんどのブラウザは、XMLとして正しい(Well formated)を判断してくれますが、文書の目的にそって正しく書いているかは、判断してくれません。当然です。そのルール(まあ、ローカルルールですな)を記載しているのが、DTDです。

最初のDOCTYPEコメント(文書型宣言)から、
"-" 企業のローカルな取り決めであり、Apple Computerが定めており、文書名は "DTD PLIST 1.0" (多分、Play Listなのかな)、Englishで書かれていますと言うことが判ります。

iTunes.xmlのDTDを取得してみると、以下のように単純です。
<!ENTITY % plistObject "(array | data | date | dict | real | integer | string | true | false )" >
<!ELEMENT plist %plistObject;>
<!ATTLIST plist version CDATA "1.0" >

<!-- Collections -->
<!ELEMENT array (%plistObject;)*>
<!ELEMENT dict (key, %plistObject;)*>
<!ELEMENT key (#PCDATA)>

<!--- Primitive types -->
<!ELEMENT string (#PCDATA)>
<!ELEMENT data (#PCDATA)>
<!ELEMENT date (#PCDATA)>

<!-- Numerical primitives -->
<!ELEMENT true EMPTY>
<!ELEMENT false EMPTY>
<!ELEMENT real (#PCDATA)>
<!ELEMENT integer (#PCDATA)>

  • 実体宣言(ENTITY)で、plistObjectを定義しています。
  • "%plistObject"は、"array, data , dict, real , integer, string, true, false の何れか”と言う文字に置き換えなさいと言うことでしょうか。%記号は、宣言名であることを示しています。
  • 次の行で、要素(ELEMENT) plistの配下の構成(ノード)は、%plistObject だ定義しています。
  • さらに、plistは属性(ATTLIST) として、version をもちます。versionは、任意の文字列(CDATA)であり、初期値は"1.0"なんだそうです。
  • array要素は、配下に %plistObjectをもちますが、何回も繰り返しもって良いそうです。
  • dist要素は、配下に、%plistObjectとkeyをもち、これも繰り返しできます。
  • key要素は、任意の文字列を配下に持ちます。

こんなところでしょうか。

DTDを下に、再度 iTunes Music Library.xmlを眺めていると、なんか構造が見えてきた気がしませんか。
# plist (play list) Version = 1.0
# dict (dictionary? , 辞書? この場合、play list自身の属性)
  # key と %plistObject 要素で、playlist自身の内容を記載。
  # key "TRACKS" の中に、dictの集団がある。
      # dict メディアの情報
      # key トラック番号 integer 1
      # key  名前 string xxxxx
      ...
      ...
      # dict メディアの情報
      # key トラック番号 integer 1
      # key  名前 string xxxxx
      ...
      (繰り返し)

なんか、かえって判んなくなってきたかな。
次回は、htmlファイルにパースしてみる予定。

2011年12月4日日曜日

[Processing] 折り紙を折り続ける

前回の続き。1回しか折らない折り紙は哀しいので、折り続けさせてみた。

問題は、折った紙を新たに操作するときに、その座標とか折る方向とかをどのように計算するか。
あらゆる状況を想定する描写関数を用意するとか、再帰処理するかと考えたが、座標変換を利用してみることにした。

利用した関数は、
      transpate(x, y) --- 現在の座標をベクトル(x, y)分だけ座標を移動させる。
                          つまり過去の座標(x, y)が、新たな(0,0)となる。
      rotate( r )     --- 同様に r ラジアンだけ、軸を回転させ、あらたな座標にする  

期待した動きは、折りおわったら、translate し rotate(90度) して、新たな折り紙を左から右に折ることだった。

しかし、ここで落とし穴が。draw()関数は、座標をリセットする。何で? まだ信じられないのだが。
しかたないので、描写の度に translate , rotate を折り返しの数 実施させた。そのため、折りすぎると描写が遅くなってしまった。

でき上がったのは、以下のアプレット。
折り続ける折り紙

以下、ソースコードの解説
int cnt = 0;   // 折り返し回数
float rad = 270; // 折る角度

void setup(){
  size(200, 200);
  color(RGB,256);
  background(255);
  frameRate(30);
}

void draw(){
  // 折り紙の左上に座標を移動
  translate(50,50);
  float rr = 40;  // 折り紙の横の半分 (折りの半径になる) 
  float hh = 80;  // 折り紙の高さ
  // 折り回数分だけ、座標と半径、高さを計算し直す
  for(int i=0;i<cnt;i++){
    translate(rr, hh);      // 次の折り紙の左上
    rotate(radians(-90));  // 折りの方向を90度回転
    // 新たな折り紙の高さと半径(横の半分)
    float temp = hh;
    hh = rr;
    rr = temp / 2;
  }
  
  background(255);
  stroke(0);
  page(rr, hh, 90);  // 折り紙の右半分
  page(rr, hh, rad); // 折り紙の左半分 radは折り角度

  // 折り曲げたら折り角度radを戻し、1回カウント
  if(--rad == 90){
    rad = 270;
    if(++cnt > 5)cnt = 0; // 5回折ったら元に戻す
  }
}

// 開いた状態の折り紙の左上を 座標( 0,0 ) として
// 折り紙の左半分を描く
void page(float r, float h, float rd){
  // 折りの軸の上座標は(r, 0)
  float x = r + r * sin(radians(rd)); // 左上位置 X座標
  float y = 0 + r * cos(radians(rd)); // 左上位置 Y座標
  line(x, y, r, 0);         // 上辺
  line(x, y, x, y + h);     // 左辺
  line(x, y + h, r, 0 + h); // 下辺
  line(r, 0 + h, r, 0);     // 右辺
}

どうも、ロジックの正解は、
* 再帰関数を行う
* draw関数を使わない
のではないかと思う。もう少し調べてみる必要があるな。

2011年11月27日日曜日

[Processing] 紙を折る

突然、折り紙のシミュレートをしてみたくなった。
久しぶりのProcessingで、習作をつくってみることにする。

エラーとバージョンアップ

簡単なコードを書いて実行してみると、動くことは動くのだが、以下のようなエラー(Warning)がでた。
2011-11-26 21:34:34.011 java[1413:1c0f] *** CPerformer: ignoring exception 'Invalid parameter not satisfying: [self canBecomeMainWindow]' raised during perform of selector 'requestFocus:' on target 'FocusManager' with args '<AWTComponentSetFocusData: 0x11d980>'

Java関連のエラーのようだが、調査しても今一。Javaのバージョンアップをしてみよ、とか Lionのせいじゃないかとか。いろいろ試して、結果 Processingを最新版にするとエラーがでなくなった。
今まで使っていたのが、1.0.1 最新は、1.5.1 なので、随分とヴァージョンアップせずにほったらかしだったのね。

折たたみ、あるいは本をめくるような動作

つくってみたのは、以下のリンクからアプレットを動かしてみてください。

紙を折るような動作

結構不満。
* ワイヤー表示なので、重なりが透けてみえる。
* 面と裏の色をつけたい。
* 永遠におり続ける動作にしたい。
* 円軌道が、平面的で立体感がない。

プログラムの解説

Processingのプログラムの基礎として
* 最初にsetup()関数が実行され、draw()関数がループ実行される。
* グローバル変数は、最初に宣言しておく。

円座標の計算は
円の中心座標 (x0, y0)
角度 th (Radian) 注) 0度は、Y軸方向。つまり、右向き矢印は、90度、上向きは 180度。
半径 R

座標 x = x0 + R * sin(th)
座標 y = y0 + R * cos(th)

float sx; float sy; // 紙の上辺、真ん中の位置。 
int sh;             // 折り紙の高さ。
int sr;             // 折り紙の上辺の半分。折る場合の半径。
float rad;          // 折り曲げ角度。単位:度。

// 初期化
void setup(){
  size(200, 200);   // 画面の大きさ
  color(RGB,256);   // 色設定 processingは、数種類の色定義を使うことが出来る。
  
  background(255);  // 背景色
  frameRate(30);    // 描写速度 draw()の実行回数 / 秒
  
  // 初期化
  sx = 100; sy = 50; sh = 80; sr = 40;  
  rad = 270; //円軌道は、左端から始まり、右回転へ。
}

void draw(){
  background(255);  // 毎回画面を真っ白にする。そうしないと前の絵が残る。
  // 折り曲げを繰り返し。
  if(rad == 90) rad = 270;
  
  stroke(0);        // 黒で線を描く
  page(90);         // 90度の左半分 → つまり、折り紙の右半分。
  page(rad);        // 角度radの状態の左半分。
  
  rad = rad - 1;    // 右回り。
}

// 折り曲げ角度 r の 折り紙の左半分をワイヤーで描く
void page(float r){
  // 左上の位置を計算
  float x = sx + sr * sin(radians(r));
  float y = sy + sr * cos(radians(r));
  // ワイヤー
  line(x, y, sx, sy);           // 上辺
  line(x, y, x , y + sh);       // 左辺
  line(x, y + sh, sx, sy + sh); // 下辺
  line(sx, sy + sh, sx, sy);    // 右辺
}

ワイヤーでやってみたが、どうも平行四辺形を描く方法があるらしい。また、三次元で描いて視点を動かすことで、立体的に表現することもできそうだ。

また、最近のProccessing本では、画像描写以外に機器制御への利用も解説しているらしい。

Processing。侮れない言語になってきたのだなあ。

2011年10月30日日曜日

統計計算言語 R を試す

統計処理を扱う言語としては、商用のS、S-Plusが有名だが、オープンソースのRも負けていない。先日も丸善の数学コーナに顔を出したが、本棚の二段分はR関連の本が並んでいます。非常に多機能だ。まだ、チュートリアルを試してみただけだが、その可能性に驚いています。

R ホームページ
http://www.R-project.org/

テキストからのデータの取込み、データ構造として、ベクトル、マトリックス、リスト、複数のデータ形式を扱うデータフレームなどのデータ構造を扱うことができる。各種のグラフの出力も可能。手続きはインタラクティブに操作できるほか、もちろん、バッチ処理可能。Windows, Linux , MacOSXのパッケージが存在しています。

普通の人がデータ処理を扱うソフトとしては、ExcelやAccessが有名だが、Excelは、ドキュメント作成ソフトに成り下がっており、Accessは、アプリケーション開発環境に自分の行く末を決めたようだ。大量の企業データを黙々と自動的に処理し、有意義な結果を導き出すツールとして、Rは非常に好都合なのだ。

さて、インストール

OSXへのインストールは簡単。以下のサイトからダウンロードして、インストール・パッケージを実行。もちろん、他のOSもある。
http://cran.md.tsukuba.ac.jp/
すでに、Lionでの検証も済んでいるようで、すばやいなあ。

実行は、一文字 r と打てばOK
baker$ r

R version 2.13.2 (2011-09-30)
Copyright (C) 2011 The R Foundation for Statistical Computing
ISBN 3-900051-07-0
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)
Rは、自由なソフトウェアであり、「完全に無保証」です。 
一定の条件に従えば、自由にこれを再配布することができます。 
配布条件の詳細に関しては、'license()'あるいは'licence()'と入力してください。 

Rは多くの貢献者による共同プロジェクトです。 
詳しくは'contributors()'と入力してください。 
また、RやRのパッケージを出版物で引用する際の形式については 
'citation()'と入力してください。 

'demo()'と入力すればデモをみることができます。 
'help()'とすればオンラインヘルプが出ます。 
'help.start()'でHTMLブラウザによるヘルプがみられます。 
'q()'と入力すればRを終了します。 

データファイルの取込から演算まで

総務省・統計局のダウンロードから、「平成17年の福岡県人口統計」を入手。
http://www.stat.go.jp/data/guide/download/index.htm

本当は時系列データのほうが、面白そうなのだけど、この手のデータはワークシート毎に年度でまとめられている。とりあえず、平成17年の市町村別人口統計を使ってみる。

Appleご用達のNumberで整形。あまり、使っていないので使い方が判らん。最終的に、一行目をヘッダー(項目名)として、二行目以降をデータ部とする典型的なデータファイルにした。

なお、一行目の項目名は、以下のようにした。(日本語でも問題ないのだが、コンソールで操作するので、日本語変換は面倒だったため)

  • No -- 市町村番号(らしい)
  • Name -- 市町村名
  • m.u15 , f.o15 -- 男性15歳未満、女性15歳未満
  • m15.65 , f15.65 -- 15歳以上65歳以内 それぞれ男性、女性
  • m.o65, f.o65 -- 65歳以上。それぞれ、男性、女性

# 変数 fukuoka に、csvファイル "fukuoka-h17-x.csv"を読み込む read.csv()
> fukuoka <- read.csv("fukuoka-h17-x.csv")

fukuokaは、マトリックス型で登録されています。マトリックス型は、行列です。

> fukuoka
      NO     Name m.u15 m15.64 m.o65 f.u15 f15.64 f.o65
1  40101   門司区  6618  31509 11515  6288  34673 18044
2  40103   若松区  6081  26608  8181  5876  28214 12357
3  40105   戸畑区  4044  21171  5714  3896  20130  8743
4  40106 小倉北区 10798  58329 15939 10461  63276 24305
5  40107 小倉南区 16805  68123 17238 15836  73155 23348
6  40108 八幡東区  4154  22722  8345  3959  23557 13077
(省略)

このあたり、ちょっと疑問。チュートリアルでは、ベクトルもマトリックスも、その要素は全て同じ型です。数字と文字を混ぜることができません。なので、それを可能にするためにデータフレームがあるのですが、上記のように変数fukuokaには、一見 文字と数字が混在しているようにみえます。

実は、この取込は一度失敗しています。取込ファイルの各要素が”double quotation”で囲まれていたため、全て文字として扱われてしまい、演算ができなくなりました。もしかすると、上記の状態はマトリックス型なのかもしれません。

要素の名称を調べてみます。

> dimnames(fukuoka)
[[1]]
 [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15"
[16] "16" "17" "18" "19" "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30"
[31] "31" "32" "33" "34" "35" "36" "37" "38" "39" "40" "41" "42" "43" "44" "45"
[46] "46" "47" "48" "49" "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" "60"
[61] "61" "62" "63" "64" "65" "66" "67" "68" "69" "70" "71" "72" "73" "74" "75"
[76] "76" "77" "78" "79" "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "90"
[91] "91" "92" "93" "94" "95" "96" "97"

[[2]]
[1] "NO"     "Name"   "m.u15"  "m15.64" "m.o65"  "f.u15"  "f15.64" "f.o65" 

[[1]]は、縦の要素をしめしています。特に指定していないため、登録順に番号が夫番されているようです。例えば、fukuoka[18,]と指定すると、18番目の行のデータすべてが表示されます。
[[2]]は、列要素の名前。csvファイルの一行目が指定されています。
名前要素は、例えば fukuoka$Name とすると、Name列の値一覧を表示しますし、Name列のみの行列(1列、x行の行列)として取り扱うことができます。
ただ、いちいち変数名を指定するのも面倒なので、attach()してやると、直接列名を使うことができます。つまり、fukuoka$f.u15 + fukuoka$m.u15は、 f.u15 + m.u15 と書けます。

以下は、attach()して、15歳以上65歳以下の比率を計算し、変数 rate.workersに格納しています。

> attach(fukuoka)
> workers <- m15.64 + f15.64
> total <- m.u15 + f.u15 + m.o65 + f.o65 + workers
> rate.workers<-workers/total

そうそう、Rの変数名は、.が使えますが、-+/*はつかえません。これらは、演算として処理されます。csvで取り込んだ時には、ご丁寧に、-は、.に置換されていました。(ヘッダー行のみ)

グラフをプロットする

簡単にできます。OSXでは、X11アプリケーションが起動し、表示します。

> hist(rate.workers , main="Histgram Rate of workers")



バッチ処理

処理を記述したファイルを実行することができる。他の言語などと組み合わせて利用すると、とても面白いことができそうだ。

以下のスクリプトは、パラメータを受け取って、上記の計算を実行した後、グラフはpdfに出力するようになっている。注意しなければならないのは、引数の扱い。引数は、ベクトルargsにセットするが、その添え字は、プログラム本体を1番目して順に格納されることだ。なんか、--argsオプションが意味ないような気がするが、このオプションのお陰で、それ以降の値は、何らかの意味を持たない(例えばオプションではない)値としてプログラムには無視されるのだろう。また、引数は文字列型なので、数字で扱いたい場合は、変換する必要がある。

# file name test.R
# パラメータを受け取る
args <- commandArgs()
# なぜ五番目なのかは、あとのコマンドをみてほしい
file <- args[5]
# pdfファイルを用意する。この時点でグラフの出力先は変更される。
pdf(file)

fukuoka <- read.csv("fukuoka-h17-x.csv")
attach(fukuoka)
workers <-m15.64 + f15.64
total <- m.u15 + f.u15 + m.o65 + f.o65 + workers
rate.workers<-workers/total
hist(rate.workers , main="Histgram Rate of workers")

# たぶん、出力デバイスの終了 pdfファイルのクローズか。
dev.off()

以下のように実行します。vanillaオプションはバッチモードを指定し、quietオプションは標準出力の制御、argsオプション以降にパラメータを書きます。Rは、基本インタラクティブなプログラムらしく、標準入力にスクリプトを食わせます。

> R --vanilla --quiet --args test1.pdf < test.R 

Rは、歴史もあり、機能も豊富で、多くのノウハウも公開されています。これからの勉強や仕事への応用がとても楽しいことになりそうです。

2011年10月22日土曜日

MacPortsを思い出す

このところ、localで作業せずリモート・ログイン & vim での開発と言う横着ぶり。
久しぶりにMacBookで、チョコチョコしようとしたら、いろいろと足りないものがあるようだ。改めて環境を整備することにして、まずはパッケージの更新とかやることにした。
(このあたりから、既に脇道にそれている。いつものことだが。まあ、今日は休みだし)

さて、mac portsを各種パッケージの利用のために使っている。各コマンドも忘れているので、主なコマンドを整理。
    sudo port selfupdate
    postコマンド自身とパッケージ情報を最新にする
    port search 検索文字
    パッケージ情報の検索
    sudo port install パッケージ
    指定したパッケージのインストール
    sudo port uninstall パッケージ
    パッケージのアンインストール
    sudo port valiants パッケージ
    パッケージ・インストール時に利用出来るオプションを表示
    sudo port install パッケージ +オプション +オプション ....
    オプション付でインストール
    sudo port deactivate パッケージ
    インストール済みのパッケージを利用不可にする
    sudo port activate パッケージ
    インストール済みの利用不可パッケージを利用可能にする
    port outdated
    古いパッケージを表示
    sudo port upgrade installed
    古いパッケージ全てをアップグレード
    sudo port upgrade パッケージ
    パッケージを指定してアップグレード

MacPortsはrpm,yumと異なり、ソースをダウンロードし、それをローカルでコンパイルする方式のパッケージ管理システムだ。そのため、XCodeも必要。
MacPortsは以前にインストールしているので、さっそく、アップグレード。
$ sudo port selfupdate
$ sudo port upgrade installed

注) selfupdateは、結局2回やりました。一回目はパッケージツリーの更新、二回目がport本体? 2.0.3 に更新された。

アップグレードの途中、perlのアップグレードでエラーが出て停止した。
--->  Activating perl5 @5.12.3_1+perl5_12
Error: Target org.macports.activate returned: Image error: /opt/local/bin/a2p is being used by the active perl5.8 port.  Please deactivate this port first, or use 'port -f activate perl5' to force the activation.
Error: Failed to install perl5
Log for perl5 is at: /opt/local/var/macports/logs/_opt_local_var_macports_sources_rsync.macports.org_release_ports_lang_perl5/perl5/main.log
Error: The following dependencies were not installed: perl5
Error: Problem while installing autoconf
To report a bug, see 

「このPort(perl5.8)を使用不可にするか、perl5を(強制的に)使用可にしろ」と言っております。現在の状況を調査。
$ port installed | grep perl
  perl5 @5.8.9_0
  perl5 @5.12.3_1+perl5_12
  perl5.8 @5.8.9_3 (active)
  perl5.12 @5.12.3_2 (active)
4つのバージョンが入っていて、2つが活性化しているのか? perl5.8.9_3を非活性化しました。

$ sudo port deactivate perl5.8 @5.8.9_3
$ sudo port upgrade outdated
これで、無事アップグレード終了。Perlの状態は

$ port installed | grep perl
  perl5 @5.8.9_0
  perl5 @5.12.3_1+perl5_12 (active)
  perl5.8 @5.8.9_3
  perl5.12 @5.12.3_2 (active)

まあ、いっか。perlは私の主力ではないので、この状態が問題ないのか不明。

MacPortsとは

http://macwiki.sourceforge.jp/wiki/index.php/MacPorts
MacPorts は MacOSX 上で利用可能なパッケージシステムの一つで、 主に UNIX 向けに開発されたオープンソース・ソフトウェアを手軽にインストールすることができます。 OpenDarwin プロジェクトの一部として 2002 年より始められ、 Landon Fuller, Kevin Van Vechten, Jordan Hubbard といった Apple inc. の従業員も多数参加しています。
以前は DarwinPorts と呼ばれていましたが、OpenDarwin プロジェクトの休止に伴い、MacPorts と改称(2006 年 8 月~)されました。 そのため、一部の配布物にはまだ DarwinPorts という表記が残っていますが、これらは全く同じものを指します。
2005 年 4 月 28 日にバージョン 1.0 がリリースされ、その当時でおよそ 2500 本のソフトウェア (ports) が導入可能でした。 最新版は 2010 年 11 月にリリースされたバージョン 1.9.2 で、7400 以上の ports が利用可能となっています。
MacPorts を使って新しくソフトウェアをインストールするには、ターミナル上で「sudo port install パッケージ名」などと入力します。 これによりアーカイブのダウンロード、展開、パッチの適用、コンフィグ、ビルドといった各段階を経て、 指定したソフトウェアのインストールが行われます。 このとき、他のパッケージに対する依存関係があれば、自動的にそれらの必要となるパッケージもインストールされます。

2011年10月10日月曜日

Ruby1.8.7とRuby1.9.2のWebApplicationをApacheで動かす

私のサクラVPSでは、いくつかのRailsアプリケーションが動いているが、それはRuby1.8.7ベースだ。
WebサーバにはApache(+ passenger module)を使い、VirtualHostで複数のWebアプリケーションを切り分けている。
さて、以前 開発用ユーザで Ruby1.9.2を試すためにrvmを導入した。
http://tech-baker.blogspot.com/2011/09/rvm-ruby-192-rails3.html
Apacheの実行環境では、デフォルトのruby1.8.7が動作してしまう。かといって、rvmをapache環境に導入したら、ruby1.8.7ベースのアプリケーションが動かない。ruby1.8.7もruby1.9.2もapache環境で動かしたい。以下のPassengerサイトを参考にチャレンジした。
http://blog.phusion.nl/2010/09/21/phusion-passenger-running-multiple-ruby-versions/

目指した環境は以下の図の通り。Apacheは、リバースプロキシとして利用され、ruby1.9.2のアプリケーションは、Passengerスタンドアローンで動かす。

図:Reverse Proxyを利用した複数Versionのruby Web app
Apache + passenger ----- app1.sample.com (ruby 1.8.7 + rails2.8.x)
Apache + passenger ----- app2.sample.com (ruby 1.8.7 + rails2.8.x)
Apache + mod_proxy ----- passenger ----- app3.sample.com ( ruby 1.9.2 + rails3 by rvm)


rails3アプリケーションは、開発ユーザdevmanのディレクトリ /home/devman/public_html/app3 に作成した。
rvm環境が出来ていることを前提で、作業は以下の手順
  1. passenger(スタンドアローン)のインストール
  2. apacheのプロキシー設定
  3. 起動スクリプト作成
  4. (必要であれば)iptablesをいじる

passengerのインストールはgemから行う。開発ユーザ環境も示しておいた。
devman$ rvm info
ruby-1.9.2-p290:

  system:
    uname:       "Linux www.miyabiit.com 2.6.18-274.3.1.el5 #1 SMP Tue Sep 6 20:13:52 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux"
    bash:        "/bin/bash => GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)"
    zsh:         " => not installed"

  rvm:
    version:      "rvm 1.8.3 by Wayne E. Seguin (wayneeseguin@gmail.com) [https://rvm.beginrescueend.com/]"

  ruby:
    interpreter:  "ruby"
    version:      "1.9.2p290"
    date:         "2011-07-09"
    platform:     "x86_64-linux"
    patchlevel:   "2011-07-09 revision 32553"
    full_version: "ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-linux]"

  homes:
    gem:          "/home/devman/.rvm/gems/ruby-1.9.2-p290"
    ruby:         "/home/devman/.rvm/rubies/ruby-1.9.2-p290"

  binaries:
    ruby:         "/home/devman/.rvm/rubies/ruby-1.9.2-p290/bin/ruby"
    irb:          "/home/devman/.rvm/rubies/ruby-1.9.2-p290/bin/irb"
    gem:          "/home/devman/.rvm/rubies/ruby-1.9.2-p290/bin/gem"
    rake:         "/home/devman/.rvm/gems/ruby-1.9.2-p290/bin/rake"

  environment:
    PATH:         "/home/devman/.rvm/gems/ruby-1.9.2-p290/bin:/home/devman/.rvm/gems/ruby-1.9.2-p290@global/bin:/home/devman/.rvm/rubies/ruby-1.9.2-p290/bin:/home/devman/.rvm/bin:/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/lib/jvm/java/bin:/usr/local/ant/bin:/home/devman/bin"
    GEM_HOME:     "/home/devman/.rvm/gems/ruby-1.9.2-p290"
    GEM_PATH:     "/home/devman/.rvm/gems/ruby-1.9.2-p290:/home/devman/.rvm/gems/ruby-1.9.2-p290@global"
    MY_RUBY_HOME: "/home/devman/.rvm/rubies/ruby-1.9.2-p290"
    IRBRC:        "/home/devman/.rvm/rubies/ruby-1.9.2-p290/.irbrc"
    RUBYOPT:      ""
    gemset:       ""

devman$ gem install passenger --pre  # rvm 環境下なので userディレクトリにインストール

あれ、nginxもインストールされるのか。nginx(エンジンエックス、高速軽量で人気のWebサーバ)がスタンドアローンのwebサーバの役割を果たし、結局passenger本体はあくまでモジュールなのね。

ApacheにVirtualhostの設定を、reverse proxy仕様で追加する。
<VirtualHost *:80>
  ServerName app3.sample.com
  DocumentRoot /home/devman/public_html/words/public
  PassengerEnabled off
  ProxyPass / http://127.0.0.1:3000/
  ProxyPassReverse / http://127.0.0.1:3000/

</p>

起動スクリプトは、私は /etc/rc.d/app3をつくり、/etc/rc.localに追加した。これで良かったのかな。停止とかはKillしよう。chmod +x /etc/rc.d/app3を忘れないこと。

起動スクリプト:/etc/rc.d/app3
#!/bin/sh
su - devman -c 'cd public_html/app3;passenger start -a 127.0.0.1 -e development -p 3000 -d'
開発環境で実行している。
これで、Apacheを再起動し、rails3のアプリケーションを起動すればよい。
devman$ sudo /etc/init.d/httpd restart
devman$ cd public_html/app3
devman$ passenger start -a 127.0.0.1 -e development -p 3000 -d

リバースプロキシーは、高速化、キャッシュ・サーバ、負荷分散など応用が広いが、こういった使い方も出来るのですね。

2011年10月9日日曜日

Rubyの御気楽構造体クラス OpenStruct

OpenStructは、御気楽な構造体クラスで、予め定義しておかなくても、自動で属性を作成してくれます。

ずいぶん前に一度知ったのだが、クラスの名前をどうしても思い出せず検索できずに、利用したくても出来ない、とても歯がゆい思いをしていた。とっても便利なクラスである。

CodeRider:openstruct baker$ irb
>> require 'ostruct'
=> true
>> man = OpenStruct.new
=> #
>> man.name = 'masahiro'
=> "masahiro"
>> man.age = 200
=> 200
>> man.childs = %w(hanako taro)
=> ["hanako", "taro"]
>> p man
#

OpenStructをアプリケーションの初期設定に利用すると便利だ。
railsの場合、RAILS_ROOT/config/initializers/app_config.rbに以下のようなコードを書いておくと起動時に初期設定してくれる。

require 'ostruct'
AppConfig = OpenStruct.new

AppConfig.default_email = "no-reply@hoge.com"
AppConfig.api_url = "hoge.com"

ネタはここから
http://rubyquicktips.com/post/1718141794/use-openstruct-for-application-configuration-variables

2011年10月1日土曜日

PHP高速化のため APCを導入してみた

ブログエンジンとして非常に有名なWordPressを導入してみた。
さすがにインストールそのものは、とても簡単だったのだが、その時にサーバのメモリ不足問題が発生し、その対策として PHP-APC を導入したので、そのメモ。

php-apc

Alternative PHP Cashe
http://php.net/manual/ja/book.apc.php
phpの実行コードを高速化するためのPHPモジュールで、中間コードの最適化やキャッシュを提供する。

WordPressをインストールするまでの手順

# root になって、作業を進めました 
$ yum list installed | grep php
php.x86_64                               5.3.8-1.el5.remi              installed
php-cli.x86_64                           5.3.8-1.el5.remi              installed
php-common.x86_64                        5.3.8-1.el5.remi              installed
php-gd.x86_64                            5.3.8-1.el5.remi              installed
php-mbstring.x86_64                      5.3.8-1.el5.remi              installed
php-mysql.x86_64                         5.3.8-1.el5.remi              installed
php-pdo.x86_64                           5.3.8-1.el5.remi              installed
php-pgsql.x86_64                         5.3.8-1.el5.remi              installed

$ mysql -u root -p
Enter password: 
mysql> create database wordpress;
mysql> grant all on wordpress.* to wordpress@localhost identified  by 'xxxxxxx';

$ mkdir work
$ cd work/
$ wget http://ja.wordpress.org/wordpress-3.2.1-ja.zip
$ unzip wordpress-3.2.1-ja.zip 
$ cd /var/www/html/
$ cp -r ~/work/wordpress sample.com
$ chown -R apache:apache sample.com
$ cd sample.com
あとは、サイトにアクセスして画面の指示通りに進めるだけ。
この後、導入しているネットワーク監視システム Xymonから、メモリ不足の警告メールが。

予め、予想されていたので、本当はApacheから nginx (エンジンエックス)にWebサーバを変更することも考えていたのですが、先ずは APCを試してみることにしました。
注) 私は、PHP, mysql を remi リポジトリからインストールしているので、下の例のように apcも、remi指定のインストールです。
$ yum install php-pecl-apc --enablerepo=remi
$ /etc/init.d/httpd restart

導入効果は中々のもの。その間のメモリ状況のグラフを以下に表示します。

* 9/30 0時頃にメモリ不足
* その後、すぐにAPCを導入
* 9/30 早朝に画面を撮る



2011年9月24日土曜日

rvm で ruby 1.9.2 + Rails3 を実現

そろそろRails3をやらなければ、と思っているうちに Version 3.1になっていた。
Rails3を渋っていた理由は様々あるが、大きいのは ruby 1.9。ruby 1.9は後方互換が結構厳しい。
Rails自身はRubygemsでしっかり管理されていて他のバージョンとの共存が可能だが、rubyは難しいと躊躇していたら、なんと rvmなるものがあるではないか。
rvm -- ruby version manager. 様々なrubyを切り替えて利用することができる
詳しくはここ。
http://beginrescueend.com/rvm/

gemでインストールする方法もあるが、今回狙っている環境は
* さくらVPS
* 特定の開発ユーザだけが、ruby 1.9を使う。通常は 1.8.7
* gem も同様
* virtual hostを利用して、1.9ベースのWebApplicationと1.8.7(rails2)を共存させる
よって、rvmサイトの指示(single user)通り、ソースからインストールすることにした。
http://beginrescueend.com/rvm/install/

rvm インストール

開発用ユーザを追加し、切り替わって
# ソースからインストール (curlが必要)
$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
# bash_profileに環境を追加
$ echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
$ source ~/.bashrc
# インストール出来たかテスト "rvm is a function"と表示されれば、OK 
$ type rvm | head -1
rvm is a function
それにしても、スクリプトの作成が素晴らしい。これぐらい、さらさら書きたいものだ。
ruby 1.9.2 をインストールしてみる。
$ rvm install 1.9.2
$ ruby -v
ruby 1.8.7 (2010-08-16 patchlevel 302) [x86_64-linux]
$ rvm use 1.9.2  # 切り替える
Using /home/bokunenjin/.rvm/gems/ruby-1.9.2-p290
$ ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-linux]
なるほど。ruby1.9 以外に、yaml rubygems もインストールさせるのか。
rvm use xxxx で他のrubyに切り替え。OSに導入している1.8.7が初期なので、ログアウトすると元に戻る。いつも1.9.2を使いたい場合は $ rvm use 1.9.2 --default でOK。


Rail3を試す

ruby 1.9.2 環境が用意できたので、早速 Rails3をインストール。
gemコマンドの実行は、カレントユーザで行う。(sudoしない。)

 $ gem update
 $ gem install rails
 $ rais new NewApp   # このあたりから、もう違う :-p
 Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem
is installed.
 $ cd NewApp
 $ rails server
プロジェクト作成時点でも、必要なgemが bundleの形で追加インストールされるのか。

sqlite3_native.so: undefined symbol エラー

sqlite3のプロジェクトを作成したときに、以下のようなエラーが出た。
sqlite3_native.so: undefined symbol: sqlite3_initialize
調べてみると、LD_LIBRARY_PATHの問題だととのことで、以下を.bashrcファイル等に追加。
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH

Rails3 (3.1.0) 気になること

* JavaScript runtimeにNode.js(V8)が使われている。
* 今回は ActiveResorceを使いこなしてみたい
* すこし、軽くなったような気がする
* MiddlewareのRackについて、詳細を知りたい
* 謎のファイル config.ru , Gemfile (Gemfile.lock)

Casein3のインストール

私は、最近のRails開発では、caseinばかり使っている。caseinは、コンパクトな CMSで、認証機能や管理者画面と、ちょっと小粋なscaffoldを持っているため開発の土台として、とても重宝している。

Rails3の場合は、専用のgemがあるので試したみた。
手順だけ、さっと書く。詳細は https://github.com/spoiledmilk/casein3をみてください。

 $ cd NewApp/
 $ vim Gemfile    # gem "casein" を追加
 $ bundle install  # caseinが必要とするgemはすべて、ここで入る。sudoしない。rvm配下なので。
 $ vim config/initializers/setup_mail.rb    # ユーザ管理用のメールを送る設定
 $ rails g casein:install       # g = generate ?
 $ rails g casein:update     # public/casein内に必要なcss, javascriptなどが配置される
 $ rake db:migrate             # ユーザ管理テーブル作成
 $ rake casein:users:create_admin email=xxxxxxxxx  # 管理者ユーザ登録
 $ rails server -d
見た目は、あんまり変わらないなあ。

2011年9月18日日曜日

CodeIgniter + Dx Auth(認証)+ PostgreSQL

CodeIgniterを使うことになった。
いろいろ、理由があって、データベースはPostgreSQL。
認証ライブラリを試してみる。いろいろなサイトで見た感じでは、Dx_Authが良いらしいので、以下の環境で構築した。

* CentOS 5.5 (final)
* PHP 5.3.8
* CodeIgniter 2.0.3
* Dx_Auth
* PostgreSQL 8.1

実際の作業は試行錯誤の連続だったのだが、記録なのでサクッと書いておく。

Postgresqlをインストール

PHPはremiリポジトリを使って最新バージョン5.3.8をインストールしたため、ドライバも対応したものをremiを使ってインストール。
# yum install postgresql-server.x86_64 postgresql-contrib.x86_64 postgresql-devel.x86_64
# yum install php-pgsql.x86_64 --enablerepo=remi
# /etc/init.d/httpd restart
# テスト用のデータベースも作成
[root@] su - postgres
-bash-3.2$ createdb -E UNICODE -O testman -U postgres cidb

CodeIgniter 2.x

CodeIgniterは、codeigniter 2.0.3 と 日本語パッケージ を導入した。
基本的にはダウンロードして、解凍して、ドキュメントルートに持ってきて、設定を少しいじるだけ。簡単なので、以下を参考してください。
http://codeigniter.jp/

Dx Authのインストール

ここを参考に
http://dexcell.shinsengumiteam.com/dx_auth/

ソースは、CodeIgniter 1.7 で検証されているそうだが、CodeIgniterは、2.xになっていろいろと変わった。修正の方法は、以下のサイトを参考にさせていただいた。
http://d.hatena.ne.jp/ozawa34/20091223/1261581994
日本語化やCI 2.x系の修正済みソースを提供されている方もいらしたので参考に
http://d.hatena.ne.jp/ozawa34/20091223/1261581994

PostgreSQL用にスキーマを変更

MySQLで試すと、あっさりと動作したので、いよいよPostgreSQLへ。
application/config/database.php のドライバ設定部分を"postgre"に変更。
//$db['default']['dbdriver'] = 'mysql';
$db['default']['dbdriver'] = 'postgre';

schema.sqlがDxAuthについているが、もちろんPostgreSQLでは、使えません。
フォーラムの以下のスレッドを参考に、PostgreSQL用のスキーマ作成SQL文をつくってみました。
http://codeigniter.com/forums/viewthread/186278/

-- --------------------------------------------------------

--
-- Table structure for table ci_sessions
--
 
CREATE TABLE ci_sessions (
  session_id varchar(40)  NOT NULL DEFAULT '0',
  ip_address varchar(16)  NOT NULL DEFAULT '0',
  user_agent varchar(150)  NOT NULL,
  last_activity integer NOT NULL DEFAULT '0',
  user_data text,
  PRIMARY KEY (session_id)
);
 
-- --------------------------------------------------------
 
--
-- Table structure for table login_attempts
--
 
CREATE TABLE login_attempts (
  id serial,
  ip_address varchar(40)  NOT NULL,
  time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  primary key(id)
);

-- --------------------------------------------------------

--
-- roles
--

create table roles(
	id serial,
	parent_id integer not null default '0',
	name varchar(30) not null,
	primary key(id)
);

INSERT INTO roles (parent_id, name) VALUES(0, 'User');
INSERT INTO roles (parent_id, name) VALUES(0, 'Admin');

-- --------------------------------------------------------

-- 
-- persissions
--

create table permissions(
	id	serial,
	role_id integer not null,
	data text,
	primary key(id)
);

-- --------------------------------------------------------
 
--
-- Table structure for table user_autologin
--
 
CREATE TABLE user_autologin (
  key_id char(32)  NOT NULL,
  user_id integer NOT NULL DEFAULT '0',
  user_agent varchar(150)  NOT NULL,
  last_ip varchar(40)  NOT NULL,
  last_login timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (key_id,user_id)
);
 
-- --------------------------------------------------------
 
--
-- Table structure for table user_profiles
--
 
CREATE TABLE user_profile (
  id serial,
  user_id integer NOT NULL,
  country varchar(20)  DEFAULT NULL,
  website varchar(255)  DEFAULT NULL,
  primary key(id)
);
 
-- --------------------------------------------------------
 
--
-- Table structure for table users
--
 
CREATE TABLE users (
  id serial,
  role_id integer not null default '1',
  username varchar(25)  NOT NULL,
  password varchar(34)  NOT NULL,
  email varchar(100)  NOT NULL,
  activated smallint NOT NULL DEFAULT '1',
  banned smallint NOT NULL DEFAULT '0',
  ban_reason varchar(255)  DEFAULT NULL,
  newpass varchar(34) default null,
  newpass_key varchar(32) default null,
  newpass_time timestamp DEFAULT CURRENT_TIMESTAMP,
  last_ip varchar(40)  NOT NULL,
  last_login timestamp DEFAULT CURRENT_TIMESTAMP,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  primary key(id)
);

-- --------------------------------------------------------

-- 
-- user_temp
--

create table user_temp (
	id serial,
	username varchar(255) not null,
	password varchar(34) not null,
	email varchar(100) not null,
	activation_key varchar(50) not null,
	last_ip varchar(40)  NOT NULL,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  primary key(id)
);

-- --------------------------------------------------------
 
--
-- Update datetime columns on update
--
 
CREATE OR REPLACE FUNCTION update_modified_column_time()
    RETURNS TRIGGER AS $$
    BEGIN
       NEW.time = now(); 
       RETURN NEW;
    END;
    $$ LANGUAGE 'plpgsql';
 
CREATE TRIGGER update_login_attempts_time BEFORE UPDATE
    ON login_attempts FOR EACH ROW EXECUTE PROCEDURE 
    update_modified_column_time();
 
CREATE OR REPLACE FUNCTION update_modified_column_user_autologin()
    RETURNS TRIGGER AS $$
    BEGIN
       NEW.last_login = now(); 
       RETURN NEW;
    END;
    $$ LANGUAGE 'plpgsql';
 
CREATE TRIGGER update_login_attempts_user_autologin BEFORE UPDATE
    ON user_autologin FOR EACH ROW EXECUTE PROCEDURE 
    update_modified_column_user_autologin();
 
 
CREATE OR REPLACE FUNCTION update_modified_column_users()
    RETURNS TRIGGER AS $$
    BEGIN
       NEW.modified = now(); 
       RETURN NEW;
    END;
    $$ LANGUAGE 'plpgsql';
 
CREATE TRIGGER update_login_attempts_users BEFORE UPDATE
    ON users FOR EACH ROW EXECUTE PROCEDURE 
    update_modified_column_users();

-- -----------------------------------------------------------------
-- no params
CREATE OR REPLACE FUNCTION unix_timestamp() RETURNS BIGINT AS '
	SELECT EXTRACT(EPOCH FROM CURRENT_TIMESTAMP(0))::bigint AS result;
' LANGUAGE 'SQL';
 
-- timestamp without time zone (i.e. 1973-11-29 21:33:09)
CREATE OR REPLACE FUNCTION unix_timestamp(TIMESTAMP) RETURNS BIGINT AS '
	SELECT EXTRACT(EPOCH FROM $1)::bigint AS result;
' LANGUAGE 'SQL';
 
-- timestamp with time zone (i.e. 1973-11-29 21:33:09+01)
CREATE OR REPLACE FUNCTION unix_timestamp(TIMESTAMP WITH TIME zone) RETURNS BIGINT AS '
	SELECT EXTRACT(EPOCH FROM $1)::bigint AS result;
' LANGUAGE 'SQL';


その他

最初にMySQLで試したのだが、その時の記録

MySQL。使わない、使えないユーザは消した。そしてDxAuthに必要なテーブルの一覧。
mysql> delete from user where user = '';
mysql> grant select,insert,update,delete,create,drop on cidb.* to miyabiman;
Query OK, 0 rows affected (0.06 sec)

[baker@www dx_auth]
$ mysql -u miyabiman -h localhost cidb -p  < schema.sql

mysql> show tables;
+----------------+
| Tables_in_cidb |
+----------------+
| ci_sessions    |
| login_attempts |
| permissions    |
| roles          |
| user_autologin |
| user_profile   |
| user_temp      |
| users          |
+----------------+
8 rows in set (0.00 sec)

作業の過程でつまずいたことなど。メモとして。
  • ログが書き込みされない
  • Session key 作成
  • PL/PGSQLが必要 # create language plpgsql;
  • テーブルが足りない
  • Primary Keyを用意する

2011年9月4日日曜日

Titaniumでバーコードライブラリを使う

検討中のiPhoneアプリの現在の課題はバーコード。(少しづつ、少しづつ進んでます)
Tituniumで利用できるiPhone用のバーコードライブラリは、TiBarが一番手のようだ。
TiBar -- ZBar integration module for Titanium Mobile.
と言うことで、ZBarライブラリをTitaniumで利用出来るようにしたものらしい。 http://code.google.com/p/tibar/

ZBarライブラリについては、ここ http://zbar.sourceforge.net/
ZBar is an open source software suite for reading bar codes from various sources, such as video streams, image files and raw intensity sensors. It supports many popular symbologies (types of bar codes) including EAN-13/UPC-A, UPC-E, EAN-8, Code 128, Code 39, Interleaved 2 of 5 and QR Code.

インストール

1. 最新版をダウンロード (2011.9.4 現在 0.4.2)
2. 解凍したモジュールを以下にコピー。
$ cp -r modules/iphone/tibar /Library/Application\ Support/Titanium/modules/iphone/
3. (プロジェクトに未登録のフレームワークを) Xcodeのプロジェクトファイルに追加する
  • AVFoundation.framework 
  • CoreMedia.framework
  • CoreVideo.framework
  • QuartzCore.framework
  • libiconv.dylib 
さらっと、書いたけど、これは一寸面倒。 
  1. /Library/Application Support/Titanium/mobilesdk/osx/1.7.2/iphone/iphone/Titanium.xcodeproj ファイルを起動し(Xcodeでファイルが開く)
  2. [Bundle Phases]タブを選び
  3. 右下にある [Link Binary with Libraries]ボタンにてライブラリリストを展開
  4. 上記のフレームワークを追加(Link)していくのだ。(選んで、追加ボタンを実施)

参考にさせてもらったのは、このサイト。 http://zaru.tofu-kun.org/2011/08/11/iphoneでバーコードをスキャンしたい!titanium-mobileでzbarを使う/

どうやら、このファイルをコピーして、初期プロジェクトファイルを作成していくらしい。
今回のコードは、既存のプロジェクトに追加したため、最初はmodule tibarの読み込みに失敗した。
再度、きれいにBuildする必要があった。
http://code.google.com/p/tibar/issues/detail?id=10

Tibarライブラリを使う

tiapp.xml プロジェクト定義ファイルに、モジュール追加記述
<modules>
 <module version="0.4.2">tibar</module>
</modules>
そして、requireする。 var TiBar = require('tibar');
var TiBar = require('tibar');  // TiBarモジュールの読み込み

// Window, Label, Button を用意
var win = Ti.UI.currentWindow;
var label = Ti.UI.createLabel({
	color:'#999',
	text:'this is scan windows.',
	font:{fontSize:20,fontFamily:'Helvetica Neue'},
	textAlign:'center',
	top:10,
	height:30
});
win.add(label);
var scanButton = Ti.UI.createButton({
	title:'Scan It',
	top:80,
	left:10,
	width:300,
	height:100
});
win.add(scanButton);

// clickイベントの中にスキャン動作を定義
scanButton.addEventListener('click', function(){
	TiBar.scan({
		// 設定パラメータ (JSON形式)
		configure: {
			// ZBarReaderViewController(VC), ZBarReaderController ( C )  の2種
			classType: "ZBarReaderViewController",
			// Library ( C ), Album ( C ), Camera ( VC ) の 3種
			sourceType: "Camera",
			// Default , Sampling , Sequence
			cameraMode: "Default",
			config:{
				"showsCameraControls":true,
				"showsZBarControls":true,
				"tracksSymbols":true, // スキャンする時に四角の枠を表示する
				//"showsSymbols":true,
				"enableCache":true,
				"showsHelpOnFail":true,
				"takesPicture":false
			},
			// この他、Symbol 設定で利用可能なバーコードを決められる
		},
		
		// 成功した場合の処理定義
		success:function(data){
			Ti.API.info('TiBar success callback!');
			if(data && data.barcode){
				Ti.UI.createAlertDialog({
					title: "Scan Result",
					message: "Barcode: " + data.barcode + 'Symbology:' + data.symbology
				}).show();
			};
		},
		// 中止した時の処理定義
		cancel:function(){
			Ti.API.info("TiBar cancel callback!");
		},
		// エラーを起こした場合の定義
		error:function(){
			Ti.API.info("TiBar error callback!");
		}
	});
});
処理の分岐を、JSON形式で書いていくのは、なんか関数型言語のような感じで面白い。
実行したら、カメラシミュレータの画面が出た。iPhoneシミュレータなので、これでおしまいのようだ。
TiBarのサイトには、シミュレータでバーコード処理を再現するための方法も書いてあったが、なんかハマりそうだったので、今回はこれで満足。

2011年8月20日土曜日

iPhoneアプリ(Titunium)からWebアプリ(Ruby on Rails)へのデータ転送

TutorialのTwitterアプリでWeb API経由でデータを取得する方法の勘所をつかんだので、iPhoneからWebアプリへのデータのアップロードを試してみる。

Webアプリケーション

Webアプリ側は、手っ取り早く Ruby On Railsで作成。
(Railsのヴァージョンは、2.3.8を入れてあるので、databaseのdefaultは、sqlite3)
Webアプリのイメージは、在庫管理用の商品コードの保管と表示。モデルは、在庫Stock。StockをiPhone側から扱うコントローラは、Apiと言う名前にした。
$ rails testapp
$ cd testapp
$ script/generate model Stock
$ script/generate controller Api index

"Stock" modelのmigrationの中身は、以下の感じ
create_table :stocks do |t|
	t.string :name # 商品名 今回は、無用。
	t.string :code # 商品コード
	t.timestamps
end

controllerの中身は、WebAPI的なのりで作成してみた。APIの仕様?は以下の通り。
http://hogehoge/api/               --- 全てをjson形式で提供(表示)
http://hogehoge/api/show/code_no   --- コード = code_no のstockをjsonで提供
http://hogehoge/api/upload/code_no --- 新しいデータを登録 code = code_no

例外処理とか未だ入れてないので、エラー頻発だ。

class ApiController < ApplicationController
    # 全て表示のアクション
	def index
		@stocks = Stock.all
		# レスポンスは、jsonにフォーマットして出力
		respond_to do |format|
			format.json { render :json => @stocks, :callback => params[:callback] }
		end
	end

	# 商品コードを指定して出力
	def show
		@stock = Stock.find_by_code(params[:id])
		respond_to do |format|
			format.json { render :json => @stock, :callback => params[:callback] }
		end
	end

	# 今回の主役。商品コードを受け取り、データベースに保管する
	# レスポンスは、textにした
	def upload
		message = 'Upload Error'
		if params[:id] && stock = Stock.new({:code => params[:id]})
			message = 'Save New Code :' + stock.code if stock.save
		end
		render :text => message
	end
end

これで、script/server でテストサーバを起動。
http://hogehoge:3000/api/ を試してみた。確かにjson形式で出力されている。


Titunium側のプログラム(一部)

さて、Tituniumアプリ側の該当部分。HTTPClientオブジェクトを操作する。

var win = Ti.UI.currentWindow;
var sendButton = Ti.UI.createButton({
	title:'Send Data',
	top:200,
	left:10,
	width:300,
	height:100
});
win.add(sendButton);

// sendボタンのclickイベントを記述
sendButton.addEventListener('click', function(){
	// HTTPClientオブジェクトを用意
	var xhr = Ti.Network.createHTTPClient();
	xhr.timeout = 1000000;
	var sample = '12345'; // サンプルデータ
	var url = 'http://hogehoge:3000/api/upload/' + sample;
	xhr.open('GET',url);
	// ロードされた場合の処理を記述。
	// ここではレスポンスを受け取ってダイアログボタンに結果を返すと定義。
	xhr.onload = function(){
		var res = this.responseText;
		Ti.UI.createAlertDialog({
	  		title: 'Send Data',
	  		message: res
	  	}).show();
	};
	// リクエストを送信する。レスポンスが帰ったときに、onloadアクションとなる。
	xhr.send();
});

iPhoneアプリをエミュレータで起動。[send]ボタンを押すと、サーバアプリにサンプルデータが送信され、その結果がモーダルボタンで表示される。

気になったこと
  • ボタンイベントに変数(この場合はデータ)を渡す方法
  • HTTPClientの接続失敗の処理方法
  • send()のタイミングは、正しいか?

2011年8月14日日曜日

OSX Lionのsandboxとは

Macの最新OS OSX Lionでは、セキュリティ面で大きな変更が行われている。マイクロソフトは、セキュリティとして自前のセキュリティソフトの提供を行っているが、Macはどうだろうか? 気になっていた点を解説している記事をみつけたので、英訳を試みてみた。

http://arstechnica.com/apple/reviews/2011/07/mac-os-x-10-7.ars/9

サンドボックス

Appleが静かにしているからといって、それはセキュリティに関して何も改善の歩を進めていないわけではありません。レパード(OX 10.5)にて、Appleは「サンドボックス」の基本的な形を加えました。スノーレパード(OX 10.6)では、デーモンプロセスの多くは「サンドボックス」内で動くようになりました。これらは、何のファンファーレも鳴らさずに行われているのです。

アプリケーションをサンドボックスの内側で動かすことは、マルウエアなどによって信用出来なくなった場合の被害を最小限に抑えることを意味します。サンドボックス化したアプリケーションは、通常のアプリケーションでは出来る様々な出来る機能を自ら制限します。例えば、通常のアプリは、そのユーザによって作成されたファイルを(同じユーザが)削除することが出来ます。当然ですが躾の良いアプリは、そんなことはしません。しかし、いったん障害が起きると、破壊的な行為が引き起こされてしまうかもしれないのです。

Lion (OSX 10.7)では、「サンドボックス セキュリティ モデル」が大きく拡張されました。Appleは、サードパーティに、サンドボックス化を強く進めています。サンドボックス化アプリは、利用する資源、機能を一覧にした「権限付与リスト」を中に持っています。
訳者注)OSXのアプリは、一見exeバイナリに見えますが、実態はフォルダです。
Lionは、30もの異なる権限リストをサポートしています。中には、ネットワーク接続を作成する権限リストや、ネットワーク接続を通じてListenする権限リストなどもあります。これらは、(ネットワーク接続関連ですが)別の2つの権限リストです。ビデオのキャプチャや組み込みカメラに接続するための権限などもあります。

(途中省略。ファイル関連のサンドボックスの権限付与リストの作成はどうするのか? 的な話。)

Appleは、ファイルの扱いなど特別な分類の行動群に高度な制限を与えることで、権限ファイル権限リスト更新の問題を解決しようとします。Lionでは、Powerbox(pboxd)と呼ばれる信頼されたデーモンプロセスが提供されています。Powerboxデーモンは、サンドボックス化されたアプリケーションにファイル表示やオープン・保存のためのダイアログを提供します。サンドボックス化されたアプリがファイルを選んだあと、Powerboxは、そのアプリのサンドボックスに小さな穴をあけます。

(途中省略。似たような機能は「最近開いたファイル」メニューに利用されている、的な話)

サンドボックス化されたプロセスは、アクティビティモニターで見ることができます。
(このイメージは、訳者のものです)

近い将来、Mac App Storeは、Appleが新しいLionテクノロジを普及させる方法として、使われていくでしょう。サンドボックスの場合、すでにそれは行われているようです。Appleは、11月から全てのアプリはサンドボックス化しないと許可しないと宣言しています。



ふう! 疲れました。私の英語力では、もういっぱいいっぱいです。熱いし!
あとは記事で読んでみてください。以下は、要約っぽいものを。

この後、記事では権限分離の話が続きます。例えば画像処理のような機能は、まとめて一つのプロセスとして、それをアプリが利用する形態を進めているそうです。サンドボックス、その共通プロセスのほうに行います。画像処理アプリは、そのプロセスのAPIを利用することで、セキュリティ上も、開発の上でも簡単に実装できることになると言う考えです。
すでに、この考えかたは、GoogleのChromeで行われているそうで、セキュリティの一つのトレンドとなっていると言って良いでしょう。

2011年8月13日土曜日

jarをつくってみる

scalaで作られたTwitter clientがあると言うので
http://www.moongift.jp/2011/07/20110722-3/
ソースコードの勉強のために、ダウンロードしてみた。興味があったのは、以下の点
1. scalaで作ったweb applicationやコマンドではなく、クライアントアプリだということ。クライアントアプリケーションは、そのGUIとかどうしているのだろうか。
2. Twitter APIを利用しているはずだが、具体的には?

開発者のサイトをみて、実際にアプリケーションをインストールして判ったのは、
https://github.com/kaizawa/kotsubu
Java Web Startで配布されていること。Java Web Startは、未だ試したことがないので、これはいつか調べることにして、まずは jarファイルの作成までをやってみた。

Sakura VPSで作業をおこなったので、java関連のパッケージをインストール。
Jakarata Antをインストール。antは、yumからだと1.6系がインストールされてしまうので、これはApacheサイトからバイナリをダウンロード。
 $ yum -y install java-1.6.0-openjdk java-1.6.0-openjdk-devel java-1.6.0-openjava-javadoc
 $ cd /usr/src/
 $ wget http://archive.apache.org/dist/ant/binaries/apache-ant-1.8.2-bin.tar.bz2
 $ tar jxvf apache-ant-1.8.2-bin.tar.bz2
 $ mv apache-ant-1.8.2 /usr/local/ant

/etc/profile にPath情報を追加。
(どうも、以前 scalaをインストールしたとき、既にJava実行環境がはいっていたようだ。)
 export JAVA_HOME=/usr/java/default
 export ANT_HOME=/usr/local/ant                      # 追加
 export PATH=$PATH:$JAVA_HOME/bin:$ANT_HOME/bin      # $ANT_HOME/bin 追加
 export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar

ダウンロードしたTwitterクライアントのソースには、Twitter用のライブラリがはいっていない。
必要なTwitter4j(Twitter APIのJavaラッパー)を以下からダウンロード。
http://twitter4j.org/ja/index.html
どこにライブラリファイルを置いて良いのか判らないので、とりあえず build.xmlファイルと同じディレクトリにおいてみた。

 $ wget http://twitter4j.org/en/twitter4j-2.2.4.zip
 $ unzip twitter4j-2.2.4.zip
 $ mkdir twitter4j
 $ mv twitter4j-2.2.4.zip twitter4j
 $ cd twitter4j/
 $ unzip twitter4j-2.2.4.zip
 $ cp lib/twitter4j-core-2.2.4.jar ../kaizawa-kotsubu-7b37fd7/
 $ cd ../kaizawa-kotsubu-7b37fd7
 $ ant

javadocパートでコンパイルエラーがでた。jar本体は出来たようす。
 $ ls -l dist/
 合計 272
 -rw-rw-r-- 1 baker baker 268597  8月 10 17:46 kotsubu.jar
 drwxrwxr-x 2 baker baker   4096  8月 10 17:46 scaladoc  # なかは空っぽ

面倒くさいので、build.xml の パートのjavadocの部分を削除して再度、ant

# 変更前のbuild.xmlの一部
< target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>

# 変更後
< target depends="test,jar" description="Build and test whole project." name="default"/>

# ant コマンド発行の結果
-do-jar-with-manifest:
-do-jar-without-manifest:
-do-jar-with-mainclass:
     [echo] To run this application from the command line without Ant, try:
     [echo] java -cp "/home/baker/work/kaizawa-kotsubu-7b37fd7/dist/kotsubu.jar" kotsubu.Main
     [echo]                 
-do-jar-with-libraries:
-post-jar:
jar:
default:
BUILD SUCCESSFUL
Total time: 8 seconds

とりあえず、ここまで。
 $ ls -l dist/
 合計 272
 -rw-rw-r-- 1 baker baker 268597  8月 10 17:46 kotsubu.jar
 drwxrwxr-x 2 baker baker   4096  8月 10 17:46 scaladoc  # なかは空っぽ

2011年8月2日火曜日

Titunium 新規プロジェクト始動

Titanium Studio 悪戦苦闘

いくつかチュートリアルをこなしたが、Titunium Studioは使いにくい。コード補完機能は日頃使っていなかったので、とても新鮮なのだが。
以下、気になったところ
  • コンパイルに時間がかかりすぎると、sessionが切れる。
  • 過去のコンパイル情報と矛盾した場合、エラーになる。
  • 再度、コンパイル & 実行すると上手くいく。
  • subverion pluginは使いにくい。
  • プロジェクトの削除の方法が不明だった。

Subversionの利用

新規プロジェクトを始める。ソース管理は、やっぱりsubversionを使うことにしたが、subversionもコマンドラインで使っていたので、Titunium Studioでの利用に随分と迷う。いろいろ試行錯誤したが、結果以下の手順にした。ある程度ソースが出来、SVN Pluginのインストールの後、
  1. [clean ..] する。 build ディレクトリを空にする。(iphoneディレクトリも無くす)
  2. command line で、import する。
  3. command line で、export する。
  4. buildフォルダの属性をignoreにする。
  5. 元のプロジェクトのIDを変更する(import時にIDで同じかを判断しているらしい)。
  6. 別のプロジェクトでimportする。
  7. 元のプロジェクトは削除する。menuから。
なかなか、スタートアップが上手くいかないねえ。

subversionプラグインのインストールは、以下を参照。
http://wiki.appcelerator.org/display/tis/Subversion

$ cd /project/dir/
 $ svn import . -m "iPhone Titanium mobile app" http://xxx/svn/trunk/app 
 $ cd ..
 $ mv app _app
 $ svn co http://xxx/svn/trunk/app
 $ cd app
 $ svn propset svn:ignore "*" build/
 $ svn commit -m "set propset svn:ignore build/*"

最初のコード app.js

さて、今日のTitaniumの勉強は、棚卸業務プログラムの手始めとして、TabGroupの制御などを学習。

Resorces/app.jsは、アプリケーションの起動ファイル。このソースが最初に処理される。
ここでは、タブグループのセットと、他のviewとのつながりを記述することにした。

Titanium.UI.setBackgroundColor('#000'); //背景をセット
var tabGroup = Ti.UI.createTabGroup();   //タググループセット

// [tab_scan]タブの生成。 [win_scan]ウインドウを所有する。
// 起動の初期画面。バーコードスキャンなど主業務を行う画面となる予定。
var win_scan = Ti.UI.createWindow({
 url: 'win_scan.js',             // win_scanが記載されるソースファイル
 title: 'SCAN',                 // cssに準拠しているらしい
 backgroundColor:'#fff'
});

var tab_scan = Ti.UI.createTab({    // 
 window: win_scan,
 icon:'KS_nav_ui.png',
 title:'scan'
});

// タブ [list_tab]
// スキャンしたコードを保管し、その一覧を表示する予定。
var win_list = Ti.UI.createWindow({
 url: 'win_list.js',
 title: 'LIST',
 backgroundColor:'#fff'
});

var tab_list = Ti.UI.createTab({
 window: win_list,
 icon:'KS_nav_ui.png',
 title:'list'
});
tab_list.setBadge(52); // バッチ表示を試してみた。

// タブ [tab_config]
// 各種設定を行う画面の予定。
var win_config = Ti.UI.createWindow({
 url: 'win_config.js',
 title: 'CONFIG',
 backgroundColor:'#fff'
});

var tab_config = Ti.UI.createTab({
 window: win_config,
 icon:'KS_nav_ui.png',
 title:'config'
})

// タブグループへの各タブの追加とスタート。
// 初期は、tabs[0] になるらしい。addTabは、配列への追加イメージか。
tabGroup.addTab(tab_scan);
tabGroup.addTab(tab_list);
tabGroup.addTab(tab_config);
tabGroup.open();

win_scan.js

ボタンの追加とボタンクリックの時のイベントリスナー処理を試してみた。

// このファイルが呼ばれた時点で "current"
var win = Ti.UI.currentWindow; // currentWindowは(appの)プロパティ。

var label = Ti.UI.createLabel({
 color:'#999',
 text:'this is scan windows.',
 font:{fontSize:20,fontFamily:'Helvetica Neue'}, // フォントは設定がこれ?
 textAlign:'center',
 top:10,
 height:30 // 指定しないと autoになり、windowいっぱいの縦幅になる。
});
win.add(label);

var scanButton = Ti.UI.createButton({
 title:'Scan It',
 top:80,
 left:10,
 width:300,
 height:100
});
win.add(scanButton);

var sendButton = Ti.UI.createButton({
 title:'Send Data',
 top:200,
 left:10,
 width:300,
 height:100
});
win.add(sendButton);

// ボタン[scanButton]がクリックされた場合のイベントを記載。
// イベントの実際は、無名関数 function() で実装。
scanButton.addEventListener('click', function(){
 Ti.UI.createAlertDialog({
  title: 'SCAN ...',
  message: 'scan complete !'
 }).show(); // オブジェクトを生成し、すぐ表示。イベント発生時になる。
});

イベントリスナーのテストをしたところ!

2011年7月17日日曜日

Titanium Mobileで Twitterアプリを作る

Titaniumで、Twitterクライアントを作成してみる。まあ、表示のみだが。
参考にしたのは、
http://gihyo.jp/dev/serial/01/titanium/0002
http://developer.appcelerator.com/blog/2011/06/titanium-studio%E3%81%A8titanium-mobile-1-7%E3%82%92%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F.html?lang=ja
http://code.google.com/p/titanium-mobile-doc-ja/wiki/get_started


記事の通りに進めてみたが、うまく表示しない。空のTableViewだけが表示される。
この解析だけで、随分と時間を食ってしまった。
結論から言うと、単なるスペルミスだったのだが....

JavaScriptでは宣言されていない変数もエラーにならない

え!そうだったけ!!
おかげで、いろいろと試行錯誤し、勉強になった。ポイントは以下の通り。
  • デバックするためには、Ti.API.info() が便利
  • サンプルコードを疑わないためにも、お手本となる動くソースが必要。Appcelerator社が提供しているサンプル集[KitchenSink]をTitanium Studioでダウンロード出来る

KitchenSinkのTwitterソースを参考に、Gihyo.jpのソースを修正したものが以下の通り。


右のように、iPhoneシミュレータで私のツイートを表示することが出来ました。ああ疲れた。

//背景色をセット
Titanium.UI.setBackgroundColor('#000');
//[タブグループ]オブジェクトtabGroupを作成
var tabGroup = Titanium.UI.createTabGroup();
//[ウインドウ]オブジェクトwin1を作成
var win1 = Titanium.UI.createWindow({  
    title:'Tab 1',
    backgroundColor:'#fff'
});
//[タブ]オブジェクトtab1を作成し、win1をプロパティにセットする
var tab1 = Titanium.UI.createTab({
    window:win1
});

// Twitter ここからはTwitter機能の実装
// ======================================================================
// TableViewオブジェクトを初期化
var tableView = Ti.UI.createTableView({
 data: [];
});

// HTTPClientオブジェクトを作成
var xhr = Ti.Network.createHTTPClient();
xhr.timeout = 1000000;
var user = 'silentbaker';

// HTTPClientオブジェクトで Twitter APIを操作。接続用メソッド?をセット
xhr.open("GET","http://api.twitter.com/1/statuses/user_timeline.json?screen_name="+user);

// 読み込み時(onloadイベント)の処理を関数で定義
xhr.onload = function()
{
 // タイムラインを配列に格納。JSON.parseが出来る。従来通り? evalも可能。
 var timeline = JSON.parse(this.responseText);
 //var timeline = eval('('+this.responseText+')');
 var currentData = [];
 // デバックの時には、Ti.API.info()を使いましょう
 Ti.API.info(timeline.length);
 // テーブル行を作成し、そのラベル プロパティにツイート文をセット
 for(var i=0 ; i < timeline.length ; i++){
  var tweet = timeline[i];
  var row = Ti.UI.createTableViewRow();
  var commentLabel = Ti.UI.createLabel();
  commentLabel.text = tweet.text;
  Ti.API.info(commentLabel.text); // Debugのため
  row.add(commentLabel);
  currentData.push(row);
 }
 // 各行(オブジェクト)の配列をtableViewオブジェクトにセット
 tableView.setData(currentData);
};
// HTTPメッセージを送信する (この時、onloadイベント発生?)
xhr.send();
// end of twitter
// =====================================================================

win1.add(tableView);   // テーブルオブジェクトをウインドウwin1に追加
win1.hideTabBar();     // タブバーは隠す
tabGroup.addTab(tab1); // タブグループにタブtab1を追加
tabGroup.open();       // アプリケーションを起動 tabGroup -> tab1 -> win1 -> tableView

Titanium MobileでiPhoneアプリ開発環境を作る

Titanium は、"チタニウム"ではなく"タイタニウム"と発音するらしい。Titan(巨人族)と関係あるのだろうか。JavascriptでiPhone, Androidアプリケーションの開発が出来る開発環境(SDK)だ。
この開発環境がすばらしいのは、iPhoneのAPIを操作できるアプリケーションを作成できること。カメラやGPS機能などを操作できるらしい。
早速、インストールしてみる。
ここを参考にしました。
http://developer.appcelerator.com/
http://developer.appcelerator.com/blog/2011/06/titanium-studio%E3%81%A8titanium-mobile-1-7%E3%82%92%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F.html?lang=ja

手順は
  1. OSXに開発環境XCodeとiOS SDKをインストール
  2. Titanium mobileをインストール
  3. Hello Worldしてみる
  4. Apple IDを取得(実機にインストールするため)

X Codeのインストール

1年前に入れたのだがバージョンが古いので、最新の4.0.2をMac AppStoreから、600円で購入。600円は高いのか、安いのか、よう判らん。昔は只なので。

インストールサイズは、なんと4G。一晩掛かりました。

Titaniumをインストール

Titaniumのアカウントを取得する。
参考にした記事では、Titanium SDKがダウンロードされるはずでしたが、Studioがダウンロードされました。有償の罠にはまったかとドキドキ。どうやら、6月14日から全てがStudio (Appcelerator Titanium Studio version 1.7)に統合され配布されることになった様子。オリジナルは、eclipusのらしい。日頃の開発はVimでやっているので、ちょっと不安。どうもIDEは、覚えなければならないことが余計にでてくるみたいで面倒なんだが。

Hello World

Appcelerator社の開発者サイトの記事通りにやって、特に問題なく終了。iPhoneシミュレータが起動した時には、ちょっとした感動ものです。(右図)



プロジェクトを作成した時に自動的に生成される初期ソースを以下解説。
// Master UIView (Titanium.UI) に背景色をセットする
Titanium.UI.setBackgroundColor('#000');
// タブグループを用意。
var tabGroup = Titanium.UI.createTabGroup();
// ウインドウwin1を用意。
var win1 = Titanium.UI.createWindow({  
    title:'Tab 1',
    backgroundColor:'#fff'
});
// タブtab1を追加し、前に追加したウインドウwin1を結びつける
var tab1 = Titanium.UI.createTab({  
    icon:'KS_nav_views.png',
    title:'Tab 1',
    window:win1
});
// ラベル label1を用意し、ウインドウwin1に追加。
var label1 = Titanium.UI.createLabel({
 color:'#999',
 text:'I am Window 1',
 font:{fontSize:20,fontFamily:'Helvetica Neue'},
 textAlign:'center',
 width:'auto'
});
win1.add(label1);
// ウインドウ win2、タブ tab2 (win2をセット)、label2を用意。
var win2 = Titanium.UI.createWindow({  
    title:'Tab 2',
    backgroundColor:'#fff'
});
var tab2 = Titanium.UI.createTab({  
    icon:'KS_nav_ui.png',
    title:'Tab 2',
    window:win2
});
var label2 = Titanium.UI.createLabel({
 color:'#999',
 text:'I am Window 2',
 font:{fontSize:20,fontFamily:'Helvetica Neue'},
 textAlign:'center',
 width:'auto'
});
win2.add(label2);
// タブグループにtab1, tab2を追加
tabGroup.addTab(tab1);  
tabGroup.addTab(tab2);  
// タブグループを(初期で)起動する
tabGroup.open();

Apple Developer Programへの参加

1万円ぐらいかかるので、もう少し技術が熟練してからですね :-p

2011年7月10日日曜日

rails + google map を試す

Google APIを試してみた。以下のサイトを参照しました。
http://kenjiro2u.blogspot.com/2009/09/google-maps-on-rails.html
http://d.hatena.ne.jp/zariganitosh/20081012/1223816212
http://ym4r.rubyforge.org/

構成は
* rails 2.3.8
* ym4r-gm --- google map APIを扱うプラグイン
* Google API

まずは、テストプロジェクトを作成し、Google APIを取り扱うプラグインYM4R/GMをインストール。
  $ rails map_test
  $ cd map_test
  $ sudo gem install ym4r
  $ ruby script/plugin install git://github.com/queso/ym4r-gm.git
  $ script/generate controller Map index 

Google Map APIを使うため、Google Maps API Keyを取得します。
4年程前かな。なぜか、このKeyの取得が上手くいかずに凝っただが、今回は簡単にGet。日本語のドキュメントが整備されているのが大きいな。
http://code.google.com/intl/ja/apis/maps/signup.html

日本語ドキュメントは大変貴重で、いろいろと気になることが判った。
* ドメイン名で取得することが吉。www.miyabiit.com ではなく、miyabiit.comでとる。
* localhost は無条件で使える。開発のため。もちろん、自分自身しかみえないけど。
* ビジネス利用のためpremierサービスがある。パスワード利用のサイト構築、有償サイトは要検討。

取得したKeyは、/config/gmaps_api_key.yml に記載します。このファイルはプラグインインストール時に自動作成されているはずです。

html header部に、以下の2つのヘルパーメソッドを埋め込み、必要なjavascript, htmlコードを生成します。
layoutファイルを利用したので、以下のように作成。

<%= GMap.header %>
  <%= @map.to_html %>

コントローラ /app/contoller/map_controller.rb にて @mapを生成し、viewに渡します。
def index
    palo_alto = [37.4419, -122.1419] # 位置情報を配列で用意
    # google map api オブジェクトを作成。表示タグ map_div
    @map = GMap.new("map_div")       
    # 位置の表示
    @map.center_zoom_init(palo_alto, 10)
    # 拡大縮小ボタン、地図タイプ切り替えボタン
    @map.control_init(:large_map => true, :map_type => true)
    # マーカの指定
    @map.overlay_init(GMarker.new(palo_alto,
      :title => "パロアルト",
      :info_window => "これは情報窓"
      )
    )
  end

viewには以下のコードを埋め込みます。
<%= @map.div(:width => 500, :height => 400) %>

これで、railsを実行すれば以下のような画面をみることが出来ます。

2011年6月27日月曜日

構造化ドキュメント作成管理ツール Sphinxを試す

ドキュメント作成ツール Sphinxが注目されている。ざっと調べてみたが、Wikiと違い構造化ドキュメントを作成するのに適しているようだ。
LaTeXも出力出来るし、アドオンでダイアグラムやUML図もできるようなので、これを私の標準ドキュメントツールとして採用することにした。
日本語サイトは、ここ http://sphinx-users.jp/index.html

さくらVPSへのインストールは、ここを参考にしました。
http://b.l0g.jp/dev/sphinx-on-centos55/

[baker]$ su -
[baker]# yum install python-pip python-setuptools
[baker]# pip-python install sphinx

あっさり、終了。インストールしたパッケージは
  • python-pip -- pythonのパッケージ管理ツール
  • python-setuptools -- パッケージ・インストーラ makeに近いのか?

一般ユーザでテストドキュメントを作成してみた。
気になったこと、問題になったこととしては
  • デフォルトのデザインがもう少しなんとかならないかな。
  • ファイル名を間違えた。警告をうっかり見過ごした。
  • ソースをみるで、日本語が化ける。文字コード指定がないのか。

ドキュメントの作成とリリース、メンテナンス、公開については
  1. リポジトリ内でプレーンテキストで保管
  2. プレーンテキストでVersion管理
  3. 自動バッチでサイトにアップし、一気にコンパイル
  4. プロジェクト管理システム内にドキュメントリンクを張る
で良いだろうか。

2011年6月26日日曜日

Node.jsのwebフレームワーク expressのチュートリアルを試す

node.jsの多分、現在一番有名なWebアプリケーションフレームワークexpressのチュートリアルをためした。
express github

cd work/
# fooと言う名のプロジェクトを作る
express foo && cd foo
# 必要とするパッケージをこのプロジェクトディレクトリ内にセット
rpm install -d
# パッケージ一覧をみる
npm list
application-name@0.0.1 /home/baker/work/foo
├─┬ express@2.3.12 
│ ├── connect@1.5.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
└── jade@0.12.4 

# 実行してみると
node app.js

パッケージの説明

  • express -- web application framework
  • connect -- 他社ミドルウエアを動作させるためのフレームワーク
  • mime -- mime type一覧 multipurpose internet mail extention
  • qs -- Query Stringsを解析してJSON形式で出力
  • jade -- viewテンプレート

基本となるアプリケーションのセットはapp.jsに書くそうだ。
railsで言うところのroute.rbだろうか。

初期プロジェクトの構成は以下のようになっている。
$ tree -L 2
.
|-- app.js              # アプリケーション本体
|-- logs                # ログ保管
|-- node_modules        # モジュールの保管場所 railsのvender/plugin
|   |-- express
|   `-- jade
|-- package.json        # パッケージ情報
|-- pids                # ロックファイル保管
|-- public              # 公開ディレクトリ 実際に公開するときはどうするかな
|   |-- images
|   |-- javascripts
|   `-- stylesheets
`-- views               # Viewファイルの保管場所
    |-- index.jade
    `-- layout.jade

いろいろと気になることは
* データベース関連はどうなるのかな。MVC構造をどうするのか。
* サンプルプログラムが提供されているけど、どうやって取得して利用するかな。
* リリース(デプロイ)する時にはどうするのか。

2011年6月19日日曜日

コンテキストメニューの「印刷」

最近は簡単な業務アプリをrailsで作成しているが、その時にいつも課題となるのが印刷。
印刷は、おおむねpdfで実現している(逃げている)が、自動で印刷したいとニーズも多い。

調べてみたが、どうもすっきりした手がない。恐らく、正解はsambaでプリンターサーバを立てることではないかと推定しているが、大掛かりすぎるな。

結局、Webアプリケーションを稼働させているサーバ(windows)にアクロバットリーダをインストールし、oleで操作することにした。pdfファイルを右クリックし、コンテクストメニューの「印刷」を選んで印刷する。この操作をOLEで実現することだ。
掲示版で記載されていたアイディアの拝借です。
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/40924

Linuxサーバでは使えないが、Net::ssh/sftpライブラリを使って、リモートのwindowsプリンタサーバにpdfファイルを送信して、このプログラムをキックさせれば良いだろう。

事前作業は2つ。
  • アクロバットリーダインストール
  • Active Script Ruby
OLEを実現するため、ASRに含まれるwin32oleライブラリを利用することにした。

#!ruby -Ks
=begin
filename : print.rb
必ず ファイルをShiftJISにて保管すること
実行 : $ ruby print.rb 
=end
require 'win32ole' # oleを操作するライブラリ

class WIN32OLE
  include Enumerable # oleのコレクション操作にeachを使うため(かな?)
end

parent, name = File.split(File.expand_path(ARGV.shift))
parent.gsub!("/", "¥¥") # フルパス指定するときに問題にならないように

shell = WIN32OLE.new('Shell.Application')  # シェルを起動
verbs = shell.NameSpace(parent).ParseName(name).Verbs # コンテクストコマンドリスト取得

if v = verbs.find {|v| v.Name == "印刷(&P)" }
  v.doIt
  sleep(2) # 起動が遅い場合 (excel とか),少し待ってあげないとだめ(らしい)。
end

2011年6月5日日曜日

iCalendarでgoogleカレンダーへの登録を自動化

会社で使っているグループウエアのスケジュール情報をGoogleカレンダーに写すことを考えてみた。グループウエアにicalファイルを発行する機能があらば良いのだが、あいにくそのような機能はない。
用意するプログラムの概要は
  • 会社のグループウエアのデータベースからsqlでスケジュールファイルを出力
  • スケジュールファイルからicalファイルを作成し、webで公開
  • Googleカレンダーで「その他のカレンダー」を追加。(url指定)

iCalendarは、RFCで定義されている、カレンダー情報の共通規約です。あんまり、普及していないような気もしますが。詳細は定義は、以下が日本語でわかりやすいです。
iCalendar

icalendarを扱うライブラリが、rubygemにありましたので、それをインストールします。
$ sudo gem install icanlendar

sqlで取得したスケジュールファイルはこんな感じ。
ZZZ FW切り替え作業 監視 |       |      | 2011-06-04 00:00:00 | 2011-06-04 00:00:00
月曜ミーティング        |       |      | 2011-06-06 09:00:00 | 2011-06-06 10:00:00

require "rubygems"
require "icalendar"
require "kconv"    # 日本語の文字コードを扱うための標準ライブラリ
require "date"
# OpenStruct (標準ライブラリ)
# javascriptのobjectのようにプロパティの追加を気楽に扱う
# http://memo.yomukaku.net/entries/244
require "ostruct" 

schdule_file = 'schedules.dat'
schedules = []
File.readlines(schedule_file).each do |line|
  v = line.split(/\|/).map{|val| val.strip}
  next unless v.size > 1
  schedule = OpenStruct.new
  schedule.user = v[0]
  schedule.event = v[1]
  schedule.content = v[2]
  schedule.place = v[3]
  schedule.start = DateTime.parse(v[4])
  schedule.end = DateTime.parse(v[5])
  schedules.push schedule
end

cal = Icalendar::Calendar.new
# VEVENT コンポーネント
# ここがスケジュールの本体
schedules.each do |schedule|
  cal.event do
    dtstart schedule.start, {'TZID' => 'Asia/Tokyo'}
    dtend   schedule.end  , {'TZID' => 'Asia/Tokyo'}
    summary schedule.event
    description schedule.content
  end
end

# STANDARD コンポーネント
standard_component = Icalendar::Component.new('STANDARD')
standard_component.custom_property('dtstart', '19700101T000000')
standard_component.custom_property('tzoffsetfrom', '+0900')
standard_component.custom_property('tzoffsetto', '+0900')
standard_component.custom_property('tzname', 'JST')

# VTIMEZONE コンポーネント
vtimezone_component = Icalendar::Component.new('VTIMEZONE')
vtimezone_component.custom_property('tzid', 'Asia/Tokyo')
vtimezone_component.add(standard_component)
cal.add(vtimezone_component)

# iCalファイル
ical_file = 'calendar.ics'
File.open(ical_file, "w+b") { |f|
    f.write(cal.to_ical.toutf8)
}

さらっと、書いたが実はいろいろと苦労した。
  • 最初はタイムゾーンを設定せずに作成したため、時間がずれた
  • Googleカレンダーの更新が遅いため、ファイルを変更してもカレンダーに反映せず

それにしても、Googleカレンダーはどの程度の間隔で外部カレンダー情報を更新しているのだろうか? サイトを調べた限りでは、数時間ということだが、なんかいつまでたっても更新しない。

困ったもんだ。

2011年5月28日土曜日

Coffee Script 最初の一杯

coffee scriptを試してみる。
Coffee Scriptは、文法がPythonに似ている言語で、コンパイルするとjavascriptを生成する。
恐らく、javascriptの強力な部分を活かし、判りにくい部分を隠ぺいしたモダンな開発言語を作成することが目的なのだと思う。javascriptベースのサーバサイド実行環境node.jsなど、javascriptだけでアプリケーションを開発する環境が整ってきている。javascriptは、インターネット・アプリケーション開発で、その言語の強力さとブラウザが実行エンジンを持つことから、重要な位置を占めてきた。サーバサイドが全てjavascriptで作成できれば、一つの言語でクライアントサイド、サーバサイドを共に開発することが出来る。しかし、ロジックを記述したり、トランザクションを記述するには、正直判りにくいと思う。coffee scriptは、そのようなニーズから生まれたのではないか。

Coffee Scriptとは
Coffee Scriptは、小規模な言語であり、コンパイルしてJavaScriptを生成する。中括弧やセミコロンに幻惑されて気づかないが、JavaScriptはいつも、その内に素晴らしいオブジェクトモデルを隠し持っている。Coffee Scriptは、JavaScriptの、その良い部分をシンプルな方法で顕在化する試みである。

CoffeeScriptの黄金律は、「まさにそれはJavascriptだ」と言うこと。コードはJavaScriptに一対一でコンパイルされ、実行時に不要な処理が行われることはない。専用のライブラリは不要だ。コンパイルされたJavaScriptコードは、読みやすく 警告なし、注釈なしで、全てのJavascript実行環境で動作するし、多くの場合、手書きのコードと同等か早く動作するでしょう。

CoffeeScript is a little language that compiles into JavaScript. Underneath all of those embarrassing braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.

The golden rule of CoffeeScript is: "It's just JavaScript". The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly (and vice-versa). The compiled output is readable and pretty-printed, passes through JavaScript Lint without warnings, will work in every JavaScript implementation, and tends to run as fast or faster than the equivalent handwritten JavaScript.
(Coffee script ホームページから 引用)

最初のサンプルをコンパイルしてみる。
# ファイル square.coffee
square = (x) -> x * x         # 無名関数"(x) -> x * x"を値にもつ。つまり関数名がsquare。
cube = (x) -> square(x) * x   # カリー化?みたいな感じ?
alert cube(5)                 # htmlでウインドウを表示させる。

$ vim square.coffee
$ coffee -c square.coffee
$ ls 
square.coffee square.js

コンパイルした結果(square.js)を見てみると
(function() {
  var cube, square;
  square = function(x) {
    return x * x;
  };
  cube = function(x) {
    return square(x) * x;
  };
  alert(cube(5));
}).call(this);

実際に実行させてみる。以下のようなテスト用ソースを用意した。
実行結果はここで

2011年5月16日月曜日

node.js と npmをインストール

node.jsやらcoffee scriptやらTitanium を試してみたくて、さくらのVPSにインストールする。
例によって、いろいろと試行錯誤した(混乱した)ので、又記録しておく。

実は、node.jsだけならば一度入れたことがありました。その時の手順は以下の通り。

$ mkdir work/node
 $ cd work/node/
 $ wget http://nodejs.org/dist/node-v0.4.0.tar.gz 
 $ tar xzf node-v0.4.0.tar.gz 
 $ cd node-v0.4.0
 $ ./configure 
 $ make 
 $ sudo make install

この時はサンプルプログラム(以下)を作って実行してみた。
/* file name : server.js
var http = require('http'); // httpライブラリを呼ぶ
http.createServer(function(request,response){
 response.writeHead(200, {'Content-Type':'text/plain'});
 response.end('Hello World');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124');

$ node server.js & 

今回はnode.js環境のパッケージマネージャであるnpmを導入して、様々なモジュールを試すための準備をすることだった。

混乱の最初は、nave。node.jsの複数のバージョンを切り替えるツールだが、これが必須だと勘違いしてしまったこと。node.jsは凄い勢いでヴァージョンがアップされているらしく、まだ仕様が安定していない。そのための対策で、rubyではrvmが有名。naveの他にnvmと言う同様の機能をもつツールもあるのだが、それがnpmと名前が似ているもんで、さらに混乱。

結局、naveは今回スキップ。
npmをgitで入れることにした。
$ git clone http://github.com/isaacs/npm.git
 $ cd npm
 $ sudo make install
! [ -d .git ] || git submodule update --init
bash: git: command not found
make: *** [submodules] エラー 127
失敗。「modulesが見つからないです」と叫ばれる。gitコマンドを調べてみる。
$ git submodule
-3d509ef6a503703e357f2ffa1df05e4f09a43bba node_modules/abbrev
-8b4395bf931a9b0d27fbb24d6f41d2a1cdbb61cb node_modules/nopt
-6e8da86d6a391f4a001971209cf1f4a66d060c4e node_modules/semver
なんだ。パッケージがないのか!と勝った気になって
$ git submodule init
$ git submodule update
$ git submodule
 3d509ef6a503703e357f2ffa1df05e4f09a43bba node_modules/abbrev (v1.0.2-3-g3d509ef)
 8b4395bf931a9b0d27fbb24d6f41d2a1cdbb61cb node_modules/nopt (v1.0.4)
 6e8da86d6a391f4a001971209cf1f4a66d060c4e node_modules/semver (v1.0.5)
よし入った。これで $ sudo make install が入らない?! 似たようなエラー。
それでは、本家npmの指示に従い
$ sudo node ./cli.js install -g
 $ npm -v
 1.0.6
これで、入ったっぽい。
良し。それでは、パッケージを試しに入れてみる。
$ npm install socket.io
 $ npm ls
 (empty)
ん? ない。そして、カレントディレクトリにnpm_modulesとか出来ている。その中にsocket.ioが入っている。なんじゃ?
いろいろ、やったが不明なので、npmをアンインストールすることに。
$ sudo npm uninstall npm -g
/usr/bin/env: node: そのようなファイルやディレクトリはありません
ええ!? これは、調べて visudoコマンドで、sudo時の環境変数リセット停止を設定。

Defaults    env_reset
Defaluts:%wheel !env_reset
Defaults    env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR \
                        LS_COLORS MAIL PS1 PS2 QTDIR USERNAME \
                        LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION \
                        LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC \
                        LC_PAPER LC_TELEPHONE LC_TIME LC_ALL LANGUAGE LINGUAS \
                        _XKB_CHARSET XAUTHORITY"
どうも、原因は
  • sudo実行時のnodeへのパスが通っていなかったこと
  • npmのインストール先は、/usr/localらしいが、此処には userレベルでのインストール権限がない
ことのようだ。npmの思想として、ローカルにインストールすることを前提としているらしい。恐らく、セキュリティの関係だと思うが。
結局、以下のサイトに従い、インストールした。
http://havelog.ayumusato.com/develop/javascript/e210-install_nodejs_and_npm.html

$ sudo chown -R $USER /usr/local
 $ curl http://npmjs.org/install.sh | sh
 $ sudo chown -R root /usr/local
 $ sudo npm install socket.io 
 $ sudo npm install express # 注: この前にnode.jsのバージョンを上げている
 $ npm ls
/home/baker
├─┬ express@2.3.4 
│ ├── connect@1.4.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
└── socket.io@0.6.17 
気持ちが悪いのは、node_modulesフォルダが、ユーザのホームディレクトリに出来ていること。
早速、coffee script をインストールしてみた。
$ npm install coffee-script
$ coffee -v
-bash: coffee: command not found
$ npm ls
/home/baker
├── coffee-script@1.1.1 
├─┬ express@2.3.4 
│ ├── connect@1.4.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
└── socket.io@0.6.17 
また変だ! 大体、プログラム本体は何処にいったのか?
結局、私が作ったnode, npm の環境はグローバルなので、
  • sudo による実行
  • -g オプションは必須
  • その結果 /usr/local/bin/ に、コマンドがインストールされる(リンク)
以下、インストールのやり直し
$ sudo npm install -g coffee-script
/usr/local/bin/cake -> /usr/local/lib/node_modules/coffee-script/bin/cake
/usr/local/bin/coffee -> /usr/local/lib/node_modules/coffee-script/bin/coffee
coffee-script@1.1.1 /usr/local/lib/node_modules/coffee-script
$ sudo npm install -g express
/usr/local/bin/express -> /usr/local/lib/node_modules/express/bin/express
connect@1.4.1 /usr/local/lib/node_modules/express/node_modules/connect
mime@1.2.2 /usr/local/lib/node_modules/express/node_modules/mime
qs@0.1.0 /usr/local/lib/node_modules/express/node_modules/qs
express@2.3.4 /usr/local/lib/node_modules/express
$ sudo npm -g install socket.io
socket.io@0.6.18 /usr/local/lib/node_modules/socket.io

$ npm uninstall express
$ npm uninstall socket.io
$ npm uninstall coffee-script

$ npm ls
/home/baker
(empty)

$ sudo npm -g ls
/usr/local/lib
├── coffee-script@1.1.1 
├─┬ express@2.3.4 
│ ├── connect@1.4.1 
│ ├── mime@1.2.2 
│ └── qs@0.1.0 
├─┬ npm@1.0.6 
│ ├── abbrev@1.0.3 
│ ├── node-uuid@1.1.0 
│ ├── nopt@1.0.4 
│ └── semver@1.0.5 
└── socket.io@0.6.18 
(追記)
あとで調べてみたところでは、.npmrcにパスを書き、あくまでローカルで実行する手もあったようだし、それが推薦のようだ。
実際に、npm install でローカルにインストールされたコマンドは、node_modules/.binにインストールされていたようで、そこにパスを通しても解決できたらしい。
散々な結果です。

2011年5月14日土曜日

チェックディジットを計算してみる

JANコードの末桁は、チェックデジットと呼ばれる読み取り防止のための数字だ。
計算方法は、モジュラス10 ウエイト3-1 (一括) と呼ばれる。
アルゴリズム名がmodulus10で、奇数桁計3倍、偶数桁 1倍、足し方が一括?だから!
JANバーコードのアルゴリズムは、末尾から奇数桁に3、偶数桁に1を掛けてから単純に足し算し、結果を10で割った余り(剰余)を求め、10からその剰余を引いた値をチェックディジットとして用いている。
例えば「49722536」というJAN8バーコードを例にすると、末尾6がチェックディジットである。この6はどう求められているかというと、次のように求めているわけである。
奇数桁の算出(4+7+2+3)×3→48
偶数桁の加算48+(9+2+5)→64
10から(2)の10の剰余を引く10-4→6
もって、ディジット6は正当であることが求められた。

rubyで書いてみた。
# check_digit.rb 
# check digit / modulus10 wait 3-1 (MOD 10)
# for JAN (EAN), ISBN-13
# usage : $ check_digit.rb 

exit unless ARGV.size == 1
num = ARGV.shift # チェックデジットを取得したい数字をセット
exit unless [7,12].include?(num.size) # JAN(EAN)は、8桁,13桁のいずれか。C/D分を減らす。

pos = total_even = total_odd = 0 # even 偶数、odd 奇数
# C/D分の桁を補完し、文字列を逆にして桁を1桁目から取る。split(//)で1文字単位に分割。
(num + '0').reverse.split(//).each do |s|
  pos += 1
  # 偶数桁、奇数桁を判断し、それぞれ加算。
  (pos % 2) == 0 ? total_even += s.to_i : total_odd += s.to_i
end
p 10 - (total_even * 3 + total_odd * 1) % 10 # ウエイト付けて、補数にして



実行結果は、以下の通り。
[baker@www work]$ ruby check_digit.rb 4972253
6  # 最終コードは、49722536 
[baker@www work]$ ruby check_digit.rb 491012345064
3  # 最終コードは、4910123450643

2011年5月11日水曜日

さくらVPSにscalaをいれた

scalaをさくらインターネット VPSにインストールしてみた。

$ su -
$ cd /usr/local/src/
$ wget http://www.scala-lang.org/downloads/distrib/files/scala-2.8.1.final.tgz
$ tar xzf scala-2.8.1.final.tgz
$ mv scala-2.8.1.final ../scala
$ exit
$ vim .bashrc

.bashrcにパスを書き込んでおくことにした。

export SCALA_HOME=/usr/local/scala
export PATH=$PATH:$SCALA_HOME/bin

手動でscala用のパスを通して、動作を確認。

[baker@www ~]$ scala
Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_23).
Type in expressions to have them evaluated.
Type :help for more information.

scala> printf("hello world!");
hello world!

全ユーザで利用できるようにしたほうがよいかな。

2011年5月8日日曜日

さくら(VPS)大戦 Redmine 編

さくらインターネットVPSのサーバに、ruby on rails製プロジェクト管理ソフトウエア Redmine をインストールした。過去サーバを設定した時の資料を見なかったこともあり、結構苦戦。

以下、失敗したところを記載しておく。実際の手順や詳細などは、以下のサイトを参考。
Mysql 5.5の評価など
version 5.5へのアップグレード
mysql_upgradeコマンド
さくらサーバへのインストールもろもろ
全般の工程(この方のサイト・デザインは美しい)
全般の工程 (たぶん、一番詳しい)

redmine用のデータベースを作成後
  $ wget http://rubyforge.org/frs/download.php/74722/redmine-1.1.3.tar.gz
  $ tar xzvf redmine-1.1.3.tar.gz 
  $ mv redmine-1.1.3 /var/www/html/redmine
  $ cd /var/www/html/config/
  $ cp database.yml.example database.yml
  $ vim database.yml
  $ rake generate_session_store
  $ rake db:migrate RAILS_ENV=production # 失敗
redmineの1.1.3は、rails 2.3.5で動作している。
  $ vim environment.rb 
  $ gem install rails -v=2.3.5
  $ rake db:migrate RAILS_ENV=production # 失敗
railsのmysqlドライバーは、2.2以降標準ではインストールされない。mysqlの接続に失敗。
  $ yum install mysql-devel
失敗。remi リポジトリからインストールしていた特別なVersionのMysqlだった。
  $ mysql --version
  $ yum install mysql-devel --enablerepo=remi
失敗。i386用のmysqlやmysql-develもアップグレードしようとしている。
さくらのVPSのCentOSは64bit。i386ではなくx86_64のみを指定。
  $ yum install mysql-devel.x86_64 --enablerepo=remi
結果、mysql 5.1 -> 5.5 に変更された。
mysqlドライバーをインストール。
  $ gem install mysql
  $ cd /var/www/html/redmine
  $ rake db:migrate RAILS_ENV=production
失敗。mysqldが起動していない。
  $ /etc/init.d/mysqld start
失敗する。ログをみると、
[ERROR] /usr/libexec/mysqld: unknown variable 'default-character-set=utf8'
とりあえず、/etc/my.cnfの[mysqld]の default-character-set=utf8 をコメントアウト
  $ /etc/init.d/mysqld start
  $ vim /var/log/mysqld.log
起動に成功したが、エラーが一杯でている。mysql_upgradeをやる。
  $ mysql_upgrade -p
  $ /etc/init.d/mysqld restart
logでは問題ないようだ。
mysql5.5では、文字列指定のオプションが変更されている。
サーバー側 --character-set-server
クライアント側 --default-character-set (今まで通り)
よって、/etc/my.cnf [mysqld] 内の指定を charset-set-server=utf8 と書き換える。
Redmineのスキーマ作成、初期データ作成(途中で、jaと指定)
  $ vim /etc/my.cnf
  $ /etc/init.d/mysqld restart
  $ rake db:migrate RAILS_ENV=production
  $ rake redmine:load_default_data RAILS_ENV=production
メールも設定
  $ cp config/email.yml.example config/email.yml
  $ vim config/email.yml
さくらのドメインサービスにて、redmine.xxxxをバーチャルホストとして指定
passenger用にヴァーチャルホスト設定を追加
  $ vim /etc/httpd/conf.d/vhost.conf 
  $ /etc/init.d/httpd restart

結局、過去の更新、設定記録を見なかった事が敗因。PHP5.3 をインストールするために、CentOSのデフォルトmysqlのVersino 5.0.x ではなく、5.1.x を入れた時の記録。これがあれば、随分と工数削減できたかも。

2011年5月4日水曜日

Actorをさわってみる

最近、システム障害で並列処理について考える機会があった。並列処理といえば、気になっていたscalaのActorについて勉強してみる。
参考にしたサイト
アクターで楽々並行プログラミング
Scalaの平行性を掘り下げる

復習も兼ねて、例にあった簡単なコードをコピーしてみる。
このプログラムでは、文字列を5回しゃべるアクターが2つ立ち上がり、お互いに関係なくしゃべったら終了します。
import scala.actors.Actor   // Actorトレイトをインポート

class Printer(val msg : String) extends Actor { // Actorを継承したPrinterクラス
  def act () { // Actorクラスの場合、act()メソッド必須。これを実行する。
    for(i <- 1 to 5) { // scalaのfor構文
      println(msg)
      Thread.sleep(3)
    }
  }
}

object App { // 実行の主体
  def main (args : Array[String]) {
    (new Printer("abcdefg")).start // Printerクラスをインスタンスにして実行させる 
    (new Printer("hijklmn")).start // Actorではstartメソッドが実行されるとact()が実施される
  }
}
Scalaの"思い出し”をメモ。
  • トレイトは、rubyのモジュールみたいなもの
  • objectはScalaではシングルトンパターンの実装。クラスのインスタンスであり定数ではない。
さて、早速実行してみました。
scala-example$ vim actor_test1.scala     # ソースを作成
scala-example$ scalac actor_test1.scala  # コンパイル
scala-example$ ls -altr                  # クラスファイルが2つ出来ている。
-rw-r--r--   1 baker  staff    276  5  4 15:36 actor_test1.scala
-rw-r--r--   1 baker  staff  14692  5  4 15:38 Printer.class
-rw-r--r--   1 baker  staff   1327  5  4 15:38 Printer$$anonfun$act$1.class
-rw-r--r--   1 baker  staff    604  5  4 15:38 App.class
-rw-r--r--   1 baker  staff    649  5  4 15:38 App$.class
drwxr-xr-x  14 baker  staff    476  5  4 15:56 .
scala-example$ scala -cp . App           # クラスを指定して実行。クラスパスはカレント。
abcdefg
hijklmn
abcdefg
hijklmn
abcdefg
hijklmn
abcdefg
hijklmn
hijklmn
abcdefg
classファイルが複数出来るのは知らなかった。また、この中間ファイルみたいな$$がついたファイルはなんだろう。これを削除すると実行がうまくいかないので、コンパイルされた実態(中間コード)なのかな?

2011年4月30日土曜日

ベジェ曲線とモンスター


Processingで描いた、かわいいモンスター達のサイト
企画良し、アイディア良し、プログラムサイズも小さくて勉強になる。

この中から、BenjaminさんのモンスタープログラムWorm Monstersのソースをお勉強。

ムーミンに出てきたニョロニョロみたいです。

修正したプログラムの実行結果は、ここ
ちょっと修正したニョロニョロ

以下、プログラムの解析。右目と左目を別々に描くのは冗長かな。
Monster [] monster; //モンスター達を扱う広域変数

void setup(){ //初期化
  size(500, 300); //キャンパスサイズ
  frameRate(25); //フレームレート設定、一秒間の描写回数、drow()の実行回数
  smooth(); //描写を滑らかにする
  
  monster = new Monster[20]; //モンスターの数を決定
  int colorOfMonster = monster.length * 5;
  for (int i=0; i < monster.length; i++){
    monster[i]= new Monster(); //新オブジェクト
    monster[i].monColor = colorOfMonster; //色プロパティを設定
    colorOfMonster -=5; //次のカラーを決定、最後は0になる?
  }
}
  
void draw(){
  background(255); //背景色 255 = 白
  fill(0); //(次の図形を)黒で埋める
  rect(0, height-50, width, 50);
  for(int i=0; i < monster.length; i++){
    monster[i].drawMonstaer(abs(monster[i].posX - mouseX)/10, int(random(0,40)));
  }
}

class Monster {
  int posX = int(random(0, width));
  int posY = height-50;
  int monH = int(random(1,5));
  int monW = int(random(30,50));
  int deform = 30; // ゆがみ
  int monColor;

  void drawMonster(float vH, int closeEys){
    if(vH > 4){
      fill(monColor);
      beginShape(); //ここから多角形、ベジェ曲線による図形を開始
      vertex(posX - monW/2, posY); //基点。ベジェ曲線の最初のアンカーポイント
      //ベジェ曲線を描く。コントロールポイント1,コントロールポイント2,最終アンカー
      bezierVertex(posX - monW/2 + deform, posY, posX - deform, posY - (monH * vH), posX, posY - (monH * vH));
      bezierVertex(posX + deform, posY - (monH * vH), posX + monW/2 - deform, posY, posX + monW/2, posY);
      endShape(); //多角形 終了
    }
    //ある程度高いやつで、目を閉じているやつは、目を描く(まばたき)
    if(vH > 11 && closeEyes > 0){
      //右目
      fill(255); //白
      noStroke(); //アウトラインは描かない
      ellipse(posX - 3, posY - (monH * vH)+10, 8, 8); //目
      fill(0); //黒
      pushMatrix(); //図形を配置
      translate(posX - 3, posY - (monH * vH) + 10); //図形を移動
      //マウスの方向?に座標を回転させる
      rotate(atan2(mouseY - (posY - (monH * vH) + 10), mouseX - posX - 3));
      ellipse(1, 0, 2, 2); //瞳を描き
      popMatrix(); //(元座標に戻って)瞳図形を配置。
      //左目
      fill(255);
      ellipse(posX + 3, posY - (monH * vH) + 10, 8,  8);
      fill(0);
      pushMatrix();
      translate(posX + 3, posY - (monH * vH) + 10);
      rotate(atan2(mouseY - (posY - (monH * vH) + 10), mouseX - posX + 3));
      ellipse(1, 0, 2, 2);
      popMatrix();
    }
  }
}

ベジェー曲線については、ここを参考に理解しました。
http://musashi.or.tv/fontguide_doc3.htm

2011年4月29日金曜日

PeasyCamのサンプルを試してみた

PeasyCamは、カメラワークを実現するためのライブラリのようだ。
これを導入すると、視点の移動や拡大、縮小、回転といった操作を実現できる。操作方法は
left-drag to rotate
mouse wheel, or right-drag up and down to zoom
middle-drag (cmd-left-drag on mac) to pan
double-click to reset
以前、OpenGLで回転、縮小、視点移動のプログラム課題をこなしたことがあるが、随分と面倒だったことを記憶している。

OSXのProcessing環境で、このライブラリのインストールに手間がかかった。マニュアルによれば、librariesの中にダウンロードし解凍したものをセットすれば良いと書かれていたが、うまく動作しない。「ライブラリが見つかりません」と警告がでる。
Processingのapp(OSXの場合、実はディレクトリ)を開き、その中のLibrariesディレクトリ、標準ライブラリと一緒の場所に入れた。

PeasyCamのサイトに掲載されているサンプルを動かしてみました。
PeasyCam サンプル
import peasy.*; //ライブラリを取込

PeasyCam cam;   //カメラワーク操作のためのオブジェクトを格納する変数を定義

void setup() { //初期化
  size(200,200,P3D); // スクリーンのサイズ x, y とP3D(高速3次元描画ライブラリ)を指定
  cam = new PeasyCam(this, 100); // カメラワーク操作オブジェクト。親オブジェクトと距離?を指定
  cam.setMinimumDistance(50); // カメラの最小距離定義
  cam.setMaximumDistance(500); // カメラの最大距離定義
}
void draw() { // loopするメソッド
  rotateX(-.5); // X軸 回転する
  rotateY(-.5); // Y軸 回転する
  background(0); // 背景色 0は?
  fill(255,0,0); // (背景を)塗りつぶす
  box(30); // キューブを用意
  pushMatrix(); // キューブ配置(Push)
  translate(0,0,20); // 座標を移動
  fill(0,0,255); // (キューブを)塗りつぶす
  box(5); // 小さなキューブを用意
  popMatrix(); // 小さなキューブを配置
}

絶対座標、相対座標 あるいは現在座標

popMatrix(), pushMatrix()が使われているが、その使い方についてのメモ。Push()とPop()
現在の変換マトリクスをマトリックススタックへポップする。push()とpop()を理解するには、まずマトリックススタック理論を理解しなければいけない。push() 機能は、今の座標をスタックに保存して、pop()は前の座標を復元する。 push() とpop()は、その他の移動メソッドとの組み合わせにおいて使われる。また、移動する領域をコントロールするのに組み込まれる。

2011年4月24日日曜日

Processing 再始動

(2011.5.11 更新)
iframeでjava appletを表示させていたが、重いのでスクリーンショットに変更。動くやつはリンクをたどってください)
Thomas Diewald さんの3D gravity gridは惑星軌道シミュレーションかな。太陽みたいな重力場が複数あり、その周りを惑星が回る。重力の大きさは格子の落ち込みで表現される。
シミュレーションとして合っているかは兎も角、恰好良い。

開発言語はProcessing。ビジュアルな表現に優れたjava系の言語だ。最近はネットワーク機能や外部組み込み機器の操作機能も注目され、gainerとかハードウエア制御環境にも利用されている。

昔、コピーしたProcessingサンプルプログラムを公開しているのでProcessingの雰囲気をつかんでみてください。
Processing:sample:blocks

早速、実行してみることにした。Processingは、過去にversion1.1をインストールしていたので、ソースをダウンロードしてきて、メインコードらしい "gravity_grid_v1.pde"をダブルクリックしてみた。なんか、「フォルダをつくるけど良いか」とか尋ねるので、良いと応える。なんかProcessingのIDEも立ち上がって、ソースを読み込んだようなので、早速 runしてみたが失敗。

結局、以下の調整などを行い、無事 runできました。
  • IDEを起動させて、全ソースの入ったフォルダをsketchbookフォルダとして指定
  • カメラワークを実現するPeasyComライブラリを追加する
  • フォントデータを作成する IDEのメニューから作成
Processingでは、プログラム一式をsketchbookと呼びます。projectに対応する言葉かな。
PeasyComライブラリの導入はちょっと大変だったので、次回の記事で :-p

2011年4月23日土曜日

新しいブログを始める

プログラムの勉強の記録を残しておきたく、ブログを始めることにした。日常のことは別のブログがあるので、ここは技術オンリの予定。

bloggerの用意したテンプレートには良いものが無かったので、bTempleteからシンプルでコードを書いたときに見栄えがしそうなものを選んで、テンプレートファイルをアップした。
調整したことは次の点。
  • id=topbanner のタグ内のimgタグを削除。
  • Google adsenseなどの不要なガジェットを削除。
  • ソースコードをハイライト表示してくれるwidgitの導入。
  • bloggerの共有ボタンが見えない点を修正。テンプレートをいじりまくった後、ここを参照して修正
  • (2011/5/8) IEやFirefoxでは画像が潰れることが判明。理由がわからないので標準のテンプレートに変更

  • (2011/5/8) preタグのborderやpaddingがIEやFirefoxでは上手く行かない。試行錯誤で修正

まだ、いろいろと調整しなければいけないし、リンク切れもあるが、徐々に修正していくことにする。まずは Let's show time !