rack-superfeedr 0.4.0 → 0.4.1
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/VERSION +1 -1
- data/lib/rack-superfeedr.rb +192 -157
- data/rack-superfeedr.gemspec +2 -2
- data/test/test_rack-superfeedr.rb +7 -2
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.1
|
data/lib/rack-superfeedr.rb
CHANGED
@@ -2,13 +2,7 @@ require 'base64'
|
|
2
2
|
require 'net/http'
|
3
3
|
require 'uri'
|
4
4
|
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
##
|
9
|
-
# This is a Rack Middleware that can be used in your rack-compatible web framework (Rails, Sinatra...) to perform subscriptions over at superfeedr
|
10
|
-
# using the PubSubHubbub API.
|
11
|
-
class Superfeedr
|
5
|
+
module SuperfeedrAPI
|
12
6
|
|
13
7
|
@@superfeedr_endpoint = "https://push.superfeedr.com/"
|
14
8
|
@@port = 80
|
@@ -18,135 +12,225 @@ module Rack
|
|
18
12
|
@@login = nil
|
19
13
|
@@password = nil
|
20
14
|
|
21
|
-
def
|
15
|
+
def superfeedr_endpoint= _superfeedr_endpoint
|
22
16
|
@@superfeedr_endpoint = _superfeedr_endpoint
|
23
17
|
end
|
24
18
|
|
25
|
-
def
|
19
|
+
def superfeedr_endpoint
|
20
|
+
@@superfeedr_endpoint
|
21
|
+
end
|
22
|
+
|
23
|
+
def port= _port
|
26
24
|
@@port = _port
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
27
|
+
def port
|
28
|
+
@@port
|
29
|
+
end
|
30
|
+
|
31
|
+
def host= _host
|
30
32
|
@@host = _host
|
31
33
|
end
|
32
34
|
|
33
|
-
def
|
35
|
+
def host
|
36
|
+
@@host
|
37
|
+
end
|
38
|
+
|
39
|
+
def base_path= _base_path
|
34
40
|
@@base_path = _base_path
|
35
41
|
end
|
36
42
|
|
37
|
-
def
|
43
|
+
def base_path
|
44
|
+
@@base_path
|
45
|
+
end
|
46
|
+
|
47
|
+
def scheme= _scheme
|
38
48
|
@@scheme = _scheme
|
39
49
|
end
|
40
50
|
|
41
|
-
def
|
51
|
+
def scheme
|
52
|
+
@@scheme
|
53
|
+
end
|
54
|
+
|
55
|
+
def login= _login
|
42
56
|
@@login = _login
|
43
57
|
end
|
44
58
|
|
45
|
-
def
|
59
|
+
def login
|
60
|
+
@@login
|
61
|
+
end
|
62
|
+
|
63
|
+
def password= _password
|
46
64
|
@@password = _password
|
47
65
|
end
|
48
66
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# A 3rd options argument can be supplied with
|
53
|
-
# - retrieve => true if you want to retrieve the previous items in the feed
|
54
|
-
# - format => 'json' or 'atom' to specify the format of the notifications, defaults to atom
|
55
|
-
# - secret => a secret string used to compyte HMAC signatures so you can check that the data is coming from Superfeedr
|
56
|
-
# - sync => true (defaults to false) if you want to perfrom a verification of intent syncrhonously
|
57
|
-
# - async => true (defaults to false) if you want to perfrom a verification of intent asyncrhonously
|
58
|
-
# - hub => if you want to use an explicit hub, defaults to Superfeedr's http://push.superfeedr.com
|
59
|
-
# It yields 3 arguments to a block:
|
60
|
-
# - body of the response (useful if you used the retrieve option)
|
61
|
-
# - success flag
|
62
|
-
# - response (useful to debug failed requests mostly)
|
63
|
-
def self.subscribe(url, id = nil, opts = {}, &blk)
|
64
|
-
endpoint = opts[:hub] || @@superfeedr_endpoint
|
65
|
-
request = prep_request(url, id, endpoint, opts)
|
66
|
-
|
67
|
-
if opts[:retrieve]
|
68
|
-
request['retrieve'] = true
|
69
|
-
end
|
70
|
-
|
71
|
-
if opts[:format] == "json"
|
72
|
-
request['format'] = "json"
|
73
|
-
end
|
67
|
+
def password
|
68
|
+
@@password
|
69
|
+
end
|
74
70
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
71
|
+
##
|
72
|
+
# Subscribe you to a url. id is optional but strongly recommanded has a unique identifier for this url. It will be used to help you identify which feed
|
73
|
+
# is concerned by a notification.
|
74
|
+
# A 3rd options argument can be supplied with
|
75
|
+
# - retrieve => true if you want to retrieve the previous items in the feed
|
76
|
+
# - format => 'json' or 'atom' to specify the format of the notifications, defaults to atom
|
77
|
+
# - secret => a secret string used to compyte HMAC signatures so you can check that the data is coming from Superfeedr
|
78
|
+
# - sync => true (defaults to false) if you want to perfrom a verification of intent syncrhonously
|
79
|
+
# - async => true (defaults to false) if you want to perfrom a verification of intent asyncrhonously
|
80
|
+
# - hub => if you want to use an explicit hub, defaults to Superfeedr's http://push.superfeedr.com
|
81
|
+
# It yields 3 arguments to a block:
|
82
|
+
# - body of the response (useful if you used the retrieve option)
|
83
|
+
# - success flag
|
84
|
+
# - response (useful to debug failed requests mostly)
|
85
|
+
def subscribe(url, id = nil, opts = {}, &blk)
|
86
|
+
endpoint = opts[:hub] || @@superfeedr_endpoint
|
87
|
+
request = prep_request(url, id, endpoint, opts)
|
88
|
+
|
89
|
+
if opts[:retrieve]
|
90
|
+
request['retrieve'] = true
|
91
|
+
end
|
92
|
+
|
93
|
+
if opts[:format] == "json"
|
94
|
+
request['format'] = "json"
|
95
|
+
end
|
96
|
+
|
97
|
+
if opts[:secret]
|
98
|
+
request['hub.secret'] = opts[:secret]
|
99
|
+
else
|
100
|
+
request['hub.secret'] = "WHAT DO WE PICK? A UNIQUE SCRET THE CALLBACK? SO WE CAN USE THAT ON NOTIFS?"
|
101
|
+
end
|
102
|
+
|
103
|
+
request['hub.mode'] = 'subscribe'
|
104
|
+
|
105
|
+
response = http_post(endpoint, request)
|
82
106
|
|
83
|
-
|
107
|
+
blk.call(response.body, opts[:async] && Integer(response.code) == 202 || Integer(response.code) == 204 || opts[:retrieve] && Integer(response.code) == 200, response) if blk
|
108
|
+
end
|
84
109
|
|
85
|
-
|
110
|
+
##
|
111
|
+
# Retrieve the content of a resource at Superfeedr
|
112
|
+
# A 2nd options argument can be supplied with
|
113
|
+
# - format => 'json' or 'atom' to specify the format of the notifications, defaults to atom
|
114
|
+
# - count => Integer (number of items to retrieve)
|
115
|
+
# - before => The id of an entry in the feed. The response will only include entries published before this one.
|
116
|
+
# - after => The id of an entry in the feed. The response will only include entries published after this one.
|
117
|
+
# It yields 3 arguments to a block (if block is supplied. If not, just returns the triplet)
|
118
|
+
# - body of the response (useful if you used the retrieve option)
|
119
|
+
# - success flag
|
120
|
+
# - response (useful to debug failed requests mostly)
|
121
|
+
def retrieve_by_topic_url(url, opts = {}, &blk)
|
122
|
+
endpoint = opts[:hub] || @@superfeedr_endpoint
|
123
|
+
request = prep_request(url, '', endpoint, opts)
|
124
|
+
|
125
|
+
if opts[:format] == "json"
|
126
|
+
request['format'] = "json"
|
86
127
|
end
|
87
128
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# It yields 3 arguments to a block (if block is supplied. If not, just returns the triplet)
|
96
|
-
# - body of the response (useful if you used the retrieve option)
|
97
|
-
# - success flag
|
98
|
-
# - response (useful to debug failed requests mostly)
|
99
|
-
def self.retrieve_by_topic_url(url, opts = {}, &blk)
|
100
|
-
endpoint = opts[:hub] || @@superfeedr_endpoint
|
101
|
-
request = prep_request(url, '', endpoint, opts)
|
102
|
-
|
103
|
-
if opts[:format] == "json"
|
104
|
-
request['format'] = "json"
|
105
|
-
end
|
129
|
+
if opts[:count]
|
130
|
+
request['count'] = opts[:count]
|
131
|
+
else
|
132
|
+
request['count'] = 10
|
133
|
+
end
|
134
|
+
|
135
|
+
request['hub.mode'] = 'retrieve'
|
106
136
|
|
107
|
-
|
108
|
-
request['count'] = opts[:count]
|
109
|
-
else
|
110
|
-
request['count'] = 10
|
111
|
-
end
|
112
|
-
|
113
|
-
request['hub.mode'] = 'retrieve'
|
137
|
+
response = http_get(endpoint, request)
|
114
138
|
|
115
|
-
|
139
|
+
if blk
|
140
|
+
blk.call(response.body, Integer(response.code) == 200, response) if blk
|
141
|
+
else
|
142
|
+
[response.body, Integer(response.code) == 200, response]
|
143
|
+
end
|
144
|
+
end
|
116
145
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
146
|
+
##
|
147
|
+
# Unsubscribes a url. If you used an id for the susbcription, you need to use _the same_.
|
148
|
+
# The optional block will be called to let you confirm the subscription (or not). This is not applicable for if you use params[:async] => true
|
149
|
+
# It returns true if the unsubscription was successful (or will be confirmed if you used async => true in the options), false otherwise
|
150
|
+
# You can also pass an opts third argument that will be merged with the options used in Typhoeus's Request (https://github.com/dbalatero/typhoeus)
|
151
|
+
|
152
|
+
##
|
153
|
+
# Subscribe you to a url. id needs to match the id you used to subscribe.
|
154
|
+
# A 3rd options argument can be supplied with
|
155
|
+
# - sync => true (defaults to false) if you want to perfrom a verification of intent syncrhonously
|
156
|
+
# - async => true (defaults to false) if you want to perfrom a verification of intent asyncrhonously
|
157
|
+
# - hub => if you want to use an explicit hub, defaults to Superfeedr's http://push.superfeedr.com
|
158
|
+
# It yields 3 arguments to a block:
|
159
|
+
# - body of the response (useful to debug failed notifications)
|
160
|
+
# - success flag
|
161
|
+
# - response (useful to debug failed requests mostly)
|
162
|
+
def unsubscribe(url, id = nil, opts = {}, &blk)
|
163
|
+
endpoint = opts[:hub] || @@superfeedr_endpoint
|
164
|
+
request = prep_request(url, id, endpoint, opts)
|
165
|
+
|
166
|
+
request['hub.mode'] = 'unsubscribe'
|
167
|
+
|
168
|
+
response = http_post(endpoint, request)
|
169
|
+
|
170
|
+
blk.call(response.body, opts[:async] && Integer(response.code) == 202 || Integer(response.code) == 204, response) if blk
|
171
|
+
end
|
172
|
+
|
173
|
+
protected
|
174
|
+
|
175
|
+
def prep_request(url, id, endpoint, opts)
|
176
|
+
feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}"
|
177
|
+
|
178
|
+
request = {
|
179
|
+
'hub.topic' => url,
|
180
|
+
'hub.callback' => generate_callback(url, feed_id)
|
181
|
+
}
|
182
|
+
|
183
|
+
if endpoint == @@superfeedr_endpoint && @@login && @@password
|
184
|
+
request['authorization'] = Base64.encode64( "#{@@login}:#{@@password}" ).chomp
|
122
185
|
end
|
123
186
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
187
|
+
if opts[:async]
|
188
|
+
request['hub.verify'] = 'async'
|
189
|
+
end
|
190
|
+
|
191
|
+
if opts[:sync]
|
192
|
+
request['hub.verify'] = 'sync'
|
193
|
+
end
|
194
|
+
|
195
|
+
request
|
196
|
+
end
|
197
|
+
|
198
|
+
def http_get(url, opts)
|
199
|
+
uri = URI.parse URI.encode(url)
|
200
|
+
uri.query = URI.encode_www_form opts || {}
|
201
|
+
uri.path=='/' if uri.path.empty?
|
202
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
203
|
+
http.use_ssl = true
|
204
|
+
request = Net::HTTP::Get.new uri.request_uri
|
205
|
+
http.request(request)
|
206
|
+
end
|
207
|
+
|
208
|
+
def http_post(url, opts)
|
209
|
+
uri = URI.parse URI.encode(url)
|
210
|
+
uri.path=='/' if uri.path.empty?
|
211
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
212
|
+
http.use_ssl = true
|
213
|
+
request = Net::HTTP::Post.new uri.request_uri
|
214
|
+
request.set_form_data (opts||{})
|
215
|
+
http.request(request)
|
216
|
+
end
|
217
|
+
|
218
|
+
def generate_callback(url, feed_id)
|
219
|
+
if @@scheme == "https"
|
220
|
+
URI::HTTPS.build({:scheme => @@scheme, :host => @@host, :path => "#{@@base_path}#{feed_id}", :port => @@port }).to_s
|
221
|
+
else
|
222
|
+
URI::HTTP.build({:scheme => @@scheme, :host => @@host, :path => "#{@@base_path}#{feed_id}", :port => @@port }).to_s
|
149
223
|
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
module Rack
|
229
|
+
|
230
|
+
##
|
231
|
+
# This is a Rack Middleware that can be used in your rack-compatible web framework (Rails, Sinatra...) to perform subscriptions over at superfeedr
|
232
|
+
# using the PubSubHubbub API.
|
233
|
+
class Superfeedr
|
150
234
|
|
151
235
|
##
|
152
236
|
# This allows you to define what happens with the notifications. The block passed in argument is called for each notification, with 4 arguments
|
@@ -203,13 +287,13 @@ module Rack
|
|
203
287
|
# Called by Rack!
|
204
288
|
def call(env)
|
205
289
|
req = Rack::Request.new(env)
|
206
|
-
if env['REQUEST_METHOD'] == 'GET' && feed_id = env['PATH_INFO'].match(/#{
|
290
|
+
if env['REQUEST_METHOD'] == 'GET' && feed_id = env['PATH_INFO'].match(/#{Rack::Superfeedr.base_path}(.*)/)
|
207
291
|
if @verification.call(req.params['hub.mode'], feed_id[1], req.params['hub.topic'], req)
|
208
292
|
Rack::Response.new(req.params['hub.challenge'], 200).finish
|
209
293
|
else
|
210
294
|
Rack::Response.new("not valid", 404).finish
|
211
295
|
end
|
212
|
-
elsif env['REQUEST_METHOD'] == 'POST' && feed_id = env['PATH_INFO'].match(/#{
|
296
|
+
elsif env['REQUEST_METHOD'] == 'POST' && feed_id = env['PATH_INFO'].match(/#{Rack::Superfeedr.base_path}(.*)/)
|
213
297
|
@callback.call(feed_id[1], req.body.read, req.env['HTTP_X_PUBSUBHUBBUB_TOPIC'], req)
|
214
298
|
Rack::Response.new("Thanks!", 200).finish
|
215
299
|
else
|
@@ -217,57 +301,8 @@ module Rack
|
|
217
301
|
end
|
218
302
|
end
|
219
303
|
|
220
|
-
|
221
|
-
|
222
|
-
def self.prep_request(url, id, endpoint, opts)
|
223
|
-
feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}"
|
224
|
-
|
225
|
-
request = {
|
226
|
-
'hub.topic' => url,
|
227
|
-
'hub.callback' => generate_callback(url, feed_id)
|
228
|
-
}
|
229
|
-
|
230
|
-
if endpoint == @@superfeedr_endpoint && @@login && @@password
|
231
|
-
request['authorization'] = Base64.encode64( "#{@@login}:#{@@password}" ).chomp
|
232
|
-
end
|
304
|
+
extend SuperfeedrAPI
|
233
305
|
|
234
|
-
if opts[:async]
|
235
|
-
request['hub.verify'] = 'async'
|
236
|
-
end
|
237
|
-
|
238
|
-
if opts[:sync]
|
239
|
-
request['hub.verify'] = 'sync'
|
240
|
-
end
|
241
|
-
|
242
|
-
request
|
243
|
-
end
|
244
|
-
|
245
|
-
def self.http_get(url, opts)
|
246
|
-
uri = URI.parse URI.encode(url)
|
247
|
-
uri.query = URI.encode_www_form opts || {}
|
248
|
-
uri.path=='/' if uri.path.empty?
|
249
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
250
|
-
http.use_ssl = true
|
251
|
-
request = Net::HTTP::Get.new uri.request_uri
|
252
|
-
http.request(request)
|
253
|
-
end
|
254
|
-
|
255
|
-
def self.http_post(url, opts)
|
256
|
-
uri = URI.parse URI.encode(url)
|
257
|
-
uri.path=='/' if uri.path.empty?
|
258
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
259
|
-
http.use_ssl = true
|
260
|
-
request = Net::HTTP::Post.new uri.request_uri
|
261
|
-
request.set_form_data (opts||{})
|
262
|
-
http.request(request)
|
263
|
-
end
|
264
|
-
|
265
|
-
def self.generate_callback(url, feed_id)
|
266
|
-
if @@scheme == "https"
|
267
|
-
URI::HTTPS.build({:scheme => @@scheme, :host => @@host, :path => "#{@@base_path}#{feed_id}", :port => @@port }).to_s
|
268
|
-
else
|
269
|
-
URI::HTTP.build({:scheme => @@scheme, :host => @@host, :path => "#{@@base_path}#{feed_id}", :port => @@port }).to_s
|
270
|
-
end
|
271
|
-
end
|
272
306
|
end
|
273
307
|
end
|
308
|
+
|
data/rack-superfeedr.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "rack-superfeedr"
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["julien51"]
|
12
|
-
s.date = "2015-04-
|
12
|
+
s.date = "2015-04-16"
|
13
13
|
s.description = "A gem that provides a rack middleware to interract with Superfeedr's API. "
|
14
14
|
s.email = "julien@superfeedr.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -3,7 +3,7 @@ require 'rack'
|
|
3
3
|
require_relative 'helper.rb'
|
4
4
|
|
5
5
|
# To run tests locally, we're using runscope's passageway which proxies requests inside the firewall. (make sure you bind to port 4567)
|
6
|
-
HOST = '
|
6
|
+
HOST = '39fbb9eba21.b.passageway.io'
|
7
7
|
PORT = 80
|
8
8
|
# Also, we need superfeedr credentials.
|
9
9
|
LOGIN = 'demo'
|
@@ -22,6 +22,11 @@ end
|
|
22
22
|
|
23
23
|
# Run an app in a thread
|
24
24
|
Thread.new do
|
25
|
+
opts = {
|
26
|
+
:Port => 4567,
|
27
|
+
# Logger: WEBrick::Log.new("/dev/null"),
|
28
|
+
AccessLog: []
|
29
|
+
}
|
25
30
|
Rack::Handler::WEBrick.run(Rack::Superfeedr.new(MyRackApp.new) do |superfeedr|
|
26
31
|
|
27
32
|
superfeedr.on_verification do |mode, feed_id, url, request|
|
@@ -39,7 +44,7 @@ Thread.new do
|
|
39
44
|
end
|
40
45
|
|
41
46
|
|
42
|
-
end,
|
47
|
+
end, opts)
|
43
48
|
end
|
44
49
|
sleep 3
|
45
50
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-superfeedr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-04-
|
12
|
+
date: 2015-04-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -159,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
159
159
|
version: '0'
|
160
160
|
segments:
|
161
161
|
- 0
|
162
|
-
hash:
|
162
|
+
hash: 1123161724621889271
|
163
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
164
|
none: false
|
165
165
|
requirements:
|