instagram 0.11.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/LICENSE.md +27 -17
- data/PATENTS.md +23 -0
- data/README.md +129 -46
- data/instagram.gemspec +18 -6
- data/lib/faraday/raise_http_exception.rb +4 -0
- data/lib/instagram/api.rb +8 -0
- data/lib/instagram/client.rb +2 -2
- data/lib/instagram/client/comments.rb +3 -3
- data/lib/instagram/client/embedding.rb +17 -0
- data/lib/instagram/client/likes.rb +3 -3
- data/lib/instagram/client/locations.rb +6 -3
- data/lib/instagram/client/media.rb +3 -3
- data/lib/instagram/client/subscriptions.rb +57 -2
- data/lib/instagram/client/tags.rb +6 -6
- data/lib/instagram/client/users.rb +15 -15
- data/lib/instagram/client/utils.rb +13 -0
- data/lib/instagram/configuration.rb +37 -25
- data/lib/instagram/connection.rb +1 -1
- data/lib/instagram/error.rb +6 -0
- data/lib/instagram/oauth.rb +2 -0
- data/lib/instagram/request.rb +4 -4
- data/lib/instagram/version.rb +1 -1
- data/spec/faraday/response_spec.rb +30 -0
- data/spec/instagram/api_spec.rb +97 -21
- data/spec/instagram/client/locations_spec.rb +24 -0
- data/spec/instagram/client/subscriptions_spec.rb +8 -5
- data/spec/instagram/client/tags_spec.rb +1 -0
- data/spec/instagram/client_spec.rb +1 -1
- metadata +23 -9
data/lib/instagram/connection.rb
CHANGED
@@ -11,7 +11,7 @@ module Instagram
|
|
11
11
|
:headers => {'Accept' => "application/#{format}; charset=utf-8", 'User-Agent' => user_agent},
|
12
12
|
:proxy => proxy,
|
13
13
|
:url => endpoint,
|
14
|
-
}
|
14
|
+
}.merge(connection_options)
|
15
15
|
|
16
16
|
Faraday::Connection.new(options) do |connection|
|
17
17
|
connection.use FaradayMiddleware::InstagramOAuth2, client_id, access_token
|
data/lib/instagram/error.rb
CHANGED
@@ -11,9 +11,15 @@ module Instagram
|
|
11
11
|
# Raised when Instagram returns the HTTP status code 500
|
12
12
|
class InternalServerError < Error; end
|
13
13
|
|
14
|
+
# Raised when Instagram returns the HTTP status code 502
|
15
|
+
class BadGateway < Error; end
|
16
|
+
|
14
17
|
# Raised when Instagram returns the HTTP status code 503
|
15
18
|
class ServiceUnavailable < Error; end
|
16
19
|
|
20
|
+
# Raised when Instagram returns the HTTP status code 504
|
21
|
+
class GatewayTimeout < Error; end
|
22
|
+
|
17
23
|
# Raised when a subscription payload hash is invalid
|
18
24
|
class InvalidSignature < Error; end
|
19
25
|
end
|
data/lib/instagram/oauth.rb
CHANGED
@@ -5,6 +5,7 @@ module Instagram
|
|
5
5
|
def authorize_url(options={})
|
6
6
|
options[:response_type] ||= "code"
|
7
7
|
options[:scope] ||= scope if !scope.nil? && !scope.empty?
|
8
|
+
options[:redirect_uri] ||= self.redirect_uri
|
8
9
|
params = authorization_params.merge(options)
|
9
10
|
connection.build_url("/oauth/authorize/", params).to_s
|
10
11
|
end
|
@@ -12,6 +13,7 @@ module Instagram
|
|
12
13
|
# Return an access token from authorization
|
13
14
|
def get_access_token(code, options={})
|
14
15
|
options[:grant_type] ||= "authorization_code"
|
16
|
+
options[:redirect_uri] ||= self.redirect_uri
|
15
17
|
params = access_token_params.merge(options)
|
16
18
|
post("/oauth/access_token/", params.merge(:code => code), raw=false, unformatted=true, no_response_wrapper=true)
|
17
19
|
end
|
data/lib/instagram/request.rb
CHANGED
@@ -2,22 +2,22 @@ module Instagram
|
|
2
2
|
# Defines HTTP request methods
|
3
3
|
module Request
|
4
4
|
# Perform an HTTP GET request
|
5
|
-
def get(path, options={}, raw=false, unformatted=false, no_response_wrapper=
|
5
|
+
def get(path, options={}, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
|
6
6
|
request(:get, path, options, raw, unformatted, no_response_wrapper)
|
7
7
|
end
|
8
8
|
|
9
9
|
# Perform an HTTP POST request
|
10
|
-
def post(path, options={}, raw=false, unformatted=false, no_response_wrapper=
|
10
|
+
def post(path, options={}, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
|
11
11
|
request(:post, path, options, raw, unformatted, no_response_wrapper)
|
12
12
|
end
|
13
13
|
|
14
14
|
# Perform an HTTP PUT request
|
15
|
-
def put(path, options={}, raw=false, unformatted=false, no_response_wrapper=
|
15
|
+
def put(path, options={}, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
|
16
16
|
request(:put, path, options, raw, unformatted, no_response_wrapper)
|
17
17
|
end
|
18
18
|
|
19
19
|
# Perform an HTTP DELETE request
|
20
|
-
def delete(path, options={}, raw=false, unformatted=false, no_response_wrapper=
|
20
|
+
def delete(path, options={}, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
|
21
21
|
request(:delete, path, options, raw, unformatted, no_response_wrapper)
|
22
22
|
end
|
23
23
|
|
data/lib/instagram/version.rb
CHANGED
@@ -39,4 +39,34 @@ describe Faraday::Response do
|
|
39
39
|
end.to raise_error(Instagram::BadRequest, /Bad words are bad./)
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
context 'when a 502 is raised with an HTML response' do
|
44
|
+
before do
|
45
|
+
stub_get('users/self/feed.json').to_return(
|
46
|
+
:body => '<html><body><h1>502 Bad Gateway</h1> The server returned an invalid or incomplete response. </body></html>',
|
47
|
+
:status => 502
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should raise an Instagram::BadGateway' do
|
52
|
+
lambda do
|
53
|
+
@client.user_media_feed()
|
54
|
+
end.should raise_error(Instagram::BadGateway)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when a 504 is raised with an HTML response' do
|
59
|
+
before do
|
60
|
+
stub_get('users/self/feed.json').to_return(
|
61
|
+
:body => '<html> <head><title>504 Gateway Time-out</title></head> <body bgcolor="white"> <center><h1>504 Gateway Time-out</h1></center> <hr><center>nginx</center> </body> </html>',
|
62
|
+
:status => 504
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should raise an Instagram::GatewayTimeout' do
|
67
|
+
lambda do
|
68
|
+
@client.user_media_feed()
|
69
|
+
end.should raise_error(Instagram::GatewayTimeout)
|
70
|
+
end
|
71
|
+
end
|
42
72
|
end
|
data/spec/instagram/api_spec.rb
CHANGED
@@ -30,15 +30,18 @@ describe Instagram::API do
|
|
30
30
|
|
31
31
|
before do
|
32
32
|
@configuration = {
|
33
|
-
:client_id => 'CID',
|
34
|
-
:client_secret => 'CS',
|
35
|
-
:scope => 'comments relationships',
|
36
33
|
:access_token => 'AT',
|
37
34
|
:adapter => :typhoeus,
|
35
|
+
:client_id => 'CID',
|
36
|
+
:client_secret => 'CS',
|
37
|
+
:connection_options => { :ssl => { :verify => true } },
|
38
|
+
:redirect_uri => 'http://http://localhost:4567/oauth/callback',
|
38
39
|
:endpoint => 'http://tumblr.com/',
|
39
40
|
:format => :xml,
|
40
41
|
:proxy => 'http://shayne:sekret@proxy.example.com:8080',
|
42
|
+
:scope => 'comments relationships',
|
41
43
|
:user_agent => 'Custom User Agent',
|
44
|
+
:no_response_wrapper => true,
|
42
45
|
}
|
43
46
|
end
|
44
47
|
|
@@ -53,16 +56,42 @@ describe Instagram::API do
|
|
53
56
|
|
54
57
|
context "after initilization" do
|
55
58
|
|
56
|
-
|
57
|
-
|
59
|
+
let(:api) { Instagram::API.new }
|
60
|
+
|
61
|
+
before do
|
58
62
|
@configuration.each do |key, value|
|
59
63
|
api.send("#{key}=", value)
|
60
64
|
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should override module configuration after initialization" do
|
61
68
|
@keys.each do |key|
|
62
69
|
api.send(key).should == @configuration[key]
|
63
70
|
end
|
64
71
|
end
|
72
|
+
|
73
|
+
describe "#connection" do
|
74
|
+
it "should use the connection_options" do
|
75
|
+
Faraday::Connection.should_receive(:new).with(include(:ssl => { :verify => true }))
|
76
|
+
api.send(:connection)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#config' do
|
84
|
+
subject { Instagram::API.new }
|
85
|
+
|
86
|
+
let(:config) do
|
87
|
+
c = {}; @keys.each {|key| c[key] = key }; c
|
88
|
+
end
|
89
|
+
|
90
|
+
it "returns a hash representing the configuration" do
|
91
|
+
@keys.each do |key|
|
92
|
+
subject.send("#{key}=", key)
|
65
93
|
end
|
94
|
+
subject.config.should == config
|
66
95
|
end
|
67
96
|
end
|
68
97
|
|
@@ -112,29 +141,76 @@ describe Instagram::API do
|
|
112
141
|
url.should_not include("scope")
|
113
142
|
end
|
114
143
|
end
|
144
|
+
|
145
|
+
describe "redirect_uri" do
|
146
|
+
it "should fall back to configuration redirect_uri if not passed as option" do
|
147
|
+
redirect_uri = 'http://localhost:4567/oauth/callback'
|
148
|
+
params = { :redirect_uri => redirect_uri }
|
149
|
+
client = Instagram::Client.new(params)
|
150
|
+
url = client.authorize_url()
|
151
|
+
url.should =~ /redirect_uri=#{URI.escape(redirect_uri, Regexp.union('/',':'))}/
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should override configuration redirect_uri if passed as option" do
|
155
|
+
redirect_uri_config = 'http://localhost:4567/oauth/callback_config'
|
156
|
+
params = { :redirect_uri => redirect_uri_config }
|
157
|
+
client = Instagram::Client.new(params)
|
158
|
+
redirect_uri_option = 'http://localhost:4567/oauth/callback_option'
|
159
|
+
options = { :redirect_uri => redirect_uri_option }
|
160
|
+
url = client.authorize_url(options)
|
161
|
+
url.should =~ /redirect_uri=#{URI.escape(redirect_uri_option, Regexp.union('/',':'))}/
|
162
|
+
end
|
163
|
+
end
|
115
164
|
end
|
116
165
|
|
117
166
|
describe ".get_access_token" do
|
118
167
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
168
|
+
describe "common functionality" do
|
169
|
+
before do
|
170
|
+
@client = Instagram::Client.new(:client_id => "CID", :client_secret => "CS")
|
171
|
+
@url = @client.send(:connection).build_url("/oauth/access_token/").to_s
|
172
|
+
stub_request(:post, @url).
|
173
|
+
with(:body => {:client_id => "CID", :client_secret => "CS", :redirect_uri => "http://localhost:4567/oauth/callback", :grant_type => "authorization_code", :code => "C"}).
|
174
|
+
to_return(:status => 200, :body => fixture("access_token.json"), :headers => {})
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should get the correct resource" do
|
178
|
+
@client.get_access_token(code="C", :redirect_uri => "http://localhost:4567/oauth/callback")
|
179
|
+
a_request(:post, @url).
|
180
|
+
with(:body => {:client_id => "CID", :client_secret => "CS", :redirect_uri => "http://localhost:4567/oauth/callback", :grant_type => "authorization_code", :code => "C"}).
|
181
|
+
should have_been_made
|
182
|
+
end
|
126
183
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
184
|
+
it "should return a hash with an access_token and user data" do
|
185
|
+
response = @client.get_access_token(code="C", :redirect_uri => "http://localhost:4567/oauth/callback")
|
186
|
+
response.access_token.should == "at"
|
187
|
+
response.user.username.should == "mikeyk"
|
188
|
+
end
|
132
189
|
end
|
133
190
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
191
|
+
describe "redirect_uri param" do
|
192
|
+
|
193
|
+
before do
|
194
|
+
@redirect_uri_config = "http://localhost:4567/oauth/callback_config"
|
195
|
+
@client = Instagram::Client.new(:client_id => "CID", :client_secret => "CS", :redirect_uri => @redirect_uri_config)
|
196
|
+
@url = @client.send(:connection).build_url("/oauth/access_token/").to_s
|
197
|
+
stub_request(:post, @url)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should fall back to configuration redirect_uri if not passed as option" do
|
201
|
+
@client.get_access_token(code="C")
|
202
|
+
a_request(:post, @url).
|
203
|
+
with(:body => hash_including({:redirect_uri => @redirect_uri_config})).
|
204
|
+
should have_been_made
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should override configuration redirect_uri if passed as option" do
|
208
|
+
redirect_uri_option = "http://localhost:4567/oauth/callback_option"
|
209
|
+
@client.get_access_token(code="C", :redirect_uri => redirect_uri_option)
|
210
|
+
a_request(:post, @url).
|
211
|
+
with(:body => hash_including({:redirect_uri => redirect_uri_option})).
|
212
|
+
should have_been_made
|
213
|
+
end
|
138
214
|
end
|
139
215
|
end
|
140
216
|
end
|
@@ -74,6 +74,30 @@ describe Instagram::Client do
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
+
describe ".location_search_lat_lng_distance" do
|
78
|
+
|
79
|
+
before do
|
80
|
+
stub_get("locations/search.#{format}").
|
81
|
+
with(:query => {:access_token => @client.access_token}).
|
82
|
+
with(:query => {:lat => "37.7808851", :lng => "-122.3948632", :distance => "5000"}).
|
83
|
+
to_return(:body => fixture("location_search.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"})
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should get the correct resource by lat/lng/distance" do
|
87
|
+
@client.location_search("37.7808851", "-122.3948632", "5000")
|
88
|
+
a_get("locations/search.#{format}").
|
89
|
+
with(:query => {:access_token => @client.access_token}).
|
90
|
+
with(:query => {:lat => "37.7808851", :lng => "-122.3948632", :distance => "5000"}).
|
91
|
+
should have_been_made
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should return an array of user search results" do
|
95
|
+
locations = @client.location_search("37.7808851", "-122.3948632", "5000")
|
96
|
+
locations.should be_a Array
|
97
|
+
locations.first.name.should == "Instagram"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
77
101
|
describe ".location_search_foursquare_v2_id" do
|
78
102
|
|
79
103
|
before do
|
@@ -136,11 +136,14 @@ describe Instagram::Client do
|
|
136
136
|
end
|
137
137
|
|
138
138
|
it "should raise an Instagram::InvalidSignature error" do
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
139
|
+
invalid_signatures = ["31337H4X0R", nil]
|
140
|
+
invalid_signatures.each do |signature|
|
141
|
+
lambda do
|
142
|
+
@client.process_subscription(@json, :signature => signature ) do |handler|
|
143
|
+
# hi
|
144
|
+
end
|
145
|
+
end.should raise_error(Instagram::InvalidSignature)
|
146
|
+
end
|
144
147
|
end
|
145
148
|
end
|
146
149
|
end
|
@@ -5,7 +5,7 @@ describe Instagram::Client do
|
|
5
5
|
client = Instagram::Client.new
|
6
6
|
endpoint = URI.parse(client.endpoint)
|
7
7
|
connection = client.send(:connection).build_url(nil).to_s
|
8
|
-
(connection
|
8
|
+
(connection).should == endpoint.to_s
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should not cache the user account across clients" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: instagram
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shayne Sweeney
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -61,7 +61,7 @@ dependencies:
|
|
61
61
|
version: '0.7'
|
62
62
|
- - <
|
63
63
|
- !ruby/object:Gem::Version
|
64
|
-
version: '0.
|
64
|
+
version: '0.10'
|
65
65
|
type: :runtime
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -71,21 +71,27 @@ dependencies:
|
|
71
71
|
version: '0.7'
|
72
72
|
- - <
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: '0.
|
74
|
+
version: '0.10'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: faraday_middleware
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
|
-
- -
|
79
|
+
- - ! '>='
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '0.8'
|
82
|
+
- - <
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0.10'
|
82
85
|
type: :runtime
|
83
86
|
prerelease: false
|
84
87
|
version_requirements: !ruby/object:Gem::Requirement
|
85
88
|
requirements:
|
86
|
-
- -
|
89
|
+
- - ! '>='
|
87
90
|
- !ruby/object:Gem::Version
|
88
91
|
version: '0.8'
|
92
|
+
- - <
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0.10'
|
89
95
|
- !ruby/object:Gem::Dependency
|
90
96
|
name: multi_json
|
91
97
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,6 +138,7 @@ files:
|
|
132
138
|
- .yardopts
|
133
139
|
- Gemfile
|
134
140
|
- LICENSE.md
|
141
|
+
- PATENTS.md
|
135
142
|
- README.md
|
136
143
|
- Rakefile
|
137
144
|
- instagram.gemspec
|
@@ -213,9 +220,16 @@ files:
|
|
213
220
|
homepage: https://github.com/Instagram/instagram-ruby-gem
|
214
221
|
licenses: []
|
215
222
|
metadata: {}
|
216
|
-
post_install_message: ! "********************************************************************************\n\
|
217
|
-
|
218
|
-
|
223
|
+
post_install_message: ! "********************************************************************************\n\nInstagram
|
224
|
+
REST and Search APIs\n------------------------------\nOur developer site documents
|
225
|
+
all the Instagram REST and Search APIs.\n(http://instagram.com/developer)\n\nBlog\n----------------------------\nThe
|
226
|
+
Developer Blog features news and important announcements about the Instagram Platform.
|
227
|
+
\nYou will also find tutorials and best practices to help you build great platform
|
228
|
+
integrations.\nMake sure to subscribe to the RSS feed so you don't miss out on new
|
229
|
+
posts: \n(http://developers.instagram.com).\n\nCommunity\n----------------------\nThe
|
230
|
+
Stack Overflow community is a great place to ask API related questions or if you
|
231
|
+
need help with your code.\nMake sure to tag your questions with the Instagram tag
|
232
|
+
to get fast answers from other fellow developers and members of the Instagram team.\n(http://stackoverflow.com/questions/tagged/instagram/)\n\n********************************************************************************\n"
|
219
233
|
rdoc_options: []
|
220
234
|
require_paths:
|
221
235
|
- lib
|