Ayende @ Rahien

Refunds available at head office

Thinking about WinFS

I'm currently watching the Channel 9 video about WinFS, and I'm using this post to keep track of the questions & thoughts I've while seeing it:

  • One big one is the ability to delete a file while someone is holding it open. NTFS doesn't allow it, but most of the Linux file system does. The issue here is the ability to upgrade core components without rebooting. If WinFS would make it possible to delete a file while some program is using it, the reasons for reboots would suddenly drop sharply.
  • What happen if I've a relationship between files and I copy the set of file to a non-winfs medium (ftp, zip, cd, etc) and then I copy it back to a winfs medium, what happen to the relationships, the meta data?
    From the video it looks like it should work.
Tags:

Published at

I'm a Castle Commiter!

I've been voted as a commiter for the Castle Project. You might recall how excited how I was when a patch I made was accepted for Catle, so you can imagine how I feel right now about it. :-D

Along with this there is another big issue: Brail has been accepted into Castle's MonoRail and will now be available as part of their services. So I would finally have a public Subversion server that I can point people to.

I'm going to update everything in the site and upload the repository tomorrow. I want to add some more usability features. Specifically, it's the Brail pre processor that worries me. It's too strict. I'm not sure how to solve this problem, but I'll give it some thought.

Tags:

Published at

Brail - How it works?

I just added an article that explains how Brail works behind the scenes. Read it here.
Tags:

Published at

Spelling mistakes

This is embarrassing, I just realized that I'd a spelling mistake in NHibernate Query Analyzer on my site for at least four months.

I fixed it, but this also means fixing the URL :-(

I updated most of the links on my sites, but be sure to fix any links to it if you have any.

The links are:

http://www.ayende.com/projects/nhibernate-query-analyzer.aspx - Project page.

http://www.ayende.com/projects/nhibernate-query-analyzer/flash-demo.aspx - The demo.

http://www.ayende.com/projects/nhibernate-query-analyzer/downloads.aspx - Download page

NHibernate Query Analyzer update

I updated NQA today with some bug fixes. Spesifically, it's solved Bug #48 (System.ArgumentException: 'null' is not a valid value for 'stream'.) and preserving stability in the face of some really funky queries.

The downloads are here.

What is making that noise?

Every one in a while, my computer would chime at me. While I like the sound, I would also like to know what it means. I ruled out all the obvious suspects, IM & Outlook.

Is there something like FileMon for sound?

MSN vs. Google

A few days ago I noticed a sudden spike in the traffic to the site, attributed to MSN Bot going crazy and hitting my site thousands of times. It's still in the range of hundreds right now. I know that others have noticed the same.

What is interesting is that it seems that I can now find things on MSN that I can't on Google.

Tags:

Published at

Book Review: Working Effectively With Legacy Code

Working Effectively with Legacy Code is a book that should be a mandatory reading for anyone who is interested in coding for a living. I don't deal with many legacy systems at the present (except for NQA, of course :-) ) and still this book has been a fountain of knowledge about techniques, tips and tricks.

This is not a book about pretty code, it's a book about ugly code and all the myrad ways you can abuse design pricipals and good coding practices. What it does is show a set to systematically take a legacy code* and break down the dependencies so you can write tests for the code. The moment you've tests, you can move forward. Until you've tests, you're in a position where all you can do is "Edit & Pray" or... you can write tests.

I've had recent experiance with developing applications in a TDD from the start (Rhino Mocks) and converting a legacy application to TDD (NQA). I can't rave enough about how much easier it is to make a change in Rhino Mocks because of those tests. When I adapted Rhino Mocks to run on .Net 2.0 I'd broken just about every part of the application because I didn't consider what the change from Hashtable to Dictionary<T> would do when there is no item in the dictionary. Because I'd tests, it was literaly the work of a minute to find the problem and fix it.

One thing to note about this book, it took me over two months to read it. Like Xp Explained 2nd Ed. I just can't seem to read it for any length of time without starting to feel a sudden need to code something. Very productive for my projects, less so for my reading backlog.

* Legacy Code == Code without tests.

Tags:

Published at

Seems that I learned soemthing after all

Did you ever had a problem that you simply couldn't solve? I had one such problem 8 months ago, I wanted some way to skip the somewhat tedious log4net logging approach:

if (logger.IsDebugEnabled)
    logger.Debug("message with ""expansive" +" " + "string concentration");

This is the recommended approach from the log4net documentation, and it makes sense, this way, if you don't enable logging, you don't pay for any string concentration. But it's very tedious to do so over and over again. I really wanted a clean way to do it, but at the time Boo's macros were far beyond my capabilities. Even for such a simple task.

Fast forward to the present....

Now I have no difficulities writing this, in fact, the entire utility was developed in one session in Notepad (the way real programmers do it :-) ). What is interesting is that in the meantime I had no education in compilers, AST, etc. I just came back to the subject and it suddenly made sense. It happened to me before in many situations, but this is the first that I can actually find a documented proof of that.

