angellist_api 1.0.5 → 1.0.6

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.
@@ -1,5 +1,13 @@
1
1
  ## Master
2
2
 
3
+ ## 1.0.6 - 14 April, 2013
4
+
5
+ - Properly raise a `TooManyRequests` error when a 403 contains a JSON error
6
+ object indicating rate limit overage (`EnhanceYourCalm` error aliased for
7
+ backwards compatibility) ([Ches Martin])
8
+ - Add missing options parameters for passing pagination params to endpoints that
9
+ support them ([Ches Martin])
10
+
3
11
  ## 1.0.5 - 27 March, 2013
4
12
 
5
13
  - Fix inability to pass direction param for startup_roles ([Ches Martin])
@@ -11,11 +11,18 @@ module AngellistApi
11
11
  # paginated and ordered by most recent story first.
12
12
  #
13
13
  # @requires_authentication Optional
14
+ # @paginated By explicit timestamp cursor
14
15
  #
15
16
  # @param options [Hash] A customizable set of options.
16
17
  # @option options [Integer] :personalized If set to 1 and a user is
17
18
  # authenticated, only activity from the authenticated user's social graph
18
19
  # is returned.
20
+ # @option options [Integer] :since If a Unix timestamp is passed in via
21
+ # this parameter, only activity since that timestamp will be returned. A
22
+ # maximum of 25 items will be returned, starting with the oldest item. A
23
+ # cursor variable is also returned, which is the timestamp of the last
24
+ # item. You may call /feed again with since set to this value to get the
25
+ # next page of activity.
19
26
  #
20
27
  # @example
21
28
  # AngellistApi.get_feed
@@ -38,6 +38,7 @@ module AngellistApi
38
38
  # follow ids, such as those from the activity feed.
39
39
  #
40
40
  # @requires_authentication Optional
41
+ # @paginated No
41
42
  #
42
43
  # @param ids [Array] IDs of the follows to fetch.
43
44
  #
@@ -52,26 +53,30 @@ module AngellistApi
52
53
  # follower first.
53
54
  #
54
55
  # @requires_authentication No
56
+ # @paginated Yes
55
57
  #
56
58
  # @param [Integer] id The id of the given user.
59
+ # @param [Hash] options A customizable set of options.
57
60
  #
58
61
  # @example Get followers of user with ID 12345.
59
62
  # AngellistApi.get_user_followers(12345)
60
- def get_user_followers(id)
61
- get("1/users/#{id}/followers")
63
+ def get_user_followers(id, options={})
64
+ get("1/users/#{id}/followers", options)
62
65
  end
63
66
 
64
67
  # Return the ids of the given user's followers, paginated and ordered by
65
68
  # most recent follower first.
66
69
  #
67
70
  # @requires_authentication No
71
+ # @paginated Yes
68
72
  #
69
73
  # @param [Integer] id The id of the given user.
74
+ # @param [Hash] options A customizable set of options.
70
75
  #
71
76
  # @example Get IDs of the followers of user with ID 12345.
72
77
  # AngellistApi.get_user_follower_ids(12345)
73
- def get_user_follower_ids(id)
74
- get("1/users/#{id}/followers/ids")
78
+ def get_user_follower_ids(id, options={})
79
+ get("1/users/#{id}/followers/ids", options)
75
80
  end
76
81
 
77
82
  # Return objects that the given user is following, paginated and ordered
@@ -79,6 +84,7 @@ module AngellistApi
79
84
  # class of objects to return.
80
85
  #
81
86
  # @requires_authentication Optional
87
+ # @paginated Yes
82
88
  #
83
89
  # @param [Integer] id The id of the given user.
84
90
  # @param [Hash] options A customizable set of options.
@@ -96,6 +102,7 @@ module AngellistApi
96
102
  # /users/:id/followers/ids.
97
103
  #
98
104
  # @requires_authentication Optional
105
+ # @paginated Yes
99
106
  #
100
107
  # @param [Integer] id The id of the given user.
101
108
  # @param [Hash] options A customizable set of options.
@@ -111,26 +118,30 @@ module AngellistApi
111
118
  # recent follower first. Responds like GET /users/:id/followers.
112
119
  #
113
120
  # @requires_authentication Optional
121
+ # @paginated Yes
114
122
  #
115
123
  # @param [Integer] id The id of the given startup.
124
+ # @param [Hash] options A customizable set of options.
116
125
  #
117
126
  # @example Get followers of startup with ID 1234.
