Category Archives: RemObjects

Using RemObjects Train for Automated Testing and Building

Train
I was recently tasked with coming up with an automated build process for one of my projects. Given the available free and commercial tools on the market I decided to give RemObjects Train a try. Train is an Open Source project from RemObjects Software (one of their many Open Source offerings). Benefits of Train include:

  • It is Open Source
  • It has a simple, extensive, extensible API
  • Scripts are friendly to version control (based on ECMAScript rather than, say, XML)
  • The tool is simple, light-weight
  • It’s free – as in beer

My requirements for the build process were limited (one of the reasons I wanted a free, simple tool rather than a capable commercial tool such as FinalBuilder or Automated Build Studio):

  1. Support for running and failing based on unit tests (using MSTest)
  2. Support for building a release configuration of several .NET assemblies
  3. Support for packaging assemblies using Inno Setup
  4. Configurable version number used for assemblies and installer

Train ended up being a very elegant and useable solution. The scripts are easy to create and maintain – I used Sublime Text 2 with its JavaScript syntax highlighting.

In the end I created two scripts: RunTests.train and BuildRelease.train. By naming my scripts with a .train extension I was able to associate them with Train.exe and can run my unit tests and build releases with a simple double-click.

My RunTests script looks like this:

//rebuild unit tests
msbuild.rebuild("../ExchangePurge.UnitTests/ExchangePurge.UnitTests.csproj", { configuration: "Release" });
msbuild.rebuild("../ExchangeSync.UnitTests/ExchangeSync.UnitTests.csproj", { configuration: "Release" });

//function for running MSTest unit tests, detecting failures
function runTest(testAssemblyPath) {
	var mstestPath = "C:/Program Files/Microsoft Visual Studio 11.0/Common7/IDE/MSTest.exe";
	shell.exec(mstestPath, "/testcontainer:" + testAssemblyPath);
}

//run unit tests
runTest("../ExchangePurge.UnitTests/bin/Release/ExchangePurge.UnitTests.dll");
runTest("../ExchangeSync.UnitTests/bin/Release/ExchangeSync.UnitTests.dll");

The script is pretty straight-forward. It builds release versions of my unit test assemblies using the msbuild API provided by Train (API documentation can be found here). It then defines a simple function – runTest – for running unit tests with MSTest.exe using the shell API. Finally, the function is called for each unit test assembly.

My BuildRelease script looks like this:

//run unit tests
run("RunTests.train");

//get version info from VersionInfo.ini
var versionInfoIni = ini.fromFile("VersionInfo.ini");
var appVersion = versionInfoIni.getValue("VersionInfo", "AppVersion", "1.0");

//update the assembly versions for each .NET project using the above version info
msbuild.updateAssemblyVersion("../ExchangePurge/Properties/AssemblyInfo.cs", appVersion);
msbuild.updateAssemblyVersion("../ExchangeSync/Properties/AssemblyInfo.cs", appVersion);
msbuild.updateAssemblyVersion("../ExchangeSyncAdmin/Properties/AssemblyInfo.cs", appVersion);

//rebuild each .NET project
msbuild.rebuild("../ExchangePurge/ExchangePurge.csproj", { configuration: "Release" });
msbuild.rebuild("../ExchangeSync/ExchangeSync.csproj", { configuration: "Release" });
msbuild.rebuild("../ExchangeSyncAdmin/ExchangeSyncAdmin.csproj", { configuration: "Release" });

//export environment variable for InnoSetup to use for app version
export("ES_AppVersion", appVersion);

//build an InnoSetup installer
inno.build("Installer.iss", { });

//copy setup.exe to folder for this version
folder.create("Output/" + appVersion);
file.copy("Output/setup.exe", "Output/" + appVersion);

This script is a bit more complex but still pretty easy to understand. It starts by using the Train API to run the RunTests script. Because of the nature of MSTest.exe and Train.exe, if the RunTests.train script fails then the BuildRelease.train script will be aborted.

The script then uses Train’s ini API to read a configurable version from VersionInfo.ini. Next, the msbuild API is used to both update the assembly version information and build release versions of each assembly.

Next, I used the Train API to export an environment variable – ES_AppVersion – with the version information read earlier from the INI file. My Inno Setup script uses ISPP, which is installed by default if you download an Inno Setup QuickStart Pack. At the top of my Installer.iss Inno Setup script I used the following code (thanks to Carlo from RemObjects):

#define MyAppVersion GetEnv("ES_AppVersion")
#if MyAppVersion == ""
#define MyAppVersion "1.0"
#endif

And then further down use the MyAppVersion ISPP macro:

