l'essentiel est invisible pour les yeux

Wednesday, May 24, 2006

[IE 7] baseタグが効かないバグ

Internet Exploler 7.0.5346.5
では、baseタグが効かないようだ。

あらゆるCSSモードを試したわけではないが、自分の環境下ではbaseタグが効かないorz

[Rails] lighttpdとWEBRickでのRequestオブジェクトの挙動

開発環境はWEBRick、プロダクション環境はlighttpd+fcgiで運用しているという方も多いと思います。ここで一つ注意が必要なのは、requestを扱う際の注意です。

requestにはユーザからのHTTPリクエストをサーバが解析しRailsがrequestオブジェクトにマッピングします。そのため、WEBサーバの実装によって一部値が変わってきます。
requestオブジェクトのプロパティの値で処理を分岐させるなどしている場合は、うまく動作しません。

同一のソースをlighttpd + fcgiとWEBRickで動作させた場合のrequestオブジェクトを比較してみます。(どちらもRAILS_ENV => development)

http://rakuto/testにアクセスしたとして話を進めます。
lighttpdは80番、WEBRickでは3000番ポートで動作させます。


lighttpd + fcgi


{"SERVER_NAME"=>"rakuto", "PATH_INFO"=>"", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate", "SERVER_ADDR"=>"222.230.136.50",
"SCRIPT_NAME"=>"/dispatch.fcgi", "REDIRECT_URI"=>"/dispatch.fcgi",
"SERVER_PROTOCOL"=>"HTTP/1.1", "HTTP_HOST"=>"rakuto",
"HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3",
"SERVER_SOFTWARE"=>"lighttpd/1.4.11", "REMOTE_ADDR"=>"222.230.136.61",
"HTTP_KEEP_ALIVE"=>"300", "HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7",
"HTTP_COOKIE"=>"__utma=146761718.1802360063.1148377808.1148377808.1148439243.2;
_session_id=1b956cee50de1e32e9c1762b1c9df19e;
__utmz=146761718.1148377808.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);
username=rakuto; __utmc=146761718",
"DOCUMENT_ROOT"=>"/var/www/rakuto/current/public", "REQUEST_URI"=>"/test/",
"SERVER_PORT"=>"80", "GATEWAY_INTERFACE"=>"CGI/1.1", "REMOTE_PORT"=>"38429",
"QUERY_STRING"=>"",
"SCRIPT_FILENAME"=>"/var/www/rakuto/current/public/dispatch.fcgi",
"HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"REQUEST_METHOD"=>"GET", "REDIRECT_STATUS"=>"200", "HTTP_CONNECTION"=>"keep-alive"}


WEBRick

{"SERVER_NAME"=>"rakuto", "PATH_INFO"=>"/test/", "REMOTE_HOST"=>"222.230.136.61",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Windows; U;
Windows NT 5.1; ja; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3",
"SERVER_PROTOCOL"=>"HTTP/1.1", "HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3",
"HTTP_HOST"=>"rakuto:3000", "REMOTE_ADDR"=>"222.230.136.61",
"SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.8.4/2006-04-28)", "HTTP_KEEP_ALIVE"=>"300",
"HTTP_COOKIE"=>"__utma=146761718.1802360063.1148377808.1148377808.1148439243.2;
_session_id=1b956cee50de1e32e9c1762b1c9df19e;
__utmz=146761718.1148377808.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);
username=rakuto; __utmc=146761718",
"HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7",
"REQUEST_URI"=>"http://rakuto:3000/test/", "SERVER_PORT"=>"3000",
"GATEWAY_INTERFACE"=>"CGI/1.1", "QUERY_STRING"=>nil, "REMOTE_USER"=>nil,
"HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"REQUEST_METHOD"=>"GET", "HTTP_CONNECTION"=>"keep-alive"}


おまけで、Mongrelも。
Mongrelの場合は、自分の環境ではWEBRickで動作させたときと挙動は同じでした。

{"SERVER_NAME"=>"rakuto", "PATH_INFO"=>"/test",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3", "SCRIPT_NAME"=>"/",
"SERVER_PROTOCOL"=>"HTTP/1.1", "HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3",
"HTTP_HOST"=>"rakuto:3000", "REMOTE_ADDR"=>"192.168.255.1",
"SERVER_SOFTWARE"=>"Mongrel 0.3.12.4", "HTTP_KEEP_ALIVE"=>"300",
"HTTP_COOKIE"=>"__utma=124112458.1769474473.1147068425.1147768112.1147937337.13;
_session_id=24d7dc5809d572486716edac18f59a33;
__utmz=124112458.1147068425.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);
username=rakuto", "HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7",
"HTTP_VERSION"=>"HTTP/1.1", "REQUEST_URI"=>"/test/",
"SERVER_PORT"=>"3000", "GATEWAY_INTERFACE"=>"CGI/1.2",
"HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"HTTP_CONNECTION"=>"keep-alive", "REQUEST_METHOD"=>"GET"}


まず一つ目の違い。
request.env["QUERY_STRING"]の値が異なります。