118
127
  # AngellistApi.get_startup_followers(1234)
119
- def get_startup_followers(id)
120
- get("1/startups/#{id}/followers")
128
+ def get_startup_followers(id, options={})
129
+ get("1/startups/#{id}/followers", options)
121
130
  end
122
131
 
123
132
  # Returns the ids of the given startup's followers, paginated and ordered
124
133
  # by most recent follower first. Responds like GET /users/:id/followers.
125
134
  #
126
135
  # @requires_authentication Optional
136
+ # @paginated Yes
127
137
  #
128
138
  # @param [Integer] id The id of the given user.
139
+ # @param [Hash] options A customizable set of options.
129
140
  #
130
141
  # @example Get IDs of followers of startup with ID 1234.
131
142
  # AngellistApi.get_startup_follower_ids(1234)
132
- def get_startup_follower_ids(id)
133
- get("1/startups/#{id}/followers/ids")
143
+ def get_startup_follower_ids(id, options={})
144
+ get("1/startups/#{id}/followers/ids", options)
134
145
  end
135
146
  end
136
147
  end
@@ -8,7 +8,8 @@ module AngellistApi
8
8
  # Returns threads that the authenticated user is involved in. A "thread"
9
9
  # is a conversation between two users, where each message is tagged with
10
10
  # the same thread_id. If the authenticated user has any unread messages
11
- #in the thread, the viewed attribute will be false. Requires scope "message".
11
+ # in the thread, the viewed attribute will be false. Requires scope
12
+ # "message".
12
13
  #
13
14
  # @requires_authentication Yes
14
15
  # @paginated Yes
@@ -13,6 +13,7 @@ module AngellistApi
13
13
  # - do not use user_ids and startup_ids at the same time
14
14
  #
15
15
  # @requires_authentication Yes
16
+ # @paginated No
16
17
  #
17
18
  # @param options [Hash] A customizable set of options.
18
19
  # @option options [Array<Integer>] :user_ids Show paths between you and
@@ -23,3 +23,4 @@ module AngellistApi
23
23
  end
24
24
  end
25
25
  end
26
+
@@ -9,6 +9,7 @@ module AngellistApi
9
9
  # Also returns the total count of positive reviews.
10
10
  #
11
11
  # @requires_authentication Optional
12
+ # @paginated Yes
12
13
  #
13
14
  # @param [Hash] options A customizable set of options.
14
15
  # @option options [Integer] :user_id user_id of the desired user. If none
@@ -21,28 +21,32 @@ module AngellistApi
21
21
  # descending.
22
22
  #
23
23
  # @requires_authentication No
24
+ # @paginated Yes
24
25
  #
25
26
  # @param [Integer] id The id of the desired tag. Currently only works for
26
27
  # tags of type MarketTag or LocationTag.
28
+ # @param [Hash] options A customizable set of options.
27
29
  #
28
30
  # @example Get children of a tag.
29
31
  # AngellistApi.get_tag_children(1654)
30
- def get_tag_children(id)
31
- get("1/tags/#{id}/children")
32
+ def get_tag_children(id, options={})
33
+ get("1/tags/#{id}/children", options)
32
34
  end
33
35
 
34
36
  # Returns parents of the given tag. For more details, see the
35
37
  # documentation for GET /tags/:id/children.
36
38
  #
37
39
  # @requires_authentication No
40
+ # @paginated Yes
38
41
  #
39
42
  # @param [Integer] id The id of the desired tag. Currently only works for
40
43
  # tags of type MarketTag or LocationTag.
44
+ # @param [Hash] options A customizable set of options.
41
45
  #
42
46
  # @example Get parents of a tag.
43
47
  # AngellistApi.get_tag_parents(1654)
44
- def get_tag_parents(id)
45
- get("1/tags/#{id}/parents")
48
+ def get_tag_parents(id, options={})
49
+ get("1/tags/#{id}/parents", options)
46
50
  end
47
51
 
48
52
  # Returns startups that are tagged with the given tag or a child of the
@@ -50,6 +54,7 @@ module AngellistApi
50
54
  # parameter.
51
55
  #
52
56
  # @requires_authentication Optional
57
+ # @paginated Yes
53
58
  #
54
59
  # @param [Hash] options A customizable set of options.
55
60
  # @option options [String] :order May be one of popularity, asc or desc.
@@ -19,6 +19,7 @@ module AngellistApi
19
19
  # Get information for a batch of up to 50 users given a list of user IDs.
