その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Time::SecondsのONE_MONTHとONE_YEARについて

Time::SecondsONE_MONTHONE_YEARを使う場合,本当にその方法で良いのかよく考えたほうが良いと思います.バグが出る可能性が高い気がします.

例えば以下の様な場合

use Time::Piece;
use Time::Seconds;

my $tp = localtime->strptime("2014-04", "%Y-%m");
say $tp->mon; # => 4
say $tp->ymd; # => 2014-04-01

$tp -= ONE_MONTH;
say $tp->mon; # => 3
say $tp->ymd; # => 2014-03-01

$tp -= ONE_MONTH;
say $tp->mon; # => 1 (!)
say $tp->ymd; # => 2014-01-30 (!!)

このコードは,Time::PieceのオブジェクトからONE_MONTHを引いてやる事によって1ヶ月前の月を表現したいという意図を表していますが,上手く動きません.
それもそのはず,Time::Secondsの実装は以下のようになっており,

https://github.com/rjbs/Time-Piece/blob/master/Seconds.pm#L29-L31

ONE_MONTHは2629744秒 (つまりONE_YEAR / 12) のようにして定義されています.
30日を秒に表すと60 * 60 * 24 * 30 = 2592000秒だからONE_MONTHよりも小さいわけですね.
加えて2014年2月は28日しか無く,つまり60 * 60 * 24 * 28 = 2419200秒なので,ここで決定的に差が生まれてしまったという訳ですね.これは具合が良くない! もちろん「1ヶ月前の今日」みたいなものも上手くは取れないですね.

そしてONE_YEARONE_YEARで31556930秒,つまり365.24225日として表現されている為,こちらも普通に使うとおかしな事になってしまいます.

use Time::Piece;
use Time::Seconds;

my $tp = localtime->strptime("2014", "%Y");

$tp -= ONE_YEAR;
say $tp->year; # 2012 (!)
say $tp->ymd;  # 2012-12-31 (!!)

のっけからやってくれる!!!! まあそれはそうですよね,という感じ.
こちらも「1年前の今日」みたいなものは上手く取れない.


で,どうするかというと,Time::Piece::Monthを使うと言った方法が考えられますが,このモジュールは内部でDate::Simpleを使っていて,Date::Simpleは呪われている(呪われていた)という問題があるので少し渋い.この方法でも問題なさそうだったらこれで良いとは思います.
問題が複雑では無かったら,LEAP_YEARNON_LEAP_YEARONE_FINANCIAL_MONTHや,あるいはONE_REAL_MONTHONE_REAL_YEARを駆使して自分で組み立てるというのも1つの手かもわかりません.

とにかく,ONE_MONTHONE_YEARを使って何らかの日付け操作をしているコードを見つけた時は疑ってみたほうが良いと思います.


手が空いたらここらへんをケアするシンプルなモジュールを書くのもやぶさかではありませんが,果たして手が空くのか!


[追記]
Time::Piece::Plus使えばええんや!!!!! という僕の中の僕が囁きましたのでシェアします.

[追記]
id:karupanerura氏「add_months/add_years ってメソッド使えや!!!」
とのこと.なんで僕はこのメソッドの存在を知らなかったのだろうか!?

ともかくONE_MONTHとかONE_YEARとかつかってたら大体おかしいことには違いないので,そういう場合は疑うってことでひとつ.

WEB+DB Press Vol.81のPerl Hackers Hubに寄稿しました

WEB+DB Press Vol.81のPerl Hackers Hubに「Perlにおける静的解析」というタイトルで寄稿致しました.商業誌デビューです.
そうそうたる著者の皆様の末席を汚しているのが僕です.そうそうそいつそいつ.

WEB+DB PRESS Vol.81

WEB+DB PRESS Vol.81

最近個人的に,Perlに限らず静的解析周りに興味があったことや,TPFのPerl::Lintの話などもあり,それじゃまあそこら辺の話で良ければ書きますよ〜〜〜〜って事で書きました.


以下おしながきです

  • 静的解析とはなにか
  • 静的解析の (Javaでの)
  • Perlにおける静的解析の背景と課題
  • PPIを使ってみる
  • Compiler::LexerおよびCompiler::Parserを使ってみる
  • 便利なPerl向け静的解析ツールの紹介
  • Compiler::Lexerを使って実際に簡単な静的解析ツールを書いてみる


Compiler::LexerやCompiler::Parserが台頭してきたり,App::PRTなどの静的解析を用いた便利なツールがぽこぽこリリースされたりするなど,Perlの静的解析周りはここ最近面白い状況になっており,その辺の話題を中心に触れてみました.Perlの静的解析にまつわる歴史的経緯についても軽く紹介しています.


