Professional Development on a MacBook Pro
Like a number of people in the Delphi Community ( Steve Trefethen, Dan Miser ), and the development community as a whole, I now do all my development on a MacBook Pro. I changed to an Apple machine six months ago, for a number of reasons:
- I needed a new laptop, and the MacBook Pro hardware is so goddamn thin, light, and good looking.
- I am becoming increasingly disillusioned by Windows, especially the new DRM stuff.
- I fancied trying out OSX, a change is as good as a rest and all that.
- Bootcamp and virtualisation allow me to use Windows XP on an Apple laptop.
- The web is freeing us from being locked into particular Operating Systems.
As a professional developer, most of my clients run Windows, and most of my native development is done using Visual Studio 2005 or Delphi 2006, connecting to SQL Server or Oracle backends. I boot my laptop into Windows to do this work, or use Parallels to run it with Mac OSX (when I got my MacBook Pro I got it with 3G of RAM so Windows runs pretty fast inside OSX). Increasingly though I am doing web development, which means I can do much more work inside just OSX.
The more I have used OSX, the more I prefer it to Windows, so I have put together this list of tools and hints for doing professional development on OSX:
Using Bootcamp
- Steve has got an excellent list of the keyboard mapping for Windows
- Parallels allows runs Windows (from a bootcamp partition) in OSX
- As does VMWare
Using OSX
One of the things there first confused me using OSX is the link between a visual Window and a running program. Having used Windows for the best part of 15 years, I had the stong mental link between having a open window and having a program running. OSX is much more like the systray in a way. In most instances, a program carries on running even if all its windows are shut. At first this seems odd, but now I am actually used to Mail and iCal running in the background, collecting mail and showing me reminders, without having windows cluttering up the place or being minimised. Also the dock changes icons depending on status of the program, so I can see that new mail is waiting, without requiring an open window to tell me.
I guess it is the unix heritage, but OSX seems much happier having loads of programs running a the same time, than Windows would (or maybe this laptop has just got too much memory).
Another change from the Windows way of thinking is about installation of software. In OSX an application is a directory with a name of BlahBlah.app. It contains all that is necessary to run. So drag and drop to install software, move things around as desired, and delete it when no longer wanted. Only software that needs to make system changes needs to have install scripts.
You need to install Quicksilver for launches programs, and more advanced voodoo. Have a look at some of the screen casts at The Apply Blog, a good beginner one is here. You won’t believe for a powerful bit of software it is.
Another thing worth getting your head around is this idea of OSX services. These are simple ‘services’ that are provided to all programs running, accessible of the programs menu. While there are some useful ones built in, there really come into the own where you start changing them using Service Scrubber and This Service. See some examples from John Gruber about writing your own scripts and making them available to all apps (although I’d recommend using Ruby over Perl ;-).
Web Development
It goes without saying that you should be using Firefox with Firebug for web development. Day to day browsing I use Camino which is the firefox engine inside a more OSX integrated and looking program.
- Textmate is a great editor, as most people know, but if you are doing any JavaScript development make sure you get the JS Tools bundle. Having your script ‘lint’ed everything you save is a great productivity improver. Also get the GetBundle bundle to make it easy to manage extensions and find new ones. Blog writing is easy with the built in markdown support.
- Eclipse (especially with the new CodeGear betas running inside Eclipse ) has plugins to do pretty much everything (although I still use Textmate most of the time).
- Anybody know of a Fiddler equivalent for OSX ?
Subversion
Does anybody use anything else anymore ? I use the tigris plugin scplugin to have subversion integration with the finder. It is not quite as good as tortoise. There is also Svnx which is ok, but I don’t find the UI all that intuitive.
Database Access
For connecting to Oracle, I use the free SQL Developer which has a native OSX version. Most of the open source databases have native clients (e.g. Postgres), but otherwise I use Aqua Studio v4.7 which is free. One of the nice things is most of the tools are Java based, so if you can find a JDBC package for your database, chuck it in /Library/Java/Extensions and then all the Java apps on your machine have connectivity to it (I have found ones for SQL Server, Oracle, MySql and Postgres).
Other Languages / Database Engines
- Anything available in the Open-source world, if not explicitly released for OSX, is normally available on DarwinPorts or Fink, projects to make linux/unix software compilable on OSX (which is a unix variant behind the scenes), and easily installable handling all the dependancies etc.
Are there any useful development tools or hints I have missed ? Let me know and I’ll try and keep a development bias list here or on this page.
And if your still using boring old Windows, come and join the revolution !
Useful Delphi code
Just realised I haven’t written a blog post for 2.5 months, and I’ve just got back from a lunchtime surfing session, so I’m not really in the mood for real work. I thought I’d post some of my basic Delphi library code I use time and again in the hope it is of some use to other Delphi developers. Do what ever you want with it, I don’t guarantee its without bugs, but most of it has been used in production code for quite a long time.
The full file with all the implementation can be found here: Source
The following are useful for conditional logic for development vs production code, debugging, etc.:
procedure BreakpointInIDE;
function CurrentExceptionMessage: string;
function IsRunningUnderIDE: boolean; inline;
The following function is useful for preventing the unsitely errors that can occur as a Delphi Application throws Exceptions as it shuts down. While ideally this should never happen, previous exceptions can leave forms in a have constructed state, and the TApplication’s attempt to clean up them then causes horrible dialogs frightening users. Even the IDE has been known to do this. The code hooks the various Exception raised pointed in the RTL to silently ignore the errors. If running inside the IDE, it will also post a message to the event log, and cause the IDE to breakpoint to highlight there is an issue.
procedure HookShutdownToHideExceptions;
Helper Classes for TList, TStream and TStrings. In particular I use the ID accessors of TStrings a lot when putting records into UI elements. Makes it very easy to look up the ID of the record in a combo box, for example. It uses the Object field in a list item to store the ID. Helpers require at least Delphi 2006.
TListHelper = class helper for TList
procedure FreeSelfAndContainedObjects;
end;
TStringsHelper = class helper for TStrings
private
function GetID(Index: Integer): ID;
procedure SetID(Index: Integer; const Value: ID);
function GetDescFromID(ID: ID): string;
function GetIDFromDesc(const Desc: string): ID;
function GetObjectByString(text: string): TObject;
public
function AddPair(const Name, Value: string): integer;
function AddID(const Desc: string; PrimaryKey: ID): Integer;
function AddObj( const Name, Value: string; Obj: TObject ): Integer;
function ContainsKey( const Key: string ): boolean;
function ContainsString( const Text: string ): boolean;
function ContainsValue( const Value: string ): boolean;
procedure FreeSelfAndContainedObjects;
function IndexOfID( ID: ID ): integer;
property DescFromID[ID:ID]: string read GetDescFromID;
property IDFromDesc[const Desc: string]: ID read GetIDFromDesc;
property IDs[Index: Integer]: ID read GetID write SetID;
property ObjectByString[ text: string ]: TObject read GetObjectByString;
end;
TStreamHelper = class helper for TStream
public
function AsString: string;
function RewindToStart: TStream; // Returns self to allow easy chaining of calls
procedure WriteString( const Text: string );
procedure WriteStringAndBreak( const Text: string );
end;
The following two classes are 2 example simple container objects based on records rather than classes. The nice thing is they don’t have to be memory managed like classes do, the compiler will handle all of that for you. They are not as fully featured as string lists, but are useful for simple tasks. They are also reasonabilly efficient, as the dyanmic arrays they use are copy by reference. The only thing to remember is to use the Clone method if you actually want a whole new copy of the data. I have about 4 or 5 different variants of these to store ID => Name, Name => Variant, etc, but you get the idea.
TSimpleStringList = record
private
FItems: array of string;
function GetCount: integer; inline;
function GetItem(index: integer): string;
procedure SetItem(index: integer; const Value: string);
public
function Add(const line: string): integer;
procedure Clear;
function Clone: TSimpleStringList;
function Contains(const s: string): boolean;
function IndexOf(const s: string): integer;
function IsMissing(const s: string): boolean;
property Count: integer read GetCount;
property Items[index: integer]: string read GetItem write SetItem; default;
end;
TSimpleStringListHelper = record helper for TSimpleStringList
public
function AsNewStringList: TStringList;
function StringsCommaSeperated: string;
end;
TSimpleDictionary = record
private
FKeys: array of string;
FValues: array of string;
function GetCount: integer; inline;
function GetKey(index: integer): string;
function GetValue(const Key: string): string;
function GetValueByIndex(index: integer): string;
procedure SetKey(index: integer; const Value: string);
procedure SetValue(const Key, Value: string);
procedure SetValueByIndex(index: integer; const Value: string);
public
function Add(const Key, Value: string): integer;
function AsNewStringList: TStringList;
procedure AddStrings(Strings: TStrings);
procedure Clear;
function Clone: TSimpleDictionary;
function ContainsKey(const Key: string): boolean;
function IndexOfKey(const Key: string): integer;
function IndexOfValue(const Value: string): integer;
property Count: integer read GetCount;
property Keys[index: integer]: string read GetKey write SetKey;
property Values[const Name: string]: string read GetValue write SetValue; default;
property ValueFromIndex[ index: integer ]: string read GetValueByIndex write SetValueByIndex;
end;
TSimpleDictionaryHelper = record helper for TSimpleDictionary
public
function KeysCommaSeperated: string;
function SemicolonDelimitedString: string;
procedure LoadFromSemiColonDelimitedString( Text: string );
procedure LoadFromStringList( StringList: TStringList );
function Text: string;
function ValueOrDefault( const Key: string; const Default: string ): string;
end;
Here is a simple subclass of Exception to allow Assertions on any Exception derived object. I tried to do this via a helper class, but the ‘self’ object in a class level helper called on a class does not appear to be a valid reference, so I couldn’t find a way to get it to work. Looking through the code to Indy inspired this approach to using Exceptions, I never use the raise statement anymore.
EBase = class( Exception )
class procedure Assert( Test: boolean; const Msg: string );
class procedure Throw( const Msg: string ); virtual;
end;
The last function is function ShowHourglass: IUnknown; This can be called in a UI function to make a form show a cursor, and when the function exits (normally or after an exception) be sure the cursor is returned to its previous state. The return value (IUnknwon) does not need to be stored, behind the scenes Delphi will store it anyway and make sure the compiler puts a hidden Try ... Finally in at the end to make sure it is cleaned up. The cleaning up of the interface is what we use to reset the cursor.
Anyway hopefully this of some use to somebody. Happy New Year to everyone, here’s to a good year from CodeGear.
ATOM / RSS and Comments
After all the effort of changing Weblogs, I’ve realised that Typo ‘out of the box’ does not support comments added to its syndication streams. I’ve hacked around in the code and managed to get something working. Rails XML helpers make this stuff pretty easy.
Weirdly there does not seem to be an agreed format for doing comments in ATOM at all, where as RSS has various different comment tags. I’ve used wfs:commentsRss and slash:comments nodes in both formats, with the comments just in Rss 2. The Feed Validator seems happy.
Anyway the following work well with my aggregator, Rss Bandit, as well as IE 7. If anyone has any problems with other aggregators, can you let me know. Thanks.
Local feeds: