Ext JS Image Field

Yesterday I spent way too much time on another simple thing in EXT that I would have expected to work out of the box. What I needed was to upload a company logo to a profile and, of course, display that logo in the form. While it's very simple to display an image in a panel or box, it's a totally different thing to display the in image in the form and update it together with other form fields. What I needed is something like this:
and after form submit it should automatically load the new image:
For image upload I used the image upload plugin from EXT examples. To display the image I needed to extend the field component like this:
Ext.form.ImageField = Ext.extend(Ext.form.Field, {
    autoCreate: {tag: 'img'}
    ,setValue: function(new_value){
        if (new_value == undefined || new_value == null) {
            this.el.dom.src = '/images/no_image.png';
        } else {
            this.el.dom.src = '/images/thumbnail/' + new_value;
        }
    }
    ,initValue : function(){
          this.setValue(this.value);
    }

    ,initComponent: function() {
        Ext.apply(this, {

        });

        Ext.form.ImageField.superclass.initComponent.apply(this);
    }
});

Ext.reg('image_field', Ext.form.ImageField);

This is a bit simplified version as I removed the rails specific code, but anyway the main points are here:
line 2 creates image tag (rather than the default text field tag)
line 5 defaults the image src property to a placeholder (in case there's no image uploaded)
line 7 - in case there is a valid image it will change the src path to that image - in my case it's routed to Images controller and action thumbnail which returns a downscaled version of the image.
Other than this don't forget to set the FormPanel's fileUpload: true and refresh the form on submit success. I usually send the updated object JSON in the response to update action.:
  def update
    contact = Contact.find(params[:id])
    on_bind(contact)
    success = contact.save
    contact = Contact.find(contact.id) if success
    
    logger.info("saved")
    
    respond_to do |format|
      format.ext_json do
        json = contact.to_ext_json(:success => success,
                                   :methods => [:image_id],
                                   :includes => [:address],
                                   :messages => {:notice => l(:contact_updated), :warning => l(:contact_update_error)})
        render :json => json #:json => {:success => success}
        headers["Content-Type" ] = "text/html"
      end
    end
  end

!!!Notice line 16 - you have to manually set the content type to "text/html" because rails will default to "text/javascript" which will not work with file uploads!!! It works with fileUpload: false but it doesn't with fileUpload: true. I don't really know if Rails or EXT is right here but did take me some time to figure this out.
this will produce the following JSON (I removed the unnecessary fields):
{"success": true,
    "messages": {"notice": "Contact was successfully updated.", "warning": "contact_update_error"},
    "data": {"contact[image_id]": 15, "contact[active]": false, "contact[client_account_id]": 2, "contact[client_account_no]": "100",
        "contact[created_at]": "2009-07-07T10:31:54+08:00", "contact[credit_term_id]": 1}}
Now in the forms success method I can use the result.data to update the form:
success: function(form, action) {
                      form.setValues(action.result.data);
                  },

Dear Railsists, Please Don’t be Obtrusive

obtrusive_or_not.png Update: thanks to Jon Wood aka jellybob, a prototype demonstration has been added, which is even better than my original jQuery btw as it degrades gracefully. Check it out in the ‘prototype-unobtrusive’ directory.

I am guessing 9 out of 10 of you reading the title is prepared for yet-another Rails drama on some obtrusive community members, and because everyone is tired of Rails dramas, I am risking that some of you won’t care to read the article - but I couldn’t resist :-). Actually I’d like to talk about usage of (un)obtrusive Javascript - why is it a bad idea to be obtrusive, especially given that (as you will learn from the article) writing unobtrusive Javascript is not harder, and you get the warm, fuzzy feeling of writing nice and clean code!

The Drill

To demonstrate the differences, I’ll lead you through the creation of a quick AJAXy shout wall both the default/standard (and obtrusive) way, then do the same with unobtrusive Javascript to show you that contrary to the popular belief, you don’t need to memorize the “Tome of Javascript Black Magick Tricks” by heart, use obscure libraries or special coding techniques to achieve clean, unobtrusive code. The shout wall is simply a form for posting a new message, and a list of messages below it, like so:

shout_wall.png

(You can check out the code used in this post from it’s github repository).

The Standard Way