手前味噌な感じで恐縮ですが,Compiler::Lexerを使った静的解析ツールの書き方を日本語で解説しているのは中々珍しいのではないかな〜と思います.
解説に従って実際にコードを書いてみると,ソースコード中のタイポを検出するスクリプトが「あら簡単!」という感じに出来上がる寸法となっています.
基本的にはこの特集で紹介しているような方法を使えば,たいていの静的解析ツールは作成出来ると思うので,「静的解析ツール,書いてみたいワ!」という方の手助けになれば幸いです *1


また,Perl Hackers HubだというのにJavaツールの解説に1ページほど割いており,我ながら最高な気分になりました.


発売日は6月24日(火)です.
興味のある方はぜひお手にとってみて下さいませ!

*1:あとは複雑さの程度問題なので,実装者の根気次第といったところでしょう

ページャNightというイベントをやります

参考: http://togetter.com/li/678306








http://www.zusaar.com/event/5477013

ページャNightというイベントをやります.皆さん冗談だと思われていたようですが冗談ではない.

イベントのページにも書きましたが,ページャはWebサービスに於いて極めて重要なポジションを占めています.ページャの実装いかんでは本当にタコな感じになってUXを損なったりしますし,ページャは一見瑣末な存在に見えてしまうので何かと多機能を求めがちですが,そのようにするとその裏では多大なる異常な努力が必要となってしまいます.
このあたりのさじ加減やら技術的なテクニックやらは,人類がインターネットを手にしてから脈々と受け継がれている筈なんですが,ぎっちょん人々はページャの話をあまり進んでしたがらないので,そうしたデッド知見を共有しようじゃあないですか皆さん!!!! というモチベーションで開催致しました.ふるってご参加下さい.

とまあこう書きましたが別にlessとかmoreとかポケベルの話とかでも良いと思います *1


というわけでページャにお詳しい皆様のトークをお待ちしています.
「喋ってもいいよ〜」という人はイベントページのコメントか,@までメンション等でお知らせいただければと思います.
まだ喋ってくれる方の数が少なくて参っているので,ぜひともよろしくお願いします!!!


[追記]
ページャってのはpaginationのこととして話を進めていましたが,正しい表現ではなかったですね.まあもはやページャだったら何でも良いです!!!

*1:僕個人はWebのページャに興味がありますが!

行の末尾の空白文字と決別する

ソースコードの各行の末尾に空白文字があると精神衛生上良くないので消す派です.常に消しておくようにすると,VCSのdiffにノイズが乗らなくて良いと思います.

そこで僕は以下のようにVimを設定して対応しています.


以下のようにすると,保存時にフックして末尾の空白を削除出来るようになります.
また,:ToggleRemoveTrailingWhiteSpaceというコマンドを実行すると,
フックして削除するか否か,という状態をトグルさせることが出来ます.
色々な理由で *1 末尾空白を消したくない時にはトグルして機能を一時的に殺すと良いでしょう.

Markdownは末尾に半角スペースを2発入れる事によってブロック内での改行を行う仕様となっているため,
自動的に半角スペースを削除されると具合が悪いことになります.
従って,以下の設定ではMarkdownの編集時には常にdisabledになるようにしています.

" 保存時にフックして常に末尾の空白を削除する
let g:does_remove_trailing_white_space = 1
au MyAutoCmd BufWritePre * call s:removeTrailingWhiteSpace()
func! s:removeTrailingWhiteSpace()
  if &ft != 'markdown' && g:does_remove_trailing_white_space == 1
    :%s/\s\+$//ge
  endif
endf

" `:ToggleRemoveTrailingWhiteSpace` というコマンドを実行すると
" 保存時にフックして空白を削除するか否かをトグル
command! -nargs=0 ToggleRemoveTrailingWhiteSpace
  \ call s:toggleRemovingTrailingWhiteSpaceStatus()
func! s:toggleRemovingTrailingWhiteSpaceStatus()
  let g:does_remove_trailing_white_space =
    \ !g:does_remove_trailing_white_space
endfunc

また,以下のようにすると末尾の空白文字を真っ赤にハイライトすることが出来るため,可視化が捗って便利.
おまけでハードタブにもささやかに色を付けたりなどしています.

highlight TrailingSpaces ctermbg=red guibg=#FF0000
highlight Tabs ctermbg=black guibg=#000000
au BufNewFile,BufRead * call matchadd('TrailingSpaces', ' \{-1,}$')
au BufNewFile,BufRead * call matchadd('Tabs', '\t')

