rest-more 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +18 -0
- data/README.md +21 -57
- data/Rakefile +1 -1
- data/lib/rest-core/client/dropbox.rb +12 -8
- data/lib/rest-core/client/facebook.rb +16 -6
- data/lib/rest-core/client/github.rb +44 -2
- data/lib/rest-core/client/instagram.rb +2 -2
- data/lib/rest-core/client/linkedin.rb +8 -2
- data/lib/rest-core/client/stackexchange.rb +67 -0
- data/lib/rest-core/client/twitter.rb +10 -9
- data/lib/rest-more.rb +8 -7
- data/lib/rest-more/version.rb +1 -1
- data/rest-more.gemspec +11 -9
- data/test/dropbox/test_dropbox.rb +0 -1
- data/test/facebook/test_api.rb +2 -2
- data/test/facebook/test_error.rb +0 -1
- data/test/facebook/test_handler.rb +0 -1
- data/test/facebook/test_misc.rb +0 -1
- data/test/facebook/test_old.rb +0 -1
- data/test/facebook/test_page.rb +0 -1
- data/test/facebook/test_parse.rb +5 -4
- data/test/facebook/test_serialize.rb +0 -5
- data/test/github/test_github.rb +31 -0
- data/test/stackexchange/test_stackexchange.rb +16 -0
- data/test/twitter/test_twitter.rb +0 -1
- metadata +9 -7
- data/lib/rest-core/client/firebase.rb +0 -105
- data/test/firebase/test_firebase.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5444cce199f7ad6519edbcbfa1db1a2ee7d1b143
|
4
|
+
data.tar.gz: 2e7c6a0750501114f7bdd7f631827f5acacf08ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73043581c21ef0dad61cba002fff199a754c96b6290ee49d44da0a78f43a3d8ea79534d239ed695b12e6c66ad1a8ad5bf91b349c0ffabb4cd86d439c87d03ca3
|
7
|
+
data.tar.gz: 9f2a1b3582b0006f88ad2b1b2619fa7c7a13a539fcf827d71f94758cf4bdc01488e89f3768a4e23e5ce0aa8a02c61be64e82ddf9be11fe8438d90186a5a40876
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## rest-more 3.2.0 -- 2014-06-27
|
4
|
+
|
5
|
+
* Removed RC::Firebase because it's extracted into [rest-firebase][]
|
6
|
+
* A lot of methods now accept callback properly.
|
7
|
+
|
8
|
+
[rest-firebase]: https://github.com/CodementorIO/rest-firebase
|
9
|
+
|
10
|
+
### Changes for RC::Github
|
11
|
+
|
12
|
+
* Added `RC::Github#all` to grab resources through all pages automatically
|
13
|
+
and concurrently. e.g. `RC::Github.new.all('users/godfat/repos')`
|
14
|
+
|
15
|
+
### Changes for RC::Linkedin
|
16
|
+
|
17
|
+
* Added `RC::Linkedin#profile` to access someone's profile easier.
|
18
|
+
|
19
|
+
### Added RC::StackExchange
|
20
|
+
|
3
21
|
## rest-more 3.1.0 -- 2014-05-09
|
4
22
|
|
5
23
|
### Changes for RC::Firebase
|
data/README.md
CHANGED
@@ -26,24 +26,34 @@ Various REST clients such as Facebook and Twitter built with [rest-core][].
|
|
26
26
|
|
27
27
|
Out-of-box REST clients built with rest-core for:
|
28
28
|
|
29
|
-
* [RC::Dropbox][]
|
30
|
-
* [RC::Facebook][] (most completed)
|
31
|
-
* [RC::
|
32
|
-
* [RC::
|
33
|
-
* [RC::
|
34
|
-
* [RC::
|
35
|
-
* [RC::Twitter][]
|
29
|
+
* [RC::Dropbox][] (via OAuth 1.0a)
|
30
|
+
* [RC::Facebook][] (via OAuth 2, most completed)
|
31
|
+
* [RC::Github][] (via OAuth 2)
|
32
|
+
* [RC::Instagram][] (via OAuth 2)
|
33
|
+
* [RC::Linkedin][] (via OAuth 1.0a)
|
34
|
+
* [RC::StackExchange][] (via OAuth 2)
|
35
|
+
* [RC::Twitter][] (via OAuth 1.0a)
|
36
36
|
|
37
37
|
[RC::Dropbox]: lib/rest-core/client/dropbox.rb
|
38
38
|
[RC::Facebook]: lib/rest-core/client/facebook.rb
|
39
|
-
[RC::Firebase]: lib/rest-core/client/firebase.rb
|
40
39
|
[RC::Github]: lib/rest-core/client/github.rb
|
41
40
|
[RC::Instagram]: lib/rest-core/client/instagram.rb
|
42
41
|
[RC::Linkedin]: lib/rest-core/client/linkedin.rb
|
42
|
+
[RC::StackExchange]: lib/rest-core/client/stackexchange.rb
|
43
43
|
[RC::Twitter]: lib/rest-core/client/twitter.rb
|
44
44
|
|
45
45
|
Rails utilities are also included for some clients.
|
46
46
|
|
47
|
+
Other clients in other gems:
|
48
|
+
|
49
|
+
* Firebase: [rest-firebase][]
|
50
|
+
* TopCoder: [topcoder][]
|
51
|
+
* YahooBuy: [rest-more-yahoo_buy][]
|
52
|
+
|
53
|
+
[rest-firebase]: https://github.com/CodementorIO/rest-firebase
|
54
|
+
[topcoder]: https://github.com/miaout17/topcoder
|
55
|
+
[rest-more-yahoo_buy]: https://github.com/GoodLife/rest-more-yahoo_buy
|
56
|
+
|
47
57
|
## REQUIREMENTS:
|
48
58
|
|
49
59
|
### Mandatory:
|
@@ -132,55 +142,6 @@ f.authorize!(:redirect_uri => redirect_uri, :code => 'code')
|
|
132
142
|
p [f.me, f.get('me/posts')]
|
133
143
|
```
|
134
144
|
|
135
|
-
### Firebase example:
|
136
|
-
|
137
|
-
Check out their
|
138
|
-
[REST API documentation](https://www.firebase.com/docs/rest-api.html)
|
139
|
-
for a complete reference, and [RC::Firebase][] for built-in APIs.
|
140
|
-
|
141
|
-
``` ruby
|
142
|
-
require 'rest-more'
|
143
|
-
|
144
|
-
f = RC::Firebase.new :site => 'https://SampleChat.firebaseIO-demo.com/',
|
145
|
-
:secret => 'secret',
|
146
|
-
:d => {:auth_data => 'something'},
|
147
|
-
:log_method => method(:puts),
|
148
|
-
:auth => false # Ignore auth for this example!
|
149
|
-
|
150
|
-
@reconnect = true
|
151
|
-
|
152
|
-
# Streaming over 'users/tom'
|
153
|
-
es = f.event_source('users/tom')
|
154
|
-
es.onopen { |sock| p sock } # Called when connected
|
155
|
-
es.onmessage{ |event, data, sock| p event, data } # Called for each message
|
156
|
-
es.onerror { |error, sock| p error } # Called whenever there's an error
|
157
|
-
# Extra: If we return true in onreconnect callback, it would automatically
|
158
|
-
# reconnect the node for us if disconnected.
|
159
|
-
es.onreconnect{ |error, sock| p error; @reconnect }
|
160
|
-
|
161
|
-
# Start making the request
|
162
|
-
es.start
|
163
|
-
|
164
|
-
# Try to close the connection and see it reconnects automatically
|
165
|
-
es.close
|
166
|
-
|
167
|
-
# Update users/tom.json
|
168
|
-
p f.put('users/tom', :some => 'data')
|
169
|
-
p f.post('users/tom', :some => 'other')
|
170
|
-
p f.get('users/tom')
|
171
|
-
p f.delete('users/tom')
|
172
|
-
|
173
|
-
# Need to tell onreconnect stops reconnecting, or even if we close
|
174
|
-
# the connection manually, it would still try to reconnect again.
|
175
|
-
@reconnect = false
|
176
|
-
|
177
|
-
# Close the connection to gracefully shut it down.
|
178
|
-
es.close
|
179
|
-
|
180
|
-
# Refresh the auth by resetting it
|
181
|
-
f.auth = nil
|
182
|
-
```
|
183
|
-
|
184
145
|
### Github example:
|
185
146
|
|
186
147
|
Check out their
|
@@ -194,6 +155,8 @@ g = RC::Github.new :access_token => 'if you have the token',
|
|
194
155
|
:log_method => method(:puts)
|
195
156
|
|
196
157
|
p [g.me, g.get('users/godfat')]
|
158
|
+
p g.all('users/godfat/repos').size # get all repositories across all pages
|
159
|
+
|
197
160
|
```
|
198
161
|
|
199
162
|
### Instagram example:
|
@@ -342,6 +305,7 @@ Which is using `RestCore::Universal` for accessing arbitrary websites.
|
|
342
305
|
|
343
306
|
## Powered sites:
|
344
307
|
|
308
|
+
* [Codementor](https://www.codementor.io/)
|
345
309
|
* [PicCollage](http://pic-collage.com/)
|
346
310
|
|
347
311
|
## CHANGES:
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ Gemgem.init(dir) do |s|
|
|
14
14
|
s.version = RestMore::VERSION
|
15
15
|
s.homepage = 'https://github.com/godfat/rest-more'
|
16
16
|
|
17
|
-
%w[rest-core].each{ |g| s.add_runtime_dependency(g, '>=3.
|
17
|
+
%w[rest-core].each{ |g| s.add_runtime_dependency(g, '>=3.2.0') }
|
18
18
|
|
19
19
|
# exclude rest-core
|
20
20
|
s.files.reject!{ |f| f.start_with?('rest-core/') }
|
@@ -68,27 +68,31 @@ end
|
|
68
68
|
module RestCore::Dropbox::Client
|
69
69
|
include RestCore
|
70
70
|
|
71
|
-
def me query={}, opts={}
|
72
|
-
get('1/account/info', query, opts)
|
71
|
+
def me query={}, opts={}, &cb
|
72
|
+
get('1/account/info', query, opts, &cb)
|
73
73
|
end
|
74
74
|
|
75
75
|
def default_root
|
76
76
|
'sandbox'
|
77
77
|
end
|
78
78
|
|
79
|
-
def download path, query={}, opts={}
|
79
|
+
def download path, query={}, opts={}, &cb
|
80
80
|
get("https://api-content.dropbox.com/1/files/#{root}/#{path}",
|
81
|
-
query, {:json_response => false}.merge(opts))
|
81
|
+
query, {:json_response => false}.merge(opts), &cb)
|
82
82
|
end
|
83
83
|
|
84
|
-
def upload path, file, query={}, opts={}
|
84
|
+
def upload path, file, query={}, opts={}, &cb
|
85
85
|
put("https://api-content.dropbox.com/1/files_put/#{root}/#{path}",
|
86
|
-
file, query, opts)
|
86
|
+
file, query, opts, &cb)
|
87
87
|
end
|
88
88
|
|
89
89
|
def ls path='', query={}, opts={}
|
90
|
-
|
91
|
-
|
90
|
+
args = ["1/metadata/#{root}/#{path}", query, opts]
|
91
|
+
if block_given?
|
92
|
+
get(*args){ |r| yield(r['contents'].map{ |c| c['path'] }) }
|
93
|
+
else
|
94
|
+
get(*args)['contents'].map{ |c| c['path'] }
|
95
|
+
end
|
92
96
|
end
|
93
97
|
end
|
94
98
|
|
@@ -5,7 +5,7 @@ require 'rest-core/util/hmac'
|
|
5
5
|
# https://developers.facebook.com/docs/reference/api
|
6
6
|
# https://developers.facebook.com/tools/explorer
|
7
7
|
module RestCore
|
8
|
-
Facebook = Builder.client(:
|
8
|
+
Facebook = Builder.client(:app_id, :secret, :data, :old_site) do
|
9
9
|
use Timeout , 10
|
10
10
|
|
11
11
|
use DefaultSite , 'https://graph.facebook.com/'
|
@@ -177,8 +177,13 @@ module RestCore::Facebook::Client
|
|
177
177
|
# beware! maybe facebook would take out the code someday
|
178
178
|
return self.data = old_data unless old_data && old_data['code']
|
179
179
|
# passing empty redirect_uri is needed!
|
180
|
-
|
181
|
-
|
180
|
+
arg = {:code => old_data['code'], :redirect_uri => ''}
|
181
|
+
|
182
|
+
if block_given?
|
183
|
+
authorize!(arg){ |r| self.data = old_data.merge(r) }
|
184
|
+
else
|
185
|
+
self.data = old_data.merge(authorize!(arg))
|
186
|
+
end
|
182
187
|
end
|
183
188
|
|
184
189
|
def parse_json! json
|
@@ -218,9 +223,14 @@ module RestCore::Facebook::Client
|
|
218
223
|
|
219
224
|
def authorize! opts={}
|
220
225
|
payload = {:client_id => app_id, :client_secret => secret}.merge(opts)
|
221
|
-
|
222
|
-
|
223
|
-
|
226
|
+
args = ['oauth/access_token', payload, {},
|
227
|
+
{:json_response => false}.merge(opts)]
|
228
|
+
|
229
|
+
if block_given?
|
230
|
+
post(*args){ |r| yield(self.data = ParseQuery.parse_query(r)) }
|
231
|
+
else
|
232
|
+
self.data = ParseQuery.parse_query(post(*args))
|
233
|
+
end
|
224
234
|
end
|
225
235
|
|
226
236
|
# old rest facebook api, i will definitely love to remove them someday
|
@@ -23,8 +23,50 @@ end
|
|
23
23
|
module RestCore::Github::Client
|
24
24
|
include RestCore
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
MAX_PER_PAGE = 100
|
27
|
+
|
28
|
+
def me query={}, opts={}, &cb
|
29
|
+
get('user', query, opts, &cb)
|
30
|
+
end
|
31
|
+
|
32
|
+
def all path, query={}, opts={}
|
33
|
+
q = {:per_page => MAX_PER_PAGE}.merge(query)
|
34
|
+
r = get(path, q, opts.merge(RESPONSE_KEY => PROMISE)).then{ |response|
|
35
|
+
body = response[RESPONSE_BODY] + (page_range(response).map{ |page|
|
36
|
+
get(path, q.merge(:page => page),
|
37
|
+
opts.merge(RESPONSE_KEY => RESPONSE_BODY))
|
38
|
+
}.inject([], &:+))
|
39
|
+
response.merge(RESPONSE_BODY => body)
|
40
|
+
}.future_response
|
41
|
+
|
42
|
+
if block_given?
|
43
|
+
yield(r[response_key(opts)])
|
44
|
+
self
|
45
|
+
else
|
46
|
+
r[response_key(opts)]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def page_range response
|
52
|
+
from = (parse_current_page(response) || 1).to_i + 1
|
53
|
+
to = (parse_last_page(response) || from - 1).to_i
|
54
|
+
if from <= to
|
55
|
+
from..to
|
56
|
+
else
|
57
|
+
[]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_current_page response
|
62
|
+
RC::ParseQuery.parse_query(URI.parse(response[REQUEST_URI]).query)['page']
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_last_page response
|
66
|
+
return unless link = response[RESPONSE_HEADERS]['LINK']
|
67
|
+
ls = RC::ParseLink.parse_link(link)
|
68
|
+
return unless last_link = ls['last']
|
69
|
+
RC::ParseQuery.parse_query(URI.parse(last_link['uri']).query)['page']
|
28
70
|
end
|
29
71
|
end
|
30
72
|
|
@@ -27,8 +27,14 @@ end
|
|
27
27
|
module RestCore::Linkedin::Client
|
28
28
|
include RestCore
|
29
29
|
|
30
|
-
def me query={}, opts={}
|
31
|
-
|
30
|
+
def me query={}, opts={}, &cb
|
31
|
+
profile('~', nil, query, opts, &cb)
|
32
|
+
end
|
33
|
+
|
34
|
+
def profile name, value=nil, fields=[], query={}, opts={}, &cb
|
35
|
+
path = if value then "#{name}=#{CGI.escape(value)}" else name end
|
36
|
+
info = if fields.empty? then '' else ":(#{fields.join(',')})" end
|
37
|
+
get("v1/people/#{path}#{info}", query, opts, &cb)
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
require 'rest-core'
|
3
|
+
|
4
|
+
# http://api.stackexchange.com/docs
|
5
|
+
module RestCore
|
6
|
+
StackExchange = Builder.client(:client_id, :client_secret, :key, :data) do
|
7
|
+
use Timeout , 10
|
8
|
+
|
9
|
+
use DefaultSite , 'https://api.stackexchange.com/'
|
10
|
+
use DefaultHeaders, {'Accept' => 'application/json'}
|
11
|
+
use DefaultQuery , nil
|
12
|
+
use Oauth2Query , nil
|
13
|
+
|
14
|
+
use CommonLogger , nil
|
15
|
+
use Cache , nil, 600 do
|
16
|
+
use ErrorHandler, lambda{ |env|
|
17
|
+
RuntimeError.new(env[RESPONSE_BODY]['error_message'])}
|
18
|
+
use ErrorDetectorHttp
|
19
|
+
use JsonResponse, true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module RestCore::StackExchange::Client
|
25
|
+
include RestCore
|
26
|
+
|
27
|
+
def me query={}, opts={}, &cb
|
28
|
+
get('me', query, opts, &cb)
|
29
|
+
end
|
30
|
+
|
31
|
+
def access_token
|
32
|
+
data['access_token']
|
33
|
+
end
|
34
|
+
|
35
|
+
def access_token= token
|
36
|
+
data['access_token'] = token
|
37
|
+
end
|
38
|
+
|
39
|
+
def authorize_url query={}, opts={}
|
40
|
+
url('https://stackexchange.com/oauth',
|
41
|
+
{:access_token => false, :key => false, :site => false,
|
42
|
+
:client_id => client_id}.merge(query), opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
def authorize! payload={}, opts={}, &cb
|
46
|
+
p = {:client_id => client_id, :client_secret => client_secret}.
|
47
|
+
merge(payload)
|
48
|
+
|
49
|
+
args = ['https://stackexchange.com/oauth/access_token', p,
|
50
|
+
{:access_token => false, :key => false, :site => false},
|
51
|
+
{:json_response => false}.merge(opts)]
|
52
|
+
|
53
|
+
if block_given?
|
54
|
+
post(*args){ |r| yield(self.data = ParseQuery.parse_query(r)) }
|
55
|
+
else
|
56
|
+
self.data = ParseQuery.parse_query(post(*args))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def default_data ; {}; end
|
62
|
+
def default_query; {:key => key, :site => 'stackoverflow'}; end
|
63
|
+
end
|
64
|
+
|
65
|
+
class RestCore::StackExchange
|
66
|
+
include RestCore::StackExchange::Client
|
67
|
+
end
|
@@ -65,28 +65,29 @@ end
|
|
65
65
|
module RestCore::Twitter::Client
|
66
66
|
include RestCore
|
67
67
|
|
68
|
-
def me query={}, opts={}
|
69
|
-
get('1.1/account/verify_credentials.json', query, opts)
|
68
|
+
def me query={}, opts={}, &cb
|
69
|
+
get('1.1/account/verify_credentials.json', query, opts, &cb)
|
70
70
|
end
|
71
71
|
|
72
|
-
def tweet status, media=nil, payload={}, query={}, opts={}
|
72
|
+
def tweet status, media=nil, payload={}, query={}, opts={}, &cb
|
73
73
|
if media
|
74
74
|
post('1.1/statuses/update_with_media.json',
|
75
75
|
{:status => status, 'media[]' => media}.merge(payload),
|
76
|
-
query, opts)
|
76
|
+
query, opts, &cb)
|
77
77
|
else
|
78
78
|
post('1.1/statuses/update.json',
|
79
79
|
{:status => status}.merge(payload),
|
80
|
-
query, opts)
|
80
|
+
query, opts, &cb)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
def search q, query={}, opts={}
|
85
|
-
get('1.1/search/tweets.json', {q: q}.merge(query), opts)
|
84
|
+
def search q, query={}, opts={}, &cb
|
85
|
+
get('1.1/search/tweets.json', {q: q}.merge(query), opts, &cb)
|
86
86
|
end
|
87
87
|
|
88
|
-
def statuses user, query={}, opts={}
|
89
|
-
get('1.1/statuses/user_timeline.json',
|
88
|
+
def statuses user, query={}, opts={}, &cb
|
89
|
+
get('1.1/statuses/user_timeline.json',
|
90
|
+
{:id => user}.merge(query), opts, &cb)
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
data/lib/rest-more.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
require 'rest-core'
|
3
3
|
|
4
4
|
module RestCore
|
5
|
-
autoload :Dropbox
|
6
|
-
autoload :Facebook
|
7
|
-
autoload :Firebase
|
8
|
-
autoload :Github
|
9
|
-
autoload :Instagram, 'rest-core/client/instagram'
|
10
|
-
autoload :Linkedin
|
11
|
-
autoload :
|
5
|
+
autoload :Dropbox , 'rest-core/client/dropbox'
|
6
|
+
autoload :Facebook , 'rest-core/client/facebook'
|
7
|
+
autoload :Firebase , 'rest-core/client/firebase'
|
8
|
+
autoload :Github , 'rest-core/client/github'
|
9
|
+
autoload :Instagram , 'rest-core/client/instagram'
|
10
|
+
autoload :Linkedin , 'rest-core/client/linkedin'
|
11
|
+
autoload :StackExchange, 'rest-core/client/stackexchange'
|
12
|
+
autoload :Twitter , 'rest-core/client/twitter'
|
12
13
|
end
|
data/lib/rest-more/version.rb
CHANGED
data/rest-more.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: rest-more 3.
|
2
|
+
# stub: rest-more 3.2.0 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "rest-more"
|
6
|
-
s.version = "3.
|
6
|
+
s.version = "3.2.0"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib"]
|
10
10
|
s.authors = ["Lin Jen-Shin (godfat)"]
|
11
|
-
s.date = "2014-
|
11
|
+
s.date = "2014-06-27"
|
12
12
|
s.description = "Various REST clients such as Facebook and Twitter built with [rest-core][].\n\n[rest-core]: https://github.com/godfat/rest-core"
|
13
13
|
s.email = ["godfat (XD) godfat.org"]
|
14
14
|
s.executables = ["rib-rest-core"]
|
@@ -49,12 +49,12 @@ Gem::Specification.new do |s|
|
|
49
49
|
"lib/rest-core/client/dropbox.rb",
|
50
50
|
"lib/rest-core/client/facebook.rb",
|
51
51
|
"lib/rest-core/client/facebook/rails_util.rb",
|
52
|
-
"lib/rest-core/client/firebase.rb",
|
53
52
|
"lib/rest-core/client/github.rb",
|
54
53
|
"lib/rest-core/client/github/rails_util.rb",
|
55
54
|
"lib/rest-core/client/instagram.rb",
|
56
55
|
"lib/rest-core/client/linkedin.rb",
|
57
56
|
"lib/rest-core/client/linkedin/rails_util.rb",
|
57
|
+
"lib/rest-core/client/stackexchange.rb",
|
58
58
|
"lib/rest-core/client/twitter.rb",
|
59
59
|
"lib/rest-core/client/twitter/rails_util.rb",
|
60
60
|
"lib/rest-core/util/rails_util_util.rb",
|
@@ -77,8 +77,9 @@ Gem::Specification.new do |s|
|
|
77
77
|
"test/facebook/test_parse.rb",
|
78
78
|
"test/facebook/test_serialize.rb",
|
79
79
|
"test/facebook/test_timeout.rb",
|
80
|
-
"test/
|
80
|
+
"test/github/test_github.rb",
|
81
81
|
"test/instagram/test_instagram.rb",
|
82
|
+
"test/stackexchange/test_stackexchange.rb",
|
82
83
|
"test/twitter/test_twitter.rb"]
|
83
84
|
s.homepage = "https://github.com/godfat/rest-more"
|
84
85
|
s.licenses = ["Apache License 2.0"]
|
@@ -97,19 +98,20 @@ Gem::Specification.new do |s|
|
|
97
98
|
"test/facebook/test_parse.rb",
|
98
99
|
"test/facebook/test_serialize.rb",
|
99
100
|
"test/facebook/test_timeout.rb",
|
100
|
-
"test/
|
101
|
+
"test/github/test_github.rb",
|
101
102
|
"test/instagram/test_instagram.rb",
|
103
|
+
"test/stackexchange/test_stackexchange.rb",
|
102
104
|
"test/twitter/test_twitter.rb"]
|
103
105
|
|
104
106
|
if s.respond_to? :specification_version then
|
105
107
|
s.specification_version = 4
|
106
108
|
|
107
109
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
108
|
-
s.add_runtime_dependency(%q<rest-core>, [">= 3.
|
110
|
+
s.add_runtime_dependency(%q<rest-core>, [">= 3.2.0"])
|
109
111
|
else
|
110
|
-
s.add_dependency(%q<rest-core>, [">= 3.
|
112
|
+
s.add_dependency(%q<rest-core>, [">= 3.2.0"])
|
111
113
|
end
|
112
114
|
else
|
113
|
-
s.add_dependency(%q<rest-core>, [">= 3.
|
115
|
+
s.add_dependency(%q<rest-core>, [">= 3.2.0"])
|
114
116
|
end
|
115
117
|
end
|
data/test/facebook/test_api.rb
CHANGED
@@ -4,7 +4,6 @@ require 'rest-more/test'
|
|
4
4
|
describe RC::Facebook do
|
5
5
|
after do
|
6
6
|
WebMock.reset!
|
7
|
-
Muack.verify
|
8
7
|
end
|
9
8
|
|
10
9
|
should 'generate correct url' do
|
@@ -83,7 +82,8 @@ describe RC::Facebook do
|
|
83
82
|
end
|
84
83
|
|
85
84
|
should 'convert query to string' do
|
86
|
-
|
85
|
+
o = Object.new
|
86
|
+
def o.to_s; 'i am mock'; end
|
87
87
|
stub_request(:get, "https://graph.facebook.com/search?q=i%20am%20mock").
|
88
88
|
to_return(:body => 'ok')
|
89
89
|
RC::Facebook.new(:json_response => false).
|
data/test/facebook/test_error.rb
CHANGED
data/test/facebook/test_misc.rb
CHANGED
data/test/facebook/test_old.rb
CHANGED
data/test/facebook/test_page.rb
CHANGED
data/test/facebook/test_parse.rb
CHANGED
@@ -113,9 +113,11 @@ describe RC::Facebook do
|
|
113
113
|
app_id = 456
|
114
114
|
rg = RC::Facebook.new(:secret => secret,
|
115
115
|
:app_id => app_id)
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
|
117
|
+
stub_request(:post, 'https://graph.facebook.com/oauth/access_token').
|
118
|
+
with(:body => {'client_id' => '456', 'client_secret' => 'lulala',
|
119
|
+
'code' => 'lalalu', 'redirect_uri' => ''}).
|
120
|
+
to_return(:body => 'access_token=lololo').times(2)
|
119
121
|
|
120
122
|
check = lambda{
|
121
123
|
rg.data['code'] .should.eq code
|
@@ -155,5 +157,4 @@ describe RC::Facebook do
|
|
155
157
|
rg.access_token .should.eq 'a'
|
156
158
|
rg.data['expires'] .should.eq '1234'
|
157
159
|
end
|
158
|
-
|
159
160
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require 'rest-more/test'
|
3
|
+
|
4
|
+
describe RC::Github do
|
5
|
+
after do
|
6
|
+
WebMock.reset!
|
7
|
+
end
|
8
|
+
|
9
|
+
should 'get all' do
|
10
|
+
link = '</users/godfat/repos?type=o&per_page=100&page=3>; rel="last"'
|
11
|
+
headers = {'Link' => link}
|
12
|
+
stub_request(:get,
|
13
|
+
'https://api.github.com/users/godfat/repos?type=o&per_page=100').
|
14
|
+
to_return(:body => [0], :headers => headers).times(2)
|
15
|
+
stub_request(:get,
|
16
|
+
'https://api.github.com/users/godfat/repos?type=o&per_page=100&page=2').
|
17
|
+
to_return(:body => [1], :headers => headers).times(2)
|
18
|
+
stub_request(:get,
|
19
|
+
'https://api.github.com/users/godfat/repos?type=o&per_page=100&page=3').
|
20
|
+
to_return(:body => [2], :headers => headers).times(2)
|
21
|
+
|
22
|
+
args = ['users/godfat/repos', {:type => 'o'}]
|
23
|
+
exps = [0, 1, 2]
|
24
|
+
g = RC::Github.new
|
25
|
+
g.all(*args) do |res|
|
26
|
+
res.should.eq exps
|
27
|
+
g.all(*args).should.eq exps
|
28
|
+
end
|
29
|
+
g.wait
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'rest-more/test'
|
3
|
+
|
4
|
+
describe RC::StackExchange do
|
5
|
+
after do
|
6
|
+
WebMock.reset!
|
7
|
+
end
|
8
|
+
|
9
|
+
should 'me' do
|
10
|
+
stub_request(:get,
|
11
|
+
'https://api.stackexchange.com/me?key=yek&site=stackoverflow').
|
12
|
+
to_return(:body => '{"name":"meme"}')
|
13
|
+
|
14
|
+
RC::StackExchange.new(:key => 'yek').me.should.eq 'name' => 'meme'
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-more
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lin Jen-Shin (godfat)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-core
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3.
|
19
|
+
version: 3.2.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
|
-
version: 3.
|
26
|
+
version: 3.2.0
|
27
27
|
description: |-
|
28
28
|
Various REST clients such as Facebook and Twitter built with [rest-core][].
|
29
29
|
|
@@ -71,12 +71,12 @@ files:
|
|
71
71
|
- lib/rest-core/client/dropbox.rb
|
72
72
|
- lib/rest-core/client/facebook.rb
|
73
73
|
- lib/rest-core/client/facebook/rails_util.rb
|
74
|
-
- lib/rest-core/client/firebase.rb
|
75
74
|
- lib/rest-core/client/github.rb
|
76
75
|
- lib/rest-core/client/github/rails_util.rb
|
77
76
|
- lib/rest-core/client/instagram.rb
|
78
77
|
- lib/rest-core/client/linkedin.rb
|
79
78
|
- lib/rest-core/client/linkedin/rails_util.rb
|
79
|
+
- lib/rest-core/client/stackexchange.rb
|
80
80
|
- lib/rest-core/client/twitter.rb
|
81
81
|
- lib/rest-core/client/twitter/rails_util.rb
|
82
82
|
- lib/rest-core/util/rails_util_util.rb
|
@@ -99,8 +99,9 @@ files:
|
|
99
99
|
- test/facebook/test_parse.rb
|
100
100
|
- test/facebook/test_serialize.rb
|
101
101
|
- test/facebook/test_timeout.rb
|
102
|
-
- test/
|
102
|
+
- test/github/test_github.rb
|
103
103
|
- test/instagram/test_instagram.rb
|
104
|
+
- test/stackexchange/test_stackexchange.rb
|
104
105
|
- test/twitter/test_twitter.rb
|
105
106
|
homepage: https://github.com/godfat/rest-more
|
106
107
|
licenses:
|
@@ -139,6 +140,7 @@ test_files:
|
|
139
140
|
- test/facebook/test_parse.rb
|
140
141
|
- test/facebook/test_serialize.rb
|
141
142
|
- test/facebook/test_timeout.rb
|
142
|
-
- test/
|
143
|
+
- test/github/test_github.rb
|
143
144
|
- test/instagram/test_instagram.rb
|
145
|
+
- test/stackexchange/test_stackexchange.rb
|
144
146
|
- test/twitter/test_twitter.rb
|
@@ -1,105 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'rest-core'
|
3
|
-
|
4
|
-
# https://www.firebase.com/docs/security/custom-login.html
|
5
|
-
# https://www.firebase.com/docs/rest-api.html
|
6
|
-
module RestCore
|
7
|
-
Firebase = Builder.client(:d, :secret, :auth) do
|
8
|
-
use Timeout , 10
|
9
|
-
|
10
|
-
use DefaultSite , 'https://SampleChat.firebaseIO-demo.com/'
|
11
|
-
use DefaultHeaders, {'Accept' => 'application/json'}
|
12
|
-
use DefaultQuery , nil
|
13
|
-
|
14
|
-
use FollowRedirect, 1
|
15
|
-
use CommonLogger , nil
|
16
|
-
use Cache , nil, 600 do
|
17
|
-
use ErrorHandler, lambda{ |env| Firebase::Error.call(env) }
|
18
|
-
use ErrorDetectorHttp
|
19
|
-
use JsonResponse, true
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class RestCore::Firebase::Error < RestCore::Error
|
25
|
-
include RestCore
|
26
|
-
class ServerError < Firebase::Error; end
|
27
|
-
class ClientError < RestCore::Error; end
|
28
|
-
|
29
|
-
class BadRequest < Firebase::Error; end
|
30
|
-
class Unauthorized < Firebase::Error; end
|
31
|
-
class Forbidden < Firebase::Error; end
|
32
|
-
class NotFound < Firebase::Error; end
|
33
|
-
class NotAcceptable < Firebase::Error; end
|
34
|
-
class ExpectationFailed < Firebase::Error; end
|
35
|
-
|
36
|
-
class InternalServerError < Firebase::Error::ServerError; end
|
37
|
-
class BadGateway < Firebase::Error::ServerError; end
|
38
|
-
class ServiceUnavailable < Firebase::Error::ServerError; end
|
39
|
-
|
40
|
-
attr_reader :error, :code, :url
|
41
|
-
def initialize error, code, url=''
|
42
|
-
@error, @code, @url = error, code, url
|
43
|
-
super("[#{code}] #{error.inspect} from #{url}")
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.call env
|
47
|
-
error, code, url = env[RESPONSE_BODY], env[RESPONSE_STATUS],
|
48
|
-
env[REQUEST_URI]
|
49
|
-
return new(error, code, url) unless error.kind_of?(Hash)
|
50
|
-
case code
|
51
|
-
when 400; BadRequest
|
52
|
-
when 401; Unauthorized
|
53
|
-
when 403; Forbidden
|
54
|
-
when 404; NotFound
|
55
|
-
when 406; NotAcceptable
|
56
|
-
when 417; ExpectationFailed
|
57
|
-
when 500; InternalServerError
|
58
|
-
when 502; BadGateway
|
59
|
-
when 503; ServiceUnavailable
|
60
|
-
else ; self
|
61
|
-
end.new(error, code, url)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
module RestCore::Firebase::Client
|
66
|
-
include RestCore
|
67
|
-
|
68
|
-
class EventSource < RestCore::EventSource
|
69
|
-
def onmessage event=nil, data=nil, sock=nil
|
70
|
-
if event
|
71
|
-
super(event, Json.decode(data), sock)
|
72
|
-
else
|
73
|
-
super
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def request env, app=app
|
79
|
-
super(env.merge(REQUEST_PATH => "#{env[REQUEST_PATH]}.json",
|
80
|
-
REQUEST_PAYLOAD => Json.encode(env[REQUEST_PAYLOAD])),
|
81
|
-
app)
|
82
|
-
end
|
83
|
-
|
84
|
-
def generate_auth opts={}
|
85
|
-
raise Firebase::Error::ClientError.new(
|
86
|
-
"Please set your secret") unless secret
|
87
|
-
|
88
|
-
header = {:typ => 'JWT', :alg => 'HS256'}
|
89
|
-
claims = {:v => 0, :iat => Time.now.to_i, :d => d}.merge(opts)
|
90
|
-
# http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-26
|
91
|
-
input = [header, claims].map{ |d| base64url(Json.encode(d)) }.join('.')
|
92
|
-
# http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20
|
93
|
-
"#{input}.#{base64url(Hmac.sha256(secret, input))}"
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
def base64url str; [str].pack('m').tr('+/', '-_'); end
|
98
|
-
def default_query; {:auth => auth}; end
|
99
|
-
def default_auth ; generate_auth ; end
|
100
|
-
end
|
101
|
-
|
102
|
-
class RestCore::Firebase
|
103
|
-
include RestCore::Firebase::Client
|
104
|
-
self.event_source_class = EventSource
|
105
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'rest-more/test'
|
3
|
-
|
4
|
-
describe RC::Firebase do
|
5
|
-
before do
|
6
|
-
stub(Time).now{ Time.at(0) }
|
7
|
-
end
|
8
|
-
|
9
|
-
after do
|
10
|
-
WebMock.reset!
|
11
|
-
Muack.verify
|
12
|
-
end
|
13
|
-
|
14
|
-
path = 'https://a.json?auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9%0A.eyJ2IjowLCJpYXQiOjAsImQiOm51bGx9%0A.C9JtzZhiCrsClNdAQcE7Irngr2BZJCH4x1p-IHxfrAo%3D%0A'
|
15
|
-
|
16
|
-
def firebase
|
17
|
-
RC::Firebase.new(:secret => 'nnf')
|
18
|
-
end
|
19
|
-
|
20
|
-
should 'get true' do
|
21
|
-
stub_request(:get, path).to_return(:body => 'true')
|
22
|
-
firebase.get('https://a').should.eq true
|
23
|
-
end
|
24
|
-
|
25
|
-
should 'put {"status":"ok"}' do
|
26
|
-
json = '{"status":"ok"}'
|
27
|
-
rbon = {'status' => 'ok'}
|
28
|
-
stub_request(:put, path).with(:body => json).to_return(:body => json)
|
29
|
-
firebase.put('https://a', rbon).should.eq rbon
|
30
|
-
end
|
31
|
-
|
32
|
-
should 'parse event source' do
|
33
|
-
stub_request(:get, path).to_return(:body => <<-SSE)
|
34
|
-
event: put
|
35
|
-
data: {}
|
36
|
-
|
37
|
-
event: keep-alive
|
38
|
-
data: null
|
39
|
-
|
40
|
-
event: invalid
|
41
|
-
data: invalid
|
42
|
-
SSE
|
43
|
-
m = [{'event' => 'put' , 'data' => {}},
|
44
|
-
{'event' => 'keep-alive', 'data' => nil}]
|
45
|
-
es = firebase.event_source('https://a')
|
46
|
-
es.should.kind_of RC::Firebase::Client::EventSource
|
47
|
-
es.onmessage do |event, data|
|
48
|
-
{'event' => event, 'data' => data}.should.eq m.shift
|
49
|
-
end.onerror do |error|
|
50
|
-
error.should.kind_of RC::Json::ParseError
|
51
|
-
end.start.wait
|
52
|
-
m.should.empty
|
53
|
-
end
|
54
|
-
|
55
|
-
check = lambda do |status, klass|
|
56
|
-
stub_request(:delete, path).to_return(
|
57
|
-
:body => '{}', :status => status)
|
58
|
-
|
59
|
-
lambda{ firebase.delete('https://a').tap{} }.should.raise(klass)
|
60
|
-
|
61
|
-
WebMock.reset!
|
62
|
-
end
|
63
|
-
|
64
|
-
should 'raise exception when encountering error' do
|
65
|
-
[400, 401, 402, 403, 404, 406, 417].each do |status|
|
66
|
-
check[status, RC::Firebase::Error]
|
67
|
-
end
|
68
|
-
[500, 502, 503].each do |status|
|
69
|
-
check[status, RC::Firebase::Error::ServerError]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|