Archive for July, 2007

Rails-style controllers for ASP.NET

Rob Conery has been putting together some great screen casts on SubSonic and his latest on generating controllers pointed out that ASP.NET doesn’t support the Rails-style http://site//controller/method style of execution.

This got me quite excited and I’ve put together a proof-of-concept web project that demonstrates mapping the path to controller methods using a IHttpHandler and reflection.

How it works

It registers the ControllerHttpHandler via the web.config:

<httpHandlers>
    <add path="/ctl/*/*" verb="POST,GET,HEAD" type="ControllerHttpHandler" />
</httpHandlers>

There is a very basic Controller abstract base class that just provides a simple way of accessing the context for dealing with request/response for now.

public abstract class Controller
{
    protected System.Web.HttpContext context;

    internal Controller(System.Web.HttpContext context) {
        this.context = context;
    }
}

We then have a test controller or two that implement from this with a couple of methods and the required constructor:

public class TestController : Controller
{
    public TestController(System.Web.HttpContext context)
      : base(context) { }

    public void Index() {
        context.Response.Write("This is the index");
    }

    public void Welcome() {
        context.Response.Write("Welcome to the TestController");
    }
}

Finally the magic that joins them up is the ControllerHttpHandler:

using System;
using System.Web;
using System.Reflection;

public class ControllerHttpHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context) {
        string[] parts = context.Request.Path.Split('/');
        if (parts.Length < 4) {
            context.Response.Write("No controller & member specified");
            return;
        }

        string controllerName = parts[2];
        string methodName = parts[3];
        Type potentialController = Type.GetType(controllerName);
        if (potentialController != null && potentialController.IsClass && potentialController.IsSubclassOf(typeof(Controller))) {
            MethodInfo potentialMethod = potentialController.GetMethod(methodName);
            if (potentialMethod != null) {
                Controller controller = (Controller) Activator.CreateInstance(potentialController, context);
                potentialMethod.Invoke(controller, null);
            }
            else
                context.Response.Write(String.Format("Method '{0}' not found on controller '{1}'", methodName, controllerName));
        }
        else
            context.Response.Write(String.Format("Controller '{0}' not found", controllerName));
    }

    public bool IsReusable {
        get { return false; }
    }
}

That’s it!

Limitations

The controllers and methods are mapped at run-time using reflection. This would probably be too slow for production. Also it currently has to be in a top-level folder because I can’t figure out how to pass the Http request back to ASP.Net to try with the rest of the stack if we don’t have a matching controller/method.

One option might be to have no httpHandlers in the web.config and add the exact controller/method maps at build or runtime. This solves both the top-level problem and potentially the speed.

Another option to address just the speed of reflection would be to cache the path/method strings to the actual method and type so the only reflection would be the Activator.CreateInstance. If that is slow then we could look at pooling the controller instances themselves.

Going forward

Parameters for a method could be extracted and parsed from the query-string – they are currently ignored.

Response is raw output – we could do something very similar to rhtml.

I’m going to chat things over with the Subsonic team and see if we can come up with anything from here.

[)amien

Hidden menu options on the Mac

Apple tends to hide away unusual functionality in order to keep the user interface easy to use.

Here are a few hidden menu options that magically appear when you press the Shift, Alt or Ctrl modifier keys.

The Apple System Menu

Standard menus

Apple menu

  • Alt > System Profiler… (Leopard only)
  • Alt > Shut Down (with no confirmation)
  • Alt > Restart (with no confirmation)
  • Alt > Log Off (with no confirmation)
  • Shift > Force Quit ? (current application)

File menu

  • Alt > Close All (in some applications)

Edit menu

  • Alt > Deselect All (in some applications)

Window menu

  • Alt > Minimize All
  • Alt > Zoom All
  • Alt > Arrange in Front

Finder

Finder menu

  • Alt > Empty Trash (no confirmation)
  • Alt > Secure Empty Trash (no confirmation)

File menu

  • Alt > Always Open With
  • Alt > Show Inspector (like Get Info but changes to reflect whatever you select)
  • Shift > Slideshow (Leopard only)
  • Shift > Add to Favourites
  • Shift > Find by Name… (Leopard only)
  • Ctrl > Open in this Window (Leopard only)
  • Ctrl > Get Summary Info

View menu

  • Alt > Clean Up
  • Alt > Keep Arranged By (Leopard only)

Go menu

  • Ctrl > Enclosing Folder in this Window (Leopard only)

Dock

Application running menu

  • Alt > Hide Others
  • Alt > Force Quit

Safari

Application menu

  • Alt > Private Browsing (no confirmation)

File menu

  • Alt > Close All Windows
  • Alt > Close Other Tabs

Bookmarks menu

  • Shift > Add Bookmark to Menu

Pages

Edit menu

  • Alt > Delete Page (no confirmation)

Insert menu

  • Alt > Custom Footmark…

QuickTime Player

Window menu

  • Alt > Show Movie Info

iPhoto

Photos menu

  • Ctrl > Hide Flagged Photos
  • Ctrl > Move Flagged to Trash
  • Alt > Clear All Flags

Events menu

  • Alt > Merge With Next

iChat

Buddies menu

  • Alt > Send Direct Message

Window menu

  • Ctrl > Logout Jabber List

Xcode

File menu

  • Ctrl > New Empty File
  • Ctrl > Close Project
  • Alt > Open in Separate Editor
  • Alt > Reveal in Finder
  • Alt > Show Inspector
  • Alt > Save All…
  • Alt > Save a Copy As…

