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.

friendship.rb — bdd_sample

RailsCasts大好きです。

  • Rails好き
  • わかりやすい
  • ネタが早い
  • 声が素敵

などいろいろ良い点がありますね。

さて、最近TextMateを使うようになりまして(メインはNetBeansです) あこがれのRailsCastsみたいな色にしたいと奮闘しましたが 本家からダウンロードできるのでした・・

http://railscasts.com/about

好きな人はぜひ!

Posted in ruby at 10月 30th, 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.

PaperClipはとってもシンプルなActiveRecordで添付ファイルを管理するプラグインです。

http://github.com/thoughtbot/paperclip/tree/master

便利すぎて鼻血がでそうなプラグインです

使い方のおさらい

カラムを追加して

class AddAvatarColumnsToUser <ActiveRecord::Migration
    def self.up
      add_column :users, :avatar_file_name,    :string
      add_column :users, :avatar_content_type, :string
      add_column :users, :avatar_file_size,    :integer
      add_column :users, :avatar_updated_at,   :datetime
    end

    def self.down
      remove_column :users, :avatar_file_name
      remove_column :users, :avatar_content_type
      remove_column :users, :avatar_file_size
      remove_column :users, :avatar_updated_at
    end
  end


モデルクラスで定義

class User <ActiveRecord::Base
    has_attached_file :avatar
end


とするだけですね。 詳細はRDocをみてください

Amazon S3を使う

PaperClipはストレージにS3を使うためのオプションがあります

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


config/s3.ymlには

access_key_id: 456...
secret_access_key: 456...


こんな感じのアクセスキーとシークレットキーを置いておきます

productionでだけS3を使いたい

開発中はローカルで完結したい場合は

has_attached_file :avatar, Proc.new {
    if RAILS_ENV == 'production'
      {
        :storage => :s3,
        :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
        :path => ":attachment/:id/:style.:extension",
        :bucket => 'mybucket'
      }
    else
      {}
    end
  }.call


こんな感じでいいかな

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

こんにちは、久々ですね

Railsでroutes.rbは本当に便利ですよね。

map.resources :users


なんて書くだけで、RESTFulなAPIが用意されます。

  • index
  • new
  • create
  • show
  • edit
  • update
  • destroy

に対応したURLをRailsがよしなにやってくれますよね。

しかし、こんなにたくさんのアクションはいらないー。わしゃcreateとshowだけでいいんじゃーなんて時もあったりします。そんなときは

map.resources :users, :only => ['create','show']


として、必要なアクションだけ列挙しましょう。

また、:exceptなんてオプションもあって、必要ないオプションを列挙します。

このへんにかいてありました

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

Railsが2.3.3に上がったので、gemをアップグレードしたりパッケージをアップデートしたりしているうちに
script/autospecが動かなくなってしまった。

解決方法はここを参考にしました

ZenTestを4.0系→4.1系にバージョンアップする場合の注意点
http://d.hatena.ne.jp/TrinityT/20090630/1246334665

  • autotest-railsをインストール
