Archive for Rails tag

MobileMe up and down at me.com

MobileMeMe.com was up, briefly, just long enough for me to grab my usual handle and get the confirmation message in fact.

I did get a brief glimpse of the UI complete with a Finder-like view of various folders once I clicked past a warning about Internet Explorer 7 not being supported. Seconds later things stopped responding and then the original placeholder was back.

It’s no secret Apple have been using the SproutCore framework in producing Me.com and that while SproutCore isn’t tied to Ruby on Rails it seems likely Apple went with Rails given the lack of interest in WebObjects and more and more Rails articles turning up on ADC.

If they’ve gone down that route I’m hoping we’re not in for a ride like the Rails powered Twitter one takes us on. Despite being relatively simple functionality it has been unable to cope with the demand and ability to scale which means downtime, slowness and features being switched off are regularly observed.

They are using WebObjects after all so expect some WebObjects and SproutCore integration love soon.

As an aside I’m in the US the week after next to meet my team and attend some training and will try and grab an iPhone 3G whilst there if not already sold out. Given that I’m only in Canada for 3 more months I don’t want to be trying to escape Roger’s expensive 3-year contract so soon.

[)amien

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

Getting started with Ruby on Rails on Mac OS X

Choosing your Ruby packages

I have decided to get to grips with Ruby on Rails and get it set-up on my MacBook Pro. There seem to be a variety of options available:

  1. Use Locomotive – an all-in-one standalone developer package
  2. Official sources – configure, compile and install each
  3. MacPorts – package management for free software
  4. Fink – package management for free software

As Scott Stevenson points out this is too many options for a system that is described as Convention over configuration.

As I already use Fink for installing the majority of free software on my Mac I decided I’d go with that. At least until Leopard ships with Ruby on Rails in-the-box.

Preparing Fink

GrinGod gave me a heads-up that we’ll need to turn on the unstable packages to get the gems package and latest version of Ruby. The details are in the Fink FAQ but in summary:

fink configure

And choose the option for unstable trees. Then type:

fink selfupdate
fink index
fink scanpackages

And make sure you have rsync or cvs updating turned on to get the descriptions.

Ruby (via Fink)

Ruby is the language we wish to use and Fink currently supports 1.6 and 1.8 The following commands will install 1.8 for us and the Ruby Gems package management system:

fink install ruby
fink install rubygems-rb18

Rails (via Gem)

Now we need the Rails web framework which will be installed using gem.

gem install rails --include-dependencies

Choosing your web server

There are a number of options available again here including Apache, LightTPD and Mongrel. I wanted to give LightTPD a shot as it is on Fink and less of a beast than Apache. I tried various combinations but even having installed the lighttpd-fastcgi module I couldn’t get the fastcgi gem to do anything but spew errors so Mongrel it is.

Mongrel (via Gem)

Back at that Terminal window type:

sudo gem install mongrel --include-dependencies

Select the mongrel 1.0.1 (ruby) and fastthread 1.0 (ruby) options as prompted.

Testing Ruby, Rails & Mongrel

Again at the Terminal window:

rails mytest

A number of files should be created if Ruby & Rails are working correctly. Now type:

cd mytest
script/server

You should see a line stating Starting Mongrel listening at 0.0.0.0:3000

Open your browser of choice and navigate to http://localhost:3000

Choosing a database

You can go with MySQL, PostgreSQL or SQLite from Fink alone. I went with MySQL for a change.

MySQL 5.0

Whilst Mac OS X Server comes with MySQL the standard edition does not. Rather than use the Fink port I went with the official MySQL 5.0 package primarily to get the preferences pane that allows me to start and stop it as required however it also includes a helpful monitoring widget.

To install download and run the MySQL…x86 package.

Copy MySQL.prefPane to /Library/PreferencePanes and re-open System Preferences. Head into the new MySQL pane and start MySQL up.

MySQL Tools

To manage MySQL and download the Mac OS X version of the GUI management tool.

Run the MySQL Administrator application and connect to localhost on the default port of 3306 with the user name root and a blank password. Don’t forget to change the password by heading over to the Account pane.

Ruby with MySQL

Install the native Ruby to MySQL library to improve db performance by heading back to the Terminal window and executing:

sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config

Select the option for mysql 2.7 (ruby)

Testing the database

As MySQL doesn’t include a sample database head into MySQL Administrator and into the Catalogs pane. Create a new database by pressing the + button and giving it the name mytest.

Select mytest from the left then choose Create Table… from the Table Actions drop-down list.

Enter the table name products then use the + button to create the following columns:

Name Type Primary Key? Nullable? Auto Increment?
id INT Y N Y
name VARCHAR(255) N N N

Click Apply, Execute, OK then Close.

Go to the Terminal window and cd into the mytest folder you generated earlier.

Edit the config/database.yml file using nano or TextEdit and change the database name from mytest_development to mytest. You will also need to enter the password here – either for your root account or ideally a special application-specific account you set-up in MySQL Administrator.

Back at the trusty Terminal execute:

script/generate scaffold product
script/server

Now open your browser to http://localhost:3000/products/ and try creating, editing and deleting some products.

Next steps

And of course trying to find an editor/IDE that supports Ruby as Xcode certainly doesn’t and TextMate costs money (I’m happy to pay for things when I’m committed to the tech – not before)

[)amien