kaeruspoon

会社でガンダム00を見る

機動戦士ガンダム00 (1) DVD

 会社で、仕事のあとにガンダム00を見ました。Filnでは、主要なアニメは全部HDレコーダに録画されていて、いつでもバカでかいテレビで見ることができるのです。
 ガンダムはここ2週間ほど見逃していたのでした。最初はいまいちだったガンダム00も、なんだかだんだんとおもしろくなってきたね。

ニコニコを戻しました。

 ニコニコの外部プレーヤーを元にもどしました。今はやっぱり、はてなとかlivedoorとかにしか許可されていないみたいです。kaeruspoonみたいな個人ブログで見られるようになる日がくるのでしょうか。Filnのリプレースが完了したら、日記自体をFilnに移してもいいかなあと思わないでもありません。

tokyobikeで新横浜まで行く

 ひさしぶりに tokyobike でサイクリングに行ってきました。ユルさんも一緒だったので、比較的のんびりペース。そのおかげか、走行距離が40kmを越えても疲労感はまったくありませんでした。でもおしりがめちゃ痛い。レイパンがやっぱりいるなあ。新横浜の日産スタジアムではフリーマーケットをやってました。

 【走行距離】 42.02km
 【最高速度】 28.3km/h
 【平均速度】 12.7km/h
 【走行時間】 3時間18分16秒
 【総走行距離】 309.4km

最近のお仕事はストアドプロシジャ

 最近はRubyを離れてストアドプロシジャ・ファンクションを組んでいます。Filnのリプレースに関するRailsプログラムは完了していて、今はDB移行のためにいろいろと試行錯誤中。最初はRubyでスクリプトを組んでDB移行をしようとしていたのですが、ストアドプロシジャのあまりの速さにRubyスクリプトは放り投げてしまいました。

webフレームワークのWavesを試してみた

 あまり時間がなかったのだけど、Rubyで書かれたwebフレームワークのWavesをチュートリアルに従って試してみました。
 まずはインストール。

sudo gem install waves

アプリの作成。

waves blog

つづいて configuration/default.rbでDBの設定をします。Railsでいうところのconfig/database.ymlと同じかな。

module Blogs
  module Configurations
    class Default < Waves::Configurations::Default
      database :host => "localhost", :adapter => 'mysql', :database => 'blogs',
        :user => 'root', :password => ''
    end
  end
end

つづいてmigrationの作成。

rake schema:migration name=initial_schema

すると、schema/migrations/001_initial_schema.rbにmigrationファイルが作成される。このあたりはRailsとほぼ一緒だね。Rails同様、作りたいテーブルのカラムを定義する。

class InitialSchema < Sequel::Migration

  def up
    create_table :entries do
      primary_key :id
      text :name
      text :title
      text :summary
      text :content
    end
  end

  def down
    drop_table :entries
  end
end

こんな感じ。primary_keyは明示的に指定してやんないといけないのかな。Railsよりは書きやすいです。
それからmigrateの実行。

rake schema:migrate

DBを確認すると、確かにentriesテーブルが作成されています。

それからコンソールで確認してみる。このirbみたいなやつはRailsにもあるけど、ぼくはほとんど使ったことがありません。

[tsukasa@] $ waves-console 
irb(main):001:0> M=Blog::Models
=> Blog::Models
irb(main):002:0> M::Entry.all
=> []

どうやら、Blogという名前空間の下に、すべてのモジュールが存在しているよう。Blog::Modelsで、すべてのModelを指し示していて、さらにその下にEntryというクラスが存在している。Wavesも、テーブル名は複数形、モデルは単数形の名前になるみたい。

irb(main):002:0> E = Blog::Models::Entry
=> Blog::Models::Entry
irb(main):003:0> E.create :title => "article title", :name => "tsukasa", :content => "yes", :summary => "y"
=> #<Blog::Models::Entry @values={:content=>"yes", :summary=>"y", :name=>"tsukasa", :title=>"article title", :id=>1}>

こんな感じで、モデルのインスタンスを作成できる。
そして、てっきりmodelsディレクトリの下にentry.rbでも作られるのかと思っていたら、そんなものはなかった。ふーん。

とりあえず、今日はここまで。明日はビューをやってみよう。

Wavesを試してみる - View編

 昨日に引き続き、webフレームワークのWavesを試してみます。今日はビューから。
 ビューのファイルはtemplatesディレクトリ下にあるみたい。チュートリアルでは、entry用のビューのためにentryディレクトリを作れと書いてある。

mkdir templates/entry

で、このtemplatesディレクトリ下にはlayoutsディレクトリとerrorsディレクトリが存在しているのだけど、名前から想像できるようにlayouts/default.mabが、Railsでいうapp/views/layouts/application.html.erbに該当するみたい。

doctype :html4_strict

html do

  head do
    title @title
  end

  body do
    layout_content
  end

end

えっと、これはなんだろう。気づいてみれば、拡張子も.mabとなっている。やっていることは見れば理解できるけど…。
どうやらこれはMarkabyというRubyっぽいテンプレートエンジンだそうです(Markup as Rubyの略らしい)。これは簡潔でいいね。と、言いたいところだけど、これってデザイナさんは置いてけぼりだ。ひとりで開発するなら全然ありだけど、チーム開発では難しいかな。WavesはMarkabyの他にもErubisをサポートしているので、そっちを使うのもいいかも。
 でもぼくはErubisよりもMarkabyが使いたいと思っているようなのでこのまま進めます。
 チュートリアルでは、templates/entry/list.mabとして以下のコードを書けと言っている。

layout :default, :title => 'Blog Entries' do
  h1 'My Blog'
  @entries.each do |entry|
    view :entry, :summary, :entry => entry
  end
end

 layoutというヘルパーが、default.mabでのlayout_contentにyieldされるみたい。Railsとちがって、これは指定してあげないといけないみたい。ここで引数に:title を指定しているけど、これがdefault.rbでは@titleになるんだな。
 @entriesには、Entryモデルのデータが勝手に入っているらしい。コントローラでモデルから取り出すわけじゃないの? 詳細はのちほどと書かれているので、とりあえずスルーしておこう。
 そしてイテレータブロックの中のviewヘルパーが、Railsでいうところの render に当たるようだ。 これは、Railsで書くならこんな感じのことを意味しているみたい。

  <%= render :partial => "entry/summary", :locals => {:entry => entry} %>

さて、呼び出され側のsummary.mabは以下のとおり。

h2 do
  a @entry.title, :href => "/entry/#{@entry.name}" 
end
textile @entry.summary
a 'Read more ...', :href => "/entry/#{@entry.name}" 

内容は見てのとおり。URLがRailsとは違ってid値を使っていない。個人的にはこっちのほうが好みだけど、この場合だとブログの記事名をユニークにしないといけなくるなる。まいっか。
textileというヘルパがよくわからないけど、ちゃんと説明がない。とりあえずスルー。

ビューに関しては以上。で、サーバを起動する。

waves-server

おー立ち上がった。デフォルトのポート番号は3000。このwebサーバはなんだろう。独自のものなのかな。URLは /entries。間違って、/entryにアクセスしたらカラフルなエラーページが表示された。カッコいい。

で、まだshowの画面がないので作れと書いてある。

layout :default, :title => @entry.title do
  h1 @entry.title
  textile @entry.content
end

これでとりあえずは動きます。コントローラやURLルーティングはいつ書くんだ。というか書かなくてもこのくらいは動くんだね。

それからcreate。チュートリアルではlist.mabにフォームを追加しているけど、そこはviewを使って書いてみた。

layout :default, :title => 'Blog Entries' do
  h1 'My Blog'

  view :entry, :form

  @entries.each do |entry|
    view :entry, :summary, :entry => entry
  end
end

form.mabは、