Note: If you’d like to follow along, please use the provided pastie links - do not try to cut & paste multiple lines from the page (single lines are OK), as it will be b0rk3d.

  1. Creating a new Rails application
    rails obtrusive-shout-wall
      
    1. rails obtrusive-shout-wall
  2. Get into the Rails dir
    cd obtrusive-shout-wall
      
    1. cd obtrusive-shout-wall
  3. Generate the resource message
      
    script/generate resource message
      
    1. script/generate resource message
  4. Add this the following to the generated migration (some_timestamp_create_messages (Get it from pastie):
    t.string :author
    t.text :message 
    
    1. t.string :author
    2. t.text :message
  5. Run the migrations:
    rake db:migrate
    
    1. rake db:migrate
  6. Because we want to view the messages in reverse order (newest one first), we add a default scope to the Message model (in message.rb):
        default_scope :order => 'created_at DESC'
    
    1. default_scope :order => ‘created_at DESC’
  7. Create the application layout - create a new file in app/views/layouts called application.html.erb, and fill it with the following content (Get it from pastie):
    
      
        <%= stylesheet_link_tag "application" %>
            <%= javascript_include_tag :defaults %>     
      
        
        <%= yield %>
        
    
    
    1. <html>
    2.   <head>
    3.     <%= stylesheet_link_tag "application" %>
    4.         <%= javascript_include_tag :defaults %>     
    5.   </head>
    6.     <body>
    7.     <%= yield %>
    8.     </body>
    9. </html>
  8. Create a file application.css and drop it into public/stylesheets. Add the following content (Get it from pastie):
    body {
        background-color:#FFFFFF;
        color:#333333;
        font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;
        margin:0 auto;
        padding:0;
        text-align:center;
        width:960px;
    }
    
    #messages {
        text-align: left;
        margin-left: 80px;
        margin-top: 50px;
    }
    
    #message-form {
        text-align: left;
    }
    
    #message-form dl {
        margin:10px 0 0 80px;
    }
    
    #message-form dd {
    color:#666666;
    font-size:11px;
    line-height:24px;
    margin:0 0 5px 80px;
    }
    
    #message-form dt {
        float:left;
        font-size:14px;
        line-height:24px;
        width:80px;
      text-align: left; 
    }
    
    #author {
      margin-right: 640px;
    }
    
    #message {
      width: 600px;
        height: 200px;
      margin-right: 194px;
    }
    
    .message {
      margin-bottom: 20px;
    }
    
    .first_row {
      padding-bottom: 10px;
    }
    
    .message-meta {
        font-size: 12px;
    }
    
    .author {
      color: #FF5050;
        font-weight: bold;
    }
    
    .new-message-label {
      text-align: left;
      padding-top: 30px;
      margin-left: 80px;
    }
    
    #submit-button {
      float : right;
      margin-right: 195px;
      margin-top: 10px;
    }
    
    1. body {
    2.     background-color:#FFFFFF;
    3.     color:#333333;
    4.     font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;
    5.     margin:0 auto;
    6.     padding:0;
    7.     text-align:center;
    8.     width:960px;
    9. }
    10.  
    11. #messages {
    12.     text-align: left;
    13.     margin-left: 80px;
    14.     margin-top: 50px;
    15. }
    16.  
    17. #message-form {
    18.     text-align: left;
    19. }
    20.  
    21. #message-form dl {
    22.     margin:10px 0 0 80px;
    23. }
    24.  
    25. #message-form dd {
    26. color:#666666;
    27. font-size:11px;
    28. line-height:24px;
    29. margin:0 0 5px 80px;
    30. }
    31.  
    32. #message-form dt {
    33.     float:left;
    34.     font-size:14px;
    35.     line-height:24px;
    36.     width:80px;
    37.   text-align: left;
    38. }
    39.  
    40. #author {
    41.   margin-right: 640px;
    42. }
    43.  
    44. #message {
    45.   width: 600px;
    46.     height: 200px;
    47.   margin-right: 194px;
    48. }
    49.  
    50. .message {
    51.   margin-bottom: 20px;
    52. }
    53.  
    54. .first_row {
    55.   padding-bottom: 10px;
    56. }
    57.  
    58. .message-meta {
    59.     font-size: 12px;
    60. }
    61.  
    62. .author {
    63.   color: #FF5050;
    64.     font-weight: bold;
    65. }
    66.  
    67. .new-message-label {
    68.   text-align: left;
    69.   padding-top: 30px;
    70.   margin-left: 80px;
    71. }
    72.  
    73. #submit-button {
    74.   float : right;
    75.   margin-right: 195px;
    76.   margin-top: 10px;
    77. }
  9. Create a new action, index in MessagesController (Get it from pastie):
    def index
      @messages = Message.all    
    end
    
    1. def index
    2.   @messages = Message.all   
    3. end
  10. This goes into app/views/messages/index.html.erb (Get it from pastie):

    Enter new message!

    <% remote_form_for :message, :html => {:id => "message-form"} do |form| %>
    Author:
    <%= text_field_tag 'author' %>
    Message:
    <%= text_area_tag 'message' %>
    <%= submit_tag "Submit!", :id => "submit-button"%> <% end %>
    <%= render :partial => 'message', :collection => @messages %>
    1. <h3 class="new-message-label">Enter new message!</h3>
    2. <% remote_form_for :message, :html => {:id => "message-form"} do |form| %> 
    3.   <dl>
    4.         <dt>Author:</dt>
    5.     <dd><%= text_field_tag ‘author’ %></dd>
    6.         <dt>Message:</dt>
    7.         <dd><%= text_area_tag ‘message’ %></dd>
    8.     </dl>
    9.     <%= submit_tag "Submit!", :id => "submit-button"%>
    10. <% end %>
    11.  
    12. <div id="messages">
    13.     <%= render :partial => ‘message’, :collection => @messages %>
    14. </div>
    We are showing the form for the messages and list the already exiting messages below the list. Note that we are using the _remote_form_for_ Rails helper to create an AJAXy form. This is already obtrusive, since if you observe the generated HTML, you will see that the form has an onsubmit parameter with some horribly looking code attached to it.:

    Obtrusive helper.png

    Sure, you can go ‘meh’ all the way, but slinging Javascript code all over the place is just as bad idea as writing inline CSS (or even worse, using HTML code for styling) or putting Rails code into views. It will work without any problems - but it’s just not the right way of doing things, especially if your code is going to hit a certain size.
  11. You probably noticed that we are rendering a message as a partial - so create a partial file app/views/messages/_message.html.erb with the following content (Get it from pastie):
    ">
    on <%= message.created_at.to_formatted_s(:long_ordinal) %>, <%= message.author %> said:
    <%= message.message %>
    1. <div class="message" id="message-<%=message.id%>">
    2.   <div class="message-meta">on
    3.    <%= message.created_at.to_formatted_s(:long_ordinal) %>,
    4.    <span class="author"><%= message.author %></span>
    5.    said:
    6.   </div>
    7.   <div><%= message.message %></div>
    8. </div>
  12. We need a ‘create’ action in MessagesController in order to process the form submission (Get it from pastie):
    def create
      @message = Message.create(:author => params[:author], :message => params[:message])
    end
    
    1. def create
    2.   @message = Message.create(:author => params[:author], :message => params[:message])
    3. end
  13. And obviously we’ll need to render something to respond to the create action. Using the standard Rails way, RJS, we might come up with something like this (in app/views/messages/create.js.rjs - Get it from pastie):
    page.insert_html :top, "messages", :partial => 'message', :object => @message
    page.visual_effect  :highlight, "message-#{@message.id}"
    
    1. page.insert_html :top, "messages", :partial => ‘message’, :object => @message
    2. page.visual_effect  :highlight, "message-#{@message.id}"
    Here we insert the “messages” partial, using the just created @message, and throw a splash of yellow fade into the mix for good measure. Easy peasy.
  14. We are done! Fire up script/server, hit localhost:3000/messages and voila!