*1:既に末尾空白がたくさんあるプロジェクトのソースコードに変更を加える時に,自分の流儀をプロジェクトに押し付けるのもなんだか気が引けるなァ……みたいな時とか

MySQL-Reference-Manual-Version-CheckerというChrome拡張を書いた

さてMySQLを使った開発をしていると,必ずと言っていいほどMySQL Reference Manualを参照することになるわけですが,
頑張って読み解いていたマニュアル (しかも英語) が実はMySQL 4.1のマニュアルで,「僕の使ってる5.6のマニュアルとちゃうやんけキエエエエエ」となり,モチベーションが根こそぎ持っていかれるということがままあると思います.

そうした悲しい事故を防ぐためにこの度,MySQL-Reference-Manual_Version-CheckerというChrome拡張をこさえました.長い名前だ.
https://chrome.google.com/webstore/detail/mysql-reference-manual-ve/nbbkeofjffbleojiodgmdipcamgmmmee
https://github.com/moznion/MySQL-Reference-Manual-Version-Checker


何をする拡張かというと,設定したReference Manualのバージョン範囲外だった時に警告を出すという拡張です.
(デフォルトだと5.1 <= version <= 5.7というバージョンレンジになっています.つまりは4系以下だと警告が出るという感じです)


実際に動かしてみると以下のような感じです.

参照しているマニュアルのバージョンが外れている場合 (デフォルトの状態で4系を参照している場合);
f:id:moznion:20140606232532p:plain
代替ページ (バージョン範囲にマッチする中で最新のマニュアルのページ) へのリンクも提供しています.

マニュアルのバージョンが適切な場合は何も出ません;
f:id:moznion:20140606232644p:plain


というような感じです.地味に便利な気がしています!
ぜひご利用下さいませ.


ところでこのChrome拡張はTypeScriptを使って書いてみたんですが,なかなか良い感じでした.
その件についてはまた別の記事で紹介できればと思います.

chrome.storage.*.set()でInfinityやundefinedやNaNを格納出来ない問題

Chrome extensionで,extension localで横断的にlocalStorageを利用するためのAPIであるchrome.storageですが,chrome.storage.local.set()chrome.storage.sync.set()ではInfinityundefinedNaN*1 を格納することが出来ません.

以下検証コードです


localStorageではこれらの値は正しく取り扱うことができるので,chromeの仕様あるいはバグ感があったのでIssueを投げました.
Issue 380509 - chromium - chrome.storage.*.set() cannot set Infinity, undefined, and NaN - An open-source project to help move the web forward. - Google Project Hosting


使えるようになって欲しい感じです.

*1:とはいえNaNを格納したいことはあまり無いでしょうが

Released Log::Minimal::Object

皆さん,Perlのアプリケーションのログを出力したい時はLog::Minimalをご利用のことと存じます.非常に便利でグレートなモジュールなので僕も毎度使っております.


さて,そのLog::Minimalですが,ログの出力をカスタムしたい時には$Log::Minimal::COLOR$Log::Minimal::TRACE_LEVELなどといったLog::Minimalの持っている変数をいじってスコープごとにゴニョゴニョ〜〜みたいな感じにやるわけですが,ある程度複雑な条件に基いてログをカスタムしたい時 (例えばローカルからのアクセスの時だけ特殊なフォーマットでログを吐きたい,だとか) なんかは記述量が増えたりそのロジックが煩雑になったりして,ログ出力の為の記述が本質的なロジックの記述を食ってしまう場合があったりします.


そうした背景からこの度リリースしたのがLog::Minimal::Objectです.
https://github.com/moznion/Log-Minimal-Object
https://metacpan.org/pod/Log::Minimal::Object

Log::Minimal::ObjectはLog::Minimalに対するOOPインタフェースを提供する簡単なラッパーです.
使い方はドキュメントを読んでもらうとして,その特徴はインスタンスごとに独立して出力のカスタマイズ出来るという点にあります.あらかじめ個別に設定したロガーインスタンスを作っておいて,条件に基いて出力するロガーを切り替えてやる,という使い方が出来ると便利なのではないかと思ったのが主な動機です.
また,副次的な感じではありますが,ログを出力するメソッドをインスタンスごとの粒度で乗っ取る,みたいな使い方もできるのでテストの時などに便利なのではないかと思っています.


まあ,ある程度の規模感のアプリケーションだと独自でロガークラスを作ってそいつに仕事をさせる事も多いと思いますが,こうしたカジュアルに使えるモジュールがあっても良いのではないか,と思って作った次第です.ご利用下さい.