form :action => '/entries', :method => 'post' do
  label 'Name'
  input :type => :text, :name => 'entry.name'
  input :type => :submit, :value => 'Add'
end

こんな感じ。Rails同様、WavesもRESTfulなURLを採用しているみたいで、createはリストに対してpostメソッドを送っている。
それから編集用のページをeditor.mabとして作成してみる。

layout :default, :title => 'Edit Entry' do
  form :action => "/entry/#{@entry.name}/", :method => 'POST' do
    label 'Title'; br
    input :type => :text, :value => @entry.title, :name => 'entry.title'; br
    label 'Summary'; br
    textarea @entry.summary, :name => 'entry.summary', :rows => 10, :cols => 80; br
    label 'Content'; br
    textarea @entry.content, :name => 'entry.content', :rows => 20, :cols => 80; br
    input :type => :submit, :value => 'Save'
  end
end

ここでは、個別のentryに対してpostメソッドを送っている。RailsみたいにPUTではないらしい。
ビューはとりあえずこんな感じかな。

Wavesを試してみる - Model編

 引き続きWavesを試してみます。
 チュートリアルによると、entryに1:nでcommentモデルとの関連付けをしています。
 まずはCommentsテーブルのmigrationを作成します。

rake schema:migration name=add_comments

で、schema/migrations/002_add_comments.rbを編集。

class AddComments < Sequel::Migration

  def up
    create_table :comments do
      primary_key :id
      foreign_key :entry_id, :table => :entries
      text :name
      text :email
      text :content
      timestamp :created_on
    end
  end

  def down
    drop_table :comments
  end

end

 そういえば、RailsではDBレベルでforeign_keyをあまりつけてないな。あと、created_onはSequelによって自動的に時刻が入るよう。Railsでいうcreated_atだね。created_onだと日付しか入らないみたいな感じがしてしまうのは、Railsの使いすぎか。ちなみにSequelというのはORMです。
 migrateをかましたら、Entryモデルを作成します。ていうか、今まで存在しなかったのかよ。デフォルトの動作がWavesの中で定義されていて、それ以外のことをやりたくなったときにはじめてコードを書くのかな。

rake generate:model name=entry

で、models/entry.rbが作成される。

module Blog
  module Models
    class Entry < Sequel::Model(:entries)
      before_save do
        set(:updated_on => Time.now) if columns.include? :updated_on
      end
    end
  end
end

before_saveはRailsと一緒かな。テーブルがupdated_onを持っていたら更新してくれるみたい。created_onはSequelでやってくれるのに、updated_onは自前でやるのか。中途半端だなあ。Wavesが勝手にコード吐いてくれるけど。
で、models/default.rbの中身がこれと一緒になっている。明示的にモデルを作成しなかったときは、default.rbが使われるんだな。たぶん。
さて、models/entry.rbにcommentmモデルとの関連付けを定義しましょう。

one_to_many :comments, :from => Blog::Models::Comment, :key => :entry_id

こいつをbefore_saveの下に追加します。Railsでいうところのhas_manyと一緒だね。相手のモデルと参照キーを指定してやんないといけない。fromって、なんだかぼくの認識だと参照先みたいな感じがしてしまう。
つづいてcommentモデルを作成する。

 rake generate:model name=comment

models/comment.rbは

module Blog
  module Models
    class Comment < Sequel::Model(:comments)      
      before_save do
        set(:updated_on => Time.now) if columns.include? :updated_on
      end
      one_to_one :entry, :from => Blog::Models::Entry
    end
  end
end

one_to_oneはRailsでいうbelongs_toだな。ここでのfromは違和感がない。

さて、viewのほうにコメントを追加してみます。チュートリアルからちょっとアレンジしてみます。templates/entry/show.mabは

layout :default, :title => @entry.title do
  a "すべて見る", :href => '/entries'
 
  h1 @entry.title
  textile @entry.content

  h2 "コメント"
  view :comment, :add, :entry => @entry
  view :comment, :list, :comments => @entry.comments
end