The Good Way

Here I am presenting only the steps that are different from the above - i.e. if step 3 is skipped, use the one from above.

  1. Creating a new Rails application
      rails unobtrusive-shout-wall
      
    1. rails unobtrusive-shout-wall
  2. Get into the Rails dir
    cd unobtrusive-shout-wall
      
    1. cd unobtrusive-shout-wall
  3. Same as above
  4. Same as above
  5. Same as above
  6. Same as above
  7. Since we are going to use jQuery (unobtrusiveness is *not* a property of jQuery, you can be just as unobtrusive with Prorotype - but I switched to jQuery just before learning how, and now I am lazy to go back check out how in the ‘prototype unobtrusive’ directory in the github repository), you have to download jQuery with some basic effects, as well as an AJAX form handling library (still from the directory unobtrusive-shout-wall - Get it from pastie):
  8. curl http://jqueryjs.googlecode.com/files/jquery-1.3.1.min.js > public/javascripts/jquery.js
    curl http://www.malsup.com/jquery/form/jquery.form.js?2.28 > public/javascripts/jquery-form.js
    curl http://view.jquery.com/tags/ui/latest/ui/effects.core.js > public/javascripts/effects.core.js
    curl http://view.jquery.com/tags/ui/latest/ui/effects.highlight.js > public/javascripts/effects.highlight.js
    
    1. curl http://jqueryjs.googlecode.com/files/jquery-1.3.1.min.js > public/javascripts/jquery.js
    2. curl http://www.malsup.com/jquery/form/jquery.form.js?2.28 > public/javascripts/jquery-form.js
    3. curl http://view.jquery.com/tags/ui/latest/ui/effects.core.js > public/javascripts/effects.core.js
    4. curl http://view.jquery.com/tags/ui/latest/ui/effects.highlight.js > public/javascripts/effects.highlight.js
    and replace
    <%= javascript_include_tag :defaults %>
    
    1. <%= javascript_include_tag :defaults %>
    with
    <%= javascript_include_tag 'jquery' %>      
    <%= javascript_include_tag 'jquery-form' %>
    <%= javascript_include_tag 'application' %>
    <%= javascript_include_tag 'effects.core' %>
    <%= javascript_include_tag 'effects.highlight' %>       
    
    1. <%= javascript_include_tag ‘jquery’ %>     
    2. <%= javascript_include_tag ‘jquery-form’ %>
    3. <%= javascript_include_tag ‘application’ %>
    4. <%= javascript_include_tag ‘effects.core’ %>
    5. <%= javascript_include_tag ‘effects.highlight’ %>
    in the layout file.
  9. Same as above
  10. Same as above
  11. Same as above - just delete “remote” from the name of the helper, i.e. use a standard Rails view helper, form_for
  12. Same as above
  13. Since we are not relying on Rails to do the rendering for as via a template file, we return the html chunk that we will render from Javascipt. So your create action should look like (Get it from pastie):
    def create
      @message = Message.create(:author => params[:author], :message => params[:message])
      render :partial => 'message', :object => @message
    end
    
    1. def create
    2.   @message = Message.create(:author => params[:author], :message => params[:message])
    3.   render :partial => ‘message’, :object => @message
    4. end
  14. Now comes the fundamentally different part - instead of using RJS to respond to the create action, we move all our code to application.js (Get if from pastie):
    $(document).ready(function() {      
      $("#message-form").ajaxForm({success: handleNewMessage});
    
      function handleNewMessage(response, statusText) {
        $("#messages").prepend(response).effect("highlight", {}, 1500);
      }    
    });
    
    1. $(document).ready(function() {     
    2.   $("#message-form").ajaxForm({success: handleNewMessage});
    3.  
    4.   function handleNewMessage(response, statusText) {
    5.     $("#messages").prepend(response).effect("highlight", {}, 1500);
    6.   }   
    7. });
    I don’t think so that this code is particularly more complicated or hard to understand that the RJS one. Everything is inside the ready() function, which means that it’s only run once the document is properly loaded. Then we declare that “#message-form” is an AJAX form, and that upon successful submission, the handleNewMessage() method should be called. And if that happens, we add the response (which is the return value of the “create” action) to the “#messages” div, just as we did in RJS. Then we apply the yellow fade! w00t!
  15. Same as above

(You can check out the code used in this post from it’s github repository).

Conclusion

As you can see, the only real difference between the obtrusive and non-obtrusive version is in the last 2 points (downloading and including the jQuery header files can be easily solved with Rails templates): instead of leaving the rendering part to Rails, we return the response as a string and dynamically insert it from jQuery. With about the same effort, we kept all the Javascript code in application.js, which is much cleaner this way (you can open up 1 file and check out all the JS/AJAX behavior in one place), especially after introducing a lot of Javascript functionality into your code - in other words, for the same amount of work we got something much better. Please try to keep this in mind when you are working with Javascript and Rails the next time - believe me, it can save you from a lot of pain!

