Ayende @ Rahien

Refunds available at head office

Castle Demo App: Complex Interactions

Castle Demo App: Complex Interactions

We have CRUD operations for users and projects. The next step is to add a way to assign / remove users from a project. We can do it both for users and projects, but this is just overly complex. We're going to allow a project edit page to add or remove users, and that is all.

I'll be the first to admit that I'm not very good with JavaScript, and I've practically no knowledge in the Ajax capabilities of MonoRail (which are based on Prototype library). I'm just going to stubmle around in this post, and see if I can make something work. Do ignore any screaming that you may hear in the background, this is just me hating browsers :-)

The reason that I'm so late with this post is that I run into a couple of problem with Ajax that weren't easy to solve (or even find). Just to put things in perspective, I wrote the code for this post in an hour and a half, and that included looking at MonoRail source, finding a bug and fixing it. In contrast, it took me much longer to write / debug / fix the problems in the java script parts. I think that this is mostly my fault, though, for not knowing the DOM and the environment well enough. Anyway, enough chit-chat, we've an application to write!

We start by adding this snipper to the head of Views\Layout\Admin.boo:

<head>

${AjaxHelper.GetJavascriptFunctions()}

<title>Administrator Section</title>

</head>

This simply add the correct javascript refeferences (<script> tag) so we could use the ProtoType functions. What I'm going to do next involve a bit of Ajax and some understanding of the tools that MonoRail gives me. I'll explain everything in a couple of minutes, but for now, here is the new EditProject method:

    public void EditProject([ARFetch("id")]Project project)

    {

        AddAllUsersToPropertyBag();

        ValidateObjectToSendToView("project", "Projects", project);

        User[] allUsers = User.FindAll();

        PropertyBag["assignedUsers"] = new User[0];

        PropertyBag["unassignedUsers"] = allUsers;

        if (project == null)

            return;

        ICollection<User> assignedUsers = project.Users;

        User[] unassignedUsers = Array.FindAll(allUsers, delegate(User user)

        {

            return assignedUsers.Contains(user) == false;

        });

        PropertyBag["assignedUsers"] = assignedUsers;

        PropertyBag["unassignedUsers"] = unassignedUsers;

    }

This requires some explanation, I believe. The first two lines are the same as before, but the rest of it is all new. The idea is to pull all the users from the database, and then filter them based on those not already assigned to the project. We then pass the assigned and unassigned users to the view. The two lines just after User.FindAll() are there because we need to give the view something to display if this is a brand new project.

Now, let's see what we need to do to the view in order to make it support our brand new interface, here we have quite a bit of word, as a matter of fact. Because I made so many changes, I'm going to show each part of the page and then provide explanation on what it does. It's a lot of code, but it is not complicated one:

<?brail

def outputUsersList(usersList, addEnabled, removeEnabled):

     index = 0

     addStyle = ""

     removeStyle =""

     addStyle = 'display:none;' if addEnabled == false

     removeStyle = 'display:none;' if removeEnabled == false

    

     for user in usersList:

           userClient = "user_id[${user.Id}]"

           moveToAssigned = "addToProject($('${userClient}'))"

           moveToUnassigned = "removeFromProject($('${userClient}'))"

           output "<tr id=${userClient} userId='${user.Id}'><td>"

           output AjaxHelper.LinkToFunction('&lt;&lt;', moveToAssigned, {'id':userClient+'.addToProject', 'style':addStyle})

           output "</td><td>${user.Name}</td><td>"

           output AjaxHelper.LinkToFunction('&gt;&gt;', moveToUnassigned, {'id': userClient+'.removeFromProject', 'style':removeStyle})

           output "</td></tr>"

           index++

     end

end

 

name = ""

defaultAssigneeId = -1

id = "0"

if ?project:

   name = project.Name

   defaultAssigneeId = project.DefaultAssignee.Id

   id = project.Id.ToString()

end

 

?>

Notice the outputUsersList() method, this one is one of the reasons I love Brail, it allows me to do Extract Method refactoring for HTML. What this method does is very simple, it gets a list of users, and two flags telling it whatever to show or hide the add / remove functionality. You can see a couple of things here, we are taking advantage of the fact that Boo (which is the base langauge for Brail) has strong inference capabilties, so we don't bother with specifying types, in fact, this method declaration looks very much like the declaration we will see in a moment, in the java script code. (Although I find Boo much easier to work with than Javascript, of course.)

We output some HTML to the client, which isn't very interesting (and vaguely resembles CGI code), but we have a call there to AjaxHelper, which I'm going to assume that you're interested at. The method we use is LinkToFunction(), which just means that MonoRail generates a link for us, and will call a (local) java script function when the link is clicked. The first parameter is the text of the link, the second is the function to call (including its parameters), the third is used to pass a dictionary of attributes for the link (and one of them is display=none, which is used to hide the link if it's not needed.

One thing to notice here is the way I'm using the javascript. First, "$()" is a function call that I get from Prototype, it'll get me the element that I need without needing to do stuff like document.getElementById(), etc. Second, I'm passing this function the id for whole <tr> element. We'll see how it makes our life easier in a couple of minutes, since it means that I can just move stuff around without worrying how it looks. Third, notice that I'm naming all the links the same way, the container id plus "addToProject" or "removeFromProject", this will allow me to get the correct link easily, without traversing the DOM to find the right one. Because I know the user id is unique, I know that both the <tr> id and the links ids will be unique in the page. One other thing that I am doing here is to add a userId attribute to the <tr> element, I'll be using this attribute in the Ajax code, to get the user I'm working on.

I think that the parts after the method require no explanation, we've already seen them before. Before I will show the Javascript, we need to take a look at the page, and see how it's laid out, otherwise you wouldn't be able to make head or tails from the scripts, so here it is:

${HtmlHelper.Form('SaveProject.rails')}

${HtmlHelper.FieldSet('Project Details:')}

${HtmlHelper.InputHidden('project.id', id)}

<table>

     <tr>

           <td>

                ${HtmlHelper.LabelFor('project.Name','Project name:')}

           </td>

           <td>

                ${HtmlHelper.InputText('project.Name',name, 50, 50)}

           </td>

     <tr>

     <tr>

           <td>

      ${HtmlHelper.LabelFor('project.DefaultAssignee.Id','Default Assignee:')}

    </td>

    <td>

           ${HtmlHelper.Select('project.DefaultAssignee.Id')}

           ${HtmlHelper.CreateOptions(allUsers, 'Name','Id', defaultAssigneeId)}}

           ${HtmlHelper.EndSelect()}

    </td>

     </tr>

</table>

<p>Users Assignments:</p>

<div  id='assignedUsersInputs'>

<?brail for user in assignedUsers: ?>

     ${HtmlHelper.InputHidden('project.Users', user.Id)}

<?brail end     ?>        

 </div>

<table>

  <tr>

    <th style="BORDER-BOTTOM: #f96 1px dashed; BORDER-RIGHT: #f96 1px dashed">Users assigned to this project:</th>

    <th style="BORDER-BOTTOM: #f56 1px dashed; BORDER-LEFT: #f56 1px dashed">Users not assigned to this project:</th>

  </tr>

  <tr>

    <td style="BORDER: #f96 1px dotted">

      <table id="assignedUsersTable">

        <tbody>

        <?brail

                outputUsersList(assignedUsers, false, true)

        ?>

        </tbody>

      </table>

    </td>

    <td style="BORDER: #f56 1px dotted">

      <table id="unassignedUsersTable">

           <tbody>

           <?brail

                outputUsersList(unassignedUsers, true,  false)

           ?>

           </tbody>

      </table>

    </td>

  </tr>

