Skip to content
Browse files

initial commit

  • Loading branch information...
0 parents commit 0254a7a066b3059552ebe71b872a4fa229ab5531 @sprsquish sprsquish committed Nov 5, 2009
Showing with 583 additions and 0 deletions.
  1. +5 −0 .document
  2. +5 −0 .gitignore
  3. +20 −0 LICENSE
  4. +64 −0 README.md
  5. +61 −0 Rakefile
  6. +51 −0 example/superfeedr.rb
  7. +65 −0 lib/superfeedr-rb.rb
  8. +89 −0 lib/superfeedr/entry.rb
  9. +36 −0 lib/superfeedr/status.rb
  10. +40 −0 spec/spec_helper.rb
  11. +119 −0 spec/superfeedr/entry_spec.rb
  12. +28 −0 spec/superfeedr/status_spec.rb
5 .document
@@ -0,0 +1,5 @@
+README.rdoc
+lib/**/*.rb
+bin/*
+features/**/*.feature
+LICENSE
5 .gitignore
@@ -0,0 +1,5 @@
+*.sw?
+.DS_Store
+coverage
+rdoc
+pkg
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Jeff Smick
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64 README.md
@@ -0,0 +1,64 @@
+Superfeedr rb
+=============
+
+A ruby library based on Blather for Superfeedr.
+
+This library is *very* opinionated. In fact there's really only one method `feed`.
+Setup the connection and
+
+Install
+-------
+Gem is hosted on [Gemcutter](http://gemcutter.org/)
+
+ sudo gem install superfeedr-rb
+
+Example
+-------
+
+ require 'rubygems'
+ require 'superfeedr-rb'
+ require 'pp'
+
+ client = Superfeedr::Client.setup 'demo@superfeedr.com', '*********'
+
+ client.feed('http://superfeedr.com/dummy.xml') do |status, entries|
+ pp({
+ :status => {
+ :feed => status.feed,
+ :code => status.code,
+ :http => status.http,
+ :next_fetch => status.next_fetch
+ },
+ :entries => entries.map { |entry| {
+ :id => entry.id,
+ :title => entry.title,
+ :published => entry.published,
+ :content => entry.content,
+ :summary => entry.summary,
+ :categories => entry.categories,
+ :links => entry.links.map { |link| {
+ :href => link.href,
+ :rel => link.rel,
+ :type => link.type,
+ :title => link.title
+ }}
+ :authors => link.authors.map { |author| {
+ :name => author.name,
+ :email => author.email,
+ :uri => author.uri
+ }}
+ }}
+ })
+ end
+
+ client.feed('http://github.com/superfeedr.atom') do |notification|
+ pp notification
+ end
+
+ EM.run { client.connect }
+
+
+Copyright
+---------
+
+Copyright (c) 2009 Jeff Smick. See LICENSE for details.
61 Rakefile
@@ -0,0 +1,61 @@
+require 'rubygems'
+require 'rake'
+
+begin
+ require 'jeweler'
+ Jeweler::Tasks.new do |gem|
+ gem.name = "superfeedr-rb"
+ gem.summary = %Q{A ruby library based on Blather for Superfeedr}
+ gem.description = %Q{A ruby library based on Blather for Superfeedr}
+ gem.email = "sprsquish@gmail.com"
+ gem.homepage = "http://github.com/sprsquish/superfeedr-rb"
+ gem.authors = ["Jeff Smick"]
+
+ gem.add_development_dependency "minitest"
+ gem.add_development_dependency "yard"
+
+ gem.add_dependency 'blather'
+
+ gem.files = FileList['examples/**/*', 'lib/**/*'].to_a
+
+ gem.test_files = FileList['spec/**/*.rb']
+ end
+ Jeweler::GemcutterTasks.new
+ task :release => 'gemcutter:release'
+rescue LoadError
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
+end
+
+require 'rake/testtask'
+Rake::TestTask.new(:test) do |test|
+ test.libs << 'lib' << 'spec'
+ test.pattern = 'spec/**/*_spec.rb'
+ test.verbose = true
+end
+
+begin
+ require 'rcov/rcovtask'
+ Rcov::RcovTask.new do |test|
+ test.libs << 'spec'
+ test.pattern = 'spec/**/*_spec.rb'
+ test.rcov_opts += ['--exclude \/Library\/Ruby,spec\/', '--xrefs']
+ test.verbose = true
+ end
+rescue LoadError
+ task :rcov do
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
+ end
+end
+
+task :test => :check_dependencies
+
+task :default => :test
+
+begin
+ require 'yard'
+ YARD::Rake::YardocTask.new
+rescue LoadError
+ task :yardoc do
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
+ end
+end
51 example/superfeedr.rb
@@ -0,0 +1,51 @@
+require 'rubygems'
+require 'superfeedr-rb'
+require 'pp'
+
+Blather.logger.level = Logger::DEBUG
+Superfeedr::Client.connect('n@d/r', 'password') do |client|
+ # Automatically subscribes to the feed
+ # If already subscribed it simply catches the events coming in.
+ client.feed('http://superfeedr.com/dummy.xml') do |status, entries|
+ return if status.failed?
+ pp({
+ :status => {
+ :feed => status.feed,
+ :code => status.code,
+ :http => status.http,
+ :next_fetch => status.next_fetch
+ },
+ :entries => entries.map { |entry| {
+ :id => entry.id,
+ :chunks => entry.chunks,
+ :chunk => entry.chunk,
+ :title => entry.title,
+ :published => entry.published,
+ :content => entry.content,
+ :summary => entry.summary,
+ :categories => entry.categories,
+ :links => entry.links.map { |link| {
+ :href => link.href,
+ :rel => link.rel,
+ :type => link.type,
+ :title => link.title
+ }},
+ :authors => entry.authors.map { |author| {
+ :name => author.name,
+ :email => author.email,
+ :uri => author.uri
+ }}
+ }}
+ })
+ end
+
+ # client.feed('http://github.com/superfeedr.atom') do |notification|
+ # pp notification
+ # end
+
+ # Catch all notifications
+ # This works because Superfeedr::Client is just a subsclass of Blather::Client
+ client.register_handler(:pubsub_event) do |evt|
+ pp evt
+ end
+end
65 lib/superfeedr-rb.rb
@@ -0,0 +1,65 @@
+%w[
+ blather
+ blather/client/client
+
+ superfeedr/entry
+ superfeedr/status
+].each { |r| require r }
+
+module Superfeedr
+
+ class Client < Blather::Client
+ def self.connect(jid, pass, host = nil, port = nil)
+ if block_given?
+ client = self.setup jid, pass, host, port
+ EM.run {
+ yield client
+ client.connect
+ }
+ else
+ super
+ end
+ end
+
+ def initialize # :nodoc:
+ super
+ @deferred = []
+ end
+
+ def feed(url, &block)
+ return if defer(:feed, url, &block)
+ self.write Blather::Stanza::PubSub::Subscribe.new(:set, 'firehoser.superfeedr.com', url, self.jid.stripped)
+
+ self.register_handler(:pubsub_event, "//ns:items[@node='#{url}']", :ns => Blather::Stanza::PubSub::Event.registered_ns) do |evt, _|
+ block.call Status.parse(evt), Entry.parse(evt)
+ end
+ end
+
+ def client_post_init # :nodoc:
+ # overwrite the default actions to take after a client is setup
+ status = Blather::Stanza::Presence::Status.new
+ status.priority = 100
+ write status
+ end
+
+ # Allow users to setup callbacks before the connection is setup
+ def defer(*args, &block) # :nodoc:
+ if @stream
+ false
+ else
+ @deferred << [args, block]
+ true
+ end
+ end
+
+ # Run all deferred commands after the connection is established
+ def post_init(stream, jid = nil) # :nodoc:
+ super
+ until @deferred.empty?
+ args = @deferred.pop
+ self.__send__ *(args[0]), &args[1]
+ end
+ end
+ end
+
+end
89 lib/superfeedr/entry.rb
@@ -0,0 +1,89 @@
+module Superfeedr
+ class Entry < Blather::Stanza::PubSubItem
+ NS = 'http://www.w3.org/2005/Atom'.freeze
+
+ def self.parse(node)
+ node.find('//ns:event/ns:items/ns:item', :ns => Blather::Stanza::PubSub::Event.registered_ns).map do |item|
+ Entry.new('item').inherit(item)
+ end
+ end
+
+ def chunks
+ self[:chunks].to_i
+ end
+
+ def chunk
+ self[:chunk].to_i
+ end
+
+ def id
+ self.entry.content_from 'ns:id', :ns => NS
+ end
+
+ def title
+ self.entry.content_from 'ns:title', :ns => NS
+ end
+
+ def published
+ if published = self.entry.content_from('ns:published', :ns => NS)
+ DateTime.parse published
+ end
+ end
+
+ def content
+ self.entry.content_from 'ns:content', :ns => NS
+ end
+
+ def summary
+ self.entry.content_from 'ns:summary', :ns => NS
+ end
+
+ def categories
+ self.entry.find('ns:category', :ns => NS).map { |cat| cat[:term] }
+ end
+
+ def links
+ self.entry.find('ns:link', :ns => NS).map { |l| Link.new.inherit(l) }
+ end
+
+ def authors
+ self.entry.find('ns:author', :ns => NS).map { |l| Author.new.inherit(l) }
+ end
+
+ def entry
+ Blather::XMPPNode.import(super)
+ end
+
+ class Link < Blather::XMPPNode
+ def href
+ self[:href]
+ end
+
+ def rel
+ self[:rel]
+ end
+
+ def type
+ self[:type]
+ end
+
+ def title
+ self[:title]
+ end
+ end
+
+ class Author < Blather::XMPPNode
+ def name
+ self.content_from 'ns:name', :ns => NS
+ end
+
+ def email
+ self.content_from 'ns:email', :ns => NS
+ end
+
+ def uri
+ self.content_from 'ns:uri', :ns => NS
+ end
+ end
+ end
+end
36 lib/superfeedr/status.rb
@@ -0,0 +1,36 @@
+module Superfeedr
+ class Status < Blather::XMPPNode
+ NS = 'http://superfeedr.com/xmpp-pubsub-ext'.freeze
+
+ def self.parse(node)
+ self.new('status').inherit node.find_first('//ns:status', :ns => NS)
+ end
+
+ def failed?
+ false
+ end
+
+ def feed
+ self[:feed]
+ end
+
+ def code
+ self.http_node[:code].to_i
+ end
+
+ def http
+ self.http_node.content
+ end
+
+ def next_fetch
+ if next_fetch = self.find_first('//ns:next_fetch', :ns => NS).content
+ DateTime.parse next_fetch
+ end
+ end
+
+ protected
+ def http_node
+ self.find_first('//ns:http', :ns => NS)
+ end
+ end
+end
40 spec/spec_helper.rb
@@ -0,0 +1,40 @@
+require 'rubygems'
+require 'minitest/spec'
+
+$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..'))
+$:.unshift File.expand_path(File.join(File.dirname(__FILE__), *%w[.. lib]))
+require 'superfeedr-rb'
+
+def message_node
+ Blather::XMPPNode.import(Nokogiri::XML(<<-XML).root)
+ <message from="firehoser.superfeedr.com" to="sprsquish@superfeedr.com">
+ <event xmlns="http://jabber.org/protocol/pubsub#event">
+ <status xmlns="http://superfeedr.com/xmpp-pubsub-ext" feed="http://superfeedr.com/dummy.xml">
+ <http code="200">957 bytes fetched in 0.228013s</http>
+ <next_fetch>2009-11-05T16:34:12+00:00</next_fetch>
+ </status>
+ <items node="http://superfeedr.com/dummy.xml">
+ <item chunks="1" chunk="1">
+ <entry xmlns="http://www.w3.org/2005/Atom">
+ <title>16:32:41</title>
+ <id>tag:superfeedr.com,2005:String/1257438761</id>
+ <published>2009-11-05T16:32:41+00:00</published>
+ <summary>sprsquish wanted to know what time it was.</summary>
+ <content>Thursday November 05 16:32:41 UTC 2009 sprsquish wanted to know what time it was.</content>
+ <category term="tag" scheme="http://www.sixapart.com/ns/types#tag" />
+ <category term="category" scheme="http://www.sixapart.com/ns/types#tag" />
+ <link type="text/html" href="http://superfeedr.com/?1257438761" title="superfeedr" rel="alternate"/>
+ <author>
+ <name>Superfeedr</name>
+ <uri>http://superfeedr.com/</uri>
+ <email>julien@superfeedr.com</email>
+ </author>
+ </entry>
+ </item>
+ </items>
+ </event>
+ </message>
+ XML
+end
+
+MiniTest::Unit.autorun
119 spec/superfeedr/entry_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+# <item chunks="1" chunk="1">
+# <entry xmlns="http://www.w3.org/2005/Atom">
+# <title>16:32:41</title>
+# <id>tag:superfeedr.com,2005:String/1257438761</id>
+# <published>2009-11-05T16:32:41+00:00</published>
+# <summary>sprsquish wanted to know what time it was.</summary>
+# <content>Thursday November 05 16:32:41 UTC 2009 sprsquish wanted to know what time it was.</content>
+# <category term="tag" scheme="http://www.sixapart.com/ns/types#tag" />
+# <category term="category" scheme="http://www.sixapart.com/ns/types#tag" />
+# <link type="text/html" href="http://superfeedr.com/?1257438761" title="superfeedr" rel="alternate"/>
+# <author>
+# <name>Superfeedr</name>
+# <uri>http://superfeedr.com/</uri>
+# <email>julien@superfeedr.com</email>
+# </author>
+# </entry>
+# </item>
+
+describe Superfeedr::Entry do
+ before do
+ @events = Superfeedr::Entry.parse message_node
+ @event = @events.first
+ end
+
+ it 'parses apart a list of items' do
+ @events.must_be_kind_of Array
+ @events.size.must_equal 1
+ end
+
+ it 'knows how many chunks it has' do
+ @event.chunks.must_equal 1
+ end
+
+ it 'knows what chunk it is' do
+ @event.chunk.must_equal 1
+ end
+
+ it 'knows its title' do
+ @event.title.must_equal '16:32:41'
+ end
+
+ it 'knows its id' do
+ @event.id.must_equal 'tag:superfeedr.com,2005:String/1257438761'
+ end
+
+ it 'knows when it was published' do
+ @event.published.must_equal DateTime.parse('2009-11-05T16:32:41+00:00')
+ end
+
+ it 'has content' do
+ @event.content.must_equal 'Thursday November 05 16:32:41 UTC 2009 sprsquish wanted to know what time it was.'
+ end
+
+ it 'has a summary' do
+ @event.summary.must_equal 'sprsquish wanted to know what time it was.'
+ end
+
+ it 'knows its categories' do
+ @event.categories.must_equal %w[tag category]
+ end
+
+ it 'has a set of links' do
+ @event.links.size.must_equal 1
+ @event.links.first.must_be_kind_of Superfeedr::Entry::Link
+ end
+
+ it 'has a set of authors' do
+ @event.authors.size.must_equal 1
+ @event.authors.first.must_be_kind_of Superfeedr::Entry::Author
+ end
+end
+
+# <link type="text/html" href="http://superfeedr.com/?1257438761" title="superfeedr" rel="alternate"/>
+describe Superfeedr::Entry::Link do
+ before do
+ @link = Superfeedr::Entry.parse(message_node).first.links.first
+ end
+
+ it 'knows its href' do
+ @link.href.must_equal 'http://superfeedr.com/?1257438761'
+ end
+
+ it 'knows its rel' do
+ @link.rel.must_equal 'alternate'
+ end
+
+ it 'knows its type' do
+ @link.type.must_equal 'text/html'
+ end
+
+ it 'knows its title' do
+ @link.title.must_equal 'superfeedr'
+ end
+end
+
+# <author>
+# <name>Superfeedr</name>
+# <uri>http://superfeedr.com/</uri>
+# <email>julien@superfeedr.com</email>
+# </author>
+describe Superfeedr::Entry::Author do
+ before do
+ @author = Superfeedr::Entry.parse(message_node).first.authors.first
+ end
+
+ it 'knows its name' do
+ @author.name.must_equal 'Superfeedr'
+ end
+
+ it 'knows its uri' do
+ @author.uri.must_equal 'http://superfeedr.com/'
+ end
+
+ it 'knows its email' do
+ @author.email.must_equal 'julien@superfeedr.com'
+ end
+end
28 spec/superfeedr/status_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+# <status xmlns="http://superfeedr.com/xmpp-pubsub-ext" feed="http://superfeedr.com/dummy.xml">
+# <http code="200">957 bytes fetched in 0.228013s</http>
+# <next_fetch>2009-11-05T16:34:12+00:00</next_fetch>
+# </status>
+
+describe Superfeedr::Status do
+ before do
+ @status = Superfeedr::Status.parse message_node
+ end
+
+ it 'knows the feed it belongs to' do
+ @status.feed.must_equal 'http://superfeedr.com/dummy.xml'
+ end
+
+ it 'knows its status code' do
+ @status.code.must_equal 200
+ end
+
+ it 'has more info about the http code' do
+ @status.http.must_equal '957 bytes fetched in 0.228013s'
+ end
+
+ it 'knows when the next fetch will be' do
+ @status.next_fetch.must_equal DateTime.parse('2009-11-05T16:34:12+00:00')
+ end
+end

0 comments on commit 0254a7a

Please sign in to comment.
Something went wrong with that request. Please try again.