koala 1.1.0 → 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.
Files changed (52) hide show
  1. data/.travis.yml +2 -1
  2. data/CHANGELOG +36 -0
  3. data/Gemfile +6 -2
  4. data/Rakefile +0 -1
  5. data/koala.gemspec +7 -8
  6. data/lib/koala/batch_operation.rb +15 -15
  7. data/lib/koala/graph_api.rb +85 -73
  8. data/lib/koala/graph_batch_api.rb +21 -11
  9. data/lib/koala/graph_collection.rb +13 -8
  10. data/lib/koala/http_service.rb +176 -0
  11. data/lib/koala/oauth.rb +34 -24
  12. data/lib/koala/realtime_updates.rb +21 -18
  13. data/lib/koala/rest_api.rb +1 -1
  14. data/lib/koala/test_users.rb +33 -17
  15. data/lib/koala/uploadable_io.rb +49 -43
  16. data/lib/koala/utils.rb +11 -0
  17. data/lib/koala/version.rb +3 -0
  18. data/lib/koala.rb +47 -45
  19. data/readme.md +58 -38
  20. data/spec/cases/{api_base_spec.rb → api_spec.rb} +27 -2
  21. data/spec/cases/error_spec.rb +32 -0
  22. data/spec/cases/graph_and_rest_api_spec.rb +12 -21
  23. data/spec/cases/graph_api_batch_spec.rb +92 -119
  24. data/spec/cases/graph_api_spec.rb +11 -14
  25. data/spec/cases/graph_collection_spec.rb +116 -0
  26. data/spec/cases/http_service_spec.rb +446 -0
  27. data/spec/cases/koala_spec.rb +36 -37
  28. data/spec/cases/oauth_spec.rb +318 -212
  29. data/spec/cases/realtime_updates_spec.rb +45 -31
  30. data/spec/cases/rest_api_spec.rb +23 -7
  31. data/spec/cases/test_users_spec.rb +123 -75
  32. data/spec/cases/uploadable_io_spec.rb +77 -36
  33. data/spec/cases/utils_spec.rb +10 -0
  34. data/spec/fixtures/facebook_data.yml +26 -24
  35. data/spec/fixtures/mock_facebook_responses.yml +131 -101
  36. data/spec/spec_helper.rb +29 -5
  37. data/spec/support/graph_api_shared_examples.rb +80 -120
  38. data/spec/support/json_testing_fix.rb +35 -11
  39. data/spec/support/koala_test.rb +187 -0
  40. data/spec/support/mock_http_service.rb +8 -5
  41. data/spec/support/ordered_hash.rb +205 -0
  42. data/spec/support/rest_api_shared_examples.rb +37 -37
  43. data/spec/support/uploadable_io_shared_examples.rb +2 -8
  44. metadata +72 -83
  45. data/lib/koala/http_services/net_http_service.rb +0 -92
  46. data/lib/koala/http_services/typhoeus_service.rb +0 -37
  47. data/lib/koala/http_services.rb +0 -46
  48. data/spec/cases/http_services/http_service_spec.rb +0 -129
  49. data/spec/cases/http_services/net_http_service_spec.rb +0 -532
  50. data/spec/cases/http_services/typhoeus_service_spec.rb +0 -152
  51. data/spec/support/live_testing_data_helper.rb +0 -40
  52. data/spec/support/setup_mocks_or_live.rb +0 -51
data/.travis.yml CHANGED
@@ -1,8 +1,9 @@
1
1
  rvm:
2
2
  - 1.8.7 # (current default)
3
3
  - 1.9.2
4
+ - 1.9.3
5
+ - ruby-head
4
6
  - rbx
5
7
  - rbx-2.0
6
8
  - ree
7
9
  - jruby
