mogli 0.0.29 → 0.0.30

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mogli/client.rb CHANGED
@@ -10,7 +10,7 @@ module Mogli
10
10
  include HTTParty
11
11
  include Mogli::Client::Event
12
12
  include Mogli::Client::User
13
-
13
+
14
14
  class ClientException < Exception; end
15
15
  class UnrecognizeableClassError < ClientException; end
16
16
  class QueryParseException < ClientException; end
@@ -19,6 +19,7 @@ module Mogli
19
19
  class OAuthException < ClientException; end
20
20
  # represents case that the facebook limit on posts to a feed has been exceeded
21
21
  class FeedActionRequestLimitExceeded < ClientException; end
22
+ class SessionInvalidatedDueToPasswordChange < ClientException; end
22
23
  class HTTPException < ClientException; end
23
24
 
24
25
  def api_path(path)
@@ -32,7 +33,7 @@ module Mogli
32
33
  def fql_multiquery_path
33
34
  "https://api.facebook.com/method/fql.multiquery"
34
35
  end
35
-
36
+
36
37
  def initialize(access_token = nil,expiration=nil)
37
38
  @access_token = access_token
38
39
  # nil expiration means extended access
@@ -49,7 +50,7 @@ module Mogli
49
50
  post_data = get(authenticator.access_token_url(code)).parsed_response
50
51
  if (response_is_error?(post_data))
51
52
  raise_client_exception(post_data)
52
- end
53
+ end
53
54
  parts = post_data.split("&")
54
55
  hash = {}
55
56
  parts.each do |p| (k,v) = p.split("=")
@@ -61,10 +62,10 @@ module Mogli
61
62
  else
62
63
  expires = nil
63
64
  end
64
-
65
+
65
66
  new(hash["access_token"],expires)
66
67
  end
67
-
68
+
68
69
  def self.raise_client_exception(post_data)
69
70
  raise_error_by_type_and_message(post_data["error"]["type"], post_data["error"]["message"])
70
71
  end
@@ -72,13 +73,15 @@ module Mogli
72
73
  def self.raise_error_by_type_and_message(type, message)
73
74
  if type == 'OAuthException' && message =~ /Feed action request limit reached/
74
75
  raise FeedActionRequestLimitExceeded.new(message)
76
+ elsif type == 'OAuthException' && message =~ /The session has been invalidated because the user has changed the password/
77
+ raise SessionInvalidatedDueToPasswordChange.new(message)
75
78
  elsif Mogli::Client.const_defined?(type)
76
79
  raise Mogli::Client.const_get(type).new(message)
77
80
  else
78
81
  raise ClientException.new("#{type}: #{message}")
79
82
  end
80
83
  end
81
-
84
+
82
85
  def self.response_is_error?(post_data)
83
86
  post_data.kind_of?(Hash) and
84
87
  !post_data["error"].blank?
@@ -87,10 +90,19 @@ module Mogli
87
90
  def self.create_from_session_key(session_key, client_id, secret)
88
91
  authenticator = Mogli::Authenticator.new(client_id, secret, nil)
89
92
  access_data = authenticator.get_access_token_for_session_key(session_key)
90
- new(access_data['access_token'],
91
- Time.now.to_i + access_data['expires'].to_i)
93
+ new(access_token_from_access_data(access_data),expiration_from_access_data(access_data))
94
+ end
95
+
96
+ def self.access_token_from_access_data(access_data)
97
+ return nil if access_data.nil?
98
+ access_data['access_token']
92
99
  end
93
100
 
101
+ def self.expiration_from_access_data(access_data)
102
+ return nil if access_data.nil? or access_data['expires'].nil?
103
+ Time.now.to_i + access_data['expires'].to_i
104
+ end
105
+
94
106
  def self.create_and_authenticate_as_application(client_id, secret)
95
107
  authenticator = Mogli::Authenticator.new(client_id, secret, nil)
96
108
  access_data = authenticator.get_access_token_for_application
