ubuntuとrails3

rails3をやってみる

WEB+DB vol. 58はrails3特集。

正直rubyも本を読んだことある程度、railsは適当なチュートリアルにそってやって
よくわかってるようなわかってないような。
そんなレベル。

ついでに
rubyも1.9.2さわっちゃえってことで
ruby1.9.2とrails3との格闘を書いておきます。

ruby1.9.2インストール

たまにしか触らないため、パッケージ管理してくれたほうがありがたいと思ったんだが、
rails3もruby1.9.2もなし。
もともとaptituderuby1.8.7インストール済み。

とりあえず自分でmakeするのはさける方向で。
そこでRVMを使う。
All about Ruby on Rails & Data recovery software
インストールは
All about Ruby on Rails & Data recovery software
にある、1. GitHub Repository (recommended)で。

$ rvm install ruby-1.9.2
$ rvm use 1.9.2

これでruby-1.9.2の利用が可能。
(注)ターミナルからぬけるとデフォルトのrubyが元々インストールされているruby-1.8.7になってしまうので毎回useしてやる必要がある?

rails3インストール

RubyGem1.3.7以上必要。
これもaptitudeで管理されているのは1.3.5。
これもすでにaptitudeでインストール済み。

このあたりでrvmでgemsをインストールしようとしてみたり、rubyが1.8系にもどっていたりで、
いったりきたり、試行錯誤しているので、おかしくなっているんですが、
おそらく

$ gem update --system

これで1.3.7になるはず。。。

次にrailsのインストール。
出発進行! Rails 3 の紹介
ここにある

$ gem install arel --pre
$ gem install rails --pre

これでrails3がインストールされる。

あとはチュートリアルにそって

詳細はWEB+DBにのってるので割愛しますが、
やったことは

$ rails new blog_app
$ rails g scaffold post title:string body:text
$ rake db:migrate
$ rake
$ rails s

てな感じでとりあえず無事にポストできたのであとは問題なく動くかなあと思ったり。
とりあえず時間がなくなり、今日はここまで。

その他こけたこと

続きを読む

urlに+(plus)とapacheのrewriteRule

webサイトを開発していると、
SEOとか静的URLとか
リソース指向だとか
URIをどうするかいろいろ設計すると思います。

なんらかのフレームワークに乗っかっている場合、
そのフレームワークのルールのまんまいくときもあるとおもいます。

ただその場合、コントローラだのアクションだの名前がついてしまいますよね。

それ以外だとapacheのrewriteRuleを用いて、
URIを定義するかと思います。



で前置きはここまでにして、
http://delicious.com/toshifumi_tegu/apache+expires
こういうdeliciousみたいなQUERYをプラスでつなぐような
URIをつくらないといけなくなったときに
apacheのrewriteRuleをつかって
はまったことをまとめておきます。


そもそもプラスってURI的には「?」などと同じ予約文字でした。

出典 http://www.studyinghttp.net/cgi-bin/rfc.cgi?3986#Sec2.2

そりゃそうですよね。phpでurlencodeすると空白は+に変わります。(rawurlencodeなら%20ですが)

だから別にphp側で空白で取れようが、+で取れようが、どっちでもいいはずなんです。

しかしQUERYに+が含まれる場合(「C++」とか)、この場合区別できないといけません。
そこで諸所のサイトでは区切り文字ではない+はエンコードして渡すということになっていたりします。

で、別にapacheのリライトルールを使わなければ、普通にそれを区別して取得することができました。


でもクールなURIにはならないので

以下のようなルールを書くかと思います。(今回使用したフレームワークの場合)

^/path-to/([^/]+)$    /var/www/index.php?controller=foo&action=bar&query=$1

これでいいような感じがするんですが、
「A+」(エンコードするとA%2B)と「B」をqueryに渡そうとすると
http://domain/path-to/A%2B+B
$_GET['query']の中には「A B」とスペース二個に変換されています。
$_SERVER['REQUEST_URI']内では「A++B」として入っています。
なくなるか区別できなくなるか。

apacheのRewriteLogを見てみると
rewriteされている時点で$1はA++Bになっています。



こりゃ困った。簡単にできるような気がしていたのに全然あかんやんっていう。


そこで発想の転換

以下の作者である先輩がRwriteしなきゃいいじゃんと教えてくれました。
http://freestyle.xrea.jp/blog/article.php?id=20

種々のフレームワークだと上記のように引数がPATHフォーマットでも大丈夫らしいです。

今回利用していたフレームワークはこちらには対応していなかったため、
対応するように変更。

1.まずエントリーポイントのindex.phpをpatht-toってファイル名でコピー
cp /www/var/index.php  /www/var/path-to
2. apache.htaccessを以下のように変更
#^/path-to/([^/]+)$    /var/www/index.php?controller=foo&action=bar&query=$1

