Rails使いの味方Herokuネタです。

HerokuでRailsアプリをデプロイする場合、タイムゾーン設定にちょっと癖があります。

Read More…

Posted in ruby, ruby on rails at 7月 28th, 2010. 1 Comment.

ツイッターに匿名で投稿できるサービスを作りました。

つぶやき@名無しさん http://nanasi3.com/

Read More…

Posted in ruby, ruby on rails, Webサービス at 7月 9th, 2010. No Comments.

NiftyGeneratorとは?

すごくニッチな話題になってしまいますが Railsでサクっとモックを作る時、僕はnifty_generatorを使います

http://github.com/ryanb/nifty-generators

nifty_generatorはRailsCastsのryanb作の、サクっと便利でシンプルなコードを生成してくれるパッケージで

  • レイアウト
  • Scaffold
  • 設定ファイル
  • 認証処理

を生成するコマンドを提供してくれます。

RailsCasts内のサンプルアプリケーションは、いつもnifty_generatorを使ってますね。

標準のGeneratorスクリプトに比べて良い点は

  • デザインがシンプルだけど適度に良く、拡張する土台にしやすい
  • レイアウトが良くできている
  • レイアウトだけ生成できる

という具合です。

特にScaffoldしなくてもレイアウトを生成してくれるのは、非常に便利なので僕は常用しているのですが、

最近ViewをHamlで書くようになってから、ちょっと不便だなぁと感じていました。

Haml対応してた

ヘルプみたら、普通に対応してるし・・。

script/generate nifty_layout --haml
create app/views/layouts
create public/stylesheets
exists app/helpers
create public/stylesheets/sass
create app/views/layouts/application.html.haml
create public/stylesheets/sass/application.sass
create app/helpers/layout_helper.rb

すごい!かわいい!nifty_layout!

そして僕はどんどんコードをエディタで書かなくなるんだ!

Posted in ruby on rails at 6月 29th, 2010. No Comments.

Facebooker

FacebookerはRuby on Rails用のFacebookプラグインです。
Facebookアプリケーションを作ったりFBMLのヘルパーがあったりREST APIへのラッパーが用意されていて、大変に便利が良いです。

使い方は

チュートリアル
http://apps.facebook.com/facebooker_tutorial/

Developing Facebook Platform Applications with Rails http://www.pragprog.com/titles/mmfacer/developing-facebook-platform-applications-with-rails

を参考にしてください。
書籍の方はちょっと情報が古いのですが、それでも十分役立ちます。

ユーザー情報の取得方法

Facebookerでのユーザー情報の取得は、とてもRails的でわかりやすいです。

script/console で実行する場合

# Facebook ユーザーID
facebook_id = 1234567890

# Facebookのセッションキー
session_key = 'xxxxxxxxxxxxxxxxxxxxxxxxx'

# Facebook APIにアクセスするためのセッション作成
fb_session = Facebooer::Session.create
fb_sessoin.secure_with!(fb_sessoin, facebook_id, 1.hour.from_now)

fb_session.user #=> セッションユーザー情報。Facebooker::Userのインスタンス
fb_session.user.friends #=> セッションユーザーの友人のユーザー情報。Facebooker::Userインスタンスの配列

ユーザー情報の取得には、Users.getInfo APIを利用する。

ユーザー情報のキャッシュ

