koala 1.2.0beta3 → 1.2.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/CHANGELOG CHANGED
@@ -4,10 +4,16 @@ New methods:
4
4
  -- Old classes are aliased with deprecation warnings (non-breaking change)
5
5
  -- TestUsers#update lets you update the name or password of an existing test user
6
6
  -- API.get_page_access_token lets you easily fetch the access token for a page you manage (thanks, marcgg!)
7
+ -- Added version.rb (Koala::VERSION)
7
8
  Updated methods:
8
9
  -- OAuth now parses Facebook's new signed cookie format
9
10
  -- API.put_picture now accepts URLs to images (thanks, marcgg!)
10
11
  -- Bug fixes to put_picture, parse_signed_request, and the test suite (thanks, johnbhall and Will S.!)
12
+ -- Smarter GraphCollection use
13
+ -- Any pageable result will now become a GraphCollection
14
+ -- Non-pageable results from get_connections no longer error
15
+ -- GraphCollection.raw_results allows access to original result data
16
+ -- Koala no longer enforces any limits on the number of test users you create at once
11
17
  Internal improvements:
12
18
  -- Koala now uses Faraday to make requests, replacing the HTTPServices (see wiki)
13
19
  -- Koala::HTTPService.http_options allows specification of default Faraday connection options
@@ -17,6 +23,7 @@ Internal improvements:
17
23
  -- Koala no longer automatically switches to Net::HTTP when uploading IO objects to Facebook
18
24
  -- RealTimeUpdates and TestUsers are no longer subclasses of API, but have their own .api objects
19
25
  -- The old .graph_api accessor is aliases to .api with a deprecation warning
26
+ -- Removed deprecation warnings for pre-1.1 batch interface
20
27
  Testing improvements:
21
28
  -- Live test suites now run against test users by default
22
29
  -- Test suite can be repeatedly run live without having to update facebook_data.yml
@@ -24,7 +31,7 @@ Testing improvements:
24
31
  -- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the rspec command
25
32
  -- Live tests can be run against the beta server by specifying BETA=true in the rspec command
26
33
  -- Tests now pass against all rubies on Travis CI
27
- -- Expanded test coverage
34
+ -- Expanded and refactored test coverage
28
35
  -- Fixed bug with YAML parsing in Ruby 1.9
29
36
 
30
37
  v1.1
data/koala.gemspec CHANGED
@@ -1,9 +1,11 @@
1
1
  # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+ require 'koala/version'
2
4
 
3
5
  Gem::Specification.new do |s|
4
6
  s.name = %q{koala}
5
- s.version = "1.2.0beta3"
6
- s.date = %q{2011-09-12}
7
+ s.version = Koala::VERSION
8
+ s.date = %q{2011-09-27}
7
9
 
8
10
  s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication.}
9
11
  s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph and REST APIs, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
@@ -71,9 +71,7 @@ module Koala
71
71
 
72
72
  def get_connections(id, connection_name, args = {}, options = {})
73
73
  # Fetchs the connections for given object.
74
- graph_call("#{id}/#{connection_name}", args, "get", options) do |result|
75
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
76
- end
74
+ graph_call("#{id}/#{connection_name}", args, "get", options)
77
75
  end
78
76
 
79
77
  def put_connections(id, connection_name, args = {}, options = {})
@@ -174,9 +172,7 @@ module Koala
174
172
 
175
173
  def search(search_terms, args = {}, options = {})
176
174
  args.merge!({:q => search_terms}) unless search_terms.nil?
177
- graph_call("search", args, "get", options) do |result|
178
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
179
- end
175
+ graph_call("search", args, "get", options)
180
176
  end
181
177
 
182
178
  # Convenience Methods
@@ -199,14 +195,12 @@ module Koala
199
195
  def get_page(params)
200
196
  # Pages through a set of results stored in a GraphCollection
201
197
  # Used for connections and search results
202
- graph_call(*params) do |result|
203
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
204
- end
198
+ graph_call(*params)
205
199
  end
206
200
 
207
201
  # Batch API
208
202
  def batch(http_options = {}, &block)
209
- batch_client = GraphBatchAPI.new(access_token)
203
+ batch_client = GraphBatchAPI.new(access_token, self)
210
204
  if block
211
205
  yield batch_client
212
206
  batch_client.execute(http_options)
@@ -214,15 +208,7 @@ module Koala
214
208
  batch_client
215
209
  end
216
210
  end
217
-
218
- def self.included(base)
219
- base.class_eval do
220
- def self.batch
221
- raise NoMethodError, "The BatchAPI signature has changed (the original implementation was not thread-safe). Please see https://github.com/arsduo/koala/wiki/Batch-requests. (This message will be removed in the final 1.1 release.)"
222
- end
223
- end
224
- end
225
-
211
+
226
212
  # Direct access to the Facebook API
227
213
  # see any of the above methods for example invocations
228
214
  def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
@@ -231,10 +217,15 @@ module Koala
231
217
  raise error if error
232
218
  end
233
219
 
234
- # now process as appropriate (get picture header, make GraphCollection, etc.)
220
+ # turn this into a GraphCollection if it's pageable
221
+ result = GraphCollection.evaluate(result, self)
222
+
223
+ # now process as appropriate for the given call (get picture header, etc.)
235
224
  post_processing ? post_processing.call(result) : result
236
225
  end
237
226
 
227
+ private
228
+
238
229
  def check_response(response)