<Directory /var/www>
    <FilesMatch "^path-to$">
        ForceType application/x-httpd-php 
    </FilesMatch>
</Directory>
3. さきほどコピーしたファイルpath-to内で
<?php
$_GET['controller'] = 'foo';
$_GET['action'] = 'bar';
$_GET['query'] = explode('+', str_replace('/path-to/', '', $_SERVER['REQUEST_URI']));

//以下通常のフレームワークのエントリーポイントと同じ

このようにすることによって意図したqueryが得れます。


ただし$_SERVER['REQUEST_URI']はデコードされていない

よってqueryもデコードされておらず、自分でデコードする必要があります。
デコードした場合、
http://www.php.net/manual/ja/filter.filters.sanitize.php
フィルターなどを利用して普段はフィルターしていても
デコードされていないため、タグなども除去されていません。
自分でデコードしてあやまってscriptタグなどを表示しないよう
再度htmlspecialcharsを実行するなどして
http://php.net/manual/ja/function.htmlspecialchars.php
セキュリティ面に考慮する必要があります。


ということで上記のような対応で、deliciousのタグ検索みたいなURIのページが生成できました。

iphone safariとYOLP地図

先日2010/03/30にYJDNよりYOLPが公開された。
Yahoo! Open Local Platformの略
YOLP(地図):Yahoo! JavaScriptマップAPI - Yahoo!デベロッパーネットワーク

ということで早速使ってみる。

とりあえず地図を表示するに載ってるサンプルコードをこぴぺ。

地図はでました。iphoneでも地図が出ますし、地図をスクロールできます。


しかしマルチタッチイベントで縮尺をかえることはできませんでした。

そこで実装してみることにする。

マルチタッチイベントに関しては
Resources - Safari - Apple Developer
こちらのappleのリファレンスを参照。

要するに
gesture系のイベントを使えばマルチタッチイベントを拾うことができる。

縮尺の拡大縮小を判別するのにはevent.scaleを用いる。
縮小動作を行った場合、scaleが1未満。
拡大動作を行った場合、scaleが1以上。

それだけじゃおもしろくないので
前エントリーのwatchPositionを使ってロギングしてみる。

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
    </head>
    <body style="width:320px">
        <div id="map" style="width:320px; height:480px"></div>
    </body>
    <script type="text/javascript" charset="utf-8" src="http://js.api.olp.yahooapis.jp/OpenLocalPlatform/V1/jsapi?appid=【APPID】"></script>

  <script type="text/javascript">
//mapのインスタンス生成
      var ymap = new Y.Map("map");
     window.onload = function() {
        var z=15;
//mapを描画
        ymap.drawMap( 
new Y.LatLng(35.662484, 139.734222),
 z , Y.LayerSetId.NORMAL );
//現在地の緯度経度を取得
        var watchId = navigator.geolocation.watchPosition(function(pos){
                if (pos.coords.accuracy < 100) {
//markerを作成
                var mark = new Y.Marker(
                    new Y.LatLng(pos.coords.latitude,pos.coords.longitude)
                    );
//地図にmarkerをセット
                ymap.addFeature(mark);
                }
                },
                function(error){},
                {
enableHighAccuracy:true,
timeout:1000,
maximumAge:0
});
     }
function gestureEnd(event){
    event.preventDefault();
   // var center=ymap.getCenter();
    var scale = event.scale;
    if(event.scale < 1.0) {
        //zoomout
        ymap.zoomOut(null,true);
    } else {
        //zoomin
        ymap.zoomIn(null,true);
    }
}
var mapDom = document.getElementById('map');
mapDom.addEventListener("gestureend", gestureEnd, false);
    </script>
</html>

ジェスチャーイベントで欲しいのは、拡大なのか縮小なのかだけなので

mapDom.addEventListener("gestureend", gestureEnd, false);

ジェスチャーの終了イベントを用いる。

実行結果をお見せすると
自宅がばれてしまうので、控えますが、
家の周りを歩いた結果、それなりの精度でマーカーが軌跡を描きますし、
地図の拡大、縮小も二本指の拡大、縮小ジェスチャーに応じて、動作しました。

これだけできるとiphoneアプリじゃなくても
ある程度のことはできそうですね。

geolocationでの位置情報取得

iphone safariやfirefox3.5以上で、javascriptを用いて位置情報が取得できる。
http://dev.w3.org/geo/api/spec-source.html

簡単に、getCurrentPositionで位置情報が取得できるのだが
iphoneでやると、なんとも精度が悪い。
一発の取得精度に依存する。
accuracyをみると700とかとんでもない値がかえってくる。
おそらく基地局依存?

そこでwatchPositionをつかって連続して取得し、一定精度になったら取得をやめる実装を行う。

こちらがgetCurrentPosition

navigator.geolocation.getCurrentPosition(function(pos){
//pos.coords.latitude, pos.coords.longitudeに緯度経度が入ってる
},
function(error){},
{enableHighAccuracy:true,timeout:1000,maximumAge:0}
);

