Using IronPython to configure Castle Windsor III

In the first two articles I introduced Pysor, the Castle Windsor configuration tool using IronPython. Now I have added some exciting functions to exploit the nice hash table and list syntax feature in IronPython.

Since the second part of this series is possible to add (named) parameters to component registration. It accepted only both literal and referential scalars. In the current revision you can exploit the nice list syntax of Python using the square brackets to add arrays and list parameters.

A parameter is a string literal or a reference to an already registered service. This value is supplied either to a constructor parameter with the same name or a property.

A example used in the last part was to provide AdditionalMessage property like this:

add( "retriverWithParam", HtmlTitleRetriever, HtmlTitleRetriever, 
	{'AdditionalMessage': "Test"})

Now suppose that we have a class that accepts an array of strings in the constructor.  We could provide them using the Python list syntax

add( "MessageStorage" , MessageStorage, MessageStorage, 
	{'messages':['first message', 'second message' ]})

This syntax works not only for array but also for ILists.

Things get more interesting when you want to add an array of registered services. The method add returns a hook to the service. You can use this hook to reference the service in the parameters :

ftp = add( "ftp", FtpFileDownloader, FtpFileDownloader)

add( "MultipleDowloaderStorage", MultipleDowloaderStorage,
	MultipleDowloaderStorage, {'dowloaders' : [ ftp] })

 

Comparison

In this section I will compare the same configuration using traditional xml syntax and the Pysor syntax. I am sure not every body would prefer Pysor. I am pretty comfortable with xml, yet I don’t like it. I will choose Pysor anyway. In the next section I will make a small project and use both configuration mechanisms and let you find by yourself, which solution is more elegant and appropriate for you.

Not breaking the tradition of almost all IoC tutorials, we will need a logging service to be located dynamically. Additionally we have two implementations: ConsoleLog  and FileLog. FileLog expect a file name in its constructor..

public interface ILog
{
    void Log(string message);
}

public class FileLog : ILog
{
    private readonly string _fileName;
    public FileLog(string fileName)
    {
        _fileName = fileName;
    }

    public void Log(string message)
    {
        Console.WriteLine(_fileName+ " -> " + message);
    }
}

public class ConsoleLog : ILog
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

In additional we have an IAlgorithm interface with following members:

public interface IAlgorithm
{
    event EventHandler OnOperationDone;
    void Run();
}

Algorithm is a class implementing this interface. AlglorithmRunnner is a class that takes an algorithm with an array of loggers, starts the algorithm and notify all loggers when an event is fired.

Configuring this setup of dependencies in App.config would look much like:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="castle" type=
"Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>
  </configSections>

  <castle>
    <components>
      <component id="fileLog"
                 type="CastleComparison.FileLog, CastleComparison"
                 service="CastleComparison.ILog, CastleComparison">
        <parameters>
          <fileName>log.txt</fileName>
        </parameters>
      </component>

      <component id="consoleLog"
                 type="CastleComparison.ConsoleLog, CastleComparison"
                 service="CastleComparison.ILog, CastleComparison">
      </component>

      <component id="algorithm"
                 type="CastleComparison.Algorithm, CastleComparison"
                 service="CastleComparison.IAlgorithm, CastleComparison">
      </component>

      <component id="runner"
                 type="CastleComparison.AlgorithmRunner, CastleComparison">
        <parameters>
          <loggers>
            <array>
              <item>${consoleLog}</item>
              <item>${fileLog}</item>
            </array>
          </loggers>
        </parameters>
      </component>

    </components>
  </castle>
</configuration>

On the other hand, configuring it with Pysor is as simple as

clr.AddReference("CastleComparison")
from CastleComparison import *

fileLog = add("fileLog", ILog, FileLog, 
	{"fileName":"log.txt"})
consoleLog = add("consoleLog", ILog, ConsoleLog)

add("algorithm ", IAlgorithm , Algorithm )

add("runner", AlgorithmRunner, AlgorithmRunner, 
	{"loggers": [consoleLog, fileLog]})

Clearly, I will always choose the second configuration.

To-do's

The updated to-do list is now

  • Adding a nicer API for referencing assemblies and importing namespaces (I have now idea how to do it).
  • Adding parameters to be passed to the constructor.
  • Passing parameters as lists or arrays.
  • Dictionary based parameters
  • Referencing already registered implementation inside the same configuration script.
  • Documenting and signing the assembly
  • Lifestyle management
  • Considering turning Pysor into an Interpreter to be used as the built-in XmlInterpreter

Source Code

Please don’t forget to download the code from GitHub and try it yourself.

All remarks , ideas, bug reports, etc. will be appreciated.

September 5, 2009 |
Tags : C# Castle DSL IoC IronPython Programming Windsor

About Me

Moukarram Kabbash This blog is kept alive by me, Moukarram Kabbash, a programmer, hobby photographer from Dortmund in Germany.

mouk.github.com