239
230
  # check for Graph API-specific errors
240
231
  # this returns an error, which is immediately raised (non-batch)
@@ -244,8 +235,6 @@ module Koala
244
235
  end
245
236
  end
246
237
 
247
- private
248
-
249
238
  def parse_media_args(media_args, method)
250
239
  # photo and video uploads can accept different types of arguments (see above)
251
240
  # so here, we parse the arguments into a form directly usable in put_object
@@ -4,6 +4,13 @@ module Koala
4
4
 
5
5
  def self.included(base)
6
6
  base.class_eval do
7
+ attr_reader :original_api
8
+
9
+ def initialize(access_token, api)
10
+ super(access_token)
11
+ @original_api = api
12
+ end
13
+
7
14
  alias_method :graph_call_outside_batch, :graph_call
8
15
  alias_method :graph_call, :graph_call_in_batch
9
16
 
@@ -46,7 +53,7 @@ module Koala
46
53
  batch_op.to_batch_params(access_token)
47
54
  })
48
55
 
49
- graph_call_outside_batch('/', args, 'post', http_options) do |response|
56
+ batch_result = graph_call_outside_batch('/', args, 'post', http_options) do |response|
50
57
  # map the results with post-processing included
51
58
  index = 0 # keep compat with ruby 1.8 - no with_index for map
52
59
  response.map do |call_result|
@@ -80,6 +87,9 @@ module Koala
80
87
  end
81
88
  end
82
89
  end
90
+
91
+ # turn any results that are pageable into GraphCollections
92
+ batch_result.inject([]) {|processed_results, raw| processed_results << GraphCollection.evaluate(raw, @original_api)}
83
93
  end
84
94
 
85
95
  end
@@ -10,12 +10,17 @@ module Koala
10
10
  # It also allows access to paging information and the
11
11
  # ability to get the next/previous page in the collection
12
12
  # by calling next_page or previous_page.
13
- attr_reader :paging
14
- attr_reader :api
13
+ attr_reader :paging, :api, :raw_response
15
14
 
15
+ def self.evaluate(response, api)
16
+ # turn the response into a GraphCollection if it's pageable; if not, return the original response
17
+ response.is_a?(Hash) && response["data"].is_a?(Array) ? self.new(response, api) : response
18
+ end
19
+
16
20
  def initialize(response, api)
17
21
  super response["data"]
18
22
  @paging = response["paging"]
23
+ @raw_response = response
19
24
  @api = api
20
25
  end
21
26
 
@@ -71,7 +71,7 @@ module Koala
71
71
  end
72
72
 
73
73
  def list_subscriptions
74
- @graph_api.graph_call(subscription_path)["data"]
74
+ @graph_api.graph_call(subscription_path)
75
75
  end
76
76
 
77
77
  def graph_api
@@ -35,7 +35,7 @@ module Koala
35
35
  end
36
36
 
37
37
  def list
38
- @api.graph_call(accounts_path)["data"]
38
+ @api.graph_call(accounts_path)
39
39
  end
40
40
 
41
41
  def delete(test_user)
@@ -72,7 +72,6 @@ module Koala
72
72
  end
73
73
 
74
74
  def create_network(network_size, installed = true, permissions = '')
75
- network_size = 50 if network_size > 50 # FB's max is 50
76
75
  users = (0...network_size).collect { create(installed, permissions) }
77
76
  friends = users.clone
78
77
  users.each do |user|
@@ -0,0 +1,3 @@
1
+ module Koala
2
+ VERSION = "1.2.0"
3
+ end
data/lib/koala.rb CHANGED
@@ -8,7 +8,6 @@ require 'openssl'
8
8
  require 'base64'
9
9
 
10
10
  # include koala modules
11
- require 'koala/http_service'
12
11
  require 'koala/oauth'
13
12
  require 'koala/graph_api'
14
13
  require 'koala/graph_batch_api'
@@ -17,11 +16,17 @@ require 'koala/graph_collection'
17
16
  require 'koala/rest_api'
18
17
  require 'koala/realtime_updates'
19
18
  require 'koala/test_users'
20
- require 'koala/utils'
19
+
20
+ # HTTP module so we can communicate with Facebook
21
+ require 'koala/http_service'
21
22
 
22
23
  # add KoalaIO class
23
24
  require 'koala/uploadable_io'
24
25
 
26
+ # miscellaneous
27
+ require 'koala/utils'
28
+ require 'koala/version'
29
+
25
30
  module Koala
26
31
 
27
32
  module Facebook
data/readme.md CHANGED
@@ -9,32 +9,41 @@ Koala
9
9
  * Flexible: Koala should be useful to everyone, regardless of their current configuration. (We support JRuby, Rubinius, and REE as well as vanilla Ruby, and use the Faraday library to provide complete flexibility over how HTTP requests are made.)
10
10
  * Tested: Koala should have complete test coverage, so you can rely on it. (Our test coverage is complete and can be run against either mocked responses or the live Facebook servers.)
11
11
 
