【Postgresql】トランザクション中、CURRENT_TIMESTAMPなどの値は固定
ハマった。
https://www.postgresql.jp/document/7.2/user/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
CURRENT_TIMESTAMP と、それに関連する関数はすべて現在のトランザクションが開始された時間を返すことを認識してください。とても重要なことです。この値はトランザクションが実行されている間に増加しません。とは言っても、timeofday() は実際の現在時間を返します。
代替として、clock_timestamp()やtimeofday()を使う。
【Java】JsoupのWhitelistは使わないほうがいい
前任者が残したコードが土壇場になって牙を剝いた。
Jsoupとは
Jsoupは、あいまいなHTMLでも確実に構文木にパースしてくれる高性能ライブラリ。
例えば<b>とだけ書いても、<html><head></head><body><b></b></body></html>まで補完してくれる。
あまつさえ、jQueryではないが、getElementByTagなどといったJavaScriptでおなじみのAPIみたいなものが使える。
Whitelistの罠
JsoupにはWhitelistクラスというフィルタールールみたいなものがあるが、結構罠を踏んだ。
- 単純なルールしか無理。「○○属性は必須だけど××は任意、その他全部禁止」とかできない。
- 致命的なのが、
<html>、<body>、<head>は無条件でホワイトリストを通過すること。(任意の付きでも通過。XSSじゃん!)
2に関しては何か抜け道があると信じたいが、JsoupクラスのAPI見る限り期待できなさそう。 そもそも、厳格なルール付けには適していなさそう。
セキュアな実装をするならば、また他のHTMLパーサを探すか上記3タグについてメタいことをしたWhitelistを実装するしかない結論に至った所で帰った。
2つのインタフェースと1つの共通インタフェースをめぐる失敗録
HTMLタグのホワイトリスト実装にはまって半日を無駄にしてしまった。ありがちな、汎用性を求めすぎた結果だった。
実装したかったこと
- テキストボックスに入力された文字列中の使用HTMLタグホワイトリストチェック
- パースは別途ライブラリがする
- 要求は以下
最初にしようとしてつぶれたこと
デザインパターンでいうDecoratorやComposerのようなことをしようとして、以下のインタフェースを定義してみた。
public interface TagMatcher {
/**
* 渡されたHTMLタグがマッチングルールに適合するかを判定します。
* @param el HTMLタグ
* @return 成否
*/
public boolean isMatch(Element el);
}
これ自体は問題なかった。呼び出し元からバインドする事項は高々これ1つだった。
まず、「属性-属性値を1つ検証するルール」「無条件でtrueとするルール」など単発のルールを実装して、 それらをDecoratorする形で「配下に持つ複数のルールのand(or)をとるルール」「配下のルールを否定するルール」を作った。
ぶちあたったのはもう一つのインタフェース。
このホワイトリストは元のXMLから与えらえる引数によって引数が定義される仕様だった。 例として、先ほどの2つのspanタグの定義は、以下のように与えられる。
span[style="color:red"],span[style="text-decoration:underline;"]
なるほど単純な繰り返しで、パース自体は比較的簡単に可能だ。
しかしつなげてみると
ホワイトリスト定義 -> (ホワイトリストを分解して汎用データ構造に仕立てるロジック) -> 汎用データ構造 -> (汎用データ構造をもとに、やりたい検証内容を仕立てるロジック) -> 検証結果
というロジックになってしまった。言い換えるなら以下だ。
インタフェースA -> (インタフェースA → 汎用データ構造の変換処理) -> 汎用データ構造 -> (汎用データ構造 → インタフェースBの変換処理) -> インタフェースB
公約数
例えて言うと、ここでいう「汎用データ構造」は公約数を因数にする数のようなものだと考えている。
インタフェース間で受け渡す情報量や表現力を増やすには、公約数を大きくせねばらなない。 乗除を減らすにはインタフェースA-Bの公約数を増やせばいい話だが、 この約数は「2」とか「10」とかのcommonな分かりやすい数が好ましい。 (例えば「19」とか「113」とか、メタったルールを約数にすると嫌なコードになる)
実装したいことと今までに積み上げたことを整理してみる。
言い換えると
- インタフェースAの数はでかい
- 公約数が小さい
- インタフェースBの数はでかい
これを守った上で取る施策は以下になる。
- 汎用データ構造をインタフェースB寄りにする(インタフェースBの約数を増やす。インタフェースA → 汎用データ構造の変換処理のボリュームを積む)
- 汎用データ構造 → インタフェースBの変換処理のボリュームを積む(約数を単純にする。)
どうあがいてもロジックに影響が出る。また、互いの機能が連携しあうため、修正が入るならばどちらか膨らんだほうのロジックに手を出さなければならない。
思い返せば、典型的なインビーダンスミスマッチだった。各両端のインタフェースの公約数、なにより内部の汎用データ構造が1つのインタフェースであると見抜けなかった。
顛末
結局、インタフェースや汎用性の美しさを捨てて、古典的な業務モデルクラスを設計した。 気を付けたのは約数の種類、具体的には業務的ルールに関する構造と言語特性・データ構造に関する構造をなるべく共通化することだった。 公約数を大きくした。
共通データ構造を新しいインタフェースで利用したり、互いのインタフェースの因数が非常に多い場合、小さな公約数にも利があったと思う。 が、局所実装は結局仕様変更によって捨てられるコードであり、まだ300行に満たない共通実装はインタフェース変更を受けるし多少の汚れがあっても十分読めるはず。。願わくば今のきれいなままで死んでほしいけれどもきっと叶わないだろう。
モダンなブラウザクライアントJavaScript開発マネージャjspm
遅れながらjspmに触った。
JavaScriptのパッケージマネージャ、ブラウザ未サポートの新鋭機能をES5にしてくれるようなトランスレータ、おまけにバンドルも勝手にしてくれるすごいやつだ。 System.jsとの併用をオススメしているが、ぶっちゃけるといらないぐらい便利だ。
ES6以降のJavaScriptは最初が面倒くさい
今現在、新しめのJavaScriptの環境で開発しようとすると、開発環境づくりが面倒くさい。
理由として、ちょっと昔話。
ES6以降、JavaScriptは複数ソースコード間の連携についてやっと取り込み始めたが、ES5既存そのまま開発者が使いだすと、色々問題がでてくる。
ひとつが、新機能文法の実装が、各ブラウザの足並みがそろわないこと。(新鋭のものでも、結構遅い。古いIEなんかは実装すらしない)
ふたつめに、ファイルを分割しまくると、下手すると数百個のファイルをブラウザがダウンロードすること。(しかもjsごとに依存チェーンを解決しながらなので、ほぼシーケンシャルにファイルをダウンロードすることになる。。)
これらの解決に、色々ツールを使っている(ES6→ES5へのTranslate、1つのjsファイルにするbundleなど)。 相互の設定が非常に面倒くさい。それぞれコマンドラインツールなので、だいたいtask runnerを導入してビルド自動化するが、プロジェクトごとにファイル構成が違ったり、あとスクリプトファイルのメンテも面倒くさい。
jspmが解決してくれること
jspmは、上記でやることを一つのコマンドラインツールにパッケージングしたツールだ。 つまり、ツールの個別インストール+タスクランナーに書かなければいけなかったことを、全部中に隠ぺいしてくれる。
jspm init としたら初期設定とどのTranslator使う?とか設定できたり、コマンド一発でbundleできたりする。
ライブラリの導入も、jspm installとNodeライクにできてしまう。
ES2015+の過渡期、JavaScriptの敷居を下げるのに大いに役立ってほしい。
サンプル
【Java】javacでcom.sun.xml.internalパッケージをコンパイルする
【bash】ls、grepの「引数が長すぎます」をawkで回避
黒魔術っぽくて敬遠していたawkをちょっと触ってみると、やっぱり黒魔術だった
# 基本1 標準出力を空白文字区切りで分割し、後のスクリプトっぽい部分で一つ目なら$1、二つ目なら$2と書く。
ls foo | awk '{print $1 $2}'
# 基本2 改行の分だけ動作を繰り返す
# ls -1 ... ファイルの一覧を改行区切りで標準出力
ls -1 foo | awk '{print $1}'
# 基本3 スクリプトっぽい部分"{ ... }"は bashではない。
# 独自(C言語っぽい?)文法。
ls -1 foo | awk '{
print "----- " $1 " -----";
# コマンドの字句を作成し、変数に保存
command = sprintf("cat foo/%s", $1);
# bash実行。
buf = system(command);
# ファイルディスクプリタを閉じる
close(command);
print "";
}'
# 基本4 演算できる。なんだこの気持ち悪いインタプリタ
echo 1 | awk '{ print $1 + 1 * 2 }'
# 基本5 if もループもできる。
echo 1 | awk '{
for (i=1; i<100; i++)
{
sum += i;
if (sum >= 100) {
break;
}
}
print "i:" i " sum:" sum;
}'
スクリプト置くならえんやこら手続きがいるけど、手順書に乗っけるだけならば検証作業はいりませんなぁ(ゲス顔) その代わり、結構遅いんやねこれ。。
本題
grep "" dir/*/*/*/*で、400byteのファイル×10万個ぐらいひっかけると、「引数が長すぎます」といわれて死んだ。
awkを使って、動的にコマンドを生成しつつ回避。
#/bin/bash
# ファイルが 数万個のところでlsとかすると、「引数が長すぎます」とエラーが出る。
# awkを使って疑似回避。
# 非常に重たい。
ls -1d msgs/*/* | awk '{
command = sprintf("grep ERROR %s/*/*", $1);
system(command);
close(command);
}'