Facebookアプリを作ってみましたシリーズ5 (Ruby on Rails + fgraph サーバーサイドでFQL)

今回はfgraphというライブラリを使ってみました

作ったアプリはこちら

http://apps.facebook.com/taianfriends/
Facebookの友達から、誕生日にさりげなくおごってもらう事を可能にする素敵なサービスです。

環境

Ruby1.9
Rials3
フェイスブックライブラリはfgraph https://github.com/jugend/fgraph

fgraphについて

このライブラリもとてもよくできている印象でした。
Railsのサポートがしっかりしていて、rakeコマンドで設定までやってくれます。(と思ったら、rakeやってもHelperが認識されない…リポジトリrails/init.rbをconfig/initializersに置けばOKです)

ただ、設計は良いと思いますが、FacebookのOpenGrapAPIに合わせたインターフェースになってないので、直感的には使えなかったです。
ドキュメントもきちんとしたものが無いのでソース確認しながらさぐりさぐり使っていく感じでした。
でもわかりやすいので特にストレスなく使えたと思います。


今回もソースそのまま貼っときますので必要であれば勝手に見てください。

こまった事

なんとfgraph はFQLサポートしてない…
一応今回はサーバーサイドFQLを使って作る事を目的としていたのでいきなり不意打ちくらった感じでしたが、
こちらの記事
http://jablko.biz/post/2184601874/fql-in-fgraph
を参考にして無事FQL動かすことができました。

いろいろ

  • Rails使っているとテーブル名は複数形だという癖がついているので、FQL書く時にとまどう
  • JavaScriptだとカラム名まちがってもエラーにならない(レスが無い)のに、サーバーサイドだとちゃんと詳細メッセージを返してくれた。
  • GraphAPIとFQLテーブルの命名規約がそろってないから、カラム名の間違いで結構な頻度でハマる(正確にはFQLテーブル同士でも統一感されてない気がする)。名称変換して統一してしまうようなライブラリを作るとかえって意味分からなくなるかな…
  • なんかFQLテーブルって設計がいろいろイケてない(詳細省略)
  • でもFQLのエラーメッセージは結構親切なのでとりあえず良く分からないFQLは実行してみるのがいいんじゃないかと

ソース

home_controller.rb

# encoding: utf-8
class HomeController < ApplicationController
  
  def index
    
    response.headers["P3P"] = 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'
    
    if fgraph_logged_in?
      
      fg_client = FGraph::Client.new(:access_token => fgraph_access_token)
      
      @user = fg_client.me
      @user.symbolize_keys!
      @user[:birthday_date] = @user[:birthday]
      calc_rokuyou!(@user)
      
      @friends = fg_client.fql("SELECT uid, name, pic_square, sex, birthday_date FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = me())")
      
      @friends.each do |friend|
        calc_rokuyou!(friend)
      end
      @friends.delete_if {|v| v[:birthday_date].nil?}
      @friends.sort!{|a, b| a[:next_birthday_date] <=> b[:next_birthday_date]}
      
      #大安じゃ無い人は5人まで
      not_taian_count = 0;
      @friends.delete_if do |v|
        if v[:next_rokuyou] != 0
          not_taian_count = not_taian_count+1
          not_taian_count > 5
        else 
          false
        end
      end
      
      render :main
    else
      render :index
    end
    
  end
  
  private 
  
  def calc_rokuyou!(user)
    user.symbolize_keys!
    if user[:birthday_date].nil? 
      user[:next_rokuyou] = nil
    else
      user[:birthday_date] = Date.strptime(user[:birthday_date], (user[:birthday_date] =~ /^\d{2}\/\d{2}$/) ? "%m/%d" : "%m/%d/%Y")
      user[:next_birthday_date] = get_next_birthday(user[:birthday_date])
      user[:next_rokuyou] = rokuyou kyureki(user[:next_birthday_date])
    end
  end
  
end


main.html.erb

<%= @user[:name]%>さんの次の誕生日は大安
<%= if @user[:next_rokuyou] == 0 then 'です。' else 'ではありません。' end %>
<br/>
<%= if @user[:next_rokuyou] == 0 then 'ハッピーなので、' else '悲しいので、' end %>
みんなにおごってもらいましょう!
<br/>
<a href="javascript:void(0);" onclick="feed(<%= if @user[:next_rokuyou] == 0 then 'true' else 'false' end %>);"><%= image_tag('btn_treat_me.png', :style => "border-style:none;")%></a>

<br/><br/>
<p>友達の誕生日一覧<br/>ハッピーな人達からおごってもらいましょう!</p>
<% if @friends.size == 0 %>
	友達の誕生日が取得できませんでした。友達をさそってからまらアクセスしてみてください。<br/>
<% else %>
	<table border="1">
	<% @friends.each do |friend| %>
	<tr <%= if friend[:next_rokuyou] == 0 then "bgcolor='#ffc0cb'" end %> >
		<td><img src="<%= friend[:pic_square] %>"/></td>
		<td><%= friend[:name] %></td>
		<td><%= friend[:next_birthday_date] %></td>
		<td><%= if friend[:next_rokuyou] == 0 then "大安" else "" end %></td>
		<td><a href="javascript:void(0);" onclick="wall('<%= friend[:name] %>','<%= friend[:uid] %>', <%= if friend[:next_rokuyou] == 0 then "true" else "false" end %>);"><%= image_tag('btn_treat_me.png', :style => "border-style:none;")%></a></td>
	</tr>
	<% end %>
	</table>
<% end %>

<script>
  function wall(name, toid, is_taian){
	if(is_taian){
		msg = name + 'さんの次の誕生日は大安です。ハッピーなのでおごってください!ありがとうございます!';
	} else {
		msg = name + 'さん、もうすぐ誕生日ですね!ハッピーなのでおごってください!ありがとうございます!';
	}
   FB.ui(
	   {
	     method: 'feed',
	     name: '誕生日なのでおごって!',
	     link: 'http://apps.facebook.com/taianfriends/',
	     picture: 'http://kissrobber.com/taianbirthday/images/logo.png',
		 to: toid,
	     description: msg,
	     actions: {'name':'powered by iq148.com', 'link':'http://iq148.com'}
	   });
  }
  function feed(is_taian){
	if(is_taian){
		msg = '<%= @user[:name]%>さんの次の誕生日は大安でハッピーなので、みんなおごってあげてください!ありがとう!';
	} else {
		msg = '<%= @user[:name]%>さんの次の誕生日は大安じゃなくて悲しいので、みんなおごってあげてください!ありがとう!';
	}
   FB.ui(
	   {
	     method: 'feed',
	     name: '誕生日なのでおごって!',
	     link: 'http://apps.facebook.com/taianfriends/',
	     picture: 'http://kissrobber.com/taianbirthday/images/logo.png',
	     description: msg,
		actions: {'name':'powered by iq148.com', 'link':'http://iq148.com'}
	   });
  }
</script>