任意に並び替えがしたいナリ、とお客さんに言われたので、acts_as_listを使ってみた。
使い方はほぼこちらの通りでOK。簡単簡単。
http://tobysoft.net/wiki/index.php?Ruby%2FRuby%20on%20Rails%2Facts_as_list
最初あるいは最後のエントリかどうかは、model.first?とかmodel.last?で取れる(view内でとても便利)。ただし常時n+1問題が発生するので、頻繁に並び替えるような用途では危険。

ついでに、ソートしてpositionを付け直す機能の実装でちょっと悩む。
普通にmove_to_bottomを使って

items.each do |item|
item.move_to_bottom
end

とかやると、positionの値が現在の最後+1から付け直されてしまう(当たり前だけど)。機能的には何ら問題ないけど、ソートするごとにpositionが増えていって気持ち悪い。
acts_as_listのソースを読んで、


items.each_with_index do |item, index|
item.insert_at(index+1)
end

なんてコードを書いてみたら、positionが[1,1,1,1,5,5,5…]みたいな面白い並びになってしまった。ガッデム。
色々試行錯誤したけど、単純に


items.each_with_index do |item, index|
item.update_attributes(:position => index + 1)

end

で良かった。KISSですな。

リンクを新しいウィンドウで開くのにtarget = “_blank”はダサいしjavascript書くか、と思ってたけど、link_toに:popup => trueを付けるだけであとは勝手にやってくれるのね。知らなかった。
あと、1バイト文字と2バイト文字(UTFだから3バイト文字か?)の混在する環境で一定バイト数以下の文字列を切り出す方法。自分で実装したのよりリファレンスマニュアルの方がスマートだった。


def jleft(len)
return “” if len <= 0 str = self[0,len] if /.\z/ !~ str str[-1,1] = ” end [/ruby]

24. 3月 2010 · Write a comment · Categories: Git

gitのリモートリポジトリがおかしくなったので仕切り直し。
ディレクトリ切ってそこに移動して
% git init –bare
でもって作業側で
% git remote add origin ssh://xxx.com/src/product
% git push origin master
これだとリモートにはリポジトリしかないから、cloneするまでファイルは見れないんだけどね。

エントリが次々destroyされる謎の現象が発生。
すわMySQLがいかれたか、あるいはメモリが尽きて誤動作でもしてるのかと思ってパニクったけど、何のことはないhas_manyしている側のmodelになぜか:dependent => :destroyが入っていた…
どう見てもケアレスミスです。本当にありがとうございました。
どういうことかと言うと、関連元のエントリをdestroy→関連先のエントリがdestroyされる→同じエントリを参照してる関連元のエントリがdestroy→…という死の連鎖が起こってしまったのだった。おそろしい。
しかもfragment cacheがあったために(関連先を共有してるmodelにまでsweeperが働かないので、cacheは残り続けた)表面上は何事もないまま内部が壊死していくというホラー。あはははは。
サービスインした後だったら首吊ってた。動き出してからmodelなんていじらないけどさ。

メール受信がタイムアウトしたので、何かトラブルかと思ってサーバにsshしたら、プロンプトが出るまで数分かかった。
何か刺さってるのかとtopを見たが特にCPU食ってるタスクもない。メモリ使用量の多いタスクを止めようとして、いつもの調子で/usr/l→TABとかやったら固まる(1分後くらいに反応)。な、何が起きている!?
ひとまず落ち着いてtail /var/log/messages…また固まった。今度は何分待っても出てこない。C-c連打でやっとプロンプトに返ってきた。
再起動…が一瞬頭をよぎるが、もし再起動に失敗したらNOCの主が帰ってくるまでどうしようもなくなる。
祈るようにdmesg。今度は反応があった。そこには
ad4: FAILURE – READ_DMA status=51 error=40 LBA=8
g_vfs_done():ad4s1e[READ(offset=23891902464, length=16384)]error = 5
がびっしりと…うわああああ。
ついにディスクが逝ったか…と思ったけどよく見るとoffsetの値は全部同じ。どうやら全面的に壊れたわけではなさそうだ。要するに破損したセクタを読み込もうとして固まっている様子。
offset=23891902464, length=16384はそれぞれ単位がbytesなので、512で割って46663872番目から32セクタがいかれてると思われる。
とりあえずddで破損セクタをサーチ。
# dd if=/dev/ad4s1e of=/dev/null skip=46663872 count=32 conv=noerror
案の定先ほどのエラーがコンソールに出るが、他に破損セクタはなく終了。んじゃbadsectするか…と思ったが、この時点で反応が正常に復帰。先ほどのddをもう一度実行すると、今度はノーエラーで終了した。どうやらhdd自身のセクタ代替機能が働いて回避するようになったらしい。
念のためsmartmontoolsをインストールして、
# smartctl -A /dev/ad4
を実行してみると、Reallocated_Sector_Ctが173。173セクタほど破損しているようだ。幸い、断末魔の叫びであるSpin_Retry_Countはゼロなので、今日明日壊れるということもない様子。まぁ、不安だから早めに交換しよう…

