L
A
M
B
D
A

@

C
O
P
A

Sproutcore + Rails

— July 19, 2008

I wasn’t at the WWDC, but even if I had been there, I probably would have missed
this too: Sproutcore.

Because of MobileMe and Live Mesh (the latter being Microsoft’s latest weapon to be a big boy on the internet, and I predict this time they will finally succeed big time), I found out that MobileMe is actually based on this open source javascript framework named Sproutcore.

You have to look for yourself what it’s all about. This is not like Prototype, jQuery or all those others. This takes MVC to the web browser, advocating fat clients. It does this with Cocoa inspired features.

I wanted Sproutcore to communicate with my rails app. It’s not as obvious at the moment unfortunately. In rails follow these steps:

rails contacts
cd contacts

Edit /config/routes.rb to look like this:

ActionController::Routing::Routes.draw do |map|
  map.connect 'sc/:controller/:action'
end

In other words, all sproutcore ajax calls will be expected to hit our site relative to /sc.

Create a simple models file:

class Contact < ActiveRecord::Base
end

Create a migrations file:

class CreateContacts < ActiveRecord::Migration
  def self.up
    create_table :contacts do |t|
      t.string :first_name, :last_name
      t.timestamps
    end
  end

  def self.down
    drop_table :contacts
  end
end

Run rake db:migrate. The contacts table is now created. Run script/console.
And then create two contacts so we have some data:

Contact.create(:first_name => "Lawrence", :last_name => "Pit")
Contact.create(:first_name => "Holy", :last_name => "Cow")

Exit the console. Now for our controller:

class ContactsController < ApplicationController
  def list
    contacts = Contact.all(:order => params[:order])
    respond_to do |wants|
      wants.js {
        records = contacts.map do |contact|
          { :guid      => contact.id,
            :type      => contact.class.name,
            :firstName => contact.first_name,
            :lastName  => contact.last_name
          }
        end
        render :text => { :records => records, :ids => contacts.map(&:id) }.to_json
      }
    end
  end
end

Sproutcore will send a Ajax GET request like http://localhost:3000/sc/contacts/list?order=id. So we only need a wants.js block. The above code can be generalized with helper methods to make this more DRY for our other controllers, but that’s for later…

On the rails side we’re pretty much done. Fire up script/server and test the above URL. You should see a simple json formatted string as output with the two contacts you created in the database. Keep your rails server running…

Now to sproutcore. The easiest way to show this is by checking out the sproutcore-samples:

cd ..
sudo gem install sproutcore
git clone git://github.com/sproutit/sproutcore-samples.git
cd sproutcore-samples
git submodule init
git submodule update
sc-server

Now point your browser to http://localhost:4020/contacts. You should see a simple demo of sproutcore showing a few contact persons. The persons you see are loaded from fixtures by sproutcore. But we want our contacts from rails. To do this, follow these steps:

open /clients/contacts/core.js. Add this line to the end:

Contacts.server.set("urlFormat", "/sc/%@/%@");

or alternatively, create the server like this:

server: SC.Server.create({ prefix: ['Contacts'], urlFormat: “/sc/%@/%@” }),

This basically tells sproutcore that when a Ajax request is made the url should look like /sc/<controller>/<action>.

Next, edit /clients/contacts/main.js. There’s a line that says to load the contacts from the fixtures (as defined in /client/contacts/fixtures/contact.js). But we want the real data. So replace that line:

// Contacts.server.preload(Contacts.FIXTURES);
Contacts.server.listFor({recordType: Contacts.Contact});

This basically says that when the page is loaded it should do an Ajax GET request to get the list of contacts.

Edit /clients/contacts/models/contact.js, and add the lines that define dataSource and resourceURL:

Contacts.Contact = SC.Record.extend(
/** @scope Contacts.Contact.prototype */ {

  dataSource: Contacts.server,
  resourceURL: 'contacts',

  fullName: function() {
    return [this.get('firstName'), this.get('lastName')].compact().join(’ ‘);
  }.property(’firstName’, ‘lastName’)
});

The value for resourceURL is what is used in the URL to make the call to rails. The datasource tells which server to use when sending the Ajax requests. Aside note: I would expect future versions of sproutcore wouldn’t need these two lines as they could be defaults.

And now the last part, the real tick to this whole thing:

# This will proxy all requests to /sc/* -> http://localhost:3000/*
proxy '/sc', :to => 'localhost:3000'

Update (thnx joel): this piece goes into the file sproutcore-samples/sc-config.

This tells sc-server to send any requests that start with /sc to http://localhost:3000 which runs our rails server.

