Perlを中心とした技術系の話などをつらつら

2008年01月25日

adsという文字列を動画のURLに入れてはいけない

ユーザーから動画が見れなくなったという問い合わせが来たので確認すると、自分達の環境では普通に見れる。原因がよくわからない。

詳しく聞いてみるとどうやらIE7 ProというIE 7のAdd-onを入れてから見れなくなったらしい。ということでそれを入れてみたところ見事に動画が再生されなくなった。

さらに詳しく調べていくと、IE7 ProのFlash広告をブロックする機能が、プレイヤーが取得しようとする動画を広告とみなしてブロックしてしまい、動画を取得できないため再生できないことがわかった。

ブロックされていた動画のURLがこれ。

http://www.flipclip.net/ads/logo_animation_a.flv

この広告ブロック、広告かどうかの判定をURLにテキストマッチをかける形で行っていて、「/ads/」という部分が判定にひっかかていたみたい。

原因がわかれば対応は簡単!

動画のURLを変更することで無事問題解決しました。

めでたしめでたし。

Posted by horiuchi at 01:35 | Permalink | Comments (2) | TrackBack (0)

2006年10月03日

巨大なFLVを再生中に他のリンクをクリックしてもなかなか移動できない現象を回避する方法

FlipClipでクリップを見ていると、再生の途中で画面内のリンクをクリックして他のページに移動しようとしても、なかなか移動できなくてイライラすることがあったので、これを回避する方法がないものかと考えていたのですが、今日試した方法が有効だったので紹介します。

この現象は、再生している動画のサイズが大きい場合によく起こる現象で、リンクをクリックしてもブラウザはこの大きな動画の再生に忙しいのか、なかなか画面を切り替えてくれません。

そこで考えたのが、クリックした時に、再生している動画をけしてしまうという方法です。

試した方法は簡単で、リンクなどをクリックしてページが切り替わるタイミングで、再生中のフレームを含むdiv要素のinnnerHTMLを空にしてしまうというものです。

コードのイメージはこんな感じです。

 <script type="text/javascript"><:!--
 Event.observe(window, 'beforeunload', function(){
     $('clipPlayer').innerHTML='';
 });
 --></script>

最初はbeforeunloadではなくunloadで試してみましたが、タイミングが遅いらしく、効果がありませんでした。

beforeunloadはブラウザによっては動かないといった情報をどこかで見たような気がしたのですが、IE, FireFox, Safariでうまく動いたので、互換性に問題なしと判断し、FlipClipでもこの方法を早速採用しました。

画面の移動がスムースになって、いい感じです。

Posted by horiuchi at 23:28 | Permalink | Comments (1) | TrackBack (0)

2006年07月31日

コネクションプーリング都市伝説は正しそう

またちょっと古いねたですが、WEB+DB PRESS vol.33でnipotanさんが書いてたコネクションプーリング都市伝説を読んだ時、ほんとのところどっちが速いのかってのをabでベンチマークをとってみました。

ベンチマークとったときにエントリを書こうと思ってたんですが、モチベーションがあがらず今になっちゃいました。。

以下がベンチマークの結果です。

プーリングを有効にした場合