[Setup]
AppVersion={#MyAppVersion}

This bit of ISPP macro code will make use of the ES_AppVersion environment variable set by the BuildRelease.train script to define the installer’s version.

Finally, the last lines of the BuildRelease.train script use the folder and file Train API’s to copy the created installer to a folder named after the version read from the INI file.

So far I’m very happy with Train as a simple, capable tool for automated testing and building. If you’d like to read more about Train you can refer to Marc Hoffman’s post on the project as well as the official documentation.

Advertisements

Objective-C Literals with Data Abstract

Xcode 4.4 introduced Objective-C Literals which I think are great. Objective-C Literals are a collection of language enhancements that make it possible to do many common practices and patterns in Objective-C by writing less code. This includes boxing numbers, working with arrays and working with dictionaries. For instance, here’s some sample code for writing and reading an array without using Objective-C Literals:

[array setObject:@"one" atIndexedSubscript:0];
[array setObject:@"two" atIndexedSubscript:1];
[array setObject:@"three" atIndexedSubscript:2];

NSLog(@"%@", [array objectAtIndex:0]);
NSLog(@"%@", [array objectAtIndex:1]);
NSLog(@"%@", [array objectAtIndex:2]);

Here is the same code, now using the new enhancements for working with arrays:

array[0] = @"one";
array[1] = @"two";
array[2] = @"three";

NSLog(@"%@", array[0]);
NSLog(@"%@", array[1]);
NSLog(@"%@", array[2]);

Xcode even includes a refactoring called Convert to Modern Objective-C Syntax that will help you convert most of your code to this newer format.

Data Abstract, a multi-platform library for data access by RemObjects Software, also supports this new syntax when dealing with rows of data. The syntax was added to Data Abstract, according to Marc Hoffman, right after the Objective-C Literals support was announced at WWDC. Here is an example of working with a row of data before this change:

[row setValue:@"one" forKey:@"firstColumn"];
[row setValue:@"two" forKey:@"secondColumn"];
[row setValue:@"three" forKey:@"thirdColumn"];

NSLog(@"%@", [row valueForKey:@"firstColumn"]);
NSLog(@"%@", [row valueForKey:@"secondColumn"]);
NSLog(@"%@", [row valueForKey:@"thirdColumn"]);

And here is the newer syntax:

row[@"firstColumn"] = @"one";
row[@"secondColumn"] = @"two";
row[@"thirdColumn"] = @"three";

NSLog(@"%@", row[@"firstColumn"]);
NSLog(@"%@", row[@"secondColumn"]);
NSLog(@"%@", row[@"thirdColumn"]);

Fantastic I think! Easier to write, easy to read, and a very consistent and familiar way to access data by subscript. However, there is no built in refactoring in Xcode to change your old DADataTableRow code to this new format.

Stuck doing this by hand? I think not!

You can migrate your code to the newer syntax using Xcode’s Find & Replace. The Find & Replace in Xcode supports two powerful features we’ll use here: Regular Expresion (Regex) patterns and being able to preview and reject changes one-by-one.

To get started, load up your project in Xcode. On the left, select the Search Navigator (or press ⌘3). At the top of the Navigator, select Replace if Find is selected. Click the magnifying glass with the down arrow to the left of the search box and click Show Find Options. Under Style, select Regular Expression.


To replace the instances of code where values are being written, use the following search pattern:

\[(.*) setValue:(.*) forKey:(.*)\];

And use the following replace pattern:

\1[\3] = \2;

To replace the instances of code where row values are being read, use the following search pattern:

\[(.*) valueForKey:(.*)\]

And the following replace pattern:

\1[\2]

Click the Preview button to preview changes first. You can use the sliders in the middle of the preview window to accept and reject individual changes.


Once you’ve reviewed the changes, click Replace and you’re done! I’m happy any time I can use Regular Expressions without ending up with more problems than I started with.

Using XtraRichEdit to Edit HTML in VCL Applications

One request heard frequently from users of DevExpress’s VCL controls is for an HTML editing control. So far this has been deferred by the folks at DevExpress. However, they do make a bang-up .NET control called XtraRichEdit which has great HTML editing functionality. Wouldn’t it be great if we could easily embed that control in a VCL application?

It turns out this isn’t too tough using RemObjects’ Hydra product. Hydra is a plugin framework for both Delphi and .NET that lets you mix and match both managed and native hosts and plugins, visual and non-visual. This means you can make use of .NET visual controls in Delphi applications and vice-versa.

Let’s take a look at how easy this is to do (hint – very easy).

You’ll need Visual Studio, Delphi, the DevExpress WinForms controls, and Hydra all installed in the same environment. While it’s possible to do this with Delphi and Visual Studio on separate machines, it’s beyond the scope of this post.

Create a new project in Visual Studio using the “Plugin Module” template in the “RemObjects Hydra” category. Afterward, use the “Add>New Item” dialog to create a new “Visual Plugin”. Add a RichEditControl to the new design surface and dock it in its parent.

This alone is enough to yield a plugin module that can be loaded in a Delphi application and display a working RichEditControl. However, there would be no way to access the richedit’s HTML text. For this we will need to use a custom interface. This is really easy to do.

Add the following interface declaration to the top of the code-behind:

    [Guid("4033C8A9-8A7C-4DE4-864C-B8F60EFFBDD7")]
    public interface IRichEditPlugin : IHYCrossPlatformInterface
    {
        string GetHtmlText();
        void SetHtmlText(string text);
    }

This will require using RemObjects.Hydra.CrossPlatform and System.Runtime.InteropServices. Both the Guid and descending from IHYCrossPlatformInterface are required by Hydra. Finally, implement the interface:

    public partial class RichEditPlugin : VisualPlugin, IRichEditPlugin
    ...
        public string GetHtmlText()
        {
            return richEditControl1.HtmlText;
        }

        public void SetHtmlText(string text)
        {
            richEditControl1.HtmlText = text;
        }

And that’s it for the .NET side of things.

On the Delphi side of things, create a new VCL forms project. Add a THYModuleManager, TPanel, TMemo, and two TButtons.

Click the Hydra menu followed by Import Interfaces from .NET Assemblies. Browse for the assembly created above and click Import on the dialog listing the IRichEditPlugin interface. Then, click the Hydra menu followed by Hydra Package Settings. Select “Build with Hydra Core Packages” and click OK.

Adding a minimal amount of code will get the RichEditControl showing:

procedure TForm2.FormCreate(Sender: TObject);
begin
  HYModuleManager1.LoadModule('RichEditModule.dll');
  HYModuleManager1.CreateVisualPlugin('RichEditModule.RichEditPlugin', FRichEditPlugin);
  FRichEditPlugin.ShowParented(Panel1);
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  FRichEditPlugin := nil;
  HYModuleManager1.UnloadModules;
end;

FRichEditPlugin is a private field declared as IHYVisualPlugin.

After you copy the assembly created in .NET (RichEditModule.dll in my example) to the same path as your debug executable, running the application will now show the Delphi application with the .NET RichEditControl embedded and fully functional.

Adding a bit more code will allow getting and setting the HTML text in the .NET RichEditControl using the text entered in the TMemo:

//Set button
procedure TForm2.Button1Click(Sender: TObject);
var
  RichEditPlugin: IRichEditPlugin;
begin
  if Supports(FRichEditPlugin, IRichEditPlugin, RichEditPlugin) then
    RichEditPlugin.SetHtmlText(Memo1.Text);
end;

//Get button
procedure TForm2.Button2Click(Sender: TObject);
var
  RichEditPlugin: IRichEditPlugin;
begin
  if Supports(FRichEditPlugin, IRichEditPlugin, RichEditPlugin) then
    Memo1.Text := RichEditPlugin.GetHtmlText;
end;

This new code will require adding the “_Import.pas” unit generated when you imported the .NET interface to your uses clause.

And that’s it. Press the run button and then try the Get and Set buttons along with editing text in the RichEditControl.

XtraRichEdit in a VCL App

This is pretty powerful stuff thanks to Hydra and the XtraRichEdit, and very easy to do! You can download the C# and Delphi source code here.

Delay Applying Updates in Data Abstract iOS Apps

Even though the code generated by the Data Abstract for Xcode template for iOS apps is mostly asynchronous, you may notice delays in the UI when applying updates to your server. If you check out the asyncRequest:didFinishApplyingChangesForTables:withErrors: method in DataAccess.m, you’ll see that, once an asynchronous request finishes, your data will be saved to the local briefcase file:

- (void)asyncRequest:(DAAsyncRequest *)request didFinishApplyingChangesForTables:(NSArray *)tables withErrors:(NSArray *)errors
{
	[self saveData];
	[self setBusy:NO];
...

This happens on the main thread and, given a large enough set of data, will cause your app to stutter.

Luckily this is pretty straight forward to address. Cocoa makes it easy enough to run code in a background thread, and that’s exactly what Alexander from RemObjects suggests:

-(void)saveDataInBackground {</pre>
    [self performSelectorInBackground:@selector(saveData) withObject:nil];
}

- (void)asyncRequest:(DAAsyncRequest *)request didFinishApplyingChangesForTables:(NSArray *)tables withErrors:(NSArray *)errors
{
    [self saveDataInBackground];
    [self setBusy:NO];
...

I’ve been using a form of the above solution for a while now and can confirm that things are running smoothly, even with a larger set of data. Alexander let me know that this would be addressed in the Xcode templates for a future DA update.

Offline Mode with Data Abstract for Xcode

Offline Mode Sample App

Data Abstract for Xcode makes it simple to support disconnected n-tier iPhone applications. The briefcase support is a snap, requiring only a few extra lines to be uncommented from the templates installed into Xcode by the Data Abstract installer.

I recently looked into adding support for an “offline” mode to a Data Abstract application I’ve been developing for the iPhone. Basically, the application should function whether the iOS device has an active network connection or not. If there is no connection, any changes to data should be cached until a network connection becomes available.

This ended up being pretty easy to do. I started by adding Reachability support to the application. You can read detailed instructions about how to do that on Stack Overflow.

Once Reachability support has been added, it’s trivial to alter the beginApplyUpdates code in DataAccess.m to only apply updates when a network connection is available:

- (DAAsyncRequest *)beginApplyUpdates
{
	if (!internetActive) {
		return nil;
	}
        ...
}

Then, modify the Reachability usage to apply any unsaved updates when a connection becomes available:

		case ReachableViaWiFi:
		case ReachableViaWWAN: {
			NSLog(@"The internet is working via WIFI.");
			internetActive = YES;
			//have we received data before?
			if (self.daTestTable) {
				//setup data adapter (to get a new connection)
				[self setupDataAdapter];
				//commit any pending changes to the server
				[self beginApplyUpdates];
			}

			break;
		}

The only tricky bit I found was the need to re-create the DARemoteDataAdapter after a network connection becomes available. This is because you may have an old connection that will cause an error if you try to reuse it.

You can download a sample application here. The archive includes a SQL script for generating the sample DB table.

Using RemObjects Olympia Server with a Custom Session Class

Today I started playing with Olympia Server for out-of-process session management. We have a RemObjects SDK service that can currently use either in-memory or in-database session state. The in-memory option is faster, but limits our pooling options.

The various session managers in RemObjects SDK are pluggable and swappable, so adding support for Olympia Server to our service was pretty straight forward. However, I quickly ran into a limitation when using Olympia Server for session management: if you make use of a custom TROSession descendant by handling CustomCreateSession (discussed here), your custom properties will not be available after they make the trip to Olympia Server and back.

After looking into the source for TROSession and TROOlympiaSessionManager, I was able to come up with a fairly simple solution that allows you to continue using a custom TROSession descendant while also making use of Olympia for out-of-process session management.

The key is to override both SaveToStream and LoadFromStream in your custom TROSession descendant. There, serialize and deserialize your custom property values using the Values name-value pair property found on TROSession.

procedure LoadFromStream(aStream: TStream; OnlyValues: Boolean = False); override;
procedure SaveToStream(aStream: TStream; OnlyValues: Boolean = False); override;

...

procedure TMySession.LoadFromStream(aStream: TStream; OnlyValues: Boolean);
begin
  inherited;
  MyProperty := Values['MyProperty'];
  MyStrings.CommaText := Values['MyStrings'];
end;

procedure TMySession.SaveToStream(aStream: TStream; OnlyValues: Boolean);
begin
  Values['MyProperty'] := MyProperty;
  Values['MyStrings'] := MyStrings.CommaText;
  inherited;
end;

That Values property is one of the properties that will be persisted round trip with Olympia, and will allow your service to continue working as-is while making use of both a custom session class and Olympia Server.

Domain Business Processors with Data Abstract for .NET

One of the nice things you can do out-of-the-box with Data Abstract for Delphi is have your server-side business logic segmented into separate classes for each logical data table. This technique is described here:

Strongly Typed DataTables and Business Helper Classes

I’ve recently started using Data Abstract for .NET, and love the new features it brings, such as DA Linq and DA SQL. While this per-data table business logic isn’t to be found, it’s pretty straight forward to cook up.

Here is an example base class that encapsulates the standard Data Abstract BusinessProcessor, and here’s a sample CustomerBusinessProcessor that ensures a name is specified.

Using this class, you can write your own descendants, and then new them up in your service’s implementation unit like so:

FProcessors := new List<DomainBusinessProcessor>;
FProcessors.Add(new CustomerBusinessProcessor(self.components));

The essential bit here is passing in the components collection to the constructor, which is passed on to the constructing call for the internal BusinessProcessor. This is necessary as it is how Data Abstract finds BusinessProcessors for a given service.

You can download a sample here, which uses the SQLite PCTrade DB that ships with Data Abstract.