kaeruspoon

iMacとMacbookとどちらを買うべきか

iMacがカッコよくていいですね。すごく欲しい。でもMacbookの利便性もいいんだよね。持ち運べるのは大きい。
外で使うことが絶対にないならiMacだけど、勉強会とかに行くならMacbookだよ。でも外で使う機会って実際のところほとんどない。
さて、どちらを買うべきか……。まあ、ubuntuでもけっこう満足してるんですけどね。

ARのwhere区を簡単に扱えるようになる「Ez-Where2」に触れてみた

RailsのActiveRecordで、Where区が複雑になるのはよくあることです。特に検索機能などで、指定された条件によってwhere区を用意するときはコードが汚くなりがちです。
そんなときに使えるのが「Ez-Where2」。超簡単にwhere区が構築できちゃいます。

インストール。

./script/plugin install http://opensvn.csie.org/ezra/rails/ez_where_two/


使い方。

cond = Caboose::EZ::Condition.new
cond += cc(:users) {name == params[:user_name]}
cond += cc(:mobile) {signature == params[:mobile_id]}

ccというメソッドがwhere区を構築する。このわかりにくいメソッド名だけはいただけないけど。includeやjoinを使っていてテーブル名の指定が必要なときは最初にそのテーブル名をシンボルで指定してあげて、続くブロックでwhere区の条件を書く。+ でつなげればAND条件になり、| でつなげればORになります。詳しくはREADMEを参照。他にもいろいろ便利そうなメソッドが用意されています。

Rails2.1の新機能、named_scopeを試してみた

okyuuにコメントしてくれた人に教えてもらったのだけど、Rails2.1のARにはnamed_scopeという機能が追加されたらしい。
というわけでさっそくRails2.1にアップデートしてどこでもお気に入りで試してみた。

class Category < ActiveRecord::Base
  DEFAULT_NAME = "無分類"
  named_scope :default, :conditions => {:title => DEFAULT_NAME}
end

とモデルに書いておけば、

  @category = @user.categories.default.first

こう書けます。これは、

  @category = @user.categories.find(:first, :conditions => ["title = ?", Category::DEFAULT_NAME])

と同じ意味です。これはコードがすっきりしていいですね。
ブロックを使えば、値を動的に変化させることもできます。

  named_scope :title_select, lambda {|t| {:conditions => ["title = ?", t]}}

こうしておけば、

  @category = @user.categories.title_select(Category::DEFAULT_NAME).first

こう書けるのです。
named_scopeはfind(:all)として働くようなので、配列が返ってくる点に注意しなければなりません。
ちなみに、上のコードだとSQL文には「limit 1」が付与されていた。firstまで見てSQL文を構築しているのか。Railsは頭がいいなあ。

デュアルディスプレイなしでは生きられない

新しい会社はディスプレイが小さくて一枚しかないのだけど、コードを読みながらイライラしていることに気づいてびっくりする。
もはやデュアルディスプレイが当たり前になっていてその利便性も忘れかけていたけど、それがどんなに幸せなことかを再認識したりしている今日この頃。ちょっと大きな会社なので、ディスプレイ一枚増やすのも何かと大変そうなのです。
家にある15インチのやつでも持っていこうかな。でもそうすると、家で開発するときに不便だしなあ。

RSpecで"You called render with invalid options"というメッセージが返されたら

RSpecでテスト中にcontroller内でrenderメソッドを使ったら、

You called render with invalid options

と怒られた。
調べてみるとRspecのバグらしい。Rails2.1のせいでもあるのか。trunkでは直っているみたいなので、

./script/plugin install http://rspec.rubyforge.org/svn/trunk/rspec
./script/plugin install http://rspec.rubyforge.org/svn/trunk/rspec_on_rails
./script/gemnerate rspec

で対処したらうまく動きました。

restful_authenticationを使ってみた

Railsでユーザ認証といえばacts_as_authenticatedだと思っていたのですが、今はもうrestful_authenticationというやつが出てきているようです。けっこう前から? アンテナ低いのはなんとかしなければ。とりあえず、今開発中のかえるキーワードで使ってみることにした。
まずはインストール。

./script/plugin source http://svn.techno-weenie.net/projects/plugins
./script/plugin install restful_authentication

restful_authenticationは、acts_as_state_machineというやつを使うのでこいつもインストール。

./script/plugin install http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk/

それからacts_as_authenticatedみたいにモデルやらコントローラを作ります。

./script/generate authenticated user sessions \
              --include-activation \
              --stateful

これでuser, user_mailer, user_observerモデルと、user, sessionコントローラが作られます。
モデルのほうはacts_as_authenticatedでおなじみのやつらと一緒。オブサーバを使うので、config/environment.rbのInitializerブロックの中で

config.active_record.observers = :user_observer

