Using Lowpro To Create Autotab using Unobtrusive JavaScript and Rails

4:16 PM EDT Saturday, April 19 2008

In the last article, I showed how to use composed_of to create a class to handle phone numbers. In this article, I'll show how you can use Unobtrusive JavaScript to make a spiffy interface for entering phone numbers. So first, here's the code we want for phone numbers:

Phone Number
<% f.fields_for :phone_number do |pn| %>
  (<%= pn.text_field :area_code, :size => 3, :class => "autotab" %>)
  <%= pn.text_field :prefix, :size => 3, :class => "autotab" %>
  -
  <%= pn.text_field :line_number, :size => 4, :class => "autotab" %>
  ext
  <%= pn.text_field :extension, :size => 4 %>
<% end %>

This goes in the form view code for the users resource. What this does is render separate fields for each part of the phone number. If you've generated the scaffolding for the users resource, you should be able to stick this in there and be able to create users with phone numbers using this form.

So that's nice, but it would be neat if we could have the cursor automatically jump from one field to the next once the correct number of digits have been entered? We've already got the first step of that in there by putting the class "autotab" on each of the first 3 fields. That is as much code as we are going to add into the actual view. We are going to do the rest unobtrusively, some might even say, Ninja-style.

The first step is to download Lowpro and put it into your public/javascripts directly. Lowpro is a library that enhances Prototype to make unobtrusive JavaScript easier. Once you've got that, make sure to include the javascript libraries in your layout:

<%= javascript_include_tag :defaults, "lowpro", "autotab" %>

Autotab is a JavaScript file that we are going to create specifically for the purpose of providing the "autotab" functionality, which is the name I'm giving to the feature where the cursor will automatically move to the next field once the correct number of characters have been entered. The correct number of characters is determined from the size attribute of the input field. For staters, make sure you have firebug installed and enter in this code to public/javascripts/autotab.js:

Event.onReady(function() {
  console.log("These are a few of my favorite things:");
  $$('input.autotab').each(function(e){
    console.log(e);
  });
});

When you load the form, you should see the output in the firebug console, with the 3 inputs that have the autotab class. This doesn't do anything interesting at this point, but just shows the basis of how unobtrusive JavaScript works. There are no event handlers directly inline in the HTML code, instead some JavaScript code execute once the page has finished loading and adds functionality to DOM elements based on something like their CSS class.

So in order to introduce the functionality we want, we'll use Lowpro's addBehavior method, which attaches an event handler to elements that match a given CSS tag. Replace the contents of public/javascript/autotab.js with this:

Event.addBehavior({
  'input.autotab:keyup' : function(e) {
    if(e.keyCode != 9 && e.keyCode != 16) {
      if(e.target.value.length == e.target.size) {
        e.target.next().focus();  
      }      
    }
  }
});

So once the page has loaded (or the DOM is "ready", I should say more correctly), this will attach this function to the keyup event handler of all of the elements on the page that match the CSS selector input.autotab. In the function, we first make sure the key being released isn't tab or shift. This is because if you are filling out the form and you press shift+tab to go back to the previous field, you don't want it to jump ahead, because you must want to edit that field if you are going back to it. After that if statement we just check to see if the length of the value in the input field matches its size. If it does, then we call next() on the target to get the next field, and call focus() to change the focus to that field.

Posted in  | Tags Rails, CSS, Javascript, Ruby, Autotab, Unobtrusive, Lowpro | 0 Comments

The Edge of Innovation

9:24 AM EDT Friday, April 11 2008

If you are a programmer that deals with web applications and you keep up with the latest trends, then there is no doubt you will at least have heard of Ruby on Rails. You might be at the level where you have read about Ruby on Rails and played with it a bit, but you really haven't immersed yourself the Ruby/Rails way. Maybe you know how to build web applications with Java, .Net or PHP, and you think "Rails is nice, but there's nothing Rails can do that you can't do in X".

If you are in this camp, it may be because you aren't willing to adapt your ways to Rails. To really understand the benefits of Rails, you have to not only learn Rails, but learn the best practices followed by good Rails programmers, like Skinny Controller, Fat Model, REST and Behavior Driven Development. You don't see the true benefit of Ruby until you start to fully embrace concepts like these. The only way to learn concepts like these is to read blogs, listen to podcasts, talk to other Ruby programmers at work, user groups and conferences and most importantly, you have to actually write code that reflects what you have learned. You must put aside your preconceived notions about what is right and what is wrong and surrender to the flow. You must unlearn what you have learned.

So if you haven't experienced this transformation first hand yet, and someone asks you "What is the biggest advantage to Ruby on Rails", I would be willing to bet your answer would be productivity. This is the way I think many people involved with technology, who don't fully grasp the Rails Way, perceive Rails. They believe, with some cynicism, that because of the dynamic nature of Rails, you can develop applications faster. They'll also probably say the downside is that Rails can't scale.

