Class: Rack::Superfeedr
- Inherits:
-
Object
- Object
- Rack::Superfeedr
- Defined in:
- lib/rack-superfeedr.rb
Overview
This is a Rack Middleware that can be used in your rack-compatible web framework (Rails, Sinatra…) to perform subscriptions over at superfeedr using the PubSubHubbub API.
Constant Summary collapse
- SUPERFEEDR_ENDPOINT =
"https://push.superfeedr.com"
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#error ⇒ Object
Shows the latest error received by the API.
-
#initialize(app, params = {}, &block) ⇒ Superfeedr
constructor
When using this Rack, you need to supply the following params (2nd argument): - :host (the host for your web app. Used to build the callback urls.) - :login - :password - :format (atom|json, atom being default) - :async (true|false), false is default.
-
#on_notification(&block) ⇒ Object
This allows you to define what happens with the notifications.
- #params ⇒ Object
- #reset(params = {}) ⇒ Object
-
#subscribe(url, id = nil, opts = {}, &block) ⇒ Object
Subscribe you to a url.
-
#unsubscribe(url, id = nil, opts = {}, &block) ⇒ Object
Unsubscribes a url.
Constructor Details
#initialize(app, params = {}, &block) ⇒ Superfeedr
When using this Rack, you need to supply the following params (2nd argument):
-
:host (the host for your web app. Used to build the callback urls.)
-
:login
-
:password
-
:format (atom|json, atom being default)
-
:async (true|false), false is default. You need to set that to false if you’re using platforms like Heroku that may disallow concurrency.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rack-superfeedr.rb', line 102 def initialize(app, params = {}, &block) raise ArgumentError, 'Missing :host in params' unless params[:host] raise ArgumentError, 'Missing :login in params' unless params[:login] raise ArgumentError, 'Missing :password in params' unless params[:password] @callback = Proc.new { |notification, feed_id| # Bh default, do nothing } @verifications = {} @params = params @params[:port] = 80 unless params[:port] @app = app @base_path = params[:base_path] || '/superfeedr/feed/' block.call(self) self end |
Instance Method Details
#call(env) ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/rack-superfeedr.rb', line 129 def call(env) req = Rack::Request.new(env) if env['REQUEST_METHOD'] == 'GET' && feed_id = env['PATH_INFO'].match(/#{@base_path}(.*)/) # Verification of intent! if @verifications[feed_id[1]] && verification = @verifications[feed_id[1]][req.params['hub.mode']] # Check with the user if verification.call(req.params['hub.topic'], feed_id[1]) Rack::Response.new(req.params['hub.challenge'], 200).finish else Rack::Response.new("not valid", 404).finish end else # By default, we accept all Rack::Response.new(req.params['hub.challenge'], 200).finish end elsif env['REQUEST_METHOD'] == 'POST' && feed_id = env['PATH_INFO'].match(/#{@base_path}(.*)/) # Notification content = nil # Body is a stream, not a string, so capture it once and save it body = req.body.read if env["CONTENT_TYPE"] content_type = env["CONTENT_TYPE"].split(";").first else content_type = "application/atom+xml" #default? end if content_type == "application/json" # Let's parse the body as JSON content = JSON.parse(body) elsif content_type == "application/atom+xml" # Let's parse the body as ATOM using nokogiri content = Nokogiri.XML(body) end # Let's now send that data back to the user. info = Hashie::Mash.new(req: req, body: body) if !@callback.call(content, feed_id[1], info) # We need to unsubscribe the user end Rack::Response.new("Thanks!", 200).finish else @app.call(env) end end |
#error ⇒ Object
Shows the latest error received by the API. Useful when a subscription or unsubscription request fails.
20 21 22 |
# File 'lib/rack-superfeedr.rb', line 20 def error @error end |
#on_notification(&block) ⇒ Object
This allows you to define what happens with the notifications. The block passed in argument is called for each notification, with 2 arguments
-
the payload itself (ATOM or JSON, based on what you selected in the params)
-
the id for the feed, if you used any upon subscription
91 92 93 |
# File 'lib/rack-superfeedr.rb', line 91 def on_notification(&block) @callback = block end |
#params ⇒ Object
125 126 127 |
# File 'lib/rack-superfeedr.rb', line 125 def params @params end |
#reset(params = {}) ⇒ Object
118 119 120 121 122 123 |
# File 'lib/rack-superfeedr.rb', line 118 def reset(params = {}) raise ArgumentError, 'Missing :host in params' unless params[:host] raise ArgumentError, 'Missing :login in params' unless params[:login] raise ArgumentError, 'Missing :password in params' unless params[:password] @params = params end |
#subscribe(url, id = nil, opts = {}, &block) ⇒ Object
Subscribe you to a url. id is optional, but recommanded has a unique identifier for this url. It will be used to help you identify which feed is concerned by a notification. The optional block will be called to let you confirm the subscription (or not). It returns true if the subscription was successful (or will be confirmed if you used async => true in the options), false otherwise. You can also pass an opts third argument that will be merged with the options used in Typhoeus’s Request (github.com/dbalatero/typhoeus) A useful option is :verbose => true for example.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rack-superfeedr.rb', line 31 def subscribe(url, id = nil, opts = {}, &block) feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}" if block @verifications[feed_id] ||= {} @verifications[feed_id]['subscribe'] = block end endpoint = opts[:hub] || SUPERFEEDR_ENDPOINT opts.delete(:hub) if endpoint == SUPERFEEDR_ENDPOINT opts[:userpwd] = "#{@params[:login]}:#{@params[:password]}" end response = ::Typhoeus::Request.post(endpoint, opts.merge({ :params => { :'hub.mode' => 'subscribe', :'hub.verify' => @params[:async] ? 'async' : 'sync', :'hub.topic' => url, :'hub.callback' => generate_callback(url, feed_id) }, :headers => { :Accept => @params[:format] == "json" ? "application/json" : "application/atom+xml" } })) @error = response.body @params[:async] && response.code == 202 || response.code == 204 # We return true to indicate the status. end |
#unsubscribe(url, id = nil, opts = {}, &block) ⇒ Object
Unsubscribes a url. If you used an id for the susbcription, you need to use _the same_. The optional block will be called to let you confirm the subscription (or not). This is not applicable for if you use params => true It returns true if the unsubscription was successful (or will be confirmed if you used async => true in the options), false otherwise You can also pass an opts third argument that will be merged with the options used in Typhoeus’s Request (github.com/dbalatero/typhoeus) A useful option is :verbose => true for example.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/rack-superfeedr.rb', line 67 def unsubscribe(url, id = nil, opts = {}, &block) feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}" if block @verifications[feed_id] ||= {} @verifications[feed_id]['unsubscribe'] = block end response = ::Typhoeus::Request.post(SUPERFEEDR_ENDPOINT, opts.merge({ :params => { :'hub.mode' => 'unsubscribe', :'hub.verify' => @params[:async] ? 'async' : 'sync', :'hub.topic' => url, :'hub.callback' => generate_callback(url, feed_id) }, :userpwd => "#{@params[:login]}:#{@params[:password]}" })) @error = response.body @params[:async] && response.code == 202 || response.code == 204 # We return true to indicate the status. end |