<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christopher H. Laco</title>
	<atom:link href="http://chrislaco.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://chrislaco.com</link>
	<description></description>
	<lastBuildDate>Tue, 02 Mar 2010 03:15:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Advance2030 Weekend</title>
		<link>http://chrislaco.com/events/advance2030-weekend/</link>
		<comments>http://chrislaco.com/events/advance2030-weekend/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 02:44:31 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Events]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=246</guid>
		<description><![CDATA[This last weekend, I was fortunate enough to attend the Advance2030 Web Development Weekend held on the LeanDog boat and spend some time with a great bunch of web folk from the area. This included sitting next to Joe Fiorini and Nate Klaiber and getting schooled in Rails on a nonstop basis. While the weekend [...]]]></description>
			<content:encoded><![CDATA[<p>This last weekend, I was fortunate enough to attend the <a href="http://advance2030.wordpress.com/">Advance2030 Web Development Weekend</a> held on the LeanDog boat and spend some time with a great bunch of web folk from the area. This included sitting next to Joe Fiorini and Nate Klaiber and getting schooled in Rails on a nonstop basis. While the weekend was a great experience, I wanted to get some thoughts up about it while they&#8217;re still fresh.</p>
<p>Friday night was a great start. After a group gathering to go over the site, we split up into pairs to get a set of story cards created. This was a relatively new experience for me in terms of planning a development cycle.  After tinkering with Cucumber over the last month, it seems to fit right into the Agile/BDD way and it&#8217;s something that certainly helps me focus on knowing what I plan on doing before I start coding.</p>
<p>Saturday was a different story. I literally spent the first 5 hours or so struggling with the Rails environment chosen for the weekend. I&#8217;m no Rails expert and I understand why some gems were chosen, like Bundler in a forward looking approach to the impending Rails3 release.  But it seems a few of us had issues with it in combination with Authlogic because of which gems loaded first and if we had previously installed any gems using sudo instead of installing them into local gems. When it&#8217;s all said and done, I could run rake spec but Command-R in Textmate never worked right. Even after moving on I spent a lot of time dealing with schema.rb merge conflicts when pulling from upstream. This isn&#8217;t a terribly surprise given that file is a bottleneck of sorts when you have a lot of people adding migrations in rapid fire.</p>
<p>All in all, I got nothing done Saturday and I&#8217;m not proud of it. I lost my will to live at around 3PM. If I were to start an event like this, I would give serious thought to removing some of these pain points for the developers, esp for people who aren&#8217;t Rails experts. Stick to the stock Rails gem management setup. The less gems you load, the less shenanigans you have to fight, esp ones that try and manage gems/paths. At list for the first iteration. Also, I would either get the migrations/schema done before the event, or at least funnel migration creation through 1-2 people. Let everyone else mock their way down from the outside in.</p>
<p>Sunday was awesome. I did what I should&#8217;ve done Saturday. Grab a card and get something out the door and stop worrying if I&#8217;m doing it &#8220;right&#8221; the first time. It&#8217;s easier to re-factor than it is to create. Move forward. By the end of the night I managed to get a calendar page up, styled with the help of Parag including next/previous and month dropdown selection support.</p>
<p>I&#8217;m definitely looking forward to the next event like this. Hopefully by then I can get my current Rails project done so I won&#8217;t suck so much at the next event. <img src='http://chrislaco.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/events/advance2030-weekend/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>WebFormViewEngine Path Defaults Seem Broken</title>
		<link>http://chrislaco.com/microsoft/webformviewengine-path-defaults-seem-broken/</link>
		<comments>http://chrislaco.com/microsoft/webformviewengine-path-defaults-seem-broken/#comments</comments>
		<pubDate>Thu, 21 Jan 2010 03:54:07 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Microsoft]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=239</guid>
		<description><![CDATA[This post is about 60% fact and 40% opinion. Not a bad ratio. I&#8217;ve been working a lot in ASP.NET MVC lately building up a set of classes to be reused across multiple sites in the near future. A few of these legos are small view classes that inherit from WebFormViewEngine. They all do small [...]]]></description>
			<content:encoded><![CDATA[<p>This post is about 60% fact and 40% opinion. Not a bad ratio. I&#8217;ve been working a lot in ASP.NET MVC lately building up a set of classes to be reused across multiple sites in the near future. A few of these legos are small view classes that inherit from <code>WebFormViewEngine</code>. They all do small things, like adding additional file search paths, loading views from resources and fetching views from remote storage. Since you can stack view engines in <code>ViewEngines.Engines</code>, you can add and number of these engines together to get the functionality you want on a site per site basis. First engine to claim it can service a view wins. Nothing special there really.</p>
<p>I stumbled across something today that seems obvious in retrospect that leads me to the opinion that the default view/partial view search patterns are broken. If you crack open Reflector.NET and point it towards <code>WebFormViewEngine</code>, you&#8217;ll see that <code>ViewLocationFormats</code> and <code>PartialViewLocationFormats</code> are declared the same:</p>
<pre style="padding-left: 30px;">~/Views/{1}/{0}.aspx</pre>
<pre style="padding-left: 30px;">~/Views/{1}/{0}.ascx</pre>
<pre style="padding-left: 30px;">~/Views/Shared/{0}.aspx</pre>
<pre style="padding-left: 30px;">~/Views/Shared/{0}.ascx</pre>
<p><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 19px; white-space: normal; font-size: 13px;">In other words, for the <code>Index</code> action on the <code>Home</code> controller, the following paths are searched:</span></p>
<pre style="padding-left: 30px;">~/Views/Home/Index.aspx</pre>
<pre style="padding-left: 30px;">~/Views/Home/Index.ascx</pre>
<pre style="padding-left: 30px;">~/Views/Shared/Index.aspx</pre>
<pre style="padding-left: 30px;">~/Views/Shared/Index.ascx</pre>
<p>This all seems well and good until you happen to have a partial view that has the same name as an action that either happens to be shared, or the parti is being overridden in the local controllers folder. Here&#8217;s a somewhat plausible example: A <code>Search</code> controller with an <code>Items</code> action:</p>
<pre style="padding-left: 30px;">~/Views/Search/Items.aspx</pre>
<pre style="padding-left: 30px;">    (the view page for the Items action)</pre>
<pre style="padding-left: 30px;">~/Views/Home/Index.ascx</pre>
<pre style="padding-left: 30px;">    (possibly an items partial view customized just for this controller/action)</pre>
<pre style="padding-left: 30px;">~/Views/Shared/Items.ascx</pre>
<pre style="padding-left: 30px;">    (the shared partial view to render items in many controllers/actions/views)</pre>
<p>This is where the problems start. By default, a call to <code>RenderPartial("Items")</code> within the <code>Items.aspx</code> page is going to find the <code>Items.aspx</code> file first and not the <code>Items.ascx</code>. I&#8217;ve never ever seen anyone in the wild put <em>partial views</em> in an <em>aspx</em> file nor have I ever seen anyone put a full <em>action view</em> in an <em>ascx</em> file. So why on earth did they all them to be interchangeable in the location paths? Broken I say. Broken.</p>
<p>The good news is that it&#8217;s super easy to tweak these kinds of things in ASP.NET MVC. Inherit from <code>WebFormViewEngine</code> and set your own <code>ViewLocationFormats</code> and <code>PartialViewLocationFormats</code>. I my case I decided to force the issue explicitly. <code>ViewLocationFormats</code> <em>only looks for aspx</em> files and <code>PartialViewLocationFormats</code> <code>only look for ascx</code> files. Now we can override shared partial views locally in controller folders without worrying about name collisions.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/microsoft/webformviewengine-path-defaults-seem-broken/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Template Toolkit for .NET</title>
		<link>http://chrislaco.com/software/template-toolkit-for-net/</link>
		<comments>http://chrislaco.com/software/template-toolkit-for-net/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 23:11:05 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[antlr]]></category>
		<category><![CDATA[ast]]></category>
		<category><![CDATA[lexer]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[template toolkit]]></category>
		<category><![CDATA[tt]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=231</guid>
		<description><![CDATA[A while back I got the urge the waste time on yet another project. This time, it&#8217;s a version of  Template Toolkit for .NET. As usual, you can find the project on GitHub.
Not knowing anything about what it takes to convert a template language to .NET I started from ground up learning how to [...]]]></description>
			<content:encoded><![CDATA[<p>A while back I got the urge the waste time on yet another project. This time, it&#8217;s a version of  <a href="http://template-toolkit.org/">Template Toolkit</a> for .NET. As usual, you can find the project on <a href="http://github.com/claco/tt.net/">GitHub</a>.</p>
<p>Not knowing anything about what it takes to convert a template language to .NET I started from ground up learning how to use ANTLR to create a lexer/parser automatically from a grammar definition. As if that wasn&#8217;t enough, my parser output is an Abstract Syntax Tree, which I then use another grammar to template it into C# code using StringTemplate templates, which ANTLR supports natively.</p>
<p>It&#8217;s slow going, but I&#8217;m learning a lot along the way. At some point I&#8217;m going to output VB.NET and it still needs a lot of work for compilation, dll caching and such. The code is in a pre beta state, but it does support basic GET/SET/DEFAULT/IF statements and I&#8217;m slowly adding more as I get more comfortable with ANTLR.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/software/template-toolkit-for-net/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Writing ReSharper 4.5.x Macros</title>
		<link>http://chrislaco.com/articles/writing-resharper-45x-macros/</link>
		<comments>http://chrislaco.com/articles/writing-resharper-45x-macros/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 00:48:45 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[macros]]></category>
		<category><![CDATA[resharper]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=212</guid>
		<description><![CDATA[While working on the MVC Marathon, I decided to try out ReSharper and write some good templates to make my time unit testing in ASP.NET MVC a little quicker. I soon realized that I needed a macro that would turn the unit test class name (HomeControllerTests) into the name of the controller being tested (HomeController).
Since [...]]]></description>
			<content:encoded><![CDATA[<p>While working on the MVC Marathon, I decided to try out ReSharper and write some good templates to make my time unit testing in ASP.NET MVC a little quicker. I soon realized that I needed a macro that would turn the unit test class name (HomeControllerTests) into the name of the controller being tested (HomeController).</p>
<p><span id="more-212"></span>Since ReSharper only has a few macros that will make copies of other template variables, I started scouring the internet for clues and found <a href="http://mindstudies.psy.soton.ac.uk/dmitri/blog/index.php/archives/202">this post</a>. It&#8217;s a little outdate for ReSharper for 4.5, so here&#8217;s what I had to do to make my custom macro.</p>
<p>You can download the source or fork your own on <a href="http://github.com/claco/resharpermacros/">GitHub</a>.</p>
<p><!--more--></p>
<h2>Creating the Macro</h2>
<ol>
<li>Create a new C# class library project.</li>
<li>Add references to the following dlls:
<pre class="brush:csharp">JetBrains.Platform.ReSharper.UI.dll
JetBrains.ReSharper.Feature.Services.dll</pre>
</li>
<li>Add a new class to your project and mark it with the Macro attribute and implement the IMacro interface:
<pre class="brush:csharp">using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.Macros;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.Hotspots;

namespace ChrisLaco.ReSharper.Macros
{
    // {#0:pattern}, etc are arguments that are passed to EvaluateQuickResult arguments[]
    [Macro("applyRegex", ShortDescription = "Replace string matching {#0:pattern} with {#1:string} in {#2:variable}", LongDescription = "Applies regex to replace value in another template variable.")]
    class ApplyRegexMacro : IMacro
    {
        #region IMacro Members

        public string EvaluateQuickResult(IHotspotContext context, IList&lt;string&gt; arguments)
        {
            if (arguments.Count != 3)
            {
                return null;
            }
            else
            {
                try
                {
                    // Using the regex argument, replate the value of the specified template variable with the specified string
                    var result = Regex.Replace(arguments[2], arguments[0], arguments[1], RegexOptions.IgnoreCase);

                    return result;
                }
                catch (Exception e)
                {
                    return "&lt;" + e.Message + "&gt;";
                }
            }
        }

        public HotspotItems GetLookupItems(IHotspotContext context, IList&lt;string&gt; arguments)
        {
            return null;
        }

        public string GetPlaceholder()
        {
            return "a";
        }

        public bool HandleExpansion(IHotspotContext context, IList&lt;string&gt; arguments)
        {
            return false;
        }

        // Must return the same # of parameters declared in the Macro
        public ParameterInfo[] Parameters
        {
            get
            {
                return new[] { new ParameterInfo(ParameterType.String), new ParameterInfo(ParameterType.String), new ParameterInfo(ParameterType.VariableReference) };
            }
        }

        #endregion
    }
}</pre>
</li>
<li>In AssemblyInfo.cs, apply assembly attributes for the ReSharper plugin manager:
<pre class="brush:csharp">using System.Reflection;
using System.Runtime.InteropServices;
using JetBrains.UI.Application.PluginSupport;

[assembly: PluginTitle("ChrisLaco.ReSharper.Macros")]
[assembly: PluginDescription("Provides additional template macros: ApplyRegexMacro.")]
[assembly: PluginVendor("Christopher H. Laco")]</pre>
<p>This information will be displayed in the ReSharper -&gt; Plugins dialag:</p>
<p><a href="http://chrislaco.com/wp-content/uploads/2009/04/picture-1.png"><img class="aligncenter size-medium wp-image-216" title="ReSharper Plugin Manager" src="http://chrislaco.com/wp-content/uploads/2009/04/picture-1-300x176.png" alt="ReSharper Plugin Manager" width="300" height="176" /></a></li>
<li>To install plugin, simply copy the compiled dll into one of the follow directories:
<pre class="brush::csharp">C:\Documents and Settings\username\Application Data\JetBrains\ReSharper\v4.5\vs9.0\Plugins
C:\Program Files\JetBrains\ReSharper\v4.5\Bin\Plugins</pre>
</li>
</ol>
<h2>Using the Macro</h2>
<ol>
<li>Select the macro from the list of available macros:<a href="http://chrislaco.com/wp-content/uploads/2009/04/picture-2.png"><img class="aligncenter size-medium wp-image-217" title="Choose Macro" src="http://chrislaco.com/wp-content/uploads/2009/04/picture-2-279x300.png" alt="Choose Macro" width="279" height="300" /></a></li>
<li>Click on the macro variables to set them:<a href="http://chrislaco.com/wp-content/uploads/2009/04/picture-3.png"><img class="aligncenter size-medium wp-image-218" title="Set Macro Variables" src="http://chrislaco.com/wp-content/uploads/2009/04/picture-3-300x235.png" alt="Set Macro Variables" width="300" height="235" /></a></li>
<li>Finish up the template text and savr your changes:<a href="http://chrislaco.com/wp-content/uploads/2009/04/picture-4.png"><img class="aligncenter size-medium wp-image-219" title="Apply Macro to Template" src="http://chrislaco.com/wp-content/uploads/2009/04/picture-4-300x125.png" alt="Apply Macro to Template" width="300" height="125" /></a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/writing-resharper-45x-macros/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Macbook Pro Go Boom</title>
		<link>http://chrislaco.com/apple/macbook-pro-go-boom/</link>
		<comments>http://chrislaco.com/apple/macbook-pro-go-boom/#comments</comments>
		<pubDate>Thu, 02 Apr 2009 00:19:57 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[applecare]]></category>
		<category><![CDATA[lcd]]></category>
		<category><![CDATA[leakage]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=203</guid>
		<description><![CDATA[Slowly over the last couple of weeks, my Macbook Pro started going downhill. It started with a big bad pixel in the top middle of the screen. Nothing worth pursuing since there was only one. Then Saturday while doing some full screen editing in Textmate I noticed a rash of speckles on the right side. [...]]]></description>
			<content:encoded><![CDATA[<p>Slowly over the last couple of weeks, my Macbook Pro started going downhill. It started with a big bad pixel in the top middle of the screen. Nothing worth pursuing since there was only one. Then Saturday while doing some full screen editing in Textmate I noticed a rash of speckles on the right side. I thought it was dirt. Of course, it didn&#8217;t come off when I cleaned the screen.<span id="more-203"></span></p>
<p>Off I went to the Apple store Sunday to get it fixed.  Aside from the wait due to the place being packed as always, the process was pretty easy. My Genius called over another Genius and said &#8220;Hey, come look at this.&#8221;  Oh no I thought. Apparently, one thing that gets the Genius folk excited are laptops that are old but in mint condition. They seemed pleased that my old 2007 was cherry. Of course it was. I pamper the thing. The Speck case doesn&#8217;t hurt either. And it doesn&#8217;t hurt my chances of getting the screen fixed without issue as well.</p>
<p>Luckily for me, the LCD replacement was an in store repair. Once the part came in Tuesday the repair was done and they called me this evening to come pick it up.</p>
<p>Nice. Thank god I spent the $ I saved on an Apple store refurb on AppleCare. <img src='http://chrislaco.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/apple/macbook-pro-go-boom/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Monitoring Local Directories with Siphon</title>
		<link>http://chrislaco.com/articles/monitoring-local-directories-with-siphon/</link>
		<comments>http://chrislaco.com/articles/monitoring-local-directories-with-siphon/#comments</comments>
		<pubDate>Thu, 19 Mar 2009 02:00:58 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[local directories]]></category>
		<category><![CDATA[monitor]]></category>
		<category><![CDATA[siphon]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=183</guid>
		<description><![CDATA[This is the third in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on GitHub.
Introduction
In this article, I&#8217;ll show you how to monitor a local directory using Siphon and go over some of the different options that are available.
The [...]]]></description>
			<content:encoded><![CDATA[<p>This is the third in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on <a title="Siphon on GitHub" href="http://github.com/claco/siphon/">GitHub</a>.</p>
<h2><span id="more-183"></span>Introduction</h2>
<p>In this article, I&#8217;ll show you how to monitor a local directory using Siphon and go over some of the different options that are available.</p>
<h2>The Scenerio</h2>
<p>Your mission, should you chose to accept it, is to check a local file system directory for new CSV files twice a day at 8am and 3pm. Upon completion, or failure of processing each file, we will rename then move them to separate directories for a human to review. In order to achieve this, we will need to:</p>
<ol>
<li>Create a custom processor class to process our csv files</li>
<li>Configure a new monitor in app.config</li>
<li>Run the monitor</li>
</ol>
<h2>Creating a Processor</h2>
<p>In order to have Siphon process data, we must create a custom processor class. More often than not, this will probably just be an adapter  between the Siphon API and your other existing software. The only requirements for a processor in Siphon is that the class:</p>
<ol>
<li>The class must have a constructor that takes no parameters (private, protected or public)</li>
<li>The class must implement the IDataProcessor interface</li>
</ol>
<p>For the sake of me getting my C# skills up to par, we&#8217;ll use C# for this example. I&#8217;m going to assume that you have a basic understanding of starting new projects, classes, and adding references to other assemblies. Once you&#8217;ve created a new class and added a reference to System.Configuration and Siphon.dll, we want to implement the IDataProcessor interface. This consists of 3 methods: Initialize, Process and Dispose:</p>
<pre class="brush:csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ChrisLaco.Siphon;

namespace CustomProcessor
{
    public class CustomProcessor : IDataProcessor
    {
        public void Initialize(ProcessorElement config)
        {
        }

        public bool Process(IDataItem item)
        {
        }

        public void Dispose()
        {
        }
    }
}</pre>
<p>A data processor has three basic actions:</p>
<ol>
<li>Initialize the process using the configuration supplied from the service or console where this monitor is running</li>
<li>Process the new data item, returning True if it was processed successfully or False if processing failed</li>
<li>Cleanup any resources</li>
</ol>
<h3>First, a word from our sponsor about IDataItem and IDataItem&lt;T&gt;</h3>
<p>When a monitor finds new items to process, it wraps them in any number of classes that implement the IDataItem&lt;T&gt; interface. This interface exposes three properties:</p>
<ol>
<li><strong>Name</strong>: A friendly name for the data item</li>
<li><strong>GetString</strong>: The contents of the data item</li>
<li><strong>Data</strong>: The underlying data item in it&#8217;s native format</li>
</ol>
<p>What gets returned from the Data property depends on what type of data each specific monitor understands. The LocalDirectoryProcessor returns FileInfo objects, as does the FtpMonitor. The DatabaseMonitor returns TableRow objects. Some monitors, like Imap and Pop3 monitors, return simple string that also has a reference to the temp file where it&#8217;s stored. It is assumed, wisely or otherwise that the monitor and the processor have to both understand and work with the same Data types. It wouldn&#8217;t make much sense for the DatabaseMonitor to send an IDataItem&lt;TableRow&gt; to a processor that assumed it was getting IDataItem&lt;FileInfo&gt;. Of course, there&#8217;s nothing stopping you from having an uber Process() method that looks to see what types it&#8217;s getting understand multiple item types. The Process method on the IDataProcessor interface is actually set to receive IDataItem [without a specific type] for just this reason and GetString will suffice it you&#8217;re only inspecting contents.</p>
<h3>Back to implementing IDataProcessor</h3>
<p>For the sake of simplicity, let&#8217;s simply inspect the name of the new files and return True if the file has one name, and False for any other file:</p>
<pre class="brush:csharp">public bool Process(IDataItem item)
{
    IDataItem&lt;FileInfo&gt; fileItem = (IDataItem&lt;FileInfo&gt;)item;

    if (fileItem.Data.Name == "success.csv")
    {
        return true;
    }
    else
    {
        return false;
    }
}</pre>
<p>First, we cast the item to an instance of IDataItem&lt;FileInfo&gt;. Next, we check the file name using the Name property of Data, which is a FileInfo object of course. If it is success.csv, return True. Otherwise, return False.</p>
<h2>Configuration</h2>
<p>Now that we our simple processor,  we need to configure a monitor in our app.config:</p>
<pre class="brush:xml">&lt;siphon&gt;
  &lt;monitors&gt;
    &lt;monitor name="CsvMonitor" type="ChrisLaco.Siphon.LocalDirectoryMonitor, Siphon"&gt;
      &lt;settings&gt;
        &lt;add name="Path" value="C:\" /&gt;
        &lt;add name="Filter" value="*.csv" /&gt;
        &lt;add name="ProcessCompleteActions" value="Rename, Move" /&gt;
        &lt;add name="ProcessFailureActions" value="Rename, Move" /&gt;
        &lt;add name="CompletePath" value="Completed" /&gt;
        &lt;add name="FailurePath" value="Failed" /&gt;
        &lt;add name="CreateMissingFolders" value="True" /&gt;
      &lt;/settings&gt;
      &lt;schedule type="ChrisLaco.Siphon.DailySchedule, Siphon"&gt;
        &lt;daily&gt;
          &lt;time value="8:00" /&gt;
          &lt;time value="15:00" /&gt;
        &lt;/daily&gt;
      &lt;/schedule&gt;
      &lt;processor type="CustomProcessor, CustomProcessor" /&gt;
    &lt;/monitor&gt;
  &lt;/monitors&gt;
&lt;/siphon&gt;</pre>
<p>First we give the monitor a name and tell Siphon what class to use:</p>
<pre class="brush:xml">&lt;monitor name="CsvMonitor" type="ChrisLaco.Siphon.LocalDirectoryMonitor, Siphon"&gt;</pre>
<p>Next, we need to configure the monitor using the settings group:</p>
<ul>
<li><strong>Path</strong>: The full path where to scan for new files.</li>
<li><strong>Filter</strong>: The file name filter to use; only returning files that match the filter.</li>
<li><strong>ProcessCompleteActions</strong>:<strong> </strong>The actions to perform on the files when processing is successful.</li>
<li><strong>ProcessFailureActions</strong>: The actions to perform on the files when processing fails.</li>
<li><strong>CompletePath</strong>: The relative or full path to the folder to move files into when processing succeeds if the Move action is set.</li>
<li><strong>FailurePath</strong>: The relative or full path to the folder to move files into when processing fails if the Move action is set.</li>
<li><strong>CreateMissingFolders</strong>: Create the Path, CompletePath and FailurePath folders if they do not already exist.</li>
</ul>
<p>In Siphon, whenever possible, the Path, FailurePath and CompletePath options accept and convert to use Uri objects internally. C:\ is converted to file:///C:/. This is because the same code is used for remote folders as well in FtpMonitor, where Path could be ftp://example.com/ instead. Just like internet uris, the CompletePath and FailurePath settings can be absolute (ftp://example.com/failure/), root relative (/failure/) or relative to the path directory (../failure). This allows you to easily nest directories if necessary.</p>
<p>Now that we have our monitor configured, we need to give it a schedule:</p>
<pre class="brush:xml">&lt;schedule type="ChrisLaco.Siphon.DailySchedule, Siphon"&gt;
  &lt;daily&gt;
    &lt;time value="8:00" /&gt;
    &lt;time value="15:00" /&gt;
  &lt;/daily&gt;
&lt;/schedule&gt;</pre>
<p>Here we&#8217;ve selected the DailySchedule class and set two times of day to schan for new files: 8:00 and 15:00. The times are in the local server time zone and based on 24 hour notation. The only thing left to do is send the data to our custom processor above:</p>
<pre class="brush:xml">&lt;processor type="CustomProcessor, CustomProcessor" /&gt;</pre>
<h2>Running Your Monitor</h2>
<p>We have a custom processor and a fresh config. Now we need to run it. There are two ways to make use of your monitors in Siphon:</p>
<ol>
<li><strong>SiphonService</strong>: Windows Service that loads monitors from app.config at startup and runs the monitors based on their schedules.</li>
<li><strong>SiphonConsole</strong>: Command line utility that loads monitors from app.config and runs the monitors manually.</li>
<li><strong>Both</strong>: Use the command line utility to run a monitor immediately that is hosted on a local/remote SiphonService.</li>
</ol>
<p>If you want to run your monitor automatically, simply add it to SiphonService.exe.config whever SiphonService is installed and restart the service. If you want to run your monitor manually fomr the command line, add it to SiphonConsole.exe.config.</p>
<p>The run a monitor, or all monitors from the command line, simple do:</p>
<pre class="brush:xml">C:\Siphon&gt;SiphonConsole.exe CsvMonitor
C:\Siphon&gt;SiphonConsole.exe *</pre>
<p>To run a monitor hosted by SiphonService, simply supply the location of the service, then the monitor name:</p>
<pre class="brush:xml">C:\Siphon&gt;SiphonConsole.exe http://localhost/ServiceAdminPath CsvMonitor
C:\Siphon&gt;SiphonConsole.exe http://myotherserver/ServiceAdminPath *</pre>
<p>If you want to run monitors on hosted under SiphonService, you must enable the remote administration in the config:</p>
<pre class="brush:xml">&lt;siphon enableRemoteAdministration="True"&gt;</pre>
<p>We&#8217;ll cover how to control the administration url/port from config as wel as using endpoint configurations instead of the http url i the console in a future post. Happy monitoring!</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/monitoring-local-directories-with-siphon/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Siphon Configuration</title>
		<link>http://chrislaco.com/articles/siphon-configuration/</link>
		<comments>http://chrislaco.com/articles/siphon-configuration/#comments</comments>
		<pubDate>Mon, 16 Mar 2009 02:53:49 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[siphon]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=166</guid>
		<description><![CDATA[This is the second in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on GitHub.
Introduction
In this article, I&#8217;m going to cover how Siphon configuration works, the options and how to use the configuration classes to read/write configuration information.
Configuration Section
Here&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>This is the second in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on <a title="Siphon on GitHub" href="http://github.com/claco/siphon/">GitHub</a>.</p>
<h2><span id="more-166"></span>Introduction</h2>
<p>In this article, I&#8217;m going to cover how Siphon configuration works, the options and how to use the configuration classes to read/write configuration information.</p>
<h2>Configuration Section</h2>
<p>Here&#8217;s a sample Siphon configuration section from the source code:</p>
<pre class="brush:xml">&lt;configSections&gt;
  &lt;section name="siphon" type="ChrisLaco.Siphon.SiphonConfigurationSection, Siphon" /&gt;
&lt;/configSections&gt;

&lt;siphon&gt;
  &lt;monitors&gt;
    &lt;monitor name="IntervalMonitor" type="ChrisLaco.Siphon.LocalDirectoryMonitor, Siphon"&gt;
      &lt;settings&gt;
        &lt;add name="Path" value="C:\" /&gt;
        &lt;add name="Filter" value="*.tmp" /&gt;
      &lt;/settings&gt;
      &lt;schedule type="ChrisLaco.Siphon.IntervalSchedule, Siphon"&gt;
        &lt;interval value="1.2:3:4" /&gt;
      &lt;/schedule&gt;
      &lt;processor type="ChrisLaco.Siphon.DummyProcessor, Siphon" /&gt;
    &lt;/monitor&gt;
  &lt;/monitors&gt;
&lt;/siphon&gt;</pre>
<p>Let&#8217;s break this down piece by piece. First, we need to tell .NET who&#8217;s in charge of the siphon configuration section:</p>
<pre class="brush:xml">&lt;configSections&gt;
 &lt;section name="siphon" type="ChrisLaco.Siphon.SiphonConfigurationSection, Siphon" /&gt;
&lt;/configSections&gt;</pre>
<p>Next, we add a new monitor element to the monitors collection:</p>
<pre class="brush:xml">&lt;monitor name="IntervalMonitor" type="ChrisLaco.Siphon.LocalDirectoryMonitor, Siphon"&gt;</pre>
<p>Here we have a monitor named IntervalMonitor and told Siphon what type to load. If you&#8217;re not familiar with using type strings in .NET configuration,  the string above tells .NET that we want to use the class ChrisLaco.Siphon.LocalDirectoryMonitor in the Siphon assembly. Note that if you want to use types that are installed into the GAC you will need to use the full type string including the Culture, PublicKeyToken and Version.</p>
<p>Next, we configure setting required by the LocalDirectoryMonitor:</p>
<pre class="brush:xml">&lt;settings&gt;
  &lt;add name="Path" value="C:\" /&gt;
  &lt;add name="Filter" value="*.tmp" /&gt;
&lt;/settings&gt;</pre>
<p>The settings consist of name/value pairs that are passed to the monitors Initialize() method. The number of options and their values vary depending on the monitor being used. Next, we&#8217;ll tell the monitor how often to look for new data:</p>
<pre class="brush:xml">&lt;schedule type="ChrisLaco.Siphon.IntervalSchedule, Siphon"&gt;
  &lt;interval value="1.2:3:4" /&gt;
&lt;/schedule&gt;</pre>
<p>Just like with the monitor element above, we need to tell Siphon what schedule class we want to use. In our case, we&#8217;re going to use the IntervalSchedule, and set the interval using a string TimeSpan.Parse() understands: 1 day, 2 hours, 3 minutes and 4 seconds. If you are interested in a daily schedule, you can also use the daily element to specify a daily schedule:</p>
<pre class="brush:xml">&lt;schedule type="ChrisLaco.Siphon.DailySchedule, Siphon"&gt;
  &lt;daily&gt;
    &lt;time value="8:00" /&gt;
    &lt;time value="15:00" /&gt;
  &lt;/daily&gt;
&lt;/schedule&gt;</pre>
<p>Note: whether you use an interval or a daily schedule, the schedule type you use must understand the configuration you&#8217;ve chosen. As we&#8217;ll see in the Configuration classes later, the configuration format is setup to handle the most common use cases, like daily schedule values. Just like the monitor element, the schedule element also has a settings element used to pass name/value options to the specified schedule type.</p>
<p>Now that we&#8217;ve chosen a monitor and a schedule, the last thing to do is to tell Siphon who will process the newly found data as it arrives:</p>
<pre class="brush:xml">&lt;processor type="ChrisLaco.Siphon.DummyProcessor, Siphon" /&gt;</pre>
<p>Here we&#8217;ve loaded the DummyProcessor, which does does nothing with new data and always returns True. As with the monitor and schedule elements, the processor element also has a settings element collection.</p>
<h2>Reading Configuration</h2>
<p>Siphon comes with a set of classes to read/write siphon configuration data. Once you have finished setting up your app.config, you can use the SiphonConfigurationSection class to load the section and loop through all of the configured monitors:</p>
<pre class="brush:vb">REM Load config section: defaults to 'siphon'
Dim section As SiphonConfigurationSection = SiphonConfigurationSection.GetSection

REM Loop through monitor elements
For Each monitor As MonitorElement In section.Monitors

    REM Get a setting
    Dim path As String = monitor.Settings("Path").Value

    REM create an instance of the specified type
    Dim instance As IDataMonitor = monitor.CreateInstance
    instance.Path = path

    REM Set the schedule
    instance.Schedule = monitor.Schedule.CreateInstance

    REM Set the processor
    instance.Processor = monitor.Processor.CreateInstance

    REM Process new data
    instance.Process()
Next</pre>
<p>The code above is pretty straight forward. One item of interest: when CreateInstance is called, it creates an instance of the specified type and calls the Initialize() method on the new instance. While we set the Path setting manually, the monitors Initialize() could have also done the same thing.</p>
<p>All monitors must implement the IDataMonitor interface. All schedules must implement the IMonitorSchedule interface and all processors must implement the IDataProcessor interface. CreateInstance() looks for a constructor with no parameters in the type being created.</p>
<h2>Writing Configuration</h2>
<p>You can also use the configuration classes to programaticaly create configuration files. SImply create the settings in code, then save the config to the specified file:</p>
<pre class="brush:vb">Dim config As Configuration = ConfigurationManager.OpenExeConfiguration("C:\Path\To\SomeApp.exe")
Dim section As New SiphonConfigurationSection
Dim monitor As New MonitorElement("TestMonitor", "LocalDirectoryMonitor, Siphon")

monitor.Settings.Add(New NameValueConfigurationElement("Path", "C:\"))
monitor.Processor = New ProcessorElement("MockProcessor, SiphonTests")
monitor.Processor.Settings.Add(New NameValueConfigurationElement("Foo", "Bar"))
monitor.Schedule.Type = "IntervalSchedule, Siphon"
monitor.Schedule.Daily.Add(New TimeElement(New TimeSpan(100)))
monitor.Schedule.Daily.Add(New TimeSpan(200))
section.Monitors.Add(monitor)

config.Sections.Add("siphon", section)
config.Save()</pre>
<p>This will update the app.config file for the specified executable path with the new configuration information. The code above will generate something like this:</p>
<pre class="brush:vb">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;configuration&gt;
    &lt;configSections&gt;
        &lt;section name="siphon" type="ChrisLaco.Siphon.SiphonConfigurationSection, Siphon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0576062738dd7ad9" /&gt;
    &lt;/configSections&gt;
    &lt;siphon&gt;
        &lt;monitors&gt;
            &lt;monitor name="TestMonitor" type="LocalDirectoryMonitor, Siphon"&gt;
                &lt;schedule type="IntervalSchedule, Siphon"&gt;
                    &lt;interval value="00:01:00" /&gt;
                    &lt;daily&gt;
                        &lt;time value="00:00:00.0000100" /&gt;
                        &lt;time value="00:00:00.0000200" /&gt;
                    &lt;/daily&gt;
                &lt;/schedule&gt;
                &lt;processor type="MockProcessor, SiphonTests"&gt;
                    &lt;settings&gt;
                        &lt;add name="Foo" value="Bar" /&gt;
                    &lt;/settings&gt;
                &lt;/processor&gt;
                &lt;settings&gt;
                    &lt;add name="Path" value="C:\" /&gt;
                &lt;/settings&gt;
            &lt;/monitor&gt;
        &lt;/monitors&gt;
    &lt;/siphon&gt;
&lt;/configuration&gt;</pre>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/siphon-configuration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An Introduction To Siphon</title>
		<link>http://chrislaco.com/articles/an-introduction-to-siphon/</link>
		<comments>http://chrislaco.com/articles/an-introduction-to-siphon/#comments</comments>
		<pubDate>Sun, 15 Mar 2009 17:27:00 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[console]]></category>
		<category><![CDATA[ftp]]></category>
		<category><![CDATA[imap]]></category>
		<category><![CDATA[msmq]]></category>
		<category><![CDATA[pop3]]></category>
		<category><![CDATA[service]]></category>
		<category><![CDATA[siphon]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=157</guid>
		<description><![CDATA[This is the first in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on GitHub.
Introduction
For me, writing software seems to be about scratching an itch, filling a need by creating your own solution. Siphon is such a project. It [...]]]></description>
			<content:encoded><![CDATA[<p>This is the first in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on <a title="Siphon on GitHub" href="http://github.com/claco/siphon/">GitHub</a>.</p>
<h2><span id="more-157"></span>Introduction</h2>
<p>For me, writing software seems to be about scratching an itch, filling a need by creating your own solution. Siphon is such a project. It seems that almost every project that come across my desk has the need to monitor some directory for files to process. Sometimes that&#8217;s a local directory. Sometimes that&#8217;s a remote ftp directory. Sometimes it&#8217;s even a pop3 mailbox. After watching numerous similar scheduled tasks, batch files, vbscript and command line utilities  being created I decided to create Siphon to help get all of these tasks back into more consistent manageable pieces.</p>
<p>Siphon is a set of tools to allow you to monitor data on a specific schedule and send that data off or processing. There are three basic parts used to accomplish this:</p>
<ol>
<li>Data Monitors: What do you want to monitor?</li>
<li>Monitor Schedules: When do you want to check for new data?</li>
<li>Data Processors: What do you want to do with the new data?</li>
</ol>
<p>These three parts are combined in code or in config files to accomplish the task of monitors and processing new data. These tasks can be configured and run manually using SiphonConsole, and command line utilities for running local/remote monitoring jobs, or SiphonService, a Windows Service that runs jobs based on the schedules supplied.</p>
<h2>Data Monitors</h2>
<p>The following data monitors are currently supported:</p>
<ul>
<li>LocalDirectoryMonitor: Monitor local file system directories for new files.</li>
<li>FtpDirectoryMonitor: Monitor remote ftp directories for new files.</li>
<li>ImapMonitor: Monitor IMAP mail boxes for new messages.</li>
<li>Pop3Monitor: Monitor POP3 mail boxes for new messages.</li>
<li>MessageQueueMonitor: Monitor MSMQ queues for new messages.</li>
<li>DatabaseMonitor: Monitor any DbProviderFactory support database for new records.</li>
</ul>
<p>Data monitors can take separate sets of actions against the data when processing complete/fails. These actions include:</p>
<ul>
<li>Delete: Delete the data when processing completes/fails.</li>
<li>Move:  Move the data when processing completes/fails.</li>
<li>Rename: Rename the data when processing completes/fails.</li>
<li>Update: Update the data when processing completes/fails.</li>
<li>Combined: combine any of these actions together, i.e. Rename then Move.</li>
</ul>
<h2>Monitor Schedules</h2>
<p>The following monitor schedules are currently supported:</p>
<ul>
<li>IntervalSchedule: Run whenever a specified amount of time as passed.</li>
<li>DailySchedule: Run at the same time(s) every day.</li>
</ul>
<h2>Requirements</h2>
<p>In order to run Siphon you must have the following:</p>
<ul>
<li>.NET 3.5</li>
<li>log4Net for logging (Free)</li>
<li>Lumisoft.NET for FTP/IMAP/POP3 protocol support (Free)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/an-introduction-to-siphon/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>So Long Parallels. Hello VirtualBox.</title>
		<link>http://chrislaco.com/software/so-long-parallels-hello-virtualbox/</link>
		<comments>http://chrislaco.com/software/so-long-parallels-hello-virtualbox/#comments</comments>
		<pubDate>Sat, 07 Mar 2009 03:22:33 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[parallels]]></category>
		<category><![CDATA[virtual box]]></category>
		<category><![CDATA[vm]]></category>
		<category><![CDATA[xp]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=152</guid>
		<description><![CDATA[Back when my XP laptop died and I finally bought a MacBook Pro, Parallels was one of my first purchases so I could run multiple VMs to do Perl software testing. At the time, that was Parallels version 3. Everything was snappy and life was good.
Then came the upgrade to Parallels 4. From day 1, [...]]]></description>
			<content:encoded><![CDATA[<p>Back when my XP laptop died and I finally bought a MacBook Pro, Parallels was one of my first purchases so I could run multiple VMs to do Perl software testing. At the time, that was Parallels version 3. Everything was snappy and life was good.</p>
<p>Then came the upgrade to Parallels 4. From day 1, I&#8217;ve had nothing but problems. It was most definitely much slower just to boot XP. Even a fresh install of the software and even an new XP VM fixed nothing. It was still slow. Even more entertaining was that when running XP in VGA mode, Safe Mode, or at the boot options screen, Parallels took up 100% of 1 or both host CPUs. After KB articles, months of patches and support, Parallels tells me that eating up all that CPU is normal. Parallels 3 didn&#8217;t have that problem. No, I won&#8217;t disable all the options that worked in version 3, and yes, 4GB ram was just fine in version 3.</p>
<p>Well, I&#8217;m getting off this train now. Clearly, Parallels 4 has issues, and I&#8217;m not the only one having it. VirtualBox is free, installed and running a fresh new install of XP and Studio 2008 and what do you know, it even runs XP in Safe Mode without eating 100% of the host CPU. Go figure.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/software/so-long-parallels-hello-virtualbox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Brother MFC-490CW Multi Function Printer</title>
		<link>http://chrislaco.com/hardware/brother-mfc-490cw-multi-function-printer/</link>
		<comments>http://chrislaco.com/hardware/brother-mfc-490cw-multi-function-printer/#comments</comments>
		<pubDate>Thu, 19 Feb 2009 05:24:12 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Hardware]]></category>
		<category><![CDATA[brother]]></category>
		<category><![CDATA[fax]]></category>
		<category><![CDATA[inkjet]]></category>
		<category><![CDATA[printer]]></category>
		<category><![CDATA[scanner]]></category>
		<category><![CDATA[wireless]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=149</guid>
		<description><![CDATA[Last year, I bought the wife a new Dell Studio 17&#8243; laptop to replace her dead laptop and her old crusty desktop. New laptops don&#8217;t come with parallel ports any more. This makes my 13 year old faithful HP LaserJet 6P very lonely. After 13 years, I&#8217;ve only had to replace the toner cartridge once. [...]]]></description>
			<content:encoded><![CDATA[<p>Last year, I bought the wife a new Dell Studio 17&#8243; laptop to replace her dead laptop and her old crusty desktop. New laptops don&#8217;t come with parallel ports any more. This makes my 13 year old faithful HP LaserJet 6P very lonely. After 13 years, I&#8217;ve only had to replace the toner cartridge once. It&#8217;s been  workhorse.<br />
<span id="more-149"></span><br />
Rather than spending money on trying to get a USB to Parallel adapter to work or trying to dig up an ethernet print server, I opted to purchase a new printer. A co worker invested in a Brother MFC-490CW and loved it. He hates everything, but loved this printer. Good enough for him; good enought for me.</p>
<p><a href="http://chrislaco.com/wp-content/uploads/2009/02/mfc-490cw.png"><img src="http://chrislaco.com/wp-content/uploads/2009/02/mfc-490cw-150x92.png" alt="MFC-490CW" title="MFC-490CW" width="150" height="92" class="aligncenter size-thumbnail wp-image-148" /></a></p>
<p>It&#8217;s wireless. I can put it anywhere. Any computer in the house can print to it. Any computer can send faxes through it. It can also scan/copy documents and images and send the results to any computer that is online.</p>
<p>Even better, the ink cartridges are cheap. Less than $10. Worth every penny and the wife is happy. I&#8217;m still a little sad that the LaserJet will be retired.</p>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/hardware/brother-mfc-490cw-multi-function-printer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