20
20
  #
21
21
  # @requires_authentication No
22
+ # @pagination No
22
23
  #
23
24
  # @param [Array] ids IDs of the users to fetch.
24
25
  #
@@ -32,6 +33,7 @@ module AngellistApi
32
33
  # Search for a user given a URL slug. Responds like GET /users/:id.
33
34
  #
34
35
  # @requires_authentication No
36
+ # @pagination No
35
37
  #
36
38
  # @param [Hash] options A customizable set of options.
37
39
  # @option options [String] :slug The URL slug of the desired user.
@@ -8,29 +8,33 @@ module AngellistApi
8
8
  # @param [String] message
9
9
  # @param [Hash] http_headers
10
10
  # @return [AngellistApi::Error]
11
- def initialize(message, http_headers)
12
- @http_headers = Hash[http_headers]
11
+ def initialize(message, http_headers={})
12
+ @http_headers = http_headers
13
13
  super message
14
14
  end
15
15
 
16
16
  # @return [Time]
17
17
  def ratelimit_reset
18
- Time.at(@http_headers.values_at('x-ratelimit-reset', 'X-RateLimit-Reset').detect{|value| value}.to_i)
18
+ reset = http_headers['x-ratelimit-reset']
19
+ Time.at(reset.to_i) if reset
19
20
  end
20
21
 
21
22
  # @return [Integer]
22
23
  def ratelimit_limit
23
- @http_headers.values_at('x-ratelimit-limit', 'X-RateLimit-Limit').detect{|value| value}.to_i
24
+ limit = http_headers['x-ratelimit-limit']
25
+ limit.to_i if limit
24
26
  end
25
27
 
26
28
  # @return [Integer]
27
29
  def ratelimit_remaining
28
- @http_headers.values_at('x-ratelimit-remaining', 'X-RateLimit-Remaining').detect{|value| value}.to_i
30
+ remaining = http_headers['x-ratelimit-remaining']
31
+ remaining.to_i if remaining
29
32
  end
30
33
 
31
34
  # @return [Integer]
32
35
  def retry_after
33
- [(ratelimit_reset - Time.now).ceil, 0].max
36
+ [(ratelimit_reset - Time.now).ceil, 0].max if ratelimit_reset
34
37
  end
35
38
  end
36
39
  end
40
+
@@ -0,0 +1,26 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 403 with "over_limit"
5
+ # in the response.
6
+ #
7
+ # @note We've inquired with the AngelList API team about using a 429 status
8
+ # in the future.
9
+ class Error
10
+ class TooManyRequests < AngellistApi::Error
11
+ # The number of seconds your application should wait before requesting data
12
+ # from the API again.
13
+ #
14
+ # This may not be supported by AngelList currently but is suggested in RFC
15
+ # 6585 for the 429 status code.
16
+ def retry_after
17
+ retry_after = http_headers['retry-after']
18
+ retry_after.to_i if retry_after
19
+ end
20
+ end
21
+
22
+ # Backwards compatability
23
+ EnhanceYourCalm = TooManyRequests
24
+ end
25
+ end
26
+
@@ -1,6 +1,6 @@
1
1
  require 'faraday'
2
2
  require 'angellist_api/error/bad_request'
3
- require 'angellist_api/error/enhance_your_calm'
3
+ require 'angellist_api/error/too_many_requests'
4
4
  require 'angellist_api/error/forbidden'
5
5
  require 'angellist_api/error/not_acceptable'
6
6
  require 'angellist_api/error/not_found'
@@ -16,13 +16,15 @@ module AngellistApi
16
16
  when 401
17
17
  raise AngellistApi::Error::Unauthorized.new(error_message(env), env[:response_headers])
18
18
  when 403
19
- raise AngellistApi::Error::Forbidden.new(error_message(env), env[:response_headers])
19
+ if env[:body]['error'] == 'over_limit'
20
+ raise AngellistApi::Error::TooManyRequests.new(error_message(env), env[:response_headers])
21
+ else
22
+ raise AngellistApi::Error::Forbidden.new(error_message(env), env[:response_headers])
23
+ end
20
24
  when 404
21
25
  raise AngellistApi::Error::NotFound.new(error_message(env), env[:response_headers])
22
26
  when 406
23
27
  raise AngellistApi::Error::NotAcceptable.new(error_message(env), env[:response_headers])
24
- when 420
25
- raise AngellistApi::Error::EnhanceYourCalm.new(error_message(env), env[:response_headers])
26
28
  end