File Downloads in Rails on Apache

Over the past half a year we worked on several systems for logistics/courier/delivery companies. It was quite fun as a big logistics and inventory control system was my very first project when I arrived to Asia 9 years ago. Last week we launched one of them. It was slightly different as most of our systems are used mainly internally but this one is also used by clients of the courier company to book deliveries. Each delivery requires printing of confirmation documents, internal routing documents, then signed and stamped documents are uploaded back to the system as a proof of delivery for clients. It has quite a number of other (technically) interesting features like integration with google maps to geo-tag the orders and notifying drivers in the area - I'll write about it soon.

As this courier company handles several hundreds of deliveries every day it requires quite a number of uploads/downloads of documents and images. The issue with uploads and downloads is that they will tie up rails processes for much longer than usual requests. The problem is that rails processes are quite expensive in terms of memory (around 70 - 100MB for simple operations). Do 20 concurrent uploads/downloads and all your rails processes and memory will get tied up - and quite wastefully as there is no 'dynamic' processing during upload or download. The process just sits there and waits for the file to get delivered. Luckily there's a simple way how to move this burden to Apache. This kind of upload/download processes will take only around 2MB on average and your rails processes are free to handle the dynamic requests.

To let apache handle file downloads use x_sendfile. All you need to do is install x_senfile module on apache and adjust you config a little. Here's the installation instructions for Ubuntu 8.04:
cd /usr/local/src
wget http://tn123.ath.cx/mod_xsendfile/mod_xsendfile.c
apxs2 -cia mod_xsendfile.c
Open apache config file and add the module line:
sudo vim /etc/apache2/apache2.conf
LoadModule xsendfile_module /usr/lib/apache2/modules/mod_xsendfile.so
Add the following 2 lines to your virtual host file
sudo vim /etc/apache2/sites-available/my_app
XSendfile on
XSendFileAllowAbove on
You will have to restart the apache and you should be done:
sudo /etc/init.d/apache2 restart
Now in your controller handling download add x_sendfile attribute and set it to true
 send_file("/home/test/big_file.zip",
           :filename => "big_file.zip",
           :type => "application/zip,
           :x_sendfile=> true)
The uploads are equally easy. You just need to apache modules libapreq2 and modporter.
wget http://mirror.nus.edu.sg/apache/httpd/libapreq/libapreq2-2.12.tar.gz
tar -xzf libapreq2-2.12.tar.gz
cd libapreq2-2.12
./configure --with-apache2-apxs=/usr/bin/apxs2
make
sudo make install
Now download and install modporter. Get it from the github. Either use the download link and unzip or just clone the repository
git clone git://github.com/actionrails/modporter.git
cd modporter
On Ubuntu I had to change reference to apxs in Rakefile as the in ubuntu it's apxs2.
vim Rakefile
Change the APXS line to the following:
APXS           = "apxs2"
No just run rake:
sudo rake
add the following to your apache2.conf
sudo vim /etc/apache2/apache2.conf
LoadModule apreq_module /usr/lib/apache2/modules/mod_apreq2.so
LoadModule porter_module /usr/lib/apache2/modules/mod_porter.so
To actually use it in your rails application you will need modporter plugin.
script/plugin install git://github.com/actionrails/modporter-plugin.git
Add the following to the application_controller.rb (application.rb before Rails 2.3)
class ApplicationController < ActionController::Base 
mod_porter_secret = "secret"
.
.
and just enable porter in you vhost file:
sudo vim /etc/apache2/sites-available/my_app
Porter On
PorterSharedSecret secret
And that's all now if you check the uploaded files they will be a ModPorter::UploadedFile Credit for this goes to Koz from theRailsWay.com - you can find more information here.

Rails *is* (still) a Ghetto

nice_ass.png

While I know the title is both asking for trouble (because of the now anecdotal original article with a similar title) and flamebaity, please read on - my goal is not to get some great stats but rather to know your opinion about the situation and discuss the possible solutions of the problem.

How it all started…

I would not like to re-iterate what has been said on several blogs, just to summarize: Matt Aimonetti, member of the Rails Activists, gave a presentation at GoGaRuCo which contained sexually explicit images (according to some - I am not here to judge whether that’s true, and it doesn’t matter anyway, as you’ll see in the rest of the post).

I am not really discussing whether it’s appropriate to have images of nude chicks in your presentation at a Ruby conference (I think it’s not, it’s unprofessional etc. - but that would be a matter of a different post Update: Someone summed this up in the article’s reddit thread nicely: If you’re a Rails programmer, or a Ruby programmer, and you don’t decry this sort of thing, you have no business calling yourself a professional. It doesn’t matter how large your website is, how easy it was to write, how much better it is over PHP or ASP.NET or J2EE; by definition, you do not belong to a professional community. That’s all there is to it. It’s incumbent on every Ruby programmer to either reject this sort of misogynistic sewage, or accept that you’re never going to advance the promotion of Rails in the public perception because members of the community still think it’s edgy or cool to put pictures of strippers in their public presentations. And here’s a hint: if your decided reaction is to talk about how unimportant this is, how much it doesn’t matter, or how much it doesn’t offend you personally, you probably don’t understand professionalism at all.) because sadly, I think there are far bigger problems here than that - shedding light on them is the real purpose of the article, not talking about pr0n at GoGaRuCo again.

Would You Walk Into a Hindu Temple with Your Shoes on?

