mogli 0.0.29 → 0.0.30

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/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