12
+ Facebook Changes on October 1, 2011
13
+ ---
14
+
15
+ **Koala 1.2 supports all of Facebook's new authentication schemes**, which will be introduced on October 1, 2011; the old Javascript library and older authentication schemes will be deprecated at the same time.
16
+
17
+ To test your application, upgrade to the latest version of Koala (see below) and configure your application according to Facebook's [OAuth 2.0 and HTTPS Migration](https://developers.facebook.com/docs/oauth2-https-migration/) guide. If you have the appropriate calls to get_user_info_from_cookies (apps using the Javascript SDK) and/or parse_signed_params (for Canvas and tab apps), your application should work without a hitch.
18
+
19
+ _Note_: in their new secure cookie format, Facebook provides an OAuth code, which Koala automatically exchanges for an access token. Because this involves a call to Facebook's servers, you should consider storing the user's access token in their session and only calling get_user_info_from_cookies when necessary (access_token not present, you discover it's expired, etc.). Otherwise, you'll be calling out to Facebook each time the user loads a page, slowing down your site. (As we figure out best practices for this, we'll update the wiki.)
20
+
12
21
  Installation
13
22
  ---
14
23
 
15
24
  Easy:
16
25
 
17
- [sudo|rvm] gem install koala --pre # for 1.2 beta
18
- [sudo|rvm] gem install koala # for 1.1
26
+ [sudo|rvm] gem install koala
19
27
 
20
28
  Or in Bundler:
21
29
 
22
- gem "koala", "~> 1.2.0beta"
23
- gem "koala" # for 1.1
30
+ gem "koala"
24
31
 
25
32
  Graph API
26
33
  ----
27
34
  The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
28
35
 
29
- # 1.2beta and above
30
36
  @graph = Koala::Facebook::API.new(oauth_access_token)
31
- # 1.1 or earlier
32
- @graph = Koala::Facebook::GraphAPI.new(oauth_access_token)
37
+ # in 1.1 or earlier, use GraphAPI instead of API
33
38
 
34
39
  profile = @graph.get_object("me")
35
40
  friends = @graph.get_connections("me", "friends")
36
41
  @graph.put_object("me", "feed", :message => "I am writing on my wall!")
37
42
 
43
+ # you can even use the new Timeline API
44
+ # see https://developers.facebook.com/docs/beta/opengraph/tutorial/
45
+ @graph.put_connections("me", "namespace:action", :object => object_url)
46
+
38
47
  The response of most requests is the JSON data returned from the Facebook servers as a Hash.
39
48
 
40
49
  When retrieving data that returns an array of results (for example, when calling API#get_connections or API#search) a GraphCollection object will be returned, which makes it easy to page through the results:
@@ -65,10 +74,8 @@ Where the Graph API and the old REST API overlap, you should choose the Graph AP
65
74
 
66
75
  Fortunately, Koala supports the REST API using the very same interface; to use this, instantiate an API:
67
76
 
68
- # 1.2beta and above
69
77
  @rest = Koala::Facebook::API.new(oauth_access_token)
70
- # 1.1 or earlier
71
- @rest = Koala::Facebook::RestAPI.new(oauth_access_token)
78
+ # in 1.1 or earlier, use RestAPI instead of API
72
79
 
73
80
  @rest.fql_query(my_fql_query) # convenience method
74
81
  @rest.fql_multiquery(fql_query_hash) # convenience method
@@ -76,10 +83,8 @@ Fortunately, Koala supports the REST API using the very same interface; to use t
76
83
 
77
84
  Of course, you can use the Graph API methods on the same object -- the power of two APIs right in the palm of your hand.
78
85
 
79
- # 1.2beta and above
80
86
  @api = Koala::Facebook::API.new(oauth_access_token)
81
- # 1.1 or earlier
82
- @api = Koala::Facebook::GraphAndRestAPI.new(oauth_access_token)
87
+ # in 1.1 or earlier, use GraphAndRestAPI instead of API
83
88
 
84
89
  @api = Koala::Facebook::API.new(oauth_access_token)
85
90
  fql = @api.fql_query(my_fql_query)
@@ -105,7 +110,7 @@ You can also get your application's own access token, which can be used without
105
110
  @oauth.get_app_access_token
106
111
 
107
112
  For those building apps on Facebook, parsing signed requests is simple:
108
- @oauth.parse_signed_request(request)
113
+ @oauth.parse_signed_request(signed_request_string)
109
114
 
110
115
  Or, if for some horrible reason, you're still using session keys, despair not! It's easy to turn them into shiny, modern OAuth tokens:
111
116
  @oauth.get_token_from_session_key(session_key)
@@ -171,5 +176,7 @@ You can also run live tests against Facebook's servers:
171
176
 
172
177
  # Again from anywhere in the project directory:
173
178
  LIVE=true bundle exec rake spec
179
+ # you can also test against Facebook's beta tier
180
+ LIVE=true BETA=true bundle exec rake spec
174
181
 
175
182
  By default, the live tests are run against test users, so you can run them as frequently as you want. If you want to run them against a real user, however, you can fill in the OAuth token, code, and access\_token values in spec/fixtures/facebook_data.yml. See the wiki for more details.
@@ -98,4 +98,29 @@ describe "Koala::Facebook::API" do
98
98
  end
99
99
  end
100
100
 
101
+ describe "with an access token" do
102
+ before(:each) do
103
+ @api = Koala::Facebook::API.new(@token)
104
+ end
105
+
106
+ it_should_behave_like "Koala RestAPI"
107
+ it_should_behave_like "Koala RestAPI with an access token"
108
+
109
+ it_should_behave_like "Koala GraphAPI"
110
+ it_should_behave_like "Koala GraphAPI with an access token"
111
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
112
+ end
113
+
114
+ describe "without an access token" do
115
+ before(:each) do
116
+ @api = Koala::Facebook::API.new
117
+ end
118
+
119
+ it_should_behave_like "Koala RestAPI"
120
+ it_should_behave_like "Koala RestAPI without an access token"
121
+
122
+ it_should_behave_like "Koala GraphAPI"
123
+ it_should_behave_like "Koala GraphAPI without an access token"
124
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
125
+ end
101
126
  end
@@ -19,30 +19,4 @@ describe "Koala::Facebook::GraphAndRestAPI" do
19
19
  api = Koala::Facebook::GraphAndRestAPI.new("token")
20
20
  end
21
21
  end
22
-
23
- describe "with an access token" do
24
- before(:each) do
25
- @api = Koala::Facebook::API.new(@token)
26
- end
27
-
28
- it_should_behave_like "Koala RestAPI"
29
- it_should_behave_like "Koala RestAPI with an access token"
30
-
31
- it_should_behave_like "Koala GraphAPI"
32
- it_should_behave_like "Koala GraphAPI with an access token"
33
- it_should_behave_like "Koala GraphAPI with GraphCollection"
34
- end
35
-
36
- describe "without an access token" do
37
- before(:each) do
38
- @api = Koala::Facebook::API.new
39
- end
40
-
41
- it_should_behave_like "Koala RestAPI"
42
- it_should_behave_like "Koala RestAPI without an access token"
43
-
44
- it_should_behave_like "Koala GraphAPI"
45
- it_should_behave_like "Koala GraphAPI without an access token"
46
- it_should_behave_like "Koala GraphAPI with GraphCollection"
47
- end
48
22
  end
@@ -430,7 +430,7 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
430
430
  end
431
431
 
432
432
  describe "usage tests" do
433
- it "can get two results at once" do
433
+ it "gets two results at once" do
434
434
  me, koppel = @api.batch do |batch_api|
435
435
  batch_api.get_object('me')
436
436
  batch_api.get_object(KoalaTest.user1)
@@ -439,7 +439,15 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
439
439
  koppel['id'].should_not be_nil
440
440
  end
441
441
 
442
- it 'should be able to make mixed calls inside of a batch' do
442
+ it 'makes mixed calls inside of a batch' do
443
+ me, friends = @api.batch do |batch_api|
444
+ batch_api.get_object('me')
445
+ batch_api.get_connections('me', 'friends')
446
+ end
447
+ friends.should be_a(Koala::Facebook::GraphCollection)
448
+ end
449
+
450
+ it 'turns pageable results into GraphCollections' do
443
451
  me, friends = @api.batch do |batch_api|
444
452
  batch_api.get_object('me')
445
453
  batch_api.get_connections('me', 'friends')
@@ -448,14 +456,14 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
448
456
  friends.should be_an(Array)
449
457
  end
450
458
 
451
- it 'should be able to make a get_picture call inside of a batch' do
459
+ it 'makes a get_picture call inside of a batch' do
452
460
  pictures = @api.batch do |batch_api|
453
461
  batch_api.get_picture('me')
454
462
  end
455
463
  pictures.first.should_not be_empty
456
464
  end
457
465
 
458
- it "should handle requests for two different tokens" do
466
+ it "handles requests for two different tokens" do
459
467
  me, insights = @api.batch do |batch_api|
460
468
  batch_api.get_object('me')
461
469
  batch_api.get_connections(@app_id, 'insights', {}, {"access_token" => @app_api.access_token})
@@ -571,30 +579,4 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
571
579
  end
572
580
  end
573
581
  end
574
-
575
- describe "new interface" do
576
- it "includes a deprecation warning on GraphAPI" do
577
- begin
578
- Koala::Facebook::GraphAPI.batch do
579
- end
580
- rescue NoMethodError => @err
581
- end
582
-
583
- # verify the message points people to the wiki page
584
- @err.should
585
- @err.message.should =~ /https\:\/\/github.com\/arsduo\/koala\/wiki\/Batch-requests/
586
- end
587
-
588
- it "includes a deprecation warning on GraphAndRESTAPI" do
589
- begin
590
- Koala::Facebook::GraphAndRestAPI.batch do
591
- end
592
- rescue NoMethodError => @err
593
- end
594
-
595
- # verify the message points people to the wiki page
596
- @err.should
597
- @err.message.should =~ /https\:\/\/github.com\/arsduo\/koala\/wiki\/Batch-requests/
598
- end
599
- end
600
582
  end
@@ -19,24 +19,4 @@ describe "Koala::Facebook::GraphAPI" do
19
19
  api = Koala::Facebook::GraphAPI.new("token")
20
20
  end
21
21
  end
22
-
23
- context "with an access token" do
24
- before :each do
25
- @api = Koala::Facebook::API.new(@token)
26
- end
27
-
28
- it_should_behave_like "Koala GraphAPI"
29
- it_should_behave_like "Koala GraphAPI with an access token"
30
- it_should_behave_like "Koala GraphAPI with GraphCollection"
31
- end
32
-
33
- context "without an access token" do
34
- before :each do
35
- @api = Koala::Facebook::API.new
36
- end
37
-
38
- it_should_behave_like "Koala GraphAPI"
39
- it_should_behave_like "Koala GraphAPI without an access token"
40
- it_should_behave_like "Koala GraphAPI with GraphCollection"
41
- end
42
22
  end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ describe Koala::Facebook::GraphCollection do
4
+ before(:each) do
5
+ @result = {
6
+ "data" => [1, 2, :three],
7
+ "paging" => {:paging => true}
8
+ }
9
+ @api = Koala::Facebook::API.new("123")
10
+ @collection = Koala::Facebook::GraphCollection.new(@result, @api)
11
+ end
12
+
13
+ it "subclasses Array" do
14
+ Koala::Facebook::GraphCollection.ancestors.should include(Array)
15
+ end
16
+
17
+ it "creates an array-like object" do
18
+ Koala::Facebook::GraphCollection.new(@result, @api).should be_an(Array)
19
+ end
20
+
21
+ it "contains the result data" do
22
+ @result["data"].each_with_index {|r, i| @collection[i].should == r}
23
+ end
24
+
25
+ it "has a read-only paging attribute" do
26
+ @collection.methods.map(&:to_sym).should include(:paging)
27
+ @collection.methods.map(&:to_sym).should_not include(:paging=)
28
+ end
29
+
30
+ it "sets paging to results['paging']" do
31
+ @collection.paging.should == @result["paging"]
32
+ end
33
+
34
+ it "sets raw_response to the original results" do
35
+ @collection.raw_response.should == @result
36
+ end
37
+
38
+ it "sets the API to the provided API" do
39
+ @collection.api.should == @api
40
+ end
41
+
42
+ describe "when getting a whole page" do
43
+ before(:each) do
44
+ @second_page = {
45
+ "data" => [:second, :page, :data],
46
+ "paging" => {}
47
+ }
48
+ @base = stub("base")
49
+ @args = stub("args")
50
+ @page_of_results = stub("page of results")
51
+ end
52
+
53
+ it "should return the previous page of results" do
54
+ @collection.should_receive(:previous_page_params).and_return([@base, @args])
55
+ @api.should_receive(:api).with(@base, @args, anything, anything).and_return(@second_page)
56
+ Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
57
+ @collection.previous_page.should == @page_of_results
58
+ end
59
+
60
+ it "should return the next page of results" do
61
+ @collection.should_receive(:next_page_params).and_return([@base, @args])
62
+ @api.should_receive(:api).with(@base, @args, anything, anything).and_return(@second_page)
63
+ Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
64
+
65
+ @collection.next_page.should == @page_of_results
66
+ end
67
+
68
+ it "should return nil it there are no other pages" do
69
+ %w{next previous}.each do |this|
70
+ @collection.should_receive("#{this}_page_params".to_sym).and_return(nil)
71
+ @collection.send("#{this}_page").should == nil
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "when parsing page paramters" do
77
+ it "should return the base as the first array entry" do
78
+ base = "url_path"
79
+ @collection.parse_page_url("anything.com/#{base}?anything").first.should == base
80
+ end
81
+
82
+ it "should return the arguments as a hash as the last array entry" do
83
+ args_hash = {"one" => "val_one", "two" => "val_two"}
84
+ @collection.parse_page_url("anything.com/anything?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}").last.should == args_hash
85
+ end
86
+ end
87
+
88
+ describe "#evaluate" do
89
+ it "returns the original result if it's provided a non-hash result" do
90
+ result = []
91
+ Koala::Facebook::GraphCollection.evaluate(result, @api).should == result
92
+ end
93
+
94
+ it "returns the original result if it's provided a nil result" do
95
+ result = nil
96
+ Koala::Facebook::GraphCollection.evaluate(result, @api).should == result
97
+ end
98
+
99
+ it "returns the original result if the result doesn't have a data key" do
100
+ result = {"paging" => {}}
101
+ Koala::Facebook::GraphCollection.evaluate(result, @api).should == result
102
+ end
103
+
104
+ it "returns the original result if the result's data key isn't an array" do
105
+ result = {"data" => {}, "paging" => {}}
106
+ Koala::Facebook::GraphCollection.evaluate(result, @api).should == result
107
+ end
108
+
109
+ it "returns a new GraphCollection of the result if it has an array data key and a paging key" do
110
+ result = {"data" => [], "paging" => {}}
111
+ expected = :foo
112
+ Koala::Facebook::GraphCollection.should_receive(:new).with(result, @api).and_return(expected)
113
+ Koala::Facebook::GraphCollection.evaluate(result, @api).should == expected
114
+ end
115
+ end
116
+ end
@@ -1,6 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Koala" do
4
+ it "has a version" do
5
+ Koala.const_defined?("VERSION").should be_true
6
+ end
7
+
4
8
  it "has an http_service accessor" do
5
9
  Koala.should respond_to(:http_service)
6
10
  Koala.should respond_to(:http_service=)
@@ -13,6 +13,13 @@ describe "Koala::Facebook::TestUsers" do
13
13
  raise Exception, "Must supply OAuth app id, secret, app_access_token, and callback to run live subscription tests!"
14
14
  end
15
15
  end
16
+
17
+ after :each do
18
+ # clean up any test users
19
+ ((@network || []) + [@user1, @user2]).each do |u|
20
+ puts "Unable to delete test user #{u.inspect}" if u && (!@test_users.delete(u) rescue false)
21
+ end
22
+ end
16
23
 
17
24
  describe "when initializing" do
18
25
  # basic initialization
@@ -131,11 +138,6 @@ describe "Koala::Facebook::TestUsers" do
131
138
  @user2 = @test_users.create(true, "read_stream,user_interests")
132
139
  end
133
140
 
134
- after :each do
135
- @test_users.delete(@user1) if @user1
136
- @test_users.delete(@user2) if @user2
137
- end
138
-
139
141
  it "should delete a user by id" do
140
142
  @test_users.delete(@user1['id']).should be_true
141
143
  @user1 = nil
@@ -191,11 +193,6 @@ describe "Koala::Facebook::TestUsers" do
191
193
  @user2 = @test_users.create(true, "read_stream,user_interests")
192
194
  end
193
195
 
194
- after :each do
195
- @test_users.delete(@user1)
196
- @test_users.delete(@user2)
197
- end
198
-
199
196
  it "should list test users" do
200
197
  result = @test_users.list
201
198
  result.should be_an(Array)
@@ -251,10 +248,11 @@ describe "Koala::Facebook::TestUsers" do
251
248
  end
252
249
  end
253
250
 
254
- it "should limit to a 50 person network" do
255
- @test_users.should_receive(:create).exactly(50).times
251
+ it "has no built-in network size limit" do
252
+ times = 100
253
+ @test_users.should_receive(:create).exactly(times).times
256
254
  @test_users.stub!(:befriend)
257
- @network = @test_users.create_network(51)
255
+ @network = @test_users.create_network(times)
258
256
  end
259
257
 
260
258
  it "should pass on the installed and permissions parameters to create" do
@@ -87,10 +87,10 @@ graph_api:
87
87
  with_token: '[{"headers":[{"name":"Location","value":"http://google.com"}]}]'
88
88
  batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "me/friends"}]) %>:
