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,263
Privacy Policy · Terms
filter by tags archive
time to read 2 min | 242 words

This post has really pissed me off:

It makes me sick to my stomach to think of all the good .NET projects that are now abandoned (or soon will be) because Microsoft seduced their authors away from doing anything that would actually benefit the .NET community.

Excuse !

Who exactly said that I owe something to anybody? Who exactly said that any of the guys who went to work for Microsoft (many of whom I consider friends) owe you something. The entire post is a whine about "I can't get the software I want for free".

Well, guess what, no one said it has to be free. Software has no right to be free. If anyone wants to stop dedicating significant amount of their time into free stuff, that is their decision, for their own reasons. Rhino Mocks is estimated at nine million dollars by Ohloh, I might decide to stop using it tomorrow, and you don't get a chance to protest that, or even to complain. Put simply, where exactly are your efforts? Where is your money and time?

Because unless you are a customer (in the sense of, money exchanged hands), you got stuff for free and now you complain because people aren't willing to do so anymore?

Now, leaving that aside, to the best of my knowledge, Castle, SubText, dasBlog and SubSonic are all alive and well and have received attention from the respective "seduced" authors.

time to read 5 min | 852 words

The last time we looked at this issue, we built all the pieces that were required, except for the most important one, actually handling the contextual menu. I am going to be as open as possible, Intellisense is not a trivial task. Nevertheless, we can get pretty good results without investing too much time if we want to. As a reminder, here is the method that is actually responsible for the magic that is about to happen:

public ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped)
{
        return new ICompletionData[] {
             new DefaultCompletionData("Text", "Description", 0),
             new DefaultCompletionData("Text2", "Description2", 1)
        };
}

Not terribly impressive yet, I know, but let us see what we can figure out now. First, we need to find what is the current expression that the caret is located on. That will give us the information that we need to make a decision. We could try to parse the text ourselves, or use the existing Boo Parser. However, the Boo Parser isn't really suitable for the kind of precise UI work that we need here. There are various incompatibilities along the way ( from the way it handles tabs to the nesting of expressions ). None of them is a blocker, and using the Boo Parser is likely the way you want for the more advance scenarios.

Reusing the #Develop parser gives us all the information we need, and we don't need to define things twice. Because we are going to work on a simple language, this is actually the simplest solution. Let us see what is involved in this.

public ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped)
{
    TextWord prevNonWhitespaceTerm = FindPreviousMethod(textArea);
    if(prevNonWhitespaceTerm==null)
        return EmptySuggestion(textArea.Caret);

    var name = prevNonWhitespaceTerm.Word;
    if (name == "specification" || name == "requires" || name == "same_machine_as" || name == "@")
    {
        return ModulesSuggestions();
    }
    int temp;
    if (name == "users_per_machine" || int.TryParse(name, out temp))
    {
        return NumbersSuggestions();
    }
    return EmptySuggestion(textArea.Caret);
}

private TextWord FindPreviousMethod(TextArea textArea)
{
    var lineSegment = textArea.Document.GetLineSegment(textArea.Caret.Line);
    var currentWord = lineSegment.GetWord(textArea.Caret.Column);
    if (currentWord == null && lineSegment.Words.Count > 1)
        currentWord = lineSegment.Words[lineSegment.Words.Count - 1];
    // we actually want the previous word, not the current one, in order to make decisions on it.
    var currentIndex = lineSegment.Words.IndexOf(currentWord);
    if (currentIndex == -1)
        return null;

    return lineSegment.Words.GetRange(0, currentIndex).FindLast(word => word.Word.Trim() != "") ;
}

Again, allow me to reiterate that this is a fairly primitive solution, but it is a good one for our current needs. I am not going to go over all the suggestion methods, but here is the ModulesSuggestion method, which is responsible for the screenshot below:

private ICompletionData[] ModulesSuggestions()
{
    return new ICompletionData[]
    {
        new DefaultCompletionData("@vacations", null, 2),
        new DefaultCompletionData("@external_connections", null, 2),
        new DefaultCompletionData("@salary", null, 2),
        new DefaultCompletionData("@pension", null, 2),
        new DefaultCompletionData("@scheduling_work", null, 2),
        new DefaultCompletionData("@health_insurance", null, 2),
        new DefaultCompletionData("@taxes", null, 2),
    };
}

And this is how it looks like.

image

It works, it is simple, and it doesn't take too much time to build. If we want to get more than this, we probably need to start utilizing the boo parser directly, which will give us a lot more context than the text tokenizer that #Develop is using for syntax highlighter. Nevertheless, I think this is good work.

time to read 1 min | 147 words

I just took a look at how this feature is exposed. I really wants this feature. I hit the 2,100 parameters limit of SQL Server too many times in the past, always when I had to do some large IN queries. So, I was very happy to hear about that feature, but I didn't really take a look until now.

Unfortunately, the way they are implemented requires a hard reference to them. You have to create the type in the server,  and then you have to reference it by name. Annoying, to say the least, and it looks like there isn't any generic solution that I can accept. This is bad because I can think of quite a few uses for this feature, from applying batches to complex queries, it can be very useful, but it is looked in its own safe, statically typed, world. Urgh!