I remember in high school I was unabled to grasp the idea of dynamic memory (on my defense, I didn't try very hard), that was on Pascal with ^ going all over the place. Six months later, I was a triple star programmer (and proud of it :-) ) in C. Again, nothing much has changed, except maybe that this time I was more willing to learn.

Anyway, here is the code, less than 60 lines of code, and I found it useful in reducing common repetitive statements.

   
  """

  This macros allows a convenient way to log properly, without getting tired by writing logger.IsXXXEnabled all the time.
  The macros depend on a ILog field/local named logger being present, this you've to define yourself.

  """
  namespace log4net
 
  import System
  import Boo.Lang.Compiler
  import Boo.Lang.Compiler.Ast
      
  class LogdbgMacro(AbstractAstMacro):
  """
  Logs a debug message if debug messages logging are enabled.
  Usage: logdbg "message" [exception]
  """
      override def Expand(macro as MacroStatement):
          return GenerateStatement("Debug"macro)
 
  class LogwarnMacro(AbstractAstMacro):
  """
  Logs a warning message if warning messages are enabled enabled.
  Usage: logwarn "warning" [exception]
  """
      override def Expand(macro as MacroStatement):
          return GenerateStatement("Warn"macro)
 
  class LogerrorMacro(AbstractAstMacro):
  """
  Logs an error message if error logging is enabled
  Usage: logerror "error" [exception]
  """
      override def Expand(macro as MacroStatement):
          return GenerateStatement("Error"macro)
 
  class LogfatalMacro(AbstractAstMacro):
  """
  Logs a fatal error message if fatal error logging is enabled
  Usage: logfatal "fatal error" [exception]
  """
      override def Expand(macro as MacroStatement):
          return GenerateStatement("Fatal"macro)
 
  class LogInfoMacro(AbstractAstMacro):
  """
  Logs an informative message if informative messages logging is enabled.
  Usage: loginfo "didja know?" [exception]
  """
      override def Expand(macro as MacroStatement):
          return GenerateStatement("Info"macro)
 
  internal def GenerateStatement(logOption as stringmacro as MacroStatement):
      if len(macro.Arguments) < or len(macro.Arguments) > :
          raise CompilerError(macro.LexicalInfo"The log can be called with either one or two parameters only!")
      isEnabledProp MethodInvocationExpressionTarget: AstUtil.CreateReferenceExpression("logger.get_Is${logOption}Enabled") )
      ifEnabled IfStatement(Condition: isEnabledPropTrueBlock: Block() )
      mie MethodInvocationExpression(Target: AstUtil.CreateReferenceExpression("logger.${logOption}") )
      mie.Arguments.Add(macro.Arguments[0])
      mie.Arguments.Add(macro.Arguments[1]) if len(macro.Arguments)==2
      ifEnabled.TrueBlock.Add(mie)
      return ifEnabled

Brail 1.2.1

A new release for a new day :-)

Brail will now notice if you change anything in the Common Scripts folder and will recompile the views so you no longer has to restart the application to test a change in Common Scripts.

Brail can now serve a request entirely from memory (previously it touched the disk at every request to verify that the file existed and wasn’t changed).

The last should be a significant perofrmance improvement, but I'm not saying anything for cetain until I'll have some performance data.

Download here.
 

Tags:

Published at

Suse Linux Eval?

I want to play a little with Mono on Linux, so I went to Suse homepage to get the latest distribution, and I see there an Eval version and an FTP version. The only difference that I can see between the two is that there the eval version has only 1,000 packages with it and the FTP version has 3,500.