8
- - ruby-head
data/CHANGELOG CHANGED
@@ -1,3 +1,39 @@
1
+ v1.2
2
+ New methods:
3
+ -- API is now the main API class, contains both Graph and REST methods
4
+ -- Old classes are aliased with deprecation warnings (non-breaking change)
5
+ -- TestUsers#update lets you update the name or password of an existing test user
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)
8
+ Updated methods:
9
+ -- OAuth now parses Facebook's new signed cookie format
10
+ -- API.put_picture now accepts URLs to images (thanks, marcgg!)
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
17
+ Internal improvements:
18
+ -- Koala now uses Faraday to make requests, replacing the HTTPServices (see wiki)
19
+ -- Koala::HTTPService.http_options allows specification of default Faraday connection options
20
+ -- Koala::HTTPService.faraday_middleware allows custom middleware configurations
21
+ -- Koala now defaults to Net::HTTP rather than Typhoeus
22
+ -- Koala::NetHTTPService and Koala::TyphoeusService modules no longer exist
23
+ -- Koala no longer automatically switches to Net::HTTP when uploading IO objects to Facebook
24
+ -- RealTimeUpdates and TestUsers are no longer subclasses of API, but have their own .api objects
25
+ -- The old .graph_api accessor is aliases to .api with a deprecation warning
26
+ -- Removed deprecation warnings for pre-1.1 batch interface
27
+ Testing improvements:
28
+ -- Live test suites now run against test users by default
29
+ -- Test suite can be repeatedly run live without having to update facebook_data.yml
30
+ -- OAuth code and session key tests cannot be run against test users
31
+ -- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the rspec command
32
+ -- Live tests can be run against the beta server by specifying BETA=true in the rspec command
33
+ -- Tests now pass against all rubies on Travis CI
34
+ -- Expanded and refactored test coverage
35
+ -- Fixed bug with YAML parsing in Ruby 1.9
36
+
1
37
  v1.1
2
38
  New methods:
3
39
  -- Added Batch API support (thanks, seejohnrun and spiegela!)
data/Gemfile CHANGED
@@ -1,7 +1,11 @@
1
1
  source :rubygems
2
2
 
3
- gemspec
3
+ group :development, :test do
4
+ gem "typhoeus"
5
+ end
4
6
 
5
7
  if defined? JRUBY_VERSION
6
8
  gem "jruby-openssl"
7
- end
9
+ end
10
+
11
+ gemspec
data/Rakefile CHANGED
@@ -7,7 +7,6 @@ rescue LoadError
7
7
  puts 'although not required, bundler is recommened for running the tests'
8
8
  end
9
9
 
10
-
11
10
  task :default => :spec
12
11
 
13
12
  require 'rspec/core/rake_task'
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.1.0"
6
- s.date = %q{2011-07-18}
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.}
@@ -29,22 +31,19 @@ Gem::Specification.new do |s|
29
31
 
30
32
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
31
33
  s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
32
- s.add_runtime_dependency(%q<multipart-post>, ["~> 1.0"])
34
+ s.add_runtime_dependency(%q<faraday>, ["~> 0.7.0"])
33
35
  s.add_development_dependency(%q<rspec>, ["~> 2.5"])
34
36
  s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
35
- s.add_development_dependency(%q<typhoeus>, ["~> 0.2.4"])
36
37
  else
37
38
  s.add_dependency(%q<multi_json>, ["~> 1.0"])
38
- s.add_dependency(%q<multipart-post>, ["~> 1.0"])
39
39
  s.add_dependency(%q<rspec>, ["~> 2.5"])
40
40
  s.add_dependency(%q<rake>, ["~> 0.8.7"])
41
- s.add_dependency(%q<typhoeus>, ["~> 0.2.4"])
41
+ s.add_dependency(%q<faraday>, ["~> 0.7.0"])
42
42
  end
43
43
  else
44
44
  s.add_dependency(%q<multi_json>, ["~> 1.0"])
45
- s.add_dependency(%q<multipart-post>, ["~> 1.0"])
46
45
  s.add_dependency(%q<rspec>, ["~> 2.5"])
47
46
  s.add_dependency(%q<rake>, ["~> 0.8.7"])
48
- s.add_dependency(%q<typhoeus>, ["~> 0.2.4"])
47
+ s.add_dependency(%q<faraday>, ["~> 0.7.0"])
49
48
  end
50
49
  end
@@ -18,25 +18,25 @@ module Koala
18
18
  @url = options[:url]
19
19
  @method = options[:method].to_sym
20
20
  @post_processing = options[:post_processing]
21
-
21
+
22
22
  process_binary_args
23
-
23
+
24
24
  raise Koala::KoalaError, "Batch operations require an access token, none provided." unless @access_token
25
25
  end