n+1

いくつか残っていたn+1問題を解決するために、:includeをごりごり挿入。
以下メモ。
普通のinclude :include => :item
複数include :inluce => [:item, :another, :yetanother]
関連先もinclude :inluce => [{:item => :related_items}, :another]
acts_as_treeを使ってると、:include => :childrenで子ツリーを先読みできた。すばらしい。
(has_manyの時はちゃんと複数形にすること)
注意点として、:throughで中間テーブルを使って、かつ関連先が削除されていた場合、 @item.related_itemsだと削除済みのエントリは読み込まれないけど、Item.find(:all, :include => :related_items)にしてると配列にnilが入ってしまい、関連先の処理をしようとしたところで怒られてしまう。(お約束のnil class発生)
回避するためには中間テーブルに:dependent => :destroyを付けておかないといけないが、不幸にしてやってなかった場合には手でテーブルを編集するか、@item.related_items.compact.eachとかダサいことをしなくてはならない。しょんぼり。

Emacsの調子が悪いので作り直すことにした。
が、Emacsのリポジトリが移行したので、cvsだと23.0までしか取得できない。ちなみに新しいSCMはbazaar。なんでそんなマイナーなの使うんだ…gitでいいじゃん。まぁいいか。
http://wiki.bazaar.canonical.com/MacOSXDownloads
から入手してインストール。GUIつきだけどコマンドラインから起動する。
% bzr explorer
で起動、http://bzr.savannah.gnu.org/r/emacs/trunkをチェックアウト。でもって例によって
% patch -p0 < ../emacs-inline.patch % ./configure –with-ns –without-x % make bootstrap % make install でビルド、nextstep/Emacs.appをApplicationsに入れてやればOK。 特に問題なく動いているようだ。 バージョンも24.0.50になってEDGEって感じ。 この後調子悪いので23.1.93に落としました。orz

<%= javascript_include_tag(:all, :cache => true) %> 

としてあると、
config.action_controller.perform_caching = false の状態では表示がおかしくなる。
(lightboxのモーダルウィンドウが出っぱなしになる)
どうにも原因不明なので、諦めてdevelopment環境でもキャッシュを有効にして作業してたんだが、viewをいじったりするたびにいちいちキャッシュを削除するのが激しくめんどくさいのできちんと調べてみた。

めんどいのでいきなり結論。
:cache => trueだとall.js(デフォルト)というJavaScriptを一つにまとめたファイルが生成されるんだけど、これが残ったままキャッシュを無効にするとall.jsまで読み込もうとしておかしくなる。
キャッシュまわりの開発のため一時的に有効にした時に生成されていた(そしてそのままgitリポジトリに取り込まれていた)のが残ってたらしい。
まぁ、考えたら当たり前なんだけど…
.gitignoreに加えておこう。
ついでに教訓。git commit -aする時はよーく考えてからにしよう…

pushが破壊的メソッドなのをうっかり忘れててえらい目にあった。
定数の配列の最後に引数を追加して返すメソッドを

FixArray.push(param) 

とか書いてた。その結果mongrelのインスタンスごとに定数が異なり、リロードされると配列がどんどん長くなるという怪奇現象が発生。我ながらなんて恐ろしいことを…

new_array = FixArray + [param] 

でおkですね。おkだよな…?
基本的すぎるミスで深く反省。

acts_as_treeを使ったモデルで

def branch 
  branch = [self] 
  if self.parent 
    branch.concat(self.parent.branch) 
  end 
  branch 
end 

みたいなメソッドを定義して、model.branchでツリーをモデルオブジェクトの配列で返す(パンくずリンクなんかに使う)ようにしてたんだけど、何故かparentが自分自身になってるレコードがあって、無限参照でスタックオーバーフローを起こしていた。どうしてこうなった…
unless self.parent == selfを入れて解決。
しかしサービスイン前に発覚してよかった。冷や汗物であった。