December 22, 2005

Ruby's executable class definitions

If you're coming to Ruby from Java, remember that class definitions are executable code. I can think of at least one situation where this has practical implications and if you're not clear about it you'll spend a couple of hours banging your head against the wall.

Consider this simple Java class:

class A
{
  int a = 10;

  public int getA()
  {
    return a;
  }
}


If you create an object of type A and call it's method getA(), you'll get 10. No surprises here.

Now, knowing that member variables in Ruby are prefixed with @ we go ahead and translate our little java class to Ruby like so:


class A
  @a = 10

  def a
    return @a
  end
end


We then create an object of class A and call its a() method:

irb(main):001:0> class A
irb(main):002:1> @a = 10
irb(main):003:1> def a
irb(main):004:2> return @a
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> A.new.a
=> nil


"WTF!!" you say. Well, if you remembered what I said, class definitions are executable code. In our case, this means that the @a = 10 is executed when the class is defined, not when an instance of the class is created. As a result, this statement defines a member variable of the instance of class Class that represents class A.

To prove the last statment is true, let's make a() a class method:

class A
  @a = 10

  def A.b
    return @a
  end
end


When we call it we get what we expected:

irb(main):001:0> class A
irb(main):002:1> @a = 10
irb(main):003:1> def A.a
irb(main):004:2> return @a
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> A.a
=> 10

Labels:

Simple color effects

RGB is the most widespread method for representing colors. HTML, CSS, Java's java.awt.Color constructors, and countless more, all expect color to be described in terms of their RGB components.

But RGB is not the best choice for doing common operations such as desaturation and certain types of gradients (e.g., shades of a color and fade to black or white). There's a much more natural way of manipulating colors for that purpose without going crazy with interpolation in the RGB color space. The trick is to work with the HSV representation, instead. In the HSV space, a color is defined by it's hue (the "rainbow" of colors), it's saturation and it's brightness or value.