hindu_temple.pngI have been living in India for 2 months last summer, working on a Rails startup. Maybe I am odd or something, but I knew that I had to remove my shoes when entering a Hindu temple, and no one had to convince me (what’s more, I didn’t even think about it for a second) wether this is the right thing to do, why is it so, whether I should do otherwise etc. This is a similar situation - I just don’t do X when speaking at a conference, if I suspect that X makes feel even one person in the room uncomfortable, whether because of his gender, race, nationality, Ruby/Rails skills, penis size or what have you - regardless whether I think it’s fine for me, my wife, for other members of the community and/or the majority of the room.

The trick is, how does a hindu feel when I enter a temple in footwear (even if that is perfectly acceptable in my country, culture, family, friends) - it’s perfectly irrelevant how do I feel in the given situation. Using the previous paragraph, try to apply this to a Ruby/Rails conference.

Shit happens…

Until this point in the story, I see no problem at all, and could even agree with the guys asking “what’s wrong with you, don’t make a fuss out of nothing” - the pictures Matt used are non-problematic in my book, and he had no idea they are problematic in anyone’s book - theoretically it could have worked, but the point is, it did not. Some members of the Ruby community got offended, and here our story begins.

…and hits the fan

One of the real problems is that after this has been pointed out, Matt still keeps answering “As mentioned many times earlier, I don’t think my presentation is inappropriate.”. As I mentioned two paragraph above, it doesn’t matter what do you think, unless of course, you don’t care about offending some members of the community. In that case you should not try to apologize at all. However, if you are trying, reciting “I don’t think my presentation is inappropriate” will not put and end to the discussion. It just doesn’t work. Why can’t you just simply apologize, admitting that this was a bad move (because it offended some, not because porn, sexual images or whatever in presentations are bad, per se) and finish the discussion?

Rails is Still a Ghetto

However, in my opinion that’s still not the worst part of the story, or to put it differently, some members of the Rails community still found a way to make things worse, by applauding to all this:

dhh_pr0n_is_great.png

OK, you say, we are all used to DHH’s style, this is just how the guy is. That’s (kind of) cool, but I heard that most of the Rails core team (and obviously Matt himself) has the same opinion - and that’s a much more serious problem, because it means that a Rails activist, backed by DHH and other Rails core members finds all this OK, despite of the fact that numerous people in the community voiced their opinion otherwise.

This is not about being a closed-minded prude, shouting for police and suing everyone using sexually explicit images in a presentation. This is not even about women, as I have seen both males and females on either side of the fence. This is about mutual respect - I don’t agree with you, but respect your feelings. Or not, as demonstrated in this case.

So Rails continues to be the most socially unacceptable framework - associated with arrogance, elitism and whatnot in the past - now add pr0n images in presentations. Thankfully RailsConf is held in Las Vegas, and that should calm down all the people who associate Rails with all this crap :-). The real problem is that people associate you with the tools you are using - think Cobol, PHP, Java… or Rails. By being part of the Rails community people associate me with Railsy stereotypes automatically, which aren’t nice at all right now.

I hear you, dear creme-de-la-creme Rails (core) member, I know you don’t give a shit, and you think this is all prude babbling - because your hourly rate is more than some of us earn in a day, and you’ll be sought after even if Rails will have a much worse image than it has now. But 99.9% of us are not in the ‘circle of trust’ and would be happier if Rails would not be constantly associated with a ghetto.

MINASWUBN

In case you are wondering what does the acronym stand for, it’s “Matz is Nice And So We Used to Be Nice”. Unfortunately, the stuff I don’t like about the Rails community is sneaking into Ruby too, it seems, as the above case demonstrates. Besides this, the count of aggressive comments and reactions on various blog posts is really disturbing to me. Please (at least Rubyists) try to avoid being contaminated by all this shit and stop thinking you are cool because you can swear on a forum (always in anonymity). You don’t have to be a douchebag just because you are a Rubyist / Rails coder, as surprising as this might sound to some.

Conclusion

I think “incidents” like this and getting more and more antisocial members are inevitable by-products of growth in a community. The questions is, whether, and if, how, do we stop them. The problem is that it seems to me the Rails “top management” doesn’t want to stop them (what’s more, even encourages them) in the first place (please prove me otherwise - maybe I don’t see the full story - I’ll be the happiest to admit that I am talking bullshit).

I have to admit I have no clue what would be the right move - burying our heads in the sand and pretending everything is fine is not. Please leave a comment if you have an idea or anything to add.

Another Training

Another training's over. Very different but equally challenging.
Seems that Ruby's been making headway to corporate world as most of the participants this time were there on their employers' budget. When I compare it with our first RoR trainings around 2 years ago with Rails 1.0 just out and more than 30% of the things still on a wish list and the only way to learn RoR was Agile Web Development With Rails...this time it was much more business-like. There's like 20 books out, including a (bit obsolete) video from Linda.Com and pretty much everybody knows about rails.
Another aspect that has changed is Rails versions. With Rails 2.3 and Ruby 1.9 out there's a lot of changes and incompatibilities that complicate the learning. Most of the books and reference materials use Rails 2.0 or early versions of 2.2. This makes it little bit difficult to have training on 2.3 as most of the "further" reading is incompatible. While the incompatibilities may seem easy to solve for experienced developers they can be quite exasperating for a newbies. On the other hand, people are quite reluctant to start learning something that is already obsolete.
Similar is true for Ruby 1.9, even though after burning they're fingers several times until it got finally compatible with Rails most people would still prefer to use 1.8.6 or 1.8.7.

The First and Second Ruby on Rails Training in Singapore