26
-
26
+
27
27
  def to_batch_params(main_access_token)
28
28
  # set up the arguments
29
29
  args_string = Koala.http_service.encode_params(@access_token == main_access_token ? @args : @args.merge(:access_token => @access_token))
30
-
30
+
31
31
  response = {
32
- :method => @method.to_s,
32
+ :method => @method.to_s,
33
33
  :relative_url => @url,
34
34
  }
35
-
35
+
36
36
  # handle batch-level arguments, such as name, depends_on, and attached_files
37
37
  @batch_args[:attached_files] = @files.keys.join(",") if @files
38
38
  response.merge!(@batch_args) if @batch_args
39
-
39
+
40
40
  # for get and delete, we append args to the URL string
41
41
  # otherwise, they go in the body
42
42
  if args_string.length > 0
@@ -46,15 +46,15 @@ module Koala
46
46
  response[:body] = args_string if args_string.length > 0
47
47
  end
48
48
  end
49
-
49
+
50
50
  response
51
51
  end
52
-
52
+
53
53
  protected
54
-
54
+
55
55
  def process_binary_args
56
56
  # collect binary files
57
- @args.each_pair do |key, value|
57
+ @args.each_pair do |key, value|
58
58
  if UploadableIO.binary_content?(value)
59
59
  @files ||= {}
60
60
  # we use a class-level counter to ensure unique file identifiers across multiple batch operations
@@ -63,12 +63,12 @@ module Koala
63
63
  id = "op#{identifier}_file#{@files.keys.length}"
64
64
  @files[id] = @args.delete(key).is_a?(UploadableIO) ? value : UploadableIO.new(value)
65
65
  end
66
- end
66
+ end
67
67
  end
68
-
68
+
69
69
  def args_in_url?
70
- @method == :get || @method == :delete
71
- end
70
+ @method == :get || @method == :delete
71
+ end
72
72
  end
73
73
  end
74
74
  end
@@ -1,30 +1,30 @@
1
1
  module Koala
2
2
  module Facebook
3
- GRAPH_SERVER = "graph.facebook.com"
3
+ GRAPH_SERVER = "graph.facebook.com"
4
4
 
5
5
  module GraphAPIMethods
6
6
  # A client for the Facebook Graph API.
7
- #
7
+ #
8
8
  # See http://github.com/arsduo/koala for Ruby/Koala documentation
9
9
  # and http://developers.facebook.com/docs/api for Facebook API documentation
10
- #
10
+ #
11
11
  # The Graph API is made up of the objects in Facebook (e.g., people, pages,
12
12
  # events, photos) and the connections between them (e.g., friends,
13
13
  # photo tags, and event RSVPs). This client provides access to those
14
14
  # primitive types in a generic way. For example, given an OAuth access
15
15
  # token, this will fetch the profile of the active user and the list
16
16
  # of the user's friends:
17
- #
18
- # graph = Koala::Facebook::GraphAPI.new(access_token)
17
+ #
18
+ # graph = Koala::Facebook::API.new(access_token)
19
19
  # user = graph.get_object("me")
20
20
  # friends = graph.get_connections(user["id"], "friends")
21
- #
21
+ #
22
22
  # You can see a list of all of the objects and connections supported
23
23
  # by the API at http://developers.facebook.com/docs/reference/api/.
24
- #
24
+ #
25
25
  # You can obtain an access token via OAuth or by using the Facebook
26
26
  # JavaScript SDK. See the Koala and Facebook documentation for more information.
27
- #
27
+ #
28
28
  # If you are using the JavaScript SDK, you can use the
29
29
  # Koala::Facebook::OAuth.get_user_from_cookie() method below to get the OAuth access token
30
30
  # for the active user from the cookie saved by the SDK.
@@ -35,19 +35,19 @@ module Koala
35
35
  # Fetchs the given object from the graph.
36
36
  graph_call(id, args, "get", options)
37
37
  end
38
-
38
+
39
39
  def get_objects(ids, args = {}, options = {})
40
40
  # Fetchs all of the given objects from the graph.
41
41
  # If any of the IDs are invalid, they'll raise an exception.
42
42
  return [] if ids.empty?
43
43
  graph_call("", args.merge("ids" => ids.respond_to?(:join) ? ids.join(",") : ids), "get", options)