What is the story here?

Tags:

Published at

Downloads Change

I got a lot of complaints about corrupted files from my server. I always managed to get the files and verify that they were fine, but I kept getting emails from people saying that there is a problem. I recently started using the Cuyahoga download module and I suspect that this is the case. Cracking open the code and looking at what is going on there didn't give a clue. It seemed to be doing all the right things.

There are two reasons for using a download module:

  • You want to know the number of times a file was downloaded.
  • You don't want people to be able to direct download the file.

I'm using the download module for the first reason, I don't really care about the second one. So I just changed the implementation to use Response.Redirect(), which should cause a normal download. I hope this will fix the issue. Cuyahoga is a pleasure to work with, the changes I needed to make were localized and very easy to discover. I'm certainly happy that I'm using it.

Tags:

Published at

Blog Transformation Done!

I found out what the problem was, dasBlog uses the "Max. Days in Main RSS Feed" for the various API as well. I just changed that value to 1000, run the script again, and then watched how my entire category system reorganize itself. Total run time for the script was 40 minutes. Add to that some more time considering how to solve the problem, learning how to use Xml-Rpc.Net, checking dasBlog source to see why I can't get all the posts, etc and that is ~2 hours.

If I would've tried doing this by hand I would still be hard at work :-)

I also changed the layout ofthe blog page a little, since the categories aren't nearly as long anymore, I pushed them up a bit.

Tags:

Published at

dasBlog and hierarchal categories

Did you know that dasBlog has a hierarchal categories with a beautiful way to represent them? I didn't until I visited Oren Ellenbogen's blog and saw how his categories looked. My immediate response was "Me Too! Me Too!", so I set out to learn the secret.

Turns out that this isn't such a big secret after all: Oren* told me that it's simply a matter of seperating the categories with '|'. There is also some css magic that goes along with it, but looking at Oren's site with the WebDeveloper Extention for Firefox quickly showed what I needed. (#1 tool for anyone who wants to develop for the web, in my opinion).

I changed a single post category, and it worked! Now I wanted to change all the rest of them. Hierarchal categories are the new thing, I hear, so I certainly wanted them for my blog. Ever for the old posts. The trouble is that I currently has over 600 posts. (When did I manage to post so much?) And I wasn't feeling like spending the rest of the weekend pouring over each and every one of them changing the categories. I immediately setup to find a programmatic solution to the problem.

The first thing I thought about was that dsaBlog has a programming interface using the Blogger API, but I never worked with it. I googled for a little bit and found the Xml-Rpc.Net library, which incidently comes with a blogger API sample. But apperantly the Blogger API doesn't let you update the categories of a single post, at least not in any way that a very curosry will show. The MetaWeblog API does however, so I used that. I compiled newtelligence.DasBlog.Runtime.Proxies\BloggerAPIClientProxy.cs to an assembly I named dasBlogger.dll (I could've also used the dasBlog assemblies, but it would've taken time to build them, so I just compiled it seperatedly).

I tested it and it worked! Then I had the really hard part, thinking about how to map my categories to hierarchal structure. Here is the code I ended up with, very simple to write and understand. But it doesn't work.  The problem is that dasBlog only return the last 30 posts, no matter what value you pass to it. I'm going to email Scott about it. Pursuing the source for dasBlog, I didn't see where this functionality is controled.

import System
import CookComputing.XmlRpc
import newtelligence.DasBlog.Runtime.Proxies

user = "Ayende Rahien"
password = "Ayende Rahien's Password"