Facebooker::Userインスタンスは、生成された後、最初にプロパティ(#name, #age ..etc)にアクセスしたタイミングでUsers.getInfo APIにリクエストを投げる。一度リクエストしたら、2度目はキャッシュしたメモリから値を返す。

Facebooker::Userインスタンスを作成しても、プロパティにアクセスしなければUsers.getInfo APIをリクエストしないので、少しはパフォーマンスに効果があるのかもしれない。

user = fb_session.user # まだREST APIにリクエストしてない
user.name #=> 'Yukio Hatoyama' このタイミングでREST APIにリクエストしている
user.sex #=> 'Male' メモリーから取得

同じユーザーでもキャッシュは効かない場合がある、ていうかほとんど。

基本的にはインスタンス変数にデータを蓄えているだけなので、同じFacebookユーザーでもオブジェクトが違えば、キャッシュは当然効いていないため、それぞれAPIへリクエストすることになる。

# UID123456789のユーザー
user = Facebooker::User.new(123456789, fb_session)
# users.getInfo APIにアクセスする
user.name #=> 'Yukio Hatoyama'

# 同じUIDを持つユーザー
sameuser = Facebooker::User.new(123456789, fb_session)
# Users.getInfo APIにアクセスする
sameuser.name #=> 'Yukio Hatoyama'

フレンド情報の取得

Facebookerでのフレンド情報取得は、Facebooker::User#friendsを利用することになる。 このようなAPIがFacebookに存在しているわけではなくて、Friends.get APIで友人のID一覧を取得した後にその人数分Users.getInfo APIをリクエストしにいっている。

従って、かなり効率が悪いです・・。

user = Facebooker::User.new(123456789, fb_session)
user.friends #=> Userインスタンスの配列コレクション。友達が20人いれば、リクエスト数は21回。

フレンド情報のキャッシュ

フレンド情報の取得にはキャッシュが効く。効かなかったらやばいよね。

user = Facebooker::User.new(123456789, fb_session)

# フレンド数分、users.getInfo APIをリクエスト
user.friends

# メモリのキャッシュから返すので、APIはリクエストしない
user.friends

# プロパティを指定した場合、もう一度人数分リクエストする
user.friends([:name, :sex])

# キャッシュから返すのでAPIはリクエストしない
user.friends([:name, :sex])

これもUserインスタンス内の変数にキャッシュしているだけだから、あまり効果なさそうだなぁ。。

最後に

FacebookerはFacebookアプリをRubyで作りたい場合、とても便利です。
情報も他のライブラリに比べて一番充実していると思います。

僕もこれまでFacebook関連のアレをやってきましたが、Facebookerのインターフェイスの良さにはしびれっぱなしです。

キャッシュに関しては、FacebookのユーザーIDベースでKey Value Storeに保持しておけるようにできたらいいなと思っていますが、Facebookの規約的にアウトじゃないのか?とか、そもそもユーザー情報の見え方がユーザーによっては違うはずなので、やっぱりサービス全体でキャッシュするのはダメかなぁ。

追記

Facebooker::User#users(ids)メソッドを使えば、リクエストが1回で済むことがわかった。
基本的にはこっちを使おうかな。

Posted in ruby, ruby on rails at 2月 19th, 2010. 1 Comment.

Railsで最近人気の認証フレームワーク「Authlogic」は、ユーザーセッションをまるでActiveRecord風なモデルのように扱えることによって、セッション周りの処理をコントローラーからモデル側へ移すことができていて、コードがシンプルになります(コントローラーがごちゃごちゃしないという点で)。

AuthlogicではユーザーセッションをAuthlogic::Session::Baseを継承したクラスで管理するようになっており

# user_session.rb
class UserSession < Authlogic::Session::Base
end

# ログインする
session = UserSession.new(:login => "bjohnson", :password => "my password", :remember_me => true)
session.save

# ログインしているユーザーセッションを取得する
current_user_session = UserSession.find

# ログアウトする
session.destroy

と、こんな感じのインターフェイスでログイン周りの処理が書けて、非常にわかりやすい。

しかし隠蔽されているとはいえ、モデルからセッションやCookieにアクセスするといった事を自分で実装したことがなかったので、一体どんな仕組になっとるんか?と考え調べました。

読んだポインタ

http://github.com/binarylogic/authlogic/blob/master/lib/authlogic/controller_adapters/abstract_adapter.rb
http://github.com/binarylogic/authlogic/blob/master/lib/authlogic/controller_adapters/rails_adapter.rb

3行で説明

  1. ActionControllerのbefore_filterの先頭で
  2. ActionControllerをラップしたアダプタークラス(RailsAdapter)への参照を
  3. Authlogic::Session::Base.controllerに代入している

モジュールをインクルードしている箇所 http://github.com/binarylogic/authlogic/blob/master/lib/authlogic/controller_adapters/rails_adapter.rb#L48

ActionController::Base.send(:include, Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation)

コントローラーの先頭のフィルターに:activate_authlogicという処理を追加している箇所 http://github.com/binarylogic/authlogic/blob/master/lib/authlogic/controller_adapters/rails_adapter.rb#L36

klass.prepend_before_filter :activate_authlogic

Authlogic::Session::Base.controllreにコントローラーのアダプターを代入している箇所 http://github.com/binarylogic/authlogic/blob/master/lib/authlogic/controller_adapters/rails_adapter.rb#L40-42

def activate_authlogic
  Authlogic::Session::Base.controller = RailsAdapter.new(self)
end

とても勉強になったんだけど、Authlogicはソースが綺麗に分割されすぎてて探しにくい。

Posted in ruby, ruby on rails at 2月 15th, 2010. No Comments.

Railsで非同期処理?

railsで非同期処理をやる場合、最近はdelayed_jobメジャーらしいですね。

以前はbackgrounDRbが定番だったようだけど、EngineYardが「友達にBackgrounDRbを使わせるな」とまで書いているので、そこまで言われると使う気になりませんでした。実際リソース食いだったし。

使い方参考ページ

使い方はそんなに難しくないので、ここで説明することは放棄します。

READMERailscastsのエピソード171でなんとかなると思います。

再試行のロジック

いろいろとすっとばして本題、キューの再試行のロジックが変だなぁと思ったのでメモです。

Delayed::Job::max_attempts

delayed_jobには試行回数上限があって、この上限値を超えるまでリトライしつづけます。 デフォルトは25回で、Delayed::Job::max_attempts という定数に設定されています。

Delayed::Worker::sleep_delay

ワーカーが起動するインターバルです。デフォルトは5秒。

例えば絶対に失敗するキューがあった時、

キュー失敗、5秒待機、キュー失敗、5秒待機、(以下25回失敗するまで繰り返し)

という流れを勝手にイメージしていたのですが、そうではないみたいです。

キューが失敗した時の再スケジューリング

キューが失敗すると、次に実行する予定時刻を決めます。そのロジックが

On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries.

となっています。

「トライした回数の4乗に5秒足した時間」後にリトライをするよう再スケジューリングするので、失敗すればするほどインターバルが開いていきます。

1回失敗したら次回のリトライは6秒後
2回失敗したら次回のリトライは21秒後
3回失敗したら次回のリトライは86秒後
4回失敗したら次回のリトライは261秒後
5回失敗したら次回のリトライは630秒後
6回失敗したら次回のリトライは1301秒後
7回失敗したら次回のリトライは2406秒後
8回失敗したら次回のリトライは4101秒後
9回失敗したら次回のリトライは6566秒後
10回失敗したら次回のリトライは10005秒後

最初の方は1分以内にリトライさせますが、10回目ともなるとリトライは2時間後。
何度も失敗していると、もうこいつアカンわ・・と見捨てられていく感じを良く表現したロジックですねー。

Posted in ruby on rails, 日記 at 12月 15th, 2009. No Comments.

なんかto_jsonについて調べてたら

activerecord/lib/active_record/serializers/json_serializer.rb

にらきすたのこなたが出てくるんだけど、これって有名なんだろうか・・。

あまりにも意外すぎて、なんの作業してたか忘れたよ。いい迷惑だ!

http://github.com/rails/rails/blob/55501b9f6ab46d45db04a81956579402511ad092/activerecord/lib/active_record/serializers/json_serializer.rb#L18-75

追記:

例えばこんな感じ、コメントの中にサンプルコードとして登場します

    #   konata = User.find(1)
    #   ActiveRecord::Base.include_root_in_json = true
    #   konata.to_json
    #   # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
    #                   "created_at": "2006/08/01", "awesome": true} }

はてブのコメントによると2年前からコードの中に存在していたらしい。
アニメ好きな外人のコミッターが犯人だったんすね。

http://blog.codefront.net/2007/10/15/konata-izumi-in-edge-rails/

Posted in ruby on rails at 11月 2nd, 2009. No Comments.

RailsでApplicationHelperのスペックを書くときにハマったのでメモ。

準備

まず普通にスペックを書く場合

spec/helpers/appliction_helper_spec.rbを以下のように準備

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe ApplicationHelper, :type => :helper do

  helper_name :application
  %w{hoge}.each do |method_name|
    it "##{method_name}メソッドが存在すること" do
      helper.should be_respond_to(method_name)
    end
  end

end

helper_nameでへルーパー名を定義しておくのがポイント。
そしてExampleの中から「helper」を通じて、エクスペクテーションを書いていきます。(言葉の使い方あってる?)

リクエストを使うスペックを書きたい場合

ヘルパーメソッドの中でリクエストを参照する場合(そもそもヘルパーがリクエストを参照するのが美しいことなのか疑問を抱きましたが、とりあえず無視)

ApplicationControllerにスタブアクションを作ってあげる、というやり方でなんとかしました。

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe ApplicationHelper, :type => :helper do

  class ApplicationController
    def stub_action
      render :text => self
    end
  end

  helper_name :application

  it "ヘルパーの中からリクエストを参照できること" do
    get :stub_action
    helper.mobile_tracking_code.should be_xxxx
  end

end

こんな感じで、普通にコントローラースペックを書くときのようにgetとかpostとかヘルパースペックの中からリクエストを使ったテストを一応かけます。

もっと良い方法がある気がする。

Posted in ruby, ruby on rails at 10月 19th, 2009. No Comments.

個人的メモ

結構リソース食うので次はdelayed_jobにしたい・・

実行してことはあるけど、正常に終了してないキューを取得する

BdrbJobQueue.find(:all, :conditions => ['finished = ? AND taken = ?',0,1])

キューをもう一回実行させる

BdrbJobQueue#release_job

上の合わせ技

BdrbJobQueue.find(:all, :conditions => ['finished = ? AND taken = ?',0,1]).each do |job|
  job.release_job
end
Posted in ruby, ruby on rails at 9月 30th, 2009. No Comments.

Amazon CloudFrontはAmazon S3上のファイルをキャッシュするソリューションです。

前回paperclipからS3を使う設定はわかったので、今回はさらにCloudFrontを利用するための設定です

前回のおさらい

has_attached_file :avatar,
  :storage => :s3,
  :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
  :path => ":attachment/:id/:style.:extension",
  :bucket => 'mybucket'

前回こんな感じでS3の設定をしました。

コンソールから利用してみると

user = User.new
user.avatar = File.open('/tmp/myface.jpg')
user.save

puts user.avatar.url #=> http://mybucket.s3.amazonaws.com/avatar/1/original.jpg

こんな感じでS3のURLがとれるようになっているはず。

s3_host_aliasを効かす

キャッシュサービスであるCloudFrontを利用する場合は、ホスト名を適宜変えるだけなので paperclipが勝手にやってくれるといいなぁ〜なんて思いました。

has_attached_file :avatar,
  :storage => :s3,
  :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
  :path => ":attachment/:id/:style.:extension",
  :bucket => 'mybucket',
  :s3_host_alias => "xxxxxxxxxxxx.cloudfront.net",
  :url => ":s3_alias_url"

オプションが2つ増えてます

  • :s3_host_alias は、エイリアスのFQDNを記述しておきます。
  • :url には「”:s3_alias_url”」を記述します。注意なのは、rubyのシンボルじゃなくて、文字列だってところです。ハマりました。
user = User.new
user.avatar = File.open('/tmp/myface.jpg')
user.save

puts user.avatar.url #=> http://xxxxxxxxxxxx.cloudfront.net/avatar/1/original.jpg

わーーこりゃ幸せだ。

余談

さらにサイト全体でアセット関係(public以下)もCloudFrontにしたければ

config/environments/#{RAILS_ENV}.rb 内で

config.action_controller.asset_host = "http://xxxxxxxxxxxx.cloudfront.net"

こうしとけば、image_path()やらimage_tag()やらjavascript_include_tag()やらを使ったときに、リクエスト先を書き換えてくれてさらに幸せ度アップ

Posted in ruby, ruby on rails at 9月 10th, 2009. No Comments.