89
89
  post:
90
- with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
90
+ with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[],\"paging\":{}}"}]'
91
91
  batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"me"}, {"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}]) %>:
92
92
  post:
93
- with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
93
+ with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[],\"paging\":{}}"}]'
94
94
  batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights"}, {"method"=>"get", "relative_url"=>"koppel?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}]) %>:
95
95
  post:
96
96
  with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},{"body":"{\"id\":\"123\"}"}]'
@@ -113,7 +113,7 @@ graph_api:
113
113
  with_token: '[null,{"body":"{}"}]'
114
114
  batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends", "omit_response_on_success" => false}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}]) %>:
115
115
  post:
116
- with_token: '[{"body":"{\"data\":[]}"},{"body":"{}"}]'
116
+ with_token: '[{"body":"{\"data\":[],\"paging\":{}}"},{"body":"{}"}]'
117
117
  batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=i-dont-exist:$.data.*.id}"}"}]) %>:
118
118
  post:
119
119
  with_token: '{"error":190,"error_description":"Error validating access token."}'
@@ -211,7 +211,7 @@ graph_api:
211
211
  no_args:
212
212
  get:
213
213
  <<: *token_required
214
- with_token: '{"data": [{}]}'
214
+ with_token: '{"data": [{}], "paging": {}}'
215
215
 
