Class: Rack::Superfeedr

Inherits:
Object
  • Object
show all
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

SUPERFEEDR_ENDPOINT =
"http://superfeedr.com/hubbub"

Instance Method Summary collapse

Constructor Details

#initialize(app, params = {}, &block) ⇒ Superfeedr

When using this Rack, you need to supply the following arguments:

  • :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.

Raises:

  • (ArgumentError)


79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/rack-superfeedr.rb', line 79

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
  @app = app
  block.call(self)
end

Instance Method Details

#call(env) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rack-superfeedr.rb', line 92

def call(env)
  req = Rack::Request.new(env)
  if env['REQUEST_METHOD'] == 'GET' && feed_id = env['REQUEST_PATH'].match(/\/superfeedr\/feed\/(.*)/)
    # 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['REQUEST_PATH'].match(/\/superfeedr\/feed\/(.*)/)
    # Notification
    content = nil
    content_type = env["CONTENT_TYPE"].split(";").first
    if content_type == "application/json"
      # Let's parse the body as JSON
      content = JSON.parse(req.body.read)
    elsif content_type == "application/atom+xml"
      # Let's parse the body as ATOM using nokogiri
      content = Nokogiri.parse(req.body.read)
    end
    # Let's now send that data back to the user.
    if !@callback.call(content, feed_id[1])
      # We need to unsubscribe the user
    end
    Rack::Response.new("Thanks!", 200).finish
  else
    @app.call(env)
  end
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



68
69
70
# File 'lib/rack-superfeedr.rb', line 68

def on_notification(&block)
  @callback = block 
end

#subscribe(url, id, &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



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rack-superfeedr.rb', line 19

def subscribe(url, id, &block)
  feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}"
  if block
    @verifications[feed_id] ||= {}
    @verifications[feed_id]['subscribe'] = block
  end
  response = Typhoeus::Request.post(SUPERFEEDR_ENDPOINT, 
  :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"
    },
  :username => @params[:login], 
  :password => @params[:password]
  )
  @params[:async] && response.code == 202 || response.code == 204 # We return true to indicate the status.
end

#unsubscribe(url, id, &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



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rack-superfeedr.rb', line 45

def unsubscribe(url, id, &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, 
  :params => {
    :hub.mode' => 'unsubscribe', 
    :hub.verify' => @params[:async] ? 'async' : 'sync',
    :hub.topic' => url,
    :hub.callback' =>  generate_callback(url, feed_id)
    },
  :username => @params[:login], 
  :password => @params[:password]
  )
  @params[:async] && response.code == 202 || response.code == 204 # We return true to indicate the status.
end