27
29
  end
28
30
 
@@ -49,3 +51,4 @@ module AngellistApi
49
51
  end
50
52
  end
51
53
  end
54
+
@@ -1,4 +1,4 @@
1
1
  module AngellistApi
2
- VERSION = '1.0.5'
2
+ VERSION = '1.0.6'
3
3
  end
4
4
 
@@ -0,0 +1,53 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.angel.co/1/me
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json
12
+ User-Agent:
13
+ - AngellistApi Ruby Gem 1.0.5
14
+ Authorization:
15
+ - Bearer <ANGELLIST_ACCESS_TOKEN>
16
+ response:
17
+ status:
18
+ code: 403
19
+ message: !!null
20
+ headers:
21
+ server:
22
+ - nginx/1.0.6
23
+ date:
24
+ - Sun, 14 Apr 2013 06:45:44 GMT
25
+ content-type:
26
+ - application/json; charset=utf-8
27
+ transfer-encoding:
28
+ - chunked
29
+ connection:
30
+ - close
31
+ vary:
32
+ - Accept-Encoding, Accept-Encoding
33
+ status:
34
+ - '403'
35
+ x-ratelimit-limit:
36
+ - '2000'
37
+ x-ratelimit-remaining:
38
+ - '2000'
39
+ x-thanks:
40
+ - For using AngelList. Rock on.
41
+ x-runtime:
42
+ - '35'
43
+ etag:
44
+ - ! '"0c13384ccaf0ae7c9e1ff289e0070619"'
45
+ cache-control:
46
+ - private, max-age=0, must-revalidate
47
+ body:
48
+ encoding: ASCII-8BIT
49
+ string: ! '{"error":"forbidden","error_description":""}'
50
+ http_version: !!null
51
+ recorded_at: Sun, 14 Apr 2013 06:45:44 GMT
52
+ recorded_with: VCR 2.4.0
53
+
@@ -0,0 +1,53 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.angel.co/1/me
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json
12
+ User-Agent:
13
+ - AngellistApi Ruby Gem 1.0.5
14
+ Authorization:
15
+ - Bearer <ANGELLIST_ACCESS_TOKEN>
16
+ response:
17
+ status:
18
+ code: 403
19
+ message: !!null
20
+ headers:
21
+ server:
22
+ - nginx/1.0.6
23
+ date:
24
+ - Sun, 14 Apr 2013 06:45:44 GMT
25
+ content-type:
26
+ - application/json; charset=utf-8
27
+ transfer-encoding:
28
+ - chunked
29
+ connection:
30
+ - close
31
+ vary:
32
+ - Accept-Encoding, Accept-Encoding
33
+ status:
34
+ - '403'
35
+ x-ratelimit-limit:
36
+ - '2000'
37
+ x-ratelimit-remaining:
38
+ - '0'
39
+ x-thanks:
40
+ - For using AngelList. Rock on.
41
+ x-runtime:
42
+ - '35'
43
+ etag:
44
+ - ! '"0c13384ccaf0ae7c9e1ff289e0070619"'
45
+ cache-control:
46
+ - private, max-age=0, must-revalidate
47
+ body:
48
+ encoding: ASCII-8BIT
49
+ string: ! '{"error":"over_limit","error_description":"You are currently over your rate limit."}'
50
+ http_version: !!null
51
+ recorded_at: Sun, 14 Apr 2013 06:45:44 GMT
52
+ recorded_with: VCR 2.4.0
53
+
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Errors' do
4
+ let(:client) { AngellistApi::Client.new }
5
+
6
+ describe AngellistApi::Error::TooManyRequests do
7
+ context 'when response is an over rate limit error',
8
+ :vcr => { :cassette_name => 'errors/too_many_requests' } do
9
+
10
+ it 'is raised' do
11
+ expect { client.me }.to raise_error described_class
12
+ end
13
+ end
14
+
15
+ context 'when response is some other 403 Forbidden error',
16
+ :vcr => { :cassette_name => 'errors/forbidden' } do
17
+
18
+ it 'is not raised' do
19
+ expect { client.me }.to raise_error AngellistApi::Error::Forbidden
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,8 @@
1
+ module HttpHeaders
2
+ # Faraday response headers are wrapped with a utility Hash subclass that
3
+ # normalizes case, underscored Symbols, etc.
4
+ def headerize(hash)
5
+ Faraday::Utils::Headers.new(hash)
6
+ end
7
+ end
8
+
@@ -32,7 +32,9 @@ describe AngellistApi::Client::Follows do
32
32
  describe "#get_user_followers" do
