superfeedr-superfeedr-ruby 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 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,69 @@
1
+ = superfeedr-ruby
2
+
3
+ Yes, the gem is called superfeedr-ruby, but the library is superfeedr only ;)
4
+
5
+ {Superfeedr.com}[http://superfeedr.com] is a Real-time Cloud Feed Parsing service
6
+
7
+ * *Realtime* : By combining different technologies we can notify you of new entries in less than 15 minutes (or it's free)
8
+ * *No* *more* *polling* : Stop wasting your time and resources fetching 'old data', and stop being late when there is new data.
9
+ * *Simplicity* : give us urls, and we'll do the rest. Check our docs and tutorials.
10
+ * *Standardization* : No more nightmares with gazillions of formats, we will send you strict ATOM, whatever the original feed format is.
11
+ * *Multi*-*channel* : receive the notifications by XMPP, on a local JID or your own external JID, or using WebHooks.
12
+ * *Cost* *saving* : we will match the cost of your existing system.
13
+
14
+ == FEATURES:
15
+
16
+ * Subscribe to a feed
17
+
18
+ Superfeedr.subscribe("http://github.com/superfeedr.atom") do |result|
19
+ puts "Yay, subscribed to the github Atom feed for Superfeedr" if result
20
+ end
21
+
22
+ * Unsubscribe from a feed
23
+
24
+ Superfeedr.unsubscribe("http://github.com/superfeedr.atom") do |result|
25
+ puts "Sad, you unsubscribed from the github Atom feed for Superfeedr" if result
26
+ end
27
+
28
+ * List subscriptions by page
29
+
30
+ Superfeedr.subscriptions(5) do |page, feeds|
31
+ puts "On page #{page}" :
32
+ puts feeds.inspect
33
+ end
34
+
35
+ * Receive notifications
36
+
37
+ Superfeedr.on_notification do |notification|
38
+ puts "The feed #{notification.feed_url} has been fetched (#{notification.http_status}: #{notification.message_status}) and will be fetched again in #{(notification.next_fetch - Time.now)/60} minutes."
39
+ notification.entries.each do |e|
40
+ puts " - #{e.title} (#{e.link}) was published (#{e.published}) with #{e.unique_id} as unique id : \n #{e.summary} (#{e.chunk}/#{e.chunks})"
41
+ end
42
+ end
43
+
44
+ == Install
45
+
46
+ Install the gem from Github :
47
+
48
+ gem sources -a http://gems.github.com
49
+ sudo gem install superfeedr-superfeedr-ruby
50
+
51
+ Source :
52
+
53
+ git clone git@github.com:superfeedr/superfeedr-ruby.git
54
+
55
+
56
+ == Example
57
+
58
+ Please see this {Gist}[http://gist.github.com/110247] while I am trying to find a way to integrate it into this document ;)
59
+
60
+
61
+ == REQUIREMENTS:
62
+
63
+ {Babylon}[http://github.com/julien51/babylon/tree/master] : please note that there are 2 gems named Baylon. The one you want is the one about XMPP, you can get it with :
64
+
65
+ sudo gem install babylon-julien51
66
+
67
+ == Copyright
68
+
69
+ Copyright (c) 2009 julien. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,80 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ config = YAML.load(File.read('VERSION.yml'))
8
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
9
+ gem.name = "superfeedr-ruby"
10
+ gem.version = "#{version}"
11
+ gem.summary = %Q{Ruby Client for the Superfeedr}
12
+ gem.email = "julien.genestoux@gmail.com"
13
+ gem.homepage = "http://github.com/julien51/superfeedr-ruby"
14
+ gem.authors = ["julien Genestoux"]
15
+ gem.rubyforge_project = "superfeedr-ruby"
16
+ gem.add_dependency('julien51-babylon')
17
+ gem.add_dependency('nokogiri')
18
+ gem.has_rdoc = true
19
+ gem.homepage = 'http://github.com/julien51/superfeedr-ruby/'
20
+ gem.files = FileList["[A-Z]*", "{bin,generators,lib,test,spec}/**/*"]
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
24
+ end
25
+
26
+ require 'spec/rake/spectask'
27
+ Spec::Rake::SpecTask.new(:spec) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.spec_files = FileList['spec/**/*_spec.rb']
30
+ end
31
+
32
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ if File.exist?('VERSION.yml')
44
+ config = YAML.load(File.read('VERSION.yml'))
45
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
46
+ else
47
+ version = ""
48
+ end
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "superfeedr-ruby #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
55
+
56
+ begin
57
+ require 'rake/contrib/sshpublisher'
58
+ namespace :rubyforge do
59
+
60
+ desc "Release gem and RDoc documentation to RubyForge"
61
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
62
+
63
+ namespace :release do
64
+ desc "Publish RDoc to RubyForge."
65
+ task :docs => [:rdoc] do
66
+ config = YAML.load(
67
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
68
+ )
69
+
70
+ host = "#{config['username']}@rubyforge.org"
71
+ remote_dir = "/var/www/gforge-projects/superfeedr-ruby/"
72
+ local_dir = 'rdoc'
73
+
74
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
75
+ end
76
+ end
77
+ end
78
+ rescue LoadError
79
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
80
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :minor: 2
@@ -0,0 +1,36 @@
1
+ class IqQueryStanza
2
+
3
+ def initialize(params = {})
4
+ @doc = Nokogiri::XML::Document.new
5
+ @iq = Nokogiri::XML::Node.new("iq", @doc)
6
+ @iq["type"] = params[:type].to_s
7
+ @iq["to"] = "firehoser.superfeedr.com"
8
+ @iq["id"] = "#{random_iq_id}"
9
+ @iq["from"] = params[:from] if params[:from]
10
+ end
11
+
12
+ def type
13
+ @iq["type"]
14
+ end
15
+
16
+ def to
17
+ @iq["to"]
18
+ end
19
+
20
+ def from
21
+ @iq["from"]
22
+ end
23
+
24
+ def id
25
+ @iq["id"]
26
+ end
27
+
28
+ def random_iq_id
29
+ rand(1000)
30
+ end
31
+
32
+ def to_s
33
+ @iq.to_s
34
+ end
35
+
36
+ end
@@ -0,0 +1,66 @@
1
+ ##
2
+ # Repesents the items published by the firehoser (the feed entries).
3
+ # They have accessors for the following fields :
4
+ # - title
5
+ # - summary
6
+ # - link
7
+ # - published
8
+ # - unique_id
9
+ # - chunks (long entries might be notified in several chunks)
10
+ # - chunk (current chunk out of chunks)
11
+ #
12
+ class Item
13
+ include SAXMachine
14
+ element :item, :as => :chunk, :value => :chunk
15
+ element :item, :as => :chunks, :value => :chunks
16
+ element :title
17
+ element :summary
18
+ element :link, :as => :link, :value => :href
19
+ element :id, :as => :unique_id
20
+ element :published
21
+
22
+ def published
23
+ Time.parse(@published)
24
+ end
25
+
26
+ def chunks
27
+ @chunks.to_i
28
+ end
29
+
30
+ def chunk
31
+ @chunk.to_i
32
+ end
33
+
34
+ end
35
+
36
+
37
+ ##
38
+ # Notification : sent every time a feed has been fetched. It has the following methods:
39
+ # - message_status : a simple message that gives information about the last fetch
40
+ # - http_status : status of the http response
41
+ # - feed_url : url of the feed
42
+ # - next_fetch : Time when the feed will be fetched again (this is purely informative and it might change)
43
+ # - items : array of new items detected (might be empty)
44
+ class NotificationStanza
45
+ include SAXMachine
46
+
47
+ def initialize(xml)
48
+ parse(xml.to_s)
49
+ end
50
+
51
+ def next_fetch
52
+ Time.parse(@next_fetch)
53
+ end
54
+
55
+ def http_status
56
+ @http_status.to_i
57
+ end
58
+
59
+ element :http, :as => :message_status
60
+ element :http, :as => :http_status, :value => :code
61
+ element :status, :value => :feed, :as => :feed_url
62
+ element :next_fetch
63
+ elements :item, :as => :entries, :class => Item
64
+
65
+ end
66
+
@@ -0,0 +1,18 @@
1
+ class SubscribeQueryStanza < IqQueryStanza
2
+
3
+ def initialize(params)
4
+ super(params.merge({:type => :set}))
5
+ pubsub = Nokogiri::XML::Node.new("pubsub", @doc)
6
+ pubsub["xmlns"] = "http://jabber.org/protocol/pubsub"
7
+ subscribe = Nokogiri::XML::Node.new("subscribe", @doc)
8
+ subscribe["node"] = params[:node]
9
+ subscribe["jid"] = from.split("/").first
10
+ pubsub.add_child(subscribe)
11
+ @iq.add_child(pubsub)
12
+ end
13
+
14
+ def node
15
+ @iq.search("subscribe").first["node"]
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ class SubscriptionsQueryStanza < IqQueryStanza
2
+
3
+ def initialize(params)
4
+ super(params.merge({:type => :get}))
5
+ pubsub = Nokogiri::XML::Node.new("pubsub", @doc)
6
+ pubsub["xmlns"] = "http://jabber.org/protocol/pubsub"
7
+ @iq.add_child(pubsub)
8
+ subscriptions = Nokogiri::XML::Node.new("subscriptions", @doc)
9
+ subscriptions["page"] = params[:page].to_s
10
+ subscriptions["jid"] = from.split("/").first
11
+ pubsub.add_child(subscriptions)
12
+ end
13
+
14
+ def page
15
+ @iq.search("subscriptions").first["page"]
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ class UnsubscribeQueryStanza < IqQueryStanza
2
+
3
+ def initialize(params)
4
+ super(params.merge({:type => :set}))
5
+ pubsub = Nokogiri::XML::Node.new("pubsub", @doc)
6
+ pubsub["xmlns"] = "http://jabber.org/protocol/pubsub"
7
+ @iq.add_child(pubsub)
8
+ unsubscribe = Nokogiri::XML::Node.new("unsubscribe", @doc)
9
+ unsubscribe["node"] = params[:node].to_s
10
+ unsubscribe["jid"] = from.split("/").first
11
+ pubsub.add_child(unsubscribe)
12
+ end
13
+
14
+ def node
15
+ @iq.search("unsubscribe").first["node"]
16
+ end
17
+
18
+ end
data/lib/superfeedr.rb ADDED
@@ -0,0 +1,163 @@
1
+ require "babylon"
2
+ require "nokogiri"
3
+ require "stanzas/iq_query_stanza.rb"
4
+ require "stanzas/notification_stanza.rb"
5
+ require "stanzas/subscribe_query_stanza.rb"
6
+ require "stanzas/unsubscribe_query_stanza.rb"
7
+ require "stanzas/subscriptions_query_stanza.rb"
8
+
9
+ ##
10
+ # Based on the API documented there : http://superfeedr.com/documentation
11
+ module Superfeedr
12
+
13
+ class NotConnected < StandardError; end
14
+
15
+ @@connection = nil
16
+ @@callbacks = {}
17
+ @@connection_callback = nil
18
+ @@notication_callback = nil
19
+
20
+ ##
21
+ # Connects your client to the Superfeedr.com XMPP server. You need to pass the following arguments :
22
+ # "jid" : login@superfeedr.com
23
+ # "password" : your superfeedr.com password
24
+ # ["host" : host for your jid or component : only useful if you use an external jid ]
25
+ # ["port" : port for your jid or component : only useful if you use an external jid ]
26
+ # ["app_type" : (client | component) only useful if you use an external jid ]
27
+ # The optional block will be called upon connection.
28
+ def self.connect(jid, password, host = nil, port = nil, app_type = "client", &block)
29
+ params = {
30
+ "jid" => jid,
31
+ "password" => password,
32
+ "host" => host,
33
+ "port" => port
34
+ }
35
+ @@connection_callback = block
36
+
37
+ run = Proc.new {
38
+ if app_type == "client"
39
+ Babylon::ClientConnection.connect(params, self)
40
+ else
41
+ Babylon::ComponentConnection.connect(params, self)
42
+ end
43
+ }
44
+
45
+ if EventMachine.reactor_running?
46
+ run.call
47
+ else
48
+ EventMachine.run {
49
+ run.call
50
+ }
51
+ end
52
+ end
53
+
54
+ ##
55
+ # Subscribe to a feed. The block passed in argument will be called upon success.
56
+ # The block will take one boolen argument : true means everything went right... false means something failed!
57
+ # (Please set Babylon's log to Log4r::INFO for more info)
58
+ def self.subscribe(feed_url, &block)
59
+ raise NotConnected unless connection
60
+ stanza = SubscribeQueryStanza.new({:node => feed_url, :from => connection.jid})
61
+ @@callbacks[stanza.id] = Hash.new
62
+ @@callbacks[stanza.id][:method] = method(:on_subscribe)
63
+ @@callbacks[stanza.id][:param] = block
64
+ send(stanza)
65
+ end
66
+
67
+ ##
68
+ # Unsubscribe from a feed. The block passed in argument will be called upon success.
69
+ # The block will take one boolen argument : true means everything went right... false means something failed!
70
+ # (Please set Babylon's log to Log4r::INFO for more info)
71
+ def self.unsubscribe(feed_url, &block)
72
+ raise NotConnected unless connection
73
+ stanza = UnsubscribeQueryStanza.new({:node => feed_url, :from => connection.jid})
74
+ @@callbacks[stanza.id] = Hash.new
75
+ @@callbacks[stanza.id][:method] = method(:on_unsubscribe)
76
+ @@callbacks[stanza.id][:param] = block
77
+ send(stanza)
78
+ end
79
+
80
+ ##
81
+ # Lists the subscriptions by page. The block passed in argument will be called with 2 arguments : the page,
82
+ # and an array of the feed's url in the page you requested.
83
+ # (Currently the Superfeedr API only supports 30 feeds per page.)
84
+ def self.subscriptions(page = 1, &block)
85
+ raise NotConnected unless connection
86
+ stanza = SubscriptionsQueryStanza.new({:page => page, :from => connection.jid})
87
+ @@callbacks[stanza.id] = Hash.new
88
+ @@callbacks[stanza.id][:method] = method(:on_subscriptions)
89
+ @@callbacks[stanza.id][:param] = block
90
+ send(stanza)
91
+ end
92
+
93
+ ##
94
+ # Specifies the block that will be called upon notification.
95
+ # Your block should take a NotificationStanza instance argument.
96
+ def self.on_notification(&block)
97
+ @@notication_callback = block
98
+ end
99
+
100
+ ##
101
+ # Called with a response to a subscriptions listing
102
+ def self.on_subscriptions(stanza, &block)
103
+ page = stanza.xpath('//xmlns:subscriptions', { 'xmlns' => 'http://jabber.org/protocol/pubsub' }).first["page"].to_i
104
+ feeds = stanza.xpath('//xmlns:subscription', { 'xmlns' => 'http://jabber.org/protocol/pubsub' }).map { |s| s["node"] }
105
+ block.call(page, feeds)
106
+ end
107
+
108
+ ##
109
+ # Called with a response to a subscribe
110
+ def self.on_subscribe(stanza, &block)
111
+ block.call(stanza["type"] == "result")
112
+ end
113
+
114
+ ##
115
+ # Called with a response to an unsubscribe.
116
+ def self.on_unsubscribe(stanza, &block)
117
+ block.call(stanza["type"] == "result")
118
+ end
119
+
120
+ ##
121
+ # ::nodoc::
122
+ def self.callbacks
123
+ @@callbacks
124
+ end
125
+
126
+ ##
127
+ # ::nodoc::
128
+ def self.connection
129
+ @@connection
130
+ end
131
+
132
+ ##
133
+ # ::nodoc::
134
+ def self.send(xml)
135
+ connection.send_xml(xml)
136
+ end
137
+
138
+ ##
139
+ # ::nodoc::
140
+ def self.on_connected(connection)
141
+ @@connection = connection
142
+ @@connection_callback.call
143
+ end
144
+
145
+ ##
146
+ # ::nodoc::
147
+ def self.on_disconnected()
148
+ @@connection = false
149
+ end
150
+
151
+ ##
152
+ # This shall not be called by your application. It is called upon stanza recetion. If it is a reply to a stanza we sent earlier, then, we just call it's associated callback. If it is a notification stanza, then, we call the notification callback (that you should have given when calling Superfeedr.connect) with a NotificationStanza instance.
153
+ def self.on_stanza(stanza)
154
+ if stanza["id"] && @@callbacks[stanza["id"]]
155
+ @@callbacks[stanza["id"]][:method].call(stanza, &@@callbacks[stanza["id"]][:param])
156
+ @@callbacks.delete(stanza["id"])
157
+ elsif stanza.name == "message" and stanza.at("event")["xmlns"] == "http://jabber.org/protocol/pubsub#event"
158
+ @@notication_callback.call(NotificationStanza.new(stanza))
159
+ # Here we need to call the main notification callback!
160
+ end
161
+ end
162
+
163
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format nested
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,11 @@
1
+ require 'spec'
2
+ require "rubygems"
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'superfeedr'
7
+ require "babylon"
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ shared_examples_for "Iq Query Stanzas" do
4
+
5
+ it "should have the right type" do
6
+ IqQueryStanza.new(@params).type.should == @params[:type]
7
+ end
8
+
9
+ it "should have the right to" do
10
+ IqQueryStanza.new(@params).to.should == "firehoser.superfeedr.com"
11
+ end
12
+
13
+ it "should have a random id" do
14
+ IqQueryStanza.new(@params).id.should match /[0..9]*/
15
+ end
16
+
17
+ it "should have the right from" do
18
+ IqQueryStanza.new(@params).from.should == @params[:from]
19
+ end
20
+
21
+ end
22
+
23
+ describe IqQueryStanza do
24
+ before(:each) do
25
+ @params = { :type => "set", :from => "me@server.com/resource"}
26
+ end
27
+
28
+ it_should_behave_like "Iq Query Stanzas"
29
+
30
+ end
@@ -0,0 +1,91 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ describe NotificationStanza do
3
+
4
+ before(:each) do
5
+ xml = <<-EOXML
6
+ <message from='firehoser.superfeedr.com' to='you@superfeedr.com'>
7
+ <event xmlns='http://jabber.org/protocol/pubsub#event'>
8
+ <status feed="http://domain.tld/path/to/feed.xml" xmlns:superfeedr='http://superfeedr.com/xmpp-pubsub-ext'>
9
+ <http code="200">9718 bytes fetched in 1.462708s : 2 new entries.</http>
10
+ <next_fetch>2009-05-10T11:19:38-07:00</next_fetch>
11
+ </status>
12
+ <items node='http://domain.tld/path/to/feed.xml'>
13
+ <item chunk="1" chunks="2" >
14
+ <entry xmlns='http://www.w3.org/2005/Atom'>
15
+ <title>Soliloquy</title>
16
+ <summary>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</summary>
17
+ <link rel='alternate' type='text/html' href='http://superfeedr.com/entries/12345789'/>
18
+ <id>tag:domain.tld,2009:Soliloquy-32397</id>
19
+ <published>2010-04-05T11:04:21Z</published>
20
+ </entry>
21
+ </item>
22
+ <item chunk="2" chunks="2" >
23
+ <entry xmlns='http://www.w3.org/2005/Atom'>
24
+ <title>Finibus Bonorum et Malorum</title>
25
+ <summary>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</summary>
26
+ <link rel='alternate' type='text/html' href='http://superfeedr.com/entries/12345788'/>
27
+ <id>tag:domain.tld,2009:Finibus-32398</id>
28
+ <published>2010-04-06T08:54:02Z</published>
29
+ </entry>
30
+ </item>
31
+ </items>
32
+ </event>
33
+ </message>
34
+ EOXML
35
+ @stanza = NotificationStanza.new(xml)
36
+ end
37
+
38
+ it "should have the right feed_url" do
39
+ @stanza.feed_url.should == "http://domain.tld/path/to/feed.xml"
40
+ end
41
+
42
+ it "should have the right message_status"
43
+
44
+ it "should have the right http_status" do
45
+ @stanza.http_status.should == 200
46
+ end
47
+
48
+ it "should have the have the right next_fetch" do
49
+ @stanza.next_fetch.should == Time.parse("2009-05-10T11:19:38-07:00")
50
+ end
51
+
52
+ it "should have the right number of items" do
53
+ @stanza.entries.count.should == 2
54
+ end
55
+
56
+ describe "items" do
57
+ before(:each) do
58
+ @item = @stanza.entries.first
59
+ end
60
+
61
+ it "should have the right chunk" do
62
+ @item.chunk.should == 1
63
+ end
64
+
65
+ it "should have the right chunks" do
66
+ @item.chunks.should == 2
67
+ end
68
+
69
+ it "should have the right title" do
70
+ @item.title.should == "Soliloquy"
71
+ end
72
+
73
+ it "should have the right summary" do
74
+ @item.summary.should == "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
75
+ end
76
+
77
+ it "should have the right link" do
78
+ @item.link.should == "http://superfeedr.com/entries/12345789"
79
+ end
80
+
81
+ it "should have the right unique_id" do
82
+ @item.unique_id.should == "tag:domain.tld,2009:Soliloquy-32397"
83
+ end
84
+
85
+ it "should have the right published" do
86
+ @item.published.should == Time.parse("2010-04-05T11:04:21Z")
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/iq_query_stanza_spec'
3
+ describe SubscribeQueryStanza do
4
+
5
+ it_should_behave_like "Iq Query Stanzas"
6
+
7
+ before(:each) do
8
+ @params = { :type => "set", :from => "me@server.com/resource", :node => "http//domain.tld/feed.xml", :type => "set", :from => "me@server.com/resource"}
9
+ end
10
+
11
+ it "should have the right node value" do
12
+ SubscribeQueryStanza.new(@params).node.should == @params[:node]
13
+ end
14
+
15
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/iq_query_stanza_spec'
3
+ describe SubscriptionsQueryStanza do
4
+
5
+ it_should_behave_like "Iq Query Stanzas"
6
+
7
+ before(:each) do
8
+ @params = { :type => "set", :from => "me@server.com/resource", :page => 3, :type => "set", :from => "me@server.com/resource"}
9
+ end
10
+
11
+ it "should have the right page value" do
12
+ SubscriptionsQueryStanza.new(@params).page.should == @params[:page].to_s
13
+ end
14
+
15
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/iq_query_stanza_spec'
3
+ describe UnsubscribeQueryStanza do
4
+
5
+ it_should_behave_like "Iq Query Stanzas"
6
+
7
+ before(:each) do
8
+ @params = { :type => "set", :from => "me@server.com/resource", :node => "http//domain.tld/feed.xml", :type => "set", :from => "me@server.com/resource"}
9
+ end
10
+
11
+ it "should have the right node value" do
12
+ UnsubscribeQueryStanza.new(@params).node.should == @params[:node]
13
+ end
14
+
15
+ end
@@ -0,0 +1,199 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Superfeedr do
4
+
5
+ before(:each) do
6
+ @mock_connection = mock(Babylon::XmppConnection, {:send_xml => true, :jid => "client@server.tld/resource"})
7
+ end
8
+
9
+ describe "connect" do
10
+ end
11
+
12
+ describe "on_stanza" do
13
+ end
14
+
15
+ describe "subscribe" do
16
+ before(:each) do
17
+ Superfeedr.stub!(:connection).and_return(@mock_connection)
18
+ Superfeedr.stub!(:send).and_return(true)
19
+ @block = Proc.new {
20
+
21
+ }
22
+ @node = "http://domain.com/feed.xml"
23
+ @mock_stanza = mock(SubscribeQueryStanza, {:id => "123"})
24
+ SubscribeQueryStanza.stub!(:new).and_return(@mock_stanza)
25
+ end
26
+
27
+ it "should raise an error if not connected" do
28
+ Superfeedr.should_receive(:connection).and_return(nil)
29
+ lambda {
30
+ Superfeedr.subscribe(@node, &@block)
31
+ }.should raise_error(Superfeedr::NotConnected)
32
+ end
33
+
34
+ it "should create a new SubscribeQueryStanza with the right url" do
35
+ SubscribeQueryStanza.should_receive(:new).with({:node => @node, :from => @mock_connection.jid}).and_return(@mock_stanza)
36
+ Superfeedr.subscribe(@node, &@block)
37
+ end
38
+
39
+ it "should add a Proc that just calls the block in params to the @@callbacks" do
40
+ Superfeedr.subscribe(@node, &@block)
41
+ Superfeedr.callbacks[@mock_stanza.id][:method].should == Superfeedr.method(:on_subscribe)
42
+ Superfeedr.callbacks[@mock_stanza.id][:param].should == @block
43
+ end
44
+
45
+ it "should send the stanza" do
46
+ Superfeedr.should_receive(:send).with(@mock_stanza).and_return(true)
47
+ Superfeedr.subscribe(@node, &@block)
48
+ end
49
+ end
50
+
51
+ describe "unsubscribe" do
52
+ before(:each) do
53
+ Superfeedr.stub!(:connection).and_return(@mock_connection)
54
+ Superfeedr.stub!(:send).and_return(true)
55
+ @block = Proc.new {
56
+
57
+ }
58
+ @node = "http://domain.com/feed.xml"
59
+ @mock_stanza = mock(UnsubscribeQueryStanza, {:id => "123"})
60
+ UnsubscribeQueryStanza.stub!(:new).and_return(@mock_stanza)
61
+ end
62
+
63
+ it "should raise an error if not connected" do
64
+ Superfeedr.should_receive(:connection).and_return(nil)
65
+ lambda {
66
+ Superfeedr.unsubscribe(@node, &@block)
67
+ }.should raise_error(Superfeedr::NotConnected)
68
+ end
69
+
70
+ it "should create a new SubscribeQueryStanza with the right url" do
71
+ UnsubscribeQueryStanza.should_receive(:new).with({:node => @node, :from => @mock_connection.jid}).and_return(@mock_stanza)
72
+ Superfeedr.unsubscribe(@node, &@block)
73
+ end
74
+
75
+ it "should add a Proc that just calls the block in params to the @@callbacks" do
76
+ Superfeedr.unsubscribe(@node, &@block)
77
+ Superfeedr.callbacks[@mock_stanza.id][:method].should == Superfeedr.method(:on_unsubscribe)
78
+ Superfeedr.callbacks[@mock_stanza.id][:param].should == @block
79
+ end
80
+
81
+ it "should send the stanza" do
82
+ Superfeedr.should_receive(:send).with(@mock_stanza).and_return(true)
83
+ Superfeedr.unsubscribe(@node, &@block)
84
+ end
85
+ end
86
+
87
+ describe "subscriptions" do
88
+ before(:each) do
89
+ Superfeedr.stub!(:connection).and_return(@mock_connection)
90
+ Superfeedr.stub!(:send).and_return(true)
91
+ @block = Proc.new {
92
+
93
+ }
94
+ @page = 3
95
+ @mock_stanza = mock(SubscriptionsQueryStanza, {:id => "123"})
96
+ SubscriptionsQueryStanza.stub!(:new).and_return(@mock_stanza)
97
+ end
98
+
99
+ it "should raise an error if not connected" do
100
+ Superfeedr.should_receive(:connection).and_return(nil)
101
+ lambda {
102
+ Superfeedr.subscriptions(@page, &@block)
103
+ }.should raise_error(Superfeedr::NotConnected)
104
+ end
105
+
106
+ it "should create a new SubscribeQueryStanza with the right url" do
107
+ SubscriptionsQueryStanza.should_receive(:new).with({:page => @page, :from => @mock_connection.jid}).and_return(@mock_stanza)
108
+ Superfeedr.subscriptions(@page, &@block)
109
+ end
110
+
111
+ it "should add a Proc that just calls the block in params to the @@callbacks" do
112
+ Superfeedr.subscriptions(@page, &@block)
113
+ Superfeedr.callbacks[@mock_stanza.id][:method].should == Superfeedr.method(:on_subscriptions)
114
+ Superfeedr.callbacks[@mock_stanza.id][:param].should == @block
115
+
116
+ end
117
+
118
+ it "should send the stanza" do
119
+ Superfeedr.should_receive(:send).with(@mock_stanza).and_return(true)
120
+ Superfeedr.subscriptions(@page, &@block)
121
+ end
122
+ end
123
+
124
+
125
+ describe "on_subscribe" do
126
+ it "should call the block with true if the stanza type is 'result'" do
127
+ xml = <<-EOXML
128
+ <iq type="result" to="you@superfeedr.com/home" from="firehoser.superfeedr.com" id="sub1">
129
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
130
+ <subscription jid="you@superfeedr.com" subscription="subscribed" node="http://domain.tld/path/to/feed.xml"/>
131
+ </pubsub>
132
+ </iq>
133
+ EOXML
134
+ stanza = Nokogiri::XML(xml)
135
+ Superfeedr.on_subscribe(stanza.root) do |res|
136
+ res.should be_true
137
+ end
138
+ end
139
+
140
+ it "should call the block with false if the stanza type is not 'result'" do
141
+ xml = <<-EOXML
142
+ <iq type="error" to="you@superfeedr.com/home" from="firehoser.superfeedr.com" id="sub1">
143
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
144
+ <subscription jid="you@superfeedr.com" subscription="subscribed" node="http://domain.tld/path/to/feed.xml"/>
145
+ </pubsub>
146
+ </iq>
147
+ EOXML
148
+ stanza = Nokogiri::XML(xml)
149
+ Superfeedr.on_subscribe(stanza.root) do |res|
150
+ res.should be_false
151
+ end
152
+ end
153
+
154
+ end
155
+
156
+ describe "on_unsubscribe" do
157
+ it "should call the block with true if the stanza type is 'result'" do
158
+ xml = <<-EOXML
159
+ <iq type='result' from='firehoser.superfeedr.com' to='you@superfeedr.com/home' id='unsub1' />
160
+ EOXML
161
+ stanza = Nokogiri::XML(xml)
162
+ Superfeedr.on_unsubscribe(stanza.root) do |res|
163
+ res.should be_true
164
+ end
165
+ end
166
+
167
+ it "should call the block with false if the stanza type is not 'result'" do
168
+ xml = <<-EOXML
169
+ <iq type='error' from='firehoser.superfeedr.com' to='you@superfeedr.com/home' id='unsub1' />
170
+ EOXML
171
+ stanza = Nokogiri::XML(xml)
172
+ Superfeedr.on_unsubscribe(stanza.root) do |res|
173
+ res.should be_false
174
+ end
175
+ end
176
+ end
177
+
178
+ describe "on_subscriptions" do
179
+ it "should call the block with the page number and the list of feeds as an array" do
180
+ xml = <<-EOXML
181
+ <iq type="result" to="you@superfeedr.com/home" id="subman1" from="firehoser.superfeedr.com">
182
+ <pubsub xmlns="http://jabber.org/protocol/pubsub" xmlns:superfeedr="http://superfeedr.com/xmpp-pubsub-ext" >
183
+ <subscriptions superfeedr:page="3">
184
+ <subscription node="http://domain.tld/path/to/a/feed/atom.xml" subscription="subscribed" jid="you@superfeedr.com" />
185
+ <subscription node="http://domain2.tld/path/to/feed.rss" subscription="subscribed" jid="you@superfeedr.com" />
186
+ </subscriptions>
187
+ </pubsub>
188
+ </iq>
189
+ EOXML
190
+ stanza = Nokogiri::XML(xml)
191
+ Superfeedr.on_subscriptions(stanza.root) do |page, subscriptions|
192
+ page.should == 3
193
+ subscriptions.should == ["http://domain.tld/path/to/a/feed/atom.xml", "http://domain2.tld/path/to/feed.rss"]
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: superfeedr-superfeedr-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - julien Genestoux
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-04 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: julien51-babylon
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: nokogiri
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description:
36
+ email: julien.genestoux@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - LICENSE
46
+ - README.rdoc
47
+ - Rakefile
48
+ - VERSION.yml
49
+ - lib/stanzas/iq_query_stanza.rb
50
+ - lib/stanzas/notification_stanza.rb
51
+ - lib/stanzas/subscribe_query_stanza.rb
52
+ - lib/stanzas/subscriptions_query_stanza.rb
53
+ - lib/stanzas/unsubscribe_query_stanza.rb
54
+ - lib/superfeedr.rb
55
+ - spec/spec.opts
56
+ - spec/spec_helper.rb
57
+ - spec/stanzas/iq_query_stanza_spec.rb
58
+ - spec/stanzas/notifications_stanza_spec.rb
59
+ - spec/stanzas/subscribe_stanza_spec.rb
60
+ - spec/stanzas/subscriptions_stanza_spec.rb
61
+ - spec/stanzas/unsubscribe_stanza_spec.rb
62
+ - spec/superfeedr_ruby_spec.rb
63
+ has_rdoc: true
64
+ homepage: http://github.com/julien51/superfeedr-ruby/
65
+ post_install_message:
66
+ rdoc_options:
67
+ - --charset=UTF-8
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ requirements: []
83
+
84
+ rubyforge_project: superfeedr-ruby
85
+ rubygems_version: 1.2.0
86
+ signing_key:
87
+ specification_version: 2
88
+ summary: Ruby Client for the Superfeedr
89
+ test_files:
90
+ - spec/spec_helper.rb
91
+ - spec/stanzas/iq_query_stanza_spec.rb
92
+ - spec/stanzas/notifications_stanza_spec.rb
93
+ - spec/stanzas/subscribe_stanza_spec.rb
94
+ - spec/stanzas/subscriptions_stanza_spec.rb
95
+ - spec/stanzas/unsubscribe_stanza_spec.rb
96
+ - spec/superfeedr_ruby_spec.rb