diff --git a/spec/cryss_spec.cr b/spec/cryss_spec.cr index 7975b2d..e55b054 100644 --- a/spec/cryss_spec.cr +++ b/spec/cryss_spec.cr @@ -15,26 +15,6 @@ require "./spec_helper" describe RSS do - ex_cloud = RSS::Cloud.new( - domain: "rpc.sys.com", - port: 80, - path: "/RPC2", - register_procedure: "pingMe", - protocol: "soap" - ) - - ex_enclosure = RSS::Enclosure.new( - url: "http://live.curry.com/mp3/celebritySCms.mp3", - length: 1069871, - type: "audio/mpeg" - ) - - base_channel = RSS::Channel.new( - link: "http://www.goupstate.com/", - title: "GoUpstate.com News Headlines", - description: "The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site." - ) - it "builds enclosure" do w3 = HEADER + ENCLOSURE_TEST @@ -85,5 +65,23 @@ describe RSS do chan = base_channel chan.skip_days = [RSS::Day::Monday, RSS::Day::Wednesday] chan.skip_hours = [2, 6, 18, 22] + + chan.to_s.strip.should eq SKIP_DAY_TEST + end + + it "adds namespace" do + chan = base_channel + chan.add_ns(dc: "http://purl.org/dc/elements/1.1") + chan.ns("dc") do |dc| + dc["rights"] = "Copyright 2002" + end + + item = RSS::Item.new(title: "New item") + item.ns("dc") do |dc| + dc["subject"] = "CSS" + end + chan << item + + chan.to_s.strip.should eq NAMESPACE_TEST end end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 910e705..ec6cbf7 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -15,6 +15,32 @@ require "spec" require "../src/cryss" +def ex_cloud + RSS::Cloud.new( + domain: "rpc.sys.com", + port: 80, + path: "/RPC2", + register_procedure: "pingMe", + protocol: "soap" + ) +end + +def ex_enclosure + RSS::Enclosure.new( + url: "http://live.curry.com/mp3/celebritySCms.mp3", + length: 1069871, + type: "audio/mpeg" + ) +end + +def base_channel + RSS::Channel.new( + link: "http://www.goupstate.com/", + title: "GoUpstate.com News Headlines", + description: "The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site." + ) +end + HEADER = "\n" CLOUD_TEST = "" @@ -63,6 +89,8 @@ SKIP_DAY_TEST = HEADER + <<-XML GoUpstate.com News Headlines http://www.goupstate.com/ The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site. + https://validator.w3.org/feed/docs/rss2.html + cryss 2 6 @@ -76,3 +104,20 @@ SKIP_DAY_TEST = HEADER + <<-XML XML + +NAMESPACE_TEST = HEADER + <<-XML + + + GoUpstate.com News Headlines + http://www.goupstate.com/ + The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site. + https://validator.w3.org/feed/docs/rss2.html + cryss + Copyright 2002 + + New item + CSS + + + +XML diff --git a/src/cryss/channel.cr b/src/cryss/channel.cr index aa596c9..2aafd75 100644 --- a/src/cryss/channel.cr +++ b/src/cryss/channel.cr @@ -137,6 +137,8 @@ module RSS # Contained elements of the channel. getter items : Array(Item) = [] of Item + @namespaces = {} of String => String + def initialize(link : URI | String, @title : String, @description : String) @link = link.is_a?(URI) ? link : URI.parse link end @@ -149,9 +151,21 @@ module RSS push item end + def add_ns(name : String | Symbol, + url : URI | String) + @namespaces["xmlns:#{name.to_s}"] = url.to_s + end + + def add_ns(**ns) + ns.each do |k, v| + add_ns(k, v) + end + end + # Serialises the channel to the XML builder. def to_xml(xml : XML::Builder) xml.element("rss", version: "2.0") do + xml.attributes(@namespaces) xml.element("channel") do emit title emit link @@ -197,6 +211,8 @@ module RSS end end + emit_custom xml + @items.each do |item| item.to_xml xml end diff --git a/src/cryss/element.cr b/src/cryss/element.cr index 6fa01a7..aa9790d 100644 --- a/src/cryss/element.cr +++ b/src/cryss/element.cr @@ -16,6 +16,8 @@ require "xml" module RSS abstract class Element + @custom = {} of String => Hash(String, String) + def to_s(io : IO) to_xml(io) end @@ -27,6 +29,38 @@ module RSS end end + # Adds a non-standard namespaced element to the component. + # + # Yields access to a set of namespaced elements and their values. + # + # Examples taken from [here](http://static.userland.com/gems/backend/rssMarkPilgrimExample.xml). + # + # ```crystal + # channel = RSS::Channel.new(/* ... */) + # channel.add_ns(dc: "http://purl.org/dc/elements/1.1") + # channel.ns("dc") do |dc| + # dc["rights"] = "Copyright 2002" + # end + # + # item = RSS::Item.new(title: "New item") + # item.ns("dc") do |dc| + # dc["subject"] = "CSS" + # end + # + # channel << item + # ``` + # + # If the namespace already exists, it will yield access to the already existing namespace rather + # than overwriting it. Duplicate keys inside that namespace will overwrite, however. + # + # Note that no sanity checking is (currently) done before serializing an element with a custom + # namespaced element, as only the top-level element (a `Channel`) has access to specifying the + # namespace imports. + def ns(name : String, &block) + @custom[name] = {} of String => String if @custom[name]?.nil? + yield @custom[name] + end + # Writes the generated XML to the provided *xml* builder. abstract def to_xml(xml : XML::Builder) @@ -37,5 +71,13 @@ module RSS private macro emit(elem, name) xml.element({{name}}) { xml.text @{{elem}}.to_s } end + + private def emit_custom(xml) + @custom.each do |ns, content| + content.each do |key, val| + xml.element("#{ns}:#{key}") { xml.text val } + end + end + end end end diff --git a/src/cryss/image.cr b/src/cryss/image.cr index 0eb2f11..01c612a 100644 --- a/src/cryss/image.cr +++ b/src/cryss/image.cr @@ -71,6 +71,8 @@ module RSS emit width if @width emit height if @height emit description if @description + + emit_custom xml end end end diff --git a/src/cryss/item.cr b/src/cryss/item.cr index bd18b20..c3662b2 100644 --- a/src/cryss/item.cr +++ b/src/cryss/item.cr @@ -135,6 +135,8 @@ module RSS xml.element("pubDate") { xml.text pub.to_rfc2822 } end xml.element("source", url: @source_url.to_s) { xml.text @source.to_s } if @source + + emit_custom xml end end end