</table>

<p>

${HtmlHelper.SubmitButton('Save')}

</p>

${HtmlHelper.EndFieldSet()}

${HtmlHelper.EndForm()}

The first part isn't interesting, since we already seen it before, what we will look at is everything below the "Users Assignments"  paragraph. We have the assignedUsersDiv, where we put all the users assigned to this project in hidden inputs. Notice that all those hidden input fields are named the same: "project.Users", this will become important when we'll save the project.

We then define a table, and use the method outputUsersList() to output the correct list to the page. If we would run the page now, we'll see something like this:

I am not a designer, and the HTML above has some hardocded values instead of using CSS, but I'm not doing this series to show you how to build pretty sites. :-) Do you see the >> and << signs near the users? This will allow us to simply move a user to or from the project.

Assume for a moment that this page was written in ASP.Net, and that a post-back is required to move a user on/off the project. I now want to add three users and take one away, how much time will this take me? About a minute or two, assuming that the connection and server are fast. And that is for doing no processing whatsoever excpet move a text box from one spot to the other.

The way we are doing this is much more user friendly (at the cost of being much less developer friendly, but that is the usual balance). Okay, we've seen the HTML code and the way it looks like, now let's move on to everyone's favoriate buzzward, Ajax:

<script lang="javascript">

     function addToProject(userTag)

     {

           Element.remove(userTag);

           $('assignedUsersTable').tBodies[0].appendChild(userTag);

           toggleLinks(userTag);

           var i =0;

           var hidden = document.createElement('input')

           hidden.type='hidden';

           hidden.value = userTag.getAttribute('userId');

           hidden.id = hidden.name = 'project.Users';   

           $('assignedUsersInputs').appendChild(hidden);

     }

     function removeFromProject(userTag)

     {

           Element.remove(userTag);

           $('unassignedUsersTable').tBodies[0].appendChild(userTag);

           toggleLinks(userTag);

           for(var node = $('assignedUsersInputs').firstChild;node !=null; node = node.nextSibling)

           {

                if(node.nodeType != 1) //input

                     continue;

                if(node.value == userTag.getAttribute('userId'))

                {

                     Element.remove(node);

                     break;

                }

           }

     }

     function toggleLinks(userTag)

     {

           Element.toggle($(userTag.id +'.addToProject'),

                $(userTag.id +'.removeFromProject'));

     }

</script>

We'll start with the simplest function, toggleLinks(), it takes a single element, and using its id, it find the related addToProject and removeFromProject links, and toggle their visibility. The Element class is part of Prototype, and provides many useful methods.

The addToProject() function will remove the element it gets from the page and add it to the assignedUsersTable and switch the links state. Then it creates a hidden input field and stores it in the originally named assignedUsersInputs <div>. This <div> is used merely as a place holder in the DOM for all the hidden fields. One super important thing to remember. An input field you create with javascript must have a name, otherwise it won't be sent to the server! I lost more than a little bit of time over this. (Thanks, Jeff!)

We're setting all the input fields to use the same name and id. The id I don't care much about, but it's important that they'll all have the same name, since we'll use that in our controller code.

The removeFromProject() function does pretty much the same thing, physically move the element from one container to another, toggle the links and then search through the children of assignedUsersInputs for the hidden field that match the user, so it can remove it.

Okay. This is it. We're done with all the client code! I'm thinking about throwing a party just for that...

Now to the SaveProject() method, which should now handle saving the assigned users for this project. How hard do you think this will be? Let's find out:

    public void SaveProject(
        [ARDataBind("project", AutoLoadUnlessKeyIs = 0)]Project project,

        [ARFetch("project.DefaultAssignee.Id",
                       Create = false, Required = true)] User defaultAssigneee,

        [ARFetch("project.Users")]User[] assignedUsers)

    {

        RenderMessage("Back to Projects", "Projects");

        try

        {

            if (project.DefaultAssignee == null)

                project.DefaultAssignee = defaultAssigneee;

            //Remove all assigned users from the project, since the autority

            //on who is assigned to the project is now the assigned users in the page.

            project.Users.Clear();

            Array.ForEach(assignedUsers,

                delegate(User users) { project.Users.Add(users); });

            project.Save();

            Flash["good"] = string.Format("Project {0} was saved successfully.", project.Name);

        }

        catch (Exception e)

        {

            Flash["DataBindErrors"] = project.ValidationErrorMessages;

            Flash["bad"] = e.Message;

            Flash["Debug"] = e.ToString();

        }

    }

That is a lot of attributes, I head you say, and I agree. But it's better than saying, that is a lot of code...  Those attirbutes saves quite a bit of time, and if you don't like them, you can access their functionality without losing anything. Personally, I like this ability, but I know that opinions about this are divergent.

What do we have here, then? We have the project and defaultAssignee parameters, which we had before, and a new one, an array of assignedUsers. I talked about ARFetch in previous posts, it take an id and gives you the matching object. It also works on arrays, so I don't need to do anything to get the list of users the admin assigned to this project. ARFetch knows to do this because all those ids were named project.Users, so it can just grab all the parameters with this name and load them by their ids.

One thing that I have to do manually, however, is to add the assigned users to the project. MonoRail will not do this for me, and that is a Good Thing. MonoRail doesn't know about the semantics of the operation, in this case, I want to remove all the users already assigned, and add the ones the admin assigned now, but what happens when I want something else? MonoRail can't tell, so it's giving us the live objects, and leave the last step for the business logic to handle.

As you can see, it's not much of a burden, we clear the Users collection, then add all the users the admin specified to the project and save the project. That is nearly it. We need to pass the assignedUsers and unassignedUsers to the view from the CreateProject action as well. Since I'm going to be duplicating code here, I'm going to refactor it out and put it in AddAllUsersToPropertyBag(), since this is the common dominator between EditProject() and CreateProject().

Okay, that was a long post. Just a couple of things before I wrap things up, just think of how much work we did in this page. We've an Ajax interface, we load and save complex data to the database, including managing many to many relationships. I may be a partial observer, but I think that we did all that with a minimum of fuss, and not much complexity.

Next, we will talk about some latent bugs and adding some more validation to the application. Then, it'll be time for the Big Security Review. I got another post coming soon, so I'll update the binaries on the site when I post that one, and not for this.

My Projects | BrailOpen Source | CastleProgrammingWebsite

Typing: Static, Explicit, Implicit And Dynamic

Another note about typing. In my opinion, people are often confused about the meaning of static typing vs. dynamic typing and explicit typing vs. implicit typing. The two are not neccearily connected. In general, a programming langague will fall into one of this categories:

  • Staticly typed, explicit typing - Java, C++, C#, VB.Net, etc
  • Staticly typed, implicit typing - Boo, C# 3.0, VB.Net 9.0 etc
  • Dynamically typed, implicit typing - Javascript, Ruby, Smalltalk, Brail :-), etc
  • Dynamically typed, explicit typing - Can't think of a single language with this, nor a single reason to want this.

Boo, C# 3.0, VB.Net 9.0 are smart enough to infer what the type of a variable is, without the developer having to tell the compiler (over and over again) what the type of the variable is.

Staticaly typed languages are very good, in my opinion, but explicit typing is a chore, nothing more. After I saw how easy it is to work with Boo, I just can't believe how hard I have to work in other languages just to make the compiler work.

