As you know (or may not know, I need to update the My Tools pod), I have a RSS.cfc component that lets you parse RSS feeds in multiple formats. What it doesn't do yet is let you generate RSS. I've begun that work and run into a slight problem.
Each type of RSS feed has different needs when it comes to metadata. Typically these are the blog name and URL along with other elements. Right now to generate an RSS feed my API works like so:
<cfset rssData = rssCFC.generateRSS("rss091",data,meta)>
where meta is a structure. I take your structure and recursively dump it where each key name becomes a pair of starting and ending tags.
What I don't do now is actually validate your metadata. So for example. The image subkey is required for RSS 0.91. Now, I can certainly build a validator for the metadata for the RSS feeds I support. But at the same time, I don't want it to be a pain in the rear for folks to use. Should I simply let folks pass in metadata in whatever format they wish? I doubt most RSS parsers would fail on a RSS feed missing an image subtag. Should I provide an optional validateMeta() method you can run before passing info in? I don't want to do this if I don't bother using it when I generate the RSS. Another option is to fill in the missing items automatically with a empty set of data.
Thoughts?
Archived Comments
Why not have a separate RSSMetaData CFC that you can create an instance of, and when you initialize that you tell it what version you want it for, then use that as the thing you pass into your generate() method. That will allow you to both have a more robust validation and provide a much better API, IMO.
I second Nathan's comment. I would also take data out of the generateRSS() function and make metadata optional. For example:
<cfset rssCFC.setTitle("My Stuff")/>
<cfset rssCFC.setDescription("A feed of my stuff.")/>
<cfset rssCFC.setURL("http://path/to/stuff")/>
<cfloop query="stuff">
<cfset rssCFC.addItem(stuff.name, stuff.url, stuff.description)/>
</cfloop>
<cfset rss091 = rssCFC.generateRSS("rss091)"/>
<cfset rss2 = rssCFC.generateRSS("rss2", rss2MetaData)/>
I disagree Patrick. The example you provided works ok for simple metadata, but won't work on the more complex MD. For example, how you would handle the Image subkey, rss.CFC.SetImageName? Err, ok, that's not to bad, but I just don't think SETs are the way to go.
As for not passing data, I kinda like what you do there. My way forces you to have a query, and forces you to have certain column names. The column stuff is easy to get around - you can QofQ to rename columns. I think I'll switch to what you have for data.
I don't know much about RSS metadata, so I could be completely off the mark. I just know that the simple feeds I've created all have a title, url, and description. That would be an easy interface for me to use. I was thinking that the metadata object could be optional and augment the standard/simple metadata.
One more thought: I don't like the idea of making generateRSS responsible for producing different kinds of feeds. You should be able to introduce new types of feeds without changing RssCFC. Instead, what do you think about creating a set of RSSWriter objects, one for each type of feed?
[cfloop query="stuff"]
[cfset rssFeed.addItem(stuff.title, stuff.url, stuff.description)]
[/cfloop]
[cfset rss091 = rss091Writer.write(rssFeed, metadata)]
[cfset rss2 = rss2Writer.write(rssFeed, metadata)]
I disagree with that as well. While it does make sense in an OO fashion, it goes away from my main goal of keeping things simple. So for example, if a person wants to offer Atom and RSS2 feeds on their site, I want them to be able to use the same CFC but with different values passed to the method.
As for your first paragraph - that's the rub. RSS Meta validation is not simple, but I'm thinking most readers don't care. So I can be "lose" and just not worry about it, or more formal and ensure people use the right metadata.
Ray: I've torn.
On the one hand, what's the point of an RSS generation CFC if it *doesn't* validate what you pass in? Generating bad RSS is easy, and doesn't need any help.
On the other, sheesh, I'm not gonna be the one to argue that you should dedicate additional hours of your life to protecting people from themselves.
Personally, I'd say just split the difference and add a isValid() method that calls feedvalidator.org to check the output. If it passes, return true, and if it doesn't, return false with a copy of the results.
I like the idea of a RSSMetaData cfc (though I'd just call it RSSItem), of which you would create an instance, set the desired fields, and then pass in to the rccCFC.addItem(rssMetaData) call. The flexibility with this approach should allow you to do everything you're looking for, with a very well formed, easy to use API. Think of it this way:
cfquery name="qryRSSItems"
SELECT somedata FROM somedatasource
/cfquery
rssCFC = CreateObject("component", "RSSFeed")
cfloop query="qryRSSItems"
rssItem = CreateObject("component", "RSSItem")
cfset rssItem.Title = "My Title"
cfset rssItem.URL = "http://somewhere.com"
cfset rssItem.Image = "blah"
cfset rssCFC.addItem(rssItem)
/cfloop
cfset rssData = rssCFC.generateFeed()
This allows you to set only the properties on the item that you need, leaving the rest to defaults. The addItem can do the basic validation, then the generateFeed method can use properties as necessary to generate the feed.