angellist_api 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG.md +15 -0
  2. data/README.md +11 -6
  3. data/Rakefile +9 -0
  4. data/lib/angellist_api.rb +1 -3
  5. data/lib/angellist_api/api.rb +2 -4
  6. data/lib/angellist_api/authentication.rb +0 -1
  7. data/lib/angellist_api/client.rb +9 -1
  8. data/lib/angellist_api/client/activity_feeds.rb +29 -8
  9. data/lib/angellist_api/client/follows.rb +105 -63
  10. data/lib/angellist_api/client/jobs.rb +72 -0
  11. data/lib/angellist_api/client/reviews.rb +17 -6
  12. data/lib/angellist_api/client/search.rb +31 -0
  13. data/lib/angellist_api/client/startup_roles.rb +23 -8
  14. data/lib/angellist_api/client/startups.rb +49 -13
  15. data/lib/angellist_api/client/status_updates.rb +45 -24
  16. data/lib/angellist_api/client/tags.rb +52 -30
  17. data/lib/angellist_api/client/users.rb +39 -17
  18. data/lib/angellist_api/configuration.rb +24 -29
  19. data/lib/angellist_api/connection.rb +27 -29
  20. data/lib/angellist_api/core_ext/hash.rb +19 -0
  21. data/lib/angellist_api/error.rb +9 -32
  22. data/lib/angellist_api/error/bad_gateway.rb +7 -0
  23. data/lib/angellist_api/error/bad_request.rb +7 -0
  24. data/lib/angellist_api/error/enhance_your_calm.rb +13 -0
  25. data/lib/angellist_api/error/forbidden.rb +7 -0
  26. data/lib/angellist_api/error/internal_server_error.rb +7 -0
  27. data/lib/angellist_api/error/not_acceptable.rb +7 -0
  28. data/lib/angellist_api/error/not_found.rb +7 -0
  29. data/lib/angellist_api/error/service_unavailable.rb +7 -0
  30. data/lib/angellist_api/error/unauthorized.rb +7 -0
  31. data/lib/angellist_api/request.rb +11 -18
  32. data/lib/angellist_api/request/angellist_api_oauth.rb +16 -0
  33. data/lib/angellist_api/request/gateway.rb +18 -0
  34. data/lib/angellist_api/request/multipart_with_file.rb +36 -0
  35. data/lib/angellist_api/response/raise_client_error.rb +51 -0
  36. data/lib/angellist_api/response/raise_server_error.rb +27 -0
  37. data/lib/angellist_api/version.rb +1 -1
  38. data/spec/fixtures/cassettes/activity_feeds.yml +273 -0
  39. data/spec/fixtures/cassettes/follows.yml +1722 -0
  40. data/spec/fixtures/cassettes/jobs.yml +2052 -0
  41. data/spec/fixtures/cassettes/reviews.yml +93 -0
  42. data/spec/fixtures/cassettes/search.yml +169 -0
  43. data/spec/fixtures/cassettes/startup_roles.yml +1481 -0
  44. data/spec/fixtures/cassettes/startups.yml +532 -0
  45. data/spec/fixtures/cassettes/status_updates.yml +125 -0
  46. data/spec/fixtures/cassettes/tags.yml +762 -0
  47. data/spec/fixtures/cassettes/users.yml +275 -0
  48. data/spec/integration/activity_feeds_spec.rb +29 -0
  49. data/spec/integration/follows_spec.rb +60 -0
  50. data/spec/integration/jobs_spec.rb +36 -0
  51. data/spec/integration/reviews_spec.rb +15 -0
  52. data/spec/integration/search_spec.rb +27 -0
  53. data/spec/integration/startup_roles_spec.rb +25 -0
  54. data/spec/integration/startups_spec.rb +36 -0
  55. data/spec/integration/status_updates_spec.rb +18 -0
  56. data/spec/integration/tags_spec.rb +29 -0
  57. data/spec/integration/users_spec.rb +23 -0
  58. data/spec/spec_helper.rb +5 -2
  59. data/spec/support/vcr.rb +12 -0
  60. data/spec/{lib → unit/lib}/angellist_api/api_spec.rb +13 -15
  61. data/spec/unit/lib/angellist_api/authentication_spec.rb +49 -0
  62. data/spec/unit/lib/angellist_api/client/activity_feeds_spec.rb +22 -0
  63. data/spec/unit/lib/angellist_api/client/follows_spec.rb +82 -0
  64. data/spec/unit/lib/angellist_api/client/jobs_spec.rb +39 -0
  65. data/spec/unit/lib/angellist_api/client/reviews_spec.rb +14 -0
  66. data/spec/unit/lib/angellist_api/client/search_spec.rb +15 -0
  67. data/spec/unit/lib/angellist_api/client/startup_roles_spec.rb +14 -0
  68. data/spec/unit/lib/angellist_api/client/startups_spec.rb +40 -0
  69. data/spec/unit/lib/angellist_api/client/status_updates_spec.rb +30 -0
  70. data/spec/unit/lib/angellist_api/client/tags_spec.rb +39 -0
  71. data/spec/unit/lib/angellist_api/client/users_spec.rb +39 -0
  72. data/spec/{lib → unit/lib}/angellist_api/configuration_spec.rb +12 -11
  73. data/spec/unit/lib/angellist_api/error_spec.rb +124 -0
  74. data/spec/unit/lib/angellist_api/request_spec.rb +32 -0
  75. data/spec/unit/lib/angellist_api_spec.rb +42 -0
  76. metadata +128 -71
  77. data/lib/faraday/request/angellist_api_oauth.rb +0 -14
  78. data/lib/faraday/request/gateway.rb +0 -18
  79. data/lib/faraday/request/multipart_with_file.rb +0 -36
  80. data/lib/faraday/request/phoenix.rb +0 -18
  81. data/lib/faraday/response/raise_http_4xx.rb +0 -45
  82. data/lib/faraday/response/raise_http_5xx.rb +0 -24
  83. data/spec/lib/angellist_api/authentication_spec.rb +0 -53
  84. data/spec/lib/angellist_api/client/activity_feeds_spec.rb +0 -16
  85. data/spec/lib/angellist_api/client/follows_spec.rb +0 -74
  86. data/spec/lib/angellist_api/client/reviews_spec.rb +0 -16
  87. data/spec/lib/angellist_api/client/startup_roles_spec.rb +0 -16
  88. data/spec/lib/angellist_api/client/startups_spec.rb +0 -23
  89. data/spec/lib/angellist_api/client/status_updates_spec.rb +0 -32
  90. data/spec/lib/angellist_api/client/tags_spec.rb +0 -40
  91. data/spec/lib/angellist_api/client/users_spec.rb +0 -30
  92. data/spec/lib/angellist_api/error_spec.rb +0 -126
  93. data/spec/lib/angellist_api/request_spec.rb +0 -51
  94. data/spec/lib/angellist_api_spec.rb +0 -38