It may seem like I have nothing much to do, but I volunteered for yet another cool initiative called code::XtremeApps:: competition. It's hard to say what was the main motivation – if the chance to promote Ruby on Rails or to promote software development as such. Either way – a nice challenge around here. Programmers in Singapore are generally in a very very low standing – possibly somewhere around a construction worker :-). The general position(ing) is that programming is a low level blue collar activity facing the same fate as manufacturing and should be outsourced to India, China and only “high level” jobs like sales and project management should be done here. While there's something to it, it's a little strange when most of the people I interviewed said they didn't really like IT and they'd studied computer science to become managers.

Considering this situation I couldn't believe the tremendous response this programming competition received. All our initial estimates were left far far behind – the number of participants for the competition itself, but even more for the accompanying trainings (both Ruby on Rails and Google Gadgets). The training capacity of 120 people was surpassed in less than 48 hours. As for the competition we had to almost double the capacity and as of today there's only a few places left. The training will be this Saturday 01/09/2007 – you can have a look at the agenda here. The training is specifically targeted at the competition participants. Unfortunately, we will have only 1 day and as the competition focus is on innovation and integration we will be mostly covering mostly more advanced topics like AJAX, mash ups and media integration. As such there will be only 3 hours to cover (or discover?) actual RoR framework. I will be doing this training with Choon Keat. It's been a great pleasure working with him as he's not only a great (RoR) programmer but turned out to be very helpful, resourceful and reliable in everything that needed to be done.

The second training is of a very different nature. It's much more in-depth – and much more pragmatic, focused on development of actual business applications rather then competition prototypes. It will be a 2 days training on 15-16/09/2007 and it will provide a much more intimate hands on experience. There's more information here.

The second training has been in the making for around a year now. I started working on it around the time I hit first difficulties when working with Changi Prison and I've been using and improving parts of it on pretty much all my one – to – one trainings. After writing the article about Changi Prison I received a lot of emails asking more about the experience, how it was different from my outside trainings and mostly about my motivation for doing something like that. Strangely enough, I had a strong dejavu experience 2 weeks ago – during the competition launch at SMU. The setting couldn't have been more different – yet somehow it brought back all my memories from my first trainings in prison. When I started my presentation more than 75% of the room (including me :-) was asleep. By the time I finished I don't think there was anybody there that wouldn't want to learn more about RoR or at least try it out. And I guess that is the answer. It's the energy of the people in front of you, their questions and their thirst for more. It's the transformation they go through learning from you and it's the transformation you go through learning from them.

And as much as it is a motivation – it's a responsibility.

Ruby on Rails Hosting

This is a problem I've been (surprisingly) fighting with ever since I've been working with Ruby on Rails. Even though the situation has improved tremendously over the past 2 years it's still far from ideal. I am not sure if it's because insufficient demand or the increased complexity compared to hosting HTML or PHP the big hosting companies don't show much interest in Ruby hosting. Sure they list it as one of the options but the offer and support is flaky at best, many times offering only 1 rails application per account or missing ways to restart FCGI processes. Not surprisingly rails developers had to take the matter in their own hands and as a result almost any RoR hosting worth considering was started like that. I am not sure how much long term market sense it makes but looking at waiting time at slice host it really seems to be working for now.

When choosing RoR hosting the main decision you have to make is shared hosting vs. VPS. Shared hosting is usually cheaper and easier to set up but you're sharing the server with many other users and it takes only one bad neighbor to make the whole server unusable. VPS, on the other hand, is slightly more expensive and you have to set up everything your self. That gives you a lot of power and flexibility but you have to be able to configure Linux server (it's really no rocket science – there's plenty of How-To-s on the net – including this blog).

Shared Hosting

A few things to watch out for when choosing shared hosting:

  1. FCGI support – most of the hosts offer FCGI (but there are still many that offer only CGI) including an easy way of restarting FCGI processes. You really don't want to open up a support ticket any time you update you application and especially not when you fix a very urgent bug.
  2. 24/7 Support – while 24/7 support is claimed to be a standard in hosting industry I am yet to really find the host with this kind of support. That is not to say that the support doesn't matter – it really does and even if not 24/7 you should really test them out first – open up tickets outside of business hours, on public holidays, etc. and see the response time as well as helpfulness and professionalism. Many big hosting companies outsource their support offshore and all you'll get outside the working hours is “I will get a senior technician to look into the problem”. This is especially important if you're not in different timezone.
  3. Number of applications you can host under one account. This includes number of domains, number of subdomains as well as number of databases that you can create. You don't have to worry that much about the actual limits of the server here because if you're not going to utilize the resources it doesn't make them available during peak, it just means that somebody else will.
  4. DNS management tools / support. This is not so important if you have 1 application but as the number increases you will have to take care of multiple domains and multiple subdomains under each domain. With the standard shared hosting you will usually only get cPanel that lets you maintain only domains/subdomains hosting on the same server. Reseller account should come with WHM that has proper DNS management tool. Of course you have always option to host your DNS elsewhere.
  5. Disk space / bandwidth limits. Usually not a problem with U.S. hosting but most of the hosting companies in Asia still offer 100MB accounts.
  6. SSH. It's very hard if not impossible to set up your rails application without SSH access and yet I've seen several hosts (mostly in Asia) to offer ruby hosting without SSH. When I asked how to install the application they asked me for step by step instructions :-). I really don't think you want to do that.
  7. SVN hosting – not crucial but a very nice bonus. It's beneficial even if you're a sole developer as it makes your repository available online. RailsPlayground.Com even bundles this with Trac.

Looking at the list seems like there's quite a few things to watch out for. From what I've used the best seems to be the RailsPlayground.Com. They have a very reasonable reasonable support, very few limits and offer quite interesting packages. The downside is that the servers do get overloaded sometimes and then they kill off your processes. This is really bad as the user will get a Rails Application Error but the Exception Notifier will not generate anything and there won't be anything in the logs. I've had several other issues there – longer HTTP POST will generate application error – again without any trace in logs and I had some intermittent problems with file upload / download. Other then that I would recommend them as most probably the best RoR shared hosting out there.