44
44
  end
45
-
45
+
46
46
  def put_object(parent_object, connection_name, args = {}, options = {})
47
47
  # Writes the given object to the graph, connected to the given parent.
48
48
  # See http://developers.facebook.com/docs/api#publishing for all of
49
49
  # the supported writeable objects.
50
- #
50
+ #
51
51
  # For example,
52
52
  # graph.put_object("me", "feed", :message => "Hello, world")
53
53
  # writes "Hello, world" to the active user's wall.
@@ -56,7 +56,7 @@ module Koala
56
56
  # publishing wall posts requires the "publish_stream" permission. See
57
57
  # http://developers.facebook.com/docs/authentication/ for details about
58
58
  # extended permissions.
59
-
59
+
60
60
  raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Write operations require an access token"}) unless @access_token
61
61
  graph_call("#{parent_object}/#{connection_name}", args, "post", options)
62
62
  end
@@ -66,24 +66,14 @@ module Koala
66
66
  raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Delete requires an access token"}) unless @access_token
67
67
  graph_call(id, {}, "delete", options)
68
68
  end
69
-
69
+
70
70
  # Connections
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
- def get_comments_for_urls(urls = [], args = {}, options = {})
80
- # Fetchs the comments for given URLs (array or comma-separated string)
81
- # see https://developers.facebook.com/blog/post/490
82
- return [] if urls.empty?
83
- args.merge!(:ids => urls.respond_to?(:join) ? urls.join(",") : urls)
84
- get_object("comments", args, options)
85
- end
86
-
87
77
  def put_connections(id, connection_name, args = {}, options = {})
88
78
  # Posts a certain connection
89
79
  raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Write operations require an access token"}) unless @access_token
@@ -98,19 +88,20 @@ module Koala
98
88
 
99
89
  # Media (photos and videos)
100
90
  # to delete photos or videos, use delete_object(object_id)
101
- # note: you'll need the user_photos or user_videos permissions to actually access media after upload
102
-
91
+ # note: you'll need the user_photos or user_videos permissions to actually access media after upload
92
+
103
93
  def get_picture(object, args = {}, options = {})
104
94
  # Gets a picture object, returning the URL (which Facebook sends as a header)
105
95
  graph_call("#{object}/picture", args, "get", options.merge(:http_component => :headers)) do |result|
106
96
  result["Location"]
107
97
  end
108
- end
109
-
98
+ end
99
+
110
100
  # Can be called in multiple ways:
111
101
  #
112
102
  # put_picture(file, [content_type], ...)
113
103
  # put_picture(path_to_file, [content_type], ...)
104
+ # put_picture(picture_url, ...)
114
105
  #
115
106
  # You can pass in uploaded files directly from Rails or Sinatra.
116
107
  # (See lib/koala/uploadable_io.rb for supported frameworks)
@@ -119,29 +110,32 @@ module Koala
119
110
  # - args: a hash of request parameters (default: {})
120
111
  # - target_id: ID of the target where to post the picture (default: "me")
121
112
  # - options: a hash of http options passed to the HTTPService module
122
- #
113
+ #
123
114
  # put_picture(file, content_type, {:message => "Message"}, 01234560)
124
115
  # put_picture(params[:file], {:message => "Message"})
125
-
116
+ #
117
+ # (Note that with URLs, there's no optional content type field)
118
+ # put_picture(picture_url, {:message => "Message"}, my_page_id)
119
+
126
120
  def put_picture(*picture_args)
127
121
  put_object(*parse_media_args(picture_args, "photos"))
128
122
  end
129
-
123
+
130
124
  def put_video(*video_args)
131
125
  args = parse_media_args(video_args, "videos")
132
126
  args.last[:video] = true
133
127
  put_object(*args)
134
128
  end
135
-
129
+
136
130
  # Wall posts
137
131
  # To get wall posts, use get_connections(user, "feed")
138
132
  # To delete a wall post, just use delete_object(post_id)
139
-
133
+
140
134
  def put_wall_post(message, attachment = {}, profile_id = "me", options = {})
141
135
  # attachment is a hash describing the wall post
142
136
  # (see X for more details)
143
- # For instance,
144
- #
137
+ # For instance,
138
+ #
145
139
  # {"name" => "Link name"
