It’s something I always wanted to write about and also a response to Peter Cooper’s post on RubyFlow calling to write about tools that we use everyday to do Ruby on Rails development. I like the idea since there are a lot of options and finding right tool for the job is essential.
Let’s start:
Hardware
- MacBook Pro 15″ with Intel Core 2 Duo 2.40GHz and 2 gigs of RAM - absolutely enough. This is a decent laptop, the only thing which bothers me is its temperature, sometime it burns my hands, literally…
- Logitech VX Nano Cordless Notebook Mouse – the best notebook mouse ever, has the smaller receiver out there + I change batteries like twice a year, love it
Base software
- Arch Linux – it’s my 6 day with this distro actually, I’m a Gentoo fanatic but from time to time I try to use something different. I usually go back to Gentoo after a month or so. We’ll see how I’ll end up now. So far I really like Arch, it’s extremely lightweight, fast and stable. Software availability is as good as in Gentoo. It’s also easy to make your own packages which is crucial for Ruby developers since it happens that we need various non-standard stuff, like let’s say Nginx with Passenger support. I’ve got a feeling that this time I won’t go back to Gentoo
- KDE – it’s my desktop environment and one of the most important reasons why I’ve switched back from OSX to Linux. KDE’s window manager is highly configurable and gives you features like moving and resizing of windows via a special key + mouse move/click, custom settings per application / window class so for instance you can configure that console window will always appear on the third virtual desktop snapped to the upper-right corner of the screen, useful desktop effects ie “Present windows” which is something like Expose in OSX but you can filter out windows by typing key words, cool window transparency (you can alt+mouse scroll to change window’s opacity…it’s useful, believe me) and much, much more…I just can’t work without those things. Here are KDE’s apps that I use:
- Konsole – terminal emulator, very powerful, has tabs, horizontal / vertical view splitting, bookmarks and fully customizable look’n'feel
- KRunner – something like quicksilver for OSX
- Dolphin – file manager, supports remote protocols, in my opinion the best file manager ever
- KDiff3 – GUI for displaying diffs and merging, it’s my mergetool in Git
- KColorEdit – color picker and editor
- KRuler – on-screen ruler
- KSnapshot – for doing screenshots
- Klipper – a handy clipboard manager, supports custom actions
- Amarok – music player
- NetBeans – I use this great IDE even for writing tiny scripts, for me it’s the best choice. I’ve tried many other IDEs/editors including Eclipse, JEdit, RubyMine, TextMate and others that I don’t remember now and NetBeans works best for me. Key features are powerful editor with macros and great shortcuts, ctrl+click navigation, fantastic JavaScript support, debugger, test/spec runner and…support for multiple projects in the same window, something that most of the IDEs don’t have and I really need it. I use following plugins:
- Ruby and Rails – what a surprise
- Database – to access databases via a GUI
- OpenFileFast – written by my friend from LLP, Marcin Kulik. Works like the open file dialog in TextMate, highly recommended. You can read more here
- Firefox – I’ve switched from Opera a long time ago because of Firebug, now I’m about to try out Chromium for normal web browsing and use Firefox only for the development. Plugins that I find useful:
- Firebug – must-have for every web-developer
- Web Developer – easy access to things like clearing cache, disabling JavaScript etc.
- Chat Zilla – IRC
- Read it Later – I never have time to read interesting things from Google Reader in the moment I find them so…I read them later
- Delicious – I still use it but rather for sharing bookmarks between work and home
Dev tools:
- GIT - probably the best SCM in the world
- QGit4 – sometimes I use this git gui to view history of a project
- ZSH with a pimped prompt for GIT
- Nginx with Passanger – better then script/server
- VirtualBox – I have 3 Windows virtual machines each with different version of Internet Explorer, only for testing of course
Communication:
- Skype – Linux version is very crappy, unfortunately I’m forced to use it
- PSI - great Jabber client
- TweetDeck – powerful Twitter client, Adobe AIR based
That would be it!
A while ago I have written a post about JavaScript helpers in Ruby on Rails and tried to explain why they are a bad idea. It’s hard to believe for me that it was almost 2 years ago! Since then so many things have happened in the Ruby world…Now Rails 3 is on its way and we already know what significant improvements and changes it will include. One of them is related to JavaScript helpers and the way how remote links and forms will be handled and I must admit that the new idea is absolutely great.
The new way is based on unobtrusive approach to JavaScript. This means that HTML code will be separated from JavaScript. I have checked out the latest sources of Ruby on Rails and found out that some of the work is already done. There is a new helper called AjaxHelper, it implements link_to_remote method which in the moment of writing this post looks like this:
def link_to_remote(name, url, options = {})
html = options.delete(:html) || {}
update = options.delete(:update)
if update.is_a?(Hash)
html["data-update-success"] = update[:success]
html["data-update-failure"] = update[:failure]
else
html["data-update-success"] = update
end
html["data-update-position"] = options.delete(:position)
html["data-method"] = options.delete(:method)
html["data-remote"] = "true"
html.merge!(options)
url = url_for(url) if url.is_a?(Hash)
link_to(name, url, html)
end |
What you see here will generate a clean markup with HTML5-compliant attributes prefixed with a word “data-”. If you are not familiar with them you can checkout a nice article by John Resig HTML 5 data- Attributes. Those attributes will instruct the additional JavaScript code how it should handle the behavior. Basically all links, buttons and forms that have the special attribute “data-remote” set to “true” will issue an AJAX request. There has been a discussion on the Rails on Rails Core group about how to implement corresponding JavaScript code. People are worried about its performance since finding all elements with data-remote=true appears to be slow in case of Prototype and jQuery. Moreover there is a problem of new elements that may be dynamically inserted after the page was loaded and all the event listeners were attached. Fortunately there is no need to be worried as our situation is a perfect example where we should use Event Delegation. DHH has already showed in his Rails 3 and the Real Secret to High Productivity presentation how links and buttons can be handled by Prototype library and it looks absolutely reasonable to me.
I would like to focus on jQuery though as it’s getting more popular even in the Rails community. Great example is my job where we use jQuery in every of our new projects. So how can we handle new remote links and forms using this popular library? Actually it’s ridiculously easy. Thanks to jQuery.live function we can easily use Event Delegation to handle AJAX calls. Just take a look at this sample of a markup that new helpers in Rails 3 will generate:
<!-- the new link to remote -->
<a href="/users" data-remote="true">Users</a>
<!-- the new remote form -->
<form action="/users" method="post" data-remote="true">
<input type="text" name="login"/>
<input type="submit"/>
</form> |
Pretty clean, I really like it! Now let’s see how we can implement jQuery handler that will send AJAX requests:
var request = function(options) {
$.ajax($.extend({ url : options.url, type : 'get' }, options));
return false;
};
// remote links handler
$('a[data-remote=true]').live('click', function() {
return request({ url : this.href });
});
// remote forms handler
$('form[data-remote=true]').live('submit', function() {
return request({ url : this.action, type : this.method, data : $(this).serialize() });
}); |
The above code will send an AJAX request when you click on a remote link or submit a remote form. Note that it will work also with new elements dynamically inserted to the DOM. The example JavaScript code is the bare minimum of course, we could have something much more sophisticated. We will be able to specify success and failure handlers and also elements that should be updated with an AJAX response text (and probably much more!), hence the JavaScript is going to be more complicated.
This is definitely a step into the right direction. I’m looking forward to Rails 3!
Web applications are getting more and more complex. The user interface of a modern web application can be as rich as its desktop equivalent. If we use JavaScript/HTML/CSS trio to build this UI then we definitely want to use AJAX. A typical approach is to use AJAX to update parts of our page using an HTML response, everyone knows that, right? Does this approach allow us to create a responsive, fast and flexible UI? The answer is no.
Here are 4 main downsides of using AJAX requests to load UI parts:
- You increase the server load – yes, you make an AJAX request only because you need a piece of HTML code that you want to insert to an already rendered page. That was cool a few years ago when AJAX was such a great innovation. Nowadays it should be considered as a less efficient solution.
- You complicate the server-side code – because you need parts of your page to be returned by the server you, obviously, need to handle that by writing more server-side code. You end up having many actions in your controllers that return different fragments of HTML. I know, you think it’s normal, everyone does that.
- You loose a lot of control over the UI – you use DHTML techniques to deal with the UI and in the same time you need the server to get parts of that UI. This leads to a code duplication and in many cases ends up with a big mess.
- A user will have to wait until a request is done – that just sucks, that poor guy has to wait because you went back to the server for a little piece of HTML. How you are going to implement a responsive UI this way? “Your servers are fast”. Of course they are, but what if user’s ISP sucks? What if the user is downloading something and 99% of his bandwidth is gone?
Rendering HTML on the client-side is ridiculously simple. Consider the following example. Let’s say we have a page with a list of some people, the list is just a simple HTML table. It could look like this:
<table>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>E-Mail</th>
</tr>
</thead>
<tbody id="people">
<!-- here go people -->
<tbody>
<tfoot>
<tr colspan="4">
<td>
<a id="previous" href="#">Previous</a>
<a id="next" href="#">Next</a>
</td>
</tr>
</tfoot>
</table> |
As you can see the tbody element is empty, we will load its content after the entire page is loaded. Instead of a bunch of “tr” elements the server should return a nice JSON data which could look like this:
var people = [
{ id : 1, first_name : 'John', last_name : 'Doe', email : 'john.doe@somewhere.com' },
{ id : 2, first_name : 'Jane', last_name : 'Doe', email : 'jane.doe@anywhere.com' },
{ id : 3, first_name : 'Third', last_name : 'Guy', email : 'third.guy@nowhere.com' }
]; |
To render these data we obviously need a template. Prototype gives us a handy class called Template which is perfect for our needs. To create a template and render the list of people we need a few lines of JavaScript:
var personRowTemplate = new Template(
'<tr id="person_#{id}"><td>#{first_name}</td><td>#{last_name}</td><td>#{email}</td></tr>');
var peopleRows = '';
people.each(function(person) {
peopleRows += personRowTemplate.evaluate(person);
});
$('people').update(peopleRows); |
That’s pretty much it. It doesn’t look spectacular, huh? Now just think about the benefits of this approach:
- You have the data in JSON format, you can use them to render things like an edit form without going back to the server, thus user experience will be better
- You can be focused on building a nice JSON API for accessing data instead of implementing actions that return small pieces of HTML
- Your client-side code is cleaner and more consistent
Sounds like a good deal, doesn’t it?
Recently, new versions of jQuery and Prototype have been released – it’s a perfect moment for a part number 2. On the official Prototype blog we can read that the general performance of CSS selectors is now improved, unfortunately only for Safari 3, but Element#up/#down/#next/#previous should now be faster on all browsers, it’s a good news as they were really slow. On the other hand we have jQuery official announcement with information that jQuery is now 300% faster – we’ll see!
This time I made a step forward and decided to use a custom JavaScript-based testing environment instead of running tests using Firebug profiler. The obvious advantage is that I was able to run all the tests on 4 different browsers. New test cases aren’t much different then in the first part, let’s say it’s a modification of the previous ones with some extra operations and a little more complex HTML structure.
Test environment setup
Libraries:
- jQuery 1.2.2
- Prototype 1.6.0.2
All the tests were run on the following browsers:
- Firefox 2.0.0.11
- Konqueror 4.00.00
- Opera 9.50_beta1
- Internet Explorer 7 (*but using Windows on VirtualBox!*)
A tiny piece of JavaScript code is responsible for running the tests, each operation is called only once inside a try-catch block, so the essential part looks like this:
try {
var start = new Date;
test();
var end = new Date - start;
this.writeResults(test, end);
} catch(e) {
test.resultCell.innerHTML = '<div style="color: red">Exception caught: '+e.message+'</div>';
} |
There is a 3 seconds break between each test run, results are automatically inserted into the results table. If you want, you can check it out on your own, just go right here and hit the ‘run tests!’ button.
The results
I’m happy to see that all tests pass on the latest Konqueror, previous version from KDE3 fails on some Prototype tests. I don’t own Mac, so you won’t see Safari results here, although I’ve run the tests on my friend’s MacBook with very similar hardware as my laptop has (Intel Core Duo 2ghz + 2 gigs of RAM), and it was faster even then Konqueror (no, it doesn’t mean his MacBook is faster then my laptop!!!!
).
I’ve run everything 3 times, here are average results in ms:
| # |
Library |
Test |
Firefox |
Konqueror |
IE7 |
Opera |
| 1 |
jQuery |
$('td.counter').addClass('marked') |
|
96.6 |
32.3 |
70 |
37 |
| Prototype |
$$('td.counter').each(function(el){
el.addClassName('marked')
}) |
|
108.3 |
49.6 |
858 |
75.7 |
| 2 |
jQuery |
$('td.counter span.special').removeClass('special') |
|
62 |
23.6 |
46.6 |
25.6 |
| Prototype |
$$('td.counter span.special').each(function(el) {
el.removeClassName('special')
}) |
|
28 |
23.7 |
167 |
24.7 |
| 3 |
jQuery |
$('td.content span.odd').css('color', 'red') |
|
124.7 |
40.3 |
63.7 |
38.3 |
| Prototype |
$$('td.content span.odd').each(function(el) {
el.setStyle('color: red')
}) |
|
55.7 |
31 |
297 |
33.7 |
| 4 |
jQuery |
$('td.content span.even').before(
'<h3 style="display: none">text</h3>'
)</code |
|
382.7 |
177.3 |
373.7 |
205.3 |
| Prototype |
$$('td.content span.even').each(function(el) {
el.insert({
before:'<h3 style="display:none">text</h3>'
})
}) |
|
359 |
90.7 |
527 |
138.7 |
| 5 |
jQuery |
$('td.content h3').show() |
|
178.7 |
227.7 |
83.3 |
1161.7 |
| Prototype |
$$('td.content h3').each(Element.show) |
|
38 |
21 |
250.7 |
19 |
| 6 |
jQuery |
|
90 |
81.3 |
33.7 |
375.3 |
| Prototype |
$$('div.special').each(Element.hide) |
|
18 |
7 |
73.3 |
12 |
| 7 |
jQuery |
$('div.special, td.content .odd').toggle() |
|
637.7 |
431.7 |
517 |
1360.3 |
| Prototype |
$$('div.special, td.content .odd').each(Element.toggle) |
|
71 |
43.7 |
106.7 |
43 |
| 8 |
jQuery |
|
132.7 |
59.3 |
123.3 |
66.7 |
| Prototype |
$$('span.odd').each(Element.remove) |
|
29 |
11.7 |
36.7 |
19.3 |
| 9 |
jQuery |
$('#data p.lost:first').html('gotcha!') |
|
5 |
1.7 |
10 |
3.3 |
| Prototype |
$('data').down('p.lost').update('gotcha!') |
|
11.7 |
2 |
10 |
7.3 |
Conclusion #2
Prototype was at least 2 times faster then jQuery in 15 cases, and jQuery was faster then Prototype in 8 cases. What library should I choose? In my case I will stick with Prototype, because it offers the same functionality as jQuery does + more and it's faster. jQuery is probably better for projects where there's a need for some fancy UI effects and that's it, but it's just an assumption, correct me if I'm wrong...
Prototype 1.6.0 was released to the public 4 days ago, at first I decided to check its performance and new features in comparison to the previous version, but being interested in other JavaScript libraries, I’ve changed my mind. Lets see how you can accomplish same tasks with the latest Prototype and jQuery libraries and simply see which one is faster. In this part I’m going to show the results of running single operations, in next part(s) I will prepare a sample website and write more complex test cases and, again, check how fast jQuery and Prototype can be (or how slow…).
Test environment setup
The following software was used:
- Prototype 1.6.0
- jQuery 1.2.1
- Firefox 2.0.0.9
- Firebug 1.05
- Webdeveloper 1.1.4
Test page contains only one table with 500 rows, it’s generated using the following RHTML template:
<table>
<% (1..500).each do |i| %>
<tr>
<td class="first">
<div><%= i %></div>
</td>
<td class="second">
<div><%= "hello from #{i}" %></div>
</td>
</tr>
<% end %>
</table> |
Running the tests
Each operation was executed using Firebug’s console with Profiler turned on and repeated 3 times, between each execution the page was reloaded. Cache in Firefox was disabled using Webdeveloper plugin. Here are the results of 7 test operations showing average times and number of method calls in each operation:
1. Adding ‘marked’ CSS class to every cell having ‘first’ CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.first').addClass('marked') |
|
102.495 |
3032 |
| Prototype #1 |
$$('td.first').each(function(cell) { cell.addClassName('marked') }); |
|
117.162 |
7056 |
| Prototype #2 |
$$('td.first').invoke('addClassName', 'marked'); |
|
129.972 |
8062 |
Comment: no practical difference, although jQuery is a little faster.
2. Removing CSS class from every table cell having “second” CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.second').removeClass('second') |
|
109.855 |
3032 |
| Prototype #1 |
$$('td.second').each(function(cell) { cell.removeClassName('second') }); |
|
87.445 |
4551 |
| Prototype #2 |
$$('td.second').invoke('removeClassName', 'second'); |
|
100.64 |
5557 |
Comment: No big difference, especially when comparing Prototype #2 option with jQuery.
3. Setting ‘color’ CSS property in every div element which is inside a cell having ’second’ CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.second div').css('color', 'red') |
|
173.906 |
3536 |
| Prototype #1 |
$$('td.second div').each(function(el) { el.setStyle({ color : 'red' }) }); |
|
87.585 |
4563 |
| Prototype #2 |
$$('td.second div').each(function(el) { el.setStyle('color : red') }); |
|
98.283 |
5064 |
| Prototype #3 |
$$('td.second div').invoke('setStyle', 'color : red'); |
|
107.81 |
6070 |
Comment: Same here, Prototype 2 x faster
4. Adding new elements before every div element which is inside a cell having ’second’ CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.second div').before('<h3>text</h3>') |
|
368.611 |
15066 |
| Prototype #1 |
$$('td.second div').each(function(el) { el.insert({ before: '<h3>text</h3>' }) }); |
|
1252.016 |
17088 |
| Prototype #2 |
$$('td.second div').invoke('insert', { before: '<h3>text</h3>' }); |
|
1316.969 |
19096 |
Comment: Wow…jQuery is almost 4 times faster, I should point out that Element#insert is a new method introduced in Prototype 1.6.0.
5. Removing all div elements which are inside cells having ’second’ CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.second div').remove() |
|
151.955 |
2032 |
| Prototype #1 |
$$('td.second div').each(Element.remove); |
|
65.785 |
3060 |
| Prototype #2 |
$$('td.second div').invoke('remove'); |
|
94,105 |
5068 |
| Prototype #3 |
$$('td.second div').each(function(el) { el.remove() }); |
|
83.591 |
4062 |
Comment: Prototype more then 2 times faster.
6. Hiding and un-hiding all div elements which are inside cells having ’second’ CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.second div').toggle();$('td.second div').toggle(); |
|
1948.825 |
47653 |
| Prototype #1 |
$$('td.second div').each(Element.toggle);$$('td.second div').each(Element.toggle); |
|
158.987 |
14106 |
| Prototype #2 |
$$('td.second div').each(function(el) { el.toggle() }); $$('td.second div').each(function(el) { el.toggle() }); |
|
191.096 |
16110 |
Comment: jQuery tragedy, Prototype toggles visibility of 500 divs 10 times faster!
7. Attaching event listeners to all div elements which are inside cells having ’second’ CSS class:
| Library |
Operation |
Time (ms) |
Method Calls |
| jQuery |
$('td.second div').bind('click', function(){ alert(this.innerHTML) }) |
|
146.19 |
3535 |
| Prototype #1 |
$$('td.second div').invoke('observe', 'click', function(){ alert(this.innerHTML) }); |
|
169,889 |
11581 |
| Prototype #2 |
$$('td.second div').each(function(el) { el.observe('click', function(){ alert(this.innerHTML) }) }); |
|
164.456 |
10575 |
Comment: no practical difference.
General conclusion and impression
Executed tests show that Prototype seems to be faster then jQuery, with the exception of the new insertion method, which performance should be improved. Although I like jQuery syntax more then Prototype, the performance is way more important then saving few lines of code. Of course tests that I made don’t show how these libraries act in a real application, which is my task for the next part(s) of this article. Despite the results I must admit that I’m very excited about jQuery, my general impression is that this library is more mature then Prototype.
You may want to check out part ||