validates_nric_of

mech May 29th, 2007

On my days at AsiaSoft, I was required to implement an NRIC checker for MapleStory registration process. The code was written in C#, so I guess I will port it to a Rails custom validator for all to use.

The actual algorithm may be incorrect as there is no one authoritative source of checking on that. The 2 best online resources I can find appear on Wikipedia and this power point file

# validates_nric_of
# Validates Singapore National ID for registration purposes.
# Take note that the Singapore government recommends non-disclosure of 
# full NRIC, so just display S791571XX if there is ever a need.
# validates_nric_of takes an array of attributes
# Examples:
# validates_nric_of :nric, :parent_nric

module ActiveRecord::Validations::ClassMethods
  def validates_nric_of(*attrs)
    configuration = {
        :message => ActiveRecord::Errors.default_error_messages[:invalid], 
        :on => :save}

    configuration.update(attrs.pop) if attrs.last.is_a?(Hash)

    validates_each(attrs, configuration) do |record, attr, value|
        record.errors.add(attr, configuration[:message]) unless is_nric?(value)
    end
  end

  def is_nric?(value)
    if value.size == 9
      value.upcase!

      century_prefix = value[0, 1]
      check_character = value[8, 1]
      ic_number = value[1, 7]

      weight = [2,7,6,5,4,3,2]
      num = 0
      next_weight = 0

      # S, T, F, G prefix lookup tables
      s = %W{J Z I H G F E D C B A}
      t = %W{G F E D C B A J Z I H}
      f = %W{X W U T R Q P N M L K}
      g = %W{R Q P N M L K X W U T}

      ic_number.each_byte do |number_in_string|
        num += number_in_string.chr.to_i * weight[next_weight]
        next_weight += 1
      end

      num = num % 11

      case century_prefix
      when "S" 
        s[num] == check_character
      when "T" 
        t[num] == check_character
      when "F" 
        f[num] == check_character
      when "G" 
        g[num] == check_character
      end
    else
      false
    end
  end
end

I usually save all my validations code on a Ruby file called custom_validators.rb at /my_rails_app/lib directory. To use it, just append this line to your /my_rails_app/config/environment.rb file.

require 'custom_validators'

This is my first custom validator, so I will explain in full what the code does. If you already know the Rails validator structure, you can skip it to “NRIC Checker Algorithm” section.

Rails Validator Structure

A new custom validator can be created as a method inside the module ActiveRecord::Validations::ClassMethods. You can do this also:

module ActiveRecord
  module Validations
    module ClassMethods
      # your validator method here
      def validates_xxx_of
        ...
      end
    end
  end

Because we anticipate users may need to pass in more than one NRIC, we make the argument an array argument with the *attrs. Next we create a hash called configuration. This hash contains various configuration options that you can overwrite such as :with, which you will provide default regular expression and :message, which is the error message you want to display. Then we come to the configuration.update part.

Hash#update will merge another hash to the existing hash and overwrite any duplicate keys. For example, if the last attribute you provide is a hash:
validates_nric_of :nric, {:message => "must be a valid Singapore NRIC"}

The hash will merge with the configuration hash and overwrite the :message key which until then is the Rails’ provided ActiveRecord::Errors.default_error_messages[:invalid] value. Note that a check is made to see if the last attribute is a hash and if it is will attrs.pop that attribute off so that it will not affect any iteration later. This also means that you have to make sure configuration option will be the last attribute you provide.

With all configuration successfully updated, we need to iterate all attribute values. We have 2 choices, either use validates_each() or use the normal for attr in attrs way.

I like to use validates_each() because the code block provides me with 3 essential values that I needed the most. I can just test the value and if it failed my validation code, will just add the attribute to the record.errors collection. Nice and easy :)

NRIC Checker Algorithm

To check for Singapore NRIC, we need 3 parts, the century prefix, the actual IC number, and the check character. The check character is based on a lookup table and different century prefix requires different lookup table.

The method is_nric?() converts the value to uppercase first. One problem I encounter is that I assign the value like this

value = value.upcase!

It is a mistake that I make and it took me quite some time to debug it. If you think you use the dangerous method (those with !) to modify the value, and you still assign it back to the original value, you will get a nil result. The point is you’re already using a dangerous method, so why bother to assign it again. It is an unknowing error that I made and I hoped others can avoid it.

