simple_facebook_connect 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/.gitignore +1 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +115 -0
  4. data/Rakefile +27 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/simple_facebook_connect/connect_controller.rb +59 -0
  7. data/app/views/shared/_facebook_connect_button.html.erb +15 -0
  8. data/config/simple_facebook_connect_routes.rb +4 -0
  9. data/generators/simple_facebook_connect_features/simple_facebook_connect_features_generator.rb +14 -0
  10. data/generators/simple_facebook_connect_features/templates/facebook_auth_getsession.xml +9 -0
  11. data/generators/simple_facebook_connect_features/templates/facebook_connect.feature +27 -0
  12. data/generators/simple_facebook_connect_features/templates/facebook_connect_steps.rb +19 -0
  13. data/generators/simple_facebook_connect_features/templates/facebook_connect_stubs.rb +43 -0
  14. data/generators/simple_facebook_connect_features/templates/facebook_users_getinfo.xml +119 -0
  15. data/generators/simple_facebook_connect_migration/simple_facebook_connect_migration_generator.rb +13 -0
  16. data/generators/simple_facebook_connect_migration/templates/migration.rb +17 -0
  17. data/init.rb +35 -0
  18. data/lib/simple_facebook_connect/controller_extension.rb +11 -0
  19. data/lib/simple_facebook_connect/extensions/routes.rb +13 -0
  20. data/lib/simple_facebook_connect/parser.rb +163 -0
  21. data/lib/simple_facebook_connect/service.rb +34 -0
  22. data/lib/simple_facebook_connect/session.rb +73 -0
  23. data/lib/simple_facebook_connect/user.rb +28 -0
  24. data/lib/simple_facebook_connect/user_extension.rb +21 -0
  25. data/lib/simple_facebook_connect.rb +14 -0
  26. data/rails/init.rb +1 -0
  27. data/simple_facebook_connect.gemspec +71 -0
  28. data/spec/fixtures/facebook.auth.getSession/default.xml +9 -0
  29. data/spec/fixtures/facebook.users.getInfo/default.xml +119 -0
  30. data/spec/lib/simple_facebook_connect/parser_spec.rb +12 -0
  31. data/spec/lib/simple_facebook_connect/user_extension_spec.rb +16 -0
  32. data/spec/lib/simple_facebook_connect/user_spec.rb +37 -0
  33. data/spec/spec_helper.rb +30 -0
  34. metadata +91 -0
@@ -0,0 +1,163 @@
1
+ require 'rexml/document'
2
+
3
+ module SimpleFacebookConnect
4
+
5
+ class FacebookApiError < StandardError; end
6
+
7
+ class Parser
8
+
9
+ module REXMLElementExtensions
10
+ def text_value
11
+ self.children.first.to_s.strip
12
+ end
13
+ end
14
+
15
+ ::REXML::Element.__send__(:include, REXMLElementExtensions)
16
+
17
+ def self.parse(method, data)
18
+ Errors.process(data)
19
+ parser = PARSERS[method]
20
+ parser.process(
21
+ data
22
+ )
23
+ end
24
+
25
+ def self.array_of(response_element, element_name)
26
+ values_to_return = []
27
+ response_element.elements.each(element_name) do |element|
28
+ values_to_return << yield(element)
29
+ end
30
+ values_to_return
31
+ end
32
+
33
+ def self.array_of_text_values(response_element, element_name)
34
+ array_of(response_element, element_name) do |element|
35
+ element.text_value
36
+ end
37
+ end
38
+
39
+ def self.array_of_hashes(response_element, element_name)
40
+ array_of(response_element, element_name) do |element|
41
+ hashinate(element)
42
+ end
43
+ end
44
+
45
+ def self.element(name, data)
46
+ data = data.body rescue data # either data or an HTTP response
47
+ doc = REXML::Document.new(data)
48
+ doc.elements.each(name) do |element|
49
+ return element
50
+ end
51
+ raise "Element #{name} not found in #{data}"
52
+ end
53
+
54
+ def self.hash_or_value_for(element)
55
+ if element.children.size == 1 && element.children.first.kind_of?(REXML::Text)
56
+ element.text_value
57
+ else
58
+ hashinate(element)
59
+ end
60
+ end
61
+
62
+ def self.hashinate(response_element)
63
+ response_element.children.reject{|c| c.kind_of? REXML::Text}.inject({}) do |hash, child|
64
+ # If the node hasn't any child, and is not a list, we want empty strings, not empty hashes,
65
+ # except if attributes['nil'] == true
66
+ hash[child.name] =
67
+ if (child.attributes['nil'] == 'true')
68
+ nil
69
+ elsif (child.children.size == 1 && child.children.first.kind_of?(REXML::Text)) || (child.children.size == 0 && child.attributes['list'] != 'true')
70
+ anonymous_field_from(child, hash) || child.text_value
71
+ elsif child.attributes['list'] == 'true'
72
+ child.children.reject{|c| c.kind_of? REXML::Text}.map { |subchild| hash_or_value_for(subchild)}
73
+ else
74
+ child.children.reject{|c| c.kind_of? REXML::Text}.inject({}) do |subhash, subchild|
75
+ subhash[subchild.name] = hash_or_value_for(subchild)
76
+ subhash
77
+ end
78
+ end #if (child.attributes)
79
+ hash
80
+ end #do |hash, child|
81
+ end
82
+
83
+ def self.booleanize(response)
84
+ response == "1" ? true : false
85
+ end
86
+
87
+ def self.anonymous_field_from(child, hash)
88
+ if child.name == 'anon'
89
+ (hash[child.name] || []) << child.text_value
90
+ end
91
+ end
92
+
93
+ class RevokeAuthorization < self
94
+ def self.process(data)
95
+ booleanize(data)
96
+ end
97
+ end
98
+
99
+ class CreateToken < self
100
+ def self.process(data)
101
+ element('auth_createToken_response', data).text_value
102
+ end
103
+ end
104
+
105
+ class RegisterUsers < self
106
+ def self.process(data)
107
+ array_of_text_values(element("connect_registerUsers_response", data), "connect_registerUsers_response_elt")
108
+ end
109
+ end
110
+
111
+ class GetSession < self
112
+ def self.process(data)
113
+ hashinate(element('auth_getSession_response', data))
114
+ end
115
+ end
116
+
117
+ class UserInfo < self
118
+ def self.process(data)
119
+ array_of_hashes(element('users_getInfo_response', data), 'user')
120
+ end
121
+ end
122
+
123
+ class UserStandardInfo < self
124
+ def self.process(data)
125
+ array_of_hashes(element('users_getStandardInfo_response', data), 'standard_user_info')
126
+ end
127
+ end
128
+
129
+ class GetLoggedInUser < self
130
+ def self.process(data)
131
+ Integer(element('users_getLoggedInUser_response', data).text_value)
132
+ end
133
+ end
134
+
135
+ class ProfileInfo < self
136
+ def self.process(data)
137
+ hashinate(element('profile_getInfo_response info_fields', data))
138
+ end
139
+ end
140
+
141
+ class Errors < self
142
+ def self.process(data)
143
+ response_element = element('error_response', data) rescue nil
144
+ if response_element
145
+ hash = hashinate(response_element)
146
+ raise FacebookApiError, hash['error_msg']
147
+ end
148
+ end
149
+ end
150
+
151
+
152
+ PARSERS = {
153
+ 'facebook.auth.revokeAuthorization' => RevokeAuthorization,
154
+ 'facebook.auth.createToken' => CreateToken,
155
+ 'facebook.auth.getSession' => GetSession,
156
+ 'facebook.connect.registerUsers' => RegisterUsers,
157
+ 'facebook.users.getInfo' => UserInfo,
158
+ 'facebook.users.getStandardInfo' => UserStandardInfo,
159
+ 'facebook.profile.getInfo' => ProfileInfo
160
+ }
161
+
162
+ end
163
+ end
@@ -0,0 +1,34 @@
1
+ require 'net/http'
2
+
3
+ module SimpleFacebookConnect
4
+
5
+ class Service
6
+ def initialize(api_base, api_path, api_key)
7
+ @api_base = api_base
8
+ @api_path = api_path
9
+ @api_key = api_key
10
+ end
11
+
12
+ def post(params)
13
+ attempt = 0
14
+ Parser.parse(params[:method], post_form(url,params) )
15
+ rescue Errno::ECONNRESET, EOFError
16
+ if attempt == 0
17
+ attempt += 1
18
+ retry
19
+ end
20
+ end
21
+
22
+ def post_form(url,params)
23
+ Net::HTTP.post_form(url, params.stringify_keys)
24
+ end
25
+
26
+ private
27
+ def url(base = nil)
28
+ base ||= @api_base
29
+ URI.parse('http://'+ base + @api_path)
30
+ end
31
+
32
+
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ module SimpleFacebookConnect
2
+
3
+ class Session
4
+ API_SERVER_BASE_URL = "api.facebook.com"
5
+ API_PATH_REST = "/restserver.php"
6
+ WWW_SERVER_BASE_URL = "www.facebook.com"
7
+ WWW_PATH_LOGIN = "/login.php"
8
+
9
+ attr_accessor :auth_token
10
+ attr_reader :session_key
11
+
12
+
13
+ def initialize(api_key, secret_key)
14
+ @api_key = api_key
15
+ @secret_key = secret_key
16
+ @session_key = nil
17
+ @uid = nil
18
+ @auth_token = nil
19
+ @secret_from_session = nil
20
+ @expires = nil
21
+ end
22
+
23
+ def login_url(options={})
24
+ "http://#{WWW_SERVER_BASE_URL}#{WWW_PATH_LOGIN}?api_key=#{@api_key}&v=1.0"
25
+ end
26
+
27
+
28
+ def secure!
29
+ response = post 'facebook.auth.getSession', :auth_token => auth_token
30
+ @session_key = response['session_key']
31
+ @uid = response['uid'].to_i
32
+ @expires = response['expires'].to_i
33
+ @secret_from_session = response['secret']
34
+ end
35
+
36
+ def user
37
+ @user ||= User.new(uid, self)
38
+ end
39
+
40
+ def post(method, params = {}, use_session_key = true, &proc)
41
+ add_facebook_params(params, method)
42
+ use_session_key && @session_key && params[:session_key] ||= @session_key
43
+ final_params = params.merge(:sig => signature_for(params))
44
+ result = service.post(final_params)
45
+ result = yield result if block_given?
46
+ result
47
+ end
48
+
49
+ private
50
+ def add_facebook_params(hash, method)
51
+ hash[:method] = method
52
+ hash[:api_key] = @api_key
53
+ hash[:call_id] = Time.now.to_f.to_s unless method == 'facebook.auth.getSession'
54
+ hash[:v] = "1.0"
55
+ end
56
+
57
+ def service
58
+ @service ||= Service.new(API_SERVER_BASE_URL, API_PATH_REST, @api_key)
59
+ end
60
+
61
+ def uid
62
+ @uid || (secure!; @uid)
63
+ end
64
+
65
+ def signature_for(params)
66
+ raw_string = params.inject([]) do |collection, pair|
67
+ collection << pair.join("=")
68
+ collection
69
+ end.sort.join
70
+ Digest::MD5.hexdigest([raw_string, @secret_key].join)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ module SimpleFacebookConnect
2
+
3
+ class User
4
+
5
+ FIELDS = [:uid, :hometown_location, :first_name, :last_name, :current_location, :pic, :locale, :email_hashes, :about_me, :interests]
6
+ attr_reader(*FIELDS)
7
+ attr_reader :session
8
+
9
+ def initialize(uid, session)
10
+ @uid = uid
11
+ @session = session
12
+ populate
13
+ end
14
+
15
+ def populate
16
+ @session.post('facebook.users.getInfo', :fields => coma_seperated_fields, :uids => uid) do |response|
17
+ FIELDS.each do |field|
18
+ instance_variable_set(:"@#{field}", response.first[field.to_s])
19
+ end
20
+ end
21
+ end
22
+
23
+ def coma_seperated_fields
24
+ FIELDS.join(',')
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ require 'zlib'
2
+ require 'digest/md5'
3
+
4
+ module SimpleFacebookConnect
5
+ module UserExtension
6
+
7
+ def self.included(base)
8
+ base.class_eval do
9
+ before_save :build_email_hash, :if => :email_changed?
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def build_email_hash
16
+ str = email.strip.downcase
17
+ self.email_hash = "#{Zlib.crc32(str)}_#{Digest::MD5.hexdigest(str)}"
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+
3
+ require 'simple_facebook_connect/parser'
4
+ require 'simple_facebook_connect/service'
5
+ require 'simple_facebook_connect/session'
6
+ require 'simple_facebook_connect/user'
7
+ require 'simple_facebook_connect/user_extension'
8
+ require 'simple_facebook_connect/controller_extension'
9
+
10
+ module SimpleFacebookConnect
11
+ class << self
12
+ attr_accessor :api_key, :secret_key
13
+ end
14
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../init'
@@ -0,0 +1,71 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{simple_facebook_connect}
5
+ s.version = "0.0.5"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alexander Lang", "Frank Pr\303\266\303\237dorf"]
9
+ s.date = %q{2009-07-22}
10
+ s.email = %q{alex@upstream-berlin.com}
11
+ s.extra_rdoc_files = [
12
+ "README.md"
13
+ ]
14
+ s.files = [
15
+ ".gitignore",
16
+ "MIT-LICENSE",
17
+ "README.md",
18
+ "Rakefile",
19
+ "VERSION",
20
+ "app/controllers/simple_facebook_connect/connect_controller.rb",
21
+ "app/views/shared/_facebook_connect_button.html.erb",
22
+ "config/simple_facebook_connect_routes.rb",
23
+ "generators/simple_facebook_connect_features/simple_facebook_connect_features_generator.rb",
24
+ "generators/simple_facebook_connect_features/templates/facebook_auth_getsession.xml",
25
+ "generators/simple_facebook_connect_features/templates/facebook_connect.feature",
26
+ "generators/simple_facebook_connect_features/templates/facebook_connect_steps.rb",
27
+ "generators/simple_facebook_connect_features/templates/facebook_connect_stubs.rb",
28
+ "generators/simple_facebook_connect_features/templates/facebook_users_getinfo.xml",
29
+ "generators/simple_facebook_connect_migration/simple_facebook_connect_migration_generator.rb",
30
+ "generators/simple_facebook_connect_migration/templates/migration.rb",
31
+ "init.rb",
32
+ "lib/simple_facebook_connect.rb",
33
+ "lib/simple_facebook_connect/controller_extension.rb",
34
+ "lib/simple_facebook_connect/extensions/routes.rb",
35
+ "lib/simple_facebook_connect/parser.rb",
36
+ "lib/simple_facebook_connect/service.rb",
37
+ "lib/simple_facebook_connect/session.rb",
38
+ "lib/simple_facebook_connect/user.rb",
39
+ "lib/simple_facebook_connect/user_extension.rb",
40
+ "rails/init.rb",
41
+ "simple_facebook_connect.gemspec",
42
+ "spec/fixtures/facebook.auth.getSession/default.xml",
43
+ "spec/fixtures/facebook.users.getInfo/default.xml",
44
+ "spec/lib/simple_facebook_connect/parser_spec.rb",
45
+ "spec/lib/simple_facebook_connect/user_extension_spec.rb",
46
+ "spec/lib/simple_facebook_connect/user_spec.rb",
47
+ "spec/spec_helper.rb"
48
+ ]
49
+ s.has_rdoc = true
50
+ s.homepage = %q{http://github.com/upstream/simple_facebook_connect}
51
+ s.rdoc_options = ["--charset=UTF-8"]
52
+ s.require_paths = ["lib"]
53
+ s.rubygems_version = %q{1.3.1}
54
+ s.summary = %q{This plugin adds the ability to sign in/sign up using facebook connect to your Rails application.}
55
+ s.test_files = [
56
+ "spec/lib/simple_facebook_connect/parser_spec.rb",
57
+ "spec/lib/simple_facebook_connect/user_extension_spec.rb",
58
+ "spec/lib/simple_facebook_connect/user_spec.rb",
59
+ "spec/spec_helper.rb"
60
+ ]
61
+
62
+ if s.respond_to? :specification_version then
63
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
+ s.specification_version = 2
65
+
66
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
+ else
68
+ end
69
+ else
70
+ end
71
+ end
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <auth_getSession_response
3
+ xmlns="http://api.facebook.com/1.0/"
4
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5
+ xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">
6
+ <session_key>5f34e11bfb97c762e439e6a5-8055</session_key>
7
+ <uid>8055</uid>
8
+ <expires>1173309298</expires>
9
+ </auth_getSession_response>
@@ -0,0 +1,119 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <users_getInfo_response xmlns="http://api.facebook.com/1.0/"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd" list="true">
5
+ <user>
6
+ <uid>8055</uid>
7
+ <about_me>This field perpetuates the glorification of the ego. Also, it has a character limit.</about_me>
8
+ <activities>Here: facebook, etc. There: Glee Club, a capella, teaching.</activities>
9
+ <affiliations list="true">
10
+ <affiliation>
11
+ <nid>50453093</nid>
12
+ <name>Facebook Developers</name>
13
+ <type>work</type>
14
+ <status/>
15
+ <year/>
16
+ </affiliation>
17
+ </affiliations>
18
+ <birthday>November 3</birthday>
19
+ <books>The Brothers K, GEB, Ken Wilber, Zen and the Art, Fitzgerald, The Emporer's New Mind, The Wonderful Story of Henry Sugar</books>
20
+ <current_location>
21
+ <city>Palo Alto</city>
22
+ <state>California</state>
23
+ <country>United States</country>
24
+ <zip>94303</zip>
25
+ </current_location>
26
+ <education_history list="true">
27
+ <education_info>
28
+ <name>Harvard</name>
29
+ <year>2003</year>
30
+ <concentrations list="true">
31
+ <concentration>Applied Mathematics</concentration>
32
+ <concentration>Computer Science</concentration>
33
+ </concentrations>
34
+ </education_info>
35
+ </education_history>
36
+ <email_hashes list="true">
37
+ <email_hash_elt>2781152470_9f9c29692798573d8c76eaaf053a1911</email_hash_elt>
38
+ </email_hashes>
39
+ <family list="true">
40
+ <family_elt list="true">
41
+ <family_elt_elt>mother</family_elt_elt>
42
+ <family_elt_elt>1394244902</family_elt_elt>
43
+ </family_elt>
44
+ <family_elt list="true">
45
+ <family_elt_elt>sister</family_elt_elt>
46
+ <family_elt_elt>48703107</family_elt_elt>
47
+ </family_elt>
48
+ <family_elt list="true">
49
+ <family_elt_elt>brother</family_elt_elt>
50
+ <family_elt_elt>1078767258</family_elt_elt>
51
+ </family_elt>
52
+ <family_elt list="true">
53
+ <family_elt_elt>brother</family_elt_elt>
54
+ <family_elt_elt>John Doe</family_elt_elt>
55
+ <family_elt_elt/>
56
+ </family_elt>
57
+ </family>
58
+ <first_name>Dave</first_name>
59
+ <hometown_location>
60
+ <city>York</city>
61
+ <state>Pennsylvania</state>
62
+ <country>United States</country>
63
+ </hometown_location>
64
+ <hs_info>
65
+ <hs1_name>Central York High School</hs1_name>
66
+ <hs2_name/>
67
+ <grad_year>1999</grad_year>
68
+ <hs1_id>21846</hs1_id>
69
+ <hs2_id>0</hs2_id>
70
+ </hs_info>
71
+ <is_app_user>1</is_app_user>
72
+ <has_added_app>1</has_added_app>
73
+ <interests>coffee, computers, the funny, architecture, code breaking,snowboarding, philosophy, soccer, talking to strangers</interests>
74
+ <last_name>Fetterman</last_name>
75
+ <locale>en_US</locale>
76
+ <meeting_for list="true">
77
+ <seeking>Friendship</seeking>
78
+ </meeting_for>
79
+ <meeting_sex list="true">
80
+ <sex>female</sex>
81
+ </meeting_sex>
82
+ <movies>Tommy Boy, Billy Madison, Fight Club, Dirty Work, Meet the Parents, My Blue Heaven, Office Space </movies>
83
+ <music>New Found Glory, Daft Punk, Weezer, The Crystal Method, Rage, the KLF, Green Day, Live, Coldplay, Panic at the Disco, Family Force 5</music>
84
+ <name>Dave Fetterman</name>
85
+ <notes_count>0</notes_count>
86
+ <pic>http://photos-055.facebook.com/ip007/profile3/1271/65/s8055_39735.jpg</pic>
87
+ <pic_big>http://photos-055.facebook.com/ip007/profile3/1271/65/n8055_39735.jpg</pic_big>
88
+ <pic_small>http://photos-055.facebook.com/ip007/profile3/1271/65/t8055_39735.jpg</pic_small>
89
+ <pic_square>http://photos-055.facebook.com/ip007/profile3/1271/65/q8055_39735.jpg</pic_square>
90
+ <political>Moderate</political>
91
+ <profile_update_time>1170414620</profile_update_time>
92
+ <quotes/>
93
+ <relationship_status>In a Relationship</relationship_status>
94
+ <religion/>
95
+ <sex>male</sex>
96
+ <significant_other_id xsi:nil="true"/>
97
+ <status>
98
+ <message>Fast Company, November issue, page 84</message>
99
+ <time>1193075616</time>
100
+ </status>
101
+ <timezone>-8</timezone>
102
+ <tv>cf. Bob Trahan</tv>
103
+ <wall_count>121</wall_count>
104
+ <work_history list="true">
105
+ <work_info>
106
+ <location>
107
+ <city>Palo Alto</city>
108
+ <state>CA</state>
109
+ <country>United States</country>
110
+ </location>
111
+ <company_name>Facebook</company_name>
112
+ <position>Software Engineer</position>
113
+ <description>Tech Lead, Facebook Platform</description>
114
+ <start_date>2006-01</start_date>
115
+ <end_date/>
116
+ </work_info>
117
+ </work_history>
118
+ </user>
119
+ </users_getInfo_response>
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe SimpleFacebookConnect::Parser, 'parse' do
4
+ it "should parse getInfo xml response" do
5
+ result = SimpleFacebookConnect::Parser.parse('facebook.users.getInfo', File.read(File.dirname(__FILE__) + '/../../fixtures/facebook.users.getInfo/default.xml'))
6
+ result.should == parsed_array
7
+ end
8
+
9
+ def parsed_array
10
+ [{"uid"=>"8055", "about_me"=>"This field perpetuates the glorification of the ego. Also, it has a character limit.", "activities"=>"Here: facebook, etc. There: Glee Club, a capella, teaching.", "affiliations"=>[{"nid"=>"50453093", "name"=>"Facebook Developers", "type"=>"work", "status"=>"", "year"=>""}], "birthday"=>"November 3", "books"=>"The Brothers K, GEB, Ken Wilber, Zen and the Art, Fitzgerald, The Emporer's New Mind, The Wonderful Story of Henry Sugar", "current_location"=>{"city"=>"Palo Alto", "state"=>"California", "country"=>"United States", "zip"=>"94303"}, "education_history"=>[{"name"=>"Harvard", "year"=>"2003", "concentrations"=>["Applied Mathematics", "Computer Science"]}], "email_hashes"=>["2781152470_9f9c29692798573d8c76eaaf053a1911"], "family"=>[{"family_elt_elt"=>"1394244902"}, {"family_elt_elt"=>"48703107"}, {"family_elt_elt"=>"1078767258"}, {"family_elt_elt"=>""}], "first_name"=>"Dave", "hometown_location"=>{"city"=>"York", "state"=>"Pennsylvania", "country"=>"United States"}, "hs_info"=>{"hs1_name"=>"Central York High School", "hs2_name"=>{}, "grad_year"=>"1999", "hs1_id"=>"21846", "hs2_id"=>"0"}, "is_app_user"=>"1", "has_added_app"=>"1", "interests"=>"coffee, computers, the funny, architecture, code breaking,snowboarding, philosophy, soccer, talking to strangers", "last_name"=>"Fetterman", "locale"=>"en_US", "meeting_for"=>["Friendship"], "meeting_sex"=>["female"], "movies"=>"Tommy Boy, Billy Madison, Fight Club, Dirty Work, Meet the Parents, My Blue Heaven, Office Space", "music"=>"New Found Glory, Daft Punk, Weezer, The Crystal Method, Rage, the KLF, Green Day, Live, Coldplay, Panic at the Disco, Family Force 5", "name"=>"Dave Fetterman", "notes_count"=>"0", "pic"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/s8055_39735.jpg", "pic_big"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/n8055_39735.jpg", "pic_small"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/t8055_39735.jpg", "pic_square"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/q8055_39735.jpg", "political"=>"Moderate", "profile_update_time"=>"1170414620", "quotes"=>"", "relationship_status"=>"In a Relationship", "religion"=>"", "sex"=>"male", "significant_other_id"=>nil, "status"=>{"message"=>"Fast Company, November issue, page 84", "time"=>"1193075616"}, "timezone"=>"-8", "tv"=>"cf. Bob Trahan", "wall_count"=>"121", "work_history"=>[{"location"=>{"city"=>"Palo Alto", "state"=>"CA", "country"=>"United States"}, "company_name"=>"Facebook", "position"=>"Software Engineer", "description"=>"Tech Lead, Facebook Platform", "start_date"=>"2006-01", "end_date"=>""}]}]
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe SimpleFacebookConnect::UserExtension, 'email=' do
4
+ class User < ActiveRecord::Base
5
+ include SimpleFacebookConnect::UserExtension
6
+
7
+ end
8
+
9
+ it "should set the email hash" do
10
+ user = User.new
11
+ user.email = 'joe@doe.com'
12
+ user.save!
13
+ user.email_hash.should == '3404385302_68ef883c573edb5d26365e8f20156eec'
14
+
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe SimpleFacebookConnect::User, 'initialize' do
4
+ before(:each) do
5
+ @session = stub 'session', :post => nil
6
+ end
7
+
8
+ it "should send a remote call to facebook" do
9
+ @session.should_receive(:post).with('facebook.users.getInfo', :fields => 'uid,hometown_location,first_name,last_name,current_location,pic,locale,email_hashes,about_me,interests', :uids => 8055)
10
+ SimpleFacebookConnect::User.new(8055, @session)
11
+ end
12
+
13
+ it "should populate the user attributes" do
14
+ @session.stub!(:post).and_yield(user_info_response)
15
+ user = SimpleFacebookConnect::User.new(8055, @session)
16
+ user.email_hashes.should == ["2781152470_9f9c29692798573d8c76eaaf053a1911"]
17
+ user.uid.should == '8055'
18
+ user.hometown_location.should == {"city"=>"York", "state"=>"Pennsylvania", "country"=>"United States"}
19
+ user.first_name.should == 'Dave'
20
+ user.last_name.should == 'Fetterman'
21
+ user.current_location.should == {"city"=>"Palo Alto", "state"=>"California", "country"=>"United States", "zip"=>"94303"}
22
+ user.pic.should == 'http://photos-055.facebook.com/ip007/profile3/1271/65/s8055_39735.jpg'
23
+ user.locale.should == 'en_US'
24
+ user.about_me.should == 'This field perpetuates the glorification of the ego. Also, it has a character limit.'
25
+ user.interests.should == 'snowboarding, philosophy, soccer, talking to strangers'
26
+ end
27
+
28
+ it "should expose the session" do
29
+ user = SimpleFacebookConnect::User.new(8055, @session)
30
+ user.session.should == @session
31
+ end
32
+
33
+
34
+ def user_info_response
35
+ [{"uid"=>"8055", "about_me"=>"This field perpetuates the glorification of the ego. Also, it has a character limit.", "activities"=>"Here: facebook, etc. There: Glee Club, a capella, teaching.", "affiliations"=>[{"nid"=>"50453093", "name"=>"Facebook Developers", "type"=>"work", "status"=>"", "year"=>""}], "birthday"=>"November 3", "books"=>"The Brothers K, GEB, Ken Wilber, Zen and the Art, Fitzgerald, The Emporer's New Mind, The Wonderful Story of Henry Sugar", "current_location"=>{"city"=>"Palo Alto", "state"=>"California", "country"=>"United States", "zip"=>"94303"}, "education_history"=>[{"name"=>"Harvard", "year"=>"2003", "concentrations"=>["Applied Mathematics", "Computer Science"]}], "email_hashes"=>["2781152470_9f9c29692798573d8c76eaaf053a1911"], "family"=>[{"family_elt_elt"=>"1394244902"}, {"family_elt_elt"=>"48703107"}, {"family_elt_elt"=>"1078767258"}, {"family_elt_elt"=>""}], "first_name"=>"Dave", "hometown_location"=>{"city"=>"York", "state"=>"Pennsylvania", "country"=>"United States"}, "hs_info"=>{"hs1_name"=>"Central York High School", "hs2_name"=>{}, "grad_year"=>"1999", "hs1_id"=>"21846", "hs2_id"=>"0"}, "is_app_user"=>"1", "has_added_app"=>"1", "interests"=>"snowboarding, philosophy, soccer, talking to strangers", "last_name"=>"Fetterman", "locale"=>"en_US", "meeting_for"=>["Friendship"], "meeting_sex"=>["female"], "movies"=>"Tommy Boy, Billy Madison, Fight Club, Dirty Work, Meet the Parents, My Blue Heaven, Office Space", "music"=>"New Found Glory, Daft Punk, Weezer, The Crystal Method, Rage, the KLF, Green Day, Live, Coldplay, Panic at the Disco, Family Force 5", "name"=>"Dave Fetterman", "notes_count"=>"0", "pic"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/s8055_39735.jpg", "pic_big"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/n8055_39735.jpg", "pic_small"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/t8055_39735.jpg", "pic_square"=>"http://photos-055.facebook.com/ip007/profile3/1271/65/q8055_39735.jpg", "political"=>"Moderate", "profile_update_time"=>"1170414620", "quotes"=>"", "relationship_status"=>"In a Relationship", "religion"=>"", "sex"=>"male", "significant_other_id"=>nil, "status"=>{"message"=>"Fast Company, November issue, page 84", "time"=>"1193075616"}, "timezone"=>"-8", "tv"=>"cf. Bob Trahan", "wall_count"=>"121", "work_history"=>[{"location"=>{"city"=>"Palo Alto", "state"=>"CA", "country"=>"United States"}, "company_name"=>"Facebook", "position"=>"Software Engineer", "description"=>"Tech Lead, Facebook Platform", "start_date"=>"2006-01", "end_date"=>""}]}]
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ __DIR__ = File.dirname __FILE__
2
+ $LOAD_PATH.unshift __DIR__ + '/../lib'
3
+
4
+ require 'simple_facebook_connect'
5
+ require 'fileutils'
6
+ require 'rubygems'
7
+ Gem::RubyGemsVersion.inspect # without this the activerecord gem doesn't load on ruby 1.9 #WTF
8
+ require 'sqlite3'
9
+ gem 'activerecord'
10
+ require 'activerecord'
11
+
12
+
13
+
14
+ FileUtils.rm_rf __DIR__ + '/../test.sqlite3'
15
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => File.join(__DIR__, '..', 'test.sqlite3')
16
+
17
+ # migrate
18
+
19
+ class CreateUsers < ActiveRecord::Migration
20
+ def self.up
21
+ create_table :users do |t|
22
+ t.string :email
23
+ end
24
+ end
25
+ end
26
+
27
+ require __DIR__ + '/../generators/simple_facebook_connect_migration/templates/migration'
28
+
29
+ CreateUsers.up
30
+ AddFacebookConnect.up