こんな感じ。見てわかるように、@entry.commentsみたいな感じでAR的にリレーション先へアクセスできるようです。
さて、add.mabとlist.mabをtemplates/commentディレクトリ下に作ります。
templates/comment/add.mab

form :action => "/comments", :method => 'POST' do
  input :type => :hidden, :name => 'comment.entry_id', :value => @entry.id
  label "名前"; br
  input :type => :text, :name => 'comment.name'; br
  label "Email"; br
  input :type => :text, :name => 'comment.email'; br
  label "コメント"; br
  textarea :name => 'comment.content', :rows => 10, :cols => 80; br
  input :type => :submit, :value => "書き込み"
end

POST先のURLが/commentsになっている。RailsみたいなURL(/entries/:id/comments)じゃないね。かわりに、hidden fieldを持っていて、そこにentry_idを入れている。これだと、URLはシンプルになっていいかも。REST的にはRailsの方が正しい気もするんだけど。
それから、templates/comment/list.mab

@comments.sort_by( &:created_on ).each do |comment|
  name = ((comment.name.nil? or comment.name.empty?) ? "通りすがり" : comment.name)
  p %Q|#{name} #{comment.created_on.strftime("%Y-%m-%d %H:%M")}|
  textile comment.content
end

チュートリアルのコードはわかりにくくて好きじゃない感じだったので書き直してみました。

で、実際にコメントを書いてみると、/comment//editorなんていうURLに飛ばされるのだけど、当然404になる。いよいよルーティングを定義する必要があるみたい。続きはまた今度にしよう。

後部文字列を取り出すtruncateを書く

 milookのリニューアル作業をしていて、文字列の後部を取り出す必要が出てきたのですが、truncateは前部しか取り出せません。なので、自分で作りました。
config/environment.rb

module ActionView
  module Helpers
    module TextHelper
      def truncate_with_back(text, length = 30, truncate_string = "...")
        if length >= 0
          truncate_without_back(text, length, truncate_string)
        else
          return if text.blank?
          text.chars.length > -length ? "#{truncate_string}#{text.chars[length..-1]}" : text      
        end
      end
      alias_method_chain :truncate, :back
    end
  end
end

RMagickのメモリばか喰いを解決する方法

 大量の画像ファイルを、それぞれいくつかのサイズのサムネイル画像を作りつつ、それぞれをDBにも保存しなければならない、という処理が必要になったのですが、RMagickのメモリ消費の阿呆さ加減にやられてしまいました。とにかく一切メモリ解放をしてないんじゃないかと思ってしまうくらいのすこさで、topコマンドで見るのが怖いくらいなのです。
 で、ネットで情報を探ってみると、まあ当然というかGCをループの終わりでやれと書いてある。しかも、GC自体が無効になっている可能性も考慮して、

  fDisabled = GC.enable
  GC.start
  GC.disable if fDisabled

こんなことをやれとあちらこちらに書いてある。で、試してみたのだけど、確かにメモリ消費の加速度は鈍った。鈍ったのだけど、やっぱりやがては限界までメモリを食い尽くしてプロセスが異常終了してしまう。さらに毎回GCが強制的に走るので、処理速度も若干遅くなった。今回は、処理速度も重要な要点のひとつなので、なかなか大変なのだ。

 で、考えたのだけど、メモリ消費が限界に達する前にそのプロセスを終了して、続きを別プロセスではじめればいいのでは? と思ったのでやってみた。

file_names = (すべての画像ファイルの名前を持つ配列)

until file_names.empty?
  files = file_names.slice!(0..999)
  pid = fork do
    files.each do |file|
      (RMagickの処理)
    end
  end
  t = Process.detach(pid)
  t.join
end

で、topコマンドの画面を見ながら実行してみると、1000ファイル単位でメモリが解放されていた。素晴らしい。

ページロード後に外部スクリプトのdocument.writeを実行する方法の間違いを直す

