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

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

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

- (Object) call(env)



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

- (Object) on_notification(&block)

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

- (Object) subscribe(url, id, &block)

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

- (Object) unsubscribe(url, id, &block)

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