VPS Hosting

VPS experience a stellar launch to popularity over the past year solving most of the problems of shared hosting for only slightly higher price. It had an easy job replacing dedicated hosting offering roughly the same but for 10 times higher price.

Here are some things to watch out for:

  1. 24/7 Support – even though it's all maintained by you and you're much less dependent on support, there still will be times when your VPS doesn't come back after restart or doesn't respond due to some runaway processes.
  2. Choice of Linux distributions – many hosts offer a selection of distributions like Ubuntu, Debian, RedHat, etc. You just choose your desired flavor from the menu and it's automatically installed for you. It's really helpful when you have experience with only one Linux flavor or to synchronize your installations when you have multiple servers at several hosting companies.
  3. Memory / Price ratio. The only important resource when choosing VPS is memory. Most of them come with sufficient disk space and run on multiprocessor machines but provide only limited memory. Anything below 256MB is not worth considering. You shouldn't pay more then US$ 29 for 256MB and usual price is around US$ 20. Some hosts provide burstable memory which means you can go over your memory limit if nobody else is using it. This can be very helpful during random hit surges or when processing memory extensive tasks. Be very careful as some of the hosts will mercilessly cut off any process that goes about the memory limit causing rails application error without any trace in logs (otherwise great VPSLink.com does that). Another thing to watch out for here is SWAP. While not ideal, it can save your ass during peak requests. Some hosts don't allow any SWAP which will cause your application to crawl when the memory limit is reached.
  4. Scalability – check how easy it is to upgrade your account – either to increase memory or to add on another server. When number of users increases, adding another server is many times the only option to scale your application.

One of the best VPS providers is Slice Host providing all of the above for the lowest price on the market. So far, I've experienced only one short downtime. You can choose your Linux distribution, reinstall everything within a few minutes they have no nonsense policies, upgrades in both directions are painless. The only downside is a long waiting list if you're a new user. Another great host is Rose Hosting. They offer burstable memory and used to have very competitive pricing. I couldn't find any way to add another option to add servers to my account.

Some general things to watch out for:

  1. cancellation policy. It's very important to read and understand it as most of the hosts have ridiculous cancellation policies – like you have to cancel at least 3 months ahead, or only 10 days before the end of the month or only on Monday, Wednesday and Sunday 3 – 4 am. Also, money back guarantee is much more a dream then reality.
  2. Server location. Many people believe believe that the closer the server is to them the faster it is. I've heard many scientific explanations to this, but based on my experience there is usually no difference in access time unless it's on the same subnet (i.e. same provider) which is hardly a case. There is so many other factors (like aggregation, PC speed, last mile connection) that will affect the actual speed that for me it doesn't make any difference in speed for my servers located in the U.S. and servers located in Singapore. The problem with local (Singaporean, Malaysian :-) hosting is that it's several times more expensive, provides several times less resources (like space, bandwidth, memory, etc.) and only 9 – 5 support. I believe this is due to lack of local competition, lack of market awareness and undue local patriotism. Anyways, ...
  3. Backup – some hosts provide automatic backup for very reasonable price (e.g. Slice Host offers images for US$ 5). While most of the hosts claim to have auto backup it happened to me several times that it took them 2 days to retrieve this backup. As such you should think of your own back up strategy – one of the ways is to use Amazons AWS. Most of our customers require direct access to back up files and recovery within 30 minutes (this means that no matter what happens we have to be able get back online within 30 minutes). Another issue is frequency of back up – most of the auto back up is daily, which is far too little for any production application. Our standard is hourly backup with possibility to increase this during peak hours.

Corporate Trainings

I finished my first commercial Ruby on Rails application in October 2005 and I've been using it ever since. For most of this time, we've been one of the very very few offering Ruby on Rails in Singapore. Most of our customers are not exactly technical so they wouldn't really care even if we used Cobol :-). What they did care about was the price and delivery time. I don't know why, but writing about advantages of Ruby (and Ruby on Rails) comes very unnatural to me. It feels like saying what everybody knows. At the very minimum, most of the technical people have heard about it – whether they like it or hate it at least the don't go like “You're programming in WHAT???”

The situation seems to be turning even in this part of the world. Thanks to Ruby brigade I met a handful of Ruby on Rails programmers and a few handfuls of wannabes. After almost 2 years in isolation it was really great to see and meet Ruby programmers (other then my own trainees and employees :-). While all this was nice, what really made an impact was ever increasing demand for corporate training. More and more big companies approach me with corporate trainings. The reason is simple...as many are parts of global corporations, many of their internal systems were created elsewhere and now they need a local support. It may come as a shock that MNCs use other systems than just SAP, but it seems to be easier and way cheaper to use a few smaller, independent systems for handling a portion of daily operation.

I started my first Ruby course in April 2005 and for the past 6 months I spent roughly 40% of my time doing training. This has been a major shift for me as only as recently as 1 year back I spent around 90% programming and only 10% on other stuff. I started training in Changi prison and it's almost unbelievable how rewarding this experience has been. I cannot even describe how much the guys have changed. When I walked there the first time I sure saw the potential – around 30 guys doing (or more fighting) with every possible limitation you can think of but still determined to succeed. It may be an old truth that you will appreciate the value of something only after you've lost it but it's really valid. All of the ubiquitous things like Internet connection, access to new books, access to community support (reading the posts or posting questions) – all this is off limits in prison. And yet I didn't find any surviving broken existence in there. So many times I have wished my own employees showed half of the determination, zeal and persistence I could see in there. The more you have, the more you start taking things for granted and the more comfortable you become.