@@ -3,42 +3,38 @@ require 'angellist_api/version'
3
3
  module AngellistApi
4
4
  # Defines constants and methods related to configuration
5
5
  module Configuration
6
- # An array of valid keys in the options hash when configuring a {AngellistApi::API}
7
- VALID_OPTIONS_KEYS = [
8
- :adapter,
9
- :endpoint,
10
- :format,
11
- :gateway,
12
- :access_token,
13
- :proxy,
14
- :user_agent,
15
- :faraday_options].freeze
6
+ # The access token if none is set
7
+ DEFAULT_ACCESS_TOKEN = nil
16
8
 
17
- # The adapter that will be used to connect if none is set
9
+ # The HTTP connection adapter that will be used to connect if none is set
18
10
  DEFAULT_ADAPTER = :net_http
19
11
 
20
- # The endpoint that will be used to connect if none is set
21
- DEFAULT_ENDPOINT = "https://api.angel.co/".freeze
12
+ # The Faraday connection options if none are set
13
+ DEFAULT_CONNECTION_OPTIONS = {}
22
14
 
23
- # The response format appended to the path and sent in the 'Accept' header if none is set
24
- #
25
- # @note JSON is preferred over XML because it is more concise and faster to parse.
26
- DEFAULT_FORMAT = :json
15
+ # The endpoint that will be used to connect if none is set
16
+ DEFAULT_ENDPOINT = "https://api.angel.co/"
27
17
 
28
- # By default, don't set a user oauth access token
29
- DEFAULT_ACCESS_TOKEN = nil
18
+ # The gateway server if none is set
19
+ DEFAULT_GATEWAY = nil
30
20
 