@@ -123,7 +135,7 @@ module Mogli
123
135
  data = self.class.post(fql_multiquery_path,:body=>default_params.merge({:queries=>queries.to_json,:format=>"json"}))
124
136
  map_data(data)
125
137
  end
126
-
138
+
127
139
  def get_and_map(path,klass=nil,body_args = {})
128
140
  data = self.class.get(api_path(path),:query=>default_params.merge(body_args))
129
141
  data = data.values if body_args.key?(:ids) && !data.key?('error')
@@ -146,13 +158,12 @@ module Mogli
146
158
 
147
159
  def extract_hash_or_array(hash_or_array,klass)
148
160
  hash_or_array = hash_or_array.parsed_response if hash_or_array.respond_to?(:parsed_response)
149
- return nil if hash_or_array == false || hash_or_array == true
150
-
161
+ return nil if hash_or_array == false
151
162
  return hash_or_array if hash_or_array.nil? or hash_or_array.kind_of?(Array)
152
163
  return extract_fetching_array(hash_or_array,klass) if is_fetching_array?(hash_or_array)
153
164
  return hash_or_array
154
165
  end
155
-
166
+
156
167
  def is_fetching_array?(hash)
157
168
  hash.has_key?("data") and hash["data"].instance_of?(Array)
158
169
  end
@@ -185,7 +196,7 @@ module Mogli
185
196
  end
186
197
  klass_to_create.new(data,self)
187
198
  end
188
-
199
+
189
200
  def capitalize_if_required(string)
190
201
  string.downcase == string ? string.capitalize : string
191
202
  end
@@ -201,12 +212,12 @@ module Mogli
201
212
  end
202
213
 
203
214
  def raise_error_if_necessary(data)
215
+ raise HTTPException if data.respond_to?(:code) and data.code != 200
204
216
  if data.kind_of?(Hash)
205
217
  if data.keys.size == 1 and data["error"]
206
218
  self.class.raise_error_by_type_and_message(data["error"]["type"], data["error"]["message"])
207
219
  end
208
220
  end
209
- raise HTTPException if data.respond_to?(:code) and data.code != 200
210
221
  end
211
222
 
212
223
  def fields_to_serialize
@@ -1,8 +1,9 @@
1
1
  module Mogli
2
2
  class Education < Model
3
3
 
4
- define_properties :start_date, :end_date, :degree
4
+ define_properties :start_date, :end_date, :degree, :with
5
5
 
6
+ hash_populating_accessor :classes, "Page"
6
7
  hash_populating_accessor :school, "Page"
7
8
  hash_populating_accessor :year, "Page"
8
9
  hash_populating_accessor :concentration, "Page"
data/lib/mogli/page.rb CHANGED
@@ -16,9 +16,6 @@ module Mogli
16
16
  # As a like
17
17
  define_properties :created_time
18
18
 
19
- # Number of checkins, not Checkin assocciation.
20
- define_properties :checkins
21
-
22
19
  def client_for_page
23
20
  if access_token.blank?
24
21
  raise MissingAccessToken.new("You can only get a client for this page if an access_token has been provided. i.e. via /me/accounts")
data/lib/mogli/user.rb CHANGED
@@ -4,12 +4,16 @@ module Mogli
4
4
 
5
5
  define_properties :username, :first_name, :last_name, :link, :about, :birthday, :gender,
6
6
  :email, :website, :timezone, :updated_time, :verified, :political, :bio,
7
- :relationship_status, :locale, :religion, :quotes, :third_party_id
7
+ :relationship_status, :locale, :religion, :quotes, :third_party_id,
8
+ :inspirational_people, :sports, :with, :middle_name
8
9
 
9
10
  def self.recognize?(hash)
10
11
  !hash.has_key?("category")
11
12
  end
12
13
 
14
+ hash_populating_accessor :favorite_athletes, "Page"
15
+ hash_populating_accessor :favorite_teams, "Page"
16
+
13
17
  hash_populating_accessor :work, "Work"
