Codice LunaRss 1_0

Vedi tutte le pagine e le modifiche recenti o scarica i sorgenti nella pagina


LunaRss v1.0 – modificato da Antonio Carpentieri

La modifica precedente è stata Codice LunaRss 0_9, la prossima modifica sarà Codice LunaRss 1_1?

Implementazioni di antonio:

* creata la classe RssStore a cui fa riferimento il test scritto da gabriele, ed ho fatto in modo che passasse il test. (RED)
class RssStore
  def initialize page
    @page = page
  end
  def load_feed_uris
    uris = Array.new
    uris << @page
    uris << @page
    uris << @page
    return uris
  end
end

era la soluzione più semplice per far passare il test :P (GREEN), così ho scritto il secondo test:

def test_load_uris    
  rss_store = RssStore.new(TestPages)
  uris=rss_store.load_feed_uris
  assert_equal ATOM_GURU, uris[0].to_s
  assert_equal RSS_ANTONIO, uris[1].to_s
  assert_equal RSS_COMMENTI_ANTONIO, uris[2].to_s
end
in questo modo ho evidenziato che la soluzione precedente, ovviamente, non era quella corretta ai nostri fini! (RED) ora posso scrivere qualcosa di più consono:
class RssStore
  def initialize page
    @page = page
  end
  def load_feed_uris
    uris = Array.new
    @page.each do |page|
      URI.extract(page.body,'http').each do |url|
        uris << url
      end
    end    
    return uris
  end
end

ecco fatto il test passa! (GREEN) Ho quasi la certezza che si possa fare un refactoring per semplificare questo codice… la famosa linea a cui accennava gabriele… ma la lascio a voi! :P

mancano ancora questi punti evidenziati da gabriele:

C’è un altro problema che sorge però: LunaRss gestisce davvero Rss? che Rss? Se la cava anche con Atom? per questo scrivo un test che lascio rosso al prossimo la palla della gestione del formato Atom e Rss 0.92—Antonio Carpentieri

E poi, e per questo non c’è anora il TestCase, come facciamo ad aggiornare il feed globale inserendo solo i nuovi item? Sinceramente non ho idea di come fare ma forse si potrebbe usare la data di creazione. Si assuma di poter usare un backend ActiveRecord nel quale infilare gli oggetti che volete, con le api base:
require 'net/http'
require 'uri'

class Feed
  def initialize(url, opts = { })
    @uri = URI.parse(url)
  end
  def read
    @uri.read
  end
end

FeedData = Struct.new(
  :channel, 
  :titles, 
  :descriptions, 
  :links
  )

class RssParser
  CHANNEL_TITLE=%r{<channel(?!s).*?>.*?(?:<title>(?:<!\[CDATA\[)*(.*?)(?:\]\]>)*</title>.*?)?</channel>}m
  ITEM_TITLE=%r{<item(?!s).*?>.*?(?:<title>(?:<!\[CDATA\[)*(.*?)(?:\]\]>)*</title>.*?)?</item>}m 
  ITEM_LINKS=%r{<item(?!s).*?>.*?(?:<link>(?:<!\[CDATA\[)*(.*?)(?:\]\]>)*</link>.*?)?</item>}m
  ITEM_DESCRIPTION=%r{<item(?!s).*?>.*?(?:<description>(?:<!\[CDATA\[)*(.*?)(?:\]\]>)*</description>.*?)?</item>}m

  def initialize(feed)
    @feed = feed
  end 

  def parse
    FeedData.new( 
      extract( CHANNEL_TITLE    ),            
      extract( ITEM_TITLE       ),
      extract( ITEM_DESCRIPTION ),
      extract( ITEM_LINKS       )
      )  
  end

  private

  def rss_data
    @data ||= @feed.read
  end

  def extract pattern
    rss_data.scan pattern 
  end
end

class RssMaker
  def align text
    text.gsub(/^\s*\./m, '') 
  end
  private :align
  def header
    align <<-HTML
             .Content-type: text/html\r\n\r\n
             .<html>
             .<body>
             HTML
  end

  def footer
    align <<-HTML
             .</body>
             .</html>
             HTML
  end

  def channel name
    align <<-HTML
             .<h3>#{name}</h3>
             HTML
  end

  def entry titolo, descrizione, link
    align <<-HTML
             .<strong>
             .  <a href="#{link}"> #{titolo} </a>
             .</strong><br/>
             .
             .<font size="-1"> #{descrizione} </font>
             .<p/>
             HTML
  end

end

##############################

# Configurations

feeds=[
    'http://www.repubblica.it/rss/scienza_e_tecnologia/rss2.0.xml',
    'http://programmazione.it/rss.xml',
    'http://www.hwupgrade.it/rss_news.xml',
    'http://www.hwupgrade.it/rss_articoli.xml',
    'http://www.beppegrillo.it/index.xml'
]

NEED_PROXY = false #switch this if you have a proxy
proxy_url = "http://10.0.0.2:8080" #put here your proxy, if you have one

##############################

class LunaRss
  def get_feeds
    rss_make = RssMaker.new
    puts rss_make.header

    feeds.each do |feed|
      parser = RssParser.new feed 
      data = parser.parse
      puts rss_make.channel( data.channel )
      0.upto data.titles.length do|i|
        puts rss_make.entry( 
          data.titles[i], 
          data.descriptions[i], 
          data.links[i] 
          )
      end
    end

    puts rss_make.footer
  end
end

class RssStore
  def initialize page
    @page = page
  end
  def load_feed_uris
    uris = Array.new
    @page.each do |page|
      URI.extract(page.body,'http').each do |url|
        uris << url
      end
    end    
    return uris
  end