216
216
  /lukeshepard/picture:
217
217
  type=large:
@@ -261,7 +261,7 @@ graph_api:
261
261
  get:
262
262
  with_token: '{"data": [{"id": "507731521_100412693339488"}], "paging": {"previous": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000", "next": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000"}}'
263
263
  no_token: '{"data": [{"id": "507731521_100412693339488"}], "paging": {"previous": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000", "next": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000"}}'
264
- "limit=25&q=facebook&until=2010-09-23T21:17:33+0000":
264
+ "limit=25&q=facebook&until=<%= TEST_DATA['search_time'] %>":
265
265
  get:
266
266
  with_token: '{"data": [{"id": "507731521_100412693339488"}], "paging": {"previous": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000", "next": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000"}}'
267
267
  no_token: '{"data": [{"id": "507731521_100412693339488"}], "paging": {"previous": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000", "next": "https:\/\/graph.facebook.com\/7204941866\/photos?limit=25&until=2008-09-15T18%3A30%3A25%2B0000"}}'
@@ -17,9 +17,7 @@ shared_examples_for "Koala GraphAPI" do
17
17
  describe "graph_call" do
18
18
  it "should pass all arguments to the api method" do
19
19
  args = [KoalaTest.user1, {}, "get", {:a => :b}]
20
-
21
20
  @api.should_receive(:api).with(*args)