What I felt the guys needed was just better resources and some direction (IT wise). We went through some really tough time – I still remember explaining recursion for the whole Sunday, OOP, TDD and many other things. Well, none of them had any prior formal IT education. But then, maybe it was more to their advantage. Most of the time I find it much harder to work with computer science graduates than with people who never studied IT but have some logical and mathematical thinking. It's surprising how difficult it is to unlearn all the coding cowboy habits as opposed to just starting afresh. This is not to say that OOP or Ruby or design patterns or TDD or agile methodologies or anything else I do is the only way (or that it's correct at all). It's just one of the ways and the one I happened (in reality it took much more then just happen :-) to believe in – but more about that in my other blog posts. Anyway, it's really great to walk in there now. It's completely a different crowd – you see people being able to think, to analyze and to even transform all that into a working product. Sure it's not all wine and roses...but I would really like to see this kind of commitment in many other organizations I worked with.

I realized much of this when talking to people at one of the Ruby Brigade meetings. That's when I realized that I may be running quite a tight ship in regards to development principles – but so far I've really appreciated that nobody on my staff would say things like: “I do tests only when I feel like”, “I've never been into testing much”, “We do bug driven development”, “What's that 'assert' thing mean?” and many others.

To conclude with, I must say that I have always been an extremely fast learner – I've never had problem to learn anything but it's a completely different ball game to teach what you know. One thing is to look at something and understand it and a completely different one is to explain it to somebody else. And I really love it :-). And it finally gives me a reason to start working on my e-learning platform I've been planning since high school.

Ruby on Rails in Singapore (finally :-)

Around a year ago I posted a long article on my blog about no Ruby programmers in Singapore. Back then I didn't manage find any programmer leave alone any company using Ruby (with or without Rails) in production. I started playing with Ruby a little less than 2 years ago and by that time I had several applications in production, and RoR had become our obvious choice for pretty much all of our new projects.

Year on, there are still not many companies using RoR here but the situation seems to be rapidly changing. Last Thursday National Library hosted a meeting of 20+ Ruby and RoR developers. Sure, 20+ may seem pathetic, considering the hopelessly sold out RailsConf and huge popularity that Ruby has gained around the world, but for the local “environment” this is really an achievement and great success for organizers (namely Doug, Sausheong and Choon Keat). It was really great to see that there are like-minded people around.

Let's cut to the chase - there were a few questions/issues raised during the meeting that I'd like address:

1) relationship with library – this question was raised by “the Rambling Librarian” Ivan, thanks to whom we were able to meet in a proper environment rather then a coffee house. I must say that I would really love if we could find ways to not only cooperate with library but find ways beneficial to more general public. We claim that we use the most advanced and cutting edge technology today and how great it is and how much fun it is, but based on the number of participants it seems that Singapore really couldn't care less. Whether it's true or not I see this as the biggest opportunity for cooperation. Library is (and should be) a perfect venue (or avenue?) for spreading this latest know-how. It will be years before it reaches formal educational system – if ever as there's no strong vendor to support the “buy-in”. In fact, I cannot think of a better opportunity to contribute to the goal set in the motto I found in library's corporate brochure: “We aim to provide Knowledge that ignites Imagination and transforms Possibility into reality”. Isn't this exactly what RoR strives to stand for? Just look at the the highlighted words – isn't this exactly how the creators and early adopters (DHH, Dave Thomas, etc.) used to sell RoR? Now unlike many other libraries in countries I lived in, NLB is highly respected among Singaporeans – and I must say rightly so (it could sure use some update of IT section – but overall it's the best library I've been a member of). As such it is in a perfect position to spread this latest knowledge through informal communities that gather around it. It brings due credibility to the scene and of course, it's up to us to show and prove that we are really worth our salt and we are worth of being NLB's partner. After all, I started RoR course in Changi Prison and I believe there's something we could do with library as well :-)

I'd suggest to meet up and discuss the possibilities and ideas – possibly together with Ivan so that we can better understand library's views and goals.

2) What's the purpose of those meetings – this question was raised by me as well as by Ivan. We discussed it quite a bit with Doug but I don't think we managed to close it on anything. Doug's idea is to simply build and grow RoR community in Singapore. I would be interested in slightly more than that. I think this roughly follows the informal divide in RoR community around the world – a divide between the free hackers inclining more to DHH and more corporate (or enterprise) developers inclining towards Dave Thomas. Doing what I do, I think it's quite clear to which category I belong. This is really not to say that one or the other is better – definitely no – they just have slightly different needs and as such they look for slightly different things. Ever since I came to Singapore (actually ever since I came to Asia 7 years ago) I've been trying to find a professional community of actual developers, people who work with the technology (whatever the technology I was using at the time). This seems to be the only one worth mentioning (so far) – I am not picking on the “sales” communities around the biggest vendors – they just weren't my cup of coffee. Thursday's meeting was a very interesting mix of people with representation from actual developers, potential developers as well as potential customers and partners. Each and every of those groups has its place and purpose in the community but all of them need and expect very different things. As such I hope we will be able to structure the meetings so that everybody can benefit and that nobody will have to sit through 2 hours of something that sounds like total gibberish to them. Again, I think this is for a longer discussion and I am very open to meet up and discuss the possibilities.

Lastly, we are a high-tech community – aren't we? All of us create huge, complicated systems every day – don't we? And yet – let's look at our presentation – just count how many times Ivan uses a word “guy” as opposed to the number of names. I think we could do much better in profiling ourselves – even the most techless business / marketing communities have websites nowadays :-)

Yes – it's a PDI (to myself as well).