View menu

  • Alt > Zoom Editor In Fully
  • Alt > Split View Horizontally

Project menu

  • Shift > Ungroup

Run menu

  • Alt > Step Into Instruction
  • Alt > Step Over Instruction

Find menu

  • Shift > Find Previous
  • Shift > Replace and Find Previous
  • Alt > Find Select Regex in Project (Damn useful!)
  • Alt > Replace All

OmniWeb

File menu

  • Alt > Save As…

Bookmarks menu

  • Alt > Open Bookmarks Window
  • Ctrl > Open All Changed Bookmarks

OmniGraffle Pro

Edit menu

  • Alt > Select None

Arrange menu

  • Alt > Bring Forward
  • Alt > Send Backward

Note: OmniGraffle Pro also toggles various toolbar buttons on alt including lock/unlock and which side the utilities drawer slides out.

OmniOutliner & OmniFocus

View menu

  • Alt > Expand Line Completely
  • Alt > Collapse Line Completely

VMWare Fusion

Virtual Machine menu

  • Alt > Start Up Guest
  • Alt > Shut Down Guest
  • Alt > Suspend Guest
  • Alt > Restart Guest

Colloquy

View menu

  • Alt > Show Inspector
  • Alt > Mark All

Window menu

  • Alt > Select Previous Active Panel
  • Alt > Select Next Active Panel

Camino

View menu

  • Alt > Reload All Tabs
  • Shift > Force Reload Page

Bookmark menu

  • Shift > Bookmark Current Page
  • Shift > Bookmark Current Tabs as Tab Group

If you like keyboard short cuts you might also want to check out KeyCue which can display all of an applications short cuts on in a single list when you hold down the Apple key including these key-modified options.

Heavy keyboard users may also want to head to the Keyboard & Mouse preferences pane where you can switch on full keyboard navigation allowing you to tab through all controls in a window. It also shows a number of useful key-navigation options you may not be aware of such as pressing Ctrl-F3 to select an item from the dock.

[)amien

What are you doing for the next 6 months to be a better developer?

Scott Hanselman posed the open question on his Hanselminutes podcast and there have already been some good responses. My own plan includes:

Improve programming techniques

My girlfriend gave me the well-regarded Code Complete, Second Editionfor my birthday. I shall read it cover to cover and adopt good practices I am not currently practising.

Manage my life

I have started reading GrinGod’s copy of Getting Things Done: The Art of Stress-Free Productivity. I will move tasks out of my head and concentrate on what is achievable right now.

I have set-up these tasks now in Midnight Inbox (great but a little rough) and will keep an eye on OmniFocus. I will be prepared to use my free Moleskine I won in the Moleskinerie summer draw if neither does the job and not immediately write my own software.

Interact with other developers

My Subversion talk at the Guernsey Software Developers Forum went well. I will seek new members and engage in discussions of development with regards to local issues such as those in the finance industry.

I will spend less time on IRC as it is distracting and the non-persistent nature means good answers are lost. Instead I will help more on forums and be prepared to wait for answers to my own questions.

Learn new technologies

I will investigate technologies and learn them where they appear applicable to my work or I find personally interesting. These include:

  • Ruby on Rails – clean MVC development with AJAX support… but what about libraries and performance?
  • LINQ – simple but powerful object-relational mapping as standard but far away in .NET 3.5
  • Cocoa – Apple’s OS X development based around Objective-C giving compilation and dynamic typing
  • MonoRail – if I’m going to continue with ASP.NET it won’t be with WebForms
  • SharpDevelop – the Visual Studio API is terrible and this project looks well designed and usable

Contribute more to open source

I will contribute more to my favourite open source projects. This includes:

  • AnkhSVN – improve user interface and head up the 1.1 release
  • SubSonic – refactor more code and help out where I can

Lead development at work

At my new job I will concentrate on the new technology and vision for the next-generation of tools to deliver to our staff and customers and lead my team as appropriate.

I will distil my experience contracting for the last 7 years into the best practices for the company and continue to lead them in adopting modern practices. We now have have source control, formalised request for change and release management procedures however we still need to embrace new tools, write comprehensive unit tests and switch to object-relational mapping for new development.

Switch keymap to Dvorak

I have swapped out my Das 2 at work for my Apple Pro with the key caps rearranged for Dvorak. I will stick to this layout until I can properly touch-type. I won’t actually make me a better developer but it should keep RSI at bay. (This post was written using Dvorak)

[)amien

Italic syntax highlighting in Visual Studio 2005

I came across a posting by Thomas Restrepo about a theme for Vim he likes called Wombat and how it wouldn’t be worth porting to Visual Studio as it doesn’t support italic syntax highlighting – as we all know.

This got me thinking and I was able to port it with italics although the process is a bit of a hack.

If I can figure out a way of making this hack re-distributable without infringing on copyrights I’ll follow this one up.

In the meantime here’s a screen-shot of it in action using Consolas.

Visual Studio 2005 with italics

I can’t stand using vim for .NET – I’ve got better things to do than commit the entire .NET Framework to memory. I remember watching a WPF screen-cast where the guy was using “his trusty editor” (vim or emacs – I forget ;-) and going on about the great keyboard short-cuts whilst constantly trying different method names, compiling yet again and finally looking up help in the absence of IntelliSense.

I did however check out the latest trunk of SharpDevelop this weekend and was quite impressed with both the product and the source code. There was a bit of flickering with the solution explorer and the icons seem to be a bit of a steal-and-mash but otherwise looks first class.

[)amien