Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,640
|
Comments: 51,273
Privacy Policy · Terms
filter by tags archive
time to read 1 min | 104 words

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.

time to read 18 min | 3550 words

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}"

 

time to read 33 min | 6566 words

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.

time to read 3 min | 522 words

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

time to read 17 min | 3282 words

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.

time to read 1 min | 161 words

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

time to read 2 min | 308 words

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.

time to read 1 min | 88 words

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.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. API Design (10):
    29 Jan 2026 - Don't try to guess
  2. Recording (20):
    05 Dec 2025 - Build AI that understands your business
  3. Webinar (8):
    16 Sep 2025 - Building AI Agents in RavenDB
  4. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  5. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
View all series

Syndication

Main feed ... ...
Comments feed   ... ...