33
33
  it "gets 1/users/<id>/followers" do
34
34
  id = "123"
35
- client.should_receive(:get).with("1/users/#{id}/followers").and_return("success")
35
+ client.should_receive(:get).
36
+ with("1/users/#{id}/followers", {}).
37
+ and_return("success")
36
38
  client.get_user_followers(id).should == "success"
37
39
  end
38
40
  end
@@ -40,7 +42,9 @@ describe AngellistApi::Client::Follows do
40
42
  describe "#get_user_follower_ids" do
41
43
  it "gets 1/users/<id>/followers/ids" do
42
44
  id = "123"
43
- client.should_receive(:get).with("1/users/#{id}/followers/ids").and_return("success")
45
+ client.should_receive(:get).
46
+ with("1/users/#{id}/followers/ids", {}).
47
+ and_return("success")
44
48
  client.get_user_follower_ids(id).should == "success"
45
49
  end
46
50
  end
@@ -66,7 +70,9 @@ describe AngellistApi::Client::Follows do
66
70
  describe "#get_startup_followers" do
67
71
  it "gets 1/startups/<id>/followers" do
68
72
  id = "123"
69
- client.should_receive(:get).with("1/startups/#{id}/followers").and_return("success")
73
+ client.should_receive(:get).
74
+ with("1/startups/#{id}/followers", {}).
75
+ and_return("success")
70
76
  client.get_startup_followers(id).should == "success"
71
77
  end
72
78
  end
@@ -74,7 +80,9 @@ describe AngellistApi::Client::Follows do
74
80
  describe "#get_startup_follower_ids" do
75
81
  it "gets 1/startups/<id>/followers/ids" do
76
82
  id = "123"
77
- client.should_receive(:get).with("1/startups/#{id}/followers/ids").and_return("success")
83
+ client.should_receive(:get).
84
+ with("1/startups/#{id}/followers/ids", {}).
85
+ and_return("success")
78
86
  client.get_startup_follower_ids(id).should == "success"
79
87
  end
80
88
  end
@@ -14,7 +14,9 @@ describe AngellistApi::Client::Tags do
14
14
  describe "#get_tag_children" do
15
15
  it "gets 1/tags/<id>/children" do
16
16
  id = "123"
17
- client.should_receive(:get).with("1/tags/#{id}/children").and_return("success")
17
+ client.should_receive(:get).
18
+ with("1/tags/#{id}/children", {}).
19
+ and_return("success")
18
20
  client.get_tag_children(id).should == "success"
19
21
  end
20
22
  end
@@ -22,7 +24,9 @@ describe AngellistApi::Client::Tags do
22
24
  describe "#get_tag_parents" do
23
25
  it "gets 1/tags/<id>/parents" do
24
26
  id = "123"
25
- client.should_receive(:get).with("1/tags/#{id}/parents").and_return("success")
27
+ client.should_receive(:get).
28
+ with("1/tags/#{id}/parents", {}).
29
+ and_return("success")
26
30
  client.get_tag_parents(id).should == "success"
27
31
  end
28
32
  end
@@ -1,124 +1,133 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AngellistApi::Error do
4
+ include HttpHeaders
4
5
 
5
- describe "#initialize" do
6
- context "message parameter" do
7
- it "does not raise an error if it is nil" do
8
- expect { AngellistApi::Error.new(nil, :header => "data") }.to_not raise_error
9
- end
10
-
11
- it "builds an error with the given message" do
12
- error = AngellistApi::Error.new("bad data", :header => 'data')
13
- error.message.should == "bad data"
14
- end
6
+ describe '#new' do
7
+ specify 'message parameter can be nil' do
8
+ expect { AngellistApi::Error.new(nil, :header => 'data') }.to_not raise_error
15
9
  end
16
10
 
17
- context "http_headers parameter" do
18
- it "raises an exception if no http headers are passed" do
19
- expect { AngellistApi::Error.new("some message", nil) }.to raise_error(ArgumentError, "odd number of arguments for Hash")
20
- end
21
-
22
- it "raises an exception if the params cant be converted to a hash" do
23
- expect { AngellistApi::Error.new("some message", "header") }.to raise_error(ArgumentError, "odd number of arguments for Hash")
24
- end
11
+ it 'builds an error with the given message' do
12
+ error = AngellistApi::Error.new('bad data', :header => 'data')
13
+ error.message.should == 'bad data'
14
+ end
25
15
 
