たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
シス管系女子の特設サイトができました、というか例によって自分で作りました。 3日ほど夜なべして。
電子書籍はいわゆる印税契約だけど紙の方は原稿料買い切り(書籍じゃなくムックだから)なので、プロモーションに工数かけてもあまり得にならないんですよね。なのに何故やったのかというと……要するに、欲しかったんですよ!!! 僕が!!!!!
いやね、連載5年目に入ろうとしてるのにWeb上では相変わらず知名度が低くて、知ってる人は知ってる的な立ち位置がいいかげん辛くなってきたというか、この間なんて「たまたま日経Linuxを見たらこんなの(#!シス管系女子Season3 Petitまとめ読み)あったんだけど、これってもしかしてシェルスクリプトマガジンのシェル女子の便乗企画……?」みたいに思われてしまった、というのは被害妄想もいいとこなんですが、「それもこれもみんな、ここ見れば大体分かるっていう位置付けの公式サイトが無いせいなんや!」と大人げなく嫉妬に狂いまして、手元にあった素材と原稿データをイラレの上で切り貼りしていわゆる1枚ペラのページのこんな妄想画像 を作って「こういうのがほしいんだよこういうのがああああああ!! 日経Linuxのサイトの中に特設ページ作ってもらえませんかね!?」と日経BPサイドに提案してみたものの、会社の方針とかであんまり他のページと違う物は載せられないのでPDF置いとくだけならまぁなんとか……と言われてしまって「そういうことじゃないんだよおおおおお!!!」と血の涙流しながらHTMLとCSSをゴリゴリ書いてさくらのレンタルサーバの一番安いプラン借りてお名前.comでドメイン取って(独自ドメイン取るのこれが初めてですよ! なんと!)突貫工事で作った、というのが真相ですハイ。 説明文が「Piro氏」とか微妙に客観なのは、元が日経BPへの提案用だったからで。
思い返せばかれこれ14~5年は前ですかね?
CSSコミューンで偉そうなこと言ってて「Web業界に進みたいな」とか一瞬思ってたあの頃。
CSS2の仕様通りに書いた物をInternet Explorerが微妙にまともにレンダリングしてくれなくて、それでもNetscape Communicator 4での悲惨な対応具合に比べればまだマシというネスケ派の自分にとっては「ギギギ……!」と歯ぎしりせずにはおれない状況で、NC4でもIEでもきちんと表示できてW3C的にも(というかAnother HTML-lint的に?)Validで且つそこそこ凝った見た目を実現して「W3Cの理想は非現実的な絵空事なんかとちゃうんやで!!!」と世界の片隅でアピールしたい!という思いからこんなスタイルシートやあんなスタイルシートやそんなスタイルシートといったいろんな実験作を書いて粋がってた日々。
当時最もCSS2の実装が進んでたGeckoエンジンでさえできることは全然限られていて、「あぁ……この仕様にある:nth-child(2n-1)
ってのが使えれば装飾の左右振り分けも簡単なのに……!」と思いながらclass="even"
とかclass="odd"
とか書いて騙し騙しやってましたとも。
ドロップシャドウひとつ作るのにも画像を作ってスライスして……よくあんなめんどくさいことやってたもんだ。
しかも人力で。
(素人でお金も無いのでDream WeaverだのFireworksだのは手が出なかった)
素人の僕でこんなんだった訳だけど、当時から業務として手がけておられた方々は僕なんかよりはるかに切実な思いでこういう事と向き合っていたのであろう。 CSS昔話 Advent Calendar 2015 - Adventarにはそういう時期の苦労話がたくさん集まってきそうな気配を感じている。
それが、今じゃどうですか。
<ruby><rb>文字の影</rb><rp>(</rp><rt>text-shadow
</rt><rp>)</rp></ruby>も<ruby><rb>ボックスの影</rb><rp>(</rp><rt>box-shadow
</rt><rp>)</rp></ruby>も<ruby><rb>角丸</rb><rp>(</rp><rt>border-radius
</rt><rp>)</rp></ruby>も背景色の半透明もグラデーションも<ruby><rb>奇数番目と偶数番目での振り分け</rb><rp>(</rp><rt>:nth-child(2n-1)
</rt><rp>)</rp></ruby>も、テキストでちょちょいっと書けば即反映ですよ。
当時は想像もしてなかった、CSSメディアクエリーなんて物もできてるし。
あの頃「こういう風に作れればいいのになあああ!!!」とイメージトレーニングしていた理想のCSS世界がまさに目の前にあるという感慨深さよ。
document.querySelectorAll()
で要素をガッと集めてきて制御したり、今画面内にある画像をdocument.elementFromPoint()
でダイレクトに取得して位置合わせしたり、Firefoxのアドオン開発でも苦労してた部分があっさりクロスブラウザで動いてくれちゃってて。
FirefoxとChromeのWindows版とAndroid版でだけ検証してリリースしちゃいましたが、後でIE11で見たら全く支障なく完璧に表示されてたし。
あまりのあっけなさに目がテンになり、その後感涙でむせび泣きかねない勢いでした。
……とまあ、昔取った杵柄で一枚ペラ&試し読み簡易マンガビューワーを作るところまではよかったんですが、宣伝のためのサイトなのにSNS連携のシェア用ボタンを入れてなかったり、FacebookやTwitterでシェアされたときにいい感じに画像を出す工夫をしてなかったり、「それやらないの今時あり得ないでしょ……」な手落ちだらけで、「エッなにそれいつの間にそんな事になってたの」と完全に浦島太郎でした。 かろうじてGoogleアナリティクスは存在を知っていたので、それだけは入れてましたが。
皆さんの助けが無ければ、作ったはいいものの結局やっぱり誰にも見られない廃墟サイト化一直線……という末路を辿っていたところでした。 大変お世話になりました。
思い出話8割に実用情報2割くらいでこんなエントリを書き記して何やってんのって自分でも思いますが、今僕が主たる仕事の場にしてるフリーソフトウェアの世界でも、プロモーションはやっぱ大事だなって思うんですよね。 先行実装があってそれなりに頑張って丁寧に作ってたのに、その存在を知らなかった人が後から作った荒い出来の物が「これ新しい!!! こんなの欲しかった!!!!」って人気をかっさらってって、先達の頑張りが全く誰からも評価されないまま消えていってしまう……俺達は、あと何回そんな悲劇を目にすればいい? 俺はあと何回、顧みられることの無い先駆者を目にすればいいんだ?
というわけで、今後仕事絡みで何か作って公開する時にまた参照したいので自分用のメモとして今回参照した情報をまとめたというのがこのエントリの趣旨なのでした。
まぁ、このsystem-admin-girl.comが実際どれだけ成果が出るかというのは分からない、ともすれば結局やっぱり廃墟になっちゃったねというオチもあり得そうではありますが、「だからこういう風にして欲しかったんだよぉぉおおおお!!」と地団駄踏んで不満溜め込んでるよりは、「思ってたやりたかったとおりの事やったけど駄目でしたわハハハ……」となる方がまだ精神衛生上良さそうなので、これで安心して眠れます。 おやすみなさい。
JSDeferredは非同期処理の制御に特化しててサイズも小さくて素敵な軽量ライブラリだ!と僕は思ってるんだけど、世の中を見回してみると「軽量ライブラリ」って言われてる物は3KB未満とかそういうのが結構あるようだし、jQueryも1.5.1のminified版は28KBって書いてあるし、そうなるとjsdeferred.jsのコメント付き版が0.3.4で19KBというのは「軽量」と呼ぶにはひょっとしてちょっとでかいのかな……という気がしてきた。
なので、JS MinifierとかPackerとかそういう風なやつでどれくらい小さくなるのか実験してみた。
/packer/はそのままだと構文エラーで動かなくなってしまった。}Deferred.
って部分が7箇所あってこれがエラーになってるので、全部 };Deferred.
に置換(Base62圧縮後だと }4.
を };4.
に置換)したら一応エラーは出なくなった。/packer/の構文解析が貧弱なせいっぽいので、JSDeferredの側で問題になる所にあらかじめセミコロンを入れておけば、この問題は無くなるっぽい気がする。……と書いたからか、ちょよんごさんが対応してくれた。ありがとうございます!
あと、closure-compiler を通すと 2668bytes でした (jsdeferred のレポジトリで rake すると URL が出るようになってます)
というアドバイスも頂いたので早速Closure Compilerにかけてみた所確かに小さくなったのですが、シンボル類まで全部失われちゃってこれ単体だと他のスクリプトと組み合わせられないのが残念ですね。(Closure Compilerは他のスクリプトも全部合わせて一緒にコンパイルして使うのが前提ってことなんだろう)
最近、JsDoc Toolkitの導入を考えてる。
ツリー型タブのAPI紹介等ではXPIDLっぽい書き方にしてみてるけど、オレオレ表記なので分かってもらえない可能性があるという心配はずっとしている。
ところで、Firefox自体のソースを見ていると以下のような書き方をしているのをよく見かける。
(略)
/**
* Given a starting docshell and a URI to look up, find the docshell the URI
* is loaded in.
* @param aDocument
* A document to find instead of using just a URI - this is more specific.
* @param aDocShell
* The doc shell to start at
* @param aSoughtURI
* The URI that we're looking for
* @returns The doc shell that the sought URI is loaded in. Can be in
* subframes.
*/
function findChildShell(aDocument, aDocShell, aSoughtURI) {
(略)
これはJavaで標準的に使われているJavadocという「ソースコードの中に埋め込まれたコメントを自動的に収集してHTML形式でドキュメントを生成する」仕組みに基づいたもので、同じ形式でコメントを埋め込めるよう、Javadocの仕様に準拠した実装が言語ごとに存在しているようだ。JavaScriptならJsDoc Toolkit、C言語ならGTK-Docが一般的らしい。あとJavaScriptに関してはGoogle Closure ToolsのClosure Compilerも対応しているらしい
事実上の標準としてみんなが見慣れた形式なのであるならば、これに合わせて書くのがいいだろう。と思ったので、とりあえずライブラリとして切り出して公開しているコードにJavadoc形式で使い方の解説を埋め込んでみることにした。
@example
に例を書く時に、例の中に<
とか>
とかのHTML的にまずい文字が含まれていると、出力されるHTMLがぶっ壊れてしまう。JsDoc Toolkit自体のソースを見てみた所(JsDoc Toolkitはそれ自体がJavaScriptで書かれている。Java上で動作するJavaScript実行環境のRhinoの上で動作している。)、ファイルの拡張子でフィルタリングを行っているらしいということが分かった。あと、テンプレートのファイルの方を編集すれば、例に埋め込んだコードのせいでHTMLがぶっ壊れてしまう問題は回避できるようだった。
そういうわけで当面の所はこんな変更を加えて使ってみることにした。以下はjsdoc_toolkit-2.3.2.zipに対する差分です。
diff -ur jsdoc-toolkit-orig/app/lib/JSDOC/JsDoc.js jsdoc-toolkit/app/lib/JSDOC/JsDoc.js
--- jsdoc-toolkit-orig/app/lib/JSDOC/JsDoc.js 2009-01-24 18:42:04.000000000 +0900
+++ jsdoc-toolkit/app/lib/JSDOC/JsDoc.js 2010-08-20 10:17:17.602464000 +0900
@@ -69,7 +69,8 @@
JSDOC.JsDoc._getSrcFiles = function() {
JSDOC.JsDoc.srcFiles = [];
- var ext = ["js"];
+ var ext = ["js", "jsm"];
+ var ignorePattern = /\.test\.js$/i;
if (JSDOC.opt.x) {
ext = JSDOC.opt.x.split(",").map(function($) {return $.toLowerCase()});
}
@@ -89,7 +90,7 @@
}
}
- return (ext.indexOf(thisExt) > -1); // we're only interested in files with certain extensions
+ return (ext.indexOf(thisExt) > -1) && !ignorePattern.test($); // we're only interested in files with certain extensions
}
)
);
diff -ur jsdoc-toolkit-orig/app/run.js jsdoc-toolkit/app/run.js
--- jsdoc-toolkit-orig/app/run.js 2009-01-08 06:32:58.000000000 +0900
+++ jsdoc-toolkit/app/run.js 2010-08-16 17:28:28.673092400 +0900
@@ -337,7 +337,7 @@
if (!path) return;
for (var lib = IO.ls(SYS.pwd+path), i = 0; i < lib.length; i++)
- if (/\.js$/i.test(lib[i])) load(lib[i]);
+ if (/\.jsm?$/i.test(lib[i])) load(lib[i]);
}
}
Only in jsdoc-toolkit: jsdoc.bat
diff -ur jsdoc-toolkit-orig/templates/jsdoc/class.tmpl jsdoc-toolkit/templates/jsdoc/class.tmpl
--- jsdoc-toolkit-orig/templates/jsdoc/class.tmpl 2009-09-03 06:37:31.000000000 +0900
+++ jsdoc-toolkit/templates/jsdoc/class.tmpl 2010-08-18 15:10:02.542253900 +0900
@@ -300,7 +300,10 @@
<if test="data.example.length">
<for each="example" in="data.example">
- <pre class="code">{+example+}</pre>
+ <pre class="code">{+String(example)
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')+}</pre>
</for>
</if>
@@ -399,7 +402,10 @@
<if test="member.example.length">
<for each="example" in="member.example">
- <pre class="code">{+example+}</pre>
+ <pre class="code">{+String(example)
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')+}</pre>
</for>
</if>
@@ -466,7 +472,10 @@
<if test="member.example.length">
<for each="example" in="member.example">
- <pre class="code">{+example+}</pre>
+ <pre class="code">{+String(example)
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')+}</pre>
</for>
</if>
@@ -565,7 +574,10 @@
<if test="member.example.length">
<for each="example" in="member.example">
- <pre class="code">{+example+}</pre>
+ <pre class="code">{+String(example)
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')+}</pre>
</for>
</if>
モック(Mock)とスタブ(Stub)の違いがよく分かってなかったんだけど、何が違うのか、そしてモックはどう使う物なのかということを、すとうさんに教えてもらって今更理解した。あとで会社のブログに書くつもりだけど、メモとして要点だけまとめておく。
自分は今まで、とりあえずユニットテストに注力していて、ある意味脅迫観念的な勢いで、ブラックボックス度合いを高くする事を心がけてた。今まではそれでだいたい問題なかった。でも最近になって、ブラックボックステストにしようとすると無理があるというケースにぶち当たるようになった。1つの機能の中でコロコロと遷移する内部状態を、どうにかして検証したいというようなケースが出てきた。
それですとうさんに相談したら、そういう時はモックを使えばいいと言われた。でも、話を聞く限りだとモックというのはテスト対象の実装の中の処理の流れを追う物のようなので、それじゃブラックボックステストにならないじゃないかと思った。それをそのまま言ったら、確かにテストはできるだけブラックボックステストになってた方がいいけど、機能テストやインテグレーションテストのような粒度の大きな単位のテストでは、処理の中で起こる様々な出来事や副作用を色々モニタリングして、すべての処理が期待通りに動いているかどうかを検証しないといけないから、必然的にホワイトボックステストにならざるを得ないと言われた。
それを聞いて、目の覚めるような思いをした。そうか、ブラックボックステストとホワイトボックステストの使い分けはそこが基準になるのか、と。今まで自分がホワイトボックステストを書かずに済んでいたのは、状態の遷移を伴うような機能を作る必要がなかったからだったんだ、テストをどうも書きにくいなあと思っていた機能は本当はホワイトボックステストにしたほうがいい物だったんだ、と。
そんなわけでUxUにモックの機能を実装した。
「JavaScript Mock」で検索するとJSMockとjqmockが上位に出てきたので、最初はそれらを参考にするように(メジャーな実装があるんだったらそれをそのまま取り入れるなりAPIを合わせるようにするのが望ましい)と言われたんだけど、ドットで繋げるメソッドチェインの記法がガンガン出てきて頭パンクした。
もう少し下の方までスクロールするとMockObject.jsというのが出てきて、こっちはファイル全体で3KBに満たない小さなライブラリなので、まずはここから始めることにした。何せコードが短いから、読むのもそんなに苦にはならない。モックの概念を言葉で説明されてもさっぱりだったけど、一通りの処理の流れを見たら、「モックというのは一体何をやらなきゃいけないのか」「どういう振る舞いが期待されているのか」ということがよく分かった。
MockObject.jsと同等の機能を一通り実装した後でもう一度JSMockの方を見たら、なるほどこれはこういう意味だったのかというのがやっと分かった。サンプルコードを見ても、モックという物の意味をそもそもよく知らない時点では、どこからどこまでがJSMockの部分なのかさっぱり分からなかったんだよね。ということで、MockObject.jsに加えてJSMock互換のAPIも付け加えてみた。jqunitの方は……もう別言語だからシラネ。
あと有名なのはJsMockito? これも頑張ったらできるかなあ、というかMITライセンスだしそのままぶち込んだ方が早いか……
先日のブラウザー勉強会で吾郷さんにお会いした際に、JavaScriptには言語の仕様として「例外がどこから投げられたのか」を知る為の仕組みが無いので独自のフレームワークを作る時に困ったという話を伺った。オレ標準JavaScript勉強会で何話せばいいか困ってた所だったので、それをネタに発表させてもらう事にした。
改めてECMAScriptの仕様書を確認してみたけど、確かに3rd Editionと5th Editionでは、例外オブジェクトの機能としてはError.prototype.name
とError.prototype.message
しか定義されていなかった(あとはconstructor
とかtoString()
とかその程度)。Wikipedia(英語の方)のECMAScriptの記事によるとECMAScript 3rd EditionはJavaScript 1.5とJScript 5.5の共通部分を抜き出す形で策定されたようで、実装してない方のIEに合わせてこうなったのか?とも思ったけど、調べてみたらJavaScript 1.5の頃はMozillaもError.prototype.stack
はサポートしてなかった。それ以降にMozillaが独自に拡張した箇所ということのようだ。でも今回調べた限りではOperaもChromeもError.prototype.stack
をサポートしていた(Operaの場合はこれに加えて、少しフォーマットが違うスタックトレースをError.prototype.stacktrace
でも取得できる、ということをedvakfさんに教えていただいた)。メジャーなJavaScript実行環境でこれに対応してないのはIE(IE9PP3を含む)くらいのようだ……と思ったらSafari(Windows版)もサポートしていなかった。
吾郷さんのそれやUxUのようにデバッグを支援するためのフレームワークでは、スタックトレースは欠かせない要素だ。将来のECMAScriptの仕様に取り込まれてくれればいいのになあ、と思う。
勉強会ではせがわようすけさんに突っ込まれたけど、セキュリティのためにはなるべくこういう情報は出さない方がいいものらしい。しかし自分はスタックトレースの何がまずいのかがよく分かっていない。会場では「例えばスタックトレースでスクリプトのURLの中にセッションIDが含まれていたらセッションハイジャックされてしまう危険性がある」という例を教えていただいたけれども、それでもまだピンと来ていなかった。
さらにツッコミを受けたことで「なるほど、クロスドメインの制約を突破されてしまう」という事が問題なのだと分かった。ただ、実際にそれが問題になるケースってほんとにあるのかな? という疑問はまだ残っている。
分かりやすい話として、通常のXMLHttpRequestやiframeでは、別ドメインのドキュメントを読み込む事ができない・読み込んだとしてもその内容にスクリプトからアクセスすることはできない。
var iframe = document.createElement('iframe');
iframe.setAttribute('src', 'http://www.google.co.jp');
document.body.appendChild(iframe);
window.setTimeout(function() {
try{
alert(iframe.contentDocument.body);
}
catch(e){
alert(e);
}
}, 3000);
例えばこういうのは、Error: Permission denied for <http://piro.sakura.ne.jp> to get property HTMLDocument.body from <http://www.google.co.jp>.と言われてエラーになる。しかしスタックトレースにエラー行の詳細な情報が含まれていると、
try {
document.write('<script type="text/javascript" src="http://www.google.co.jp/" async="false" defer="false"></script>');
}
catch(e) {
var contentsFragment = e.stack;
}
とか
try {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'http://www.google.co.jp/');
script.setAttribute('async', 'false');
script.setAttribute('defer', 'false');
document.body.appendChild(script);
}
catch(e) {
var contentsFragment = e.stack;
}
とか
// window.onerrorはECMAScriptの仕様にはない独自拡張
window.onerror = function(aMessage, aSource, aLineNumber) {
// ここでaMessage, aSourceから情報を取れる可能性がある
}
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'http://www.google.co.jp/');
script.setAttribute('async', 'false');
script.setAttribute('defer', 'false');
document.body.appendChild(script);
という風にして、別ドメインのドキュメントの内容を部分的にとはいえ読み取れてしまう可能性がある、というわけだ。特に3番目のwindow.onerrorを使う例はMFSA 2010-47: エラーメッセージのスクリプトファイル名からのクロスサイトデータ漏えいで実際に脆弱性になっていたことが確認されていて、既に修正されている。
なお、実際に現行バージョンのFirefox・Opera・Chromeで試してみたところ、1番目・2番目のtry-catchを使った例ではそもそも例外を例外として(そもそも、エラーが発生したのかどうかすら)捕捉できなかったので、今の所問題にはならない。ただ、今は問題にならなくても、今後登場するJavaScriptの実装ではこういう場合でも情報を取れるようになっている可能性はあるので、将来的に脆弱性の原因になるかもしれないという警告は確かにアリだとは思う。という所までは何とか理解できた。
ブラウザのレベルでは、クロスドメインやクロスオリジンになる時だけは例外を出さないとか詳細な情報を出さないとか、そういう対応の仕方はあるだろうけれども、「ECMAScript」の仕様でそこに言及するのは変だろうなあ。というややこしい事情を勘案すると、もう丸ごと全部仕様からドロップしてしまえという判断になるんかなあ。
いくつか前例はあるようだ。
僕の契約はスタンダードプランなんだけど、複数ユーザでFreeBSD 7のサーバを共用してる状態で、管理者権限は無い。クライアントはUbuntu 10.04。という前提で。
何はともあれ、まずはリモート操作のための準備を整える。
ssh ユーザ名@ドメイン名
これでsshでログインできるんだけど、いちいちパスワードを訊かれてしまう。なのでsshの公開鍵を置いておくことにした。
再度上記の要領でsshでログインを試みると、今度はパスワードを訊かれなくなる。scpも。
$_conf['data_dir']
などで指定しているデータディレクトリの位置を、先程作ったデータディレクトリへのパスに書き換える。 ~/p2data/ にデータディレクトリがあってrep2のindex.phpが ~/www/rep2/index.php にあるという位置関係なら、値は "../../p2data/"
になる。$_conf['secure']['auth_host']
の値を 0
にする。上記の参考エントリではこれだけでいけるという風に書いてあるんだけど、僕の場合はPHPのエラーで動かなかった。magic_quotes_gpcをOffにしろとかなんとか。
さくらのレンタルサーバのサーバコントロールパネルを見てみると「PHP設定の編集」というのがあったので、ここを見てみたところ、デフォルトの設定に対して必要な設定項目だけ上書き指定できるようだったので、magic_quotes_gpc = Off
と記入して保存した。その後再度rep2のページにアクセスしてみた所、今度はちゃんとユーザ登録用のフォームが表示された。
実はローカルで今までrep2を動かしてたので、そのログや「お気にスレ」などのデータを引き継ぎたかった。ローカルのデータは ~/public_html/rep2/data/ に置いてた、という前提で。
今まで見てたスレの過去ログが見えるようになっていれば成功。
今回の事で、さくらのレンタルサーバにsshで入る方法を習得した。もう少し色々試したら、定期的にサーバにある内容をまとめて圧縮してバックアップということもできるようになれるかな。なれるといいな。
またRailsなんですけど。
携帯端末のメールで絵文字を入力した物を送信して、Railsアプリ(いわゆる勝手サイトにあたるもの)でそれを受信したら何か処理をしたい、っていう場面でどぉぉぉーもうまくいかなくて一日悩んでた。
ActionMailer::Base
を継承した独自のクラス(Mailman
とかそういう名前で定義してる)のMailman.receive()
に渡ってきたメールの内容から、絵文字を検出したかった。
class Mailman < ActionMailer::Base
def receive(mail)
some_operation(mail.subject)
some_operation(mail.body)
end
def some_operation(string)
# ここで絵文字を検出したい
end
end
jpmobileとかMbMailとかが利用できるのかなと思ったんだけど、うまくいかない。DoCoMoの端末から絵文字入りのメールを送っても、絵文字のコードにマッチするはずの正規表現に全然マッチしない。
んで、もっとよく調べてみたら、勝手サイトだからなのか何なのか、DoCoMoのメールサーバからメールが送られる時点で絵文字の情報は完璧に失われるんですね。絵文字は全部「〓」に変換されてしまってて、〓の文字コードは(Unicodeだと)0x3013だから、さっきのコード表から作った正規表現にはマッチするわけがない。
まあ、やりたかった事は「絵文字があったらエラーを返す」という事だったので、「〓」が有るか無いかだけ見るというソリューションでだいたい問題なかったんですけれども。(「〓」自体を送信できないという問題は残るけど、こんな文字は普段使わないからその問題は無視する)
1日まるまる無駄にしてしまった……
所用でRuby on Railsのテストケースを書いているのですが、メールを受信してあれこれする処理のテストのためのfixtureを用意するのにいちいちホントにメールを送信してそのソース文字列をコピペするとかそういう事をやってたら面倒すぎたので、Subjectとかのヘッダ部分を簡単に書き換える方法を探した所、Rubyでやるのが早いっぽい事が分かったのですが、忘れそうなのでメモしておきます。
irbを起動して
require "base64"
def encode( str )
"=?iso-2022-jp?B?" + Base64.encode64( NKF.nkf( '-j --utf8-input', str ) ).chomp + "?="
end
を貼り付ける。後はencode("文字列")
でその都度結果を見ればいい。
いろんな人がいろんな解説を既に書いてるみたいだけど、ツリー型タブでタブのインデントや折り畳みのアニメーションをCSS Transitionsで書き直してみるにあたり、自分で書いてみるまでよく分からんかったので、理解している内容をまとめてみることにした。
CSS Transitionsのフルスペックの説明でもないし、「こんなことができるぜすげーだろ!」というデモでもない。現実的にどういう場面でどういう風に使う事ができるのか、という事を淡々と述べます。いろんな機能を詰め込んだよく分からんデモやスクリプトと組み合わせること前提の変なデモを見たせいで混乱してる僕みたいな人が、CSS Transitionsを理解するための手助けになれれば幸いです。
マージンや色などの値を数値で表せるプロパティや、画像を表示する系のプロパティなどについて、スクリプトあるいは:hoverなどの疑似クラスによって動的に値が変化するようにしている場面で、元の状態と新しい状態の間をなめらかに変化させるようにする。
例えば、こんな風に。
ul.menu li {
transition: margin-left 0.25s ease-out;
}
ul.menu li:not(:hover) {
margin-left: 0;
}
ul.menu li:hover {
margin-left: 50px;
}
この例では、分かりやすくするために敢えて指定を分けて書いている。ここでは以下のようなことが起こっている。
margin-left: 0;
が適用されている。:hover
の状態になると、margin-left
が50px
になるようにアニメーションが始まる。:hover
の状態が解除されると、margin-left
が0
になるようにアニメーションが始まる。:hover
が解除されたらぽわーんとなめらかに引っ込む」ようなGUI要素をスクリプト無しで書けるようになる、という所に意義がある。例を挙げると、以下のような物が該当するだろう。
:hover
の時だけ出っ張るメニュー項目が、なめらかに出っ張る/引っ込むようになる、とか。:hover
すると色が変わるボタンが、「じわっ……」と色が変わるようになる、とか。:hover
で影が付く時に、「じわっ……」と影が濃くなる、とか。className
を書き換えて終了値を間接的に指定する、という使い方。margin-left
が0
であったのを、20px
を設定してアニメーションが始まり、20px
に向けて変化していっている最中の10px
の時点でmargin-left
を0
に再設定した場合、前のアニメーションが中断されて、新たに10px
から0
に向けて変化するアニメーションが始まる事になる。「Windows Vista以降のAero Glass有効時のネイティブアプリのボタンみたいな物をWebに簡単に持ち込めるようにする」「GUIの端々にほんのちょっとだけ小粋なアニメーション効果を加えたい時に、スクリプトを書かなくてもよくなる」という話ですね。それ以上でもそれ以下でもないと思っておくと、CSS Transitionsを理解しやすいと思います。
transitionend
イベント(WebKitではwebKitTransitionEnd
)が発行されるので、アニメーションの終了だけは検知できる。transitionend
イベントを使ってその都度新しい「アニメーション終了時点の値」を設定し直してやることで、アニメーションを繰り返している。CSS Transitionsだけではループはできない。transition:
アニメーションさせたいプロパティの名前
アニメーション終了までにかける時間(省略すると0)
イージングの種類(省略するとease)
アニメーションが始まるまでの遅延(省略すると0);
これで1セットと考えると分かりやすい。複数のプロパティをアニメーションさせたい時は、
transition: margin-left 1s ease;
transition: margin-top 1s ease;
とは書けないので、カンマ区切りにして
transition: margin-left 1s ease,
margin-top 1s ease;
と書く。
アニメーション終了までにかける時間はCSS3 Values and Unitsの時間の値で指定する。例えば250ミリ秒で完了させたい時は250ms
(ミリ秒単位)または0.25s
(秒単位)と書く。アニメーションが始まるまでの遅延も同様。
イージング関数(値の変化の仕方)には、以下のいずれかを指定できる。
linear
ease-in
ease-out
ease
ease-in-out
ease
をもう少しきびきびさせたような感じcubic-bezier()
現状ではまだ仕様が固まっていなくてどのベンダも先行実装の段階なので、ベンダープレフィクス付きの指定を書いてやらないといけない。
-webkit-transition: SafariとGoogle Chrome用の指定;
-moz-transition: Firefox用の指定;
-o-transition: Opera用の指定;
transition: 仕様通りの指定;
CSS TransitionsがRecommendationになったら、ベンダープレフィクス無しの記述だけでいけるようになるはず。
僕はXHTMLのルビ(小さな字で読み仮名をふったりするアレ)を使いたくてXHTML 1.1を選択してるんですが、過去にXHTML2で検討されてたl要素(パラグラフより細かい単位の「行」を示すための物)やiframeやなんかをどうしても埋め込みたくなって、しかしそのせいでバリデータでエラーが出てしまうのも嫌だったので、見よう見まねで頑張って独自の文書型宣言を書いてみたんですよ。いつやったのか忘れたけど、結構前に。
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % XLINK.xmlns.attrib "xmlns:xhtml2 CDATA #FIXED 'http://www.w3.org/2002/06/xhtml2'">
<!ENTITY % Inline.extra "| xhtml2:l | iframe">
<!ELEMENT xhtml2:l (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
<!ATTLIST xhtml2:l
id ID #IMPLIED
class CDATA #IMPLIED
style CDATA #IMPLIED
title CDATA #IMPLIED>
<!ELEMENT iframe (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
<!ATTLIST iframe
id ID #IMPLIED
class CDATA #IMPLIED
style CDATA #IMPLIED
title CDATA #IMPLIED
longdesc CDATA #IMPLIED
src CDATA #IMPLIED
frameborder ( 1 | 0 ) '1'
marginwidth CDATA #IMPLIED
marginheight CDATA #IMPLIED
scrolling ( yes | no | auto ) 'auto'
height CDATA #IMPLIED
width CDATA #IMPLIED
>
]>
CGIを使ってIEにはこの文書型宣言を出さないようにしてますが、FirefoxとかChromeとかでトップページあたりを開いてソースを見れば、こういうのが頭にくっついてるのが分かると思います。
で、バリデータ的にはこれでvalidになったのでめでたしめでたしだったんですが、GeckoのDTDパーサ部分にバグがあるらしくて、この文書型宣言を正しく解釈してくれないんですよね。最後の]>
ではなくその前の>
の部分で文書型宣言が終わったものと見なされてしまうせいで、Firefoxでこのサイトのページを開くと、ページの頭に謎の]>
という文字列がくっついてしまう状態になってたのです。表示が崩れるのが嫌だったので、この]>
がテキストノードとしてページの頭に存在してる時は動的に削除するようなJavaScriptを書いて、長らくごまかしてました。
そしたら先日、W3CのHTML5のルビに関する議論の中で紹介されてたXHTMLルビサポートのページを見たという方(Leif Halvard Silliさん)がメールを下さいまして、以下のようなハックを使えばページの頭に謎の]>
が出てくる事を防げるよ、と教えてくれたんです。
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % XLINK.xmlns.attrib "xmlns:xhtml2 CDATA #FIXED 'http://www.w3.org/2002/06/xhtml2'">
<!ENTITY % Inline.extra "| xhtml2:l | iframe">
<!ELEMENT xhtml2:l (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
<!ATTLIST xhtml2:l
id ID #IMPLIED
class CDATA #IMPLIED
style CDATA #IMPLIED
title CDATA #IMPLIED>
<!ELEMENT iframe (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
<!ATTLIST iframe
id ID #IMPLIED
class CDATA #IMPLIED
style CDATA #IMPLIED
title CDATA #IMPLIED
longdesc CDATA #IMPLIED
src CDATA #IMPLIED
frameborder ( 1 | 0 ) '1'
marginwidth CDATA #IMPLIED
marginheight CDATA #IMPLIED
scrolling ( yes | no | auto ) 'auto'
height CDATA #IMPLIED
width CDATA #IMPLIED
>
<?parser-hack ><!-- ?>
]>
<!--><?!-->
強調箇所がそのハック。処理命令(PHPのコード片を埋め込んだりするのに使うのと同じ奴)の記法でコメントとして解釈できる文字列を埋め込んで、問題の部分を無視させるという物のようです。バリデータに通しても、これでもvalidです。素晴らしいです。
同じような事をやってる酔狂な人がもしいたら役立てて欲しいと思ったので、氏に許可を得てエントリにさせて頂きました。Thanks a lot, Leif!
何故これがvalidなのかも考えてみよう。
<?parser-hack ><!-- ?>
><!--
という内容」を含んだ処理命令として解釈される。[
から]
までの間に登場しうる内容として処理命令も含まれている。そして、上記の箇所は一つの完結した処理命令である。よって、文書型宣言中に登場しても何ら問題ない。<!--><?!-->
><?!
という内容」を含んだコメントと解釈される。という具合で文法的には何も問題ないので、W3Cのバリデータはこれをvalidと判断する。きちんとXMLパーサを実装しているブラウザ上でも同様です。
一方、Geckoの解釈はどうか。これは「ソースを表示」での色分けを見るとよく分かる。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
Geckoはまず、この部分だけで完結した文書型宣言と認識する。「[
」で終わりと判断したのではなくて、その次の <!ENTITY
が来た時点で「あ、もう文書型宣言は終わってたのね」と見なしてるようだ。
<!ENTITY ~>
<!ELEMENT ~>
<!ATTLIST ~>
この辺はすべて、それぞれ別々の宣言として解釈されている。という事で、
]>
何もハックをしない場合はこの部分だけが取り残されてしまって、Gecko的にはこれが「XML的には不正だけどSGML的にはアリ」なテキストノードとして扱われて、ゴミとして表示されてしまうわけだ。
では、ハック有りの場合はどうなるか。
<?parser-hack ><!-- ?>
]>
<!--><?!-->
Geckoはこれを3つのノードとして解釈している。
<?parser-hack >
まずGeckoは、これを1つの処理命令と判断する。XMLの仕様では ?>
が来るまで処理命令の終わりにはならないんだけど、Geckoのパーサは >
で処理命令が終わったものと見なしてしまう。
<!-- ?>
]>
<!-->
これは当然1つのコメントになる。
<?!-->
最後、これも <?
から >
までで1つの処理命令と見なされる。
という事で、ハック有りの時はどの文字もテキストノードとして取り残されずに済むので、画面上は何もゴミが表示されずに済む。
こんなのよく考えるなー、と、読み解いてみて改めて感心しました。