localist-instagvram 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.yardopts +9 -0
- data/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/README.md +144 -0
- data/Rakefile +22 -0
- data/instagram.gemspec +42 -0
- data/lib/faraday/oauth2.rb +36 -0
- data/lib/faraday/raise_http_4xx.rb +37 -0
- data/lib/faraday/raise_http_5xx.rb +29 -0
- data/lib/instagram.rb +26 -0
- data/lib/instagram/api.rb +23 -0
- data/lib/instagram/client.rb +19 -0
- data/lib/instagram/client/comments.rb +62 -0
- data/lib/instagram/client/likes.rb +58 -0
- data/lib/instagram/client/locations.rb +59 -0
- data/lib/instagram/client/media.rb +63 -0
- data/lib/instagram/client/real_time.rb +8 -0
- data/lib/instagram/client/subscriptions.rb +156 -0
- data/lib/instagram/client/tags.rb +59 -0
- data/lib/instagram/client/users.rb +165 -0
- data/lib/instagram/client/utils.rb +15 -0
- data/lib/instagram/configuration.rb +90 -0
- data/lib/instagram/connection.rb +31 -0
- data/lib/instagram/error.rb +19 -0
- data/lib/instagram/oauth.rb +27 -0
- data/lib/instagram/request.rb +45 -0
- data/lib/instagram/version.rb +3 -0
- data/spec/faraday/response_spec.rb +28 -0
- data/spec/fixtures/access_token.json +9 -0
- data/spec/fixtures/followed_by.json +1 -0
- data/spec/fixtures/follows.json +1 -0
- data/spec/fixtures/location.json +1 -0
- data/spec/fixtures/location_recent_media.json +1 -0
- data/spec/fixtures/location_search.json +1 -0
- data/spec/fixtures/media.json +1 -0
- data/spec/fixtures/media_comment.json +1 -0
- data/spec/fixtures/media_comment_deleted.json +1 -0
- data/spec/fixtures/media_comments.json +1 -0
- data/spec/fixtures/media_liked.json +1 -0
- data/spec/fixtures/media_likes.json +1 -0
- data/spec/fixtures/media_popular.json +1 -0
- data/spec/fixtures/media_search.json +1 -0
- data/spec/fixtures/media_unliked.json +1 -0
- data/spec/fixtures/mikeyk.json +1 -0
- data/spec/fixtures/recent_media.json +1 -0
- data/spec/fixtures/requested_by.json +12 -0
- data/spec/fixtures/shayne.json +1 -0
- data/spec/fixtures/subscription.json +12 -0
- data/spec/fixtures/subscription_deleted.json +1 -0
- data/spec/fixtures/subscription_payload.json +14 -0
- data/spec/fixtures/subscriptions.json +22 -0
- data/spec/fixtures/tag.json +1 -0
- data/spec/fixtures/tag_recent_media.json +1 -0
- data/spec/fixtures/tag_search.json +1 -0
- data/spec/fixtures/user_media_feed.json +1 -0
- data/spec/fixtures/user_search.json +1 -0
- data/spec/instagram/api_spec.rb +110 -0
- data/spec/instagram/client/comments_spec.rb +71 -0
- data/spec/instagram/client/likes_spec.rb +66 -0
- data/spec/instagram/client/locations_spec.rb +78 -0
- data/spec/instagram/client/media_spec.rb +78 -0
- data/spec/instagram/client/real_time_spec.rb +13 -0
- data/spec/instagram/client/subscriptions_spec.rb +148 -0
- data/spec/instagram/client/tags_spec.rb +78 -0
- data/spec/instagram/client/users_spec.rb +237 -0
- data/spec/instagram/client_spec.rb +23 -0
- data/spec/instagram_spec.rb +97 -0
- data/spec/spec_helper.rb +54 -0
- metadata +307 -0
@@ -0,0 +1,165 @@
|
|
1
|
+
module Instagram
|
2
|
+
class Client
|
3
|
+
# Defines methods related to users
|
4
|
+
module Users
|
5
|
+
# Returns extended information of a given user
|
6
|
+
#
|
7
|
+
# @overload user(id=nil, options={})
|
8
|
+
# @param user [Integer] An Instagram user ID
|
9
|
+
# @return [Hashie::Mash] The requested user.
|
10
|
+
# @example Return extended information for @shayne
|
11
|
+
# Instagram.user(20)
|
12
|
+
# @format :json
|
13
|
+
# @authenticated false unless requesting it from a protected user
|
14
|
+
#
|
15
|
+
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
|
16
|
+
# @rate_limited true
|
17
|
+
# @see TODO:docs url
|
18
|
+
def user(*args)
|
19
|
+
id = args.first || 'self'
|
20
|
+
response = get("users/#{id}")
|
21
|
+
response["data"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns users that match the given query
|
25
|
+
#
|
26
|
+
# @format :json
|
27
|
+
# @authenticated false
|
28
|
+
# @rate_limited true
|
29
|
+
# @param query [String] The search query to run against user search.
|
30
|
+
# @param options [Hash] A customizable set of options.
|
31
|
+
# @option options [Integer] :count The number of users to retrieve. Maxiumum of 100 allowed per page.
|
32
|
+
# @return [Array]
|
33
|
+
# @see TODO:doc url
|
34
|
+
# @example Return users that match "Shayne Sweeney"
|
35
|
+
# Instagram.user_search("Shayne Sweeney")
|
36
|
+
def user_search(query, options={})
|
37
|
+
response = get('users/search', options.merge(:q => query))
|
38
|
+
response["data"]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a list of users whom a given user follows
|
42
|
+
#
|
43
|
+
# @overload follows(id=nil, options={})
|
44
|
+
# @param options [Hash] A customizable set of options.
|
45
|
+
# @return [Hashie::Mash]
|
46
|
+
# @example Returns a list of users the authenticated user follows
|
47
|
+
# Instagram.user_follows
|
48
|
+
# @overload follows(id=nil, options={})
|
49
|
+
# @param user [Integer] An Instagram user ID.
|
50
|
+
# @param options [Hash] A customizable set of options.
|
51
|
+
# @option options [Integer] :cursor (nil) Breaks the results into pages. Provide values as returned in the response objects's next_cursor attribute to page forward in the list.
|
52
|
+
# @option options [Integer] :count (nil) Limits the number of results returned per page, maximum 150.
|
53
|
+
# @return [Hashie::Mash]
|
54
|
+
# @example Return a list of users @mikeyk follows
|
55
|
+
# Instagram.user_follows(4) # @mikeyk user ID being 4
|
56
|
+
# @see TODO:docs url
|
57
|
+
# @format :json
|
58
|
+
# @authenticated false unless requesting it from a protected user
|
59
|
+
#
|
60
|
+
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
|
61
|
+
# @rate_limited true
|
62
|
+
def user_follows(*args)
|
63
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
64
|
+
id = args.first || "self"
|
65
|
+
response = get("users/#{id}/follows", options)
|
66
|
+
response["data"]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a list of users whom a given user is followed by
|
71
|
+
#
|
72
|
+
# @overload user_followed_by(id=nil, options={})
|
73
|
+
# @param options [Hash] A customizable set of options.
|
74
|
+
# @return [Hashie::Mash]
|
75
|
+
# @example Returns a list of users the authenticated user is followed by
|
76
|
+
# Instagram.user_followed_by
|
77
|
+
# @overload user_followed_by(id=nil, options={})
|
78
|
+
# @param user [Integer] An Instagram user ID.
|
79
|
+
# @param options [Hash] A customizable set of options.
|
80
|
+
# @option options [Integer] :cursor (nil) Breaks the results into pages. Provide values as returned in the response objects's next_cursor attribute to page forward in the list.
|
81
|
+
# @option options [Integer] :count (nil) Limits the number of results returned per page, maximum 150.
|
82
|
+
# @return [Hashie::Mash]
|
83
|
+
# @example Return a list of users @mikeyk is followed by
|
84
|
+
# Instagram.user_followed_by(4) # @mikeyk user ID being 4
|
85
|
+
# @see TODO:docs url
|
86
|
+
# @format :json
|
87
|
+
# @authenticated false unless requesting it from a protected user
|
88
|
+
#
|
89
|
+
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
|
90
|
+
# @rate_limited true
|
91
|
+
def user_followed_by(*args)
|
92
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
93
|
+
id = args.first || "self"
|
94
|
+
response = get("users/#{id}/followed-by", options)
|
95
|
+
response["data"]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns a list of users whom a given user is followed by
|
99
|
+
#
|
100
|
+
# @overload user_requested_by()
|
101
|
+
# @param options [Hash] A customizable set of options.
|
102
|
+
# @return [Hashie::Mash]
|
103
|
+
# @example Returns a list of users awaiting approval of a ollow request, for the authenticated user
|
104
|
+
# Instagram.user_requested_by
|
105
|
+
# @overload user_requested_by()
|
106
|
+
# @return [Hashie::Mash]
|
107
|
+
# @example Return a list of users who have requested to follow the authenticated user
|
108
|
+
# Instagram.user_requested_by()
|
109
|
+
# @see TODO:docs url
|
110
|
+
# @format :json
|
111
|
+
# @authenticated truei
|
112
|
+
# @rate_limited true
|
113
|
+
def user_requested_by()
|
114
|
+
response = get("users/self/requested-by")
|
115
|
+
response["data"]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the 20 most recent media items from the currently authorized user's feed.
|
119
|
+
#
|
120
|
+
# @overload user_media_feed(options={})
|
121
|
+
# @param options [Hash] A customizable set of options.
|
122
|
+
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
|
123
|
+
# @option options [Integer] :count Specifies the number of records to retrieve, per page. Must be less than or equal to 100.
|
124
|
+
# @return [Array]
|
125
|
+
# @example Return the 20 most recent media images that would appear on @shayne's feed
|
126
|
+
# Instagram.user_media_feed() # assuming @shayne is the authorized user
|
127
|
+
# @format :json
|
128
|
+
# @authenticated true
|
129
|
+
# @rate_limited true
|
130
|
+
# @see TODO:docs URL
|
131
|
+
def user_media_feed(*args)
|
132
|
+
options = args.first.is_a?(Hash) ? args.pop : {}
|
133
|
+
response = get('users/self/feed', options)
|
134
|
+
response["data"]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns a list of recent media items for a given user
|
138
|
+
#
|
139
|
+
# @overload user_recent_media(id=nil, options={})
|
140
|
+
# @param options [Hash] A customizable set of options.
|
141
|
+
# @return [Hashie::Mash]
|
142
|
+
# @example Returns a list of recent media items for the currently authenticated user
|
143
|
+
# Instagram.user_recent_media
|
144
|
+
# @overload user_recent_media(id=nil, options={})
|
145
|
+
# @param user [Integer] An Instagram user ID.
|
146
|
+
# @param options [Hash] A customizable set of options.
|
147
|
+
# @option options [Integer] :max_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
|
148
|
+
# @option options [Integer] :count (nil) Limits the number of results returned per page, maximum 150.
|
149
|
+
# @return [Hashie::Mash]
|
150
|
+
# @example Return a list of media items taken by @mikeyk
|
151
|
+
# Instagram.user_recent_media(4) # @mikeyk user ID being 4
|
152
|
+
# @see TODO:docs url
|
153
|
+
# @format :json
|
154
|
+
# @authenticated false unless requesting it from a protected user
|
155
|
+
#
|
156
|
+
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
|
157
|
+
# @rate_limited true
|
158
|
+
def user_recent_media(*args)
|
159
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
160
|
+
id = args.first || "self"
|
161
|
+
response = get("users/#{id}/media/recent", options)
|
162
|
+
response["data"]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Instagram
|
2
|
+
class Client
|
3
|
+
# @private
|
4
|
+
module Utils
|
5
|
+
private
|
6
|
+
|
7
|
+
# Returns the configured user name or the user name of the authenticated user
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
def get_username
|
11
|
+
@user_name ||= self.user.username
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require File.expand_path('../version', __FILE__)
|
3
|
+
|
4
|
+
module Instagram
|
5
|
+
# Defines constants and methods related to configuration
|
6
|
+
module Configuration
|
7
|
+
# An array of valid keys in the options hash when configuring a {Instagram::API}
|
8
|
+
VALID_OPTIONS_KEYS = [
|
9
|
+
:adapter,
|
10
|
+
:client_id,
|
11
|
+
:client_secret,
|
12
|
+
:access_token,
|
13
|
+
:endpoint,
|
14
|
+
:format,
|
15
|
+
:user_agent,
|
16
|
+
:proxy
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
# An array of valid request/response formats
|
20
|
+
#
|
21
|
+
# @note Not all methods support the XML format.
|
22
|
+
VALID_FORMATS = [
|
23
|
+
:json].freeze
|
24
|
+
|
25
|
+
# The adapter that will be used to connect if none is set
|
26
|
+
#
|
27
|
+
# @note The default faraday adapter is Net::HTTP.
|
28
|
+
DEFAULT_ADAPTER = Faraday.default_adapter
|
29
|
+
|
30
|
+
# By default, don't set an application ID
|
31
|
+
DEFAULT_CLIENT_ID = nil
|
32
|
+
|
33
|
+
# By default, don't set an application secret
|
34
|
+
DEFAULT_CLIENT_SECRET = nil
|
35
|
+
|
36
|
+
# By default, don't set an application redirect uri
|
37
|
+
DEFAULT_REDIRECT_URI = nil
|
38
|
+
|
39
|
+
# By default, don't set a user access token
|
40
|
+
DEFAULT_ACCESS_TOKEN = nil
|
41
|
+
|
42
|
+
# The endpoint that will be used to connect if none is set
|
43
|
+
#
|
44
|
+
# @note There is no reason to use any other endpoint at this time
|
45
|
+
DEFAULT_ENDPOINT = 'https://api.instagram.com/v1/'.freeze
|
46
|
+
|
47
|
+
# The response format appended to the path and sent in the 'Accept' header if none is set
|
48
|
+
#
|
49
|
+
# @note JSON is the only available format at this time
|
50
|
+
DEFAULT_FORMAT = :json
|
51
|
+
|
52
|
+
# By default, don't use a proxy server
|
53
|
+
DEFAULT_PROXY = nil
|
54
|
+
|
55
|
+
# The user agent that will be sent to the API endpoint if none is set
|
56
|
+
DEFAULT_USER_AGENT = "Instagram Ruby Gem #{Instagram::VERSION}".freeze
|
57
|
+
|
58
|
+
# @private
|
59
|
+
attr_accessor *VALID_OPTIONS_KEYS
|
60
|
+
|
61
|
+
# When this module is extended, set all configuration options to their default values
|
62
|
+
def self.extended(base)
|
63
|
+
base.reset
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convenience method to allow configuration options to be set in a block
|
67
|
+
def configure
|
68
|
+
yield self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create a hash of options and their values
|
72
|
+
def options
|
73
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
74
|
+
option.merge!(key => send(key))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Reset all configuration options to defaults
|
79
|
+
def reset
|
80
|
+
self.adapter = DEFAULT_ADAPTER
|
81
|
+
self.client_id = DEFAULT_CLIENT_ID
|
82
|
+
self.client_secret = DEFAULT_CLIENT_SECRET
|
83
|
+
self.access_token = DEFAULT_ACCESS_TOKEN
|
84
|
+
self.endpoint = DEFAULT_ENDPOINT
|
85
|
+
self.format = DEFAULT_FORMAT
|
86
|
+
self.user_agent = DEFAULT_USER_AGENT
|
87
|
+
self.proxy = DEFAULT_PROXY
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'faraday_middleware'
|
2
|
+
Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
|
3
|
+
|
4
|
+
module Instagram
|
5
|
+
# @private
|
6
|
+
module Connection
|
7
|
+
private
|
8
|
+
|
9
|
+
def connection(raw=false)
|
10
|
+
options = {
|
11
|
+
:headers => {'Accept' => "application/#{format}; charset=utf-8", 'User-Agent' => user_agent},
|
12
|
+
:proxy => proxy,
|
13
|
+
:ssl => {:verify => false},
|
14
|
+
:url => endpoint,
|
15
|
+
}
|
16
|
+
|
17
|
+
Faraday::Connection.new(options) do |connection|
|
18
|
+
connection.use Faraday::Request::OAuth2, client_id, access_token
|
19
|
+
connection.adapter(adapter)
|
20
|
+
connection.use Faraday::Response::RaiseHttp5xx
|
21
|
+
unless raw
|
22
|
+
case format.to_s.downcase
|
23
|
+
when 'json' then connection.use Faraday::Response::ParseJson
|
24
|
+
end
|
25
|
+
end
|
26
|
+
connection.use Faraday::Response::RaiseHttp4xx
|
27
|
+
connection.use Faraday::Response::Mashify unless raw
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Instagram
|
2
|
+
# Custom error class for rescuing from all Instagram errors
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# Raised when Instagram returns the HTTP status code 400
|
6
|
+
class BadRequest < Error; end
|
7
|
+
|
8
|
+
# Raised when Instagram returns the HTTP status code 404
|
9
|
+
class NotFound < Error; end
|
10
|
+
|
11
|
+
# Raised when Instagram returns the HTTP status code 500
|
12
|
+
class InternalServerError < Error; end
|
13
|
+
|
14
|
+
# Raised when Instagram returns the HTTP status code 503
|
15
|
+
class ServiceUnavailable < Error; end
|
16
|
+
|
17
|
+
# Raised when a subscription payload hash is invalid
|
18
|
+
class InvalidSignature < Error; end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Instagram
|
2
|
+
# Defines HTTP request methods
|
3
|
+
module OAuth
|
4
|
+
# Return URL for OAuth authorization
|
5
|
+
def authorize_url(options={})
|
6
|
+
options[:response_type] ||= "code"
|
7
|
+
params = access_token_params.merge(options)
|
8
|
+
connection.build_url("/oauth/authorize/", params).to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return an access token from authorization
|
12
|
+
def get_access_token(code, options={})
|
13
|
+
options[:grant_type] ||= "authorization_code"
|
14
|
+
params = access_token_params.merge(options)
|
15
|
+
post("/oauth/access_token/", params.merge(:code => code), raw=false, unformatted=true)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def access_token_params
|
21
|
+
{
|
22
|
+
:client_id => client_id,
|
23
|
+
:client_secret => client_secret
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Instagram
|
2
|
+
# Defines HTTP request methods
|
3
|
+
module Request
|
4
|
+
# Perform an HTTP GET request
|
5
|
+
def get(path, options={}, raw=false, unformatted=false)
|
6
|
+
request(:get, path, options, raw, unformatted)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Perform an HTTP POST request
|
10
|
+
def post(path, options={}, raw=false, unformatted=false)
|
11
|
+
request(:post, path, options, raw, unformatted)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Perform an HTTP PUT request
|
15
|
+
def put(path, options={}, raw=false, unformatted=false)
|
16
|
+
request(:put, path, options, raw, unformatted)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Perform an HTTP DELETE request
|
20
|
+
def delete(path, options={}, raw=false, unformatted=false)
|
21
|
+
request(:delete, path, options, raw, unformatted)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Perform an HTTP request
|
27
|
+
def request(method, path, options, raw=false, unformatted=false)
|
28
|
+
response = connection(raw).send(method) do |request|
|
29
|
+
path = formatted_path(path) unless unformatted
|
30
|
+
case method
|
31
|
+
when :get, :delete
|
32
|
+
request.url(path, options)
|
33
|
+
when :post, :put
|
34
|
+
request.path = path
|
35
|
+
request.body = options unless options.empty?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
raw ? response : response.body
|
39
|
+
end
|
40
|
+
|
41
|
+
def formatted_path(path)
|
42
|
+
[path, format].compact.join('.')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Faraday::Response do
|
4
|
+
before do
|
5
|
+
@client = Instagram::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
{
|
9
|
+
400 => Instagram::BadRequest,
|
10
|
+
404 => Instagram::NotFound,
|
11
|
+
500 => Instagram::InternalServerError,
|
12
|
+
503 => Instagram::ServiceUnavailable
|
13
|
+
}.each do |status, exception|
|
14
|
+
context "when HTTP status is #{status}" do
|
15
|
+
|
16
|
+
before do
|
17
|
+
stub_get('users/self/feed.json').
|
18
|
+
to_return(:status => status)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should raise #{exception.name} error" do
|
22
|
+
lambda do
|
23
|
+
@client.user_media_feed()
|
24
|
+
end.should raise_error(exception)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|