26
- it "is set with the params" do
27
- error = AngellistApi::Error.new("some message", :some => "value")
28
- error.http_headers.should == { :some => "value" }
29
- end
16
+ it 'sets http_headers attribute' do
17
+ error = AngellistApi::Error.new('some message', :some => 'value')
18
+ error.http_headers.should == { :some => 'value' }
30
19
  end
31
20
  end
32
21
 
33
- describe "#ratelimit_reset" do
34
- it "returns the time set as x-rate-limit-reset if it's set" do
35
- time1 = Time.now.to_i
36
- time2 = time1 + 3600
37
- error = AngellistApi::Error.new("message", {'x-ratelimit-reset' => time1, 'X-RateLimit-Reset' => time2})
38
- error.ratelimit_reset.should == Time.at(time1)
39
- end
22
+ # NOTE: AngelList doesn't actually support this yet...
23
+ describe '#ratelimit_reset' do
24
+ subject { described_class.new('message', headers).ratelimit_reset }
25
+
26
+ context 'with X-RateLimit-Reset header set' do
27
+ let(:unixtime) { Time.now.to_i }
28
+ let(:headers) { headerize('X-RateLimit-Reset' => unixtime) }
40
29
 
41
- it "returns the time set as X-RateLimit-Reset if x-rate-limit-reset is empty" do
42
- time = Time.now.to_i
43
- error = AngellistApi::Error.new("message", {'x-ratelimit-reset' => nil, 'X-RateLimit-Reset' => time})
44
- error.ratelimit_reset.should == Time.at(time)
30
+ it 'returns the time of reset' do
31
+ subject.should == Time.at(unixtime)
32
+ end
45
33
  end
46
34
 
47
- it "returns 0 if both X-RateLimit-Reset or x-rate-limit-reset are not set" do
48
- error = AngellistApi::Error.new("message", {})
49
- error.ratelimit_reset.should == Time.at(0)
35
+ context 'with X-RateLimit-Reset unset' do
36
+ let(:headers) { headerize({}) }
37
+ it { should be_nil }
50
38
  end
51
39
  end
52
40
 
53
- describe "#ratelimit_limit" do
54
- it "returns the number set as x-rate-limit-limit if it's set" do
55
- error = AngellistApi::Error.new("message", {'x-ratelimit-limit' => 500, 'X-RateLimit-Limit' => 400})
56
- error.ratelimit_limit.should == 500
57
- end
41
+ describe '#ratelimit_limit' do
42
+ subject { described_class.new('message', headers).ratelimit_limit }
58
43
 
59
- it "returns the number set as X-RateLimit-Limit if x-rate-limit-limit is empty" do
60
- error = AngellistApi::Error.new("message", {'x-ratelimit-limit' => nil, 'X-RateLimit-Limit' => 600})
61
- error.ratelimit_limit.should == 600
44
+ context 'with X-RateLimit-Limit header set' do
45
+ let(:limit) { '1000' }
46
+ let(:headers) { headerize('X-RateLimit-Limit' => limit) }
47
+
48
+ it 'returns the limit as an integer' do
49
+ subject.should eq 1000
50
+ end
62
51
  end
63
52
 
64
- it "returns 0 if both X-RateLimit-Limit or x-rate-limit-limit are not set" do
65
- error = AngellistApi::Error.new("message", {})
66
- error.ratelimit_limit.should == 0
53
+ context 'with X-RateLimit-Limit unset' do
54
+ let(:headers) { headerize({}) }
55
+ it { should be_nil }
67
56
  end
68
57
  end
69
58
 
70
- describe "#ratelimit_remaining" do
71
- it "returns the number set as x-rate-limit-remaining if it's set" do
72
- error = AngellistApi::Error.new("message", {'x-ratelimit-remaining' => 22, 'X-RateLimit-Remaining' => 23})
73
- error.ratelimit_remaining.should == 22
74
- end
59
+ describe '#ratelimit_remaining' do
60
+ subject { described_class.new('message', headers).ratelimit_remaining }
61
+
62
+ context 'with X-RateLimit-Remaining header set' do
63
+ let(:remaining) { '22' }
64
+ let(:headers) { headerize('X-RateLimit-Remaining' => remaining) }
75
65
 