For any given color, change the brightness to get a darker or lighter shade. If you want to desaturate it (without changing it's brightness), simply reduce it's saturation value.

Here's a simple example, and the relevant Javascript code:


function RGB(r, g, b)
{
   this.r = r
   this.g = g
   this.b = b
}

RGB.prototype = {
   toHSV: function()
   {
     var max = Math.max(this.r, this.g, this.b)
     var min = Math.min(this.r, this.g, this.b)

     var s = (max - min) / max

     switch (max) {
       case this.r: return new HSV(60 * (this.g - this.b) / (max - min), s, max)
       case this.g: return new HSV(60 * (this.b - this.r) / (max - min) + 120, s, max)
       case this.b: return new HSV(60 * (this.r - this.g) / (max - min) + 240, s, max)
     }
   },

   toHex: function()
   {
     var r = Math.floor(this.r * 255).toString(16)
     var g = Math.floor(this.g * 255).toString(16)
     var b = Math.floor(this.b * 255).toString(16)

     return "#" + (r.length == 1? "0" : "") + r
       + (g.length == 1? "0" : "") + g
       + (b.length == 1? "0" : "") + b
   }
}


function HSV(h, s, v)
{
   this.h = h
   this.s = s
   this.v = v
}

HSV.prototype = {
   toRGB: function()
   {
     var sextant = Math.floor(this.h / 60) % 6

     var f = this.h / 60 - sextant
     var p = this.v * (1 - this.s)
     q = this.v * (1 - f * this.s)
     t = this.v * (1 - (1 - f) * this.s)

     switch (sextant) {
       case 0: return new RGB(this.v, t, p)
       case 1: return new RGB(q, this.v, p)
       case 2: return new RGB(p, this.v, t)
       case 3: return new RGB(p, q, this.v)
       case 4: return new RGB(t, p, this.v)
       case 5: return new RGB(this.v, p, q)
     }
   },

   toHex: function()
   {
     return this.toRGB().toHex()
   }
}


function desaturate(box, color)
{
   box.style.backgroundColor = color.toHex()

   color.s -= .01
   if (color.s >= 0) {
     window.setTimeout(function() { desaturate(box, color), 50 })
   }
}

function fadeToBlack(box, color)
{
   box.style.backgroundColor = color.toHex()

   color.v -= .01
   if (color.v >= 0) {
     window.setTimeout(function() { fadeToBlack(box, color), 50 })
   }
}

function fadeToWhite(box, color)
{
   box.style.backgroundColor = color.toHex()

   var v = 1 - color.v
   var distance = Math.sqrt(color.s * color.s + v * v)

   color.s -= 0.01 * color.s / distance
   color.v += 0.01 * v / distance

   if (color.v <= 1 && color.s >= 0) {
     window.setTimeout(function() { fadeToWhite(box, color), 50 })
   }
}

December 21, 2005

Internationalized GET parameters with Tomcat

One tiny, oft overlooked detail when working with internationalized web apps is that the Content-Type header specifies the type and encoding of the body of the http request.

Say you want to add searching to your site, which is implemented as a form that submits using method="GET", and your target audience is Japanese people. How do you deal with internationalized query parameters, considering that GET requests do not have a body?

Historically, query strings were expected to contain characters in the ISO-8859-1 character set, with anything but a subset of ASCII encoded using the %xx notation. In discussions a few years ago it was agreed that using UTF-8 encoding for international characters was the best alternative. This is reflected in RFC 3986 (which is backed by the W3C):


When a new URI scheme defines a component that represents textual data consisting of characters from the Universal Character Set [UCS], the data should first be encoded as octets according to the UTF-8 character encoding [STD63]; then only those octets that do not correspond to characters in the unreserved set should be percent-encoded. For example, the character A would be represented as "A", the character LATIN CAPITAL LETTER A WITH GRAVE would be represented as "%C3%80", and the character KATAKANA LETTER A would be represented as "%E3%82%A2".



If you're running Tomcat, watch out! If your URL contains unicode characters, the browser will properly encode them as UTF-8 and then do the %xx magic for each byte. Tomcat will treat each encoded byte in the UTF-8 representation as a single ISO-8859-1 character and make your Japanese users very unhappy.

Unfortunately, this is Tomcat's default behavior. Fortunately, there is server configuration option that will force it to treat URLs as per RFC 3986. To enable it, edit server.xml and add the following parameter to the connector element:


<Server ...>
  <Service ...>
    <Connector ... URIEncoding="UTF-8"/>
      ...
    </Connector>
  </Service>
</Server>


BTW, I've tested this in Tomcat 5.5.12. I don't know whether it will work in previous versions.

December 15, 2005

ANTLR for Ruby

The latest release of ANTLR v3 adds support for Ruby as a target language, courtesy of yours truly. Let's look at an example to see what's possible.

The following grammar generates a parser that can evaluate simple arithmetic expressions involving integer numbers, +, -, *, /, ( and ).


grammar Calculator;
options {
  language = Ruby;
}

@members {
  @stack = []

  def result
    @stack[0]
  end
}

parse: expression;

expression: mult (
    '+' mult {
      @stack.push(@stack.pop + @stack.pop)
    }
  | '-' mult {
      a = @stack.pop
      b = @stack.pop
      @stack.push(b - a)
    }
  )* ;

mult: atom (
    '*' atom {
      @stack.push(@stack.pop * @stack.pop)
    }
  | '/' atom {
      a = @stack.pop
      b = @stack.pop
      @stack.push(b / a.to_f)
    }
  )* ;


atom: n=NUMBER { @stack.push($n.text.to_i) }
  | '(' expression ')';

NUMBER: ('0'..'9')+;

WS: (' ' | '\n' | '\t')+ { channel = 99 };


First, we need to process the grammar with ANTLR. You'll need to have Java installed in order to do this. ANTLR will generate two files: Calculator.rb and CalculatorLexer.rb.

You'll also need the ANTLR ruby runtime library (just a small .rb file) which you can get here. Just drop into the same directory as the two .rb files created above (in the future I may package it as a gem).

We got our parser, so let's try it out. Create another ruby file with the following:


require 'Calculator'
require 'CalculatorLexer'

lexer = CalculatorLexer.new(ANTLR::CharStream.new(STDIN))
parser = Calculator.new(ANTLR::TokenStream.new(lexer))
parser.parse

puts parser.result


Now, run the program, type an expression and press CTRL-D (or CTRL-Z if in Windows) to see the results, like so:


(5 + 4) * (10 - 2) / 3
^Z
24.0


Simple... but cool, eh?

ANTLR v3 is still in pre-alpha stage, and the Ruby backend is a work in progress. So far, it can handle syntactic and semantic predicates, token labels, etc. and I'm planning to add support for scopes and token/rule parameters and return values next. AST creation will come after that.

Labels: