facebook_api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Tekin Suleyman
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,62 @@
1
+ = Facebook API
2
+
3
+ A simple, lightweight Ruby library for accessing the Facebook REST API. Currently used in Facebook Connect applications,
4
+ but could easily be extended for use in canvas applications.
5
+
6
+ == Usage
7
+
8
+ require 'facebook_api'
9
+
10
+ FacebookApi.configure do |config|
11
+ config.api_key = '55e9919b8c017abe484c9fb336dffb90'
12
+ config.secret_key 'fc881eb66493e0b845a3528c018cdd56'
13
+ config.canvas_page_name = 'crowdfm_publisher'
14
+ config.callback_url = 'http://crowd.fm/'
15
+ end
16
+
17
+ session = FacebookApi::Session.new(session_key, uid)
18
+
19
+ # Make REST API calls
20
+ response = session.call('Friends.get', :uid => '12345')
21
+ # Make calls with file attachments
22
+ response = session.call('Photos.upload', {:uid => '12345', :aid => '67890', :caption => 'your caption'}, File.new('/path/to/image.jpg))
23
+ # Make fql calls
24
+ response = session.call_fql('SELECT page_id FROM page_admin WHERE uid="12345"')
25
+
26
+ Both FacebookApi::Session#call and FacebookApi::Session#call_fql will generally return a hash, parsed from the JSON returned
27
+ by Facebook. However, for some API calls Facebook returns non-valid JSON, usually either 'true', 'false' or a string literal
28
+ (e.g. '12345'). In these cases, the return value from #call and #call_fql will be either true, false or the string literal
29
+ respectively.
30
+
31
+ === How to use in a Rails application
32
+
33
+ With a couple of simple methods, you can make Facebook sessions available to you in your controllers and views:
34
+
35
+ class ApplicationController < ActionController::Base
36
+ # Requiring a valid facebook connect session for your controller actions with a before_filter:
37
+ before_filter :require_facebook_session, :except => [:login]
38
+ # Make the facebook_session available in your views by making it a helper_method:
39
+ helper_method :facebook_session
40
+
41
+ private
42
+
43
+ def facebook_session
44
+ @facebook_session ||= facebook_session_from_cookies
45
+ end
46
+
47
+ def facebook_session_from_cookies
48
+ if FacebookApi.verify_connect_cookies_signature(cookies)
49
+ FacebookApi::Session.new(cookies["#{FacebookApi.api_key}_session_key"], cookies["#{FacebookApi.api_key}_user"])
50
+ end
51
+ end
52
+
53
+ def require_facebook_session
54
+ unless facebook_session
55
+ redirect_to facebook_connect_path
56
+ end
57
+ end
58
+ end
59
+
60
+ == Copyright
61
+
62
+ Copyright (c) 2010 Tekin Suleyman. See LICENSE for details.
@@ -0,0 +1,112 @@
1
+ module FacebookApi
2
+ # FacebookApi::Session is your window to the Facebook REST API. Once you have a
3
+ # valid session, you can make API calls with #call and fql calls with #call_fql.
4
+ #
5
+ # Example usage:
6
+ #
7
+ # session = FacebookApi::Session.new(session_key, uid)
8
+ #
9
+ # # Make REST API calls
10
+ # response = session.call('Friends.get', :uid => '12345')
11
+ # # Make calls with file attachments
12
+ # response = session.call('Photos.upload', {:uid => '12345', :aid => '67890', :caption => 'your caption'}, File.new('/path/to/image.jpg))
13
+ # # Make fql calls
14
+ # response = session.call_fql('SELECT page_id FROM page_admin WHERE uid="12345"')
15
+ #
16
+ # The response from an API #call will usually be a hash, converted from the JSON
17
+ # returned by Facebook. For some API calls however, Facebook returns literal
18
+ # values such as 'true', 'false' or an identifier (e.g. '12334234').
19
+ # In these cases, #call returns either true, false or the literal respectively.
20
+ #
21
+ class Session
22
+ attr_reader :session_key, :uid #:nodoc:
23
+
24
+ # Initialise a FacebookApi::Session with a valid session key and uid.
25
+ def initialize(session_key, uid)
26
+ @session_key = session_key
27
+ @uid = uid
28
+ end
29
+
30
+ # Alias for the FacebookApi.logger.
31
+ def logger
32
+ FacebookApi.logger
33
+ end
34
+
35
+ # Makes a Facebook API REST call.
36
+ # If a file is specified, this will be included in the call, e.g. when calling Photos.upload.
37
+ # Example usage:
38
+ #
39
+ # response = session.call('Friends.get', :uid => '12345')
40
+ # response = session.call('Photos.upload', {:uid => '12345', :aid => '67890', :caption => 'your caption'}, File.new('/path/to/image.jpg))
41
+ #
42
+ # Returns the response from Facebook as either a hash, boolean or literal, depending on what Facebook returns.
43
+ # Raises FacebookApi::Error if Facebook returns with an error.
44
+ def call(method, params = {}, file = nil)
45
+ params[:method] = method
46
+ begin
47
+ params = prepare_params(params)
48
+ logger.debug "Sending request to facebook: #{params.inspect}"
49
+ params[nil] = file if file
50
+ response = RestClient.post(FacebookApi::REST_URL, params)
51
+ rescue SocketError => e
52
+ raise IOError.new("Cannot connect to facebook: #{e}")
53
+ end
54
+ logger.debug "Receiving response from facebook: \"#{response.body}\""
55
+ parse_facebook_json response
56
+ end
57
+
58
+ # Makes a Facebook API REST FQL call.
59
+ # Returns the response from Facebook as either a hash, boolean or literal, depending on what Facebook returns.
60
+ # Example:
61
+ #
62
+ # response = session.call('SELECT page_id FROM page_admin WHERE uid="12345"')
63
+ #
64
+ # Raises FacebookApi::Error if Facebook returns with an error.
65
+ def call_fql(query)
66
+ call('Fql.query', :query => query)
67
+ end
68
+
69
+ # Prepares passed in params ready for sending to Facebook with a REST call.
70
+ def prepare_params(params)
71
+ s_params = {}
72
+ params.each_pair {|k,v| s_params[k.to_s] = v }
73
+ s_params['api_key'] = FacebookApi.api_key
74
+ s_params['v'] = FacebookApi::API_VERSION
75
+ s_params['call_id'] = Time.now.to_f.to_s
76
+ s_params['format'] = 'JSON'
77
+ s_params['sig'] = FacebookApi.calculate_signature(s_params)
78
+ s_params
79
+ end
80
+
81
+ # Because Facebook does not always return valid JSON, we need to pre-parse it and catch
82
+ # the special cases.
83
+ # If the response is valid JSON, this returns the parsed response. Otherwise it catches
84
+ # "true", "false" and string letirals, returning true, false or the string respectively.
85
+ # Raises Facebook::APIError if the response from Facebook is an error message.
86
+ def parse_facebook_json(response)
87
+ body = response.body
88
+ if looks_like_json? body
89
+ data = JSON.parse body
90
+ raise FacebookApi::Error.new(data['error_msg'], data['error_code']) if data.include?('error_msg')
91
+ else
92
+ data = parse_literal body
93
+ end
94
+ data
95
+ end
96
+
97
+ private
98
+
99
+ def looks_like_json?(string)
100
+ # If it starts with a '[' or a '{', then it looks like JSON to me.
101
+ string =~ /^[\[\{]/
102
+ end
103
+
104
+ def parse_literal(string)
105
+ case string
106
+ when 'true' then true
107
+ when 'false' then false
108
+ else string
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,117 @@
1
+ require 'logger'
2
+ require 'digest/sha2'
3
+ require 'rest_client'
4
+ require 'json'
5
+
6
+ require 'facebook_api/session'
7
+
8
+ module FacebookApi
9
+
10
+ class Configuration #:nodoc:
11
+ attr_accessor :api_key, :secret_key, :canvas_page_name, :callback_url
12
+ end
13
+
14
+ API_VERSION = '1.0' #:nodoc:
15
+ REST_URL = 'http://api.facebook.com/restserver.php' #:nodoc:
16
+
17
+ @logger = nil
18
+ @config = Configuration.new
19
+
20
+ # Returns the logger for Facebook calls.
21
+ # By default, this outputs to STDOUT.
22
+ def self.logger
23
+ unless @logger
24
+ @logger = ::Logger.new($stdout)
25
+ @logger.level = Logger::INFO
26
+ end
27
+ @logger
28
+ end
29
+
30
+ # Returns the api key. set this with #configure.
31
+ def self.api_key
32
+ config.api_key
33
+ end
34
+
35
+ # Returns the secret key. set this with #configure.
36
+ def self.secret_key
37
+ config.secret_key
38
+ end
39
+
40
+ # Allows you to set your Facebook configuration for accessing the REST API:
41
+ #
42
+ # FacebookApi.configure do |config|
43
+ # config.api_key = 'YOUR_API_KEY'
44
+ # config.secret_key = 'YOUR_SECRET_KEY'
45
+ # config.canvas_page_name = 'YOUR_CANVAS_PAGE_NAME'
46
+ # config.callback_url = 'YOUR_CALLBACK_URL'
47
+ # end
48
+ def self.configure(&block)
49
+ yield @config
50
+ end
51
+
52
+ # Returns the current Facebook configuration. This gets set with #configure.
53
+ def self.config
54
+ @config
55
+ end
56
+
57
+ # Verifies the signature of parmaters sent by Facebook.
58
+ # Returns true if the signature is valid, false otherwise
59
+ # See the API docs here[http://wiki.developers.facebook.com/index.php/Verifying_The_Signature] for
60
+ # more details on how this is calculated.
61
+ def self.verify_facebook_params_signature(args)
62
+ signature = args.delete('fb_sig')
63
+ return false if signature.nil?
64
+
65
+ signed_args = Hash.new
66
+ args.each do |k, v|
67
+ if k =~ /^fb_sig_(.*)/
68
+ signed_args[$1] = v
69
+ end
70
+ end
71
+
72
+ signature == calculate_signature(signed_args)
73
+ end
74
+
75
+ # Verifies the signature in the cookies set by Facebook Connect checks out.
76
+ # Returns true if the signature is valid, false otherwise.
77
+ # See the API docs here[http://wiki.developers.facebook.com/index.php/Verifying_The_Signature#Signatures_and_Facebook_Connect_Sites] for
78
+ # more details on how this is calculated.
79
+ def self.verify_connect_cookies_signature(args)
80
+ signature = args.delete(api_key)
81
+ return false if signature.nil?
82
+
83
+ signed_args = Hash.new
84
+ args.each do |k, v|
85
+ if k =~ /^#{api_key}_(.*)/
86
+ signed_args[$1] = v
87
+ end
88
+ end
89
+
90
+ signature == calculate_signature(signed_args)
91
+ end
92
+
93
+ # Calculates a signature, as described in the API docs here[http://wiki.developers.facebook.com/index.php/Verifying_The_Signature#Generating_the_Signature].
94
+ def self.calculate_signature(params)
95
+ params_string = params.sort.inject('') { |str, pair| str << pair[0] << '=' << pair[1] }
96
+ Digest::MD5.hexdigest(params_string + secret_key)
97
+ end
98
+
99
+ # Helper to convert <tt>ActiveSupport::TimeWithZone</tt> from local time to Pacific time.
100
+ # Use this when sending date/times to Facebook as Facebook expects times to be
101
+ # sent as Pacific time converted to a Unix timestamp.
102
+ def self.convert_time(time)
103
+ if time.is_a?(ActiveSupport::TimeWithZone)
104
+ pacific_zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
105
+ pacific_zone.parse(time.strftime("%Y-%m-%d %H:%M:%S"))
106
+ else
107
+ time
108
+ end
109
+ end
110
+
111
+ # Raised if a Facebook API call fails and returns an error response.
112
+ class Error < StandardError
113
+ def initialize(error_msg, error_code = 1) #:nodoc:
114
+ super("FacebookApi::Error #{error_code}: #{error_msg}" )
115
+ end
116
+ end
117
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: facebook_api
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Tekin Suleyman
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-07 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rest-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">"
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 2
31
+ version: 1.4.2
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: test-unit
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :development
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: shoulda
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :development
57
+ version_requirements: *id003
58
+ - !ruby/object:Gem::Dependency
59
+ name: mocha
60
+ prerelease: false
61
+ requirement: &id004 !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ type: :development
69
+ version_requirements: *id004
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ prerelease: false
73
+ requirement: &id005 !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ type: :development
81
+ version_requirements: *id005
82
+ description:
83
+ email: tekin@tekin.co.uk
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - README.rdoc
90
+ files:
91
+ - LICENSE
92
+ - README.rdoc
93
+ - lib/facebook_api/session.rb
94
+ - lib/facebook_api.rb
95
+ has_rdoc: true
96
+ homepage: http://tekin.co.uk
97
+ licenses: []
98
+
99
+ post_install_message:
100
+ rdoc_options:
101
+ - --main
102
+ - README.rdoc
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project:
122
+ rubygems_version: 1.3.6
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: A simple, lightweight Ruby library for accessing the Facebook API
126
+ test_files: []
127
+