76
- it "returns the number set as X-RateLimit-Remaining if x-rate-limit-remaining is empty" do
77
- error = AngellistApi::Error.new("message", {'x-ratelimit-remaining' => nil, 'X-RateLimit-Remaining' => 32})
78
- error.ratelimit_remaining.should == 32
66
+ it 'returns remaining request count as an integer' do
67
+ subject.should eq 22
68
+ end
79
69
  end
80
70
 
81
- it "returns 0 if both X-RateLimit-Remaining or x-rate-limit-remaining are not set" do
82
- error = AngellistApi::Error.new("message", {})
83
- error.ratelimit_remaining.should == 0
71
+ context 'with X-RateLimit-Remaining unset' do
72
+ let(:headers) { headerize({}) }
73
+ it { should be_nil }
84
74
  end
85
75
  end
86
76
 
87
- describe "#retry_after" do
88
- context "a normal Error object" do
89
- let(:error) { AngellistApi::Error.new("message", {}) }
77
+ # NOTE: AngelList doesn't actually support this yet...
78
+ describe '#retry_after' do
79
+ subject { described_class.new('message', headerize({})).retry_after }
80
+ let(:now) { Time.now }
81
+
82
+ before { Timecop.freeze(now) }
83
+ after { Timecop.return }
90
84
 
91
- it "give you the time in seconds when it is acceptable to make another call" do
92
- Timecop.freeze(Time.now) do
93
- error.should_receive(:ratelimit_reset) { Time.now + 3600 }
94
- error.retry_after.should == 3600
95
- end
85
+ context 'when limit reset time is known' do
86
+ let(:reset_at) { now + 3600 }
87
+
88
+ before do
89
+ described_class.any_instance.stub(:ratelimit_reset).and_return(reset_at)
96
90
  end
97
91
 
98
- it "return 0 if the ratelimit_reset time is in the past" do
99
- Timecop.freeze(Time.now) do
100
- error.should_receive(:ratelimit_reset) { Time.now - 3600 }
101
- error.retry_after.should == 0
102
- end
92
+ it 'gives time in seconds until it is acceptable to make another call' do
93
+ subject.should eq reset_at - now
103
94
  end
104
95
  end
105
96
 
106
- context "an EnhanceYourCalm Error object" do
107
- it "return the number of seconds set as retry-after if it's set" do
108
- error = AngellistApi::Error::EnhanceYourCalm.new("message", {'retry-after' => 12, 'Retry-After' => 13})
109
- error.retry_after.should == 12
110
- end
97
+ context 'when limit reset time is not known' do
98
+ it { should be_nil }
99
+ end
111
100
 
112
- it "return the number set as Retry-After if retry-after is empty" do
113
- error = AngellistApi::Error::EnhanceYourCalm.new("message", {'retry-after' => nil, 'Retry-After' => 16})
114
- error.retry_after.should == 16
115
- end
101
+ context 'when limit reset time is past' do
102
+ let(:reset_at) { now - 3600 }
116
103
 
117
- it "return 0 if both Retry-After or retry-after are not set" do
118
- error = AngellistApi::Error::EnhanceYourCalm.new("message", {})
119
- error.retry_after.should == 0
104
+ before do
105
+ described_class.any_instance.stub(:ratelimit_reset).and_return(reset_at)
120
106
  end
107
+
108
+ it { should be_zero }
109
+ end
110
+ end
111
+ end
112
+
113
+ # NOTE: AngelList doesn't actually support this yet...
114
+ describe AngellistApi::Error::TooManyRequests do
115
+ include HttpHeaders
116
+
117
+ subject { described_class.new('message', headers).retry_after }
118
+
119
+ context 'with Retry-After header set' do
120
+ let(:wait) { '12' }
121
+ let(:headers) { headerize('Retry-After' => wait) }
122
+
123
+ it 'returns the seconds to wait until retrying' do
124
+ subject.should == 12
121
125
  end
122
126
  end
127
+
128
+ context 'with Retry-After unset' do
129
+ let(:headers) { headerize({}) }
130
+ it { should be_nil }
131
+ end
123
132
  end
124
133
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: angellist_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-03-27 00:00:00.000000000 Z
13
+ date: 2013-04-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: hashie
@@ -203,12 +203,12 @@ files:
203
203
  - lib/angellist_api/core_ext/hash.rb
204
204
  - lib/angellist_api/error/bad_gateway.rb
205
205
  - lib/angellist_api/error/bad_request.rb
206
- - lib/angellist_api/error/enhance_your_calm.rb
207
206
  - lib/angellist_api/error/forbidden.rb