categories = {
"Ayende.Utilities" : "Programming | Utilities",
"Blog" : "Website | Blog",
"Boo" : "Programming | Boo",
"Boo On Rails" : "My Projects | Brail",
"Books" : "Culture | Books",
"Brail" : "My Projects | Brail",
"Bugs" : "Programming | Bugs",
"dasBlog" : "Website | Blog | dasBlog",
"Evil" : "Miscellaneous | Evil",
"eXtreme Programming" : "Programming | XP",
"Humor" : "Humor",
"Miscellaneous" : "Miscellaneous",
"Movies" : "Culture | Movies",
"Music" : "Culture | Music",
"NHibernate Query Analyzer" : "My Projects | NHibernate Query Analyzer",
"Programming" : "Programming",
"Rhino Mocks" : "My Projects | Rhino Mocks",
"Website" : "Website",
"Windows" : "Programming",
"Writings" : "Culture | Writings"
}
log = IO.File.CreateText("log.txt")
dasBlog = BloggerAPIClientProxy()
dasBlog.Url = "http://www.ayende.com/Blog/blogger.aspx"
posts = dasBlog.metaweblog_getRecentPosts("",user, password, 999)
start = DateTime.Now
for post in posts:
try:
for i in range( len (post.categories) ):
newCategory = categories[ post.categories[i] ]
if newCategory is null:
print "'${post.categories[i]}' is an unknown category"
continue
post.categories[i] = newCategory
if len(post.categories) == 0:
post.categories = ("Uncategorized",)
dasBlog.metaweblog_editPost(post.postid, user, password, post, true)
print "Updated post: ${post.title}"
except e:
print "Error updated post: ${post.title}"
print e.Message
log.WriteLine(e.ToString())
log.WriteLine("--- --- ---")
print "Took ${DateTime.Now - start}"

 * Last time I checked there were more than 8,500 people in Israel named Oren, and 7 named Oren Eini (none of them is related to me, btw).

Tags:

Published at

Funny quote

I just read this post; it's a review about an apperantly bad magazine* which contain this quote:

 For all this wonderful material, you'll never guess the price tag. Come on, guess it. I thought so. It's 69 silver ones an issue!! I can get a book for that kind of dough. With thoughts in it.

I just made me burst out laughing, but I seem to do it a lot today.

Tags:

Published at

Hebrew & Programming

I'm reading some programming forums in Hebrew right now. And I can't help but cringe at the syntax that I see there. Hebrew is writen right to left (as is only proper), while code and the most common terms are written left to right.

The result is that there is a pretty ugly mesh of right to left & left to right text, add in some more terms that was translated to Hebrew and you'll start to get a headache just reading a single question.

The Hidden Parameter

I have a problem doing AST manipulation at run time, sometimes the code work, and sometimes it doesn't. I know what cause the error, and I can fix it for one scenario, but then the other scenario is borken. I'm pretty sure that I'm doing something nasty, but I don't know what. I'm trying to build a method and reference its arguments, but I keep getting errors when I try to test this.

I'm actually reading raw IL (at least it's better than ASMx86), trying to figure out some things that aren't going well. The problem I've have is with parameter indexing. I've a piece of code that generate other code, and it works if I'm passing it a method with 3 parameters, but not if I pass a method with 4 parameters. Actually, that is not a good explanation, I generate a method (at compile time, using the Boo Compiler AST) based on another method. I add two parameters to this method. The problem is what to do when I'm referencing those parameters. Since the IL reference them by index (ldarg.1, ldarg.s, etc) and not by name (that is for us humans), I'm getting problems about null reference methods on some cases (but only some). The logic I'm using is

Found it!

I started to write this question when I suddenly saw the difference between the two cases, there is something in the asking that makes me suddenly view the problem in a different light. As you can see above, I first thought that this is a problem with the number of the parameters a method has, but the issue was different altogther. One of the methods was static, the other an instance method.

First, some background, what is the difference between an instance and a static method? A static method can't use this, can't access non static resources, etc. It took me so long to figure this out because I didn't notice that one method was static and the other wasn't. An instance method actually get this as a hidden parameter. The practical meaning of this is that for instance methods parameter indexing starts with 1, while parameter indexing for static methods starts with 0. That was the reason for the problem, and after adding three lines, the code is working for both conditions.

Thinking about it, the second I realized that the difference between the method wasn't the number of parameters but the static/instance issue, it was obvious what was causing the problem. (Reading a lot of the IL code didn't hurt, either :-).) I guess I should be thankful that I've experiance in C++, otherwise I would likely still be scratching my head.

Tags:

Published at

Brail 1.2

A new release of Brail, this time featuring:

  • Calling subviews with parameters:
    OutputSubView(“something”, {“val”:value, “one”:other} )
  • Output methods (temp name) for:
    [Html]
    def outputEncodedHtml():
        return “something < or > the other”

    Supported attributes right now is Html / MarkDown / Row.

Some cool stuff there to make the framework more flexible. Check it out here

Tags:

Published at

Environment Breakage

I just had about half of my tests break for no conceivable reason, this cause much head scratching and panic I figure out that I switch NUnit versions (the one that I used was compiled for 1.1 apperantly).

I've no idea why switching 2.2.2 with 2.3 (the one that came with TestDriven 1.1) would cause this breakage. The issue is a lot of tests failing because Cassini fails to run.

Rhino Mocks 2.4 - More on DRY

As I said, I update the minor version number every time I add a big feature, this time it's all about DRY and .Net 2.0. From the start Rhino Mocks was capable of being used on the 2.0 framework, but its home was the 1.1. That caused some pain to me every single time that I had to write something like:

IDemo demo = (IDemo) mocks.CreateMock(typeof(IDemo));

Just count the number of times that I repeat the type of the mock object here, three times! This is stupid and it violate the DRY principal in a fairly big way. Unfortantely, there is no good way to resovle this issue on the 1.1 framework, but there is using the 2.0. Generics! {Excuse me while I sing the joy of no more custom collections}.

The goal was to reach this:

IDemo demo = mocks.CreateMock<IDemo>();

The first thing to do was to test Rhino Mocks in mocking a generic interface:

[Test]

public void MockAGenericInterface()

{

    using (MockRepository mocks = new MockRepository())

    {

        IList<int> list = mocks.CreateMock < IList<int> >();

        Assert.IsNotNull(list);

        Expect.Call(list.Count).Return(5);

        mocks.ReplayAll();

        Assert.AreEqual(5, list.Count);

    }

}

I run this test, and it passed! Did I mention that I dig DynamicProxy yet? I then started to clean up some warning about obsolete interfaces (no one wants to hear that their code is obsolete, right?) which I managed to do cleanly and easily, then I moved to take care of the collections. I've mentioned in the past that I've had problems with custom collections becoming too big (40% of the application code), so I was very eager to use them.

I had a problem with replacing a hash table with a Dictionary<T> because of the KeyNotFoundException, which I was aware of, but didn't really pay attention to. The tests made it really easy to pin point the problem (although 80+ failing tests did make my heart go THUD!), which was very easy to fix (and I was using the indexer in just one place, strange).

That done, I change MockRepository to a partial class, and put all the generic methods in a seperate file (I really like this, btw). That was merely a matter of adding a couple of T and typeof(T) in the right places and I was done. A couple of tests and I was done, .Net 2.0 & generics support!

One thing to note:

Rhino Mocks remains compatible with the 1.1 framework and will continue to do so for the foreseeable future!

Users of the 2.0 framework version would get a mocking framework that allows them to use generics to specify the mock objects, that internally uses the System.Collections.Generics and that is all.

The downloads are at the usual place and the API Documentation has been updated. I've yet to update the documentation itself, but I'll do it later today, I hope.

Tags:

Published at

Using ILMerge on .Net 2.0 Beta 2

I've some assemblies that I want to merge to one big assembly. I used to do it for .Net 1.1 with ILMerge, but now it fails since I'm compiling for the 2.0 framework.

The first thing that I tried was to download the latest bits (from three days ago, actually). That still didn't work, so I tried to use runtime binding, which would cause ILMerge to run on the .Net 2.0 platform. That worked!

Here is the binding that I used:

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v2.0.50215" />       
    </startup>
</configuration>

Just to note, any assemblies produced by ILMerge after this change will be a 2.0 assemblies, even if the originating assemblies were 1.0 or 1.1. I'm sure that there are other, arguably better, ways to solve this, but I simply saved the above file as ILMerge2.exe.config and copied the ILMerge.exe to ILMerge2.exe; now I can just call ilmerge2 when I want to merge files to a 2.0 assemby, and ilmerge when I want to merge to a 1.1 assembly.

Tags:

Published at

Partial classes question

Given this class declaration:

File1:

public partial class MocksRepository : IDisposable
{
   //implement disposable

}

 File2:

public partial class MocksRepository : IDisposable
{

}

Why do I get an error saying:

Error 1 'Rhino.Mocks.MocksRepository' does not implement interface member 'System.IDisposable.Dispose()' D:\Code\rhino-mocks\src\Rhino.Mocks\MocksRepositoryGenerics.cs 12 26 Rhino.Mocks 2.0