diff --git a/lib/facebooker2.rb b/lib/facebooker2.rb index 698ed65..0f6294d 100644 --- a/lib/facebooker2.rb +++ b/lib/facebooker2.rb @@ -1 +1,13 @@ # Facebooker2 +module Facebooker2 + + class << self + attr_accessor :api_key, :secret, :app_id + end + +end + +require "mogli" + +require "facebooker2/rails/controller" +require "facebooker2/rails/helpers/facebook_connect" \ No newline at end of file diff --git a/lib/facebooker2/rails.rb b/lib/facebooker2/rails.rb new file mode 100644 index 0000000..c22f33f --- /dev/null +++ b/lib/facebooker2/rails.rb @@ -0,0 +1,4 @@ +module Facebooker2 + module Rails + end +end \ No newline at end of file diff --git a/lib/facebooker2/rails/controller.rb b/lib/facebooker2/rails/controller.rb new file mode 100644 index 0000000..5834a56 --- /dev/null +++ b/lib/facebooker2/rails/controller.rb @@ -0,0 +1,59 @@ +require "digest/md5" +require "ruby-debug" +module Facebooker2 + module Rails + module Controller + + def current_facebook_user + fetch_client_and_user_from_cookie + @_current_facebook_user + end + + def current_facebook_client + fetch_client_and_user_from_cookie + @_current_facebook_client + end + + def fetch_client_and_user_from_cookie + return if @_fb_user_fetched + app_id = Facebooker2.app_id + if (hash_data = fb_cookie_hash_for_app_id(app_id)) and + fb_cookie_signature_correct?(fb_cookie_hash_for_app_id(app_id),Facebooker2.secret) + @_current_facebook_client = Mogli::Client.new(hash_data["access_token"],hash_data["expires"].to_i) + @_current_facebook_user = Mogli::User.new(:id=>hash_data["uid"]) + @_current_facebook_user.client = @_current_facebook_client + end + @_fb_user_fetched = true + end + + def fb_cookie_hash_for_app_id(app_id) + return nil unless fb_cookie_for_app_id?(app_id) + hash={} + data = fb_cookie_for_app_id(app_id) + data.split("&").each do |str| + parts = str.split("=") + hash[parts.first] = parts.last + end + hash + end + + def fb_cookie_for_app_id?(app_id) + !fb_cookie_for_app_id(app_id).nil? + end + + def fb_cookie_for_app_id(app_id) + cookies["fbs_#{app_id}"] + end + + def fb_cookie_signature_correct?(hash,secret) + sorted_keys = hash.keys.reject {|k| k=="sig"}.sort + test_string = "" + sorted_keys.each do |key| + test_string += "#{key}=#{hash[key]}" + end + test_string += secret + Digest::MD5.hexdigest(test_string) == hash["sig"] + end + end + end +end \ No newline at end of file diff --git a/lib/facebooker2/rails/helpers/facebook_connect.rb b/lib/facebooker2/rails/helpers/facebook_connect.rb new file mode 100644 index 0000000..6b0c5ad --- /dev/null +++ b/lib/facebooker2/rails/helpers/facebook_connect.rb @@ -0,0 +1,45 @@ +module Facebooker2 + module Rails + module Helpers + module FacebookConnect + # + # Render an element, similar to + # fb_login_button. Adds a js redirect to the onlogin event via rjs. + # + # ==== Examples + # + # fb_login_and_redirect '/other_page' + # => + # + # Like #fb_login_button, this also supports the :text option + # + # fb_login_and_redirect '/other_page', :text => "Login with Facebook", :v => '2' + # => Login with Facebook + # + def fb_login_and_redirect(url, options = {}) + js = update_page do |page| + page.redirect_to url + end + + text = options.delete(:text) + + content_tag("fb:login-button",text,options.merge(:onlogin=>js)) + end + + # + # Logs the user out of facebook and redirects to the given URL + # args are passed to the call to link_to_function + def fb_logout_link(text,url,*args) + function= "FB.logout(function() {window.location.href = '#{url}';})" + link_to_function text, function, *args + end + + def fb_server_fbml(style=nil,&proc) + style_string=" style=\"#{style}\"" if style + content = capture(&proc) + concat("") + end + end + end + end +end \ No newline at end of file diff --git a/spec/facebooker2_spec.rb b/spec/facebooker2_spec.rb new file mode 100644 index 0000000..71c7601 --- /dev/null +++ b/spec/facebooker2_spec.rb @@ -0,0 +1,20 @@ +require "spec_helper" +describe Facebooker2 do + + describe "Configuration" do + it "allows setting of the api_key" do + Facebooker2.api_key = "123456" + Facebooker2.api_key.should == "123456" + end + + it "allows setting of the secret" do + Facebooker2.secret = "mysecret" + Facebooker2.secret.should == "mysecret" + end + + it "has an app_id" do + Facebooker2.app_id = "12345" + Facebooker2.app_id.should == "12345" + end + end +end \ No newline at end of file diff --git a/spec/helpers/facebook_connect_spec.rb b/spec/helpers/facebook_connect_spec.rb new file mode 100644 index 0000000..90a1379 --- /dev/null +++ b/spec/helpers/facebook_connect_spec.rb @@ -0,0 +1,49 @@ +require "spec_helper" +require "ruby-debug" +describe Facebooker2::Rails::Helpers::FacebookConnect, :type=>:helper do + include Facebooker2::Rails::Helpers::FacebookConnect + describe "fb_login_and_redirect" do + it "renders a login button" do + fb_login_and_redirect("/").should == + "" + end + + it "allows you to specify the text of the button" do + fb_login_and_redirect("/",:text=>"my test").should == + "my test" + end + end + + describe "Logging out" do + it "has an fb_logout_link" do + fb_logout_link("logout","/").should == + "logout" + end + end + + describe "server FBML" do + it "renders the yielded content inside of an fbml block" do + fb_server_fbml do + + end + output_buffer.should == "" + end + + it "includes the content inside the block" do + fb_server_fbml do + "inner text" + end + output_buffer.should == "" + end + + it "allows specifying style attributes" do + fb_server_fbml "width: 750px;" do + + end + output_buffer.should == "" + + end + end + +end + diff --git a/spec/rails/controller_spec.rb b/spec/rails/controller_spec.rb new file mode 100644 index 0000000..5619b8a --- /dev/null +++ b/spec/rails/controller_spec.rb @@ -0,0 +1,88 @@ +require "spec_helper" + +class FakeController + include Facebooker2::Rails::Controller +end + +describe Facebooker2::Rails::Controller do + before(:each) do + Facebooker2.app_id = "12345" + Facebooker2.secret = "42ca6de519d53f6e0420247a4d108d90" + end + describe "Cookie handling" do + let :controller do + controller = FakeController.new + controller.stub!(:cookies).and_return("fbs_12345"=>"access_token=114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.&expires=0&secret=1e3375dcc4527e7ead0f82c095421690&session_key=57f0206b01ad48bf84ac86f1-12451752&sig=4337fcdee4cc68bb70ec495c0eebf89c&uid=12451752") + controller + end + + it "knows if a cookie exists for this app" do + controller.fb_cookie_for_app_id?(12345).should be_true + end + + it "knows when there isn't a cookie" do + controller.fb_cookie_for_app_id?(432432).should be_false + end + + it "gets the hash from the cookie" do + controller.stub!(:cookies).and_return("fbs_12345"=>"param1=val1¶m2=val2") + controller.fb_cookie_hash_for_app_id(12345).should == {"param1"=>"val1", "param2"=>"val2"} + end + + it "creates a user from the cookie" do + controller.current_facebook_user.should_not be_nil + controller.current_facebook_user.should be_an_instance_of(Mogli::User) + controller.current_facebook_user.id.should == "12451752" + end + + it "doesn't create a user if there is no app cookie" do + Facebooker2.app_id="other_app" + controller.current_facebook_user.should be_nil + end + + it "creates a client from the cookie" do + controller.current_facebook_client.should_not be_nil + controller.current_facebook_client.should be_an_instance_of(Mogli::Client) + controller.current_facebook_client.access_token.should == "114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA." + end + + it "verifies that the signature is correct" do + controller.fb_cookie_signature_correct?({ + "access_token" => "114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.", + "expires" => "0", + "secret" => "1e3375dcc4527e7ead0f82c095421690", + "session_key" => "57f0206b01ad48bf84ac86f1-12451752", + "uid" => "12451752", + "sig" => "4337fcdee4cc68bb70ec495c0eebf89c"}, + "42ca6de519d53f6e0420247a4d108d90" + ).should be_true + end + + it "returns false if the signature is not correct" do + controller.fb_cookie_signature_correct?({ + "access_token" => "114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.", + "expires" => "0", + "secret" => "1e3375dcc4527e7ead0f82c095421690", + "session_key" => "57f0206b01ad48bf84ac86f1-12451752", + "uid" => "5436785463785", + "sig" => "4337fcdee4cc68bb70ec495c0eebf89c"}, + "42ca6de519d53f6e0420247a4d108d90" + ).should be_false + + end + + + end + + describe "Methods" do + it "has a current_facebook_user" do + + end + + it "has a current_facebook_client" do + + end + + end + +end \ No newline at end of file diff --git a/spec/spec.opts b/spec/spec.opts new file mode 100644 index 0000000..e69de29 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..8a0daf1 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,26 @@ +require "rubygems" +require "active_support" +require "action_pack" +require "action_view" +require "action_controller" +require 'action_controller/test_process' +require 'action_controller/integration' +require 'active_support/test_case' +require 'spec/test/unit' + + +require "facebooker2" +gem "rspec-rails" + +#required because view tests need a controller +class ApplicationController < ActionController::Base +end + +#load just the files needed for helper tests so that we don't need a full rails stack +require "spec/rails/example/functional_example_group" +require "spec/rails/example/helper_example_group" +require 'spec/rails/interop/testcase' + +Spec::Example::ExampleGroupFactory.default(ActiveSupport::TestCase) + +