208
207
  - lib/angellist_api/error/internal_server_error.rb
209
208
  - lib/angellist_api/error/not_acceptable.rb
210
209
  - lib/angellist_api/error/not_found.rb
211
210
  - lib/angellist_api/error/service_unavailable.rb
211
+ - lib/angellist_api/error/too_many_requests.rb
212
212
  - lib/angellist_api/error/unauthorized.rb
213
213
  - lib/angellist_api/error.rb
214
214
  - lib/angellist_api/request/angellist_api_oauth.rb
@@ -224,6 +224,8 @@ files:
224
224
  - README.md
225
225
  - CHANGELOG.md
226
226
  - spec/fixtures/cassettes/activity_feeds.yml
227
+ - spec/fixtures/cassettes/errors/forbidden.yml
228
+ - spec/fixtures/cassettes/errors/too_many_requests.yml
227
229
  - spec/fixtures/cassettes/follows.yml
228
230
  - spec/fixtures/cassettes/jobs.yml
229
231
  - spec/fixtures/cassettes/messages.yml
@@ -237,6 +239,7 @@ files:
237
239
  - spec/fixtures/cassettes/tags.yml
238
240
  - spec/fixtures/cassettes/users.yml
239
241
  - spec/integration/activity_feeds_spec.rb
242
+ - spec/integration/errors_spec.rb
240
243
  - spec/integration/follows_spec.rb
241
244
  - spec/integration/jobs_spec.rb
242
245
  - spec/integration/messages_spec.rb
@@ -251,6 +254,7 @@ files:
251
254
  - spec/integration/users_spec.rb
252
255
  - spec/spec_helper.rb
253
256
  - spec/support/authentication.rb
257
+ - spec/support/http_headers.rb
254
258
  - spec/support/vcr.rb
255
259
  - spec/unit/lib/angellist_api/api_spec.rb
256
260
  - spec/unit/lib/angellist_api/authentication_spec.rb
@@ -285,7 +289,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
285
289
  version: '0'
286
290
  segments:
287
291
  - 0
288
- hash: 704109515206394600
292
+ hash: 74367016323838857
289
293
  required_rubygems_version: !ruby/object:Gem::Requirement
290
294
  none: false
291
295
  requirements:
@@ -294,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
298
  version: '0'
295
299
  segments:
296
300
  - 0
297
- hash: 704109515206394600
301
+ hash: 74367016323838857
298
302
  requirements: []
299
303
  rubyforge_project:
300
304
  rubygems_version: 1.8.25
@@ -303,6 +307,8 @@ specification_version: 3
303
307
  summary: Ruby wrapper for the Angellist API.
304
308
  test_files:
305
309
  - spec/fixtures/cassettes/activity_feeds.yml
310
+ - spec/fixtures/cassettes/errors/forbidden.yml
311
+ - spec/fixtures/cassettes/errors/too_many_requests.yml
306
312
  - spec/fixtures/cassettes/follows.yml
307
313
  - spec/fixtures/cassettes/jobs.yml
308
314
  - spec/fixtures/cassettes/messages.yml
@@ -316,6 +322,7 @@ test_files:
316
322
  - spec/fixtures/cassettes/tags.yml
317
323
  - spec/fixtures/cassettes/users.yml
318
324
  - spec/integration/activity_feeds_spec.rb
325
+ - spec/integration/errors_spec.rb
319
326
  - spec/integration/follows_spec.rb
320
327
  - spec/integration/jobs_spec.rb
321
328
  - spec/integration/messages_spec.rb
@@ -330,6 +337,7 @@ test_files:
330
337
  - spec/integration/users_spec.rb
331
338
  - spec/spec_helper.rb
332
339
  - spec/support/authentication.rb
340
+ - spec/support/http_headers.rb
333
341
  - spec/support/vcr.rb
334
342
  - spec/unit/lib/angellist_api/api_spec.rb
335
343
  - spec/unit/lib/angellist_api/authentication_spec.rb
@@ -1,13 +0,0 @@
1
- require 'angellist_api/error'
2
-
3
- module AngellistApi
4
- # Raised when AngellistApi returns the HTTP status code 420
5
- class Error::EnhanceYourCalm < AngellistApi::Error
6
- # The number of seconds your application should wait before requesting date
7
- # from the API again
8
- def retry_after
9
- @http_headers.values_at('retry-after', 'Retry-After').detect {|value| value }.to_i
10
- end
11
- end
12
- end
13
-