time to read 1 min | 85 words

I am currently working on an interesting application, basically, rule engine, data + DSL, and other fun stuff. Unfortunately, here is how the UI is right now:

image

Yes, the disclaimer is in the UI.

Therefor, I currently looking for a WPF dev / designer. I am currently in New York, but there is no location limitation.

If you are interested, please contact me.

Code or data?

time to read 3 min | 581 words

Here is a question that came up in the book's forums:

I can't figure out how to get the text of the expression from expression.ToCodeString() or better yet, the actual text from the .boo file.

It appears to automagically convert from type Expression to a delegate. What I want is to be able to when a condition is evaluated display the condition that was evaluated, so if when 1 < 5 was evaluated I would be able to get the string "when 1 < 5" - Any way to do this?

Let us see what the issue is. Given this code:

when order.Amount > 10:
	print "yeah!"

We want to see the following printed:

Because 'order.Amount > 10' evaluated to true, executing rule action.
yeah!

The problem, of course, is how exactly to get the string that represent the rule. It is actually simple to do, we just need to ask the compiler nicely, like this:

public abstract class OrderRule
{
    public Predicate<Order> Condition { get; set; }
    public string ConditionString { get; set; }
    public Action RuleAction { get; set; }
    protected Order order;
    public abstract void Build();

    [Meta]
    public static Expression when(Expression expression, Expression action)
    {
        var condition = new BlockExpression();
        condition.Body.Add(new ReturnStatement(expression));
        return new MethodInvocationExpression(
            new ReferenceExpression("When"),
            condition,
            new StringLiteralExpression(expression.ToCodeString()),
            action
            );
    }


    public void When(Predicate<Order> condition, string conditionAsString, Action action)
    {
        Condition = condition;
        ConditionString = conditionAsString;
        RuleAction = action;
    }

    public void Evaluate(Order o)
    {
        order = o;
        if (Condition(o) == false)
            return;
        Console.WriteLine("Because '{0}' evaluated to true, running rule action",ConditionString);
        RuleAction();
    }
}

The key here happens in the when() static method. We translate the call to the when keyword to a call to the When() instance method. Along the way, we aren't passing just the arguments that we got, we are also passing a string literal with the code that was extracted from the relevant expression.

time to read 4 min | 620 words

Another interesting question from Chris Ortman:

So I write my dsl, and tell my customer to here edit this text file?
How do I tell them what the possible options are? Intellisense?
This is a web app, and my desire to build intellisense into a javascript rich text editor is very low.
It might be a good excuse to try out silverlight but even then it seems a large task.
Or I put express or #Develop on the server and make that the 'admin' gui?

This is actually a question that comes up often. Yes, we have a DSL and now it is easy to change, how are we going to deal with changes that affect production?

There are actually several layers to this question. First, there is the practical matter of having some sort of a UI to enable this change. As Chris has noted, this is not something that can be trivially produced as part of the admin section. But the UI is only a tiny part of it. This is especially the case if you want to do things directly on production.

There is a whole host of challenges that come up in this scenario (error handling, handling frequent changes, cascading updates, debugging, invasive execution, etc) that needs to be dealt with. In development mode, there is no issue, because we can afford to be unsafe there. For production, that is not an option. Then you have to deal with issues such as providing auditing information, "who did what, why and when". Another important consideration is the ability to safely roll back a change.

As you can imagine, this is not a simple matter.

My approach, usually, is to avoid this requirement as much as possible. That is, I do not allow to do such things on production. Oh, it is still possible, but it is a manual process that is there for emergency use only. Similar to the ability to log in to the production DB and run queries, is should be reserved, rare and avoided if possible.

However, this is not always possible. If the client needs the ability to do edit the DSL scripts on production, then we need to provide a way for them to do so. What I have found to be useful is to not provide a way to work directly on production. No, I am not being a smartass here, I actually have a point. Instead of working directly on the production scripts, we start, as part of the design, to store the scripts in an SVN server, which is part of the application itself.

If you want to access the scripts, you check them out of the SVN server. Now you can edit them with any tool you want, and finish by committing them back to the repository. The application monitors the repository and will update itself when a commit is done to the /production branch.

This has several big advantages. First, we don't have the problem of partial updates, we have a pretty good audit trail and we have built in reversibility. In addition to that, we avoid the whole problem of having to build a UI for editing the scripts on production, we use the same tools that we use during development for that.

As a side benefit, this also means that pushing script changes to a farm is builtin.

And yes, this is basically continuous integration as part of the actual applicatio.

time to read 1 min | 97 words

I took this picture about a year and a half ago on my phone, while visiting a client. I then email it to myself. I don't think that it arrived, and I forgot about this.

This has just landed in my mailbox. It says, in Hebrew: "Mommy said that you can't put collections in the session on Hibernate will beat you up".

I don't ever own the phone this picture was taken on. How the hell did it arrive?

image

time to read 1 min | 85 words

Here is the next slide in my presentation.

image

Code quality is a hard metric, it can be backed up by numbers, if you need to. Fairly often, it is measured by gut feeling and "this is a mess".

One interesting problem with code quality is that you generally don't have a good way to measure the maintainability of a particular solution, regardless of its current code quality.

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   ... ...