polipoを使ってみる
はてブ界隈で話題になっていたので、polipoを使ってみました。squidとどっちがいいのだろう。しばらく様子見です。
はてブ界隈で話題になっていたので、polipoを使ってみました。squidとどっちがいいのだろう。しばらく様子見です。
とうとうニコニコ動画のプレミアム会員になってしまった。いつかやるとは思っていたが…。
土曜日は、南伊豆の河津まで桜を見にいきました。ひさしぶりのドライブです。有名な河津桜はちょうど満開で、菜の花もたくさん咲いていて素敵でした。
新宿にいったついでに寄ったコムサでひさしぶりに服を買いました。欲しい服とかアクセサリがたくさんありすぎて困った。
「WEB+DB PRESS Vol.43」を読みました。
会社で、仕事のあとにガンダム00を見ました。Filnでは、主要なアニメは全部HDレコーダに録画されていて、いつでもバカでかいテレビで見ることができるのです。
ガンダムはここ2週間ほど見逃していたのでした。最初はいまいちだったガンダム00も、なんだかだんだんとおもしろくなってきたね。
ニコニコ動画がSP1になって、外部でも動画が見られるようになりました。さっそくkaeruspoonも対応してみました。これはいいね。
ニコニコの外部プレーヤーを元にもどしました。今はやっぱり、はてなとかlivedoorとかにしか許可されていないみたいです。kaeruspoonみたいな個人ブログで見られるようになる日がくるのでしょうか。Filnのリプレースが完了したら、日記自体をFilnに移してもいいかなあと思わないでもありません。
ひさしぶりに tokyobike でサイクリングに行ってきました。ユルさんも一緒だったので、比較的のんびりペース。そのおかげか、走行距離が40kmを越えても疲労感はまったくありませんでした。でもおしりがめちゃ痛い。レイパンがやっぱりいるなあ。新横浜の日産スタジアムではフリーマーケットをやってました。
【走行距離】 42.02km
【最高速度】 28.3km/h
【平均速度】 12.7km/h
【走行時間】 3時間18分16秒
【総走行距離】 309.4km
「double array」でググると、googleの求人広告が現れることに気づいた(どうでもよいお話)。
最近はRubyを離れてストアドプロシジャ・ファンクションを組んでいます。Filnのリプレースに関するRailsプログラムは完了していて、今はDB移行のためにいろいろと試行錯誤中。最初はRubyでスクリプトを組んでDB移行をしようとしていたのですが、ストアドプロシジャのあまりの速さにRubyスクリプトは放り投げてしまいました。
あまり時間がなかったのだけど、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でも作られるのかと思っていたら、そんなものはなかった。ふーん。
とりあえず、今日はここまで。明日はビューをやってみよう。
昨日に引き続き、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を試してみます。
チュートリアルによると、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になる。いよいよルーティングを定義する必要があるみたい。続きはまた今度にしよう。
と思っていたんだけど、実際のところはそんなに変化しなかった。innoDBだからなのかなあ。と思っていたら、500万以上のレコードを insert into ... select.. で一度にインサートしようとしたら極端に遅くなった。O(n^2)な感じなのかな。
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
大量の画像ファイルを、それぞれいくつかのサイズのサムネイル画像を作りつつ、それぞれを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ファイル単位でメモリが解放されていた。素晴らしい。
「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もおもしろい。
次期開発マシンはMacbookか。
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 を 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をやってみたりしているのもそれがあるから。新しい言語も覚えたいなあ、と思っていて、最近気になっているのが、
です。ちょっとやってみたい。でも仕事には全然使わないだろうな(それでもいいけど)。仕事で使うといえば、Flushもちゃんとやりたいんだよね。ASの文法がぱっと見た感じJavaみたいでちょっと食わず嫌いなのだけど。
ぼくはAUの携帯電話をつかっているのだけど、小田急新宿駅ではネットへぜんぜんつながらない。なんとかしてくれ。
近所の桜並木の道をユルさんと一緒にのんびりサイクリングしてきました。素敵だ。
コードギアスがけっこうおもしろくて全話見てしまった。二期が来週から始まるらしくてちょうどいいタイミングです。