22
-
23
21
  @api.graph_call(*args)
24
22
  end
25
23
 
@@ -27,6 +25,27 @@ shared_examples_for "Koala GraphAPI" do
27
25
  Koala.stub(:make_request).and_return(Koala::Response.new(500, {"error" => "An error occurred!"}, {}))
28
26
  lambda { @api.graph_call(KoalaTest.user1, {}) }.should raise_exception(Koala::Facebook::APIError)
29
27
  end
28
+
29
+ it "passes the results through GraphCollection.evaluate" do
30
+ result = {}
31
+ @api.stub(:api).and_return(result)
32
+ Koala::Facebook::GraphCollection.should_receive(:evaluate).with(result, @api)
33
+ @api.graph_call("/me")
34
+ end
35
+
36
+ it "returns the results of GraphCollection.evaluate" do
37
+ expected = {}
38
+ @api.stub(:api).and_return([])
39
+ Koala::Facebook::GraphCollection.should_receive(:evaluate).and_return(expected)
40
+ @api.graph_call("/me").should == expected
41
+ end
42
+
43
+ it "returns the post_processing block's results if one is supplied" do
44
+ other_result = [:a, 2, :three]
45
+ block = Proc.new {|r| other_result}
46
+ @api.stub(:api).and_return({})
47
+ @api.graph_call("/me", {}, "get", {}, &block).should == other_result
48
+ end
30
49
  end