Dynamic langauges are cool (Boo has some of the features that they can offer, and they are simply amazing), but you the ability to check the program for syntax validity. This is not a trivial matter. Yes, syntax violations are easy to fix, but if I write a bunch of code, and I need to fix the errors there one after the other, instead of one bunch, it's annoying and wasting my time. I'm not talking about unit testing here, I'm talking before I get to the point where my program even run.

In short, I think that the sweat spot for me is Boo. Staticly typed, implicit typing (but can be explicit if you want), with dynamic features avialable if you want them. I see the point in dynamic languages, and I enjoy doing work with Javascript (the only dynamic langauge I work with on regular basis), doing things that I could never do in C#.

But the point is that 90% of what you can do in a dynamic langauge you can do in a static one if the compiler is smart enough. The best is if the compiler allows you to say "yes, I know what I'm doing, let's do this bit on runtime".

Tags:

Published at

Quick note on static vs. dynamic typing

I'm reading James Robertson's blog, mainly for the rants. I mostly agree with what he says, if not with the tone, but today I read something of his in the comments that really annoyed me.

Detection of a missing method is utterly irrelevant. Why? Because such a flaw would turn up in initial testing.

I just did a bunch of javascript, which is a dynamic language with just this kind of behavior. Sorry, this isn't even close to satisfying. I had about.. oh... maybe 50 lines of code, and I kept running into problems with method not found, etc.

If I don't have an IDE when I program (and I do significant work from Notepad2), I like to compile and see what went wrong. I usually don't get those method names correctly. I misspell, or mis capitalize, or forget to put a closing bracket or something.

A compile gives me all those errors in a format that I can see and fix immediately. Runtime errors means that I have to fix those erorrs one by one. This isn't fun.

Tags:

Published at

Rhino Mocks 2.6.3: Bug Fix for regression with generic types.

Okay, now you know why I try to avoid releases of Rhino Mocks, every time I make a release, I usually have a week of sudden activity, with new features / fixes coming in.

This time it's a regression that happened with regard to generic types. The issue is that you can't mock any generic type more than once. I had tests for mocking generic types, but I didn't have any tests for trying the same type multiply times.

Well, now I do, and it's fixed. So you can go ang get sources and binaries from this page.

Tags:

Published at

What is wrong with JavaScript?

Stefan asks What Is Wrong With Javascript, I think that I've at least a partial answer to that. Here are my reasons:

  • Until very recently, Javascript was just too slow by far to consider writing applications with it.
  • Lack of a rich class library.
  • Until recently, lack of good debugging tools.
  • The browser is a really sucky place to develop in.
  • The impression of being a Toy Languague.
  • Lack of strong typing, this bytes me so often that I feel like screaming. I missed a semi column, or misspelled a varaible... go figure when/where this is going to blow (usually silently).
  • The browser is not a nice place to work with.
  • Easily modificable by the users.
  • Easily disabled by the users.
  • While the langauge is mostly standartize, there were (and are) problems with the environments that you work with, which makes life painful.
  • No typing is annoying to me.
  • Did I mention that the browser is not an application platform?

Maybe I'm bitter, but I'm butting my head against the wall in a relatively simple JavaScript problem for a couple of days now, and this is not a new feeling. Everytime that I need to do something with Javascript I approach it with disdain, not because the langauge is bad, I actually enjoy a lot of the things that I can do with Javascript. I don't like it because I know that I'm going to spend quite a few hours chasing stupid stuff all over the place.

For instance, take the most recent puzzle, creating input controls dynamically and sending them to the server:

<script language="javascript" type="text/javascript">

function addUser()

{

    var users = document.getElementById('users');

    var hidden = document.createElement('input');

    hidden.value = 'foo';

    users.appendChild(hidden);

    var i =0;

    for(var node = users.firstChild;node !=null; node = node.nextSibling)

    {

        if(node.nodeType != 1) //input

                continue;

            node.id = 'project.Users['+i+'].Id';

        i++;

    }

}

</script>

And the HTML:

<form id="form1" runat="server">

    <div id="users">

        <input id="Button1" type="button" value="Add User" onclick="javascript:addUser();" />

    </div>

    <asp:TextBox ID="PostBackValues" runat="server" Height="225px" TextMode="MultiLine" Width="596px"></asp:TextBox><br />

    <asp:Button ID="submit" runat="server" Text="submit" OnClick="submit_Click" /><br />

</form>

Now, pressing the add users cause a nice text input to be added to the page, I then hit sumbit, and watch the result from this code-behind:

protected void submit_Click(object sender, EventArgs e)

{

    foreach (string key in Request.Params.AllKeys)

    {

        PostBackValues.Text += string.Format(" {0}: {1}\r\n", key, Request.Params[key]);

    }

}

I don't see the values I just put in. Even more important, I can't see them in the request stream when I'm using Fiddler to check this. What happened to them, I don't know, and all my debugging turned out blank. I know that there is a two line fix for this, and probably a reasonable explanation, but getting to this point wasn't a fun process, and hitting a wall like that isn't uncommon for me when using Javascript. I had similar problems doing some smart UI with tables & colors in Javascript, especially since I wanted to do this on both IE and Firefox.

Rhino Mocks 2.6.2: Memory Requirements Improvements

Okay, this release should be a major improvement memory wise, I changed the implementation so instead of generating dynamic assemblies per MockRepository, all the Mock Repositories will generate to the same assembly.

This should prove to be a dramatic reduction in the amount of space that Rhino Mocks consumes.  Sources and binaries are avialable here.

FREAKING UN-Believable!


I just watched DNR TV #5, and I must say that it was one of the most amazing videos that I have ever seen. This is especially true when you consider that the feature being implemented on the show was one I wished that I had nearly a year ago.
This video features Mark Miller doing on-the-fly implementation of source code organizing (sort by visibility, then by member type, then alphebatically, etc).
Beside being a cool demonstration, and one of the few videos that managed to make me stop everything and watch them, the features of Code Rush that were shown were simply amazing. I've already downloaded the trail version, and I'm using it right now. Simply awesome show, and awesome technology.
The ease of developing with the DXCore is amazing, as well.

Tags:

Published at

SqlClr outperforms T-SQL (7 times faster!)

Okay, a couple of months ago I posted about Finding Use for SqlClr for date processing, since it was much easier to do it in .Net than in SQL. Since then I retracted my statement about it.

If you read my blog for a while, you probably noticed that I'm doing quite a bit of performance tuning lately. After making the switch from SqlClr to T-Sql, I noticed a sudden drop in performance. Some investigation lead me to the change from SqlClr function to T-Sql functon.

Here is the source code for the .Net function (yes, I occasionally do VB.Net):

<SqlFunction()> _

Public Shared Function FirstDayOfMonth(ByVal year As Integer, ByVal month As Integer) As DateTime
   
Return New DateTime(year, month, 1)
End Function

And here is the T-Sql function:

CREATE

FUNCTION FirstDayOfMonth(@year int, @month int)
RETURNS DATETIME WITH SCHEMABINDING AS
BEGIN
   
RETURN DATEADD(MONTH,@month-1,DATEADD(YEAR,@year-1900,0))
END

As you can see, none of them do anything terribly complicated. I then run the following query on a table with 40 million records:

SELECT

1 FROM BigTable
WHERE SqlClr.FirstDayOfMonth(YEAR(Date),MONTH(Date)) = '20010101'

This query has no meaning whatsoever, it just make sure that the function is running for a long time. This query run for 34 seconds, before returning (an empty result set).

SELECT

1 FROM BigTable
WHERE [T-Sql].FirstDayOfMonth(YEAR(Date),MONTH(Date)) = '20010101'