14
18
  hash_populating_accessor :education, "Education"
15
19
 
data/lib/mogli/work.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  module Mogli
2
2
  class Work < Model
3
3
 
4
- define_properties :start_date, :end_date, :description
4
+ define_properties :start_date, :end_date, :description, :with, :from
5
5
 
6
+ hash_populating_accessor :projects, "Page"
6
7
  hash_populating_accessor :employer, "Page"
7
8
  hash_populating_accessor :location, "Page"
8
9
  hash_populating_accessor :position, "Page"
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+ describe Mogli::AppClient do
3
+ describe "subscribing" do
4
+ let :client do
5
+ a=Mogli::AppClient.new('key')
6
+ a.application_id="123"
7
+ a
8
+ end
9
+
10
+ it "should allow you to subscribe with callback url, fields and verify token" do
11
+ Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",an_instance_of(Hash))
12
+ client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"])
13
+ end
14
+
15
+ it "should pass the type as the object field" do
16
+ Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:object=>"user")})
17
+ client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"])
18
+ end
19
+
20
+ it "sends the list of fields comma separated" do
21
+ Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:fields=>"username,friends")})
22
+ client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username,friends"])
23
+ end
24
+ it "sends the callback url" do
25
+ Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:callback_url=>"http://localhost:3000")})
26
+ client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"])
27
+ end
28
+
29
+ it "should send the verify token if provided" do
30
+ Mogli::Client.should_receive(:post).with("https://graph.facebook.com/123/subscriptions",{:body=>hash_including(:verify_token=>"TOKEN")})
31
+ client.subscribe_to_model(Mogli::User,:callback_url=>"http://localhost:3000",:fields=>["username"],:verify_token=>"TOKEN")
32
+
33
+ end
34
+
35
+ it "Lists the existing subscriptions" do
36
+ client.should_receive(:get_and_map_url).with("https://graph.facebook.com/123/subscriptions","Subscription")
37
+ client.subscriptions
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,92 @@
1
+ require "spec_helper"
2
+ describe Mogli::Authenticator do
3
+
4
+ let :authenticator do
5
+ Mogli::Authenticator.new("123456","secret","http://example.com/url")
6
+ end
7
+
8
+ it "has the client id" do
9
+ authenticator.client_id.should == "123456"
10
+ end
11
+
12
+ it "has the secret" do
13
+ authenticator.secret.should == "secret"
14
+ end
15
+
16
+ it "has the callback url" do
17
+ authenticator.callback_url.should == "http://example.com/url"
18
+ end
19
+
20
+ it "creates the authorize_url" do
21
+ authenticator.authorize_url.should == "https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl"
22
+ end
23
+
24
+ it "creates the authorize_url with scopes as an array" do
25
+ authenticator.authorize_url(:scope=>[:user_photos,:user_videos,:stream_publish]).should ==
26
+ "https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&scope=user_photos,user_videos,stream_publish"
27
+ end
28
+
29
+ it "creates the access_token_url" do
30
+ authenticator.access_token_url("mycode").should == "https://graph.facebook.com/oauth/access_token?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&client_secret=secret&code=mycode"
31
+ end
32
+
33
+ it "can trade session_keys for access_tokens" do
34
+ response = [{"access_token" => "myaccesstoken", "expires" => 5000},
35
+ {"access_token" => "youraccesstoken", "expires" => 5000}]
36
+ response.stub!(:code).and_return(200)
37
+
38
+ Mogli::Client.should_receive(:post).
39
+ with("https://graph.facebook.com/oauth/exchange_sessions",
40
+ :body => {:type => "client_cred", :client_id => "123456",
41
+ :client_secret => "secret",
42
+ :sessions => "mysessionkey,yoursessionkey"}).
43
+ and_return(response)
44
+
45
+ authenticator.
46
+ get_access_token_for_session_key(["mysessionkey","yoursessionkey"]).
47
+ should == [{"access_token" => "myaccesstoken", "expires" => 5000},
48
+ {"access_token" => "youraccesstoken", "expires" => 5000}]
49
+ end
50
+
51
+ it "can trade one session_key for an access_tokens" do
52
+ response = {"access_token" => "myaccesstoken", "expires" => 5000}
53
+ response.stub!(:code).and_return(200)
54
+ Mogli::Client.should_receive(:post).
55
+ with("https://graph.facebook.com/oauth/exchange_sessions",
56
+ :body => {:type => "client_cred", :client_id => "123456",
57
+ :client_secret => "secret", :sessions => "mysessionkey"}).
58
+ and_return(response)
59
+
60
+ authenticator.
61
+ get_access_token_for_session_key("mysessionkey").
62
+ should == {"access_token" => "myaccesstoken", "expires" => 5000}
63
+ end
64
+
65
+ it "can ask for an application token" do
66
+ response = "access_token=123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo"
67
+ response.stub!(:code).and_return(200)
68
+ Mogli::Client.should_receive(:post).
69
+ with("https://graph.facebook.com/oauth/access_token",
70
+ :body=> {
71
+ :grant_type => 'client_credentials',
72
+ :client_id => "123456",
73
+ :client_secret => "secret"
74
+ }).
75
+ and_return(response)
76
+
77
+
78
+ token = authenticator.get_access_token_for_application
79
+ token.should =="123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo"
80
+ end
81
+
82
+ it "raises an error if not a 200" do
83
+ response = "access_token=123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo"
84
+ response.stub!(:code).and_return(500)
85
+ Mogli::Client.should_receive(:post).and_return(response)
86
+ lambda do
87
+ token = authenticator.get_access_token_for_application
88
+ end.should raise_error(Mogli::Client::HTTPException)
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+ describe Mogli::Client do
3
+ let :client do
4
+ Mogli::Client.new
5
+ end
6
+
7
+ it "fetches an event by id" do
8
+ client.should_receive(:get_and_map).with(118515001510745,Mogli::Event).and_return("event")
9
+ client.event(118515001510745).should == "event"
10
+ end
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+ describe Mogli::Client do
3
+ let :client do
4
+ Mogli::Client.new
5
+ end
6
+
7
+ it "fetches a user by id" do
8
+ client.should_receive(:get_and_map).with(12451752,Mogli::User).and_return("user")
9
+ client.user(12451752).should == "user"
10
+ end
11
+
12
+ end
@@ -0,0 +1,354 @@
1
+ require "spec_helper"
2
+ describe Mogli::Client do
3
+
4
+ let :client do
5
+ Mogli::Client.new
6
+ end
7
+
8
+
9
+ describe "creation" do
10
+ it "allows creating with an access_token" do
11
+ client = Mogli::Client.new("myaccesstoken")
12
+ client.access_token.should == "myaccesstoken"
13
+ end
14
+
15
+ context "when exchanging a session key" do
16
+ it "allows creating" do
17
+ Time.stub!(:now).and_return(1270000000)
18
+ authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
19
+ authenticator.should_receive(:get_access_token_for_session_key).
20
+ with('mysessionkey').
21
+ and_return({'access_token' => 'myaccesstoken',
22
+ 'expires' => 5000})
23
+ Mogli::Authenticator.should_receive(:new).and_return(authenticator)
24
+ client = Mogli::Client.create_from_session_key(
25
+ 'mysessionkey', '123456', 'secret')
26
+ client.access_token.should == 'myaccesstoken'
27
+ client.expiration.should == Time.at(1270005000)
28
+ end
29
+
30
+ it "allows creating and authenticating as an application" do
31
+ authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
32
+ authenticator.should_receive(:get_access_token_for_application).
33
+ and_return("123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo")
34
+ Mogli::Authenticator.should_receive(:new).and_return(authenticator)
35
+ client = Mogli::Client.create_and_authenticate_as_application(
36
+ 'mysessionkey', '123456')
37
+ client.access_token.should == '123456|3SDdfgdfgv0bbEvYjBH5tJtl-dcBdsfgo'
38
+ end
39
+
40
+ it "doesn't bail when the session key is stale" do
41
+ Time.stub!(:now).and_return(1270000000)
42
+ authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
43
+ authenticator.should_receive(:get_access_token_for_session_key).
44
+ with('mysessionkey').
45
+ and_return(nil)
46
+ Mogli::Authenticator.should_receive(:new).and_return(authenticator)
47
+ lambda {
48
+ client = Mogli::Client.create_from_session_key(
49
+ 'mysessionkey', '123456', 'secret')
50
+ }.should_not raise_exception(NoMethodError, /nil/)
51
+ end
52
+
53
+ it "treats missing expiries as nonexpiring access tokens" do
54
+ Time.stub!(:now).and_return(1270000000)
55
+ authenticator = Mogli::Authenticator.new('123456', 'secret', nil)
56
+ authenticator.should_receive(:get_access_token_for_session_key).
57
+ with('mysessionkey').
58
+ and_return({'access_token' => 'myaccesstoken'})
59
+ Mogli::Authenticator.should_receive(:new).and_return(authenticator)
60
+ client = Mogli::Client.create_from_session_key(
61
+ 'mysessionkey', '123456', 'secret')
62
+ client.expiration.should == Time.at(Time.now + 10*365*24*60*60)
63
+ end
64
+ end
65
+
66
+ it "sets the access_token into the default params" do
67
+ client = Mogli::Client.new("myaccesstoken")
68
+ client.default_params.should == {:access_token=>"myaccesstoken"}
69
+ end
70
+
71
+ it "sets the expiration time if provided" do
72
+ old_now=Time.now
73
+ Time.stub!(:now).and_return(old_now)
74
+ client = Mogli::Client.new("myaccesstoken",old_now.to_i)
75
+ client.expiration.to_i.should == old_now.to_i
76
+ end
77
+
78
+ it "should know if the session is expired" do
79
+ client = Mogli::Client.new("myaccesstoken",Time.now - 100)
80
+ client.should be_expired
81
+ end
82
+
83
+ it "allow creation with no access token" do
84
+ client = Mogli::Client.new
85
+ client.access_token.should be_nil
86
+ end
87
+
88
+ it "doesn't include the access_token param when not passed" do
89
+ client = Mogli::Client.new
90
+ client.default_params.should == {}
91
+ end
92
+
93
+
94
+ it "create get an unescaped access token from an authenticator and code" do
95
+ Mogli::Client.should_receive(:get).with("url").and_return(mock("response",:parsed_response=>
96
+ "access_token=114355055262088%7C2.6_s8VD_HRneAq3_tUEHJhA__.3600.1272920400-12451752|udZzWly7ptI7IMgX7KTdzaoDrhU.&expires=4168"))
97
+ client = Mogli::Client.create_from_code_and_authenticator("code",mock("auth",:access_token_url=>"url"))
98
+ client.access_token.should == "114355055262088|2.6_s8VD_HRneAq3_tUEHJhA__.3600.1272920400-12451752|udZzWly7ptI7IMgX7KTdzaoDrhU."
99
+ client.expiration.should_not be_nil
100
+ (client.expiration > Time.now).should be_true
101
+ end
102
+
103
+ it "create set the expiration into the future if there is on param" do
104
+ Mogli::Client.should_receive(:get).with("url").and_return(mock("response",:parsed_response=>"access_token=114355055262088%7C2.6_s8VD_HRneAq3_tUEHJhA__.3600.1272920400-12451752|udZzWly7ptI7IMgX7KTdzaoDrhU."))
105
+ client = Mogli::Client.create_from_code_and_authenticator("code",mock("auth",:access_token_url=>"url"))
106
+ (client.expiration > Time.now+365*24*60*60).should be_true
107
+ end
108
+
109
+ it "create throw exception if facebook returns exception" do
110
+ err_msg = "Some message"
111
+ err_type = "ErrorType"
112
+ response={"error"=>{"type"=>err_type,"message"=>err_msg}}
113
+ mock_response = mock("response",:parsed_response=>response,:is_a? => true,:[] => response["error"])
114
+ Mogli::Client.should_receive(:get).with("url").and_return(mock_response)
115
+ begin
116
+ client = Mogli::Client.create_from_code_and_authenticator("code",mock("auth",:access_token_url=>"url"))
117
+ rescue Exception => e
118
+ e.message.should == "#{err_type}: #{err_msg}"
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+
125
+ describe "Making requests" do
126
+ it "posts with the parameters in the body" do
127
+ Mogli::Client.should_receive(:post).with("https://graph.facebook.com/1/feed",:body=>{:message=>"message",:access_token=>"1234"})
128
+ client = Mogli::Client.new("1234")
129
+ client.post("1/feed","Post",:message=>"message")
130
+ end
131
+
132
+ it "parses errors in the returns of posts" do
133
+ Mogli::Client.should_receive(:post).and_return({"error"=>{"type"=>"OAuthAccessTokenException","message"=>"An access token is required to request this resource."}})
134
+ client = Mogli::Client.new("1234")
135
+ lambda do
136
+ result = client.post("1/feed","Post",:message=>"message")
137
+ end.should raise_error(Mogli::Client::OAuthAccessTokenException, "An access token is required to request this resource.")
138
+ end
139
+
140
+ it "parses http response errors" do
141
+ Mogli::Client.should_receive(:post).and_return(mock("httpresponse",:code=>500))
142
+ client = Mogli::Client.new("1234")
143
+ lambda do
144
+ result = client.post("1/feed","Post",:message=>"message")
145
+ end.should raise_error(Mogli::Client::HTTPException)
146
+
147
+ end
148
+
149
+ it "creates objects of the returned type" do
150
+ Mogli::Client.should_receive(:post).and_return({:id=>123434})
151
+ client = Mogli::Client.new("1234")
152
+ result = client.post("1/feed","Post",:message=>"message")
153
+ result.should == Mogli::Post.new(:id=>123434)
154
+ end
155
+
156
+ it "creates object in a way that ignore invalid properties" do
157
+ Mogli::Client.stub!(:post).and_return({:id=>123434,:completely_invalid_property=>1})
158
+ client = Mogli::Client.new("1234")
159
+ lambda do
160
+ result = client.post("1/feed","Post",:message=>"message")
161
+ end.should_not raise_error
162
+ end
163
+
164
+ it "raises specific exception if Facebook-imposed posting limit exceeded for feed" do
165
+ error_message = "Feed action request limit reached"
166
+ Mogli::Client.should_receive(:post).and_return({"error"=>{"type"=>"OAuthException","message"=>error_message}})
167
+ client = Mogli::Client.new("1234")
168
+ lambda do
169
+ result = client.post("1/feed","Post",:message=>"message")
170
+ end.should raise_error(Mogli::Client::FeedActionRequestLimitExceeded, error_message)
171
+ end
172
+
173
+ end
174
+
175
+ it "allows deleting" do
176
+ Mogli::Client.should_receive(:delete).with("https://graph.facebook.com/1",:query=>{:access_token=>"1234"})
177
+ client = Mogli::Client.new("1234")
178
+ client.delete("1")
179
+ end
180
+
181
+
182
+ describe "fql queries" do
183
+ it "defaults to json" do
184
+ Mogli::Client.should_receive(:post).with("https://api.facebook.com/method/fql.query",:body=>{:query=>"query",:format=>"json",:access_token=>"1234"})
185
+ client = Mogli::Client.new("1234")
186
+ client.fql_query("query")
187
+ end
188
+
189
+ it "supports xml" do
190
+ Mogli::Client.should_receive(:post).with("https://api.facebook.com/method/fql.query",:body=>{:query=>"query",:format=>"xml",:access_token=>"1234"})
191
+ client = Mogli::Client.new("1234")
192
+ client.fql_query("query",nil,"xml")
193
+ end
194
+
195
+ it "creates objects if given a class" do
196
+ Mogli::Client.should_receive(:post).and_return({"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" })
197
+ client = Mogli::Client.new("1234")
198
+ client.fql_query("query","user").should be_an_instance_of(Mogli::User)
199
+ end
200
+
201
+ it "returns a hash if no class is given" do
202
+ Mogli::Client.should_receive(:post).and_return({"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" })
203
+ client = Mogli::Client.new("1234")
204
+ client.fql_query("query").should be_an_instance_of(Hash)
205
+ end
206
+
207
+ it "doesn't create objects if the format is xml" do
208
+ Mogli::Client.should_receive(:post).and_return({"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" })
209
+ client = Mogli::Client.new("1234")
210
+ client.fql_query("query","user","xml").should be_an_instance_of(Hash)
211
+ end
212
+ end
213
+
214
+ describe "fql multiqueries" do
215
+ it "defaults to json" do
216
+ fql = {"query1"=>"select uid from user"}
217
+ Mogli::Client.should_receive(:post).with("https://api.facebook.com/method/fql.multiquery",:body=>{:format=>"json",:queries=>fql.to_json,:access_token=>"1234"})
218
+ client = Mogli::Client.new("1234")
219
+ client.fql_multiquery(fql)
220
+ end
221
+
222
+ it "returns hash with query names and values matching desired classes" do
223
+ Mogli::Client.should_receive(:post).and_return([
224
+ {"name"=>"users", "fql_result_set"=>[{"uid"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino"}]},
225
+ {"name"=>"comment_user","fql_result_set"=>[{"fromid"=>12451752}]}
226
+ ])
227
+
228
+ f = Mogli::FqlMultiquery.new(Mogli::Client.new("1234"))
229
+ f.add_named_query_for_class("comment_user", "SELECT fromid FROM comment WHERE post_id = 123343434", Mogli::Comment)
230
+ f.add_named_query_for_class("users", "SELECT uid, first_name, last_name FROM user WHERE uid IN (SELECT fromid FROM #comment_user)", Mogli::User)
231
+ results = f.results
232
+ results["users"].first.class.should == Mogli::User
233
+ results["comment_user"].first.class.should == Mogli::Comment
234
+ end
235
+ end
236
+
237
+ describe "result mapping" do
238
+
239
+ let :user_data do
240
+ {"id"=>12451752, "first_name"=>"Mike", "last_name"=>"Mangino" }
241
+ end
242
+
243
+ it "returns the raw value with no class specified" do
244
+ client.map_data(user_data).should be_an_instance_of(Hash)
245
+ end
246
+
247
+ it "returns nil if we get a false response" do
248
+ client.map_data(false,Mogli::User).should be_false
249
+ end
250
+ it "returns false if we get a false response" do
251
+ client.map_to_class(false,Mogli::User).should be_false
252
+ end
253
+
254
+
255
+ it "returns the array if no class is specified and there is only a data parameter" do
256
+ client.map_data({"data"=>[user_data,user_data]}).should be_kind_of(Array)
257
+ end
258
+
259
+ it "creates an instance of the class when specified" do
260
+ user = client.map_data(user_data,Mogli::User)
261
+ user.should be_an_instance_of(Mogli::User)
262
+ user.id.should == 12451752
263
+ end
264
+
265
+ it "creates an array of instances when the data is an array" do
266
+ users = client.map_data([user_data,user_data],Mogli::User)
267
+ users.should be_an_instance_of(Array)
268
+ users.each {|i| i.should be_an_instance_of(Mogli::User) }
269
+ users.size.should == 2
270
+ end
271
+
272
+ it "creates an array of instances when the data is just a hash with a single data parameter" do
273
+ users = client.map_data({"data"=>[user_data,user_data],"paging"=>{}},Mogli::User)
274
+ users.should be_kind_of(Array)
275
+ users.each {|i| i.should be_an_instance_of(Mogli::User) }
276
+ users.size.should == 2
277
+ end
278
+
279
+ it "create an instance of fetching array when there is a data element" do
280
+ users = client.map_data({"data"=>[user_data,user_data],"paging"=>{}},Mogli::User)
281
+ users.should be_an_instance_of(Mogli::FetchingArray)
282
+ end
283
+
284
+ it "sets the client on the array" do
285
+ users = client.map_data({"data"=>[user_data,user_data],"paging"=>{}},Mogli::User)
286
+ users.client.should == client
287
+ end
288
+
289
+ it "sets the next url on the array" do
290
+ users = client.map_data({"data"=>[user_data,user_data],"paging"=>{"next"=>"next"}},Mogli::User)
291
+ users.next_url.should == "next"
292
+ end
293
+
294
+ it "sets the previous url on the array" do
295
+ users = client.map_data({"data"=>[user_data,user_data],"paging"=>{"previous"=>"prev"}},Mogli::User)
296
+ users.previous_url.should == "prev"
297
+ end
298
+
299
+ it "sets the classes on the array" do
300
+ users = client.map_data({"data"=>[user_data,user_data],"paging"=>{"previous"=>"prev"}},Mogli::User)
301
+ users.classes.should == [Mogli::User]
302
+ end
303
+
304
+ it "sets the client in the newly created instance" do
305
+ user = client.map_data(user_data,Mogli::User)
306
+ user.client.should == client
307
+ end
308
+
309
+ it "returns nil if Facebook says false" do
310
+ Mogli::Client.should_receive(:get).and_return(false)
311
+ client.get_and_map(148800401968,"User").should be_nil
312
+ end
313
+
314
+ it "raises an exception with specific message when there is just an error" do
315
+ lambda do
316
+ client.map_data({"error"=>{"type"=>"OAuthAccessTokenException","message"=>"An access token is required to request this resource."}})
317
+ end.should raise_error(Mogli::Client::OAuthAccessTokenException, "An access token is required to request this resource.")
318
+ end
319
+
320
+ it "raises a OAuthException when a token is invalidated by a user logging out of Facebook" do
321
+ lambda do
322
+ client.map_data({"error"=>{"type"=>"OAuthException","message"=>"Error validating access token."}})
323
+ end.should raise_error(Mogli::Client::OAuthException, "Error validating access token.")
324
+ end
325
+
326
+ it "raises a generic ClientException when the exception type is not recorgnized" do
327
+ lambda do
328
+ client.map_data({"error"=>{"type"=>"Foo","message"=>"Lorem ipsum."}})
329
+ end.should raise_error(Mogli::Client::ClientException, "Foo: Lorem ipsum.")
330
+ end
331
+
332
+ describe "Instance creation" do
333
+ it "will find the class in the Mogli namespace if given a string" do
334
+ client.create_instance("User",{:id=>1}).should be_an_instance_of(Mogli::User)
335
+ end
336
+
337
+ it "will NOT use the value from the type field if it exists" do
338
+ client.create_instance("Post",{:id=>1,"type"=>"status"}).should be_an_instance_of(Mogli::Post)
339
+ end
340
+
341
+ it "call the recognize method on each class passing the data and will use the one that recognizes it" do
342
+ Mogli::User.should_receive(:recognize?).with(:id=>1).and_return(false)
343
+ Mogli::Post.should_receive(:recognize?).with(:id=>1).and_return(true)
344
+ client.create_instance(["User","Post"],{:id=>1}).should be_an_instance_of(Mogli::Post)
345
+ end
346
+
347
+ it "will use the first class if none are recognized" do
348
+ Mogli::Page.should_receive(:recognize?).with(:id=>1).and_return(false)
349
+ client.create_instance(["Page"],{:id=>1}).should be_an_instance_of(Mogli::Page)
350
+ end
351
+ end
352
+ end
353
+
354
+ end