sudo gem install autotest-rails


  • ZenTest-4.0.0/lib/autotest/*をZenTest-4.1.3/lib/autotest/ へコピー
# Macではこんな感じ
sudo rsync -ave /Library/Ruby/Gems/1.8/gems/ZenTest-4.0.0/lib/autotest/ /Library/Ruby/Gems/1.8/gems/ZenTest-4.1.3/lib/autotest


これでまたscript/autospecが使えるようになりました。

Posted in ruby, ruby on rails at 7月 23rd, 2009. No Comments.

DBに配列やハッシュのデータを保存したいなと思って調べました。

ActiveRecord.serializeで実に簡単に実現できるんですね。
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002229

If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, then specify the name of that attribute using this method and it will be handled automatically. The serialization is done through YAML. If class_name is specified, the serialized object must be of that class on retrieval or SerializationTypeMismatch will be raised.

このメソッドでオブジェクトとして保存・取得したい属性の名前を指定すると、自動的に処理されるようになる。 YAMLでシリアライズされ、class_nameオプションが指定されると格納できるオブジェクトのクラスを制限できる。あるいはSerializationTypeMismatchの例外となる。

ポイント

  • シリアライズしたいカラムの型をtextにする
  • serializeメソッドでカラムを指定する
  • 格納できるクラスを指定可能
  • 内部的にはYAML

実例

さて、やってみます

まずマイグレーション内で、text型のdataというフィールドを用意する。
ここにシリアライズしたデータが格納されます。

class CreateItems <ActiveRecord::Migration
  def self.up
    create_table :items do |t|
      t.text :data
    end
  end
  def self.down
    drop_table :items
  end
end


そしてモデル内でシリアライズするフィールドを指定。

class Item <ActiveRecord::Base
  # シリアライズされたデータとして扱いたいフィールドを指定する
  serialize :data
end


script/consoleなどをおもむろに起動し、本当にオブジェクトが保存できるのか確認します。

# 配列を代入する
Item.create :data => [1,2,3]

item = Item.find(:all).last
p item.data #=>[1,2]
p item.data.class #=>Array


ちゃんと配列として取得できました。めでたしめでたし。

どうなってるの?

DBに格納する際にオブジェクトをYAML化しているようです。

検索したい場合

findしたい場合は、オブジェクトではなく文字列として評価されてしまうので Object#to_yamlを使うことになります。

Item.find_by_params([1,2,3]) # => これではレコードが見つからない
Item.find_by_params([1,2,3].to_yaml) # => 見つかる


使い勝手が悪い。。

格納する型を限定する

このままだとオブジェクトならなんでも格納できてしまいます。 そこで格納するクラスを指定してやります。

class Item <ActiveRecord::Base
  # 配列以外のクラスは格納しない
  serialize :data, Array
end


script/consoleで試してみます

# 配列の格納は成功
>> Item.create :data => ['bob','alice']
=> #<item id: 1, data: ["bob", "alice"]>

# ハッシュの格納は例外が発生する
>> Item.create :data => {:name => 'bob'}
ActiveRecord::SerializationTypeMismatch: data was supposed to be a Array, but was a Hash


これでかなり使い勝手が良くなりましたけど、配列の中身までは保証できないので、精神的安定のために使う程度でしょうか?

最後に

DBにシリアライズしたデータを保存しなければならない状況は無いにこした事は無いと思いますので 設計レベルで回避できる場合はそちらをおすすめします。

Posted in ruby on rails, 日記 at 5月 21st, 2009. No Comments.

factorygirl

フィクスチャはメンテナンスしづらい

書籍The Rails Wayの中には「皆フィクスチャが嫌い」という項目があります(次の項目は「フィクスチャはそれほど悪くない」ですが)。

フィクスチャが嫌われる最大の原因は「メンテナンスが難しい」という事でしょう。
中間テーブルを必要とする多対多の関連をフィクスチャにしようと思ってうんざりした経験は誰にもでもあると思います。

Fixtureのhas_manyでこんな書き方したい
http://www.func09.com/wordpress/archives/369

フィクスチャの代わりにFactoryGirlを使ってみる

Factory GirlはRailsのテストにおいてfixtureの代替となるツールです。
つまりテスト時のモデルデータを用意するための仕組みです。
Factory Girlはフィクスチャと違い、Rubyのスクリプトで直接データを定義していきます。

特徴としては

  • Rubyコードで定義(YAMLやCSVではない)
  • 関連のメンテナンスが楽
  • 定義の継承もできる
  • 同じ定義から連続的なデータを生成できる(シーケンス)

Rubyコードで実データを作る感覚でテストデータを定義するので、フィクスチャと違い中間テーブルのメンテナンスから解放されます。 Read More...

Posted in ruby, ruby on rails, 日記 at 4月 26th, 2009. No Comments.