require File.dirname(__FILE__) + '/../spec_helper' describe "SAXMachine" do describe "element" do describe "when parsing a single element" do before :each do @klass = Class.new do include SAXMachine element :title end end it "should provide an accessor" do document = @klass.new document.title = "Title" document.title.should == "Title" end it "should allow introspection of the elements" do @klass.column_names.should =~ [:title] end it "should not overwrite the setter if there is already one present" do @klass = Class.new do def title=(val) @title = "#{val} **" end include SAXMachine element :title end document = @klass.new document.title = "Title" document.title.should == "Title **" end describe "the class attribute" do before(:each) do @klass = Class.new do include SAXMachine element :date, :class => DateTime end @document = @klass.new @document.date = DateTime.now.to_s end it "should be available" do @klass.data_class(:date).should == DateTime end end describe "the required attribute" do it "should be available" do @klass = Class.new do include SAXMachine element :date, :required => true end @klass.required?(:date).should be_true end end it "should not overwrite the accessor when the element is not present" do document = @klass.new document.title = "Title" document.parse("") document.title.should == "Title" end it "should *not* overwrite the value when the element is present (new behaviour!)" do document = @klass.new document.title = "Old title" document.parse("New title") document.title.should == "Old title" end it "should save the element text into an accessor" do document = @klass.parse("My Title") document.title.should == "My Title" end it "should save cdata into an accessor" do document = @klass.parse("<![CDATA[A Title]]>") document.title.should == "A Title" end it "should save the element text into an accessor when there are multiple elements" do document = @klass.parse("My Titlebar") document.title.should == "My Title" end it "should save the first element text when there are multiple of the same element" do document = @klass.parse("My Titlebar") document.title.should == "My Title" end end describe "when parsing multiple elements" do before :each do @klass = Class.new do include SAXMachine element :title element :name end end it "should save the element text for a second tag" do document = @klass.parse("My TitlePaul") document.name.should == "Paul" document.title.should == "My Title" end end describe "when using options for parsing elements" do describe "using the 'as' option" do before :each do @klass = Class.new do include SAXMachine element :description, :as => :summary end end it "should provide an accessor using the 'as' name" do document = @klass.new document.summary = "a small summary" document.summary.should == "a small summary" end it "should save the element text into the 'as' accessor" do document = @klass.parse("here is a description") document.summary.should == "here is a description" end end describe "using the :with option" do describe "and the :value option" do before :each do @klass = Class.new do include SAXMachine element :link, :value => :href, :with => {:foo => "bar"} end end it "should escape correctly the ampersand" do document = @klass.parse("asdf") document.link.should == "http://api.flickr.com/services/feeds/photos_public.gne?id=49724566@N00&lang=en-us&format=atom" end it "should save the value of a matching element" do document = @klass.parse("asdf") document.link.should == "test" end it "should save the value of the first matching element" do document = @klass.parse("") document.link.should == "first" end describe "and the :as option" do before :each do @klass = Class.new do include SAXMachine element :link, :value => :href, :as => :url, :with => {:foo => "bar"} element :link, :value => :href, :as => :second_url, :with => {:asdf => "jkl"} end end it "should save the value of the first matching element" do document = @klass.parse("") document.url.should == "first" document.second_url.should == "second" end end end describe "with only one element" do before :each do @klass = Class.new do include SAXMachine element :link, :with => {:foo => "bar"} end end it "should save the text of an element that has matching attributes" do document = @klass.parse("match") document.link.should == "match" end it "should not save the text of an element that doesn't have matching attributes" do document = @klass.parse("no match") document.link.should be_nil end it "should save the text of an element that has matching attributes when it is the second of that type" do document = @klass.parse("no matchmatch") document.link.should == "match" end it "should save the text of an element that has matching attributes plus a few more" do document = @klass.parse("no matchmatch") document.link.should == "match" end end describe "with multiple elements of same tag" do before :each do @klass = Class.new do include SAXMachine element :link, :as => :first, :with => {:foo => "bar"} element :link, :as => :second, :with => {:asdf => "jkl"} end end it "should match the first element" do document = @klass.parse("no matchfirst matchno match") document.first.should == "first match" end it "should match the second element" do document = @klass.parse("no matchfirst matchsecond matchhi") document.second.should == "second match" end end end # using the 'with' option describe "using the 'value' option" do before :each do @klass = Class.new do include SAXMachine element :link, :value => :foo end end it "should save the attribute value" do document = @klass.parse("hello") document.link.should == 'test' end it "should save the attribute value when there is no text enclosed by the tag" do document = @klass.parse("") document.link.should == 'test' end it "should save the attribute value when the tag close is in the open" do document = @klass.parse("") document.link.should == 'test' end it "should save two different attribute values on a single tag" do @klass = Class.new do include SAXMachine element :link, :value => :foo, :as => :first element :link, :value => :bar, :as => :second end document = @klass.parse("") document.first.should == "foo value" document.second.should == "bar value" end it "should not fail if one of the attribute hasn't been defined" do @klass = Class.new do include SAXMachine element :link, :value => :foo, :as => :first element :link, :value => :bar, :as => :second end document = @klass.parse("") document.first.should == "foo value" document.second.should be_nil end end describe "when desiring both the content and attributes of an element" do before :each do @klass = Class.new do include SAXMachine element :link element :link, :value => :foo, :as => :link_foo element :link, :value => :bar, :as => :link_bar end end it "should parse the element and attribute values" do document = @klass.parse("hello") document.link.should == 'hello' document.link_foo.should == 'test1' document.link_bar.should == 'test2' end end describe "when specifying namespaces" do before :all do @klass = Class.new do include SAXMachine element :a, :xmlns => 'urn:test' element :b, :xmlns => ['', 'urn:test'] end end it "should get the element with the xmlns" do document = @klass.parse("hello") document.a.should == 'hello' end it "shouldn't get the element without the xmlns" do document = @klass.parse("hello") document.a.should be_nil end it "shouldn't get the element with the wrong xmlns" do document = @klass.parse("hello") document.a.should be_nil end it "should get an element without xmlns if the empty namespace is desired" do document = @klass.parse("hello") document.b.should == 'hello' end it "should get an element with the right prefix" do document = @klass.parse("hello") document.a.should == 'hello' end it "should not get an element with the wrong prefix" do document = @klass.parse("hello") document.a.should be_nil end it "should get a prefixed element without xmlns if the empty namespace is desired" do pending "this needs a less pickier nokogiri push parser" document = @klass.parse("hello") document.b.should == 'hello' end it "should get the namespaced element even it's not first" do document = @klass.parse("foofoobar") document.a.should == 'bar' end end end end describe "elements" do describe "when parsing multiple elements" do before :each do @klass = Class.new do include SAXMachine elements :entry, :as => :entries end end it "should provide a collection accessor" do document = @klass.new document.entries << :foo document.entries.should == [:foo] end it "should parse a single element" do document = @klass.parse("hello") document.entries.should == ["hello"] end it "should parse multiple elements" do document = @klass.parse("helloworld") document.entries.should == ["hello", "world"] end it "should parse multiple elements when taking an attribute value" do attribute_klass = Class.new do include SAXMachine elements :entry, :as => :entries, :value => :foo end doc = attribute_klass.parse("") doc.entries.should == ["asdf", "jkl"] end end describe "when using the class option" do before :each do class Foo include SAXMachine element :title end @klass = Class.new do include SAXMachine elements :entry, :as => :entries, :class => Foo end end it "should parse a single element with children" do document = @klass.parse("a title") document.entries.size.should == 1 document.entries.first.title.should == "a title" end it "should parse multiple elements with children" do document = @klass.parse("title 1title 2") document.entries.size.should == 2 document.entries.first.title.should == "title 1" document.entries.last.title.should == "title 2" end it "should not parse a top level element that is specified only in a child" do document = @klass.parse("no parsecorrect title") document.entries.size.should == 1 document.entries.first.title.should == "correct title" end it "should parse out an attribute value from the tag that starts the collection" do class Foo element :entry, :value => :href, :as => :url end document = @klass.parse("paul") document.entries.size.should == 1 document.entries.first.title.should == "paul" document.entries.first.url.should == "http://pauldix.net" end end end describe "full example" do XMLNS_ATOM = "http://www.w3.org/2005/Atom" XMLNS_FEEDBURNER = "http://rssnamespace.org/feedburner/ext/1.0" before :each do @xml = File.read('spec/sax-machine/atom.xml') class AtomEntry include SAXMachine element :title element :name, :as => :author element :origLink, :as => :orig_link, :xmlns => XMLNS_FEEDBURNER element :summary element :content element :published end class Atom include SAXMachine element :title element :link, :value => :href, :as => :url, :with => {:type => "text/html"} element :link, :value => :href, :as => :feed_url, :with => {:type => "application/atom+xml"} elements :entry, :as => :entries, :class => AtomEntry, :xmlns => XMLNS_ATOM end end # before it "should parse the url" do f = Atom.parse(@xml) f.url.should == "http://www.pauldix.net/" end it "should parse all entries" do f = Atom.parse(@xml) f.entries.length.should == 5 end it "should parse the feedburner:origLink" do f = Atom.parse(@xml) f.entries[0].orig_link.should == 'http://www.pauldix.net/2008/09/marshal-data-to.html' end end end