koala 1.10.1 → 1.11.0rc
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/Gemfile +2 -0
- data/changelog.md +16 -0
- data/lib/koala/api/graph_batch_api.rb +5 -2
- data/lib/koala/errors.rb +6 -4
- data/lib/koala/http_service.rb +14 -2
- data/lib/koala/oauth.rb +3 -2
- data/lib/koala/test_users.rb +1 -1
- data/lib/koala/utils.rb +8 -0
- data/lib/koala/version.rb +1 -1
- data/readme.md +1 -1
- data/spec/cases/error_spec.rb +14 -8
- data/spec/cases/graph_api_batch_spec.rb +9 -0
- data/spec/cases/graph_collection_spec.rb +4 -4
- data/spec/cases/http_service_spec.rb +24 -0
- data/spec/cases/oauth_spec.rb +28 -0
- data/spec/cases/test_users_spec.rb +2 -2
- data/spec/fixtures/facebook_data.yml +4 -1
- data/spec/fixtures/vcr_cassettes/friend_list_next_page.yml +121 -0
- data/spec/integration/graph_collection_spec.rb +21 -0
- data/spec/support/koala_test.rb +72 -22
- metadata +24 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44c50b37f43f7bc407a12420de96e89f181d7b6f
|
4
|
+
data.tar.gz: 21203b9c7703b5bbacd43711ce990d3684a9d34c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29b23719eaaedf448162e7d477525d5a999d975ad6d2847bf5a4170d08bb42efcfee924d99c9ea747aa0310b046ba38ea4804a69aa858e9b52be9097bc706d41
|
7
|
+
data.tar.gz: 89dd9133b9976792fda084c954046f0828c30e48c727a3c9f37c15a5f0905e24d6161f884c025a5ae5040b7d4cd5a0f7c3918f4ef6a70352656dae8a0749cb0b
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/changelog.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
v.1.11.0
|
2
|
+
========
|
3
|
+
|
4
|
+
Updated features:
|
5
|
+
* OAuth now supports decoding the new JSON responses from the access token endpoints (thanks, ridiculous!)
|
6
|
+
* Batch API now accepts either symbols or strings for the access token (thanks, mattmueller!)
|
7
|
+
* Incorporate user message and title into error handling (thanks, richrines and isra17!)
|
8
|
+
|
9
|
+
Bug fixes:
|
10
|
+
* Fixed bug in GraphCollection URL versioning (thanks, deviousdodo and jankowy!)
|
11
|
+
* TestUsers#create_network now properly handles options (thanks, crx!)
|
12
|
+
|
13
|
+
Documentation improvements:
|
14
|
+
* Updated gem version (thanks, thomasklemm!)
|
15
|
+
|
16
|
+
|
1
17
|
v.1.10.1
|
2
18
|
========
|
3
19
|
|
@@ -19,12 +19,15 @@ module Koala
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def graph_call_in_batch(path, args = {}, verb = "get", options = {}, &post_processing)
|
22
|
-
#
|
22
|
+
# normalize options for consistency
|
23
|
+
options = Koala::Utils.symbolize_hash(options)
|
24
|
+
|
25
|
+
# for batch APIs, we queue up the call details (incl. post-processing)
|
23
26
|
batch_calls << BatchOperation.new(
|
24
27
|
:url => path,
|
25
28
|
:args => args,
|
26
29
|
:method => verb,
|
27
|
-
:access_token => options[
|
30
|
+
:access_token => options[:access_token] || access_token,
|
28
31
|
:http_options => options,
|
29
32
|
:post_processing => post_processing
|
30
33
|
)
|
data/lib/koala/errors.rb
CHANGED
@@ -14,14 +14,14 @@ module Koala
|
|
14
14
|
# http_status, then the error was detected before making a call to Facebook. (e.g. missing access token)
|
15
15
|
class APIError < ::Koala::KoalaError
|
16
16
|
attr_accessor :fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message,
|
17
|
-
:http_status, :response_body
|
17
|
+
:fb_error_user_message, :fb_error_user_title, :http_status, :response_body
|
18
18
|
|
19
19
|
# Create a new API Error
|
20
20
|
#
|
21
21
|
# @param http_status [Integer] The HTTP status code of the response
|
22
22
|
# @param response_body [String] The response body
|
23
23
|
# @param error_info One of the following:
|
24
|
-
# [Hash] The error information extracted from the request
|
24
|
+
# [Hash] The error information extracted from the request
|
25
25
|
# ("type", "code", "error_subcode", "message")
|
26
26
|
# [String] The error description
|
27
27
|
# If error_info is nil or not provided, the method will attempt to extract
|
@@ -51,9 +51,11 @@ module Koala
|
|
51
51
|
self.fb_error_code = error_info["code"]
|
52
52
|
self.fb_error_subcode = error_info["error_subcode"]
|
53
53
|
self.fb_error_message = error_info["message"]
|
54
|
+
self.fb_error_user_message = error_info["error_user_message"]
|
55
|
+
self.fb_error_user_title = error_info["error_user_title"]
|
54
56
|
|
55
57
|
error_array = []
|
56
|
-
%w(type code error_subcode message).each do |key|
|
58
|
+
%w(type code error_subcode message error_user_title error_user_message).each do |key|
|
57
59
|
error_array << "#{key}: #{error_info[key]}" if error_info[key]
|
58
60
|
end
|
59
61
|
|
@@ -78,7 +80,7 @@ module Koala
|
|
78
80
|
# Any error with a 5xx HTTP status code
|
79
81
|
class ServerError < APIError; end
|
80
82
|
|
81
|
-
# Any error with a 4xx HTTP status code
|
83
|
+
# Any error with a 4xx HTTP status code
|
82
84
|
class ClientError < APIError; end
|
83
85
|
|
84
86
|
# All graph API authentication failures.
|
data/lib/koala/http_service.rb
CHANGED
@@ -88,8 +88,10 @@ module Koala
|
|
88
88
|
ssl[:verify] = true unless ssl.has_key?(:verify)
|
89
89
|
end
|
90
90
|
|
91
|
-
# if an api_version is specified
|
92
|
-
|
91
|
+
# if an api_version is specified and the path does not already contain
|
92
|
+
# one, prepend it to the path
|
93
|
+
api_version = request_options[:api_version] || Koala.config.api_version
|
94
|
+
if api_version && !path_contains_api_version?(path)
|
93
95
|
begins_with_slash = path[0] == "/"
|
94
96
|
divider = begins_with_slash ? "" : "/"
|
95
97
|
path = "/#{api_version}#{divider}#{path}"
|
@@ -123,6 +125,16 @@ module Koala
|
|
123
125
|
end).join("&")
|
124
126
|
end
|
125
127
|
|
128
|
+
# Determines whether a given path already contains an API version.
|
129
|
+
#
|
130
|
+
# @param path the URL path.
|
131
|
+
#
|
132
|
+
# @return true or false accordingly.
|
133
|
+
def self.path_contains_api_version?(path)
|
134
|
+
match = /^\/?(v\d+(?:\.\d+)?)\//.match(path)
|
135
|
+
!!(match && match[1])
|
136
|
+
end
|
137
|
+
|
126
138
|
# deprecations
|
127
139
|
# not elegant or compact code, but temporary
|
128
140
|
|
data/lib/koala/oauth.rb
CHANGED
@@ -316,11 +316,12 @@ module Koala
|
|
316
316
|
end
|
317
317
|
|
318
318
|
def parse_access_token(response_text)
|
319
|
-
|
319
|
+
MultiJson.load(response_text)
|
320
|
+
rescue MultiJson::ParseError
|
321
|
+
response_text.split("&").inject({}) do |hash, bit|
|
320
322
|
key, value = bit.split("=")
|
321
323
|
hash.merge!(key => value)
|
322
324
|
end
|
323
|
-
components
|
324
325
|
end
|
325
326
|
|
326
327
|
def parse_unsigned_cookie(fb_cookie)
|
data/lib/koala/test_users.rb
CHANGED
@@ -166,7 +166,7 @@ module Koala
|
|
166
166
|
#
|
167
167
|
# @return the list of users created
|
168
168
|
def create_network(network_size, installed = true, permissions = '', options = {})
|
169
|
-
users = (0...network_size).collect { create(installed, permissions, options) }
|
169
|
+
users = (0...network_size).collect { create(installed, permissions, {}, options) }
|
170
170
|
friends = users.clone
|
171
171
|
users.each do |user|
|
172
172
|
# Remove this user from list of friends
|
data/lib/koala/utils.rb
CHANGED
@@ -29,5 +29,13 @@ module Koala
|
|
29
29
|
@posted_deprecations << message
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
# Ensures that a hash uses symbols as opposed to strings
|
34
|
+
# Useful for allowing either syntax for end users
|
35
|
+
def symbolize_hash(hash)
|
36
|
+
return hash unless hash.is_a?(Hash)
|
37
|
+
|
38
|
+
hash.inject({}){ |memo,(key,value)| memo[key.to_sym] = symbolize_hash(value); memo }
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
data/lib/koala/version.rb
CHANGED
data/readme.md
CHANGED
data/spec/cases/error_spec.rb
CHANGED
@@ -5,7 +5,7 @@ describe Koala::Facebook::APIError do
|
|
5
5
|
expect(Koala::Facebook::APIError.new(nil, nil)).to be_a(Koala::KoalaError)
|
6
6
|
end
|
7
7
|
|
8
|
-
[:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :http_status, :response_body].each do |accessor|
|
8
|
+
[:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :fb_error_user_message, :fb_error_user_title, :http_status, :response_body].each do |accessor|
|
9
9
|
it "has an accessor for #{accessor}" do
|
10
10
|
expect(Koala::Facebook::APIError.instance_methods.map(&:to_sym)).to include(accessor)
|
11
11
|
expect(Koala::Facebook::APIError.instance_methods.map(&:to_sym)).to include(:"#{accessor}=")
|
@@ -22,12 +22,14 @@ describe Koala::Facebook::APIError do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
context "with an error_info hash" do
|
25
|
-
let(:error) {
|
25
|
+
let(:error) {
|
26
26
|
error_info = {
|
27
27
|
'type' => 'type',
|
28
28
|
'message' => 'message',
|
29
29
|
'code' => 1,
|
30
|
-
'error_subcode' => 'subcode'
|
30
|
+
'error_subcode' => 'subcode',
|
31
|
+
'error_user_message' => 'error user message',
|
32
|
+
'error_user_title' => 'error user title'
|
31
33
|
}
|
32
34
|
Koala::Facebook::APIError.new(400, '', error_info)
|
33
35
|
}
|
@@ -36,15 +38,17 @@ describe Koala::Facebook::APIError do
|
|
36
38
|
:fb_error_type => 'type',
|
37
39
|
:fb_error_message => 'message',
|
38
40
|
:fb_error_code => 1,
|
39
|
-
:fb_error_subcode => 'subcode'
|
41
|
+
:fb_error_subcode => 'subcode',
|
42
|
+
:fb_error_user_message => 'error user message',
|
43
|
+
:fb_error_user_title => 'error user title'
|
40
44
|
}.each_pair do |accessor, value|
|
41
45
|
it "sets #{accessor} to #{value}" do
|
42
46
|
expect(error.send(accessor)).to eq(value)
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
|
-
it "sets the error message
|
47
|
-
expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message [HTTP 400]")
|
50
|
+
it "sets the error message appropriately" do
|
51
|
+
expect(error.message).to eq("type: type, code: 1, error_subcode: subcode, message: message, error_user_title: error user title, error_user_message: error user message [HTTP 400]")
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
@@ -58,13 +62,15 @@ describe Koala::Facebook::APIError do
|
|
58
62
|
|
59
63
|
context "with no error_info and a response_body containing error JSON" do
|
60
64
|
it "should extract the error info from the response body" do
|
61
|
-
response_body = '{ "error": { "type": "type", "message": "message", "code": 1, "error_subcode": "subcode" } }'
|
65
|
+
response_body = '{ "error": { "type": "type", "message": "message", "code": 1, "error_subcode": "subcode", "error_user_message": "error user message", "error_user_title": "error user title" } }'
|
62
66
|
error = Koala::Facebook::APIError.new(400, response_body)
|
63
67
|
{
|
64
68
|
:fb_error_type => 'type',
|
65
69
|
:fb_error_message => 'message',
|
66
70
|
:fb_error_code => 1,
|
67
|
-
:fb_error_subcode => 'subcode'
|
71
|
+
:fb_error_subcode => 'subcode',
|
72
|
+
:fb_error_user_message => 'error user message',
|
73
|
+
:fb_error_user_title => 'error user title'
|
68
74
|
}.each_pair do |accessor, value|
|
69
75
|
expect(error.send(accessor)).to eq(value)
|
70
76
|
end
|
@@ -519,6 +519,15 @@ describe "Koala::Facebook::GraphAPI in batch mode" do
|
|
519
519
|
expect(insights).to be_an(Koala::Facebook::GraphCollection)
|
520
520
|
end
|
521
521
|
|
522
|
+
it "handles requests passing the access token option as a symbol instead of a string" do
|
523
|
+
me, insights = @api.batch do |batch_api|
|
524
|
+
batch_api.get_object('me')
|
525
|
+
batch_api.get_connections(@app_id, 'insights', {}, {:access_token => @app_api.access_token})
|
526
|
+
end
|
527
|
+
expect(me['id']).not_to be_nil
|
528
|
+
expect(insights).to be_an(Koala::Facebook::GraphCollection)
|
529
|
+
end
|
530
|
+
|
522
531
|
it "preserves batch-op specific access tokens in GraphCollection returned from batch" do
|
523
532
|
# Provide an alternate token for a batch operation
|
524
533
|
@other_access_token_args = { 'access_token' => @app_api.access_token }
|
@@ -145,15 +145,15 @@ describe Koala::Facebook::GraphCollection do
|
|
145
145
|
end
|
146
146
|
|
147
147
|
describe "#next_page" do
|
148
|
-
let(:paging){ {"next" => "http://example.com
|
148
|
+
let(:paging){ {"next" => "http://example.com/abc?a=2&b=3"} }
|
149
149
|
|
150
150
|
it "should get next page" do
|
151
|
-
expect(@api).to receive(:get_page).with(["", {"a" => "2", "b" => "3"}])
|
151
|
+
expect(@api).to receive(:get_page).with(["abc", {"a" => "2", "b" => "3"}])
|
152
152
|
@collection.next_page
|
153
153
|
end
|
154
154
|
|
155
155
|
it "should get next page with extra parameters" do
|
156
|
-
expect(@api).to receive(:get_page).with(["", {"a" => "2", "b" => "3", "c" => "4"}])
|
156
|
+
expect(@api).to receive(:get_page).with(["abc", {"a" => "2", "b" => "3", "c" => "4"}])
|
157
157
|
@collection.next_page("c" => "4")
|
158
158
|
end
|
159
159
|
end
|
@@ -171,4 +171,4 @@ describe Koala::Facebook::GraphCollection do
|
|
171
171
|
@collection.previous_page("c" => "4")
|
172
172
|
end
|
173
173
|
end
|
174
|
-
end
|
174
|
+
end
|
@@ -296,6 +296,12 @@ describe Koala::HTTPService do
|
|
296
296
|
expect(@mock_connection).to receive(:get).with("/v12/anything", anything)
|
297
297
|
Koala::HTTPService.make_request("/anything", {}, "get")
|
298
298
|
end
|
299
|
+
|
300
|
+
it "doesn't add a version if the path already contains one" do
|
301
|
+
expect(Koala.config).to receive(:api_version).and_return("v11")
|
302
|
+
expect(@mock_connection).to receive(:get).with("/v12/anything", anything)
|
303
|
+
Koala::HTTPService.make_request("/v12/anything", {}, "get")
|
304
|
+
end
|
299
305
|
end
|
300
306
|
|
301
307
|
it "makes a POST request if the verb isn't get" do
|
@@ -378,6 +384,24 @@ describe Koala::HTTPService do
|
|
378
384
|
end
|
379
385
|
end
|
380
386
|
|
387
|
+
describe ".path_contains_api_version?" do
|
388
|
+
it "works when the path is prefixed by a slash" do
|
389
|
+
expect(Koala::HTTPService.path_contains_api_version?('/v2.1/anything')).to be true
|
390
|
+
end
|
391
|
+
|
392
|
+
it "works when the path is not prefixed by a slash" do
|
393
|
+
expect(Koala::HTTPService.path_contains_api_version?('v2.1/anything')).to be true
|
394
|
+
end
|
395
|
+
|
396
|
+
it "works with versions without a ." do
|
397
|
+
expect(Koala::HTTPService.path_contains_api_version?('v21/anything')).to be true
|
398
|
+
end
|
399
|
+
|
400
|
+
it "returns nil for paths without a version" do
|
401
|
+
expect(Koala::HTTPService.path_contains_api_version?('/anything')).to be false
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
381
405
|
describe "deprecated options" do
|
382
406
|
before :each do
|
383
407
|
allow(Koala::HTTPService).to receive(:http_options).and_return({})
|
data/spec/cases/oauth_spec.rb
CHANGED
@@ -449,6 +449,34 @@ describe "Koala::Facebook::OAuth" do
|
|
449
449
|
@oauth.get_access_token_info(@code)
|
450
450
|
end
|
451
451
|
|
452
|
+
it "properly decodes JSON results" do
|
453
|
+
result = {
|
454
|
+
"access_token" => "foo",
|
455
|
+
"expires_in" => "baz",
|
456
|
+
"machine_id" => "bar"
|
457
|
+
}
|
458
|
+
allow(Koala).to receive(:make_request).and_return(
|
459
|
+
Koala::HTTPService::Response.new(
|
460
|
+
200,
|
461
|
+
MultiJson.dump(result),
|
462
|
+
{}
|
463
|
+
)
|
464
|
+
)
|
465
|
+
expect(@oauth.get_access_token_info(@code)).to eq(result)
|
466
|
+
end
|
467
|
+
|
468
|
+
it "falls back to URL-style parsing " do
|
469
|
+
result = "access_token=foo&expires_in=baz&machine_id=bar"
|
470
|
+
allow(Koala).to receive(:make_request).and_return(
|
471
|
+
Koala::HTTPService::Response.new(200, result, {})
|
472
|
+
)
|
473
|
+
expect(@oauth.get_access_token_info(@code)).to eq({
|
474
|
+
"access_token" => "foo",
|
475
|
+
"expires_in" => "baz",
|
476
|
+
"machine_id" => "bar"
|
477
|
+
})
|
478
|
+
end
|
479
|
+
|
452
480
|
if KoalaTest.code
|
453
481
|
it "properly gets and parses an access token token results into a hash" do
|
454
482
|
result = @oauth.get_access_token_info(@code)
|
@@ -328,7 +328,7 @@ describe "Koala::Facebook::TestUsers" do
|
|
328
328
|
perms = ["read_stream", "offline_access"]
|
329
329
|
installed = false
|
330
330
|
count = 25
|
331
|
-
expect(@test_users).to receive(:create).exactly(count).times.with(installed, perms, anything)
|
331
|
+
expect(@test_users).to receive(:create).exactly(count).times.with(installed, perms, anything, anything)
|
332
332
|
allow(@test_users).to receive(:befriend)
|
333
333
|
@test_users.create_network(count, installed, perms)
|
334
334
|
end
|
@@ -336,7 +336,7 @@ describe "Koala::Facebook::TestUsers" do
|
|
336
336
|
it "accepts http options that are passed to both the create and befriend calls" do
|
337
337
|
count = 25
|
338
338
|
options = {:some_http_option => true}
|
339
|
-
expect(@test_users).to receive(:create).exactly(count).times.with(anything, anything, options).and_return({})
|
339
|
+
expect(@test_users).to receive(:create).exactly(count).times.with(anything, anything, anything, options).and_return({})
|
340
340
|
# there are more befriends than creates, but we don't need to do the extra work to calculate out the exact #
|
341
341
|
expect(@test_users).to receive(:befriend).at_least(count).times.with(anything, anything, options)
|
342
342
|
@test_users.create_network(count, true, "", options)
|
@@ -59,4 +59,7 @@ subscription_test_data:
|
|
59
59
|
challenge_data:
|
60
60
|
"hub.challenge": "1290024882"
|
61
61
|
"hub.verify_token": "myverificationtoken|1f54545d5f722733e17faae15377928f"
|
62
|
-
"hub.mode": "subscribe"
|
62
|
+
"hub.mode": "subscribe"
|
63
|
+
|
64
|
+
vcr_data:
|
65
|
+
oauth_token: CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD
|
@@ -0,0 +1,121 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://graph.facebook.com/v2.2/me/friends?access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.9.0
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Facebook-Api-Version:
|
22
|
+
- v2.2
|
23
|
+
Etag:
|
24
|
+
- "\"405645cb180f38f9c1e373a1fe301c47c2a763db\""
|
25
|
+
Content-Type:
|
26
|
+
- application/json; charset=UTF-8
|
27
|
+
Pragma:
|
28
|
+
- no-cache
|
29
|
+
Access-Control-Allow-Origin:
|
30
|
+
- "*"
|
31
|
+
X-Fb-Rev:
|
32
|
+
- '1543964'
|
33
|
+
Cache-Control:
|
34
|
+
- private, no-cache, no-store, must-revalidate
|
35
|
+
Expires:
|
36
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
37
|
+
X-Fb-Debug:
|
38
|
+
- sR8XVZI7FA7B7iZBo18REPDIaoeLkIbkg06WTYKMmJ/wzV/Gr5wYOYE47wIIgvnLBMgTe2D3RKvGo668AMWYXw==
|
39
|
+
Date:
|
40
|
+
- Fri, 26 Dec 2014 14:54:14 GMT
|
41
|
+
Connection:
|
42
|
+
- keep-alive
|
43
|
+
Content-Length:
|
44
|
+
- '834'
|
45
|
+
body:
|
46
|
+
encoding: UTF-8
|
47
|
+
string: "{\"data\":[{\"name\":\"Brian Rosenthal\",\"id\":\"6580\"},{\"name\":\"Waynn
|
48
|
+
Lue\",\"id\":\"202035\"},{\"name\":\"Chris Baclig\",\"id\":\"216743\"},{\"name\":\"Scott
|
49
|
+
Kleper\",\"id\":\"219493\"},{\"name\":\"Nick Elser\",\"id\":\"429772\"},{\"name\":\"Ian
|
50
|
+
Spivey\",\"id\":\"700103\"},{\"name\":\"Hong Dave\",\"id\":\"1215057\"},{\"name\":\"Trek
|
51
|
+
Glowacki\",\"id\":\"2203865\"},{\"name\":\"Tung Bach Ly\",\"id\":\"2803850\"},{\"name\":\"Isaac
|
52
|
+
Wolkerstorfer\",\"id\":\"2900100\"},{\"name\":\"Avishai Weiss\",\"id\":\"2900461\"},{\"name\":\"Harold
|
53
|
+
Liss\",\"id\":\"2900570\"},{\"name\":\"Ryo Chijiiwa\",\"id\":\"2901026\"},{\"name\":\"Luke
|
54
|
+
Shepard\",\"id\":\"2901279\"},{\"name\":\"Daniela Wellisz\",\"id\":\"2903998\"},{\"name\":\"Daniel
|
55
|
+
Hirschberg\",\"id\":\"2904437\"},{\"name\":\"Borja Sotomayor\",\"id\":\"2905626\"},{\"name\":\"David
|
56
|
+
Underwood\",\"id\":\"2906370\"},{\"name\":\"Avi Schwab\",\"id\":\"2908086\"},{\"name\":\"Kathleen
|
57
|
+
Burnett\",\"id\":\"6827168\"},{\"name\":\"Solomon Hursey\",\"id\":\"6857763\"},{\"name\":\"Sarah
|
58
|
+
Allen\",\"id\":\"10100146\"},{\"name\":\"Zach Wolfenbarger\",\"id\":\"10115065\"},{\"name\":\"JT
|
59
|
+
Archie\",\"id\":\"10603565\"},{\"name\":\"Colin McCloskey\",\"id\":\"11308679\"}],\"paging\":{\"next\":\"https:\\/\\/graph.facebook.com\\/v2.2\\/2905623\\/friends?access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&limit=25&offset=25&__after_id=enc_Aez9Lwck5wLvRR_mHYNCzYNGmnYunJfnA3Z8hYXQ0xOluJfy4KVv4YYD1I4Im036hrA\"},\"summary\":{\"total_count\":821}}"
|
60
|
+
http_version:
|
61
|
+
recorded_at: Fri, 26 Dec 2014 14:54:14 GMT
|
62
|
+
- request:
|
63
|
+
method: get
|
64
|
+
uri: https://graph.facebook.com/v2.2/2905623/friends?__after_id=enc_Aez9Lwck5wLvRR_mHYNCzYNGmnYunJfnA3Z8hYXQ0xOluJfy4KVv4YYD1I4Im036hrA&access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&limit=25&offset=25
|
65
|
+
body:
|
66
|
+
encoding: US-ASCII
|
67
|
+
string: ''
|
68
|
+
headers:
|
69
|
+
User-Agent:
|
70
|
+
- Faraday v0.9.0
|
71
|
+
Accept-Encoding:
|
72
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
73
|
+
Accept:
|
74
|
+
- "*/*"
|
75
|
+
response:
|
76
|
+
status:
|
77
|
+
code: 200
|
78
|
+
message: OK
|
79
|
+
headers:
|
80
|
+
Facebook-Api-Version:
|
81
|
+
- v2.2
|
82
|
+
Etag:
|
83
|
+
- "\"ad597c7482d414a109407197c49d9c102b245fec\""
|
84
|
+
Content-Type:
|
85
|
+
- application/json; charset=UTF-8
|
86
|
+
Pragma:
|
87
|
+
- no-cache
|
88
|
+
Access-Control-Allow-Origin:
|
89
|
+
- "*"
|
90
|
+
X-Fb-Rev:
|
91
|
+
- '1543964'
|
92
|
+
Cache-Control:
|
93
|
+
- private, no-cache, no-store, must-revalidate
|
94
|
+
Expires:
|
95
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
96
|
+
X-Fb-Debug:
|
97
|
+
- 4rfPGqFD0OuxktinjEn9le7gO3aRg+E1a6367E4h3cFk3O/vsYMACfGJFCAgsLakTiOMJaaNj4ep+CkgFPnFiw==
|
98
|
+
Date:
|
99
|
+
- Fri, 26 Dec 2014 14:54:15 GMT
|
100
|
+
Connection:
|
101
|
+
- keep-alive
|
102
|
+
Content-Length:
|
103
|
+
- '972'
|
104
|
+
body:
|
105
|
+
encoding: UTF-8
|
106
|
+
string: "{\"data\":[{\"name\":\"Andrew Kane\",\"id\":\"12460925\"},{\"name\":\"Robin
|
107
|
+
Greenwood\",\"id\":\"34602916\"},{\"name\":\"Tom Elliott\",\"id\":\"36902548\"},{\"name\":\"Brian
|
108
|
+
Maula\",\"id\":\"77800072\"},{\"name\":\"Brian Wis\",\"id\":\"78500422\"},{\"name\":\"Ryan
|
109
|
+
Merket\",\"id\":\"136300045\"},{\"name\":\"Alex Coles\",\"id\":\"202905335\"},{\"name\":\"Chris
|
110
|
+
Vincent\",\"id\":\"205300220\"},{\"name\":\"Craig Stoe\",\"id\":\"500388531\"},{\"name\":\"Alex
|
111
|
+
Neill\",\"id\":\"504120418\"},{\"name\":\"Riste\\u00e1rd Br\\u00e9anainn\",\"id\":\"10152350043011181\"},{\"name\":\"Rafi
|
112
|
+
Jacoby\",\"id\":\"518938454\"},{\"name\":\"Marc Blinder\",\"id\":\"519467425\"},{\"name\":\"Erik
|
113
|
+
Ogan\",\"id\":\"542404932\"},{\"name\":\"Nigel Benito Griffin\",\"id\":\"566130340\"},{\"name\":\"Karla
|
114
|
+
Geci\",\"id\":\"569882615\"},{\"name\":\"Dennis Schneider\",\"id\":\"582757122\"},{\"name\":\"Chad
|
115
|
+
Krsek\",\"id\":\"605431196\"},{\"name\":\"Simon Cross\",\"id\":\"605665581\"},{\"name\":\"Matt
|
116
|
+
Patterson\",\"id\":\"629514342\"},{\"name\":\"Adeel Ahmad\",\"id\":\"630665156\"},{\"name\":\"Nicholas
|
117
|
+
Solter\",\"id\":\"647473136\"},{\"name\":\"David Morcillo Mu\\u00f1oz\",\"id\":\"674832191\"},{\"name\":\"Ana
|
118
|
+
Luisa Santos\",\"id\":\"685010072\"},{\"name\":\"Matthias Wiemann\",\"id\":\"693274202\"}],\"paging\":{\"next\":\"https:\\/\\/graph.facebook.com\\/v2.2\\/2905623\\/friends?limit=25&offset=50&access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&__after_id=enc_Aez79XsNc6mXAfVDW2zvCkfHWTRTl_YprQ2VgrtLMqPa71dSOCoC34H-6YHCCNonr-w\",\"previous\":\"https:\\/\\/graph.facebook.com\\/v2.2\\/2905623\\/friends?limit=25&offset=0&access_token=CAACEdEose0cBAKuiUM40KZBEsq2l0iggaMGZBPI74svGQRMmZCPXb7eZCYPhNUbVXnnYZCjXKFKIc7HgYllr4RDIKrANHm6kKncOx0Y3UpDqLliRGZAnSEUypyFworUnBMOQJBlAuB1wlwYJZB7LIZCobCcnT2q9QwrZBpK3qAZB3u7ZAJaZAdsMZBsyALkAXatoj75leWXhgXfT1QiJHZBGoRlz07Q85z1dZBReK4ZD&__before_id=enc_Aey0OlxZalVBlh623QavOqlYyEOlclWjZMQ_7Hv2x4BHFV_JzG9H2j-_nOikaW5PsyY\"},\"summary\":{\"total_count\":821}}"
|
119
|
+
http_version:
|
120
|
+
recorded_at: Fri, 26 Dec 2014 14:54:15 GMT
|
121
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Koala
|
4
|
+
RSpec.describe "requests using GraphCollections" do
|
5
|
+
let(:api) { Facebook::API.new(KoalaTest.vcr_oauth_token) }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
# use the right version of the API as of the writing of this test
|
9
|
+
Koala.config.api_version = "v2.2"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can access the next page of a friend list" do
|
13
|
+
KoalaTest.with_vcr_unless_live("friend_list_next_page") do
|
14
|
+
result = api.get_connection("me", "friends")
|
15
|
+
expect(result).not_to be_empty
|
16
|
+
expect(result.next_page).not_to be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/spec/support/koala_test.rb
CHANGED
@@ -5,6 +5,7 @@ module KoalaTest
|
|
5
5
|
attr_accessor :oauth_token, :app_id, :secret, :app_access_token, :code, :session_key
|
6
6
|
attr_accessor :oauth_test_data, :subscription_test_data, :search_time
|
7
7
|
attr_accessor :test_user_api
|
8
|
+
attr_accessor :vcr_oauth_token
|
8
9
|
end
|
9
10
|
|
10
11
|
# Test setup
|
@@ -12,31 +13,13 @@ module KoalaTest
|
|
12
13
|
def self.setup_test_environment!
|
13
14
|
setup_rspec
|
14
15
|
|
15
|
-
|
16
|
-
# By default the Koala specs are run using stubs for HTTP requests,
|
17
|
-
# so they won't fail due to Facebook-imposed rate limits or server timeouts.
|
18
|
-
#
|
19
|
-
# However as a result they are more brittle since
|
20
|
-
# we are not testing the latest responses from the Facebook servers.
|
21
|
-
# To be certain all specs pass with the current Facebook services,
|
22
|
-
# run LIVE=true bundle exec rake spec.
|
23
|
-
Koala.http_service = Koala::MockHTTPService
|
24
|
-
KoalaTest.setup_test_data(Koala::MockHTTPService::TEST_DATA)
|
25
|
-
else
|
16
|
+
if live?
|
26
17
|
# Runs Koala specs through the Facebook servers
|
27
18
|
# using data for a real app
|
28
19
|
live_data = YAML.load_file(File.join(File.dirname(__FILE__), '../fixtures/facebook_data.yml'))
|
29
20
|
KoalaTest.setup_test_data(live_data)
|
30
21
|
|
31
|
-
|
32
|
-
adapter = ENV['ADAPTER'] || "typhoeus" # use Typhoeus by default if available
|
33
|
-
begin
|
34
|
-
require adapter
|
35
|
-
require 'typhoeus/adapters/faraday' if adapter.to_s == "typhoeus"
|
36
|
-
Faraday.default_adapter = adapter.to_sym
|
37
|
-
rescue LoadError
|
38
|
-
puts "Unable to load adapter #{adapter}, using Net::HTTP."
|
39
|
-
end
|
22
|
+
activate_adapter!
|
40
23
|
|
41
24
|
Koala.http_service.http_options[:beta] = true if ENV["beta"] || ENV["BETA"]
|
42
25
|
|
@@ -46,6 +29,17 @@ module KoalaTest
|
|
46
29
|
else
|
47
30
|
KoalaTest.validate_user_info(token)
|
48
31
|
end
|
32
|
+
else
|
33
|
+
# By default the Koala specs are run using stubs for HTTP requests,
|
34
|
+
# so they won't fail due to Facebook-imposed rate limits or server timeouts.
|
35
|
+
#
|
36
|
+
# However as a result they are more brittle since
|
37
|
+
# we are not testing the latest responses from the Facebook servers.
|
38
|
+
# To be certain all specs pass with the current Facebook services,
|
39
|
+
# run LIVE=true bundle exec rake spec.
|
40
|
+
activate_vcr!
|
41
|
+
Koala.http_service = Koala::MockHTTPService
|
42
|
+
KoalaTest.setup_test_data(Koala::MockHTTPService::TEST_DATA)
|
49
43
|
end
|
50
44
|
end
|
51
45
|
|
@@ -79,6 +73,28 @@ module KoalaTest
|
|
79
73
|
end
|
80
74
|
end
|
81
75
|
|
76
|
+
# If we're running live tests against the Facebook API, don't use the VCR
|
77
|
+
# fixtures. (Another alternative would be to rerecord those fixtures, but we
|
78
|
+
# don't necessarily want to do that.
|
79
|
+
def self.with_vcr_unless_live(name)
|
80
|
+
if live?
|
81
|
+
yield
|
82
|
+
else
|
83
|
+
begin
|
84
|
+
# if we're using VCR, we don't want to use the mock service, which was
|
85
|
+
# an early implementation of the same type of tool
|
86
|
+
old_adapter = Koala.http_service
|
87
|
+
Koala.http_service = Koala::HTTPService
|
88
|
+
activate_adapter!
|
89
|
+
VCR.use_cassette(name) do
|
90
|
+
yield
|
91
|
+
end
|
92
|
+
ensure
|
93
|
+
Koala.http_service = old_adapter
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
82
98
|
def self.setup_test_data(data)
|
83
99
|
# make data accessible to all our tests
|
84
100
|
self.oauth_test_data = data["oauth_test_data"]
|
@@ -90,12 +106,14 @@ module KoalaTest
|
|
90
106
|
self.code = data["oauth_test_data"]["code"]
|
91
107
|
self.session_key = data["oauth_test_data"]["session_key"]
|
92
108
|
|
109
|
+
self.vcr_oauth_token = data["vcr_data"]["oauth_token"]
|
110
|
+
|
93
111
|
# fix the search time so it can be used in the mock responses
|
94
112
|
self.search_time = data["search_time"] || (Time.now - 3600).to_s
|
95
113
|
end
|
96
114
|
|
97
115
|
def self.testing_permissions
|
98
|
-
"read_stream,
|
116
|
+
"read_stream, publish_actions, user_photos, user_videos, read_insights"
|
99
117
|
end
|
100
118
|
|
101
119
|
def self.setup_test_users
|
@@ -143,7 +161,7 @@ module KoalaTest
|
|
143
161
|
perms.each_pair do |perm, value|
|
144
162
|
if value == (perm == "read_insights" ? 1 : 0) # live testing depends on insights calls failing
|
145
163
|
puts "failed!\n" # put a new line after the print above
|
146
|
-
raise ArgumentError, "Your access token must have
|
164
|
+
raise ArgumentError, "Your access token must have #{testing_permissions.join(", ")}, and lack read_insights. You have: #{perms.inspect}"
|
147
165
|
end
|
148
166
|
end
|
149
167
|
puts "done!"
|
@@ -199,4 +217,36 @@ module KoalaTest
|
|
199
217
|
def self.app_properties
|
200
218
|
mock_interface? ? {"desktop" => 0} : {"description" => "A test framework for Koala and its users. (#{rand(10000).to_i})"}
|
201
219
|
end
|
220
|
+
|
221
|
+
def self.live?
|
222
|
+
ENV['LIVE']
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.activate_vcr!
|
226
|
+
require 'vcr'
|
227
|
+
|
228
|
+
VCR.configure do |c|
|
229
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
230
|
+
c.hook_into :webmock # or :fakeweb
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.activate_adapter!
|
235
|
+
unless @adapter_activation_attempted
|
236
|
+
# allow live tests with different adapters
|
237
|
+
adapter = ENV['ADAPTER'] || "typhoeus" # use Typhoeus by default if available
|
238
|
+
begin
|
239
|
+
# JRuby doesn't support typhoeus on Travis
|
240
|
+
unless defined? JRUBY_VERSION
|
241
|
+
require adapter
|
242
|
+
require 'typhoeus/adapters/faraday' if adapter.to_s == "typhoeus"
|
243
|
+
Faraday.default_adapter = adapter.to_sym
|
244
|
+
end
|
245
|
+
rescue LoadError
|
246
|
+
puts "Unable to load adapter #{adapter}, using Net::HTTP."
|
247
|
+
ensure
|
248
|
+
@adapter_activation_attempted = true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
202
252
|
end
|
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: koala
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.11.0rc
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Koppel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: faraday
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: addressable
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
description: Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write
|
@@ -64,11 +64,11 @@ extra_rdoc_files:
|
|
64
64
|
- readme.md
|
65
65
|
- changelog.md
|
66
66
|
files:
|
67
|
-
-
|
68
|
-
-
|
69
|
-
-
|
70
|
-
-
|
71
|
-
-
|
67
|
+
- .autotest
|
68
|
+
- .gitignore
|
69
|
+
- .rspec
|
70
|
+
- .travis.yml
|
71
|
+
- .yardopts
|
72
72
|
- Gemfile
|
73
73
|
- Guardfile
|
74
74
|
- LICENSE
|
@@ -115,6 +115,8 @@ files:
|
|
115
115
|
- spec/fixtures/cat.m4v
|
116
116
|
- spec/fixtures/facebook_data.yml
|
117
117
|
- spec/fixtures/mock_facebook_responses.yml
|
118
|
+
- spec/fixtures/vcr_cassettes/friend_list_next_page.yml
|
119
|
+
- spec/integration/graph_collection_spec.rb
|
118
120
|
- spec/spec_helper.rb
|
119
121
|
- spec/support/custom_matchers.rb
|
120
122
|
- spec/support/graph_api_shared_examples.rb
|
@@ -128,25 +130,25 @@ licenses:
|
|
128
130
|
metadata: {}
|
129
131
|
post_install_message:
|
130
132
|
rdoc_options:
|
131
|
-
-
|
132
|
-
-
|
133
|
-
-
|
133
|
+
- --line-numbers
|
134
|
+
- --inline-source
|
135
|
+
- --title
|
134
136
|
- Koala
|
135
137
|
require_paths:
|
136
138
|
- lib
|
137
139
|
required_ruby_version: !ruby/object:Gem::Requirement
|
138
140
|
requirements:
|
139
|
-
- -
|
141
|
+
- - '>='
|
140
142
|
- !ruby/object:Gem::Version
|
141
143
|
version: '0'
|
142
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
145
|
requirements:
|
144
|
-
- -
|
146
|
+
- - '>'
|
145
147
|
- !ruby/object:Gem::Version
|
146
|
-
version:
|
148
|
+
version: 1.3.1
|
147
149
|
requirements: []
|
148
150
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.2.
|
151
|
+
rubygems_version: 2.2.2
|
150
152
|
signing_key:
|
151
153
|
specification_version: 4
|
152
154
|
summary: A lightweight, flexible library for Facebook with support for the Graph API,
|
@@ -171,6 +173,8 @@ test_files:
|
|
171
173
|
- spec/fixtures/cat.m4v
|
172
174
|
- spec/fixtures/facebook_data.yml
|
173
175
|
- spec/fixtures/mock_facebook_responses.yml
|
176
|
+
- spec/fixtures/vcr_cassettes/friend_list_next_page.yml
|
177
|
+
- spec/integration/graph_collection_spec.rb
|
174
178
|
- spec/spec_helper.rb
|
175
179
|
- spec/support/custom_matchers.rb
|
176
180
|
- spec/support/graph_api_shared_examples.rb
|