Now refresh your browser at http://localhost:4020/contacts and lo and behold, the contacts are now coming from rails.

Last note: currently Sproutcore in not at v1.0, and it’s REST API is not well developed. I’m pretty sure this will be fixed quite soon though. This is a winner!

14 comments

Hello Functor: pattern based method dispatch in ruby

— June 18, 2008

One of the more innovative initiatives going on in ruby land is the waves project. It’s yet another open source framework to build ruby-based web applications. This one, as they all do I suppose, claims to be “the next-generation”. It’s chock full of beautiful concise code. Lambdas rule highly in waves. An implementation of resource based mappings where Resources are first class citizens before even going into the MVC paradigm (if at all necessary) is underway.

Out of this project two little gems have appeared that are just beautiful in their design and implementation. One is Autocode, a ruby library for auto-loading and auto-creation of classes and modules. More information about Autocode is here.

The other, and to me the more fascinating one, is a simple little gem called Functor. It is extensively described here. It was itself based on a recent development by Topher Cyll which resulted in his multi gem.

An example of using Functor straight out of the Waves project:

functor( :normalize, Symbol, Hash ) { | name, options | options.merge!( :name => name ) }
functor( :normalize, String, Hash ) { | pattern, options | options.merge!( :pattern => pattern ) }
functor( :normalize, Regexp, Hash ) { | regexp, options | options.merge!( :pattern => regexp ) }
functor( :normalize, Exception ) { | exception | { :exception => exception } }
functor( :normalize, Hash ) { | options | options }

It’s called like this:

def map( *args, &block )
  options = normalize( *args )
  ...
end

Consider for a moment how this code would look like without Functor. You’d probably end up with a dozen nested if-then-else and/or case-when statements.

So Functor seems to behave like a method overloader. Something that natively exists in languages like java and C++. Matz refuses to implement method overloading in ruby because “orthogonal features [...] can explode into complexity”. And I think he’s right in that. Functor allows orthogonality to creep in. But if you don’t go overboard with this stuff you’d be ‘right.

The beauty of Functor however is that it allows more than just method overloading. Method overloading matches on the class of it’s arguments. In contrast, Functor matches the arguments first by trying the === operator, then the == operator and if that didn’t match it will try using call if you provided a lambda or a Proc (or anything really that responds to :call). So for example, this would be possible:

functor( :get, /^foo$/ ) { |p| "foo!" }
functor( :get, /^bar$/ ) { |p| "bar!" }

Calling get("bar") would return bar!

You can play around with functor by installing it as a gem (source is http://gems.github.com/):

sudo gem install -r functor

Or get the source code at github.

No comments

Rails plugin validates_as_time

— May 19, 2008

Exactly one year ago Railscast episode 32 was released. Watch the episode to see Ryan Bates explaining how to use a virtual attribute in your rails models to allow editing time attributes with text fields in a more flexible way.

Based on this I decided to make it even more flexible. The result is the rails plugin validates_as_time. It supports the fabulous Chronic gem automatically if you have it installed, otherwise it falls back to using Time.parse as the method to parse time string values.

In it’s simplest form just define the time fields you want to validate with string values:

  class Project < ActiveRecord::Base
    validates_as_time :starts_at, :ends_at
  end

and in your view:

  <%= f.text_field :starts_at_string %>
  <%= f.text_field :ends_at_string %>

You can clone/fork the source via:
http://github.com/lawrencepit/validates_as_time

No comments

Recording rails exceptions

— May 7, 2008

Railscast episode 104 shows how to record exceptions on a production site for analysis purposes. It refers to the exception_logger plugin from defunkt.

As Ryan Bates later noted on his blog you probably want to add authentication to the exception_logger controller so not everyone can access it. The README of the plugin shows how to do this.

However, I found the solution provided in the README to be cumbersome, a bit too ‘hackish’ to my taste, and sadly it didn’t work anyways (probably it did work with rails 1.x, but it doesn’t with rails 2.x). I also didn’t like to copy code that is already present in my ApplicationController and AdminController.

Therefor I forked defunkt’s code, refactored it a bit, and now it easily leverages existing (authentication) code in your controllers.

To see it in action:

  git submodule add git://github.com/lawrencepit/exception_logger \
  vendor/plugins/exception_logger

and then create this controller for example:

  class LoggedExceptionsController < AdminController
    include ExceptionLoggableControllerMixin
    self.application_name = "My Application Name"
  end

So basically all you have to do is include the module ExceptionLoggableControllerMixin in your own controller.

The source of my fork can be found at http://github.com/lawrencepit/exception_logger

No comments




about
code
twitter