31
50
 
32
51
  # SEARCH
@@ -349,12 +368,6 @@ end
349
368
 
350
369
  # GraphCollection
351
370
  shared_examples_for "Koala GraphAPI with GraphCollection" do
352
-
353
- it "should create an array-like object" do
354
- call = @api.graph_call("contextoptional/photos")
355
- Koala::Facebook::GraphCollection.new(call, @api).should be_an(Array)
356
- end
357
-
358
371
  describe "when getting a collection" do
359
372
  # GraphCollection methods
360
373
  it "should get a GraphCollection when getting connections" do
@@ -380,74 +393,14 @@ shared_examples_for "Koala GraphAPI with GraphCollection" do
380
393
  end
381
394
 
382
395
  it "should get a GraphCollection when paging through results" do
383
- @results = @api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=>"2010-09-23T21:17:33+0000"}])
396
+ @results = @api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=> KoalaTest.search_time}])
384
397
  @results.should be_a(Koala::Facebook::GraphCollection)
385
398
  end
386
399
 
387
400
  it "should return nil if the page call fails with nil" do
388
401
  # this happens sometimes
389
402
  @api.should_receive(:graph_call).and_return(nil)
390
- @api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=>"2010-09-23T21:17:33+0000"}]).should be_nil
391
- end
392
-
393
- # GraphCollection attributes
394
- describe "the GraphCollection" do
395
- before(:each) do
396
- @result = @api.get_connections(KoalaTest.page, "photos")
397
- end
398
-
399
- it "should have a read-only paging attribute" do
400
- @result.methods.map(&:to_sym).should include(:paging)
401
- @result.methods.map(&:to_sym).should_not include(:paging=)
402
- end
403
-
404
- describe "when getting a whole page" do
405
- before(:each) do
406
- @second_page = stub("page of Fb graph results")
407
- @base = stub("base")
408
- @args = stub("args")
409
- @page_of_results = stub("page of results")
410
- end
411
-
412
- it "should return the previous page of results" do
413
- @result.should_receive(:previous_page_params).and_return([@base, @args])
414
- @api.should_receive(:graph_call).with(@base, @args).and_yield(@second_page)
415
- Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
416
-
417
- @result.previous_page#.should == @page_of_results
418
- end
419
-
420
- it "should return the next page of results" do
421
- @result.should_receive(:next_page_params).and_return([@base, @args])
422
- @api.should_receive(:graph_call).with(@base, @args).and_yield(@second_page)
423
- Koala::Facebook::GraphCollection.should_receive(:new).with(@second_page, @api).and_return(@page_of_results)
424
-
425
- @result.next_page#.should == @page_of_results
426
- end
427
-
428
- it "should return nil it there are no other pages" do
429
- %w{next previous}.each do |this|
430
- @result.should_receive("#{this}_page_params".to_sym).and_return(nil)
431
- @result.send("#{this}_page").should == nil
432
- end
433
- end
434
- end
435
-
436
- describe "when parsing page paramters" do
437
- before(:each) do
438
- @graph_collection = Koala::Facebook::GraphCollection.new({"data" => []}, Koala::Facebook::API.new)
439
- end
440
-
441
- it "should return the base as the first array entry" do
442
- base = "url_path"
443
- @graph_collection.parse_page_url("anything.com/#{base}?anything").first.should == base
444
- end
445
-
446
- it "should return the arguments as a hash as the last array entry" do
447
- args_hash = {"one" => "val_one", "two" => "val_two"}
448
- @graph_collection.parse_page_url("anything.com/anything?#{args_hash.map {|k,v| "#{k}=#{v}" }.join("&")}").last.should == args_hash
449
- end
450
- end
403
+ @api.get_page(["search", {"q"=>"facebook", "limit"=>"25", "until"=> KoalaTest.search_time}]).should be_nil
451
404
  end
452
405
  end
453
406
  end
@@ -3,7 +3,7 @@ module KoalaTest
3
3
 
4
4
  class << self
5
5
  attr_accessor :oauth_token, :app_id, :secret, :app_access_token, :code, :session_key
6
- attr_accessor :oauth_test_data, :subscription_test_data
6
+ attr_accessor :oauth_test_data, :subscription_test_data, :search_time
7
7
  end
8
8
 
9
9
  # Test setup
@@ -36,7 +36,7 @@ module KoalaTest
36
36
  puts "Unable to load adapter #{adapter}, using Net::HTTP."
37
37
  end
38
38
 
39
- Koala.http_service.http_options[:beta] = true if ENV["beta"]
39
+ Koala.http_service.http_options[:beta] = true if ENV["beta"] || ENV["BETA"]
40
40
 
41
41
  # use a test user unless the developer wants to test against a real profile
42
42
  unless token = KoalaTest.oauth_token
@@ -84,6 +84,9 @@ module KoalaTest
84
84
  self.secret = data["oauth_test_data"]["secret"]
85
85
  self.code = data["oauth_test_data"]["code"]
86
86
  self.session_key = data["oauth_test_data"]["session_key"]
87
+
88
+ # fix the search time so it can be used in the mock responses
89
+ self.search_time = data["search_time"] || (Time.now - 3600).to_s
87
90
  end
88
91
 
89
92
  def self.testing_permissions
@@ -97,15 +100,34 @@ module KoalaTest
97
100
  print "Setting up test users..."
98
101
  @test_user_api = Koala::Facebook::TestUsers.new(:app_id => self.app_id, :secret => self.secret)
99
102
 
100
- # create two test users with specific names and befriend them
101
- @live_testing_user = @test_user_api.create(true, testing_permissions, :name => user1_name)
102
- @live_testing_friend = @test_user_api.create(true, testing_permissions, :name => user2_name)
103
- @test_user_api.befriend(@live_testing_user, @live_testing_friend)
104
- self.oauth_token = @live_testing_user["access_token"]
103
+ RSpec.configure do |config|
104
+ config.before :all do
105
+ # before each test module, create two test users with specific names and befriend them
106
+ KoalaTest.create_test_users
107
+ end
108
+
109
+ config.after :all do
110
+ # after each test module, delete the test users to avoid cluttering up the application
111
+ KoalaTest.destroy_test_users
112
+ end
113
+ end
105
114
 
106
115
  puts "done."
107
116
  end
108
117
 
118
+ def self.create_test_users
119
+ @live_testing_user = @test_user_api.create(true, KoalaTest.testing_permissions, :name => KoalaTest.user1_name)
120
+ @live_testing_friend = @test_user_api.create(true, KoalaTest.testing_permissions, :name => KoalaTest.user2_name)
121
+ @test_user_api.befriend(@live_testing_user, @live_testing_friend)
122
+ self.oauth_token = @live_testing_user["access_token"]
123
+ end
124
+
125
+ def self.destroy_test_users
126
+ [@live_testing_user, @live_testing_friend].each do |u|
127
+ puts "Unable to delete test user #{u.inspect}" if u && !(@test_user_api.delete(u) rescue false)
128
+ end
129
+ end
130
+
109
131
  def self.validate_user_info(token)
110
132
  print "Validating permissions for live testing..."
111
133
  # make sure we have the necessary permissions
@@ -18,7 +18,8 @@ module Koala
18
18
  TEST_DATA = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'fixtures', 'facebook_data.yml'))