This query run for 3 minutes and 29 seconds, 7 times slower than SqlClr! I'm not sure how to take this news, since I was completely convinced that it would be the other side that would win the race. Anyone got an explanation?


Castle Demo App: CRUD Operations on Projects

I thought about leaving the CRUD for Projects for the readers, but when I started to implement it myself, I realized that there were several things here that I hand't explained. As a quick note, I made some modifications to the code, to structure it better, there shouldn't be any changes to functionality, but I added a few utility functions and made some of the views more generalized.

In general, those changes are from having NoSuchUser and NoSuchProject views to having a single NoSuchObject view, nothing more. Okay, let's talk about projects. A project is a little bit more complicated than a user, since it actually contains a reference to a user. This means that we can't just use exact same techniques as we used for the CRUD operations on the User class. The changes are fairly minors, but they deserve their own post, so here it is, Views\Admin\EditProject.boo:

<?brail

      name = ""

      defaultAssigneeId = -1

      id = "0"

      if ?project:

         name = project.Name

         defaultAssigneeId = project.DefaultAssignee.Id

         id = project.Id.ToString()

      end

     

?>

${HtmlHelper.Form('SaveProject.rails')}

${HtmlHelper.FieldSet('Project Details:')}

${HtmlHelper.InputHidden('project.id', id)}

<table>

      <tr>

            <td> 

                  ${HtmlHelper.LabelFor('project.Name','Project name:')}

            </td>

            <td>

                  ${HtmlHelper.InputText('project.Name',name, 50, 50)}

            </td>

      <tr>

      <tr>

            <td>

      ${HtmlHelper.LabelFor('project.DefaultAssignee.Id','Default Assignee:')}

    </td>

    <td>

            ${HtmlHelper.Select('project.DefaultAssignee.Id')}

            ${HtmlHelper.CreateOptions(allUsers, 'Name','Id', defaultAssigneeId)}}

            ${HtmlHelper.EndSelect()}

    </td>

      </tr>

</table>

${HtmlHelper.SubmitButton('Save')}

${HtmlHelper.EndFieldSet()}

${HtmlHelper.EndForm()}

Most of what you see here we have already seen. The only new thing is the Select in the end, which would display the list of all the users that who can be the default assignee to this project. A couple of words on the CreateOptions() method. The first parameter is the collection of items to use, the second is the display text, the third is the value text. The forth is the id of the selected object. As always, this view is going to be used for both editing a project and adding a new one.

Another thing to note is the bolded line at the start of the view, where we use the '?' prefix to denote an optional variable. Where before we have to use IsDefined(), we can now just use the '?' prefix to get a null result when the value has not been defined for the view.

Now we can turn to write the SaveProject() method, which is a bit more complicated:

    public void SaveProject([ARDataBind("project", AutoLoadUnlessKeyIs = 0)]Project project,

        [ARFetch("project.DefaultAssignee.Id", Create=false, Required=true)] User defaultAssigneee)

    {

        RenderMessage( "Back to Projects", "Projects");

        try

        {

            if (project.DefaultAssignee == null)

                project.DefaultAssignee = defaultAssigneee;

            project.Save();

            Flash["good"] = string.Format(

                "Project {0} was saved successfully.", project.Name);

        }

        catch (Exception e)

        {

            Flash["DataBindErrors"] = project.ValidationErrorMessages;

            Flash["bad"] = e.Message;

            Flash["Debug"] = e.ToString();

        }

    }

As you can see, I'm using the ARDataBind for the project again, but now I combine it with ARFetch for the defaultAssignee. The reason for that is that while ARDataBind is smart enough to give me the correctly configured object when the object already exists in the database, it's not smart enough to figure out the correct connections by itself, hence, the ARFetch attribute. It simply gives me back the object with the same id as the one that was passed, if I see that the Default Assignee property on the project is null (which will only happen when we create a new project), I set the defaultAssignee manually. ARFetch will simply load a value from the database based on its primary key, so it's not as smart as ARDataBind, but as you can see, it certainly has it uses.

Here is the EditProject() and CreateProject() methods:

    public void CreateProject()

    {

        AddAllUsersToPropertyBag();

        RenderView("EditProject");

    }

 

    public void EditProject([ARFetch("id")]Project project)

    {

        AddAllUsersToPropertyBag();

        ValidateObjectToSendToView("project", "Projects", project);

    }

There isn't much going with them, CreateProject simply push all the users for the view to use, and render the EditProject view. EditProject uses ARFetch to get the correct project and make it (and all the users) available for the view to use. Here are the confirmProjectDelete and DeleteProject methods:

    public void ConfirmDeleteProject([ARFetch("id")]Project project)

    {

        if (project == null)

            RenderNoSuchObject("project");

        RenderDelete("deleteProject", "Projects", project);

    }

 

    public void DeleteProject([ARFetch("id")]Project project)

    {

        if (project == null)

        {

            RenderNoSuchObject("project");

            return;

        }

        project.Delete();

        RedirectToAction("Projects");

    }

You can probably guess that I'm very much in love with the ARFetch attribute :-) I trust that there isn't much need to explain what the code does, it's pretty straight forward.  Another change that I've made was with the Global.asax, where the code moved to a code behind file. I also added a session per request support, which will be helpful when we'll start dealing with lazy collections:

public class MythicalBugTrackerApplication : HttpApplication

{

    const string SessionScope = "Active.Record.SessionScope";

 

    public override void Init()

    {

        base.Init();

        log4net.Config.DOMConfigurator.Configure();

        Castle.ActiveRecord.Framework.IConfigurationSource source =

               System.Configuration.ConfigurationManager.GetSection("activerecord") as Castle.ActiveRecord.Framework.IConfigurationSource;

        Castle.ActiveRecord.ActiveRecordStarter.Initialize(typeof(MythicalBugTracker.Model.User).Assembly, source);

 

        this.BeginRequest += new EventHandler(MythicalBugTrackerApplication_BeginRequest);

        this.EndRequest += new EventHandler(MythicalBugTrackerApplication_EndRequest);

    }

 

    void MythicalBugTrackerApplication_EndRequest(object sender, EventArgs e)

    {

        ISessionScope scope = Context.Items[SessionScope] as ISessionScope;

        if (scope != null)

            scope.Dispose();

    }

 

    void MythicalBugTrackerApplication_BeginRequest(object sender, EventArgs e)

    {

        Context.Items.Add(SessionScope, new SessionScope());

    }

}

The utility methods I added are:

  • ValidateObjectToSendToView - make sure that the object is not null (if yes, redirect to the NoSuchObject page) and push it to the view
  • RenderNoSuchObject - display a generic error about missing object
  • RenderDelete - render the cofirm delete page
  • AddAllUsersToPropertyBag - make all the users avialable to the view
  • RenderMessage - render the generic message (good, bad, debug, databind violations)

Note: You can now get the source for the Mythical Bug Tracker here, In order to run this code you need:

  • New build of castle, revision 1700 and onward at least (just get the latest from Subversion, that should be enough).
  • .Net 2.0
  • SQL Server or SQL Express

Enjoy,

Brail Update: Nullable Parameters

Okay, so I run into a couple of problems with NUnit versioning, but it's here. I added support for nullable parameters in Brail, so now you can do:

${?user}

And if the user has not been set, it'll return null. You can get the latest bits here. I won't publish a Castle Demo App today, but I'll certainly attempt to use the new feature tomorrow in the Ajax part.

Messing with DasBlog, the bad point