Server Hostname:        rakuda.localhost
Server Port:            80
Document Path:          /
Document Length:        12323 bytes
Concurrency Level:      20
Time taken for tests:   107.840 seconds
Complete requests:      1000
Failed requests:        0
Broken pipe errors:     0
Total transferred:      12579000 bytes
HTML transferred:       12323000 bytes
Requests per second:    9.27 [#/sec] (mean)
Time per request:       2156.80 [ms] (mean)
Time per request:       107.84 [ms] (mean, across all concurrent requests)
Transfer rate:          116.65 [Kbytes/sec] received
Connnection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0     0    0.0      0     1
Processing:   640  2140  486.9   1999  4674
Waiting:      639  2139  486.9   1999  4673
Total:        640  2140  486.9   1999  4674
ERROR: The median and mean for the initial connection time are more than twice the standard
       deviation apart. These results are NOT reliable.
Percentage of the requests served within a certain time (ms)
  50%   1999
  66%   2066
  75%   2141
  80%   2267
  90%   2650
  95%   3388
  98%   3774
  99%   3941
 100%   4674 (last request)

コネクションプーリングを無効にした場合

Server Hostname:        rakuda.localhost
Server Port:            80
Document Path:          /
Document Length:        12323 bytes
Concurrency Level:      20
Time taken for tests:   104.939 seconds
Complete requests:      1000
Failed requests:        0
Broken pipe errors:     0
Total transferred:      12579000 bytes
HTML transferred:       12323000 bytes
Requests per second:    9.53 [#/sec] (mean)
Time per request:       2098.78 [ms] (mean)
Time per request:       104.94 [ms] (mean, across all concurrent requests)
Transfer rate:          119.87 [Kbytes/sec] received
Connnection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0     0    0.1      0     3
Processing:   353  2081  502.0   1969  5219
Waiting:      351  2081  502.0   1969  5219
Total:        353  2081  502.0   1969  5219
Percentage of the requests served within a certain time (ms)
  50%   1969
  66%   2033
  75%   2117
  80%   2192
  90%   2503
  95%   3011
  98%   4032
  99%   4571
 100%   5219 (last request)

これ以外にもいくつかパスを替えてベンチマークをとったところ、いずれも若干ですがプーリングしないほうが早かったので、現在はプーリングしないほうで運用しています。

Posted by horiuchi at 10:41 | Permalink | Comments (0) | TrackBack (1)

Sledgeのセッション用テーブルはMyISAMにしています。

naoyaさんのエントリーnaoyaのはてなダイアリー - MyISAM vs InnoDBに反応して、久々にエントリを書いてみます。

FlipClipは、すべてMyISAMで運用しているんですが、理由はあんまりなくて、デフォルトでMyISAMだからってのが大きいのと、スナップショットを取るのにmysqlsnapshotに手を加えたものを使っているので、InnoDBにするとそれを使えなくなるというのが主な理由です。それにまだDBがボトルネックだうはーってな状態になったことがないってのもあります。

それでも少し前にサーバのチューニングをそろそろ考えたほうがいいかなーということを考えたことがありまして、

その時にチューニングの候補に考えたものの一つが、Sledgeのセッション用のテーブルをMyISAMからInnoDBに替えてみるということでした。

セッションテーブルはスナップショットを取る必要ないですし、InnoDBにして早くなったらラッキーってのと、Sledgeのセッション用のテーブルは1リクエストで必ず1回書き込みが行われるので、読み込みと書き込みが同程度発生するので、更新系が多い場合はInnoDBがよいというのにあてはまるかなと思ったからです。

そこで実際にSledgeで作ったアプリケーションを使ってセッションテーブルをInnoDBにした場合と、MyISAMにした場合でどれほど性能に差がでるかabを使って簡単にベンチマークしてみました。

以下がabを使って同時10リクエストで合計1000リクエスト実行した際の結果です。

MyISAMの場合

Benchmarking rakuda.localhost (be patient)
Server Software:        Apache/1.3.36
Server Hostname:        rakuda.localhost
Server Port:            80
Document Path:          /
Document Length:        14911 bytes
Concurrency Level:      10
Time taken for tests:   110.009 seconds
Complete requests:      1000
Failed requests:        0
Broken pipe errors:     0
Total transferred:      15167000 bytes
HTML transferred:       14911000 bytes
Requests per second:    9.09 [#/sec] (mean)
Time per request:       1100.09 [ms] (mean)
Time per request:       110.01 [ms] (mean, across all concurrent requests)
Transfer rate:          137.87 [Kbytes/sec] received
Connnection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0     0    0.0      0     1
Processing:   292  1096  223.5   1031  1976
Waiting:      291  1096  223.5   1031  1976
Total:        292  1096  223.5   1031  1976
ERROR: The median and mean for the initial connection time are more than twice the standard
       deviation apart. These results are NOT reliable.
Percentage of the requests served within a certain time (ms)
  50%   1031
  66%   1071
  75%   1109
  80%   1145
  90%   1338
  95%   1753
  98%   1832
  99%   1888
 100%   1976 (last request)

InnoDBの場合

Benchmarking rakuda.localhost (be patient)
Server Software:        Apache/1.3.36
Server Hostname:        rakuda.localhost
Server Port:            80
Document Path:          /
Document Length:        14911 bytes
Concurrency Level:      10
Time taken for tests:   110.730 seconds
Complete requests:      1000
Failed requests:        0
Broken pipe errors:     0
Total transferred:      15167000 bytes
HTML transferred:       14911000 bytes
Requests per second:    9.03 [#/sec] (mean)
Time per request:       1107.30 [ms] (mean)
Time per request:       110.73 [ms] (mean, across all concurrent requests)
Transfer rate:          136.97 [Kbytes/sec] received
Connnection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0     0    0.0      0     0
Processing:   765  1102  273.9   1010  2476
Waiting:      765  1102  273.9   1010  2476
Total:        765  1102  273.9   1010  2476
Percentage of the requests served within a certain time (ms)
  50%   1010
  66%   1059
  75%   1120
  80%   1180
  90%   1466
  95%   1787
  98%   1973
  99%   2198
 100%   2476 (last request)

結果としてMyISAMもInnoDBも速度的にはほとんど変わらなかった(ちょっとMyISAMのほうが速かった)ので、今まで通りMyISAMでいいかという結論に達しました。

Posted by horiuchi at 09:44 | Permalink | Comments (2) | TrackBack (1)

2006年05月23日

JSON::Syckで改行を含むデータをダンプすると改行の後にスペースが2個入る

Ajaxる時のサーバとのデータ交換フォーマットとして、JSONを使う時、perlでサーバ側を実装する際にはJSONとかJSON::Syckというモジュールを使うとperlのデータ構造をJSONフォーマットに変換してくれるので便利です。

昨日これを使って、サーバからデータを取得し、textareaに取得したデータを入れるということをしたところ、改行を含んだテキストだと、改行の後にスペースが2個入ってしまうという現象に遭遇しました。

要はこうなってほしいところが、

こうなってしまうんです。

調べてみると、クライアントで受け取ったJSONデータをevalした段階ですでにスペースを含んでいたので、サーバ側でJSONデータを作成するところに問題がありそうだということがわかりました。

そこで以下のようなスクリプトを書いて、JSON,JSON::SyckがどんなJSONデータを吐き出すか見てみました。

#!/usr/bin/env perl
use strict;
use JSON::Syck;
use JSON;
my $data = {
    key => "foo\nbar\nbaz\n"
};
print "# Dumped by JSON::Syck $JSON::Syck::VERSION:\n";
print JSON::Syck::Dump($data), "\n";
print "# Dumped by JSON $JSON::VERSION:\n";
print objToJson($data), "\n";

結果は以下のとおり。

# Dumped by JSON::Syck 0.12:
{"key":"foo\n\
  bar\n\
  baz\n"}
# Dumped by JSON 1.05:
{"key":"foo\nbar\nbaz\n"}

どうやらJSON::Syckを使ってダンプしたデータは、整形のため、改行の後に、スペースが2個入っているようです。それでこれをJavaScriptでevalするとこのスペースもデータとして含まれてしまうため、上記のような現象が起きたみたいです。

そもそも改行後にスペースが入ってしまうのが問題なのか、スペースが入るのは問題なくて、クライアント側のJavaScriptでのパースに問題があるのかわかってませんが、試した限りだと、IEとFirefoxでスペースつきのデータの扱い方が異なるようで、挙動が変わってしまい、困りました。

とりあえず、サーバ側で改行コードを[BR]とかに変換しておいて、クライアント側でそれを改行コードとか、<br />に変換するという風にして対処しましたが、ちょっと付け焼刃的ですかねー。

Posted by horiuchi at 09:58 | Permalink | Comments (0) | TrackBack (1)

2006年05月21日

OOなJavaScriptの勉強がてら、ボタンの2度押し禁止機能を簡単に実現するライブラリを作ってみました

ちょっと時代に乗り遅れている気もしますが、最近prototype.jsをつかったOOなJavaScriptを書くのにはまっています。

今日はbindとbindAsEbentListenerの勉強がてら、これまたかなり昔話題になった「2度押し禁止ボタン」を実現するコードを書いてみました。

input.js

使い方はこんな感じです。

 // prototype.jsとinput.jsをロードします。
 <script type="text/javascript" src="/path/to/prototype.js"></script>
 <script type="text/javascript" src="/path/to/input.js"></script>
 <script type="text/javascript"><!--
 // windoのロード時に2度押し禁止機能を有効にします。
 Event.observe(window, 'load', function(){
     // ボタンのIDか要素のオブジェクトを渡せばクリックすると押せなくなります。
     new Input.DisableAfterClick('myButton');
 }, false);
  --></script>

ボタンのテキストを変えたい場合は2番目の引数に代替テキストを渡します。

画像の場合は代替画像のURLを渡します。

    new Input.DisableAfterClick('mySubmitButton2', '送信中です');
    new Input.DisableAfterClick('myImageButton2', '/path/to/image2.gif');

一定時間経過後にボタンを再度有効にしたい場合は、3番目の引数に有効にするまでの時間をミリ秒単位で渡します。

    new Input.DisableAfterClick('myButton', '1秒後にまた有効になります', 1000);

サンプルページにサンプルがあります。

また、input.jsの中身はこんな感じです。画像ボタンの扱いはもっといい方法があるのかなーとも思うんですが、おかしなところがあったら突っ込んでください。

var Input = new Object();
Input.DisableAfterClick = Class.create();
Input.DisableAfterClick.prototype = {
    initialize : function(input, alt, timer){
        this.input = $(input);
        this.alt   = alt;
        this.timer = timer;
        this.org   = this.input.type == 'image'
                   ? this.input.src
                   : this.input.value;
        this.stopEvent = this._stopEvent.bindAsEventListener(this);
        Event.observe(this.input, 'click', this.disable.bind(this));
    },
    disable : function() {
        this.input.blur();
        if (this.input.type == 'image'){
            if ( this.alt )
                this.input.src = this.alt;
            Event.observe(this.input, 'click', this.stopEvent);
        } else {
            if ( this.alt )
                this.input.value = this.alt;
            this.input.disabled = true;
        }
        if (this.timer)
            setTimeout(this.enable.bind(this), this.timer);
    },
    enable : function() {
        if (this.input.type == 'image'){
            if ( this.alt )
                this.input.src = this.org;
            Event.stopObserving(this.input, 'click', this.stopEvent);
        } else {
            if ( this.alt )
                this.input.value = this.org;
            this.input.disabled = '';
        }
    },
    _stopEvent : function (evt) {
        evt.stop();
        return false;
    }
};

Posted by horiuchi at 02:37 | Permalink | Comments (3) | TrackBack (1)

2006年03月15日

_blankを使わないで別ウィンドウを開くにはrel="external"を使うのが美しいと思う。

はてなブックマークをみていたら、気になるエントリーを発見。


[戯] target="_blank" を使わないで新しいウィンドウでリンクを開く方法


target="_blank"という書き方がXHTML 1.1 や XHTML Basicに準拠していないので、これらに準拠するようにしつつ、別ウィンドウで開くにはどうすればよいかという話です。

別ウィンドウで開くにはJavaScriptを使えってのが推奨される方法なんですが、onclickを使って定義するのはめんどうということで、この記事では、aタグにclass="popup"という属性を与えておけば、JavaScriptで別ウィンドウを開くということをしています。

この件については友人のHTML、CSSマスターなkawachi君と話したことがあって、そのときは、下のエントリで紹介されているrel="external"という方法を使うのがいいんじゃないかという結論に達しました。


Opening a link in a new window - the valid way


僕は上のエントリを参考に以下のようなjsファイルを用意して使っています。


//external.js
function externalLinks() {
 if (!document.getElementsByTagName) return;
 var anchors = document.getElementsByTagName("a");
 for (var i=0; i<anchors.length; i++) {
   var anchor = anchors[i];
   if (anchor.getAttribute("href") &&
       anchor.getAttribute("rel") == "external")
     anchor.target = "_blank";
 }
}
Event.observe(window,'load', externalLinks, false);

使い方は簡単でこのjsファイルをインクルードするだけです。


<script type="text/javascript" src="/path/to/prototype.js"></script<
<script type="text/javascript" src="/path/to/external.js"></script>

load時にこのファンクションを実行するためにprototype.jsのEvent.observeを使っているため、

prototype.jsもインクルードしています。ここはwindow.onload=externalLinks;に書き換えれば、

prototype.jsは必要ありません。

このファイルをインクルードすると、rel="external"と指定したaタグをクリックすると別ウィンドウで開くようになります。


<a rel="external" href="http://www.google.com/">別ウィンドウで開きます。</a>

rel="external"というこのリンク先は外部のサイトだよという関連性を示す記述に対して、JavaScriptで別ウィンドウで開くんだよと定義してあげるというアプローチが美しいなと思っています。

Posted by horiuchi at 13:02 | Permalink | Comments (16) | TrackBack (1)

2005年09月30日

続・onload時に複数のfunctionを実行するJavaScript

ちょっと前に書いたonload時に複数のfunctionを実行するJavascriptというエントリーへのkoさんのコメントでaddEventListenerというのを初めて知りました。

これは何なんだろうということで、調べてみると、W3C DOM Level2 のイベントモデルでイベントハンドリングを行う際に使う関数だということがわかりました。

もう少し調べてみると、このDOM Level2、FirefoxなどMozilla系のブラウザには実装されているようなのですが、IEには実装されていないため、addEventListenerが使えず、そのかわりに同等の機能をもったattachEventという関数が定義されているそうです。

将来的にはIEもDOM Level2をサポートして、addEventListenerが使えるようになると思いますが、現状では、ブラウザを判定してaddEventListenerを使うか、attachEventを使うかを切り替える必要がありそうです。うーん、めんどくさい。。

なので、ブラウザに依存しない前回書いた方法もまんざら無駄骨ということもなかったかな、と思っていたんですが、昨日友人にイベントの登録はprototype使えば簡単にできるよといわれ、無駄骨だったことが確定しましたw

prototype.jsを使ったonload時に実行するfunctionの登録はこんな感じです。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Event observe sample</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<script type="text/javascript" src="/js/prototype-1.3.1.js"></script>
<script type="text/javascript">
function foo() {alert('foo')}
function bar() {alert('bar')}
function baz() {alert('baz')}
Event.observe(window, 'load', foo, false);
Event.observe(window, 'load', bar, false);
Event.observe(window, 'load', baz, false);
</script>
</head>
<body></body>
</html>

prototype.jsで定義されているEvent.observeを使って関数を登録します。引数は最初に対象となるオブジェクトを渡すところ以外はaddEventListenerと同じです。IE6とFirefox1.0.7で動作することを確認しました。

IEと、Firefoxでは関数の実行順が逆になってましたが、DOM Level2でもイベントの実行順は定義されてないことと、そもそも実行順に結果が依存するような関数はひとつにまとめておくべきだと思うので、これは問題にならなそうです。

prototypeにはこれ以外にもまだまだ知らない便利な関数、クラスがたくさんありそうです。prototype恐るべし。

Posted by horiuchi at 10:37 | Permalink | Comments (1) | TrackBack (0)

2005年09月19日

onload時に複数のfunctionを実行するJavaScript

昨日に引き続きJavaScriptねたです。

ページを表示した時点でJavaScriptを実行したい場合、

window.onload=function(){ alert('called when window is loaded.'); }

のようにwindowオブジェクトのonloadイベントに実行したいfunctionをセットしてやればいいのですが、これだと、オンロード時にひとつのfunctionしか実行できません。

そこで、オンロード時に複数のfunctionを実行できるようなスクリプトを書いてみました。今回のコードはnaoyaさんのprototype.js でデザインパターン - IteratorのエントリにあるIteratorパターンのコードをそのまま借りたリスペクト指向プログラミングになってますw

//multiple_onload.js
var OnloadFunction = Class.create();
OnloadFunction.prototype = {
    initialize : function(func) {
        this.func = func;
    },
    getFunc : function() {
        return this.func;
    }
};
var OnloadFunctions = Class.create();
OnloadFunctions.prototype = {
    initialize : function() {
        this.last = 0;
        this.functions = new Array();
    },
    getFunctionAt : function(index) {
        return this.functions[index];
    },
    appendFunction : function(func) {
        this.functions[this.last] = func;
        this.last++;
    },
    getLength : function() {
        return this.last;
    },
    iterator : function() {
        return new OnloadFunctionIterator(this);
    }
}
var OnloadFunctionIterator = Class.create();
OnloadFunctionIterator.prototype = {
    initialize : function(functions) {
        this.functions = functions;
        this.index = 0;
    },
    hasNext : function () {
        return this.index < this.functions.getLength();
    },
    next : function() {
        return this.functions.getFunctionAt(this.index++);
    }
}
var onloadFunctions = new OnloadFunctions();
function multipleOnload () {
    var it = onloadFunctions.iterator();
    while (it.hasNext()){
        var func = it.next();
        func.func();
    }
}
window.onload = multipleOnload;

使用方法はこんな感じです。

動作させるにはprototype.jsが必要なので最初にインクルードしています。

<html>
<head>
<script type="text/javascript" src="/js/prototype-1.3.1.js" ></script>
<script type="text/javascript" src="/js/multiple_onload.js" ></script>
<script type="text/javascript">
functon foo() {alert('foo');}
functon bar() {alert('bar');}
functon baz() {alert('baz');}
onloadFunctions.appendFunction(new OnloadFunction(foo) ); 
onloadFunctions.appendFunction(new OnloadFunction(bar) ); 
onloadFunctions.appendFunction(new OnloadFunction(baz) ); 
</script>
</head>
<body>
...
</body>
</html>

まず、multiple_onload.jsをインクルードしておきます。そして、定義したfunctionをappendFunctionメソッドを使ってセットしておきます。

これでfoo,bar,bazがオンロード時に順番に実行されます。

Posted by horiuchi at 06:03 | Permalink | Comments (7) | TrackBack (1)

2005年09月18日

画像のスワップをクラス名で制御するJavaScript(プリロード付き)

ちょっとJavaScriptをまじめに勉強しようかと思い始めました。

というわけで、手始めにマウスオーバーすると別画像に切り替わるスクリプトを書いてみました。

マウスオーバーで画像が切り替わるなんて、Dreamweaver使えばJavaScriptを知らなくてもできてしまうんですが、Dreameweaverが吐き出すコードはHTMLタグの中にonload="xxxxxxxx"とか、onmouseover="xxxxx"をやたらと追加して、非常に見づらいのです。

これだとテキストエディタでちょろっとHTMLを編集したい時に大変ですし、きっとGoogleさんもクロールしにくいでしょうから、imgタグに予めスワップ用に設定しておいたクラス名を記述しておけば、マウスオーバー、マウスアウトで画像が切り替わるようにしてみました。

作成したスクリプトはこんな感じです。クラス名から要素を取得するところはprototype.jsから借りてきました。

// swap_image.js
var imagesNormal = new Object();
var imagesHilite = new Object();
function SwapImage (name,normalSrc,hiliteSrc) {
   this.name = name;
   this.normalSrc = normalSrc;
   this.hiliteSrc = hiliteSrc;
}
function setupSwapImages(swapImages) {
    preloadSwapImages(swapImages);
    setupSwapEvent(swapImages);
}

function preloadSwapImages (a) { for (var i=0; i < a.length; i++){ imagesNormal[a[i].name] = new Image(); imagesNormal[a[i].name].src = a[i].normalSrc; imagesHilite[a[i].name] = new Image(); imagesHilite[a[i].name].src = a[i].hiliteSrc; } } function setupSwapEvent (a) { for (var i=0; i<a.length; i++){ var images = getElementsByClassName(a[i].name); for (var j=0; j<images.length; j++){ var image = images[j]; if (image.getAttribute("src")){ image.onmouseover = function () {swapImage( this, "hilite" );} image.onmouseout = function () {swapImage( this, "normal" );} } } } } // 2006-05-15追記 function swapImage(image,type){ if (type=="hilite") { image.src = imagesHilite[image.className].src; } else if (type=="normal") { image.src = imagesNormal[image.className].src; } } // 追記ここまで(こぴぺしわすれ・・。) // copied from prototype.js v1.3.1 // http://prototype.conio.net/ function getElementsByClassName (className) { var children = document.getElementsByTagName('*') || document.all; var elements = new Array(); for (var i = 0; i < children.length; i++) { var child = children[i]; var classNames = child.className.split(' '); for (var j = 0; j < classNames.length; j++) { if (classNames[j] == className) { elements.push(child); break; } } } return elements; }

実際の使用例はこんな感じです。クラス名により画像のスワップを制御するようにすることで、HTMLがすっきりしました。さらにhead要素内のscriptタグを削除してもValidなHTMLのままってところもいいですよね。

<html>
<head>
<script type="text/javascript" src="/js/swap_image.js" ></script>
<script type="text/javascript">
var swapImages = [
new SwapImage('class01','/images/foo.gif', '/images/foo_onmouse.gif'),
new SwapImage('class02','/images/bar.gif', '/images/bar_onmouse.gif'),
new SwapImage('class03','/images/baz.gif', '/images/baz_onmouse.gif')
];
window.onload = function () {setupSwapImages(swapImages);};
</script>
</head>
<body>
<img src="/images/foo.gif" class="class01" />
<img src="/images/bar.gif" class="class02" />
<img src="/images/baz.gif" class="class03" />
</body>
</html>

Posted by horiuchi at 02:38 | Permalink | Comments (2) | TrackBack (0)

2005年08月22日

クリックするとすべて選択されるテキストエリア


GoogleAdsenseのAdsenseコードを表示するテキストエリアがちょっと便利。クリックすると、テキストエリア内のコードがすべて選択された状態になるので、あとは右クリックしてコピーするだけでOK。ちょっとした気遣いだけど、これだけですごく使い勝手がよくなっている気がします。

実際どうやって実現しているのかなとコードを覗いてみると、こんな感じ。

<textarea readonly rows="12" cols="65" onclick="this.focus();this.select()" id="code">

標準関数のfocus()とselect()を使うだけでできてしまうので、非常に簡単ですね。早速まねしよう。

Posted by horiuchi at 16:36 | Permalink | Comments (0) | TrackBack (0)

2005年05月30日

SEPY ActionScript Editor - オープンソースなActionScriptエディタ

SEPY ActionScript Editor: Epic->yoshimax

もうひとつyoshimaxさんのより。

オープンソースなActionScriptエディタSEPY ActionScript Editor というのがyoshimaxさんの周りではやっているらしい。

Posted by horiuchi at 10:36 | Permalink | Comments (0) | TrackBack (0)

FLASHとPHP間のやり取りにはPHPObject

yoshimaxさんのFLASHとのRPC PHPObjectのエントリより。

FlashとPHP間の通信(Flash Remoting)にはPHPObjectを使うのがはやっているらしい。

Posted by horiuchi at 10:31 | Permalink | Comments (0) | TrackBack (0)

Live Journalのバックエンド

qootas.org/blogさんのBlog Hackers Conference 2005のエントリで取り上げられていたInside LiveJournal's Backend

という資料が興味深いです。

前のエントリで書いた画像とか動画のような大きなファイルを扱うにはどうしたらいいかという疑問にたいする解答もこの中からつかめるかも。

perlベースのリバースプロキシロードバランサー兼WebサーバPerlbalは負荷分散に使えるだけでなく、mod_perlと組み合わせてファイル閲覧の認証とかもできるようなので、もう少し詳しく調べてみようかと思います。

Posted by horiuchi at 09:49 | Permalink | Comments (2) | TrackBack (0)

2005年05月26日

Seleniumすごい

Selenium: blog.bulknews.netより

Javascriptを利用したWebアプリケーションのテストツールSeleniumというのが熱いらしいので、ちょっと試してみました。

これちょっとすごいですね。

一番驚いたのが、テストケースの作成が簡単にできること。HTMLのテーブル内にテストの手順を書いていけばそれがそのまま実行可能なテストケースになってしまいます。

このHTML見た目はちょっとしたテスト仕様書なので、そのまま成果物として出すことも可能かもしれません。ただ、内容が英語になるので、ちょっと読みにくくはありますが。。

テストケースを日本語で書けたらほんとそのままテスト仕様書になるなぁと思ってみました。

Posted by horiuchi at 21:30 | Permalink | Comments (0) | TrackBack (2)

2005年05月24日

Prototype.jsのEffectサンプル

オブジェクト指向JavaScriptライブラリPrototypeを試すために作ったページがそのままサンプルになりそうだったので、公開してみます。

Prototype.js - Effectサンプル

イベントを定義するだけでここまでのことが出来るのにちょっと感動しました。

Prototypeいい感じです。

ちょっと気になった点

  • Effect.Fade()とEffect.AppearはFirefoxでみるといい感じだけどIEだとしょぼい
  • Effect.Scaleは動きがもっさりしているような。。
  • sleipnirでEffect.Puff()中にブラウザごと落ちることがしばしば。

Posted by horiuchi at 02:30 | Permalink | Comments (0) | TrackBack (2)

2005年04月26日

郵便番号変換でXMLRPCサーバのベンチ

XML-RPCを使って、郵便番号から住所引っ張ってくるサービスを作る際に、perlとPHPどちらの方がパフォーマンスがでるのか気になったので、ちょっと実験してみました。

perlの方はnaoyaさんのApache::XMLRPC::Lite を使った mod_perl ハンドラによる XML-RPC サーバのエントリを参考にApache::XMLRPC::LiteとClass::DBIを使って実装し、PHPの方はXMLRPCの実装にXML-RPC for PHPにあるライブラリを使い、DBとの連携は組み込みのmysql_connectとかを使って実装してみました。ちなみにPHPはまともに動くコードを初めて書きましたが、意外と書けるものですねw

ベンチマークにはabを使い、同時接続数が1,5,10,20の場合をそれぞれ測定してみました。

ab -n 100 -c 1 -T text/xml -p post.txt -w  http://horiuchi.vcube.net/zipconv/xmlrpc > result.html

以下結果です。

レスポンスを強制的にbase64にした場合を測定したのはSOAP::Liteのドキュメントにそうするとパフォーマンスをあがるというようなことが書かれていたので試してみました。

ですが、結果としてはそれほどパフォーマンスはあがってません。逆に同時アクセス数を大きくするとパフォーマンスが落ちてしまいました。

また、PerlとPHPではそれほどパフォーマンスに違いがないようです。

PHPだと劇的に早いという結果になったらPHPにしようかとも思ったのですが、結局どちらにしても違いはそれほどなさそうなので、得意のPerlで行くことにしました。

Posted by horiuchi at 10:42 | Permalink | Comments (2) | TrackBack (0)

2005年03月20日

画像を使わず角の丸いボックスを表示する

[を] CSSで角を丸くするより

画像を使わないで角の丸いボックスを作れるんですね。

hori-uchi.com

Posted by horiuchi at 02:21 | Permalink | Comments (4) | TrackBack (0)

2005年02月28日

Web標準化Tips

Web標準化Tips - Web標準普及プロジェクト

ブラウザ判定でなくオブジェクト検出を使った方がいいとか、Web標準化に関するTIPSがわかりやすくまとまってます。メモ。

Posted by horiuchi at 10:19 | Permalink | Comments (0) | TrackBack (1)

2004年11月25日

iモード向けサイトでリダイレクト使う際は最後の/に注意

今日iモード向けのコンテンツで、QVGA端末かQQVGA端末かを判別して各端末用のページにリダイレクトするというスクリプトを書いたんですが、リダイレクトする際に必ず「ページが移動しました」のようなメッセージがでてしまうという現象に出くわしました。

今まで同じようなスクリプトを書いたときにはこんな現象が起きたことがなかったので、一瞬とまどいましたが、原因はなんてことなく、Locationで指定した飛び先のURLの最後に/(スラッシュ)がついていなかったからでした。

たとえば、http://hori-uchi.com/qvga/index.htmlにリダイレクトさせたくて、

print "Location: http://hori-uchi.com/qvga";
print "\n\n";

と記述すると、携帯のブラウザではまずhori-uchi.comの/qvgaというファイルを見に来るのですが、そのファイルがないので(ディレクトリなので)、次に/qvga/index.htmlを取得しようと試みます。どうやらこのファイルがないので・・・のあたりで、「ページが移動しました」のようなメッセージを表示するようです。/があるとないとで大違いなんですね。

Posted by horiuchi at 00:26 | Permalink | Comments (2) | TrackBack (0)

2004年10月04日

高品質なWebアプリケーションの開発手法

ここ最近、高品質なWebアプリケーションを効率よく開発するにはどうしたらいいかということを真剣に考えています。というのも、今まで構築してきたシステムにプログラムのバグが立て続けに発生するようになってきたためです。今までのプログラマ個人主義の開発体制の限界がきたのかもしれません。この問題を解決するために、まずはシステムになぜバグが発生したのか、その原因を把握することから始めたいと思います。

ケース1: 単純にテストが足りない

ユーザは時として、プログラマが予測しないようなデータを入力してきますし、XSS、SQLインジェクションなどをねらった悪意あるデータを入力される可能性もあります。それらの想定される様々な入力に対して十分なテストが行われていないと、一見正常に動いているように見えても、後々バグが見つかるということになってしまいます。また、システムのあまり重要でない機能に多いのですが、単純にプログラムのアルゴリズムが間違っていたなんていうはずかしいバグもありました。

ケース2: 長期の継続的な追加開発によるシステムの破綻

何の開発手法も確立していない状態で、数年にわたりシステムを継続的に開発していくと、コードはスパゲティのように複雑に絡み合い、あるバグの修正が別のバグを引き起こすというようなひどい状況になりえます。また、数年前のプロジェクトということもあり、ソースコードの管理がきちんと行われていなかったりして、修正したつもりがデグレードというような恐ろしいことも起こりえます。このケースではクライアントさんとの付き合いも長くなっているので、問題は深刻です。

ケース3: 開発担当者はもういない

古いプロジェクトになるとそのシステムを開発した人間がすでにいなくなっているケースです。バグが発生したときには仕方なく今いる人間が、ソースコードを読み直し、システムの内容を把握、そして修正を行わないとなりません。コーディング規約などが整備されていないため、なれない人間だとソースコードの解読にかなりの時間を要します。そしてこのような非建設的な事柄に貴重な時間を浪費してしまいます。

問題可決には何をすべきか

こうやって、失敗したケースを並べてみると何をしたらいいのか何となく見えてきます。おおざっぱにまとめると以下のような感じでしょうか。

  • テスト方法の整備
  • コーディング規約の整備
  • ドキュメントの整備

ではこれらをどう実践していくかということですが、XP(エクストリーム・プログラミング)という開発手法を応用することで、うまくいきそうな気がします。XPというとペアプログラミング、テストファーストという言葉がすぐ浮かんでくるんじゃないかと思いますが、物事をできるだけシンプルに考えようという姿勢が僕は好きです。また、開発速度が上がることも期待できそうです。開発速度について、はてなのnaoyaさんのWeblogには以下のように書かれています。

はてな はまぞう - ASIN リンク支援ツールの開発裏話 : NDO::Weblog

フレームワークを使って、もう一人のエンジニアとのペアプログラミング。毎朝にスタンディング・ミーティング(立って会議)をして仕様を策定し、二日ちょっとで作り上げました。やろう、と決めてから三日以内にリリースというスピードは、Perl フレームワークと、XP のいいとこ取りをした開発スタイルに依るところが非常に大きいです。

XPとフレームワークを利用して、3日で機能を開発したそうです。他にもXPを導入している会社の話をちらほらと聞きますし、どうやらWebアプリケーション開発にXPが有用なのは確かなようです。

それではXPを使うと先ほどのケースはどのように解決できるでしょうか?

ケース1: 単純にテストが足りない。

これを解決するためにはテスト方法を整備しなければなりません。XPではテストを機能テスト、単体テストの2つに分けて行います。機能テストはユーザ(クライアント)がこのように動いてほしいと思った通りの挙動を示すかのテストで、単体テストはプログラム内の各オブジェクトのメソッドが期待通りに動くかのテストです。前者はテストの性質上、ユーザ側に行ってもらうのが理想です。といってもクライアントによってはWebの知識があまりない場合も多いので現実的にはディレクタがユーザと話し合ってテストケースを決めるという形になるでしょうか。また、後者の単体テストはプログラマの仕事になります。ここで重要なのがテストもプログラミングするという点です。テストスクリプトを作成し、残しておくことで繰り返しテストを行うことができますし、様々な良い副作用も期待できます。これらの2つのテストをきちんと行うことで、バグはグンと減ると考えられます。また、ペアプログラミングを行い、もう一人の人間がテストケースを考えるというようにすれば、さらにバグの比率は減るでしょう。

ケース2: 長期の継続的な追加開発によるシステムの破綻

XPを使うことで、このケースも失敗のリスクを減らすことができます。機能を追加するごとに単体テストを行うことを義務づけます。テストはテストスクリプトとして残っているため、それを毎回実行することで、ある機能を実装したおかげで別のところにバグが生じた場合も事前にそれを見つけることができるようになります。また、CVSなどのリソース管理システムを導入し、リソースを管理することでデグレードの危険を回避します。

ケース3: 開発担当者はもういない

これを解決するにはドキュメントの整備が必要ですが、得てしてプログラマはドキュメントを書くのが嫌いです(偏見?)。XPでは単体テスト用に書いたテストスクリプトがそのままドキュメントになり得ます。メソッドがどのような振る舞いを期待されているかがテストをみればわかります。プログラマにとっては文章でまとめられたドキュメントよりもこちらの方がシステムを理解するのに役立つのではないでしょうか。また、もういない誰かが書いたテストケースをすぐに実行できます。自分の修正が正しいのかもそれで確認ができます。自分のせいでシステムが壊れていないかも確認できます。テストスクリプトを書くということにはこのような副作用もあるんです。

XPをいきなりどかんとすべて導入というのはちょっと無理があるので、まずはコーディング規約の整備、単体テストの義務づけあたりからやっていこうかと思います。

Posted by horiuchi at 00:16 | Permalink | Comments (0) | TrackBack (0)

2004年06月10日

Webmaster Toolkit

NDO::Weblog: SEO 用 mod_rewrite ルールを生成する Webmaster Toolkit

NDO::Weblogで紹介されていたWebmaster Toolkitというサイト。SEO対策ツールやHTMLユーティリティーなど役に立ちそうなツールがたくさんありました。非常に便利そうなのでメモ。

Posted by horiuchi at 10:05 | Permalink | Comments (0) | TrackBack (0)

2004年05月12日

IEでキャッシュに関係なく画像を取得し直す方法

Webページの更新作業を行い、更新内容をお客さんに確認してもらうというような時に、 画像が更新されていないとクレームがくることがたまーにあったりするんですが、 たいていはブラウザのキャッシュが問題だったりするわけです。
今までそんなときは更新ボタンを何度か押してみてくださいと アドバイスしていたんですが、これが確実な方法ではないんですよね。
そんなことを友人に話したら、さらっとIEでキャッシュに関係なく画像を取得し直す方法を教えてくれました。 ずばりCtl+F5だそうです。
これからは自信を持って「Ctl+F5をおしてみてください」といえます(笑)

Posted by horiuchi at 09:19 | Permalink | Comments (0) | TrackBack (0)

2004年02月10日

クッキーとアンダースコアを含むホスト名

あるWebアプリケーションを作っている際に、クッキーをどうしても保存できないというトラブルに遭遇ししました。調べていくと、全く同じスクリプトを別ホストに設置すると正常に保存できることが確認できたので、どうもホスト名に問題がありそうです。ということで正常にクッキーを保存できるホスト名とできないホスト名を眺めていると、ある違いに気づきました。そう、クッキーを保存できないホスト名には"_"(アンダースコア)が含まれていたんです。。アンダースコアってRFCが定めるホスト名のガイドラインに違反している文字列なんですね。そういえば、このサイトのドメインを取得する際、「hori-uchi.com」にしようか、「hori_uchi.com」にしようか悩んだんですが、「hori_uchi.com」はドメイン登録できなかったような記憶がうっすらあります。。

話をクッキーにもどしますが、IE5.01SP1以降ではRFCに準拠しないホスト名からクッキーはセキュリティの観点から保存しないようになっているようです。 こちらに詳しい情報がまとめられています。 それによると、

このIEのセキュリティパッチは、悪意あるWebサイトが他のWebサイトのCookieを取得できる脆弱性を排除するためのもので、IE5.01SP1以降のリリースのIEリリース、SPに含まれます。 このパッチを適用すると、以下のいずれかに該当する場合、cookieはブラウザに保存されません。
  1. URLのホスト名が、RFCのドメイン名のガイドラインに違反している文字を含む場合("_"など)
  2. URLに正しいドメイン名の記述がない場合
  3. 3.
  4. ドメインの記述に一つ以上のピリオドが含まれていない場合
ということだそうです。RFCにはきちんと目を通しておかないといけませんね。。

Posted by horiuchi at 08:35 | Permalink | Comments (0) | TrackBack (0)

2003年11月19日

Macromedia Flex

Flashコンテンツをプログラミング言語で開発できる「Macromedia Flex」(MYCOM PC WEB)
米Macromediaは、XMLベースのプログラミング言語でFlashコンテンツの開発を可能にするサーバフレームワーク「Macromedia Flex」を発表した。すでにベータ版の提供が開始されており、2004年上半期に製品版がリリースされる。
だそうです。

Posted by horiuchi at 00:27 | Permalink | Comments (0) | TrackBack (0)

2003年11月16日

CSSVault - CSSを使ったサイトや技の紹介

CSS Vault - Unlocking creativity
Acollection of CSS resources and CSS sites that help to inspire and teach every designer.
CSSのTipsやCSSをうまく使ったサイトを紹介しているサイト。CSSを学ぶにはいいサイトかも。

Posted by horiuchi at 23:03 | Permalink | Comments (0) | TrackBack (0)

2003年11月14日

CSSのdl要素について

冷麺 | CSSによるシナリオ風の文書整形
この記事の、僕にとっての最大のポイントは「会話」のマークアップにdl要素を使っても何も問題ないということで、
dl要素を会話のマークアップに使うというのはW3Cの勧告でも例として取り上げられているそう。
これを適用した例としてあげられていた冷麺 | フジテレビの報道番組を斬る!に激しく同意。

Posted by horiuchi at 14:04 | Permalink | Comments (0) | TrackBack (1)

2003年11月09日

TDD - テスト駆動開発

@IT:特集 「テスト駆動開発」はプログラマのストレスを軽減するか?

ソフトウェア開発の世界にも日々の進歩がある。そしてその中には、使えばさまざまな恩恵を受けられる技法もある。しかし、それらを現場ですぐに活用できるとは限らない。

XPをウェブアプリケーションの開発に適用できないかとXPの本を何冊か読んだのですが、全てを適用するのは難しいかなと思ってました。
そこでこのTDD。XPが顧客までを巻き込んだ開発スタイルであるのに対し、これはプログラマー一人でも実践できそうです。試してみようかなー。

Posted by horiuchi at 14:59 | Permalink | Comments (0) | TrackBack (0)