146
140
  # "link" => "http://www.example.com/",
147
141
  # "caption" => "{*actor*} posted a new review",
@@ -150,19 +144,19 @@ module Koala
150
144
 
151
145
  self.put_object(profile_id, "feed", attachment.merge({:message => message}), options)
152
146
  end
153
-
147
+
154
148
  # Comments
155
149
  # to delete comments, use delete_object(comment_id)
156
150
  # to get comments, use get_connections(object, "likes")
157
-
151
+
158
152
  def put_comment(object_id, message, options = {})
159
153
  # Writes the given comment on the given post.
160
154
  self.put_object(object_id, "comments", {:message => message}, options)
161
155
  end
162
-
156
+
163
157
  # Likes
164
158
  # to get likes, use get_connections(user, "likes")
165
-
159
+
166
160
  def put_like(object_id, options = {})
167
161
  # Likes the given post.
168
162
  self.put_object(object_id, "likes", {}, options)
@@ -175,42 +169,44 @@ module Koala
175
169
  end
176
170
 
177
171
  # Search
178
-
172
+
179
173
  def search(search_terms, args = {}, options = {})
180
174
  args.merge!({:q => search_terms}) unless search_terms.nil?
181
- graph_call("search", args, "get", options) do |result|
182
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
175
+ graph_call("search", args, "get", options)
176
+ end
177
+
178
+ # Convenience Methods
179
+
180
+ def get_page_access_token(object_id)
181
+ result = get_object(object_id, :fields => "access_token") do
182
+ result ? result["access_token"] : nil
183
183
  end
184
- end
185
-
186
-
184
+ end
185
+
186
+ def get_comments_for_urls(urls = [], args = {}, options = {})
187
+ # Fetchs the comments for given URLs (array or comma-separated string)
188
+ # see https://developers.facebook.com/blog/post/490
189
+ return [] if urls.empty?
190
+ args.merge!(:ids => urls.respond_to?(:join) ? urls.join(",") : urls)
191
+ get_object("comments", args, options)
192
+ end
193
+
187
194
  # GraphCollection support
188
195
  def get_page(params)
189
196
  # Pages through a set of results stored in a GraphCollection
190
197
  # Used for connections and search results
191
- graph_call(*params) do |result|
192
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
193
- end
198
+ graph_call(*params)
194
199
  end
195
-
196
-
200
+
197
201
  # Batch API
198
202
  def batch(http_options = {}, &block)
199
- batch_client = GraphBatchAPI.new(access_token)
203
+ batch_client = GraphBatchAPI.new(access_token, self)
200
204
  if block
201
205
  yield batch_client
202
206
  batch_client.execute(http_options)
203
207
  else
204
208
  batch_client
205
209
  end
206
- end
207
-
208
- def self.included(base)
209
- base.class_eval do
210
- def self.batch
211
- 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.)"
212
- end
213
- end
214
210
  end
215
211
 
216
212
  # Direct access to the Facebook API
@@ -221,38 +217,54 @@ module Koala
221
217
  raise error if error
222
218
  end
223
219
 
224
- # 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.)
225
224
  post_processing ? post_processing.call(result) : result
226
225
  end
226
+
227
+ private
227
228
 
228
229
  def check_response(response)
229
230
  # check for Graph API-specific errors
230
231
  # this returns an error, which is immediately raised (non-batch)
231
232
  # or added to the list of batch results (batch)
232
233
  if response.is_a?(Hash) && error_details = response["error"]
233
- APIError.new(error_details)
234
+ APIError.new(error_details)
234
235
  end
235
236
  end
236
-
237
- private
238
-
237
+
239
238
  def parse_media_args(media_args, method)
240
239
  # photo and video uploads can accept different types of arguments (see above)
241
240
  # so here, we parse the arguments into a form directly usable in put_object
242
241
  raise KoalaError.new("Wrong number of arguments for put_#{method == "photos" ? "picture" : "video"}") unless media_args.size.between?(1, 5)
243
-
242
+
244
243
  args_offset = media_args[1].kind_of?(Hash) || media_args.size == 1 ? 0 : 1
245
-
244
+
246
245
  args = media_args[1 + args_offset] || {}
247
246
  target_id = media_args[2 + args_offset] || "me"
