Simple client-side form validation in javascript using jQuery

I’m new to jQuery (I come from a prototype background), so I quite enjoyed my first little jaunt using jQuery… and I used it to create a simple client-side form validation library.

As we are generating a lot of our forms server side which in turn can be rendered into several different formats, so it’s important for us to have a simple way to attach validation to form elements.

For this method to work, each form element needs to be wrapped up in a container, a li is used in this example. Each container then has a class applied to it to indicate the type of validation required for the form field contained by the wrapper. Here’s the HTML snippet:

<ol>
	<li class="validation required"><label for="name">Contact name</label></li>
	<li class="validation date"><label for="dob">Contact DOB</label></li>
</ol>

Now all that needs to be done is a validation function bound to each input field, that’s easy because each input field is wrapped in a container with the “validation” class. The second class is the type of validation that is required for the input field. To keep it generic, you can just eval the class name as long as there is a function to match, heres the javascript:

init: function() {
$(‘li.validation’).find(“:input”).each(function() {
var validations = $(this).parent().get(0).className.split(‘ ‘);
for (var i=0; i

Creating common fields in your Rails model database migrations

Sometimes you want a set of common set of fields in a bunch of your tables. DRY it up a little so you can write this in your migrations:

include MigrationHelpers

class CreateUserTales < ActiveRecord::Migration
  def self.up
    create_table :user_tales do |t|
      t.common_fields
      t.integer :tale_id, :null => false
    end
    add_common_field_indexes :user_tales
    add_index :user_tales, :user_id
    foreign_key :user_tales, :tale_id, :tales
  end
end

You need a /lib/migration_helpers.rb containing this:

module MigrationHelpers

  # Common fields for which we want to create an index by default.
  # Rather than create indexes for every field we create them by default only for those fields which do not change every time the record is updated.
  COMMON_INDEX_FIELDS = %w(owner_id created_by created_at)

  class ActiveRecord::ConnectionAdapters::TableDefinition
    def common_fields
      self.integer :owner_id, :created_by, :updated_by
      self.integer :version, :default => 0, :null => false
      self.boolean :is_deleted, :default => false, :null => false
      self.timestamps                   # adds created_at and updated_at as datetimes
    end
  end

  # Create indexes for the Common Fields
  def add_common_field_indexes(table_name, columns_to_index=COMMON_INDEX_FIELDS)
    columns_to_index.each do |column_name|
      index_name = "index_#{table_name}_on_#{column_name}"
      execute "CREATE INDEX #{index_name} ON #{quote_table_name(table_name)} (#{column_name})";
    end
  end

  # Hide mysql's foreign key syntax behind a simple method
  def foreign_key(from_table, from_column, to_table)
    constraint_name = "fk_#{from_table}_#{from_column}"
    execute %{ALTER TABLE #{from_table} ADD CONSTRAINT #{constraint_name} FOREIGN KEY (#{from_column}) REFERENCES #{to_table}(id)}
  end
end
Posted in Rails. 1 Comment »

Seeding data as part of Rails database migrations

So, we stumbled across a slight problem when trying to seed data as part of our database migration scripts. The de-facto standard seems to be to keep the seed data in yaml files and load them in using fixtures. This is a perfectly adequate way of doing it for seed data that will never change, but doesn’t really fit into the infrastructure of migrations. There are also many plugins out there to help with using fixtures to seed data, but none of them allow you to have different seed data for different migration versions.

After bumping our heads together, we came up with a way of managing seed data using the same philosophy as data structure migrations. The only prerequisite to your data structure is to add a column named “came_from_migration” to any table that is going to contain seeded data. This way when moving down migrations it is possible to determine what data that shouldn’t be there (and delete it).

  1. Add a “fixtures” folder to /db/migrate. Your seed data yaml files will be saved here.
  2. Create your seed data yaml file and name it in a similar fashion to your migration script using the table name as the descriptor e.g. 005_table_name_to_seed.yml. Save this in the folder you created above. This means that you can seed several tables as part of one migration version and it’s nice and easy to see what’s going on from just the file name.
  3. Now I needed to write a couple of helper methods to move to and from the seed data. I’ve placed these in a MigrationsHelper module which is included in any migration that needs to seed data. And because we are so nice, here they are:
  def seed_from_yaml(table_name)
    info = get_migration_file_info(caller[0])
    fixture_folder = RAILS_ROOT + "/db/migrate/#{info[:type]}_fixtures"
    fixture_file = "#{info[:version]}_#{table_name}"
    puts "Seeding #{table_name} from #{info[:type]} fixture version #{info[:version]}"

    require 'active_record/fixtures'    
    connection = ActiveRecord::Base.connection
    seed_data = Fixtures.new(connection, table_name.to_s, nil, File.join(fixture_folder, fixture_file))
    seed_data.insert_fixtures
  end
  
  def deseed(table_name)
    info = get_migration_file_info(caller[0])
    puts "Deseeding #{table_name} with anything newer than #{info[:version]}"
        
    execute %{DELETE FROM #{quote_table_name(table_name)} WHERE #{quote_column_name('came_from_migration')}=#{info[:version].to_i}}
  end
  
  private
    def get_migration_file_info(file)
      file.gsub!(/:.*/, '') # get rid of everything after the colon
      migration = file.split '/'
      db_type = migration.include?("global") ? "global" : "private"
      migration_file = migration[migration.length-1].split '_'
      migration_number = migration_file[0]
      { :type => db_type, :version => migration_number, :file => migration_file }
    end

Seeding data when not using migrations

What about when running tests or deploying, i.e. seeding data when not using migrations and coming from a empty database? Well, the schema should be held for you in your _schema.rb file, but that doesn’t cover any seed data. The answer is to create a *_seed.yml containing all the data in the database (in the same way the *_schema.rb file contains the complete data structure schema of the database). This way you can load in the seed data after the tables have been created. We have yet to write this method, so is left as an exercise for the reader at present. The basic premise is to go through every table in the schema, check for the existence of the “came_from_migration” column, do a “select * from table where came_from_migration is not null” on tables with that column and then concatenate the output from the select statements to a *_seed.yml and save in /db. There may also be problems with referential integrity with seed data and to what order it is inserted into the database. No doubt we will come across these difficulties soon.

Bibliography

Supported data types for create_table in Rails 2.0

Even given the sketchy documentation, I managed to glean the data types for create_table from the ActiveRecord::Migration Rails Framework Docs as being:

  • :string
  • :text
  • :integer
  • :float
  • :decimal
  • :datetime
  • :timestamp
  • :time
  • :date
  • :binary
  • :boolean

A default value can be specified by passing an options hash like { :default => 11 }. Other options include :limit and :null e.g. { :limit => 50, :null => false }

Posted in Rails. Tags: . 1 Comment »

Selenium RC environments with whitespace characters

We use selenium grid to orchestrate our acceptance testing over multiple browsers on multiple platforms. One of the nice things with registering environments with the selenium grid hub, is that you can use descriptive names like: “IE on Windows XP” and “Firefox on OS X” for example.

I was having difficulties with environment names with whitespace characters, so after a lot of experimentation, here are the tweaks I made to get it working across all Linux and Windows.

The Rakefile for the selenium-grid needs to have the rc_args method redefined as follows (notice the changes on lines 6 and 7):

def rc_args(options)
args = []
args << "-host" << (options[:host] || ENV['HOST'] || "localhost") args << "-port" << options[:port] args << "-hubUrl" << (options[:hub_url] || ENV['HUB_URL'] || 'http://localhost:4444') args << "-env" << "\"#{(options[:environment] || ENV['ENVIRONMENT'] || "*chrome")}\"" args << "-env" << "\"#{(options[:environment] || ENV['ENVIRONMENT'] || "*chrome")}\"" args << (options[:selenium_args] || ENV['SELENIUM_ARGS'] || "") args end [/sourcecode] Now you can run the RCs from the command line as follows: On windows:

rake rc:start PORT=5555 HUB_URL=http://172.16.7.122:4444 HOST=172.16.7.119 ENVIRONMENT="IE on Windows Vista"

On linux:

rake rc:start PORT=5555 HUB_URL=http://172.16.7.122:4444 HOST=172.16.7.119 ENVIRONMENT=Safari\ on\ OS\ X

 

Welcome

Workbooks is developing a new application, delivered using the “Software as a Service” model. Eventually we’ll no doubt post more about what the application does, and who it’s aimed at – if you want to know more go to the Workbooks website. This is the blog of Workbooks’ development team; as we discover things which may be useful to the community in general we’ll publish them here.

Technically, we’re using Ruby on Rails and ExtJS as a starting point. We’re fans of the Apple MacBook Pro and use it to run VMware Fusion as a host for 64-bit Linux development systems.

There are a lot of blogs which are quickly created and become moribund. Then there are those which have a community of subscribers in the millions. While I doubt this will be the latter, we’ll do our best to fill it with useful technical information.