http://rakuto/testでアクセスしてみます。

"QUERY_STRING"=>"" # lighttpd + fcgi
"QUERY_STRING"=>nil # webcik
# => Mongrel プロパティが存在しない


あれ、lighttpdではQUERY_STRINGが空のときはnilではなくて空文字列になるんだ。

次に、htttp://rakuto/test?q=hogeでアクセスしてみます。

"QUERY_STRING"=>"" # lighttpd + fcgi
"QUERY_STRING"=>"q=hoge" # webcik


・・・lighttpd + fcgiの場合は、request.evn["QUERY_STRING"]は使えないようですね。

controller内でQUERY_STRINGの値でその後の処理を期待した、コードを書いていたらダメポと言うことです。WEBRickからlighttpd環境に移したときに正常に動作しません。

class IndexController < ApplicationController
def index
query_string =request.env["QUERY_STRING"].nil?? "" : request.env['QUERY_STRING']}
・・・
end
end



二つ目の違い
request.env["PATH_INFO"]の値もlighttpd+fcgi環境下では正常に設定されません。


"PATH_INFO"=>"" # lighttpd + fcgi
"PATH_INFO"=>"/test" # WEBRick


結論
request.env内の値は、WEBサーバの実装依存。この値を元にロジックを実装するのは危険!ということを学びました。

Saturday, May 20, 2006

[JavaScript]MarkUpBuilder - DOMの生成

JavascriptでのDOM生成について、調べていたところ、マークアップビルダ、存在しないメソッドの記事を見つけて、SpiderMonkey以外のJavascript実装のブラウザでも汎用的に使えるように、実装してみました。

Prototype.jsに依存しています。


var MarkupBuilder = Class.create();
MarkupBuilder.DOM = ["ul", "ol", "li", "td", "tr", "thead", "tbody", "tfoot", "table", "th",
"tbody", "input", "span", "p", "a", "div", "img", "textarea", "label", "script", "iframe"]
MarkupBuilder.cache = null;
MarkupBuilder.UA = navigator.userAgent.toLowerCase();
MarkupBuilder.prototype = {
__noSuchMethod__ : function(name, args) {
var element = document.createElement(name);
var attrs = args[0] || {};
var bMSIE = MarkupBuilder.UA.indexOf("msie") != -1;
for(var name in attrs) {
if(bMSIE && name.indexOf('on') != -1) // IE setAttribute bug
attrs[name] = new Function(attrs[name]);
else if(!bMSIE && name == "className"){
var _class = attrs[name], name = "class";
attrs[name] = _class;
}
try {
if(attrs.hasOwnProperty(name)) element.setAttribute(name, attrs[name]);
} catch(e) {throw e;}
}
for(var i=1, len=args.length;i<len;++i) {
if(args[i] instanceof Array)
for(var j=0, len2=args[i].length;j<len2;++j) element.appendChild(args[i][j]);
else
if(typeof args[i] == "string" || args[i] instanceof String) element.innerHTML += args[i];
else element.appendChild(args[i]);
}
return element;
},
initialize : function() {
if(MarkupBuilder.cache) return MarkupBuilder.cache;
if(!navigator.userAgent.match(/Gecko/)) {
var proto = $A(MarkupBuilder.DOM).inject({}, function(methods, tagName, idx) {
methods[tagName] = function() {return this.__noSuchMethod__(tagName, arguments)};
return methods;});
if(this.__proto__) this.__proto__ = proto;
else Object.extend(MarkupBuilder.prototype, proto);
}
MarkupBuilder.cache = this;
}
}

追記(2006/05/20):
・IEではsetAttributeメソッドでイベントリスナが登録できないバグに対応
・効率化のためにシングルトンにした。
・IE以外では、classNammをclassに変換。
・DOMの一次元配列を渡せるようにした
・HTMLを挿入できるようにした


使用例

var b = new MarkupBuilder();
var dom = b.div({id: "id1", className: "class1"},"divInnerText",
b.span({id: "id2", className: "class2"}, "spanInnerText"),
b.a({id: "id3", onclick: "return false;", href: ""}, "link"));

生成されるHTML

<div class="class1" id="id1">
divInnerText
<span class="class2" id="id2"> spanInnerText </span>
<a href="" onclick="return false;" id="id3"> link
</div>

SpiderMonkey系のブラウザでは、__noSuchMethod__を使用し、それ以外のブラウザではMarkupBuilderを拡張しています。
IEではobj.__proto__が使用できないので、Object.extend()を使って拡張しています。

Firefox1.5とIE6では動作確認していますが、バグがあるかもです。


追記
随分久しぶりの投稿になってしまいました。。
現在、「UIE Japan、今度は「組み込みエンジニア」募集」で紹介されているサービスを実装中です。今までに無い面白いサービスを6月末あたりにリリースしますのでお楽しみに。

JavaScriptを本格的にやり始めてから2ヶ月がたちました。
面白いですね。と言うことで、リリース予定の新しいサービスはJavaScriptゴリゴリです。