248
- options = media_args[3 + args_offset] || {}
249
-
250
- args["source"] = Koala::UploadableIO.new(*media_args.slice(0, 1 + args_offset))
247
+ options = media_args[3 + args_offset] || {}
251
248
 
252
- options[:http_service] = Koala.base_http_service if args["source"].requires_base_http_service
249
+ if url?(media_args.first)
250
+ # If media_args is a URL, we can upload without UploadableIO
251
+ args.merge!(:url => media_args.first)
252
+ else
253
+ args["source"] = Koala::UploadableIO.new(*media_args.slice(0, 1 + args_offset))
254
+ end
253
255
 
254
256
  [target_id, method, args, options]
255
- end
257
+ end
258
+
259
+ def url?(data)
260
+ return false unless data.is_a? String
261
+ begin
262
+ uri = URI.parse(data)
263
+ %w( http https ).include?(uri.scheme)
264
+ rescue URI::BadURIError
265
+ false
266
+ end
267
+ end
256
268
  end
257
269
  end
258
- end
270
+ end
@@ -1,21 +1,28 @@
1
1
  module Koala
2
2
  module Facebook
3
3
  module GraphBatchAPIMethods
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
+
10
17
  alias_method :check_graph_api_response, :check_response
11
18
  alias_method :check_response, :check_graph_batch_api_response
12
19
  end
13
20
  end
14
-
21
+
15
22
  def batch_calls
16
23
  @batch_calls ||= []
17
24
  end
18
-
25
+
19
26
  def graph_call_in_batch(path, args = {}, verb = "get", options = {}, &post_processing)
20
27
  # for batch APIs, we queue up the call details (incl. post-processing)
21
28
  batch_calls << BatchOperation.new(
@@ -26,7 +33,7 @@ module Koala
26
33
  :http_options => options,
27
34
  :post_processing => post_processing
28
35
  )
29
- nil # batch operations return nothing immediately
36
+ nil # batch operations return nothing immediately
30
37
  end
31
38
 
32
39
  def check_graph_batch_api_response(response)
@@ -45,8 +52,8 @@ module Koala
45
52
  args.merge!(batch_op.files) if batch_op.files
46
53
  batch_op.to_batch_params(access_token)
47
54
  })
48
-
49
- graph_call_outside_batch('/', args, 'post', http_options) do |response|
55
+
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|
@@ -57,10 +64,10 @@ module Koala
57
64
  if call_result
58
65
  # (see note in regular api method about JSON parsing)
59
66
  body = MultiJson.decode("[#{call_result['body'].to_s}]")[0]
60
-
67
+
61
68
  unless call_result["code"].to_i >= 500 || error = check_response(body)
62
69
  # Get the HTTP component they want
63
- data = case batch_op.http_options[:http_component]
70
+ data = case batch_op.http_options[:http_component]
64
71
  when :status
65
72
  call_result["code"].to_i
66
73
  when :headers
@@ -78,10 +85,13 @@ module Koala
78
85
  else
79
86
  nil
80
87
  end
81
- end
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
86
96
  end
87
97
  end
@@ -10,25 +10,30 @@ 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
15
-
13
+ attr_reader :paging, :api, :raw_response
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
+
22
27
  # defines methods for NEXT and PREVIOUS pages
23
28
  %w{next previous}.each do |this|
24
-
29
+
25
30
  # def next_page
26
31
  # def previous_page
27
32
  define_method "#{this.to_sym}_page" do
28
33
  base, args = send("#{this}_page_params")
29
34
  base ? @api.get_page([base, args]) : nil
30
35
  end
31
-
36
+
32
37
  # def next_page_params
33
38
  # def previous_page_params
34
39
  define_method "#{this.to_sym}_page_params" do
@@ -36,7 +41,7 @@ module Koala
36
41
  parse_page_url(@paging[this])
37
42
  end
38
43
  end
39
-
44
+
40
45
  def parse_page_url(url)
41
46
  match = url.match(/.com\/(.*)\?(.*)/)
42
47
  base = match[1]
@@ -48,7 +53,7 @@ module Koala
48
53
  end
49
54
  [base,new_params]
50
55
  end
51
-
56
+
52
57
  end
53
58
  end
54
59
  end