Hatena::ブログ(Diary)

present このページをアンテナに追加 RSSフィード Twitter

2012-02-10

Rails3 レシピブック

まだ Rails2 が主流だったころ Rails を勉強していたんですが、Google App Engine が出たので、途中でやめて Python に移ってしまいました。それから去年までずっと Python

今年から Ruby に戻って、Rails3 に再挑戦しています。Python で Django や Kay といったフレームワーク使っていたから、Rails2 のときどこで躓いていたのかわからないくらい、Rails3 にすんなり入れました。他のフレームワークで培ったノウハウは Rails でも生かせる、というのを実感中。

足りないのは「Rails3 ではメールどうやればいいんだっけ」「Rails3 ではリレーションどうすればいいんだっけ」といった、Rails3 でのやり方くらいです。そこで本書のようなレシピブックの出番。

Google 先生に聞くのもいいですけど、やっぱり手元にあった方が調べるのに早いです。本書は Rails 入門レベルのレシピからだんだん高度になっていくので、Rails 入門したてでもなんとか読み進めていける内容だと思います。

Rails 初挑戦でいきなり本書だけ、っていうのはさすがにキツイと思いますが、既に他の言語でフレームワークに慣れ親しんでいる人なら、Rails の勉強はレシピブックとネットの情報で足りるんじゃないでしょうか。私の場合は、今のところ何とかなっています。

Rails3レシピブック 190の技
高橋 征義 松田 明 諸橋 恭介
ソフトバンククリエイティブ
売り上げランキング: 60469

2012-02-08

Ruby でベンチマーク計測

Javaサーブレットコンテナ、まぁ Jetty なんですけど、Jetty の HTTP サーバーとしての性能がどれくらいか分かる資料が無かったので、Rubyベンチマークを計測するスクリプトを書いてみました。ついでにサーブレットも計測しています。benchmark モジュール初挑戦。

# coding: utf-8
require "open-uri"
require "benchmark"

HTML_URL = "http://192.168.56.101:8080/helloworld/"
SERVLET_URL = HTML_URL + "hello"

# ベンチマーク対象のメソッド
def open_service(uri)
  # 指定した URI にアクセスしてレスポンスを取得するだけ
  open uri do |s|
    s.read
  end
end

# 渡したブロックを 1, 10, 100, 1000 回繰り返すのにかかる時間を計測
def do_benchmark(caption, &block)
  Benchmark.benchmark(caption + Benchmark::CAPTION,
                      7,
                      Benchmark::FMTSTR) do |r|
    [1, 10, 100, 1000].each do |n|
      # block を n 回繰り返したときのベンチマーク
      r.report n.to_s do
        n.times &block
      end
    end
  end
end

# 静的ファイルにアクセス
do_benchmark("static  ") do
  open_service(HTML_URL)
end
puts

# サーブレットにアクセス
do_benchmark("servlet") do
  open_service(SERVLET_URL)
end

このスクリプトを実行した結果は次の通り。


C:\work>ruby benchmark_jetty.rb
static        user     system      total        real
1        0.078000   0.312000   0.390000 (  0.419042)
10       0.016000   0.000000   0.016000 (  0.128013)
100      0.156000   0.078000   0.234000 (  1.221122)
1000     0.764000   1.560000   2.324000 ( 12.743274)

servlet      user     system      total        real
1        0.000000   0.000000   0.000000 (  0.014002)
10       0.000000   0.016000   0.016000 (  0.134013)
100      0.063000   0.156000   0.219000 (  1.281129)
1000     1.451000   0.670000   2.121000 ( 13.073307)

このスクリプトで計測したデータが、実際に役に立つかどうかは微妙ですけどね。ていうか、たぶん JMeter あたりを使って計測し直すこと間違いなし。

2012-02-06

Sequel でマイグレーション

Sequel にはマイグレーション機能があるので、Rails と同じようにテーブルの作成やスキーマ変更ができます。

サンプル程度ならアプリケーションのファイルに create_table べた書きでいいけど、ちゃんとしたサービス作るときはマイグレーションを使ったほうが良いですよね。

マイグレーションファイルの名前は、最初に通し番号またはタイムスタンプを付けて、APP_ROOT/db/migrate ディレクトリに配置するのが慣習。

001_create_users.rb
Sequel.migration do
  up do
    create_table :users do
      primary_key   :id
      String        :name, :null => false
      String        :password, :null => false
      DateTime      :created_at
    end
  end

  down do
    drop_table :users
  end
end

あとは APP_ROOT で次のコマンドを実行。

sequel -m ./db/migrate sqlite://sample.db

たったこれだけ。簡単ですね。sqlite://〜 の部分は、使っているデータベースに合わせて修正してください。

2012-02-03

Sinatra + OmniAuth で Twitter の OAuth を試してみた

Sinatra アプリの認証に Warden を使おうとしてたけど、「ユーザー名とパスワードを自前で保存したくない」と思い直して、OAuth や OpenID を検討し始めました。TwitterFacebook や GitHub など、いろんな OAuth に対応したいから、使うライブラリは OmniAuth かな。

Sinatra + OmniAuth で、Twitter の OAuth を使って認証するサンプルを書いてみました。

# coding: utf-8
require "sinatra"
require "omniauth"
require "omniauth-twitter" # Twitter の OAuth を使うなら必須

# Sinatra のセッションを有効にする
enable :sessions

# OmniAuth の設定
use OmniAuth::Builder do
  # Twitter の OAuth を使う
  provider :twitter, "Consumer key", "Consumer secret"
end

get "/" do
  erb :index
end

# Twitter の認証が成功したら呼び出される
get "/auth/:provider/callback" do
  # 認証情報は request.env に格納されている
  @auth = request.env["omniauth.auth"]
  erb :home
end

__END__
@@layout
<!DOCTYPE html>
<html>
    <head>
        <title>Sinatra-OmniAuth</title>
    </head>
    <body>
        <%= yield %>
    </body>
</html>

@@index
<a href="/auth/twitter">Twitter でログイン</a>

@@home
<h1>Wellcome</h1>
<pre>
    <%= @auth %>
</pre>

たったこれだけのコードで実装できてしまった…。以前、Python + AppEngine + Kay Framework で Twitter の OAuth に挑戦したときは、結構コード書いたのにな。

2012-02-02

Sequel::Model でキャストできない値をセットしたら例外が発生するのを抑制してみる

例えば、Sequel::Model を使って次のようなクラスを定義したとします。

# coding: utf-8
require "sequel"

DB = Sequel.sqlite("test.db")

DB.create_table? :records do
  primary_key   :id
  Date          :date
  String        :summary
  Float         :kcal
  DateTime      :created_at
end

class Record < Sequel::Model
  plugin :validation_helpers

  def validate
    validates_presence [:summary, :kcal, :date]
    validates_numeric :kcal
    validates_type Date, :date
    validates_type String, :summary
    validates_type DateTime, :created_at
  end
end

このクラスに対して

Record.new(:kcal => "hoge")

とか

@record = Record[id]
@record.set(:kcal => "hoge")

とかやると、Float に変換できないため例外が発生してしまいます。

入力はとりあえず何でも受け付けて、検証してみてエラーだったらメッセージを表示したいので、セットしたときに例外を出されたら都合が悪い。

そこで、キャストできなかったとき例外が発生するのを抑制してみます。

Sequel::Model.raise_on_typecast_failure = false

これで OK。ActiveRecord と同じ感覚で使っていたから、ハマってしまいました。