watchPositionを使って実装

var wathId;
watchId = navigator.geolocation.watchPosition(function(pos){
//任意の精度以下になったら取得をやめる
if(pos.coords.accuracy < 300){
navigator.geolocation.clearWatch(watchId);
},
function(error){},
{enableHighAccuracy:true,timeout:1000,maximumAge:0}
);

これで満足いく精度が得られる。
ただ任意の精度以下にならない場合の処理時間でtimeoutするなどの工夫が必要。

サンプルソース

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>untitled</title>
</head>
<body>
<script type="text/javascript">
function getPos(){
	navigator.geolocation.getCurrentPosition(sFunc,
fFunc,
{enableHighAccuracy:true,timeout:1000,maximumAge:0} );
}
var sFunc=function(pos){
	alert(pos.coords.accuracy);
	if(pos.coords.accuracy>100){
		getPos();
	}
		
}
function display(pos,ptime){
	var a = document.getElementById("test");
	var b=["<ul>",
	"<li>time: ",ptime/1000,"</li>",
	"<li>lat : ",pos.coords.latitude,"</li>",
	"<li>lon: ",pos.coords.longitude,"</li>",
	"<li>accuracy: ",pos.coords.accuracy,"</li>",
	"</ul>"];
	a.innerHTML = b.join('');	
}
var fFunc=function(e) {
	
}
var watchId;
var startTime;
var currentTime;
function watchPos(){
    startTime = new Date();
	watchId = navigator.geolocation.watchPosition(function(pos){
            currentTime = new Date();
            var processingTime = currentTime - startTime;
		display(pos,processingTime);
        if(pos.coords.accuracy < 300 || processingTime > 15000){
        navigator.geolocation.clearWatch(watchId);
			}
	},function(){},
{enableHighAccuracy:true,timeout:1000,maximumAge:0});
}
</script>
<input type="button" value="get Position" onclick="getPos();">
<input type="button" value="watch position" onclick="watchPos();">
<div id="test"></div>
</body>
</html>

入門 HTML5

入門 HTML5

JavaScript 第6版

JavaScript 第6版

foreign key制約

子テーブルが管理テーブルにforeign key制約をはっていて、管理テーブルには、
子テーブルからforeign keyが張られている、キーのレコードが複数存在する場合、
InnoDBではその一つレコードのキーが存在する場合でも外部キー制約違反が起こる。

http://dev.mysql.com/doc/refman/5.1/ja/innodb-foreign-key-constraints.html

SQL スタンダードからの逸脱:InnoDB は同じ参照キー値を持つ親テーブル内にいくつかの行があると、外部キーチェック内で同じキー値を持つ別の親行がまるで存在しないかのように機能します。例えば、もし RESTRICT タイプ制約を定義し、いくつかの親行を持つ子行があれば、InnoDB はそれらの親行の削除を許可しません。

あと子テーブルからforeign keyを設定する際、そのforeign keyの名前を宣言しないと、自動で名前がつけられる。
alter tableでforeign keyを削除する際、その名前を指定しないとsqlがエラーになる。

ALTER TABLE `table_name` DROP FOREIGN KEY `key_name`;

key_nameがそれにあたる。
キー名はshow create table文で確認することができる。

シェルスクリプトでカウントアップ

シェルスクリプトでカウントアップして、ループを回すとき

#!/bin/sh
i=0
while [ $i -lt 256 ]
do
    echo $i
    i=`expr $i + 1`
done

とかすると思うのですが、
もっとスマートに書きたい

そこで調べた。
linuxにはseqってコマンドがあるらしい

ただしBSDにはなし
そこでjotを使う。

jot 回数 スタート エンド
#!/bin/sh
for i in `jot 256 0 255`
do
    printf "%02x\n" $i
done

これでカウントアップのループができる。

shellscriptでperl

shellscript内でperlの置換などを走らせると簡単に書けます。
sedとかawkもあると思いますが
perl勉強も兼ねて、perlを使ってみようと。
まずperlオプション

あとに続く文字列をperlのプログラムと解釈

perl -e


標準入力から読み取ったものを処理

perl -n

標準入力から読み取り最後に標準出力

perl -p

ちょっと省略しすぎだと思うんですが、理解があまいため
おまじないのように

perl -pe

って書いてます。

置換は

perl -pe 's/before/after/g'

で置換されたものが出力

ファイルに書き出す場合は

perl -i.back -pe 's/before/after/g' fileName

でバックアップファイルfileName.backを取りつつ、fileNameに書き出し。

shellscriptの変数を渡す場合

HOGE="hogehoge"

cat file.txt | perl -i.back -e "s/xxx/$HOGE/g" file.txt

これで$HOGEが展開され、変数HOGEの値で置換できる。

と簡単にできちゃうので置換の際にはどんどん使っていこうと思った