JSCron よりもうちょっと Cron っぽいことをする
JSCron が Cron としてはかなりいまいちで不満だったので、適当にでっちあげてみた。
if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (elt /* , from */) { var len = this.length; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (;from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; } } function Cron() { var self = arguments.callee; if (self.instance == null) { this.initialize.apply(this, arguments); self.instance = this; } return self.instance; } Cron.prototype.month_mapping = [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ]; Cron.prototype.week_mapping = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ]; Cron.prototype.REGEXP_NUMBER = '((?:\\d|\\*)(?:[\\d\\*\\,\\-\\/]*\\d)?)'; Cron.prototype.REGEXP_MONTH = '((?:\\d|\\*)(?:[\\d\\*\\,\\-\\/]*\\d)?|' + Cron.prototype.month_mapping.join('|') +')'; Cron.prototype.REGEXP_WEEKDAY = '((?:\\d|\\*)(?:[\\d\\*\\,\\-\\/]*\\d)?|' + Cron.prototype.week_mapping.join('|') + ')'; Cron.prototype.REGEXP_CRON = new RegExp( '^' + [ Cron.prototype.REGEXP_NUMBER, Cron.prototype.REGEXP_NUMBER, Cron.prototype.REGEXP_NUMBER, Cron.prototype.REGEXP_NUMBER, Cron.prototype.REGEXP_MONTH, Cron.prototype.REGEXP_WEEKDAY ].join(' ') + '$', 'i' ); Cron.prototype.initialize = function() { this.tasks = {}; this.counter = 0; return this; } Cron.prototype.wakeUp = function() { var self = this; this.pid = setInterval(function() { self.exec.apply(self); },1000); } Cron.prototype.shutdown = function() { clearInterval(this.pid); } Cron.prototype.parse = function(cronfield) { function expandAsterisk(max) { var expando = []; for(var i = 0; i < max; i++) expando.push(i); return expando; } function expandInterval(spec, max) { var expando = []; var sp = spec.split('/'); if (sp.length != 2) throw "illigal spec: " + spec; var interval = parseInt(sp[1]); if (interval <= 0) throw "illigal spec: " + spec; var range; if (sp[0].charAt(0) == '*') { range = [0, max - 1]; } else { range = sp[0].split('-'); range = [parseInt(range[0]), parseInt(range[1])]; } if (range.length != 2 || range[0] > range[1]) throw "illigal spec: " + spec; for (var pos = range[0], max = range[1]; pos <= max; pos += interval) expando.push(pos); return expando; } function expandRange(spec, max) { var expando = []; var range = spec.split('-'); range = [parseInt(range[0]), parseInt(range[1])]; if (range[0] < 0 || range[1] > max) throw "illigal spec: " + spec; for (var i = range[0], rangeMax = range[1]; i <= rangeMax; i++) expando.push(i); return expando; } function expand(spec, max) { var expando = []; var intValue = parseInt(spec); if (typeof spec == 'number') { expando.push(spec); } else if (spec.indexOf(',') != -1) { var specs = spec.split(','); for (var i = 0, length = specs.length; i < length; i++) expando = expando.concat(expand(specs[i], max)); } else if (spec.indexOf('/') != -1) { expando = expando.concat(expandInterval(spec, max)); } else if (spec.indexOf('-') != -1) { expando = expando.concat(expandRange(spec, max)); } else if (spec == '*') { expando = expando.concat(expandAsterisk(max)); } else if (intValue >= 0 && intValue <= max) { expando.push(intValue); } else { throw "illigal spec: " + spec; } return expando; } var timespec = this.REGEXP_CRON.exec(cronfield); if (!timespec) throw 'illigal arguments: ' + cronfield; timespec.shift(); var month = this.month_mapping.indexOf(timespec[4].toLowerCase()); var weekday = this.week_mapping.indexOf(timespec[5].toLowerCase()); if (month != -1) timespec[4] = month; if (weekday != -1) timespec[5] = weekday; if (timespec[5] == 7) timespec[5] = 0; return [ expand(timespec[0], 60), // seconds expand(timespec[1], 60), // minutes expand(timespec[2], 24), // hours expand(timespec[3], 31), // day expand(timespec[4], 12), // month expand(timespec[5], 7) // weekday ]; } Cron.prototype.register = function (fn, spec) { var id = this.counter; if (id == 0) this.wakeUp(); var sp = this.parse(spec); this.tasks['_' + id] = { callback: fn, timespec: sp }; this.counter++; return id; } Cron.prototype.exec = function () { var now = new Date(); var currents = [now.getSeconds(), now.getMinutes(), now.getHours(), now.getDate(), now.getMonth(), now.getDay()]; for(var i in this.tasks) if (this.tasks.hasOwnProperty(i)) { var spec = this.tasks[i]; var matched = true; for (var l = 0, len = currents.length; l < len; l++) { if (spec.timespec[l].indexOf(currents[l]) == -1) { matched = false; break; } } if (matched) spec.callback.apply(); } return this; } Cron.prototype.cancel = function(id) { delete this.tasks['_' + id]; for (var i in this.tasks) if (this.tasks.hasOwnProperty(i)) return this; this.shutdown(); return this; } /** console.log(new Date()); var timer = new Cron(); var pid = timer.register(function() { console.log(new Date()); }, "1,3,10-15,20-59/5 0-33/1,35-37,40-59 * * JUL mon"); setTimeout(function() { console.log('cancel: ' + pid); timer.cancel(pid); },10000); */
http://github.com/send/misc/tree/master の js/cron.js に置いてありますが、 gist にしなかったのはバカだなーと思いますが、コミットしたら面倒になってしまったので gist や coderepos 等に持っていきたい人は好きに持っていって弄って下さい。
バグは勿論、パフォーマンス面やメモリの使いかた等、不味いところはまだまだあるのはわかってるので、なんかあったら教えて下さると俺が喜びます。
if 文と {} とコーディングスタイル
最近、また if 文における {} 省略の話をよく見掛けるようになった。何年たっても繰り返される話なんだろうなと思う。
なので、人のことをとやかくいう前に、自分のコードの変遷を考えてみると、簡単に言うと以下の様になる。
- OOP 厨以前
- {} 必須派
- {} なしはバグの温床
- OOP 厨時代
- {} 以前に分岐ありえない
- {} 以前に分岐は悪。クラス分けするのが正義
- OOP 厨脱却後
- 分岐はあり。{} は基本的に使わない
- 後々ややこしいことありそうだなと思うところは {} つける
- {} ありの部分は、将来リファクタリング入る可能性もあるので要注意
三項演算子の話も似たようなことでよく出てくる話なんだけど、どっちの話も同じような話で、そもそも意味がわかってないという人と、使いかたが合理的じゃないという人が槍玉に上げられてるような気がする。
で、そういうことが発生しないようなコーディング規約みたいな制約をみんな使っちゃっていて、そういうものの大半は、ダメな人がダメなことをしないようにという排除方向に向かってるとしか思えない。
勿論、それが局所的な状況においては必要なケースもあるだろうし、政治的な理由でそうなることも多いと思う。
でも、俺は十把一絡でそういうことをやるのがいいとは思えないし、その人のレベルによって自由度はある程度以上は絶対必要だと思う。そのスキルに応じて、その人が納得出来るコードや自然な書き方というものを書いていける環境は、ちゃんあるべきだと思う。
そういうことを考えると、プログラミング一つを取っても、習熟度に応じたインタフェースという意味での規約があって、そこを超えたらまた変わってくるコーディングスタイルが出てきてという形が、綺麗なのかなと考えたりしてる。
まあ、そんな観点は抜きにしても、 if の後に {} 使ってるかどうかなんてレベルの話であれば、そういうチェッカーは殆どの環境でツールとして存在してるし、そういうツールが無かったとしたら、あなたがその環境のことを知らないのでもっと勉強しなきゃいけないレベルか、逆にアーリーアダブター過ぎるので実装すればみんな幸せあなたも幸せってことなんですよね。
結局、コーディングスタイルレベルだったら、殆どの場合、機械的に抑えられる話だし、大した要件じゃない。
そもそも {} あるなし論争で済むような短いロジックだったらテストコードをちゃんと書かせた方がよっぽどマシだ。
それとも、昨今の状況でも if と {} とかで喧喧囂囂とかするレベルの難しいことがいろいろあるのか?
そんなことが致命的なレベルで重要な話なの?
そんなことより、ウォーターフォールでも TDD でもなんでも良いんだけど、品質保証しづらい一番キツい部分をどう担保するかみたいな部分の方が大切じゃないのかなあなど思ったりする。
プロンプトの色をローテートする
Introduction of the ZSH に載ってるやりかただと乱数を使ってるために同じ色になったりしてちょっと不満だったので zshrc を以下のようにしてみた。
PROMPT_COLOR=32 precmd() { PROMPT_COLOR="$[32 + ($PROMPT_COLOR - 31) % 5]";} PROMPT=$'%{^[[${PROMPT_COLOR}m%}%U%n@$HOST'"%u%{^[[m%} %(!.#.$) " RPROMPT=$'%{^[[${PROMPT_COLOR}m%}[%~]%{^[[m%}'
これで、行が変わる毎にプロンプトの色が 32-37 の間でローテートしてくれていい感じになる。
Twitter でベーシック認証越しになにかやるのは控えた方が良い
PHP6 を使ってみる 1
PHP6 をそろそろ試してみようと思ったので、まずはインストールから。
OS は、 Mac OSX Tiger.
インストール先は /opt/local/php6 .
まずは configure.
./configure \ --prefix=/opt/local/php6 \ --with-apxs2=/opt/local/apache2/bin/apxs \ --enable-filter=shared \ --with-openssl=shared \ --with-bz2=shared \ --with-curl=shared \ --enable-exif=shared \ --with-gd=shared \ --with-jpeg-dir=/opt/local \ --with-png-dir=/opt/local \ --with-zlib-dir=/opt/local \ --with-freetype-dir=/opt/local \ --enable-gd-native-ttf=shared \ --enable-gd-jis-conv=shared \ --enable-pdo \ --with-pdo-mysql=/opt/local/lib/mysql5 \ --with-ldap=shared \ --with-imap=/opt/local \ --with-kerberos \ --with-imap-ssl=shared \ --enable-mbstring \ --with-xsl=shared \ --with-xmlrpc=shared \ --enable-zip=shared \ --enable-intl=shared \ --with-gettext=shared,/opt/local \ --with-mcrypt=shared,/opt/local
いちいち *-dir の類にパス指定するのも馬鹿っぽいから LD_LIBRARY_PATH や LDFLAGS に /opt/local/lib 入れたりしたんだけど、結局上手く行かずにベタに指定したけど、そういうもんなのかな?
あと、入ってると make で失敗するオプションが --enable-phar
. /Users/kazuaki/work/php/php6/php6.0-200807250830/ext/phar/phar.php requires PHP extension Phar.
って言われても、どうすんだよって感じ。 PHP5 の Phar は入ってるけど、そこじゃないんだろな。
気になるけど、追っかけようと思ったけど、今日は力尽きたので、やる気になったら調べる。
PDO に関しても OSX だと shared は、まだサポートされてなかったりで shared 外したりした。
んで configure 終わったら、いつものように make && make test
んで、 sudo make install
最後に、cd /opt/local/bin/; sudo ln -s /opt/local/php6/bin/php php6
した。
疲れたので今日はここまで。
URL っぽい文字列にリンク当てる vimperator plugin
URL っぽいのにリンク貼られてない場合、開くのが面倒だなと感じてきたので作った。
こういうのは GM とかの方が良いかなあとか思ったけど、重そうなので vimperator plugin で。
:anc
で、その buffer の url っぽい文字列にリンク当てる。
liberator.buffer.evaluateXPath() とか Range.surroundContents() 初めて使ったけど便利。
.zlogin にした
前回の続き。
ログイン項目に登録しても上手く動かなかったので、 .zlogin に仕込んで動かすようにしてる。
スクリプト自体もコマンドの取扱いをちょっと神経質にしてみたり、ちょっとした bug fix した。
supervisedium
#!/bin/sh RM="/bin/rm" CAT="/bin/cat" OPEN="/usr/bin/open" WHOAMI="/usr/bin/whoami" SLEEP="/bin/sleep" PIDFILE="/tmp/supervisediumd.`$WHOAMI`" COMMAND="/Users/kazuaki/bin/supervisediumd" case $1 in "clean" ) echo "cleaning pidfile: $PIDFILE" $RM -rf $PIDFILE ;; "start" | "" ) if [ -f $PIDFILE ] ; then echo "supervisediumd is already running" exit 0 else $COMMAND $PIDFILE & echo "starting supervisediumd $!" fi ;; "stop" | "shutdown" ) echo "stopping supervisediumd..." if [ -f $PIDFILE ] ; then kill `$CAT $PIDFILE` fi ;; "restart" ) echo "restarting supervisediumd..." if [ -f $PIDFILE ] ; then kill `$CAT $PIDFILE` fi while [ -f $PIDFILE ] ; do $SLEEP 1 done $COMMAND $PIDFILE & ;; * ) $CAT <<_EOT_ usage: $0 [start|stop|shutdown|clean|restart] start : start supervisediumd stop, shutdown : shutdown supervisediumd clean : cleaning pidfile restart : restart supervisediumd _EOT_ ;; esac
supervisediumd
#!/bin/sh # settings COUNT_LIMIT=20 CPU_LIMIT=750 APP="/Applications/Adium.app" AWK="/usr/bin/awk" GREP="/usr/bin/grep" PS="/bin/ps" EXPR="/bin/expr" OPEN="/usr/bin/open" SLEEP="/bin/sleep" RM="/bin/rm" PSINFO=`$PS ux | $GREP $APP | $GREP -v $GREP` if [ -z "$PSINFO" ] ; then $OPEN -a $APP fi PIDFILE=$1 if [ -f $PIDFILE ] ; then if [ -z "$PSINFO" ] ; then echo "start supervising adium" else echo "supervisediumd is already running" fi exit 0 fi trap "$RM -rf $PIDFILE; echo 'stopped supervisediumd'" EXIT echo $$ > $PIDFILE echo "start supervising adium" COUNT=0 while [ 1 ] ; do PSINFO=`$PS ux | $GREP $APP | $GREP -v $GREP` PID=`echo $PSINFO | $AWK '{print $2}'` CPU=`echo $PSINFO | $AWK '{print $3 * 10}'` if [ $CPU -gt $CPU_LIMIT ] ; then COUNT=`$EXPR $COUNT + 1` if [ $COUNT -gt $COUNT_LIMIT ] ; then kill $PID $OPEN -a $APP COUNT=0 fi else COUNT=0 fi $SLEEP 1 done