radamanthus-superfeedr-ruby 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.
@@ -0,0 +1,71 @@
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 Gemcutter :
47
+
48
+ sudo gem install superfeedr-ruby
49
+
50
+ Source :
51
+
52
+ git clone git@github.com:superfeedr/superfeedr-ruby.git
53
+
54
+
55
+ == Example
56
+
57
+ Create a directory called superfeedr-example. In the superfeedr-example directory create a file called example.rb and copy the code from the gist below into that file. Replace "demo@superfeedr.com" with "your-username@superfeedr.com" and replace the "*******" with your Superfeedr password. Next, create a directory called log in the superfeedr-example directory. Finally, run it with:
58
+
59
+ ruby example.rb
60
+
61
+ Example code: {Gist}[http://gist.github.com/110247]
62
+
63
+ == REQUIREMENTS:
64
+
65
+ {Skates}[http://github.com/julien51/skates/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 :
66
+
67
+ sudo gem install skates
68
+
69
+ == Copyright
70
+
71
+ Copyright (c) 2009 julien. See LICENSE for details.
@@ -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('skates')
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
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 4
4
+ :patch: 3
@@ -0,0 +1,2 @@
1
+ ---
2
+ :superfeedr_jid: firehoser.superfeedr.com
@@ -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"] = Superfeedr.conf[:superfeedr_jid]
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,278 @@
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
+ # <item xmlns="http://jabber.org/protocol/pubsub" chunks="2" chunk="1">
13
+ # <entry xmlns="http://www.w3.org/2005/Atom" xml:lang="">
14
+ # <title>cool</title>
15
+ # <content type="text">cool</content>
16
+ # <id>tag:pubhubsubbub-example-app,2009:ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw</id>
17
+ # <published>2010-03-25T16:57:18Z</published>
18
+ # <link type="text/html" href="http://pubsubhubbub-example-app.appspot.com/ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw" title="" rel="alternate"/>
19
+ # </entry>
20
+ # </item>
21
+
22
+ require 'time'
23
+
24
+ class Link
25
+ def initialize(node)
26
+ @node = node
27
+ end
28
+
29
+ def title
30
+ @node["title"]
31
+ end
32
+
33
+ def href
34
+ @node["href"]
35
+ end
36
+
37
+ def rel
38
+ @node["rel"]
39
+ end
40
+
41
+ def type
42
+ @node["type"]
43
+ end
44
+
45
+ end
46
+
47
+ class Author
48
+ def initialize(node)
49
+ @node = node
50
+ end
51
+
52
+ def name
53
+ if !@name
54
+ if name = @node.at_xpath("./atom:name", {"atom" => "http://www.w3.org/2005/Atom"})
55
+ @name = name.text
56
+ end
57
+ end
58
+ end
59
+
60
+ def uri
61
+ if !@uri
62
+ if uri = @node.at_xpath("./atom:uri", {"atom" => "http://www.w3.org/2005/Atom"})
63
+ @uri = uri.text
64
+ end
65
+ end
66
+ end
67
+
68
+ def email
69
+ if !@email
70
+ if email = @node.at_xpath("./atom:email", {"atom" => "http://www.w3.org/2005/Atom"})
71
+ @email = email.text
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ class Category
79
+ def initialize(node)
80
+ @node = node
81
+ end
82
+
83
+ def term
84
+ @node["term"]
85
+ end
86
+ end
87
+
88
+ class Location
89
+ def initialize(node)
90
+ @node = node
91
+ end
92
+
93
+ def point
94
+ @point ||= @node.text
95
+ end
96
+
97
+ def lat
98
+ @lat ||= point.split().first.to_f
99
+ end
100
+
101
+ def lon
102
+ @lon ||= point.split().last.to_f
103
+ end
104
+
105
+ end
106
+
107
+ class Item
108
+
109
+ def initialize(node)
110
+ @node = node
111
+ end
112
+
113
+ def title
114
+ @title ||= @node.at_xpath("./atom:entry/atom:title", {"atom" => "http://www.w3.org/2005/Atom"}).text
115
+ end
116
+
117
+ def summary
118
+ if !@summary
119
+ if summary = @node.at_xpath("./atom:entry/atom:summary", {"atom" => "http://www.w3.org/2005/Atom"})
120
+ @summary = summary.text
121
+ end
122
+ end
123
+ @summary
124
+ end
125
+
126
+ def content
127
+ if !@content
128
+ if content = @node.at_xpath("./atom:entry/atom:content", {"atom" => "http://www.w3.org/2005/Atom"})
129
+ @content = content.text
130
+ end
131
+ end
132
+ @content
133
+ end
134
+
135
+ def unique_id
136
+ @unique_id ||= @node.at_xpath("./atom:entry/atom:id", {"atom" => "http://www.w3.org/2005/Atom"}).text
137
+ end
138
+
139
+ def published
140
+ if !@published
141
+ if published = @node.at_xpath("./atom:entry/atom:published", {"atom" => "http://www.w3.org/2005/Atom"}).text
142
+ @published = Time.parse(published)
143
+ end
144
+ end
145
+ @published
146
+ end
147
+
148
+ def chunks
149
+ @node["chunks"].to_i
150
+ end
151
+
152
+ def chunk
153
+ @node["chunk"].to_i
154
+ end
155
+
156
+ def links
157
+ if !@links
158
+ @links = []
159
+ @node.xpath("./atom:entry/atom:link", {"atom" => "http://www.w3.org/2005/Atom"}).each do |node|
160
+ @links.push(Link.new(node))
161
+ end
162
+ end
163
+ @links
164
+ end
165
+
166
+ def authors
167
+ if !@authors
168
+ @authors = []
169
+ @node.xpath("./atom:entry/atom:author", {"atom" => "http://www.w3.org/2005/Atom"}).each do |node|
170
+ @authors.push(Author.new(node))
171
+ end
172
+ end
173
+ @authors
174
+ end
175
+
176
+ def categories
177
+ if !@categories
178
+ @categories = []
179
+ @node.xpath("./atom:entry/atom:category", {"atom" => "http://www.w3.org/2005/Atom"}).each do |node|
180
+ @categories.push(Category.new(node))
181
+ end
182
+ end
183
+ @categories
184
+ end
185
+
186
+ def locations
187
+ if !@locations
188
+ @locations = []
189
+ @node.xpath("./atom:entry/georss:point", {"atom" => "http://www.w3.org/2005/Atom", "georss" => "http://www.georss.org/georss"}).each do |node|
190
+ @locations.push(Location.new(node))
191
+ end
192
+ end
193
+ @locations
194
+ end
195
+
196
+ end
197
+
198
+
199
+ ##
200
+ # Notification : sent every time a feed has been fetched. It has the following methods:
201
+ # - message_status : a simple message that gives information about the last fetch
202
+ # - http_status : status of the http response
203
+ # - feed_url : url of the feed
204
+ # - next_fetch : Time when the feed will be fetched again (this is purely informative and it might change)
205
+ # - items : array of new items detected (might be empty)
206
+ #
207
+ #
208
+ # <message xmlns="jabber:client" from="firehoser.superfeedr.com" to="julien@superfeedr.com">
209
+ # <event xmlns="http://jabber.org/protocol/pubsub#event">
210
+ # <status xmlns="http://superfeedr.com/xmpp-pubsub-ext" feed="http://pubsubhubbub-example-app.appspot.com/feed">
211
+ # <http code="200">25002 bytes fetched in 0.73s for 1 new entries.</http>
212
+ # <next_fetch>2010-03-25T17:06:30+00:00</next_fetch>
213
+ # <title>PubSubHubBub example app</title>
214
+ # </status>
215
+ # <items node="http://pubsubhubbub-example-app.appspot.com/feed">
216
+ # <item xmlns="http://jabber.org/protocol/pubsub" chunks="1" chunk="1">
217
+ # <entry xmlns="http://www.w3.org/2005/Atom" xml:lang="" xmlns:xml="http://www.w3.org/XML/1998/namespace">
218
+ # <title>cool</title>
219
+ # <content type="text">cool</content>
220
+ # <id>tag:pubhubsubbub-example-app,2009:ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw</id>
221
+ # <published>2010-03-25T16:57:18Z</published>
222
+ # <link type="text/html" href="http://pubsubhubbub-example-app.appspot.com/ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw" title="" rel="alternate"/>
223
+ # </entry>
224
+ # <entry xmlns="http://www.w3.org/2005/Atom" xml:lang="" xmlns:xml="http://www.w3.org/XML/1998/namespace">
225
+ # <title>great</title>
226
+ # <content type="text">great</content>
227
+ # <id>tag:pubhubsubbub-example-app,2009:ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAx</id>
228
+ # <published>2010-03-25T16:57:19Z</published>
229
+ # <link type="text/html" href="http://pubsubhubbub-example-app.appspot.com/ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAx" title="" rel="alternate"/>
230
+ # </entry>
231
+ # </item>
232
+ # </items>
233
+ # </event>
234
+ # </message>
235
+
236
+ class NotificationStanza < Skates::Base::Stanza
237
+
238
+ XMLNS = {
239
+ "ps" => "http://jabber.org/protocol/pubsub#event",
240
+ "ps2" => "http://jabber.org/protocol/pubsub",
241
+ "sf" => "http://superfeedr.com/xmpp-pubsub-ext" } unless defined? XMLNS
242
+
243
+ def next_fetch
244
+ if !@next_fetch
245
+ time = @node.at_xpath("./ps:event/sf:status/sf:next_fetch", XMLNS).text
246
+ @next_fetch = Time.parse(time)
247
+ end
248
+ @next_fetch
249
+ end
250
+
251
+ def http_status
252
+ @http_status ||= @node.at_xpath("./ps:event/sf:status/sf:http/@code", XMLNS).text.to_i
253
+ end
254
+
255
+ def feed_url
256
+ @feed_url ||= @node.at_xpath("./ps:event/sf:status/@feed", XMLNS).text
257
+ end
258
+
259
+ def message_status
260
+ @message_status ||= @node.at_xpath("./ps:event/sf:status/sf:http", XMLNS).text
261
+ end
262
+
263
+ def title
264
+ @title ||= @node.at_xpath("./ps:event/sf:status/sf:title", XMLNS).text
265
+ end
266
+
267
+ def entries
268
+ if !@entries
269
+ @entries = []
270
+ @node.xpath("./ps:event/ps:items/ps2:item", XMLNS).each do |node|
271
+ @entries.push(Item.new(node))
272
+ end
273
+ end
274
+ @entries
275
+ end
276
+
277
+ end
278
+