rack-superfeedr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'rack'
6
+ gem 'nokogiri'
7
+ gem 'typhoeus'
8
+
9
+
10
+ # Add dependencies to develop your gem here.
11
+ # Include everything needed to run rake, tests, features, etc.
12
+ group :development do
13
+ gem "shoulda", ">= 0"
14
+ gem "bundler", "~> 1.0.0"
15
+ gem "jeweler", "~> 1.6.4"
16
+ gem "rcov", ">= 0"
17
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 julien
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = rack-superfeedr
2
+
3
+ A gem that provides a rack middleware to interact with Superfeedr's PubSubHubbub API. It let you subscribe, unsubscribe and receive incoming notifications.
4
+
5
+ == Contributing to rack-superfeedr
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2012 Superfeedr. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "rack-superfeedr"
18
+ gem.homepage = "http://github.com/superfeedr/rack-superfeedr"
19
+ gem.license = "MIT"
20
+ gem.add_dependency "rack"
21
+ gem.add_dependency "nokogiri"
22
+ gem.add_dependency "typhoeus"
23
+ gem.summary = %Q{A gem that provides a rack middleware to interract with Superfeedr's API. }
24
+ gem.description = %Q{A gem that provides a rack middleware to interract with Superfeedr's API. }
25
+ gem.email = "julien@superfeedr.com"
26
+ gem.authors = ["julien51"]
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ test.rcov_opts << '--exclude "gems/*"'
44
+ end
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "rack-superfeedr #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,21 @@
1
+ require 'sinatra'
2
+ require 'rack-superfeedr'
3
+
4
+ use Rack::Superfeedr, { :host => "plant-leg.showoff.io", :login => "demo", :password => "demo", :format => "json", :async => false } do |superfeedr|
5
+ Superfeedr = superfeedr
6
+ superfeedr.on_notification do |notification|
7
+ puts notification.to_s
8
+ end
9
+ end
10
+
11
+ get '/hi' do
12
+ "Hello World!"
13
+ end
14
+
15
+ get '/subscribe' do
16
+ Superfeedr.subscribe("http://push-pub.appspot.com/feed", 123)
17
+ end
18
+
19
+ get '/unsubscribe' do
20
+ Superfeedr.unsubscribe("http://push-pub.appspot.com/feed", 123)
21
+ end
@@ -0,0 +1,135 @@
1
+ require 'base64'
2
+ require 'typhoeus'
3
+ require 'json'
4
+ require 'nokogiri'
5
+
6
+ module Rack
7
+ ##
8
+ # This is a Rack Middleware that can be used in your rack-compatible web framework (Rails, Sinatra...) to perform subscriptions over at superfeedr
9
+ # using the PubSubHubbub API.
10
+ class Superfeedr
11
+
12
+ SUPERFEEDR_ENDPOINT = "http://superfeedr.com/hubbub"
13
+
14
+ ##
15
+ # 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
16
+ # is concerned by a notification.
17
+ # The optional block will be called to let you confirm the subscription (or not).
18
+ # It returns true if the subscription was successful (or will be confirmed if you used async => true in the options), false otherwise
19
+ def subscribe(url, id, &block)
20
+ feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}"
21
+ if block
22
+ @verifications[feed_id] ||= {}
23
+ @verifications[feed_id]['subscribe'] = block
24
+ end
25
+ response = Typhoeus::Request.post(SUPERFEEDR_ENDPOINT,
26
+ :params => {
27
+ :'hub.mode' => 'subscribe',
28
+ :'hub.verify' => @params[:async] ? 'async' : 'sync',
29
+ :'hub.topic' => url,
30
+ :'hub.callback' => generate_callback(url, feed_id)
31
+ },
32
+ :headers => {
33
+ :Accept => @params[:format] == "json" ? "application/json" : "application/atom+xml"
34
+ },
35
+ :username => @params[:login],
36
+ :password => @params[:password]
37
+ )
38
+ @params[:async] && response.code == 202 || response.code == 204 # We return true to indicate the status.
39
+ end
40
+
41
+ ##
42
+ # Unsubscribes a url. If you used an id for the susbcription, you need to use _the same_.
43
+ # 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
44
+ # It returns true if the unsubscription was successful (or will be confirmed if you used async => true in the options), false otherwise
45
+ def unsubscribe(url, id, &block)
46
+ feed_id = "#{id ? id : Base64.urlsafe_encode64(url)}"
47
+ if block
48
+ @verifications[feed_id] ||= {}
49
+ @verifications[feed_id]['unsubscribe'] = block
50
+ end
51
+ response = Typhoeus::Request.post(SUPERFEEDR_ENDPOINT,
52
+ :params => {
53
+ :'hub.mode' => 'unsubscribe',
54
+ :'hub.verify' => @params[:async] ? 'async' : 'sync',
55
+ :'hub.topic' => url,
56
+ :'hub.callback' => generate_callback(url, feed_id)
57
+ },
58
+ :username => @params[:login],
59
+ :password => @params[:password]
60
+ )
61
+ @params[:async] && response.code == 202 || response.code == 204 # We return true to indicate the status.
62
+ end
63
+
64
+ ##
65
+ # This allows you to define what happens with the notifications. The block passed in argument is called for each notification, with 2 arguments
66
+ # - the payload itself (ATOM or JSON, based on what you selected in the params)
67
+ # - the id for the feed, if you used any upon subscription
68
+ def on_notification(&block)
69
+ @callback = block
70
+ end
71
+
72
+ ##
73
+ # When using this Rack, you need to supply the following arguments:
74
+ # - :host (the host for your web app. Used to build the callback urls.)
75
+ # - :login
76
+ # - :password
77
+ # - :format (atom|json, atom being default)
78
+ # - :async (true|false), false is default. You need to set that to false if you're using platforms like Heroku that may disallow concurrency.
79
+ def initialize(app, params = {}, &block)
80
+ raise ArgumentError, 'Missing :host in params' unless params[:host]
81
+ raise ArgumentError, 'Missing :login in params' unless params[:login]
82
+ raise ArgumentError, 'Missing :password in params' unless params[:password]
83
+ @callback = Proc.new { |notification, feed_id|
84
+ # Bh default, do nothing
85
+ }
86
+ @verifications = {}
87
+ @params = params
88
+ @app = app
89
+ block.call(self)
90
+ end
91
+
92
+ def call(env)
93
+ req = Rack::Request.new(env)
94
+ if env['REQUEST_METHOD'] == 'GET' && feed_id = env['REQUEST_PATH'].match(/\/superfeedr\/feed\/(.*)/)
95
+ # Verification of intent!
96
+ if @verifications[feed_id[1]] && verification = @verifications[feed_id[1]][req.params['hub.mode']]
97
+ # Check with the user
98
+ if verification.call(req.params['hub.topic'], feed_id[1])
99
+ Rack::Response.new(req.params['hub.challenge'], 200).finish
100
+ else
101
+ Rack::Response.new("not valid", 404).finish
102
+ end
103
+ else
104
+ # By default, we accept all
105
+ Rack::Response.new(req.params['hub.challenge'], 200).finish
106
+ end
107
+ elsif env['REQUEST_METHOD'] == 'POST' && feed_id = env['REQUEST_PATH'].match(/\/superfeedr\/feed\/(.*)/)
108
+ # Notification
109
+ content = nil
110
+ content_type = env["CONTENT_TYPE"].split(";").first
111
+ if content_type == "application/json"
112
+ # Let's parse the body as JSON
113
+ content = JSON.parse(req.body.read)
114
+ elsif content_type == "application/atom+xml"
115
+ # Let's parse the body as ATOM using nokogiri
116
+ content = Nokogiri.parse(req.body.read)
117
+ end
118
+ # Let's now send that data back to the user.
119
+ if !@callback.call(content, feed_id[1])
120
+ # We need to unsubscribe the user
121
+ end
122
+ Rack::Response.new("Thanks!", 200).finish
123
+ else
124
+ @app.call(env)
125
+ end
126
+ end
127
+
128
+ protected
129
+
130
+ def generate_callback(url, feed_id)
131
+ URI::HTTP.build({:host => @params[:host], :path => "/superfeedr/feed/#{feed_id}" }).to_s
132
+ end
133
+
134
+ end
135
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'rack-superfeedr'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestRackSuperfeedr < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-superfeedr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - julien51
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-19 00:00:00.000000000 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ requirement: &2157754460 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2157754460
26
+ - !ruby/object:Gem::Dependency
27
+ name: nokogiri
28
+ requirement: &2157753260 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2157753260
37
+ - !ruby/object:Gem::Dependency
38
+ name: typhoeus
39
+ requirement: &2157751980 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2157751980
48
+ - !ruby/object:Gem::Dependency
49
+ name: shoulda
50
+ requirement: &2157750620 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *2157750620
59
+ - !ruby/object:Gem::Dependency
60
+ name: bundler
61
+ requirement: &2157749360 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 1.0.0
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *2157749360
70
+ - !ruby/object:Gem::Dependency
71
+ name: jeweler
72
+ requirement: &2157747820 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.6.4
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *2157747820
81
+ - !ruby/object:Gem::Dependency
82
+ name: rcov
83
+ requirement: &2157745760 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *2157745760
92
+ - !ruby/object:Gem::Dependency
93
+ name: rack
94
+ requirement: &2157744040 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ type: :runtime
101
+ prerelease: false
102
+ version_requirements: *2157744040
103
+ - !ruby/object:Gem::Dependency
104
+ name: nokogiri
105
+ requirement: &2157738420 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: *2157738420
114
+ - !ruby/object:Gem::Dependency
115
+ name: typhoeus
116
+ requirement: &2157737340 !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ type: :runtime
123
+ prerelease: false
124
+ version_requirements: *2157737340
125
+ description: ! 'A gem that provides a rack middleware to interract with Superfeedr''s
126
+ API. '
127
+ email: julien@superfeedr.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files:
131
+ - LICENSE.txt
132
+ - README.rdoc
133
+ files:
134
+ - .document
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.rdoc
138
+ - Rakefile
139
+ - VERSION
140
+ - examples/sinatra_app.rb
141
+ - lib/rack-superfeedr.rb
142
+ - test/helper.rb
143
+ - test/test_rack-superfeedr.rb
144
+ has_rdoc: true
145
+ homepage: http://github.com/superfeedr/rack-superfeedr
146
+ licenses:
147
+ - MIT
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ segments:
159
+ - 0
160
+ hash: -1927463494459343346
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 1.6.2
170
+ signing_key:
171
+ specification_version: 3
172
+ summary: A gem that provides a rack middleware to interract with Superfeedr's API.
173
+ test_files: []