19
19
  TEST_DATA.merge!('oauth_token' => Koala::MockHTTPService::ACCESS_TOKEN)
20
20
  TEST_DATA['oauth_test_data'].merge!('code' => Koala::MockHTTPService::OAUTH_CODE)
21
-
21
+ TEST_DATA['search_time'] = (Time.now - 3600).to_s
22
+
22
23
  # Useful in mock_facebook_responses.yml
23
24
  OAUTH_DATA = TEST_DATA['oauth_test_data']
24
25
  OAUTH_DATA.merge!({
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: koala
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0beta3
5
- prerelease: 5
4
+ version: 1.2.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-12 00:00:00.000000000Z
12
+ date: 2011-09-27 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
16
- requirement: &70118378561560 !ruby/object:Gem::Requirement
16
+ requirement: &70248641333480 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70118378561560
24
+ version_requirements: *70248641333480
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: faraday
27
- requirement: &70118378560320 !ruby/object:Gem::Requirement
27
+ requirement: &70248641333000 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.7.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70118378560320
35
+ version_requirements: *70248641333000
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70118378559340 !ruby/object:Gem::Requirement
38
+ requirement: &70248641332520 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '2.5'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70118378559340
46
+ version_requirements: *70248641332520
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &70118378557940 !ruby/object:Gem::Requirement
49
+ requirement: &70248641332040 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: 0.8.7
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70118378557940
57
+ version_requirements: *70248641332040
58
58
  description: Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write
59
59
  access to the social graph via the Graph and REST APIs, as well as support for realtime
60
60
  updates and OAuth and Facebook Connect authentication. Koala is fully tested and
@@ -89,12 +89,14 @@ files:
89
89
  - lib/koala/test_users.rb
90
90
  - lib/koala/uploadable_io.rb
91
91
  - lib/koala/utils.rb
92
+ - lib/koala/version.rb
92
93
  - readme.md
93
- - spec/cases/api_base_spec.rb
94
+ - spec/cases/api_spec.rb
94
95
  - spec/cases/error_spec.rb
95
96
  - spec/cases/graph_and_rest_api_spec.rb
96
97
  - spec/cases/graph_api_batch_spec.rb
97
98
  - spec/cases/graph_api_spec.rb
99
+ - spec/cases/graph_collection_spec.rb
98
100
  - spec/cases/http_service_spec.rb
99
101
  - spec/cases/koala_spec.rb
100
102
  - spec/cases/oauth_spec.rb
@@ -133,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
135
  version: '0'
134
136
  segments:
135
137
  - 0
136
- hash: -3213298653109204495
138
+ hash: 2837078681376474267
137
139
  required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  none: false
139
141
  requirements:
@@ -148,11 +150,12 @@ specification_version: 3
148
150
  summary: A lightweight, flexible library for Facebook with support for the Graph API,
149
151
  the REST API, realtime updates, and OAuth authentication.
150
152
  test_files:
151
- - spec/cases/api_base_spec.rb
153
+ - spec/cases/api_spec.rb
152
154
  - spec/cases/error_spec.rb
153
155
  - spec/cases/graph_and_rest_api_spec.rb
154
156
  - spec/cases/graph_api_batch_spec.rb
155
157
  - spec/cases/graph_api_spec.rb
158
+ - spec/cases/graph_collection_spec.rb
156
159
  - spec/cases/http_service_spec.rb
157
160
  - spec/cases/koala_spec.rb
158
161
  - spec/cases/oauth_spec.rb