と指定しておきます。
acts_as_authenticatedではaccountコントローラひとつだったけど、アカウント管理関係はuserコントローラに、ログイン処理関係はsessionコントローラにわかれています。
上記で--include-activationと定義しているけど、これだけでメールを使ったユーザ登録処理が実装される。これは便利ですね。acts_as_authenticatedでは自分で書いていたから。
--statefulはacts_as_state_machineを使ってユーザの管理をするよ、という宣言。acts_as_state_machineなんてはじめてきいたけど、userモデルを読むとなんとなくわかる。

  acts_as_state_machine :initial => :pending
  state :passive
  state :pending, :enter => :make_activation_code
  state :active,  :enter => :do_activate
  state :suspended
  state :deleted, :enter => :do_delete

こんな感じで状態が宣言されている。initialが初期状態で、enterキーでその状態になったときに実行するメソッドを示しているみたいだ。
さらに、

  event :register do
    transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) }
  end

こんな感じで状態が変化するイベントを定義する。イベントは

current_user.activate!

といったようにUserインスタンスのメソッドとして「!」つきでコールすればいいみたい。なかなかおもしろい。
で、あとは rake db:migrate を実行すればOK。ログイン時にメールアドレスを使用したかったらちょっと書き直さなければいけないのはacts_as_authenticatedと同様。あと、reset_sessionを要所要所に追加したほうがよさそうです。
本登録の認証のため、avtivateはactivate_codeパラメータを受け取れるようにmap.connectで別に書くか、デフォルトのルーティングを生かして、activateコードをidで受け取るように書き換える必要があります。
これでとりあえず動きますが、ユーザの状態管理をするならconfig/routesの設定もしておきます。

  map.resources :users, :member => {:suspend => :put, :unsuspend => :put, :purge => :delete}
  map.resource :session

ユーザ状態の変更アクションはusers_pathへリダイレクトするので、indexアクションを用意しておきましょう。

acts_as_state_machineのfind_in_stateを複数状態指定可能にする

acts_as_state_machineのfind_in_stateは便利ですが、stateはひとつしか指定できません。たとえば、登録ユーザと管理者ユーザの両方でログイン可能なときなどはちょっとメンドくさいです。
なので、find_in_stateで複数のstate指定をできるようにしてみました。
config/initializers/acts_as_state_machine.rbを新規作成して以下のコードを書きます。

module ScottBarron
  module Acts
    module StateMachine
      module ClassMethods
        protected
        def with_state_scope(target_states)
          target_states = [target_states] unless target_states.is_a?(Array)
          raise InvalidState unless target_states.all? {|s| states.include?(s)}

          cond = []
          cond_param = []
          target_states.each do |st|
            cond << "#{table_name}.#{state_column} = ?"
            cond_param << st.to_s
          end

          with_scope :find => {:conditions => [cond.join(" OR "), cond_param].flatten} do
            yield if block_given?
          end
        end
      end
    end
  end
end

これで、複数の指定をarrayで指定できるようになります。

u = find_in_state :first, [:active, :admin], :conditions => {:email => email}

count_in_stateもcalculate_in_stateも複数指定可になります。ちょっと便利になりました。

ちょっと調べ物中

かえるキーワードを今作っています。このkaeruspoonで実装されていたキーワード自動リンク機能(今は停止しています)を、独立したアプリにしたものです。誰でも自分のブログでかえるキーワードの機能が使えるようにしようと考えていたのですが、ちょっと考える必要が出てきました。
クロスドメインになるからJSONPでも使おうかと思ったのですが、JSONPでは送信データをURLクエリとして送るので、ブログ記事みたいな大きなデータでは使えないことに気づきました。POSTを使えればいいんだけど、いい方法はないのかな。それともJSONPで数回にわけて処理を行うか……。もうちょっと調べてみようと思います。

Passenger(mod_rails)の速度を計ってみた

ウワサのPassenger(mod_rails)を試してみました。
インストールのやり方はあちらこちらのサイトに書かれているし、すごく簡単(しかも親切)なので問題はないと思います。
ぼくとしては速度が一番気になっていたのでabで計測テストをやってみました。

ab -n 500 -c 2 http://milook.kaeruspoon.net/ 

テスト対象はmilookを使います。同時接続数が2と少ないのは、サーバ上でThinのプロセスが2個しかないからです。
kaeruspoon系のwebアプリはすべてThinで動いているので、まずはThinで計測してみました。

1. Apache2 + proxy_balancer + Thin(0.8.1)