But anyway, I'm hear to say that productivity is the most over-rated benefit of Rails. The real advantage to Rails is that it is written in Ruby, which is a very powerful language that will change the way you think about programming. It's funny, I've thought to myself a number of times about how interesting it is that Ruby fundamentally changes the way you think about programming and that "Thinking in Ruby" would probably be a great book. But ironically, Bruce Eckel, the author of the "Thinking in..." line of programming books, seems to be happy with Python and not willing to give Ruby a chance. Who knows, that article is a few years old now, so maybe he's changed his attitude towards Ruby since then. I know mine has.

It's hard to quantify the advantage that "Thinking in Ruby" brings. The simplest way I can state it is that you will look at problems differently and come up with better solutions, solutions you may not have thought of if you were programming in other languages. The way I support this claim is by looking at some of the web application development innovations that have come out of the Ruby community.

The first is Rails itself. Rails has been copied, ported, attempted to be ported or talked about being ported to almost every other mainstream language you could think of, including Groovy, PHP, JavaScript, Perl, Java and .Net. This phenomenon is unique to Rails, I can't think of any other web application framework that can say that. If not the whole framework itself, parts of it such as convention over configuration, migrations and embracing REST have influenced the way web application development is done in almost every language.

Another example is HAML. HAML is a truly new and different take of the problem of generating HTML from a combination of dynamic code and HTML. It is a new idea and it has been ported to PHP, Python, and .Net. Whenever you have a framework or library that is being ported to other languages, it shows that the framework being ported contains new and good ideas about programming. In other words, it is a contribution to the paradigm of web development and a clear sign that the original language that the framework was implemented is at the edge of innovation.

Another example is Behavior Driven Development. This example is even more interesting because the idea originally started in Java with the JBehave framework. Even though the idea for behavior driven development started with Java, the idea didn't really take off until it was implemented in RSpec. They are fairly similar in terms of syntax. Here's an example from the JBehave website:

public class CheeseBehaviour extends UsingJMock {
    public void shouldRequireTheUseOfMocks() {
        // given
        Mock cheese = new Mock(Cheese.class);
        Sheep sheep = new Sheep((Cheese)cheese.proxy());

        //expect
        cheese.expects(once()).method("squelch").withAnyArguments();

        // when
        sheep.eatCheese();

        // then
        verify();
    }
}

and here it is converted to RSpec:

describe Sheep do
  before do
    @cheese = mock(Cheese)
    @sheep = mock(Sheep, :cheese => @cheese)
  end
  it "should squelch when it eats cheese" do
    @cheese.should_receive(:squelch)
    @sheep.eat_cheese
  end
end

For whatever reason, JBehave really never took hold in the Java community, but RSpec has in the Ruby community. RSpec has been ported to .Net, PHP and Groovy. All of those projects describe their code as a port of RSpec, not JBehave. Again it is Ruby influencing the wider web application development community.

Post World War II, the center of the art world was New York City and it was there that the modern art movemement was born. New York was where innovation in the art world was happening. In that time period if you wanted to be a serious artist, you had to go to New York to experience the movement first hand. Today, I believe the Ruby community is leading the way in innovative techniques for web application development. There is certainly innovation happening in other languages like Python, Smalltalk and Erlang as well, but I don't think any one other language/community is doing as much as Ruby. As far as I can tell, languages like Java, .Net and PHP are doing nothing to innovate web application development. They are simply lagging behind, playing catch up and trying to figure out how implement new features pioneered in the Ruby community and others as closely as possible, given the limitations of the language. So if you are a web developer, I suggest you ask yourself this question. Are the languages and frameworks you are working with leading others to come up with new ideas? Are the languages and frameworks that you are working helping you come up with new ideas? If not, embrace Ruby and someday you will discover an elegant solution to a problem, one that you may not have without Ruby.

A language that doesn't affect the way you think about programming is not worth knowing. -- Alan Perlis

Posted in  | Tags RSpec, Rails, Python, PHP, Javascript, groovy, Ruby, .Net, HAML, Java | 0 Comments

One Thing About Lisp That Is Better Than Other Languages

3:50 PM EDT Tuesday, March 18 2008

Ruby:

>> 850/550
=> 1

Python:

>>> 850/550
1

JavaScript:

>>> 850/550
1.5454545454545454

Perl:

perl -e 'print 850/550'
1.54545454545455p

Lisp:

[1]> (/ 850 550)
17/11

Posted in  | Tags Ratios, Perl, Python, Javascript, Fractions, Ruby, lisp | 0 Comments

Instantiating a object from a string

8:01 PM EDT Tuesday, August 28 2007

Today, while doing some Ruby programming, I needed to create an instance of a class (instantiate an object), but I didn't have the class name, just the class name in a string. Here's a Ruby method that takes the name of the class as a string and returns an instance of that class:

def create(class_name)
  Object.const_get(class_name).new
end

So if you do this:

create "Array"

You get an instance of an Array, in other words, the equivalent of Array.new. And in Rails, you can do:

def create(class_name)
  class_name.constantize.new
end

That's cool. It got me thinking, how do you do this in other languages? Here's what I came up with:

//JavaScript
function create(class_name) {
    return eval("new "+class_name+"()")
}

Got any others? Perl? Python? Lisp? Erlang? PHP? C? Java?

Posted in  | Tags Rails, Javascript, Ruby | 1 Comments