lib/atom/entry.rb in atom-tools-1.0.0 vs lib/atom/entry.rb in atom-tools-2.0.0

- old
+ new

@@ -2,13 +2,66 @@ require "atom/element" require "atom/text" module Atom - NS = "http://www.w3.org/2005/Atom" - PP_NS = "http://www.w3.org/2007/app" + class Control < Atom::Element + attr_accessor :draft + is_element PP_NS, :control + + on_parse [PP_NS, 'draft'] do |e,x| + e.set(:draft, x.text == 'yes') + end + + on_build do |e,x| + unless (v = e.get(:draft)).nil? + el = e.append_elem(x, ['app', PP_NS], 'draft') + el.text = (v ? 'yes' : 'no') + end + end + end + + module HasCategories + def HasCategories.included(klass) + klass.atom_elements :category, :categories, Atom::Category + end + + # categorize the entry with each of an array or a space-separated + # string + def tag_with(tags, delimiter = ' ') + return if not tags or tags.empty? + + tag_list = unless tags.is_a?(String) + tags + else + tags = tags.split(delimiter) + tags.map! { |t| t.strip } + tags.reject! { |t| t.empty? } + tags.uniq + end + + tag_list.each do |tag| + unless categories.any? { |c| c.term == tag } + categories.new :term => tag + end + end + end + end + + module HasLinks + def HasLinks.included(klass) + klass.atom_elements :link, :links, Atom::Link + end + + def find_link(criteria) + self.links.find do |l| + criteria.all? { |k,v| l.send(k) == v } + end + end + end + # An individual entry in a feed. As an Atom::Element, it can be # manipulated using accessors for each of its child elements. You # should be able to set them using an instance of any class that # makes sense # @@ -25,170 +78,57 @@ # # There are also +categories+, +links+, +authors+ and +contributors+, # each of which is an Array of its respective type and can be used # thusly: # - # author = entry.authors.new - # author.name = "Captain Kangaroo" + # author = entry.authors.new :name => "Captain Kangaroo", :email => "kanga@example.net" + # class Entry < Atom::Element + is_atom_element :entry + # the master list of standard children and the types they map to - element :id, String, true - element :title, Atom::Text, true - element :content, Atom::Content, true + atom_string :id - element :rights, Atom::Text - # element :source, Atom::Feed # complicated, eg. serialization + atom_element :title, Atom::Title + atom_element :summary, Atom::Summary + atom_element :content, Atom::Content - element :authors, Atom::Multiple(Atom::Author) - element :contributors, Atom::Multiple(Atom::Contributor) + atom_element :rights, Atom::Rights - element :categories, Atom::Multiple(Atom::Category) - element :links, Atom::Multiple(Atom::Link) + # element :source, Atom::Feed # XXX complicated, eg. serialization - element :published, Atom::Time - element :updated, Atom::Time, true + atom_time :published + atom_time :updated + time ['app', PP_NS], :edited - element :summary, Atom::Text + atom_elements :author, :authors, Atom::Author + atom_elements :contributor, :contributors, Atom::Contributor - def initialize # :nodoc: - super "entry" + element ['app', PP_NS], :control, Atom::Control - # XXX I don't think I've ever actually used this - yield self if block_given? - end + include HasCategories + include HasLinks - # parses XML into an Atom::Entry - # - # +base+ is the absolute URI the document was fetched from - # (if there is one) - def self.parse xml, base = "" - if xml.respond_to? :to_atom_entry - xml.to_atom_entry(base) - elsif xml.respond_to? :read - self.parse(xml.read) - else - begin - REXML::Document.new(xml.to_s).to_atom_entry(base) - rescue REXML::ParseException - raise Atom::ParseError - end - end - end + atom_link :edit_url, :rel => 'edit' def inspect # :nodoc: "#<Atom::Entry id:'#{self.id}'>" end - # declare that this entry has updated. - # - # (note that this is different from Atom::Feed#update!) - def updated! - self.updated = Time.now + def draft + control and control.draft end - # categorize the entry with each of an array or a space-separated - # string - def tag_with tags - return unless tags + alias :draft? :draft - (tags.is_a?(String) ? tags.split : tags).each do |tag| - categories.new["term"] = tag - end + def draft! + self.draft = true end - # the @href of an entry's link[@rel="edit"] - def edit_url - begin - edit_link = self.links.find do |link| - link["rel"] == "edit" - end - - edit_link["href"] - rescue - nil - end - end - - def draft - elem = REXML::XPath.first(extensions, "app:control/app:draft", {"app" => PP_NS}) - - elem and elem.text == "yes" - end - def draft= is_draft - nses = {"app" => PP_NS} - draft_e = REXML::XPath.first(extensions, "app:control/app:draft", nses) - control_e = REXML::XPath.first(extensions, "app:control", nses) - - if is_draft and not draft - unless draft_e - unless control_e - control_e = REXML::Element.new("control") - control_e.add_namespace PP_NS - - extensions << control_e - end - - draft_e = REXML::Element.new("draft") - control_e << draft_e - end - - draft_e.text = "yes" - elsif not is_draft and draft - draft_e.remove - control_e.remove if control_e.elements.empty? + unless control + instance_variable_set '@control', Atom::Control.new end - - is_draft + control.draft = is_draft end - -# XXX this needs a test suite before it can be trusted. -=begin - # tests the entry's validity - def valid? - self.class.required.each do |element| - unless instance_variable_get "@#{element}" - return [ false, "required element atom:#{element} missing" ] - end - end - - if @authors.length == 0 - return [ false, "required element atom:author missing" ] - end - - alternates = @links.find_all do |link| - link["rel"] == "alternate" - end - - unless @content or alternates - return [ false, "no atom:content or atom:link[rel='alternate']" ] - end - - alternates.each do |link| - if alternates.find do |x| - not x == link and - x["type"] == link["type"] and - x["hreflang"] == link["hreflang"] - end - - return [ false, 'more than one atom:link with a rel attribute value of "alternate" that has the same combination of type and hreflang attribute values.' ] - end - end - - type = @content["type"] - - base64ed = (not ["", "text", "html", "xhtml"].member? type) and - type.match(/^text\/.*/).nil? and # not text - type.match(/.*[\+\/]xml$/).nil? # not XML - - if (@content["src"] or base64ed) and not summary - return [ false, "out-of-line or base64ed atom:content and no atom:summary" ] - end - - true - end -=end end end - -# this is here solely so that you don't have to require it -require "atom/xml"