31
- # By default, don't use a proxy server
21
+ # The proxy server if none is set
32
22
  DEFAULT_PROXY = nil
33
23
 
34
24
  # The value sent in the 'User-Agent' header if none is set
35
- DEFAULT_USER_AGENT = "AngellistApi Ruby Gem #{AngellistApi::VERSION}".freeze
25
+ DEFAULT_USER_AGENT = "AngellistApi Ruby Gem #{AngellistApi::VERSION}"
36
26
 
37
- DEFAULT_GATEWAY = nil
38
-
39
- DEFAULT_FARADAY_OPTIONS = {}.freeze
27
+ # An array of valid keys in the options hash when configuring a {AngellistApi::API}
28
+ VALID_OPTIONS_KEYS = [
29
+ :adapter,
30
+ :connection_options,
31
+ :endpoint,
32
+ :gateway,
33
+ :access_token,
34
+ :proxy,
35
+ :user_agent
36
+ ]
40
37
 
41
- # @private
42
38
  attr_accessor *VALID_OPTIONS_KEYS
43
39
 
44
40
  # When this module is extended, set all configuration options to their default values
@@ -60,14 +56,13 @@ module AngellistApi
60
56
 
61
57
  # Reset all configuration options to defaults
62
58
  def reset
59
+ self.access_token = DEFAULT_ACCESS_TOKEN
63
60
  self.adapter = DEFAULT_ADAPTER
61
+ self.connection_options = DEFAULT_CONNECTION_OPTIONS
64
62
  self.endpoint = DEFAULT_ENDPOINT
65
- self.format = DEFAULT_FORMAT
66
- self.access_token = DEFAULT_ACCESS_TOKEN
63
+ self.gateway = DEFAULT_GATEWAY
67
64
  self.proxy = DEFAULT_PROXY
68
65
  self.user_agent = DEFAULT_USER_AGENT
69
- self.gateway = DEFAULT_GATEWAY
70
- self.faraday_options = DEFAULT_FARADAY_OPTIONS
71
66
  self
72
67
  end
73
68
  end
@@ -1,48 +1,46 @@
1
+ require 'faraday'
1
2
  require 'faraday_middleware'
2
- require 'faraday/request/phoenix'
3
- require 'faraday/request/multipart_with_file'
4
- require 'faraday/request/gateway'
5
- require 'faraday/request/angellist_api_oauth'
6
- require 'faraday/response/raise_http_4xx'
7
- require 'faraday/response/raise_http_5xx'
3
+ require 'angellist_api/core_ext/hash'
4
+ require 'angellist_api/request/multipart_with_file'
5
+ require 'angellist_api/request/gateway'
6
+ require 'angellist_api/request/angellist_api_oauth'
7
+ require 'angellist_api/response/raise_client_error'
8
+ require 'angellist_api/response/raise_server_error'
8
9
 
9
10
  module AngellistApi
10
- # @private
11
11
  module Connection
12
12
  private
13
13
 
14
+ # Returns a Faraday::Connection object
15
+ #
16
+ # @param options [Hash] A hash of options
17
+ # @return [Faraday::Connection]
14
18
  def connection(options={})
15
- merged_options = faraday_options.merge({
19
+ default_options = {
16
20
  :headers => {
17
- 'Accept' => "application/#{format}",
18
- 'User-Agent' => user_agent
21
+ :accept => 'application/json',
22
+ :user_agent => user_agent
19
23
  },
20
24
  :proxy => proxy,
21
25
  :ssl => {:verify => false},
22
26
  :url => options.fetch(:endpoint, api_endpoint)
23
- })
24
-
25
- Faraday.new(merged_options) do |builder|
26
- builder.use Faraday::Request::Phoenix if options[:phoenix]
27
- builder.use Faraday::Request::MultipartWithFile
28
- builder.use Faraday::Request::AngellistApiOAuth, authentication if authenticated?
27
+ }
28
+
29
+ @connection ||= Faraday.new(connection_options.deep_merge(default_options)) do |builder|
30
+ builder.use AngellistApi::Request::MultipartWithFile
31
+ builder.use AngellistApi::Request::AngellistApiOAuth, authentication if authenticated?
29
32
  builder.use Faraday::Request::Multipart