「javascriptの実行中に外部スクリプトを実行する方法で、document.writeにハマる」
上の記事で書いたスクリプトは重要なことをひとつ忘れていました。
それは書き換えたdocument.writeを元に戻すことです。これを忘れていたためにIEで戻るボタンを押すとブラウザが落ちるという事象に遭遇してしまいました。

で、どのタイミングで元に戻すかで、ちょっと悩んだ。外部スクリプトの実行が終わったあとが最適なんだけど、それを知る手段がなさそうだったから。でもネットを探っていたら、
setTimeoutの実行は、functionの評価が終わってから
こんな記事を見つけた。これを使えばうまいことをできそうです。

var document.write_original = document.write;
var buffer = [];

function notice_end() {
        document.write = write_original;
        $('sample').innerHTML = buffer.join("");
}

document.write = function(){
        setTimeout(notice_end, 0);
        for(var i=0; i < arguments.length; i++){
                buffer.push(arguments[i]);
        }
};

(document.writeを使う外部スクリプトを実行)

こんな感じでうまく動きました。setTimeoutの動きはおもしろいな。こんな書き方ができるjavascriptもおもしろい。

Wavesでmigrationが作れないとき

Wavesで新しいMigrationを作るときに

rake schema:migration name=create_xxx

なんてしますが、Blogが定義されていないというようなエラーが発生したりします。
そんなときは lib/task/schema.rbの

version = ( ENV['version'].nil? ?
      Sequel::Migrator.get_current_migration_version( Blog.database ) :
      ENV['version'].to_i  ) + 1

Blog.databaseの部分を自分の作ったアプリの名前に変えればOK。wavesコマンドでコード生成時にバグがあるのかな。
と思ったら、gemディレクトリ/wavesディレクトリ/app/lib/tasks/schema.rbの中にBlog.databaseとかかれていた。ベタうちかよ。

腹が立ったので直してみた。
gemディレクトリ/wavesディレクトリ/app/lib/tasks/schema.rb をschema.rb.erbに改名して、

version = ( ENV['version'].nil? ?
      Sequel::Migrator.get_current_migration_version( <%= name %>.database ) :
      ENV['version'].to_i  ) + 1

こうやればOK。
コミットしようかと思ったけど、どうすればいいかよくわかんないや。まいいか。

software RAID1 を debianでちゃんと使う

以前、「Software RAID1 を centOS で正しく使う」のような記事を書いたことがあるのですが、debianでも同様でした。OSインストール時にRAID1を設定しても、2つ目のディスクにGRUBはインストールされません。
なので、こちらをまたまた参考にして、インストール後にgrubで以下のように設定をしました。

# grub
grub> device (hd0) /dev/sdb
grub> root (hd0,0)
grub> install /boot/grub/stage1 (hd0) /boot/grub/stage2 p /boot/grub/menu.lst
grub> quit

これでOKです。

そろそろ新しいものをやりたい

 同僚の方ともちょっと話していたのだけど、Railsに触るのもちょっと飽きてきている今日この頃だったりします。Wavesをやってみたりしているのもそれがあるから。新しい言語も覚えたいなあ、と思っていて、最近気になっているのが、

プログラミングErlang
  • プログラミングErlang
  • 作者/アーティスト: Joe Armstrong
  • 出版社/メーカー: オーム社
  • メディア: 単行本(ソフトカバー)
  • 発売日: 2008-02-23

です。ちょっとやってみたい。でも仕事には全然使わないだろうな(それでもいいけど)。仕事で使うといえば、Flushもちゃんとやりたいんだよね。ASの文法がぱっと見た感じJavaみたいでちょっと食わず嫌いなのだけど。

コードギアス 反逆のルルーシュ 1: 福山潤,櫻井孝宏,ゆかな,小清水亜美,名塚佳織,折笠富美子,木村貴宏,谷口悟朗,大河内一楼: DVD

コードギアス 反逆のルルーシュ 1 DVD

コードギアスがけっこうおもしろくて全話見てしまった。二期が来週から始まるらしくてちょうどいいタイミングです。