In my spare time I have been working on a geo-location application that runs on a BlackBerry mobile phone connecting to a Rails server where the geo data is stored.
If you happen to still be using Rails < 2.1 then this may be interesting, however, with the recent release of 2.1 timezones have been fixed up pretty well. Of course I wanted to get something that worked for my application in pre 2.1 days so I rolled together a few different libraries that seems to work pretty well. Having said that, the JavaScript will help you determine a users timezone no matter what version of Rails you are using.
The particular use case that I want to hit is that people can login to the web application from wherever they want and the application will automagically display the times in the timezone of the user viewing the site - not the timezone of the user that created the geo data and not the timezone of the server.
The general approach taken is to get the users timezone offset through JavaScript in the browser, then save that in the users profile, and then have a helper method to convert any times that are saved in the servers timezone to the users local zone.
Prerequisites
Gems: tzinfo (0.3.9, 0.3.8), tztime (0.1.0)
Plugins, tzinfo_timezone, tztime
Now for the code
So how do we figure out the users timezone? Through JavaScript of course! In application.js put the following script at the top - don’t put it on the onload event since it should set as soon as a user hits the page:
Cookie.set("tz", (new Date()).getTimezoneOffset());
This sets a cookie in the browsr to be the JavaScript timezone offset, which we will use later on the server. Note that this is using a simple cookie abstraction.
Now in application.rb we need some magic that will take that timezone offset from the cookie and update the users profile with their current timezone. This will be achieved with an around_filter (damn I love around filters) like so:
around_filter :set_timezone
def set_timezone
if logged_in? && browser_timezone && (browser_timezone.name != current_user.time_zone || current_user.time_zone.nil?)
current_user.update_attribute(:time_zone, browser_timezone.name)
end
TzTime.zone = logged_in? ? current_user.time_zone : browser_timezone
yield
TzTime.reset!
end
def browser_timezone
return nil if cookies[:tz].blank?
@browser_timezone ||= begin
min = cookies[:tz].to_i
TimeZone[-min/60]
end
end
That takes care of getting the users current timezone. Now all we have left is to make it accessible to the application with a simple function in the application_helper.rb as follows:
def local_time(time_at)
TimeZone[TzTime.zone].utc_to_local(time_at.utc)
end
Finally, we can just call local_time from anywhere passing in a time that is in the servers timezone from the database:
local_time(location.updated_at)
So if you are stuck on Rails < 2.1 that is a good approach to getting timezones working.
Despite it occurring on one of the nicest days of the year so far in Vancity, sixteen people came over to the Nitobi offices and hacked the day away. The beer arrived early so those of us who’s projects made a turn for the worse were getting sauced already by two or three - just after the pizza lunch.
When everything was said and done, Yohei came out on top with a Papervision3D version of the Nitobi office! It was pretty damn cool and you can play with it below.
Alexei also showed his NitobiBug tool that is a cross-browser Ajax debugging tool similar to FireBug.
Thanks to everyone for coming out despite the amazing weather (which you can see out the windows of the model of the office below) and the opening of the new Vancouver Apple store
We will be having the next one some time in the fall I think.
This is a really exciting new project (working with Verb Exchange) where we are building an AIR XMPP client for a VOIP system that lets people use their existing phones to make cheap calls and SMS.
Pretty much all the desks had people hacking and there were five of us around the boardroom table writing code. Hopefully everyone will update the wiki with their names and projects. There was a collaborative flex app, a Rails web application, a blog API written in merb, a Facebook app, foosball cup holders, an iPhone app, declarative YUI components, a command line twitter client, and a few others that my hangover is preventing me from recalling. Hopefully people will update the wiki with the full list.
Ryan had the best hack that was an Adobe AIR application that controlled music being played on a different computer - way cool and hopefully he will polish it up and release it for everyone! For his efforts Ryan won a copy of Adobe Creative Suite Master Collection!
There is still lots of beer left at the office so I am sure that this will be a productive week coming up.
Thanks to everyone who participated and hopefully we will have another hack day in a couple of months.
The second Nitobi Hack Day will be happening on Feb 9, 2008 from 10am to 6pm. You can come and start hacking at 9 things will officially start at 10am SHARP! Of course you can always come by later and if you don’t know anyone just ask for someone that works at Nitobi to show you the ropes. You can signup over at Upcoming.
The event is meant to bring hackers from around Vancouver to the Nitobi office and dedicate a day to building something amazing! That something can be hardware, software or whatever. The hacking is generally focused on Ajax (of course), social networking (Facebook apps anyone?), wireless (Joe and I got this covered), AIR / Flex, and web development in general - of course the hacking can still be robotics, Wii hacks or whatever you think is cool.
Nitobi will provide the foosball, beer, prizes (like books, shirts, software and an IPod Nano) and you provide the hax0r skillz.
Over the past few years both Ajax and Flex have come a long way. Previous versions of Flex (and Flash before that) were nowhere near where Flex 2 / 3 Beta are at and one can achieve amazing things with Flex these days. The same can be said for Ajax. I think that both Flex and Ajax have been moving forward in step. In fact, Ajax has been around for a lot longer than Flex and was already creating real RIAs back in the late 90’s when Flash 3 was still only being used to make website intro’s. Certain frameworks, like Ext, make it simple to create applications with a desktop like feel using splitters, panels and common desktop UI widgets.
What is an RIA
I use the term RIA here with some reservation just because of the differing perceptions of what it really means - sort of like early perceptions of DHTML only being useful for annoying flashing text and simple animations. In terms of Ajax, one needs only look to today’s best of breed Ajax applications such as the Google online application suite including their great spreadsheet and mapping applications, which either don’t have equivalents in other RIA technologies or are simply better than their competitors; most people would choose Google Maps over the Flex based Yahoo! maps any day. Really, other than Buzzword, there are few popular Flex based RIAs (and even fewer that are actually available to the public) and frankly is there anything that much more amazing about Buzzword compared to a Google Doc? Google Doc even “feels” faster and more responsive to boot.
If RIA is about eliminating the page refresh or drag and drop then Ajax definitely has the right tools to create an RIA application. If RIA is about creating a “desktop like” experience then Ajax is definitely an RIA. If RIA is about improving the user experience and usability of network connect applications then Ajax is undoubtedly an RIA. One look at Google Mail and anyone can appreciate that it for the most part loads faster, searches faster, and generally performs far better than a desktop mail program like Outlook. Even in areas that Ajax may struggle, such as video or graphics, Ajax can take advantage of a wide variety of other technologies such as Flash, SVG or Canvas.
Flex Shortcomings
As with Ajax there are a lot of shortcomings to Flex applications. Dealing with the browser back button, search indexing, rendering HTML content are just a few of the problems that face Flex developers - some of them equally problematic for Ajax applications others not.
On the other hand, one of the biggest strengths of Ajax, which is often quoted as a weakness, is that it depends on the fragmentation of browsers and standards. As we know, overspecializing breeds in weakness. While Ajax may change rather slowly due to the glacial pace of browser advances and constant bickering over standards (something that is changing more rapidly now as the browser market becomes more competitive), at least the people doing the standards are less impacted from having budgets, managers and boards to report to - i.e. closed products like Flex are produced by companies that need to make money. Furthermore, any gaps in the technologies provided by the browser vendors are often filled in by the tireless work of Ajax framework developers.
Flex has a hard time fitting into conventional - and I dare say preferred - web development processes, which will keep it on the fringe for some time to come. The process that Flash and now Flex developers go through when building an application is that they have Flex builder to design their application and maybe write some ActionScript to access some web service endpoints on a server somewhere. Then the developer needs to build their web service endpoints using a different tool (likely Eclipse or Visual Studio). Everything is separated between the client and server only connected by the web service endpoints yet the client side development is still not strictly for a designer but a designer and an engineer need to work on the Flex application. One interesting thing about this approach to development is that it fits in well with the enterprise SOA sort of idea; the Flex app is only a consumer of enterprise services and does nothing on the server. This is one reason that Flex is becoming popular in applications for SAP and Salesforce I think. However, hat is not to say that Ajax has no place in enterprise applications. The one thing that I do really like about Flex development is the declarative approach which few Ajax frameworks have done, I digress.
On the other hand, Ajax developers are used to using one tool for doing their server and client development whether it be Dreamweaver, Eclipse, or Visual Studio. Arguably the most popular Ajax frameworks, notably ASP.NET Ajax and Google Web Toolkit, actually combine both server and client development in one environment. In those environments Ajax developers are able to write server side code (binding to databases, interfacing with external web services, writing server side events, and so on) as well as client side code (CSS, JavaScript and HTML) all in one place. Even better, the server side code often encapsulates all the HTML, JavaScript and CSS required for an Ajax component like a tree or grid making Ajax development a painless and productive RIA endeavour.
Ajax developers also have so many choices between different Ajax frameworks that provide different widgets and various fundamental approaches to Ajax itself. Some are integrated with the server while others are completely client side. Whereas if a developer chooses Flex, they have really only got one option for their framework, their widgets, their tech support and their sanity.
Testing
Unit testing is all well and good but where is the functional test framework for Flex that is Selenium to Ajax? I concede that functional testing is more important for Ajax just because you may be trying to hit four or five different web browsers with your application but it is important for Flex development even beyond just checking for browser nuances.
Mashups
Finally, one of the biggest drawbacks of Flex, and strengths of Ajax as evidenced by the Web 2.0 craze, is the fact that Flex simply does not play nice in the world of the web. Sure you can access data across domains from Flex if the remote server has read permissions set in the crossdomain.xml file, however, in general Flex is not amenable to building mashup applications. There is no easy way to specify some Flex widget and have it dynamically included in another Flex application (probably due to what I say in the conclusion). On the other hand Ajax RIAs are free to use any of the many technologies at their disposal and mashup content from anywhere on the net - including Flex content. Ajax applications were popularized due to the early mashup - in particular maps based ones with Google Maps. Flex is a heavy (handed?), non-standard, monolithic approach to building RIAs that are conducive to keeping the application separate from others.
Conclusion
Ajax is made for the browser and the browser will continue to be the universal approach to accessing content over a network for some time to come. Not the least reason of which is the rule of least power. The web became popular for a reason and that reason is the ease with which content can be created. This is as true for HTML as it is for Ajax and is the main reason that Ajax is alive, kicking and will be around for a while despite new RIA technologies.
This is some code that I wrote about a year ago at the Flex Component Developers Summit (and more recently presented at XTech) to show how declarative Ajax and Flex can work together to create dynamic, rich and compelling Internet applications.
The idea is simple. Take a single declaration - in this case XHTML - of some user-interface component and then use it to build a UI using either Ajax or Flex. All this from just one declaration.
What happens is that we take a single declarative data grid and converts it using XSLT on the client (so it only works Firefox, IE and soon Safari) into a declarative Nitobi Ajax Grid and to a Flex declarative MXML DataGrid. I use the FABridge to get the string of MXML generated from the XSL transformation into a stub Flex application where a Flex DataGrid is instantiated (deserialized) from the MXML declaration. It can an be seen live here (note: create the Flex grid first then the Ajax one - something funny that I can’t be bothered to fix ;)) and the code can be downloaded from here.
So by using a declarative approach and a little XSLT on the client we were able to quickly choose between using a Flex DataGrid or a Nitobi Ajax Grid to display our tabular data in!
Really the most interesting part is the MXML deserialization stuff. The only contents of the Flex application are two functions for performing the deserialization. I have listed the main part of the code that takes an XML document of an MXML DataGrid declaration and actually instantiates a DataGrid according to that declaration. It’s pretty quick and dirty but at least gets the right thing out! Essentially it just looks at each XML element and creates an Object out of it and sets all the properties on it from the XML element attributes and then recurses through the child elements doing the same. There are some special attributes though like datasources that need a little more care.
public function initGrid(html) {
// setup a tagname to datatype hash - maybe this already exists somewhere
controls['DataGrid'] = ‘mx.controls.DataGrid’;
controls['ArrayCollection'] = ‘mx.collections.ArrayCollection’;
controls['Object'] = ‘Object’;
controls['columns'] = ‘Array’;
controls['DataGridColumn'] = ‘mx.controls.dataGridClasses.DataGridColumn’;
// load the HTML into XML DOM
var mxml:XML = new XML(’<root>’+html+’</root>’);
parseXml(AjaxBox, mxml);
}
public function parseXml(parent, mxml) {
var item:String;
// get all the elements in our XML doc - this should of course walk the xml tree recursively
var itemList:XMLList = mxml.elements(’*');
for (item in itemList) {
// get the tag name of the XML node
var tagName:String = itemList[item].localName();
// get the class by using getDefinitionByName() method
var ClassReference:Class = Class(getDefinitionByName(controls[tagName]));
// create an instance of the class
var myObject:Object = new ClassReference();
// get all the attributes and set the properties
var attrList:XMLList = XML(itemList[item]).attributes();
for (var attr:String in attrList) {
myObject[attrList[attr].localName()] = attrList[attr].toString();
}
// now parse the children of this node
parseXml(myObject, itemList[item]);
if (parent.hasOwnProperty(tagName)) {
parent[tagName] = myObject;
} else if (parent.hasOwnProperty(”length”)) {
if (parent.hasOwnProperty(”source”)) {
parent.source.push(myObject);
} else {
parent.push(myObject);
}
} else if (parent.hasOwnProperty(”dataProvider”) && tagName == “ArrayCollection”) {
// This means we need to create a datasource for the Grid
parent.dataProvider = myObject;
} else {
parent.addChild(DisplayObject(myObject));
}
}
}
Who wants a job? We have some very exciting Flex and Ajax development opportunities coming up with some high profile companies and we need some kick ass people to help us out.
In case anyone is wondering, it seems that the easiest way to include the FABridge in your Flex applications is by copying the .as file into a folder in your application named bridge. The error that most people get due to this problem is “could not resolve <fab:Fabridge> to a component implementation”.