#!/usr/local/bin/ruby require 'xmlparser' require "xmltree" require 'xmltreebuilder' class Module # element_accessor creates attribute accesssors but also # maintains a list of attribute names in a class # variable @@element_accessor. # # Usage just like attr_accessor. # # class Image # element_accessor :title, :url, :link, :width, :height # end def element_accessor(*symbols) # initialize the class variable if not initialized already self.instance_eval(%Q! unless class_variables.include? "@@element_accessor" @@element_accessor = [] end !) # add attribute accessor, and add symbol to the maintained list symbols.each do |symbol| self.instance_eval %Q[attr_accessor :#{symbol.id2name}] self.instance_eval %Q[@@element_accessor << "#{symbol.id2name}"] end end private :element_accessor end class Object # map_elements is a method which tries to find a xml element # for each element attribute defined with element_accessor. # Then it calls the write accessor to assign the value of the # xml node to the attribute. # # Thus it maps xml elements directly into class attributes. def map_elements(xmlNode) cvar_element_accessor = self.class.instance_eval %Q[@@element_accessor] cvar_element_accessor.each do |e| value = "" elements = xmlNode.getElementsByTagName(e) if elements.size > 0 value = elements[0].firstChild.data end element_attribute_writer = method(e+"=") element_attribute_writer.call(value) end end end module RSS include XML::SimpleTree def do_items(xmltree) xmltree.getElementsByTagName("item").each do | s | i = Item.new i.map_elements(s) yield i end end private :do_items def do_channel(xmltree) xmltree.getElementsByTagName("channel").each do | s | c = Channel.new c.map_elements(s) return c # there can be only one end end private :do_channel def do_textinput(xmltree) xmltree.getElementsByTagName("textinput").each do | s | ti = TextInput.new ti.map_elements(s) return ti end end private :do_textinput def do_image(xmltree) xmltree.getElementsByTagName("image").each do | s | im = Image.new im.map_elements(s) return im end end private :do_image def do_textinput(xmltree) if(xmltree.getElementsByTagName("textinput") == nil) then return nil end xmltree.getElementsByTagName("textinput").each do | s | ti = TextInput.new ti.title = get_data(s.getElementsByTagName("title")) ti.description = get_data(s.getElementsByTagName("description")) ti.name = get_data(s.getElementsByTagName("name")) ti.link = get_data(s.getElementsByTagName("link")) return ti end end private :do_textinput def get_data(elems) if elems[0] == nil then return nil end return elems[0].firstChild.data end private :get_data class Channel element_accessor :title, :link, :description, :url, :language, :rating, :copyright, :pubdata, :lastbuilddate, :docs, :managingEditor, :webmaster attr_accessor :image, :textinput def initialize @items = Array.new end def each_item @items.each do | item | yield item end end def add_item(item) if(@items.length >= 15) then raise "Limit of 15 items per channel" end @items.push item end def to_xml tree = Document.new(ProcessingInstruction.new("xml", "version='1.0' encoding='ISO-8859-1'"), DocumentType.new("rss", "PUBLIC '-//Netscape Communications//DTD RSS 0.91//EN' 'http://my.netscape.com/publish/formats/rss-0.91.dtd'"), Element.new("rss",[ Attr.new('version', "0.91")], Element.new("channel", nil, Element.new("title", nil, @title), Element.new("link", nil, @link), Element.new("description", nil, @description), Element.new("language", nil, @language), Element.new("image", nil, Element.new("title", nil, @image.title), Element.new("link", nil, @image.link), Element.new("url", nil, @image.url) ) ) ) ) rss = tree.getElementsByTagName("rss")[0] chan = tree.getElementsByTagName("channel")[0] @items.each do | item | chan.appendChild(Element.new("item", nil, Element.new("title", nil, item.title), Element.new("link", nil, item.link)) ) end if(@textinput != nil) then title = Element.new("title", nil, @textinput.title) description = Element.new("description", nil, @textinput.description) link = Element.new("link", nil, @textinput.link) textinput = Element.new("name", nil, @textinput.name) chan.appendChild(Element.new("textinput", nil, title, description, textinput, link)) end tree.to_s end end class Item element_accessor :title, :link end class Image element_accessor :title, :url, :link, :width, :height def initialize @width = "88" @height = "31" end end class TextInput element_accessor :title, :description, :name, :link end def from_xml(xml) builder = XML::DOM::Builder.new(0) builder.setBase("./") begin xmltree = builder.parse(xml, true) rescue XMLParserError line = builder.line print "#{$0}: #{$!} (in line #{line})\n" exit 1 end ## unify sequential Text nodes xmltree.documentElement.normalize xmltree.trim chan = do_channel xmltree chan.image = do_image xmltree chan.textinput = do_textinput xmltree inp = do_textinput xmltree if(inp != nil) then chan.textinput = inp end do_items(xmltree) do | item | chan.add_item item end return chan end end