Requests per second:    1.03 [#/sec]


2. Apache2 + proxy_balancer + mongrel(1.1.5)
つづいて、たぶん今一番一般的な構成のmongrelを試してみます。

Requests per second:    1.00 [#/sec]

やっぱりThinのほうがちょっと速いね。

3. Apache2 + Passenger(1.0.5)
いよいよPassengerです。

Requests per second:    1.04 [#/sec]

お、Thinよりもさらに速い!(微妙だけど)

この結果を踏まえて、kaeruspoon系のwebアプリはすべてPassengerで動かすことに決めました(もうPassengerで動いています)。
PassengerのためにGCを改良したRuby Enterprise Editionを使うとさらにメモリ消費量も抑えられるとか。今後試していこうと思います。
今回のテストではメモリ消費まではチェックしていません。こちらの方の記事を読むかぎりでは、けっこうよさそうです。

ちなみに、PassengerではApacheのプロセスのほかにSpawnサーバ(スポーンと読むみたい)のプロセスも立ち上がります。
これはRailsフレームワークのコードをキャッシュしてくれるやつだそうです。それから、webアプリのコードをキャッシュしてくれるやつも別に存在します。Railsのバージョンが同じなら、複数のアプリがひとつのキャッシュを共有してくれるようにできているみたい。

Passengerを試していて一番気に入ったのが、mongrelやThinみたいに別個にwebサーバを立ち上げる必要がないところ。proxy_balancerも使わなくていいし。Apacheを頼りにできるのが精神的にかなり楽です。

追記:かえるイメージだけはPassengerでうまく動かなかったのでThinに戻しました。どこが悪いのかよくわからないな。まあ、そういうこともあるということで、シビアなサービスで使うにはまだまだ様子見なのかもしれません。

さらに追記:うまく動かなかった理由がわかりました。「ImageScienceを使っているRailsアプリをPassengerで動かすとエラーになるときの対処」を参照。

ImageScienceを使っているRailsアプリをPassengerで動かすとエラーになるときの対処

かえるイメージがちゃんと動かなかった原因がやっとわかりました。
かえるイメージの中ではImageScienceを使っているのだけど、こいつが".ruby_inline"というようなファイルを、環境変数HOMEかINLINEDIRで指定されたディレクトリ下に作ります。
ところがPassengerで動かすと、環境変数のHOME、INLINEDIRともに何もわたってこないため、ImageScienceのinlline.rbの処理の中でエラーとなってしまいます。
というわけで、RAILS_ROOT/tmp下に適当にディレクトリを作って、config/environment.rbの中で環境変数INLINEDIRをそのディレクトリへのPATHに指定してあげると、ちゃんと動きました。
よかったよかった。これでPassengerを心置きなく使えるぞ。

確認画面はクライアントサイドで

RubyKaigiの山本陽平さんのセッションで、「確認画面はREST的にはどういう位置づけになるのか」という質問があったけど、「確認画面というのはサーバサイドで考えるものではない」みたいな回答があった。確認画面はリソースではない、つまりURIで表現する必要がないということだと思うのだけど、これはちょっと目からウロコ的なお話だった。そうだよね。確認画面なんてクライアントサイドで全部実現できるもんね。言われれば当たり前のことだけど、ちょっとはっとさせられた。

今日やっていたこと

・AmazonResoucesの開発(generatorプラグインを作ってみる)
・かえるキーワードの開発(JSONP諦め→WebAPIにするかも)
・Passengerの適用
・Apacheの勉強
RubyKaigiのustをみる
・FiveRunsでkaeruspoonのパフォーマンスチェック
・memcachedの適用
・MySQLのQueryCacheを使おうとしたらデフォで使われていた
ユルさんのお出迎え
・オムライス

tokyobikeで江ノ島にいってきた

ひさしぶりにtokyobike(自転車です)に乗ってサイクリングにいきました。前の記事にあるように江ノ島までいってかえってきたのです。
今までで最長の距離でした。シーザーみたいな気分になって帰ってきました。とりあえず、tokyobikeと今の装備では限界を越えている距離であることが判明した今日この頃。次にこの距離を走るときは、ちゃんとロードバイクを買って装備もちゃんとしてからにしよう。

【最高速度】42.5km/h
【平均速度】15.2km/h
【走行距離】79.11km
【走行時間】5時間12分48秒
【総走行距離】409.9km

Amazonの商品を手軽に扱えるプラグイン、AmazonResourcesをつくりました

Amazon商品を手軽に扱えるRailsのプラグイン、AmazonResourcesを作ってみました。

product = Amazon.find_by_asin(4274066967)

こんな感じに使います。テーブルに対象の商品がないときはAmazonのWebAPIで情報を取得して、自動でテーブルに保存してからインスタンスを返してくれます。また、テーブルに保存してから一週間以上経過すると、Amazonに問い合わせて最新情報を取得します。
これを使えばWebAPIのことを意識することなくAmazonの商品を手軽に扱えるようになります。もともとkaeruspoonで使っていたものをプラグインにしたものだったりします。
ISBN13にも対応しています。

product = Amazon.find_by_isbn13("978-4274066962")


AmazonResourcesgithubで公開しているので、誰でもインストールして使えます。

1.Hpricotのインストール
AmazonResourcesはHpricotを使用しているので、gemに入っていないときはインストールしておきます。

sudo gem install hpricot


2.AmazonResourcesのインストール

./script/plugin install git://github.com/tsukasaoishi/amazonresources.git


3.Amazon商品を管理するモデルを作成する

./script/generate amazon_resources (モデル名) (AmazonWebサービスのアクセスキーID) (アソシエーションタグ)

こんな感じで作成することができます。
例えば、

./script/generate amazon_resources amazon XXXXYYYZZZ AAAAA-22

とすれば、amazonsテーブルのためのmigrationファイルとamazonモデルが作成されます。

4.Amazon商品を保存するテーブルを作成する

rake db:migrate


これでOKです。
ちなみに取得できる情報は、ぼくが個人的に必要だと思う最低限の項目だけにしていますので、他の項目も取得したい方はモデルを改良して自由につかってください。