社畜なのでVドメインからMドメインにドメインの管理業者を変更した。そしたらDNSの設定やらなんやらを忘れていて、tech.portalshit.net が死んでた。最近は技術っぽい内容も www.portalshit.net に書いてるしここの存在意義が微妙になってきた。Jekyll、便利ではあるんだけど、rbenv で Ruby のバージョン上げるごとに gem install jekyll gsl
とかやるのが若干めんどくさく感じられるようになってきてしまった。ただ記事を書くためだけにそこまでやりたくないかも。
時刻・日付関連のテストを書くときに気をつけること
自戒エントリー。
自分は Mongoid を使って開発してるんですけど、時間の境界値のテストが不十分で問題に遭遇したのでメモっておきます。
Mongoid には Date 型や DateTime 型があるけど、データベースにデータを保存するときには Time 型に変換されます(MongoDB に Date とか DateTime がないから?)。Mongo Shell で DB の中の値を見ると Time 型の値が入ってる。
だからクエリを生成するときに Date.today
はなるべく使わない方がいい。Date.today
では時間を 00:00:00 として扱い、月末のデータの取得に失敗する可能性が高いから。Rails console で確認すると、以下のようになります。
pry(1.9.3-p194)> 1.month.ago.end_of_month
# => Fri, 31 Aug 2012 23:59:59 JST +09:00
pry(1.9.3-p194)> Date.today.prev_month.end_of_month.to_time
# => 2012-08-31 00:00:00 +0900
上記は二つとも「先月末の月の終わり」を検索していますが、後者では時間が 00:00:00 になっています。例えば以下のような Mongoid でのクエリでは、8月31日の午前0時0分1秒以降に作成された Document を取得することが出来ません。ナンテコッタイ!!!
Model.where(:created_at.lte => Date.today.prev_month.end_of_month)
「Date.today
を時刻の生成の起点にしたとしても、to_time
してあげればいいのでは?」と思う方もいるでしょう。確認してみましょう。
pry(1.9.3-p194)> Date.today.prev_month.end_of_month.to_datetime
# => Fri, 31 Aug 2012 00:00:00 +0000
#prev_month
メソッドが呼び出された時点のオブジェクトの型は Date
型なので、Date
型に対して時間の操作を行って最後に DateTime
型に
結論
時刻まで考慮した時間の範囲でクエリを作るときは以下に留意すると良いでしょう。
-
Date.today
は使わない -
DateTime.now
か1.month.ago
などを使う
Pygments を使って Syntax Highlight する Lokka プラグイン
ここにはっつけるコードのシンタックスハイライトには Google Code Prettify をこれまで使ってたんですけど、どうもいまいちでした。有名な SyntaxHighlighter も好きになれなかった。はてなブログのように綺麗なシンタックスハイライトさせたい! ということで作った。
かなりシャレオツな感じにシンタックスハイライトできるようになったと思います。拾ってきた Monokai スタイルの CSS を当てています。
動作環境
裏側で使っているのは Python の Pygments。JavaScript オンリーで色付けするやつよりもこいつの方が圧倒的に綺麗でした。なのでこのプラグインを使うには Python と Pygments が必要です。Heroku では動くんでしょうか。動作未確認です。
使い方
HTML の pre タグのクラス名を lexer として Pygments に渡します。Ruby のコードを書くのであれば以下のようにします。
<pre class="ruby">
<code>
class Book
def off
"all your book is 10 yen"
end
end
</code>
</pre>
やってること
JavaScript で pre タグを探してサーバーにコードの中身と pre タグのクラス名を投げると、Pygmentize された HTML が返ってくるようになっております。そいつを JavaScript で拾って pre タグの中身を入れ替え。
当初は
```ruby
class Book
def off
"all your book is 10 yen"
end
end
```
みたいな感じで GitHub 風にしたいと思っていて、以下のような JavaScript を書いていたんですが JavaScript 力が低すぎて断念。
$('.body').each(function() {
var Pygmentize = function(lexar, snippet) {
var result;
$.ajax({
type: 'POST',
url: '/pygmentize',
async: false,
data: {
lexar: lexar,
snippet: snippet
},
}).done(function(data) {
result = data;
});
return result;
}
var entryBody = $(this).text();
entryBody = entryBody.replace(/```(.+?) ([sS]*?)```/g,
function(whole, lexar, snippet) {
return Pygmentize(lexar, snippet);
}
);
$(this).html(entryBody);
})
※Ajax なのに async: false
とかしててイマイチ感ありますね。
とはいえ、汎用性の高い pre タグのクラス名を拾ってくる、という形での実装にしたので、 Markdown でなくても HTML でも Textile でもハイライトできるので結果オーライとします。
ちなみに Kramdown でクラス名を指定したいときは以下のようにするみたいです。
> hogehoge
{: .hoge }
Vim で RSense を使う
RSense という Ruby のプログラムを書いているときに、レシーバの型に応じた補完候補を表示してくれるソフトがあります。Emacs とか Vim と組み合わせて使うと便利らしいです。Java で IDE 使って開発すると補完候補がわさわさ出てきて殆ど鼻くそほじってるだけでプログラミングできるという話を聞いたので、Ruby でも鼻くそほじりながらプログラミングしたいなと思ってこいつを導入してみることにしました。春頃やったときはなかなかうまく Vim から使うことが出来なくて諦めてたんだけど 、つい最近できるようになったのでやり方をメモっておきます。
Mac でのお話です
前提条件ですが、Mac で使ってます。環境は Homebrew で構築してます。また RSense を使うには Java Runtime Environment が必要です。あなたと Java
RSense のインストール
JRE のインストールは済んでいるものとします。Homebre で RSense をインストールしましょう。簡単です。
brew install rsense
インストールが済んだら以下のようなメッセージが表示されると思うので、指示に従いましょう。
If this is your first install, create default config file:
ruby /usr/local/Cellar/rsense/0.3/libexec/etc/config.rb > ~/.rsense
すると ~/.rsense
というファイルが作られ、中身は以下のようになっています。
home = /usr/local/Cellar/rsense/0.3/libexec
load-path = /Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/site_ruby/1.9.1:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/site_ruby/1.9.1/x86_64-darwin11.4.0:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/site_ruby:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/vendor_ruby/1.9.1:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/vendor_ruby/1.9.1/x86_64-darwin11.4.0:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/vendor_ruby:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/x86_64-darwin11.4.0
gem-path = /Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1:/Users/morygonzalez/.gem/ruby/1.9.1
rbenv 使ってる人は load-path にちゃんと rbenv のパスが含まれているか確認して下さい。
Vim の設定
このままではまだ RSense がインストールされただけで、Vim から利用することができません。Homebrew で入れた RSense には rsense.vim がついてくるので、こいつを Vim の plugin ディレクトリにコピーします。
cp /usr/local/Cellar/rsense/0.3/libexec/etc/rsense.vim ~/.vim/plugin/
次に .vimrc で rsenseHome を指定しなければなりません。
let g:rsenseHome = "RSense home"
と書きます。僕はこの rsenseHome が分からなくてハマりました。先ほどの config.rb を実行したときに生成された ~/.rsense に書いてある home をここに指定します。なので rsenseHome は /usr/local/Cellar/rsense/0.3/libexec
です。
ここまで済んだところで Vim から RSense が認識されているか確かめます。適当に Vim を起動して :se ft=ruby
とし、:RSenseVersion
とコマンドを打ってみます。ここで RSense 0.3
のように成功されたら設定完了です。filetype が ruby になっているファイルで 1.
と打った後、補完候補を呼び出すコマンド(^X ^U
)で候補を呼び出せます。以下のような感じ。
“ネオコン” と連携させましょう
さらに言うと、たいていの Vim ユーザーの皆さんは neocomplcache も使ってるでしょうから、neocomplcache と RSense を連携させましょう。ネオコン作者の Shougo さんのブログ記事を参考にして下さい。
以上で完了です。これで Vim で Ruby な皆さんも鼻くそほじりながらプログラミングできますね。
Fukuoka.rb のご紹介
今年の3月くらいから、寿限無の和田さんが発起人になって Fukuoka.rb という勉強会をやってる。毎週木曜の 19:30 からで、ペ社は会場を提供している。勉強会と言ってもやってることは技術書の輪読で、最初は『メタプログラミング Ruby』を読んだ。メタプログラミング Ruby は中級者以上向けの本でなかなかレベルが高く、当初は 30人ちかくいた参加者が回を重ねるごとに減っていき、最後は5, 6人くらいしか参加者がいなかったけれども、歯を食いしばって出席し続けた。簡潔に書ける、美しいコードが書ける、という以上のことができる Ruby の側面を学んだと思う。いまは二冊目の課題図書として洋書の『Eloquent Ruby』を読んでいる。
福岡でやってる Ruby の勉強会、何回か出たことあるけど、Rails ハンズオンみたいな初心者向けの内容だったり技術より精神面を重視した内容だったりで個人的には消化不良な感じがあった。また開催が不定期で、プログラマ同士の情報交換がしにくいと感じていた。
Fukuoka.rb は原則的に毎週開催で、扱う内容も初心者よりというよりは中上級者向けで、本を読みながら喧々諤々とした議論も繰り広げられるし、自分はあまり参加しないのだけど勉強会の後はみんなで飲みに行ったりもする。業務で感じていた疑問を詳しい人に尋ねたり情報交換しやすい。
個人的には、中学生の頃に通っていた隣町の塾を思い出す。自分は田舎に住んでいたので、都会の塾に行くだけでレベルの高さに驚くとともに刺激を受けて、都会っ子に追いつこうと勉強を頑張った。Fukuoka.rb は参加者のレベルが高く、なかなかついて行くのが大変なのだけど、確実に Ruby 力が高まっていると思うし、出てて良かったと感じる。
Asakusa.rb や Yokohama.rb のような Ruby コミュニティをずっとうらやましく思っていたけど、これらに近い Ruby コミュニティが福岡にできつつあると思う。時々は Ruby コミッターの @nagachika さんも参加している。福岡在住の Ruby プログラマで腕に覚えがある人は、是非毎週木曜日に天神プライムビル8階で開催されている Fukuoka.rb にお越しください。面白くしていきましょう。
並にぎりを上にぎりにランクアップする技術
hsbt さんが会社に入ってから大きく雰囲気変わった。hsbt さんは並の寿司を上にぎりとかにランクアップするパワーがあると思う。IRC とか Ustream ごしにその存在感を感じる。こういう人がメンターだなんて今年ペ社に入った新卒エンジニアはとてつもなくラッキーだったと思う。世の中には訳のわからない Java の研修を延々受けさせられてる新卒 SE さんもいるのに、ペ社の新卒エンジニアは WEB+DB PRESS に寄稿するような人から最新シャレオツ Rails 開発事情をたたき込まれたのだ。下手をすると中途で入って実サービスに投入されてる経歴不詳の怪しい30過ぎのおっさんよりも良いコーディングマナーを身につけているかもしれない。新卒エンジニア氏一名が自分の部署に配属されることになったので、来月自分は戦力外通告を受けて気がついたらハローワークに日参しているかもしれない。
なんか hsbt さんばかり持ち上げてしまったけど antipop さんも Webistrano をさくっと Rails 3.2.5 で動くようにしたりとかすごい。技術力ある人は技術力に比例して行動力が高い気がする。やらなきゃと思ったこと、読まなきゃと思った技術書を一晩とかでさくっと実装・読破してしまう。そういうところが並のエンジニアと大きく異なる点だと思う。
Rails の skip_callback とか reset_callbacks とか
あるモデルがあって、#save が実行されたときに同一モデル内で複製したインスタンスも一緒に保存したかった。一個目の #save が走る前にコールバックメソッドを使って複製したインスタンスを保存するようにした。コードだと以下のような感じ。
class Model
before_validation :method_one
before_create :method_two
def method_one
...
end
def mothod_two
Model.reset_callbacks(:validation)
Model.reset_callbacks(:create)
@model = Model.new
@model.save
end
end
なんで reset_callbacks 呼んでるのかというと二回コールバックメソッドを走らせないため。一回目の #save (コントローラーから呼ばれる)が呼ばれたときだけコールバックメソッドを実行して、二回目の #save (モデルのコールバックメソッド内で呼ばれる)では実行したくないから。
しかしここではまってしまった。なんとコールバックメソッドで複製したドキュメントを DB 内で確認すると created_at が空になっている。なんじゃこりゃ。
どうも reset_callbacks(:create)
がいかんかったみたい。ORM が実行する create 周辺のコールバックメソッドも軒並みリセットされてしまう模様。
そういうわけで以下の様にして解決した。
class Model
before_validation :method_one
before_create :method_two
def method_one
...
end
def hoge
Model.skip_callback :create, :before, :sell
Model.skip_callback :create, :after, :send_notification
Model.reset_callbacks(:validation)
@model = Model.new
@model.save
end
end
callback、便利だけど奥が深い。ちなみにこれら skipp_callback とか reset_callbacks とかは ActiveModel や ActiveRecord (僕はMongoidで開発してます)などの OR マッパーのメソッドではなく、 ActiveSupport::Callbacks のメソッドだったりします。ActiveSupport も奥が深い。