30
33
  builder.use Faraday::Request::UrlEncoded
31
- builder.use Faraday::Request::Gateway, gateway if gateway
32
- builder.use Faraday::Response::RaiseHttp4xx
33
- unless options[:raw]
34
- case options.fetch(:format, format).to_s.downcase
35
- when 'json', 'phoenix'
36
- builder.use Faraday::Response::Mashify
37
- builder.use Faraday::Response::ParseJson
38
- when 'xml'
39
- builder.use Faraday::Response::Mashify
40
- builder.use Faraday::Response::ParseXml
41
- end
42
- end
43
- builder.use Faraday::Response::RaiseHttp5xx
34
+ builder.use AngellistApi::Request::Gateway, gateway if gateway
35
+ builder.use AngellistApi::Response::RaiseClientError
36
+ builder.use Faraday::Response::Mashify
37
+ builder.use Faraday::Response::ParseJson
38
+ builder.use AngellistApi::Response::RaiseServerError
44
39
  builder.adapter(adapter)
45
40
  end
41
+
42
+ @connection
46
43
  end
47
44
  end
48
45
  end
46
+
@@ -0,0 +1,19 @@
1
+ class Hash
2
+
3
+ # Merges self with another hash, recursively
4
+ #
5
+ # @param hash [Hash] The hash to merge
6
+ # @return [Hash]
7
+ def deep_merge(hash)
8
+ target = self.dup
9
+ hash.keys.each do |key|
10
+ if hash[key].is_a?(Hash) && self[key].is_a?(Hash)
11
+ target[key] = target[key].deep_merge(hash[key])
12
+ next
13
+ end
14
+ target[key] = hash[key]
15
+ end
16
+ target
17
+ end
18
+ end
19
+
@@ -3,57 +3,34 @@ module AngellistApi
3
3
  class Error < StandardError
4
4
  attr_reader :http_headers
5
5
 
6
+ # Initializes new Error object
7
+ #
8
+ # @param [String] message
9
+ # @param [Hash] http_headers
10
+ # @return [AngellistApi::Error]
6
11
  def initialize(message, http_headers)
7
12
  @http_headers = Hash[http_headers]
8
13
  super message
9
14
  end
10
15
 
16
+ # @return [Time]
11
17
  def ratelimit_reset
12
18
  Time.at(@http_headers.values_at('x-ratelimit-reset', 'X-RateLimit-Reset').detect{|value| value}.to_i)
13
19
  end
14
20
 
21
+ # @return [Integer]
15
22
  def ratelimit_limit
16
23
  @http_headers.values_at('x-ratelimit-limit', 'X-RateLimit-Limit').detect{|value| value}.to_i
17
24
  end
18
25
 
26
+ # @return [Integer]
19
27
  def ratelimit_remaining
20
28
  @http_headers.values_at('x-ratelimit-remaining', 'X-RateLimit-Remaining').detect{|value| value}.to_i
21
29
  end
22
30
 
31
+ # @return [Integer]
23
32
  def retry_after
24
33
  [(ratelimit_reset - Time.now).ceil, 0].max
25
34
  end
26
35
  end
27
-
28
- # Raised when AngellistApi returns the HTTP status code 400
29
- class BadRequest < Error; end
30
-
31
- # Raised when AngellistApi returns the HTTP status code 401
32
- class Unauthorized < Error; end
33
-
34
- # Raised when AngellistApi returns the HTTP status code 403
35
- class Forbidden < Error; end
36
-
37
- # Raised when AngellistApi returns the HTTP status code 404
38
- class NotFound < Error; end
39
-
40
- # Raised when AngellistApi returns the HTTP status code 406
41
- class NotAcceptable < Error; end
42
-
43
- # Raised when AngellistApi returns the HTTP status code 420
44
- class EnhanceYourCalm < Error
45
- # The number of seconds your application should wait before requesting date from the API again
46
- def retry_after
47
- @http_headers.values_at('retry-after', 'Retry-After').detect {|value| value }.to_i
48
- end
49
- end
50
-
51
- # Raised when AngellistApi returns the HTTP status code 500
52
- class InternalServerError < Error; end
53
-
54
- # Raised when AngellistApi returns the HTTP status code 502
55
- class BadGateway < Error; end
56
-
57
- # Raised when AngellistApi returns the HTTP status code 503
58
- class ServiceUnavailable < Error; end
59
36
  end
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 502
5
+ class Error::BadGateway < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 400
5
+ class Error::BadRequest < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,13 @@
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
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 403
5
+ class Error::Forbidden < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 500
5
+ class Error::InternalServerError < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 406
5
+ class Error::NotAcceptable < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 404
5
+ class Error::NotFound < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 503
5
+ class Error::ServiceUnavailable < AngellistApi::Error; end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'angellist_api/error'
2
+
3
+ module AngellistApi
4
+ # Raised when AngellistApi returns the HTTP status code 401
5
+ class Error::Unauthorized < AngellistApi::Error; end
6
+ end
7
+
@@ -1,6 +1,11 @@
1
1
  module AngellistApi