end

require 'test/unit'
require 'stringio'

class TestLunaRss < Test::Unit::TestCase
  TEST_DATA_RSS_20 = <<-HTML
      <?xml version="1.0"?>
      <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
        <channel>
          <title>Ruby Italia</title>
          <link>http://ruby-it.org/</link>
          <description>Tracking all versions for Ruby Italia</description>
          <language>it-it</language>
          <ttl>40</ttl>
          <item>
            <title>Chiaroscuro</title>
            <description>description too long</description>
            <pubDate>Mon, 08 May 2006 13:33:54 +0200</pubDate>
            <guid>http://ruby-it.org/pages/Chiaroscuro#936</guid>
            <link>http://ruby-it.org/pages/Chiaroscuro#936</link>
            <dc:creator>Chiaroscuro</dc:creator>
          </item>
        </channel>
      </rss>
    HTML

  TEST_DATA_RSS_092 = <<-HTML
    <rss version="0.92">  
      <channel>
        <title>PDI^2</title>
        <link>http://riffraff.blogsome.com</link>
        <description>Propulsione d'Improbabilità Infinita</description>
        <lastBuildDate>Wed, 24 May 2006 08:02:34 +0000</lastBuildDate>
        <docs>http://backend.userland.com/rss092</docs>
        <language>en</language>
        <item>
          <title>Summer of Code</title>
          <description>
            another description too long
          </description>
          <link>http://riffraff.blogsome.com/2006/05/24/summer-of-code/</link>
        </item>
      </channel>
    </rss>
  HTML

  TEST_DATA_ATOM = <<-HTML
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en">
      <title>mozillaZine.org</title>
      <link rel="alternate" type="text/html" href="http://www.mozillazine.org/"/>
      <modified>2006-05-24T23:26:54-08:00</modified>
      <tagline>Your Source for Daily Mozilla News and Advocacy</tagline>
      <id>tag:mozillazine.org,2004:1</id>
      <copyright>Copyright (c) 2004, The Mozillazine Organization</copyright>
        <entry>
          <title>Opera Watch Soliciting Questions for Blake Ross Interview</title>
          <link rel="alternate" type="text/html" href="http://www.mozillazine.org/talkback.html?article=8583"/>
          <modified>2006-05-24T16:17:17-08:00</modified>
          <created>2006-05-24T16:17:17-08:00</created>
          <issued>2006-05-24T16:17:17-08:00</issued>
          <id>tag:mozillazine.org,2004:article8583</id>
          <author>
        <name>mozillaZine.org</name>
          </author>
          <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.mozillazine.org/">
        man! it's too long TOO LONG!!!
          </content>
        </entry>
    </feed>
  HTML

  def test_parser_rss_20
    parser = RssParser.new(StringIO.new(TEST_DATA_RSS_20))
    data = parser.parse
    assert_equal "Ruby Italia".to_s, data.channel[0].to_s
    assert_equal "Chiaroscuro".to_s, data.titles[0].to_s
    assert_equal "description too long".to_s, data.descriptions[0].to_s
    assert_equal "http://ruby-it.org/pages/Chiaroscuro#936".to_s, data.links[0].to_s
  end  

  def test_parser_rss_092
    parser = RssParser.new(StringIO.new(TEST_DATA_RSS_092))
    data = parser.parse
    assert_equal "PDI^2".to_s, data.channel[0].to_s
    assert_equal "Summer of Code".to_s, data.titles[0].to_s
    assert_equal "another description too long".to_s, data.descriptions[0].to_s
    assert_equal "http://riffraff.blogsome.com/2006/05/24/summer-of-code/".to_s, data.links[0].to_s
  end

  def test_parser_atom
    parser = RssParser.new(StringIO.new(TEST_DATA_ATOM))
    data = parser.parse
    assert_equal "mozillaZine.org".to_s, data.channel[0].to_s
    assert_equal "Opera Watch Soliciting Questions for Blake Ross Interview".to_s, data.titles[0].to_s
    assert_equal "man! it's too long TOO LONG!!!".to_s, data.descriptions[0].to_s
    assert_equal "http://www.mozillazine.org/talkback.html?article=8583".to_s, data.links[0].to_s
  end

end

class TestLunaRssStore < Test::Unit::TestCase
  Page=Struct.new :title, :body
  ATOM_GURU = 'http://guragedev.blogspot.com/atom.xml'
  RSS_ANTONIO = 'http://www.antoniocangiano.com/xml/rss20/feed.xml'
  RSS_COMMENTI_ANTONIO = 'http://www.antoniocangiano.com/xml/rss20/comments/feed.xml'
  TestPages=[Page.new("Andrea",<<Andrea),Page.new("antonio", <<Antonio)]
bla bla 
feed: #{ATOM_GURU}
bla bla
Andrea

ble bn
ble 
aleklena
feed: #{RSS_ANTONIO}
feed: #{RSS_COMMENTI_ANTONIO}
Antonio

  def test_load_data
    rss_store = RssStore.new(TestPages)
    uris=rss_store.load_feed_uris
    assert_equal 3, uris.size
  end

  def test_load_uris    
    rss_store = RssStore.new(TestPages)
    uris=rss_store.load_feed_uris
    assert_equal ATOM_GURU, uris[0].to_s
    assert_equal RSS_ANTONIO, uris[1].to_s
    assert_equal RSS_COMMENTI_ANTONIO, uris[2].to_s
  end
end
Updated on May 25, 2006 12:13 by Antonio Carpentieri (213.140.19.203)