Next, we parse several parts of the NRIC for later processing. The weighted system used is 2, 7, 6, 5, 4, 3, 2. To get the lookup table checker index digit, we need to perform the following equation:

((IC1 * 2) + (IC2 * 7) + (IC3 * 6) + (IC4 * 5) + 
(IC5 * 4) + (IC6 * 3) + (IC7 * 2)) mod 11

With this index, we can compare it to our lookup table that has previously been setup according to specification. Of course, the correct lookup table will have to be used based on the century prefix such as “S”, “T”, “F” or “G”.

Next century may use the “U” and “H” prefix. This validator is not stable enough for those usage and I will not be around to maintain it also.

Hope you guys find it useful!

Resources

  • To understand the NRIC checking algorithm, check out this PPT
  • Duncan Beevers’s validates_not_format_of
Filed in Ruby and Rails

Introduction to Featured Driven Development and the Crystal Methodologies

vernon May 23rd, 2007

http://www.infoq.com/presentations/fdd-crystal-agile-overview

Filed in Agile Methologies

Machines are us.

vernon May 7th, 2007 Filed in

Installing Ruby and Rails on Fedora

mech February 24th, 2007

Hosting kordels means playing with a Linux platform. And Linux is a platform I am not very familiar with being a Windows guy. After wasting many clone of Fedora Parallels HD image files, I finally get Ruby and Rails to play nice with my Fedora. I hope the following step-by-step can save some trouble for someone.

Read the rest of this entry Filed in Ruby and Rails

Yoman!

mech February 13th, 2007

Well there is really no one to kick around for the launch of Yoman! – the sweat and smelly blood of Vernon and mech. But still, we rejoice within our inner self and try hard not to think what may come. Good time ahead, I supposed.

We started Yoman! as a Web studio, doing the stuffs we loved to do. No project manager nagging, no ignoring of clients, and no senseless meanings on processes. We hate rigid processes, because it blocks off creativity in the name of conventions.

That is why on this launch day, we are going to show you our “Process”. Our kick butt process. But first, our site definition or rather, our mission statement:

Yoman Definition

Web Standards and Rails Development. The 2 towers that rise high above all. It is not just popularity or hype-meter. It is about the correct and beautiful way. Web Standards ease the job of maintaining a Web site, not creating them. And Rails applications almost always are interface-rich, engaging, and feel just right. Yoman’s philosophy will always revolves around these 2 technologies’ evolution.

Now, to our “Process”...

Yoman Working Process

I will explain how we “process” and I hope there is nothing stopping you from thinking it is not obvious :)

First, Discovery. We will first notice ourselves and find our passage of light. Only with a clear idea of who we are will we be proud of the work that we do. Some people may call this passion. Of course we don’t spent the whole day discovering ourselves, that is cheeky to say the least. Instead, we discover our potential client and our team’s behavior. Yes, their behaviors, not their requirements!

Requirements give features, and behaviors give interactions. Now, features are important and will always be there, but it is not the focus on Yoman “Process”. We take down features and compile a list, that is for sure. But, we seek behavioral features and extrapolate it into kick butt (sori) Web site.

Second, Prototyping. Fast and sweet. We usually like to give rapid prototyping lesson to our client and team (in case you have not discover, the team is the client and the client is the … okay you have the idea).

There are 3 phases of prototyping that we like to perform in lock step (i.e. they never overlap). They are “Paper Prototyping”, “Wireframe Prototyping” and “HTML Prototyping”.

When we meet our client, it is paper prototype. When we meet our team, it is either wireframe or HTML. After all, seeing is believing isn’t it?

Third, Nail it! Once we think we have it right, we stop being creative and become boring. Nail it means code it. Nothing but code, be it XHTML, CSS, JavaScript, or Ruby.

Fourth, Scope-lessly Consuming Requests. Because we know clients will not stop asking for more. So instead of restricting them or ask them to sign any “Change Request”, we accept them with open arms and heavily curse them the very minute they are away.

After the swearing, we REST. A good rest is the vitamin for everything.

Filed in Announcements