I would like to apologize to anything who suddenly got a boat load of my old posts, I played with dasBlog's settings, and I forgot to set the settings back to normal.
Tags:

Published at

Posting Graphs

Here are the graphs that I promised; we'll start off with the number of posts in each month:

I do wonder what happened in June, 05, I posted like mad.

 

Here are the posts per day of the week:

Tags:

Published at

Playing With DasBlog

Every once in a while you see a couple of messages from the guys that use a Blog with a DB backend post some wonderful statistics about their Blog. I couldn't be happier with dasBlog (can you say zero headache in over two years of using it?), but I really like the statistics abilities. Well, I like to think of myself as a developer, and as such, I can solve such problems. Put this together with the SQL stuff I've been doing lately, and that dasBlog has Web Services functionality, and the solution is obvious, isn't it?

I'm going to explain how I did it now, and then post the stats in the next post (along with some nice graphs, I hope.) The first thing that I did was to take my previous post about dasBlog's Web Services, and took it for a spin, here are the results:

import System

import System.Data

import System.Data.SqlClient

import CookComputing.XmlRpc

import newtelligence.DasBlog.Runtime.Proxies

 

user = "Ayende Rahien"

password = "Ayende@ayende.com"

start = DateTime.Now

 

log = IO.File.CreateText("log.txt")

dasBlog = BloggerAPIClientProxy()

dasBlog.Url = "http://www.ayende.com/Blog/blogger.aspx"

posts = dasBlog.metaweblog_getRecentPosts("",user, password, 1500)

print "Got ${posts.Length} posts from server"

using con = SqlConnection("""Data Source=.;AttachDbFilename=F:.mdf;Integrated Security=True;User Instance=True"""):

      con.Open()

      using command = con.CreateCommand():

            command.CommandText = "INSERT INTO BlogStats(Time, Title) VALUES(@time, @title)"

            time = command.Parameters.Add("time", SqlDbType.DateTime)

            title = command.Parameters.Add("title", SqlDbType.NChar)

            for post in posts:

                  try:

                        time.Value = post.dateCreated

                        title.Value = post.title

                        command.ExecuteNonQuery()

                  except e:

                        print "Error updated post: ${post.title}"

                        print e.Message

                        log.WriteLine(e.ToString())

                        log.WriteLine("--- --- ---")

print "Took ${DateTime.Now - start}"

 

Tags:

Published at

Castle Demo App: Let There Be A New User

Before we start with our regular content, a couple of bug fixes, the ValidateLength on the Name and Email properties of the User class should looks like this: 

[

ValidateLength(0,50)]

Specifying just one number means the exact length of the string.

Okay, so far we have seen how to edit users and then save them to the database, but we haven't seen yet how to actually create the users. For myself, I think the users will be less than inclined to add themselves to the database by hand, using SQL, and I can just imagine the expression on the face of the DBA when he hears this suggestion.

Okay, I hear you, I won't bring that up again, looks like we have to do some work to make it happen. The question is, how much work is needed? We did a fair amount of work yesterday, to support editing users, how much of this work do you think we can use to support creating users?

Before anything else, I'm getting tired of having to remember all those URLs in my head, we'll add an index page to our administration section. Add an empty method called Index() to the AdminController class and create the following view at Views\Admin\Index.boo

<ul>

  <li>

    ${HtmlHelper.LinkTo('Admin Projects','projects')}

  </li>

  <li>

    ${HtmlHelper.LinkTo('Admin Users', 'users')}

  </li>

</ul>

Now we create the CreateUser method:

    public void CreateUser()

    {

        RenderView("EditUser");

    }

In the EditUser.boo view, we need to make a single change, and that is to initialize the id varaible to "0" instead of empty string, like this (fragement):

<?brail

      name = ""

      email = ""

      id = "0"

Let's beef up our SaveUser method, to make it capable of handling both existing and new users, the new stuff is bolded:

    public void SaveUser([ARDataBind("user",AutoLoadUnlessKeyIs=0)]User user)

    {

        RenderView("GenericMessage");

        try

        {

            user.Save();

            Flash["good"] = string.Format(

                "User {0} was saved successfully.", user.Name);

        }

        catch (Exception e)

        {

            Flash["DataBindErrors"] = user.ValidationErrorMessages;

            Flash["bad"] = e.Message;

        }

    }

I added some better handling for errors, and we now direct the framework to automatically load the user from the database unless the id is equal to 0. I think that this may require a couple of changes to the GenericMessage view, like this:

<p>

<font color="red">

<?brail

 if IsDefined('bad'):

  output bad

 end

?>

</font>

</p>

<?brail if IsDefined('DataBindErrors'): ?>

<table>

      <tr>

            <thead>Validation Errors</thead>

      </tr>

      <?brail for error in DataBindErrors: ?>

      <tr>

            <td>${error}</td>

      </tr>

      <?brail end ?>

</table>

<?brail end ?>

<p>

      <font color="green">

      <?brail

      if IsDefined('good'):

        output good

      end

      ?>

      </font>

</p>

Okay, we're done with creating users. Play with it a bit, try to add a non email adress, put it strings that are too long, use names that already eixst, etc. I won't claim that this is the greatest UI in the world, but it's functional.

Next stop, deleting users, should be simple. For now, we'll build it using normal pages & post backs. In a short while, I'll see how to use the Ajax support in MonoRail. We'll start by editing the users.boo page to add a link to the delete page, like this (fragement):

    <td>

      <?brail

        output HtmlHelper.LinkTo("Delete","admin","ConfirmDeleteUser",user.Id)

      ?>

    </td>

As long we're on a roll, we'll add a link to the create user page, like this (fragement):

${HtmlHelper.LinkTo('Create New User','admin','CreateUser')}

Now we get to write the delete functionality itself. We'll start with Views\Admin\ConfirmDeleteUser.boo, which looks like this:

<p>

  ${user.Name} says he is sorry, are you still angry?

</p>

<p>

      ${HtmlHelper.LinkTo('Yes, Delete!', 'Admin', 'DeleteUser',User.Id)} 

<p>

</p>

      ${HtmlHelper.LinkTo('Well, no...', 'Admin', 'Users')} 

</p>

And now to write the code that will actually handle all of this. Here it is in full:

    public void ConfirmDeleteUser([ARFetch("id",Create=false)]User user)

    {

        if (user == null)

        {

            RenderView("NoSuchUser");

            return;

        }

        PropertyBag.Add("User", user);

    }

 

    public void DeleteUser([ARFetch("id", Create = false)]User user)

    {

        if (user == null)

        {

            RenderView("NoSuchUser");

            return;

        }

        user.Delete();

        RedirectToAction("Users");

    }