2
2
  # Defines HTTP request methods
3
3
  module Request
4
+ # Perform an HTTP DELETE request
5
+ def delete(path, params={}, options={})
6
+ request(:delete, path, params, options)
7
+ end
8
+
4
9
  # Perform an HTTP GET request
5
10
  def get(path, params={}, options={})
6
11
  request(:get, path, params, options)
@@ -10,34 +15,22 @@ module AngellistApi
10
15
  request(:post, path, params, options)
11
16
  end
12
17
 
13
- # Perform an HTTP PUT request
14
- def put(path, params={}, options={})
15
- request(:put, path, params, options)
16
- end
17
-
18
- # Perform an HTTP DELETE request
19
- def delete(path, params={}, options={})
20
- request(:delete, path, params, options)
21
- end
22
-
23
18
  private
24
19
 
25
20
  # Perform an HTTP request
26
21
  def request(method, path, params, options)
27
- response = connection(options).send(method) do |request|
22
+ response = connection(options).run_request(method, nil, nil, nil) do |request|
23
+ request.options[:raw] = true if options[:raw]
28
24
  case method.to_sym
29
- when :get, :delete
30
- request.url(formatted_path(path, options), params)
31
- when :post, :put
32
- request.path = formatted_path(path, options)
25
+ when :delete, :get
26
+ request.url(path, params)
27
+ when :post
28
+ request.path = path
33
29
  request.body = params unless params.empty?
34
30
  end
35
31
  end
36
32
  options[:raw] ? response : response.body
37
33
  end
38
34
 
39
- def formatted_path(path, options={})
40
- path
41
- end
42
35
  end
43
36
  end
@@ -0,0 +1,16 @@
1
+ require 'faraday'
2
+
3
+ module AngellistApi
4
+ module Request
5
+ class AngellistApiOAuth < Faraday::Middleware
6
+ def call(env)
7
+ env[:request_headers]['Authorization'] = "Bearer #{@options[:access_token]}" if @options[:access_token]
8
+ @app.call(env)
9
+ end
10
+
11
+ def initialize(app, options)
12
+ @app, @options = app, options
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ require 'faraday'
2
+
3
+ module AngellistApi
4
+ module Request
5
+ class Gateway < Faraday::Middleware
6
+ def call(env)
7
+ url = env[:url].dup
8
+ url.host = @gateway
9
+ env[:url] = url
10
+ @app.call(env)
11
+ end
12
+
13
+ def initialize(app, gateway)
14
+ @app, @gateway = app, gateway
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ require 'faraday'
2
+
3
+ module AngellistApi
4
+ module Request
5
+ class MultipartWithFile < Faraday::Middleware
6
+ def call(env)
7
+ if env[:body].is_a?(Hash)
8
+ env[:body].each do |key, value|
9
+ if value.is_a?(File)
10
+ env[:body][key] = Faraday::UploadIO.new(value, mime_type(value.path), value.path)
11
+ elsif value.is_a?(Hash) && (value['io'].is_a?(IO) || value['io'].is_a?(StringIO))
12
+ env[:body][key] = Faraday::UploadIO.new(value['io'], mime_type('.'+value['type']), '')
13
+ end
14
+ end
15
+ end
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ private
21
+
22
+ def mime_type(path)
23
+ case path
24
+ when /\.jpe?g/i
25
+ 'image/jpeg'
26
+ when /\.gif$/i
27
+ 'image/gif'
28
+ when /\.png$/i
29
+ 'image/png'
30
+ else
31
+ 'application/octet-stream'
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end