I don’t want to make much ceremony around this, but I thought I’d mention it incase anybody else is interested.
As a part of a project I’m working on I needed a simple file to store some data in, and I didn’t want it to be XML (for no reason other than the verbosity). I could have used my own format, but instead I’ve gone for YAML. If you’ve worked with Ruby on Rails at all, then you’ll be familiar with YAML. It’s a human readable (and writable) text format.
Of course, I still needed to be able to parse my YAML document. There was a project announced 2 years ago to create a .Net parser, but like many things, it seems very much abandoned. So, after my recent adventure with OMeta#, I thought I’d hack on this too.
Introducing YaYAML: Yet another YAML parser.
Don’t get your hopes up, I’ve only implemented exactly what I needed out of the spec, which is very little indeed. However, it’s something I will carry on with when time permits. So what can you parse with YaYAML? Only documents containing a single flat sequence or mapping. No nesting, no multiple documents in a file, no variables.
Notice: I’m a novice at OMeta, and as such, you shouldn’t take my advice as best-practice. This is based on my exploratory findings.
String parsing is hard. I don’t think anyone will deny that. You can parse it by hand, you can use regular expressions, you can walk it character by character. With SharpDiff, I needed to parse some serious text, it was in an expected format, but there are numerous rules surrounding it. I did not fancy parsing it by hand, or with regular expressions.
I considered writing a parser for it, but dismissed that after my dealings with ANTLR in BooLangStudio. It’s not so much that ANTLR is bad, it’s just long winded; it’s completely visitortastic.
Anyway, after 10 minutes down the path of parsing it myself, I cracked and decided to look into alternatives to ANTLR. I came across OMeta# and (with a reassuring nudge by Jeffery Olson) I went with it.
So what is OMeta#?
At it’s heart, OMeta is just a really great string parser, and OMeta# is a .Net implementation of it. Combine that with a fairly decent definition language and codegen’d parser creation, and you’re onto a winner.
It allows you to write a syntax grammar and parse content with it, without having to worry about lexing, tokenizing and all that jazz. You only need to learn one language, OMeta.
While working on SharpDiff, I took a detour and implemented another very simple Git diff format. It’s a stat/metrics diff that just tells you which files have changed, and how many additions and removals have occurred in each. A pretty straight forward diff crying out to be used as an example.
A basic OMeta-based parser
Git has a diff format called stat (and it’s counter-part numstat, that we’ll be using). It produces output like so:
1 0 myFile.txt
3 1 anotherFile.txt
If you’re following along at home, you’ll need to create yourself a new git repository. In there place a couple of text files, add some content to them and commit them. Then make some changes to those files, but don’t commit that. Now you can execute git --numstat to get the above output.
The Git documentation is a wonderful thing, and it provides us with a nice outline of the numstat output.
1 2 README
3 1 arch/{i386 => x86}/Makefile
That is, from left to right:
the number of added lines;
a tab;
the number of deleted lines;
a tab;
pathname (possibly with rename/copy information);
a newline.
Before we get to the meat, there’s a couple of things we need to do. Firstly, (at the time of writing) there are no binaries available for OMeta#, so you’ll have to download the source and compile yourself. Once you’ve done that, you need to reference the OMetaSharp.dll and OMetaSharp.OMetaCS.dll.
Due to OMeta codegening our parser, it really needs to be built separately to our main project. If we don’t do that, we could get ourselves in a mess when we write an invalid grammar and generate some uncompiling code from it.
I won’t go over how to do this, but basically wrap the following code in a separate console app, changing the paths to point to where your code actually lives.
public void RebuildGitNumstatParser()
{
var contents = File.ReadAllText(@"..\..\..\SharpDiff\Parsers\GitNumstatParser.ometacs");
var result = Grammars.ParseGrammarThenOptimizeThenTranslate
<OMetaParser, OMetaOptimizer, OMetaTranslator>
(contents,
p => p.Grammar,
o => o.OptimizeGrammar,
t => t.Trans);
File.WriteAllText(@"..\..\..\SharpDiff\Parsers\GitNumstatParser.cs", result);
}
That just gets OMeta to compile our grammar, then spit out a C# file.
One more thing, then we can get going. For this generation to work, we really need to give it a grammar and parser to begin with. So we’ll create an empty grammar and parser.
using OMetaSharp;
ometa GitNumstatParser : Parser {
}
public class GitNumstatParser : Parser
{
}
Now we can begin! One of the wonderful things about OMeta# is that it can be test driven very easily, which really surprised me.
So lets create our first test!
[Test]
public void ParsesNumber()
{
var result = Parse<int>("1", x => x.Number);
Assert.That(result, Is.EqualTo(1));
}
In this test, we pass the Parse function a generic type parameter (int) which is our expected return type, then a string and an expression. The string is our input content, and the expression is the grammar rule to use for parsing.
I’m also using a shortcut method for parsing, which you can see below:
When you run this test, it should pass. We didn’t do anything, I hear you say. That’s because we didn’t have to in this case. OMeta# comes with a few predefined rules that you can sometimes take advantage of (or override to give different meaning in your grammar). In this case, we’ve used the Number rule, which parses a string and returns an integer from it.
Onto the next test.
[Test]
public void ParsesAdditionsAndSubtractionValues()
{
var result = Parse<FileStats>("3\t8\tmyFile.txt\r\n", x => x.Number);
Assert.That(result.Additions, Is.EqualTo(3));
Assert.That(result.Subtractions, Is.EqualTo(8));
}
We need a class to represent our file statistics, I’ve created one called FileStats.
public class FileStats
{
public FileStats(int additions, int subtractions)
{
Additions = additions;
Subtractions = subtractions;
}
public int Additions { get; private set; }
public int Subtractions { get; private set; }
}
When you run this test, it will fail (and at the time of writing, fail with a nasty unhelpful OMeta exception). This is because we’re still trying to use the Number rule, so we need to create our FileStats rule.
Open up your ometacs file and follow along.
using SharpDiff.FileStructure.Numstat;
using OMetaSharp;
ometa GitNumstatParser : Parser {
FileStats = Number:adds '\t' Number:subs
-> { new FileStats(adds.As<int>(), subs.As<int>()) }
}
Right, that’s a bit of an overload, so lets go through it.
The line is made up from three parts:
Rule name
Pattern to match
Code to produce
For this line, we’re creating a rule called FileStats, which matches the Number:adds '\t' Number:subs pattern, and produces the code new FileStats(adds.As<int>(), subs.As<int>()).
Lets examine what the pattern is doing. Firstly, we’re using the Number rule that we used in our first test. The colon denotes an assignment to a variable, so we’re getting the match from the Number rule and putting that in an adds variable. We then match a single tab character (’\t’), and then another Number. Whitespace is ignored in the OMeta grammar, which means you can structure the file pretty much however you like.
Our pattern gives us two variables, adds and subs, we need to do something with them. The -> operator designates the next curly brace surrounded region to be your desired C# code output. For this rule, we’re creating a new instance of our FileStats class, and passing our two variables into the constructor (converting them to ints at the same time).
At this point, run your side executable that recreates the generated parser. You should now be able to alter your test to use the FileStats rule instead of Number, and have it pass.
So what can we parse now? We’re able to get the additions and subtractions from a line such as 1 4 myFile.txt.
We still need to be able to get the filename back though, so onto our next test.
[Test]
public void ParsesFilename()
{
var result = Parse<string>("anotherFile.txt", x => x.Filename);
Assert.That(result, Is.EqualTo("anotherFile.txt"));
}
This test won’t compile until we create our Filename rule, so off to the ometacs with us!
We’ve now got an extra rule at the end of the file, Filename. I can’t stress this enough, watch your commas; OMeta#s errors are poor, and something like that can trip you up.
Our new rule looks complex, but is pretty simple. It uses the built-in LetterOrDigit rule, which matches a single letter or digit. We suffix that call with a + which (like regex) matches one or more instances; that match is then stored in a name variable. Following that is a single full-stop. Finally, another LetterOrDigit+, which matches our extension. We then concatenate those strings in our C# output.
Again, recompile your parser. Now your test should pass. It simply matches any filename with an extension (room for improvement here!).
Now we can bring the two together, so we can parse a whole line. Next test!
[Test]
public void ParsesFullFileLine()
{
var result = Parse<FileStats>("3\t8\tmyFile.txt\r\n", x => x.FileStats);
Assert.That(result.Additions, Is.EqualTo(3));
Assert.That(result.Subtractions, Is.EqualTo(8));
Assert.That(result.Filename, Is.EqualTo("myFile.txt"));
}
We need to update our FileStats class so it supports the filename.
public class FileStats
{
public FileStats(int additions, int subtractions, string filename)
{
Additions = additions;
Subtractions = subtractions;
Filename = filename;
}
public int Additions { get; private set; }
public int Subtractions { get; private set; }
public string Filename { get; private set; }
}
Our grammar file is going to undergo a bit of refactoring too. We’re covered by tests, so why shouldn’t we? Our FileStats rule doesn’t really describe the whole line it should be matching. Really what we should have is a LineStats (that’s what our FileStats currently is) that just matches the numbers, then a FileStats that matches the numbers and the filename.
So what we’ve done here is redo our FileStats rule as LineStats, which instead of creating an instance of the FileStats class, just returns an array of it’s two matches { adds, subs }; this encapsulates that particular bit of behavior. Next we’ve created our new FileStats rule, which calls our LineStats rule and captures the matches. It then matches a single tab, and the filename using our Filename rule, followed by a NewLine; these are then combined and pushed into the constructor for our FileStats class. We’ve defined NewLine at the end, and it simply matches the \r\n characters.
Rebuilding and running that test should now give success. We’re now able to completely parse a diff line. Only one thing left to do, parse multiple lines together.
[Test]
public void CanParseMultipleLines()
{
var result = ParseList<FileStats>(
"3\t8\tfile.txt\r\n" +
"5\t1\tanotherFile.txt\r\n", x => x.FullFile);
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result[0].Filename, Is.EqualTo("file.txt"));
Assert.That(result[0].Additions, Is.EqualTo(3));
Assert.That(result[0].Subtractions, Is.EqualTo(8));
Assert.That(result[1].Filename, Is.EqualTo("anotherFile.txt"));
Assert.That(result[1].Additions, Is.EqualTo(5));
Assert.That(result[1].Subtractions, Is.EqualTo(1));
}
Our final test is a bit longer than the others, but it’s simple. We pass in some multiline input, and then assert that we have two FileStat objects, and that they’re correctly formed.
For this test we’re using another helper method, which returns a list of rule matches.
Again, this test will fail because we haven’t defined the FullFile rule. So again to the ometacs file.
FullFile = FileStats+:files -> { files },
All we do this time is add this rule to the top, which uses the + operator to match multiple FileStats rules. They’re then returned as they are so we can convert them to an enumerable.
That’s it, recompile and run, you should now be parsing full diff outputs with ease!
As you can see OMeta# is pretty easy, and it’s very easy to test drive. There are a few quirks to doing it that way (such as having to create the compiled grammar before your test will run), but it’s a lot smoother than I expected. The grammars are quite simple too, and there are some shortcuts you can take which help make things easier. The SharpDiff grammar currently stands at just 49 lines, and it’s capable of parsing 90% of the standard git output, not bad!
I’ll just introduce you to another little bit of syntax that can make things simpler. Our Filename rule matches filenames with extensions, but it doesn’t match filenames without! That could be a problem; however, instead of creating a new rule for this, you can create multiple patterns for a rule. Each pattern will get evaluated if the one before it fails.
So we can update our Filename rule to the following, which tries to match a filename with an extension, and if it can’t do that, it then tries without an extension.
SharpDiff is a library for parsing the output of various diffing tools. It’s primary purpose is to reduce the time spent by SCM UI developers in handing diff output.
Why SharpDiff
I’ve got a few tools in mind that require parsing of diff files. I figure it’s a pretty common thing for SCM UI developers to have to do, so I thought i’d put it out for others to use.
Parsing your first diff
The implementation is not concrete yet, but the current (easiest) way to parse a diff file is as follows.
From there you have a compiled version of your diff document. Intellisense will be your friend here, but basically you have a Chunks collection, and a Files collection.
The Wikipedia article on Diffs is worth a read if you’re interested. In short though, chunks are formed as a chunk header containing the affected lines, and the lines themselves.
@@ -1,3 +1,6 @@
This is a small text file
+that I quite like,
with a few lines of text
-inside, nothing much.
The chunk header is @@ -1,3 +1,6 @@. The -1,3 describes the affected lines in the original file, the first number (ignoring the minus) is the start line, and the second number is the total of context lines plus subtraction lines. The second range (+1,6) is in the new file, and that starts on the first line, and has six affected lines; in this case it’s all the context lines plus all addition lines.
All the other lines are the actual changes themselves. A line prefixed with a + is an addition line, a line prefixed with a - is a subtraction line, and lines prefixed with a space are context lines. Context lines are used for aligning the changes in the document. They’re also useful for determining if the document has changed since the diff was created.
In the context of SharpDiff, your Chunks collection contains an instance of a Chunk for every part in the diff that resembles the above. Each chunk has the range info for the original and new file, and a Lines collection of each line.
Early Days
WARNING: SharpDiff is still in early development. I wouldn’t recommend using it in production code yet, as the parser is severely limited, and there’s next to no error handling.
The biggest flaw is that it only supports the standard git-diff output, and doesn’t handle many special circumstances.
It does support:
Standard git-diff header
Index extended header
Chunk header
Chunk lines (added, removed, and contextual)
No newline at end of file
Multiple chunks per diff
Multiple diff per input string
It doesn’t support:
Other extended headers
Formats other than git-diff
Example
As quick example of what you could use this library for, here’s a screenshot of a git-diff application:
If you’re interested in helping with SharpDiff, then let me know. All comments, suggestions, and contributions are welcome. Feel free to contact me either through github, or my e-mail.
I said previously in my Brace Matching post that I want to try to document some of my findings while working on BooLangStudio. Well this is my second post on the subject.
When you’re implementing a custom language in Visual Studio, there’s a very good chance that you’re going to want to handle indentation slightly differently to the defaults. Every language has it’s own rules, after all.
Most of the resources I found online we’re pretty poor for how to get this working. Most people we’re pointing to overriding OnCommand in your derived Source class, or implementing some interop interface (i.e. IVsLanguageLineIndent). I could get none of these working. OnCommand never got raised, and the interfaces we’re useless.
I tried a few different methods for handling indentation, but none of them worked very well. I tried capturing the enter key press, but that didn’t work. Then I tried capturing alterations to the document, but those also got fired for navigating the document, so you’d press the up key and add a new line!
It ended up being pretty simple to implement, once I finally found the correct way to do it. I’ll cover how I did this for BooLangStudio below.
In your LanguageService, there’s a method you can override called CreateViewFilter which sets everything in motion.
Create yourself a class that derives from ViewFilter, and then return an instance of it from the overridden CreateViewFilter method in your language service.
Note: You need to make sure your project is configured to use Smart Indentation. If your project is complete enough to allow the user to customise this, then you’re fine. However, if not you can hard code this value yourself. In your language service there’s a GetLanguagePreferences method that returns all the preferences for your project. In that method you can set languagePreferences.IndentStyle = IdentingStyle.Smart, which is what I’ve done in BooLangStudio.
You’ll kick yourself for how easy this is. Now override the HandleSmartIndent method in your derived ViewFilter. That’s it really, in there you can access the Source object and do as you wish with smart indentation.
Taking BooLangStudio as an example, you can see in our BooViewFilter.cs that I delegate the work to a HandleSmartIndentAction. This is to make testing easier by having as little dependencies on Visual Studio as possible.
The Execute method of our HandleSmartIndentAction.cs class gets the caret location and passes it to an instance of a LineIndenter class, which determines (based on the previous line to the caret) whether the next line should be indented, or outdented.
So that’s how to implement Smart Indentation in your Visual Studio Extensibility project, and a little bit of implementation details of BooLangStudio.
Continuing on from my post about an alternative syntax for the non-disposable using statements, here’s a class I’ve been using lately. It serves as a wrapper around changing the colours in a console window. It’s not a difficult thing to do, it’s just a bit awkward because you have to maintain the original colour in a variable while you do your business.
It’s not too bad when you’re only doing it once, but when you start swapping colours all over the place, then it can become very noisy. So this is where my class comes into play. Using the same syntax I described in my previous post, I’ve wrapped up the colour changing in a helper method that takes an Action delegate. This allows you to write much more intention revealing code.
There’s been a bit of discussion of late about using statements, and how they’re more often being used for purposes other than just releasing resources. As always, there are those people who think it’s a flagrant abuse of a feature and shouldn’t be done, then there are those that like it. I’m in between. I do like what the using statement gives us, but I also think it is a bit of an abuse.
The “traditional” usage of the using statement can be found quite often in the land of files and streams. Take the following example, which opens a file and then closes it when it drops out of the using scope.
using (var stream = File.OpenRead("myFile.txt"))
{
// do something with the file
}
Examples of the alternative usage can be found all over the place, but Rhino Mocks is one that’s close to my heart. Here’s from the record/replay syntax, anything in the scope of the using is recorded, and once it drops out of scope it’s no longer in record mode.
using (mocks.Record())
{
Expect.Call(customer.Address)
.Return("123 Rester St");
}
Again, I do like what the using statement gives us outside of releasing resources (I’m not disputing it’s usefulness there). However, I think the using keyword itself adds noise and clouds intention.
With the adoption of 3.5, I’ve started using an alternative syntax instead of usings. Actions and anonymous methods to the rescue.
Scope(() =>
{
// do something within this scope
});
It’s a little bit more noisy in the compiler satisfying department, but because you have full control over naming, you can reveal intention more. No more unclear “using”.
So how does it work? Simple really, the method takes an Action delegate, which it the executes almost immediately. I say almost, because you can execute code before and after the execution. That gives you the benefits of the using statements wrapping ability.
public void Scope(Action action)
{
// do something before
action();
// do something after
}
Some more examples:
File.OpenRead("myFile.txt", file =>
{
// do something with the file
});
We had an issue recently where NHibernate was performing very poorly on our production server, but not on our developer machines or our test server. I investigated the issue and narrowed it down to two symptoms.
Symptom 1:
Very poor performance. I’m talking 10+ seconds per page load, with no more than 5 queries being executed by NHibernate.
Symptom 2:
Empty log files. None of our log files had any data in on the live server, but they did on our other systems.
I decided to investigate the second symptom first, as it may be causing the first (ends up it was).
Firstly, I noticed that our logging was set to DEBUG. Must’ve been a leftover from when we first deployed NHibernate, very sloppy, I know. So I reset that to WARN, but it had no effect.
When files aren’t being written to, you should always check the directory permissions. Low and behold, it was a permissions problem. Our test server had different users allowed to write to the Logs directory than our production server. I granted the same users access, NETWORK SERVICE and IUSR_MACHINENAME in our case.
After I recycled the IIS worker processes, we were flying again. We’re back to having < 1sec page loads.
This is pure speculation, but what I believe was happening is this: Logging was set to DEBUG, so it was logging a lot. With each log call, the logger was failing to get write access to the files and throwing an exception, that exception would probably have propagated a bit too. The combination of the sheer amount of data being written to the log, and an exception per log call, were responsible for the severe slowdown.
So in short: Always make sure NHibernate has write access to its own log directory!
I’ve been meaning to write up some of my experiences developing for Visual Studio while I’ve been working on BooLangStudio, but I can never seem to find the time; either that, or when I can I’m not confident enough in what I’m doing to put it out here as a valid resource.
I’ll start small, here’s a quick guide to how I’ve implemented brace matching using the managed Visual Studio extensibility SDK.
What exactly is brace matching? Brace matching is where paired characters are highlighted when one or the other is selected in the editor.
There are a couple of things you need to consider when you implement brace matching.
Firstly, there are different types of braces that can be matched (depending on your language). Taking C# as an example, the brace matching works with parentheses (()), box brackets ([]), braces ({}), and chevrons (<>). These all need to be paired independently, as to avoid matching an open parenthesis with a closing brace.
Secondly, the matching has to work bi-directionally. The user can place their caret at either side of the bracket pair, and the highlighting should know where both sides are regardless.
LanguageService implementation
A lot of the legwork of implementing a language in Visual Studio is done in the LanguageService, and brace matching is no exception. You should already have a ParseSource method in your LanguageService; this is where we’re going to work.
The ParseSource method has a ParseRequest parameter, which exposes a Reason property. When this property is set to MatchBraces, that’s when we need to do our processing.
public override AuthoringScope ParseSource(ParseRequest request)
{
if (request.Reason == ParseReason.MatchBraces)
{
// match braces here
}
}
What needs to be done is pretty simple: Parse the open document and find the partner to the brace that the caret is on.
In BooLangStudio I’ve implemented this in the following fashion:
Run the document through a BracketPairFinder, which creates a list of bracket pairs in the document
Get the index of the caret (baring in mind you have to translate between the Request’s Line and Column, and an actual string index)
Find the pair that the caret is positioned at
Get the opposite bracket from the pair
Get the Line and Column of the opposite bracket
It’s upto you how you implement the above steps, as long as you end up with the opposite bracket to the one you started with. I’ll illustrate using the BooLangStudio source.
public override AuthoringScope ParseSource(ParseRequest request)
{
if (request.Reason == ParseReason.MatchBraces)
{
// find all pairs
var bracketFinder = new BracketPairFinder();
var bracketPairs = bracketFinder.FindPairs(request.Text);
// get index of caret from source text
Source source = languageService.GetSource(request.View);
int indexOfCaret = source.GetPositionOfLineIndex(request.Line, request.Col);
// find the partner to the bracket at the caret
int? partner = bracketPairs.FindPartnerIndex(indexOfCaret);
if (partner != null)
{
// tell Visual Studio about the pair
}
}
}
The Source class has a helpful GetPositionOfLineIndex method, which translates between a Line and Column to a single string index. Very handy!
Once you’ve got your indices, we need to inform Visual Studio of our findings. You do that by setting the request.Sink.FoundMatchingBrace to true, then calling the MatchPair method on the Sink. You need to pass two TextSpan instances to the MatchPair method; the first is the left brace, and the second the right.
public override AuthoringScope ParseSource(ParseRequest request)
{
if (request.Reason == ParseReason.MatchBraces)
{
...
if (partner != null)
{
// tell Visual Studio about the pair
request.Sink.FoundMatchingBrace = true;
int nextLine, nextCol;
source.GetLineIndexOfPosition(partner.Value, out nextLine, out nextCol);
request.Sink.MatchPair(
new TextSpan
{
iStartLine = request.Line,
iEndLine = request.Line,
iStartIndex = request.Col,
iEndIndex = request.Col
},
new TextSpan
{
iStartLine = nextLine,
iEndLine = nextLine,
iStartIndex = nextCol,
iEndIndex = nextCol + 1
}, 0);
}
return new AuthoringScope(); // replace with your implementation
}
}
The Source class has another helpful method: GetLineIndexOfPosition method, which translates back to Line and Column from a single string index.
Finally, just return an empty AuthoringScope, as it isn’t used as part of this parse request.
That’s it! You should have now successfully implemented brace matching. You may need to tweak the TextSpan indexes depending on your parser implementation, but it shouldn’t be far wrong.
BooLangStudio implementation
If you’re interested in seeing how I handle the brace parsing, I may cover that in a future post. However, you can find all the source in my github BooLangStudio fork. Some interesting one’s in particular are:
StringWalker - A simple string parser used by the BracketPairFinder, which walks over a string maintaining a code-aware state. Basically, it lets you know whether it’s currently inside a bracket or string literal. Handy for not matching characters inside strings.
A couple of people have already covered this already, specifically Bobby Johnson, Matt Hinze, and Zachariah Young. I figure I should say something on it anyway.
I’ve adopted a project from Jeremy Miller that I think has the potential to be a really useful tool. It’s called Fluent NHibernate, and it’s primarily a fluent API for mapping classes with NHibernate.
We’re all well aware how awesome NHibernate is, but I think we all also have a bit of a dislike for the amount of XML you need to write to get your classes mapped; not only that, but also how the mappings are distinctly separate from the rest of your application. They’re often neglected and untested. One of the core tenets of the project is that we need a more succinct, readable, and testable way of writing your mappings.
Firstly, you’ll note that there is a marginal reduction in lines of code, but that’s not what we’re particularly striving for. Instead we’re intent on reducing the verbosity and noise of the code. This manifests itself in a convention over configuration design for the API, where we choose the most common setups and use those as the default. For example with the id element in the hbm file, you’re required to specify what the generator type is; however, in our fluent API we check the type of your identity property and decide what generator we should use. Int’s and longs default to identity, while GUIDs use the guid.comb generator. You can change these explicitly, but when you are using the default, it greatly reduces the verbosity of your mapping.
Testability
Another one of our goals is to make your mappings more robust. I imagine most people have had the problem where you’ve renamed a property and not updated the mapping file; due to there being no compile time validation, the only way to catch these mistakes are at run time (hopefully you had tests to cover that!). With the way our API is designed, you use the actual properties on your classes to create the mapping, so there’s nothing to forget. If you rename a property, your IDE will either rename the property in the mapping, or fail at compilation.
We also want to help you verify that your mappings are set up properly, not just syntactically valid. So to make your integration tests a bit easier, we’re providing an API for testing your mappings.
[Test]
public void VerifyCustomerSaves()
{
new PersistenceSpecification<Customer>()
.CheckProperty(x => x.Name, "James Gregory")
.CheckProperty(x => x.Age, 22)
.VerifyTheMappings();
}
Behind the scenes the PersistenceSpecification creates an instance of your entity, then populates it with the values you specify through the CheckProperty method. This entity is then saved to the database, then reloaded through a separate connection. The returned entity is then compared to the one originally saved, and any differences fail the test. It’s a fairly standard integration test, except we’ve taken the time to write all the wiring up that needs to be done, so you don’t have to.
The Framework
We’re working towards our first official release, which will have a fairly solid implementation of the API. Once that’s out in the wild, we’re going to focus on our Framework.
Our framework is a layer that sits on-top of the API to provide an even better experience. We’re looking to integrate with your favorite container, which will reduce the code you need to write to integrate NHibernate into your system. Then we’re going to tackle extensible conventions, which will allow you to specify your own implied conventions for your application. For example, if you’re always going to call your identifier “ID”, then why should you have to specify it every time? You shouldn’t!
Development is progressing at a nice pace, and I expect we’ll be able to get our first release out within the next few weeks. The testing API hasn’t been kept quite as up to date as the main API, but we’re working on that too. It’s open-source, so suggestions and patches are welcome.
It’s been a long time since I’ve written anything about BooLangStudio. Let me assure you that the development is still very much underway, and we’re steadily working towards our first release.
To fill you in a bit on what’s been going on. Jeffery Olson has completely rewritten the syntax highlighting to use the more flexible Boo.Pegs lexer, which allows us to overcome some of the obstacles we were facing with the traditional Boo lexer. There were limitations to what we could do with the traditional lexer, without modifying the Boo source. This was leading us down a road we weren’t keen on, one of maintaining a fork of the Boo source that contained our specific changes. With the move to the Boo.Pegs lexer, we’re free of this scenario!
Justin Chase has been tackling various issues all over the place. Several related to the build process, specifically how the Boo binaries are located on your machine, which has been a bit of a problem when there’s multiple developers working on a project whom both have Boo installed in a different location. He’s also been spiffing up the interface by adding some much needed icons to the projects.
Torkel Ödegaard has been working various issues throughout the project. He’s created a properties dialog for the project which allows you to alter the usual settings for your project, assembly name, default namespace etc… He also managed to get debugging working, which is awesome.
Then there’s me, I’ve been working on the intellisense capabilities. We’ve had intellisense support from day one, but it’s never been very extensive. For a long time it only worked on the current file you had open.
The entire solution is now processed behind-the-scenes to provide intellisense. Import statements are now recognised and imported types appear in the local scope intellisense. Any assemblies or projects that are referenced have their top-level types and namespaces included in the intellisense too.
Additionally I’ve been working on improving the method suggestion logic, which was initially very flakey. It boils down to being able to transform the current line into the desired type, which can be difficult in certain cases (StringBuilder().Append("Hello world").ToString(). for example).
I believe that about covers what we’ve been upto lately. Our biggest problem currently is that most of these features are implemented in separate branches, and haven’t yet been merged into our central repository. We’re working on this, and I reckon once we’ve done that we’ll be looking at doing a release.
Why aren't I working for my self? Sat at home enjoying a cup of tea and good music in the comfort of my own home, while enjoying working too 22 hrs ago