This require some explanation. The ARFetch attribute will take an id and will pull it out of the database and into our parameter (passing null if it can't find it). We just make sure that it is handled properly. This is it, we know has full CRUD for a user. I'm going to leave the implementation of similar operation for projects as an exercise for the reader.

Next time, we'll see how we can add some Ajax stuff to it, so we can keep up with the hype.

Boo just got a whole lot better

One of my wishes just came true today, Boo just got a new feature that I wanted for a long time. It's not something that you're likely to find in any way useful unless you mess with the guts of Boo. The new feature is adding @,$,? prefixes to the Boo Parser. Why is this a good thing?

Beacuse it let me, and any other Boo Macro Developer, has more ways to get infromation from the user without it being a true pain in the ass. For instance, I can have this:

with longVariableName:

      @Foo = "name"

      @Blah = "foo"

You could do this previously, but this require a naming convention (prefix everything with a _, which is a valid variable name). Another possibility is this:

ignoreNulls:

      ?something.Foo()

This can take anything prefixed with a ? and emit code that would skip the call if the variable is null. I'm going to be putting it in Brail in the next couple of hours (hopefully :-) ).

Refactoring SQL

Okay, I just went through a SQL refactoring tha I consider interesting. We'll start with this code:

Cursor transactions IS

      SELECT L.CustomerId ,

         Sum(Y.Debt)    Sum_Debt ,

         Sum(Y.Credit)  Sum_Credit

      FROM    Transactions           Y  join 

              Customers              L

      ON L.CustomerId    = Y.CustomerId

      AND L.Status          NOT IN (1 , 2 , 3)

      GROUP BY L.CustomerId

 

For rec0 IN transactions Loop

 

      UPDATE CustomerStatus

        SET Debt = rec0.Sum_Debt,

            Credit = rec0.Sum_Credit

      WHERE CustomerId = rec0.CustomerId

 

End Loop;

The above is using Oracle syntax, which I find much saner for cursors than what SQL Server requires you to do. This is undoubtly one of the reasons that cursors are so much more popular in Oracle than in SQL Server.

From now on I'm using SQL Server syntax, btw. The first thing that I did was to remove the cursor. I did it by simply adding a FROM clasue to the update, like this:

UPDATE CustomerStatus

  SET Debt = rec0.Sum_Debt,

      Credit = rec0.Sum_Credit

FROM

(

      SELECT L.CustomerId ,

         Sum(Y.Debt)    Sum_Debt ,

         Sum(Y.Credit)  Sum_Credit

      FROM    Transactions       Y  join 

              Customers          L

      ON L.CustomerId    = Y.CustomerId

      AND L.Status          NOT IN (1 , 2 , 3)

      GROUP BY L.CustomerId

) rec0

WHERE CustomerId = rec0.CustomerId

 

This is much better performance wise, but I challange you to understand it when the query grows just a little bit more complex. Add a few more where clauses, do some more processing, and it's Write Once Hope You'll Never Read type of thing.

 

I then decided to use a Common Table Expression to simplify the statement, the end result looks like this:

 

WITH rec0 (CustomerId, Sum_Debt, Sum_Credit) AS

(

      SELECT L.CustomerId ,

         Sum(Y.Debt)    Sum_Debt ,

         Sum(Y.Credit)  Sum_Credit

      FROM  Transactions            Y  join 

            Customers               L

      ON L.CustomerId    = Y.CustomerId

      AND L.Status          NOT IN (1 , 2 , 3)

      GROUP BY L.CustomerId

)

UPDATE CustomerStatus

  SET Debt = rec0.Sum_Debt,

      Credit = rec0.Sum_Credit

FROM rec0

WHERE CustomerId = rec0.CustomerId

 

I find this much easier to understand than the previous one, and it's performance it just fine. One thing I wish I could do is either nest WITH statements or cascade them.

 

By nesting I mean something like:

 

WITH DrunkCustomers (CustomerId) AS

(

      WITH Bar(CustomerId)

      AS

      {

            SELECT CustomerId FROM Transactions

            WHERE Payee like '%bar%'

      }

      SELECT L.CustomerId ,

         Sum(Y.Debt)    Sum_Debt ,

         Sum(Y.Credit)  Sum_Credit

      FROM  Bar                           Y  join 

                  Customers               L

      ON L.CustomerId    = Y.CustomerId

      AND L.Status          NOT IN (1 , 2 , 3)

      GROUP BY L.CustomerId

)

 

By cascading I mean:

 

 

WITH Bar(CustomerId)

AS

{

      SELECT CustomerId FROM Transactions

      WHERE Payee like '%bar%'

}

WITH DrunkCustomers (CustomerId) AS

(

      SELECT L.CustomerId ,

         Sum(Y.Debt)    Sum_Debt ,

         Sum(Y.Credit)  Sum_Credit

      FROM  Bar                           Y  join 

                  Customers               L

      ON L.CustomerId    = Y.CustomerId

      AND L.Status          NOT IN (1 , 2 , 3)

      GROUP BY L.CustomerId

)

 

Sadly, none of those options works.

Tags:

Published at

Why So Many Snippets

So Microsoft has posted a lot more snippets, a Jeff has made them more accessible. I downloaded them and took a look. I'm pretty sure that you could hear my cries of horror all the way to Remond.

Why? That is the question I have. Snippets are useful for things like generating a foreach (although the Microsoft implementation sucks, try the way ReSharper does it.) or maybe generating a method skeleton. I see them doing things there like Hashing A Password, Encrypting Files, etc.

Why? This is just a recipe for disaster. If I want to hash a password, I write a method to do it and then I call it. I don't create a template for hashing a password. By all means, provide more utility methods, but don't do it like this. Allow me to hardwire method calls to intelli sense, cool. Encouraging users toward code duplication, Not Cool.

Brail Update

Okay, I got around (finally!) to work a little with Brail, and the end result is that there were a plenty of improvements:

  • Removed useless error handling from Brail, now you'll get the noraml ASP.Net Yellow Screen Of Death on errors. More consistent with the rest of the framework. I can't really understand what I was thinking when I added this.
  • Added support for <?brail ?> tags for Brail, so it can be valid XML documnets. The old way of using <% %> is still supported, but I would suggest using <?brail ?> for new stuff, it's much better, and it actually works in VS.Net. This also mean that you can use all sorts of XML processing on your Brail scripts.
  • Added logging for Brail, so you can see the Boo code the preprocessor is generating. This is mostly useful for cases where you get an error and you can't figure out what / where / how. 
  • Added more verbose output to compiler errors in Brail. Should give a lot more information about problems.
  • Changed the way Brail name the generated class, now the chances for name clashes are much lower. (View: Users.boo and a parameter Users will not cause problems).
  • Fixed a bug with Brail not picking up changes that occured by VS.Net

You can get the latest stuff from the Castle Repository.

Have fun.

Great Quote on Complexity

Patrick Logan has a post web services. I've very little to do with web services so far (notice the lack of posts complaining about stuff in Indigo :-) ).

What caught my eye was this quote:

Vendors will have to differentiate themselves on something more than wizards that mask complexity.

I agree whole heartedly, and for much more than merely SOAP or Web Services.

Castle Demo App: Updating our Users

Okay, here we go again. One thing that I forgot to mention in the last post is how you access the pages you created. You simply need to use the following URLs:

Before we continue, a couple of words. When I started this series, I had the intention to make it possible to use this with the latest release of Castle. Unfortantely, while writing this series, I encountered a couple of bugs in Brail (and there has been some other changes to MonoRail) that meant that what I write here is relevant to the newest bits. You can get the bits from the Castle Subversion repository.

Just as a note, I noticed that editing Brail documents in VS.Net isn't much fun. I highly recommend using SharpDevelop to edit the views. I support boo and it has much nicer code highlighting than the one you get from VS.Net. That said, I made some modifications to Brail so you could write valid XML documents using:

<?brail

  import Boo.Lang

 

  for i in Builtins.range(3):

    output i

  end

?>

You can still use the <% and %>, but I will use the <?brail ?> style from now on. Note: you can't mix the two in a single page.

After you finish admiring those lovely page, (and curse me for making you download and compile the latest bits) you'll probably want to add some user interaction to the mix, wouldn't you? Let's add editing support for the users.

First, let's us add an edit link to the users page:

<table border="1" width="70%">

     <tr>

           <td>

                <B>Id</B>

           </td>

           <td>

                <B>Name</B>

           </td>

           <td>

                <B>Email</B></td>

    <td></td>

     </tr>

<?brail

     for user in allUsers:

?>

     <tr>

           <td>

                ${user.Id}

           </td>

           <td>

                ${user.Name}

           </td>

           <td>

                ${user.Email}

           </td>

    <td>

      <?brail

        output HtmlHelper.LinkTo("Edit","admin","EditUser",user.Id)

      ?>

    </td>

     </tr>

<?brail

     end

?>

As you can see, I moved to use the <?brail ?> notation. I bolded the new parts. Refresh the page again (no need to recompile anything). You should see a nice "Edit" link from the users list. It doesn't do anything ( and you'll get an error page if you'll try to follow the link.

We'll start by create the EditUser method on the AdminController class, with a single integer as a parameter:

    public void EditUser(int id)

    {

        User user = User.TryFind(id);

        if (user == null)

        {

            RenderView("NoSuchUser");

            return;

        }

        PropertyBag.Add("User", user);

    }

It's a simple method, it gets the id of the user (this is handled by MonoRail automagically) and then try to pull the user from the database. If it doesn't exists, it redirect to the error page. If it does exist, it add the user to the property bag and we're done with the controller. Now it's time to create the views.

The error view is really simple, it's actually just this:

A user with the id: ${Id} could not be found.

Save it to: Views\Admin\NoSuchUser.boo, and now we create the more complex view at Views\Admin\EditUser.boo:

<?brail

     name = ""

     email = ""

     id = ""

     password = ""  

     if IsDefined("user"):

        name = user.Name

        email = user.Email

        id = user.Id.ToString()

        #remember, no getter for the password

        password = "Dummy Password"

     end

    

?>

${HtmlHelper.Form('SaveUser.rails')}

${HtmlHelper.FieldSet('User Details:')}

${HtmlHelper.InputHidden('id', id)}

<table>

     <tr>

           <td>

                ${HtmlHelper.LabelFor('user.Name','User name:')}

           </td>

           <td>

                ${HtmlHelper.InputText('user.Name',name, 50, 50)}

           </td>

     <tr>

     <tr>

           <td>

      ${HtmlHelper.LabelFor('user.Email','User email:')}

    </td>

    <td>

      ${HtmlHelper.InputText('user.Email',email, 50, 50)}

    </td>

     </tr>

     <tr>

           <td>

      ${HtmlHelper.LabelFor('user.Password','Password:')}

    </td>

    <td>

      ${HtmlHelper.InputPassword('user.Password',password)}

    </td>

     </tr>

</table>

${HtmlHelper.SubmitButton('Save')}

${HtmlHelper.EndFieldSet()}

${HtmlHelper.EndForm()}

I'm using a couple of new things here. The HtmlHelper is just a way to pack a bunch of HTML code into reusable form. You can think of the helper as MonoRail version of the WebForms Controlls, although that isn't 100% accurate.

The part in the beginning require some explanation. Brail is partially late bound language. If it can, it detects types of variables during compilation, but if it's not possible, it will wait for runtime to discover what it can about the variables. Because it's possible that the the controller will not pass the user varaible to the view (and we'll see why in a second), we check to see if the variable exists before using it.

One thing that you should notice is that when ever I use the ${...} syntax and I need to pass a string to a method I use the single quote character '. Trying to use the double quote character " will cause an error.

Pay attention to the naming convention of the items in the form, they all start with "user.", and that will be very helpful down the road.

Okay, now run the page (http://localhost:8080/admin/EditUser.rails?id=1) and see what you get. It's functional, if not pretty. But still we have a problem, we now display the users details in an editable way, but we can't save it. Let's build the SaveUser action.

We have several options we can use:

We can do it the hard way, by manually pulling the data out of the Request.Form (relax, I can hear the groans up to here).  Or, we can allow MonoRail to pull the data and pass it to us using the method parameters. We can even allow MonoRail to construct an object out of the fields. But we'll take the really easy way and let MonoRail do nearly all the work.

We need to do the following in order to make it all work seamlessly:

  • Add reference to: Castle.MonoRail.ActiveRecordSupport.dll
  • Add reference to: Castle.Components.Binder.dll
  • Change the base class of AdminController to be ARSmartDispatcherController.

I present to you, in its entirely, the SaveUser() method:

    public void SaveUser([ARDataBind("user", AutoLoad=true)]User user)

    {

        RenderView("GenericMessage");

        try

        {

            user.Save();

            Flash["good"] = string.Format(

                "User {0} was saved successfully.", user.Name);

        }

        catch (Exception e)

        {

            Flash["bad"] = e.Message;

        }

    }

Okay, we have the ARDataBind attribute for the user parameter, and this tells MonoRail that all the parameters that start with "user" belong to this class, and that it is an Active Record class. Since MonoRail has Active Record integration, it simply pull the correct record from the database and give it to us, with the updated values! Then we can decide what to do with it. Here, I'm just saving it to the database and putting a message to the Flash (which is what we use to put transient messages, it's implemented the same way the PropertyBag is, but things on the Flash are usually error or one time messages, things that can appear and disappear.)

We then choose to render the GenericMessage view, which is located at Views\Admin\GenericMessage.boo and looks like this:

<font color="red">

<?brail

 if IsDefined('bad'):

  output bad

 end

?>

</font>

<font color="green">

<?brail

if IsDefined('good'):

  output good

end

?>

</font>

The CSS purist will go crazy, but this is the quickest way to do it :-) Okay, we're done, let's go home.

Oh, I see that you're still here, well, there are a couple of other things that we need to do before we can move on. We got a couple of problems with this approach. We aren't really verifying string lengths, for instance, and a user can change its name to one that already exists. While we can let the database handle this, I really want to make some check in my code as well (and I'm certainly not going to verify email adresses using the database).

We'll go back to our User class and make a couple of changes.

To start with, we need to change the base class of User from ActiveRecord<User> to ActiveRecordValidationBase<User> Then, we need to add a couple of attributes to the Name and Email properties, here they are:

    [Property]

    [ValidateEmail, ValidateLength(50)]

    public string Email

    {

        get { return _email; }

        set { _email = value; }

    }

 

    [Property]

    [ValidateIsUnique, ValidateLength(50)]

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

Now it will automatically validate before save. I'll leave building the EditProject site as an excersize for the reader. I meant to talk about creating / deleting users today, but I fear that I needed to refresh myself with a couple of things (and fix / improve Brail in the process).

I'll do that tomorrow, and then you can see just how much reuse we can reap from today's work.

Sharp Develop Rocks

I was pretty harsh about Sharp Develop in the past, but I'm using it lightly lately, and it is a great tool. Especially if you are doing any kind of work with Boo.
Tags:

Published at

Rad kills

I can only agree with Jeffrey on his post RAD kills. . . software post. And I'll top that one with an observation of my own, there is significant investment to learning how to work with RAD tools, with all their twists and quirks.

I would say that there is just no faster way to display a table on a page than by dragging the table to the form and hitting F5. The problem is that the moment you want to do something with it that isn't obvious, you are suddenly out of drag & drop land and into "understand the RAD tool in depth and then figure out how you can put a seam there to make the change you want".

Here is a good example, I got a collection of objects from the database, and the way they are built and the way that they should be displayed to the user is completely different. I have no idea how I would deal with it in the ".Net Approved Way", I dealt with it by defining a Dictionarty that mapped types to translator methods, like this:

translator.Add(typeof(BugStatus),

                delegate(object status)

                {

                  return Localization.GetEnumValue((Enum)stats);

                });

translator.Add(typeof(User),

                delegate(object user)

                {

                  return ((Users)user).Name;

                });

During databind, the objects are changed to the way I need them. I can a reversed function if I want to get an object from its string representation. When I tried to do it using the ASP.Net tools it was so frustrating...

Tags:

Published at

SQL Tip

Michael got a SQL Tip that is going to save me quite a bit of trouble. How to make SQL Server lie about its alias.

This means that I can move code between computers with SQL Express and SQL Server without going completely NUTS with stuff failing the first time, because I forgot the connection string.

Tags:

Published at

Castle Demo App: Getting serious, the first real page

Okay, so far we played around with MonoRail, and I, at least, has quite a bit of fun. Just to point something out before we carry on: While MonoRail has strong scaffolding capabilities (the ability to generate pages for CRUD operations automatically), I don't like the idea of using them, we're going to build everything from scratch.

The first thing that we need to do is to add is some wrapper HTML so our pages will not be HTML fragements (like the previous one). We do it by specifying a layout. Layouts are very similar to master pages in ASP.Net 2.0. We will build a very simple one before we more on to more complex stuff. A layout usually resides in the Views\Layouts folder, since I aspire to be original and surprising, I decided to call mine Default.boo. (Bet you didn't see that coming). Here is the boring layout:

<!DOCTYPE xhtml

PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>

<body>

${ChildOutput}

</body>

</html >

Notice the part where it says ${ChildOutput}? That is the part where the view data will go. Now what? Shall we start with administration? Why not. Here is what we are trying to achieve. We'll have a secured part of our site where an administrator can manage users and projects. We'll ignore security for now, (I'll show how to add that on later on), so let's start doing something that you can show around in the office.

As usual, we'll start by creating a class, this time, it's the AdminControll class. Right now it is going to have only two methods, Users() and Projects():

[Layout("Admin")]

public class AdminController : SmartDispatcherController

{

    public void Users()

    {

        User[] users = User.FindAll();

        PropertyBag.Add("allUsers", PaginationHelper.CreatePagination(users, 25));

    }

 

    public void Projects()

    {

        Project[] projects = Project.FindAll();

        PropertyBag.Add("allProjects", PaginationHelper.CreatePagination(projects, 25));

    }

}

I'm using SmartDispatcherController as the base class because I'll later want the functionality it provides (match request parameters to method overload). For the moment, we can ignore the PaginationHelper calls. We add the Admin.boo layout to Views\Layouts, this one just proclaims that this is the administrator section:

<!DOCTYPE xhtml

PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>

<title>Administrator Section</title>

<body>

${ChildOutput}

</body>

</html >

We create a new directory called Admin under Views, and create two files there, Users.boo and Projects.boo. Here is how the Users.boo view looks like. It's quite a bit of code, but there is nothing complicate to it:

<p>Our users:</p>

<table border="1" width="70%">

      <tr>

            <td>

                  <B>Id</B>

            </td>

            <td>

                  <B>Name</B>

            </td>

            <td>

                  <B>Email</B></td>

      </tr>

<%

for user in allUsers:

%>

      <tr>

            <td>

                  ${user.Id}

            </td>

            <td>

                  ${user.Name}

            </td>

            <td>

                  ${user.Email}

            </td>

      </tr>

<%

end

%>

</table>

<p>Pagination Data:</p>

<div class="pagination">

<table width="70%" >

  <tr>

  <td>Showing Users: ${allUsers.FirstItem} - ${allUsers.LastItem} of ${allUsers.TotalItems}</td>

  <td>

<%

      if allUsers.HasFirst:

            output PaginationHelper.CreatePageLink( 1, "first" )

      else:

            output "first"

      end

%>

  </td>  <td>

<%

      if allUsers.HasPrevious:

            output PaginationHelper.CreatePageLink( allUsers.PreviousIndex, "prev" )

      else:

            output "prev"

      end

%>

 </td> <td>

     

<%    if allUsers.HasNext:

            output PaginationHelper.CreatePageLink( allUsers.NextIndex, "next" )

      else:

            output "next"

      end

%>

 </td>  <td>

<%

      if allUsers.HasLast:

            output PaginationHelper.CreatePageLink( allUsers.LastIndex, "last" )

      else:

            output "last"

      end

%>

  </td>

  </tr>

</table>

</div>

The code above should be very familiar for anyone who ever used a <Repeater> on ASP.Net. Most of the code here is simplly to deal with the pagination. We will remove this code in the near future and put it where it belongs, in a reusable method. But for now, it does the job. You get a nice list of users (well, one user, but you get the point). We will return to this page in the future, and see how pagination is handled automatically by the framework, without us needing to do anything about it.

The Projects.boo view is nearly identical, but there is a piece of logic here that we can push outside. Just like in the rest of our code, we want to avoid duplication. We will now seperate the code to a seperate re-usable function (can you hear the OO fanatics pant in excitement in the back of the room?)

Brail will make any methods or classes that are stored in Views\CommonScripts to all the views in the application. So we start by creating this directory and then the script: "Views\CommonScripts\PaginationFooter.boo". Common Scripts are different than views, since they don't have a default output and they can't mix markup and code like views can do. This require some minor changes to the pagination footer:

import System.Text

import Castle.MonoRail.Framework.Helpers

 

def PaginationFooter(page as Page, pagingHelper as PaginationHelper):

      output = StringBuilder()

      output.Append("""<p>Pagination Data:</p> <div class="pagination"> <table width="70%" >

           <tr>   <td>Showing: ${page.FirstItem} - ${page.LastItem} of ${page.TotalItems}</td>

             <td>""") 

      if page.HasFirst:

            output.Append( pagingHelper.CreatePageLink( 1, "first" ) )

      else:

            output.Append( "first" )

      end

      output.Append("  </td>  <td> ")

      if page.HasPrevious:

            output.Append( pagingHelper.CreatePageLink( page.PreviousIndex, "prev" ) )

      else:

            output.Append( "prev" )

      end

      output.Append(" </td> <td> ")

      if page.HasNext:

            output.Append( pagingHelper.CreatePageLink( page.NextIndex, "next" ) )

      else:

            output.Append( "next" )

      end

      output.Append(" </td>  <td> ")

      if page.HasLast:

            output.Append( pagingHelper.CreatePageLink( page.LastIndex, "last" ) )

      else:

            output.Append( "last" )

      end

      output.Append("""  </td>  </tr> </table>  </div>""")

      return output.ToString()

end

We needed to move from markup & code to explicit string building, which we return to the caller. Just to show you the difference, check out how th Project.boo page looks like now:

<p>Our Projects:</p>

 

<table border="1" width="70%">

      <tr>

            <td>

                  <B>Id</B>

            </td>

            <td>

                  <B>Name</B>

            </td>

            <td>

                  <B>Default Assignee</B></td>

      </tr>

<%

      for project in allProjects:%><tr>

            <td>

                  ${project.Id}

            </td>

            <td>

                  ${project.Name}

            </td>

            <td>

                  ${project.DefaultAssignee.Name}

            </td>

      </tr>

<%

      end

%>

</table>

${PaginationFooter(allProjects, PaginationHelper)}

I made similar modifications to the Users.boo view, cutting its length it by more than half. It's pretty late around here, so I think I'll finish with this for today.

Future topics:

  • Adding edit / create / update functionality for both users and projects.
  • Using Ajax to connect users to projects.
  • Adding security to the application.- this one will touch on more subjects than just MonoRail

In the meantime, have a good day, and don't let Mrs. WebForms catch you looking at prettier options :-)