Returning to Java

Posted by Peter Donald Tue, 17 Jan 2006 23:07:00 GMT

Over the last year I have wandered away from the Java programming language and experimented with different languages. Over the last six months I have had little contact with java programming except for occasionally helping out a student. However most of my time has been spent reading, teaching and programming in other languages such as Ruby (via Ruby On Rails), Prolog, Erlang and a dash of eLisp and Scheme.

I have just returned to working with Java and I find it lacking. Some of the features that java lacks do not really fit with Javas philosophy such as “open” classes. Why java does not support the richness of rubys collection classes is beyond me.

Open Classes

Ruby has the ability to re-open classes and add, remove and rename methods that have already been defined. At times this has been a lifesaver as I could re-open a class that was defined by a framework and fix bugs without having to keep a separate branch for all my dependencies.

This feature also allowed me to ensure that my code worked across different versions of libraries. For example the Struct class differs betweem Ruby 1.8.2 and Ruby 1.8.3 in the way that it handles symbols with ’?’ but I was able to re-open the class and make the behaviour consistent. I have also been able to fix many rails variations and bugs between versions so that my code would work out of the box.

I don’t think this is a feature that Java or any statically typed language should support but it is useful none the less.

Collection Classes

Ruby also has an amazingly nice set of collection classes. While it is true that the classes may include a lot of cruft, I really like the four search methods defined in Enumerable ; select/find_all, detect/find, reject, and collect/map. (select is aliased to find_all or vice versa as is detect and find, and collect and map). I believe they were originally derived from Smalltalks collection classes and it is a pity they did not find their way into java.

Consider doing something like iterating through a unordered list of 3D vertexes and any in front of the plane are placed in a new list but transformed via a matrix associated with the plane.

results = my_vertexes.select {|v| v.in_front?(my_plane) }.
     collect {|v| v.transform(my_matrix)}

While the equivelent java code is something like

LinkedList<Vertex> tempList = new LinkedList<Vertex>()
for( Vertex v : myVertexes )
{
  if( v.isInFront(myPlane) )
  {
    tempList.add( v );
  }
}
LinkedList<Vertex> results = new LinkedList<Vertex>()
for( Vertex v : tempList )
{
    tempList.add( v.transform(myMatrix) );
}

I suspect that both code snippets could be improved but they are only meant to demonstrate a point. Namely that ruby code tends to support the common case with a far shorter syntax and IMHO smaller cognitive load than equivelent code in java. My guess is that java would make it much easier to optimize the code for speed but that is rarely the most important feature of software.

Conclusion

While I am remembering that there is things I miss about java there is also things that I wish java had. I am finding it difficult to live without rubys “blocks” but still looking forward to hacking on java again.

Posted in  | Tags , ,  | no comments | no trackbacks

Uncacheable web pages in Rails

Posted by Peter Donald Thu, 15 Dec 2005 03:36:00 GMT

In an attempt to ensure that a page is always fetched from the server I needed to add the following code to my base controller. Seems to be a bit long-winded but does the job.

class ApplicationController < ActionController::Base
  before_filter :force_no_cache

private
  def force_no_cache
    # set modify date to current timestamp
    response.headers["Last-Modified"] = CGI::rfc1123_date(Time.now)

    # set expiry to back in the past 
    # (makes us a bad candidate for caching)
    response.headers["Expires"] = 0

    # HTTP 1.0 (disable caching)
    response.headers["Pragma"] = "no-cache"

    # HTTP 1.1 (disable caching of any kind)
    # HTTP 1.1 'pre-check=0, post-check=0' (IE specific)
    response.headers["Cache-Control"] = 
      "no-cache, no-store, must-revalidate, pre-check=0, post-check=0"

  end

Posted in  | no comments | no trackbacks

Dumping database to YAML fixtures

Posted by Peter Donald Wed, 14 Dec 2005 02:28:00 GMT

I finally got fed up with the bugs in Rails 1.0 handling of mysql connections and have decided to move to postgres. I have talked about the move and even migrated my DDL to the database agnostic schema language. The one thing I had not yet thought about was how to move my data.

After doing a little bit of asking and searching I decided to just dump my data out the databse to fixtures and then reload these fixtures. This is surprisingly simple using the rake task.

desc 'Dump a database to yaml fixtures. '
task :dump_fixtures => :environment do
  path = ENV['FIXTURE_DIR'] || "#{RAILS_ROOT}/data"

  ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
  ActiveRecord::Base.connection.
             select_values('show tables').each do |table_name|
    i = 0
    File.open("#{path}/#{table_name}.yml", 'wb') do |file|
      file.write ActiveRecord::Base.connection.
          select_all("SELECT * FROM #{table_name}").inject({}) { |hash, record|
      hash["#{table_name}_#{i += 1}"] = record
        hash
      }.to_yaml
    end
  end
end

desc "Reset Database data to that in fixtures that were dumped"
task :load_dumped_fixtures => :environment do
  require 'active_record/fixtures'
  ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
  path = ENV['FIXTURE_DIR'] || "#{RAILS_ROOT}/data"
  Dir.glob("#{path}/*.{yml}").each do |fixture_file|
    Fixtures.create_fixtures(path, File.basename(fixture_file, '.*'))
  end
end
This is Mysql specific due to the use of select_values('show tables') but apparently sqlite usues select_values('.table') and postgres uses the following.
 select_values(<<-end_sql
SELECT c.relname
 FROM pg_class c
   LEFT JOIN pg_roles r     ON r.oid = c.relowner
   LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
 WHERE c.relkind IN ('r','')
   AND n.nspname IN ('myappschema', 'public')
   AND pg_table_is_visible(c.oid)
end_sql

This worked like a charm except when my data contained embedded ERB directives because when rails loads the fixtures it attempts to evaluate the fixture as an ERB script. In this scenario I just needed to nip into the read_fixture_files method in $RUBY_HOME\activerecord-1.13.2\lib\active_record\fixtures.rb and comment out the erb rendering while I imported my data.

Update: The code for this can be found is available in dump_fixtures.rake

Posted in  | no comments | no trackbacks

Many-to-many rails goodness with the through directive

Posted by Peter Donald Mon, 05 Dec 2005 06:15:00 GMT

The has_many :through option will rock your socks off! (Or at least make things a little easier) Read on to understand why!

Rails has the ability to declare many-to-many relationships between ActiveRecord objects using the has_and_belongs_to_many macro. HABTM relationships as they are affectionately known, require a third join table that contains the keys of the two domain objects.

Consider the example where a Student can be enrolled in 1 or more Subjects and each Subject can have 1 or more Students. This would be modelled by the following domain classes.

class Student < ActiveRecord::Base
  has_and_belongs_to_many :subjects
  ...
end

class Subject < ActiveRecord::Base
  has_and_belongs_to_many :students
  ...
end

With the sql DDL looking something like;


CREATE TABLE students (
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL,
  ...
  PRIMARY KEY (id) 
) ENGINE = InnoDB;

CREATE TABLE subjects (
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL,
  ...
  PRIMARY KEY (id) 
) ENGINE = InnoDB;

CREATE TABLE students_subjects (
  student_id INT NOT NULL,
  subjects_id INT NOT NULL,
  FOREIGN KEY (student_id) REFERENCES students(id),
  FOREIGN KEY (subject_id) REFERENCES subjects(id)
) ENGINE = InnoDB;

At some point you may want to add in some information about the HABTM relationship such as the year that the student enrolled in the subject. This can be done using push_with_attributes and adding an extra ‘year’ column to the SQL DDL.
class Subject < ActiveRecord::Base
  has_and_belongs_to_many :students
  ...
  def enrol(student)
    students.push_with_attributes(student, :year => Time.now.year)
  end
end

Join tables with attributes tend to become ugly fast and it is rare that a few days don’t go by on the rails mailing list without someone asking for features that imply they are using join tables as a crutch for a missing domain object. Enrolment would be a good choice for the example above.

However if we introduce an Enrolment object, the naive approach of accessing the students of the subject (or vice versa) is extremely inefficient as you will first hit the enrolments table before loading from the students table (or conversly the subjects table). A more efficient way using hand crafted SQL would be the following

class Subject < ActiveRecord::Base
  has_many :enrolments
  ...
  def students
    find_by_sql(
      "SELECT students.* " +
      "FROM subjects, enrolments, students " +
 "WHERE enrolments.student_id = students.id AND + "
      "      enrolments.subject_id = #{id}"
    )
  end
end

This pattern is likely to be duplicated across many domain objects. Luckily David Heinemeier Hansson mentioned that a :through option will be supported on the the has_many macro that allows you to replace the above code with;

class Subject < ActiveRecord::Base
  has_many :enrolments
  has_many :students, :through => :enrolments
  ...
end

Rails just keeps getting better and better!

Posted in  | no comments | no trackbacks

Validations for non-ActiveRecord Model Objects

Posted by Peter Donald Fri, 02 Dec 2005 11:48:00 GMT

Rails provides support for validating form input if the form is backed by an ActiveRecord. The application I am currently working on has a form that has a large number of input parameters but is not persisted to the database. I still wanted to use the ActiveRecord Validations as they make my life easier but I did not know if there was an simple way to do this.

Initially I created a dummy table in the database with just an id field and made my model object sub-class ActiveRecord. I could then use the validations with all the fields I had defined using attr_accessor. This looked something like;

class Search < ActiveRecord::Base

  attr_accessor :user_name, :email, :locator

  validates_length_of :user_name, 
                      :within => 6..20, 
 :too_long => "pick a shorter name", 
                      :too_short => "pick a longer name"
  validates_format_of :email, 
                     :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
  validates_numericality_of :locator
  ...
end

In my controller I created the Search object in the same way that I created all the other model objects but I never called save. Instead I called the valid? method to check whether the model passed all the validations. If the model is not valid the @search.errors object is populated with all the errors.

class NavigatorController < ApplicationController

  def search
    @search = Search.new(params[:search])
    if @search.valid?
    ...
    end
  end
  ...
end

Of course this left a bad taste in my mouth as it is a seriously ugly hack that requires an empty table in the database just to get form validation working. So I began to look at what I needed to do to implement an ActiveForm object. I was not looking forward to this task as I had read on the rails mailing list that the Validations were intermingled with ActiveRecord::Base and difficult to untangle.

This could not be further from the truth. The first thing I did was create a new ActiveForm class and include ActiveRecord::Validations. This caused a few errors as the ActiveRecord::Validations class attempts to call alias_method for methods that do not exist in ActiveForm. I implement these methods (save and update_attribute) so that they raise a NotImplementedError exception. Then I attempt to call the valid? method but it calls the new_record? method which I implement to return true. To view the errors in the view using the standard helper methods I need to implement the human_attribute_name method. These changes seem to get basic validations working.

The only validations that are not working are validates_uniqueness_of and validates_numericality_of. validates_uniqueness_of is not expected to work as it accesses the database so I just make it raise a NotImplementedError exception. validates_numericality_of does not work as it relies on a method named ”#{attr_name}_before_type_cast” for each attribute named “attr_name”. This is an artifact of the type coercion that ActiveRecord performs on input parameters. ActiveRecord will convert an input parameter from a string to an integer if the underlying database record stores the field as an integer. As this does not occur with ActiveForm I just duplicated the method and replaced ”#{attr_name}_before_type_cast” with ”#{attr_name}”.

The only functionality that ActiveForm was missing was the ability to create a model object from a hash. As ActiveForm does not need to do any type coercion this is as simple as

  def initialize(attributes = nil)
   if attributes
      attributes.each do |key,value|
        send(key.to_s + '=', value)
      end
    end
    yield self if block_given?
  end

At this stage ActiveForm is in a usable state and it took less than 20 minutes. It only took that long because I needed to restart webrick for each change (not to mention the fact that I had never looked at ActiveRecord before). Isn’t ruby/rails great?

To get this working grab the active_form.rb file and place it in the app/models directory. You can then make your model objects extend ActiveForm and use them like regular ActiveRecord objects.

I cleaned up a few warts of ActiveForm like overriding methods you should not be calling (save!, save_with_validation, create!, validate_on_create, validate_on_update). I hope to get motivated enough to send a patch that enables this style of functionality in the core once edge rails is working for me again.

Update:

It seems there is already a HowTo on the rails wiki that describes a similar technique. However rather than duplicating validates_numericality_of they handle the calls to ”#{attr_name}_before_type_cast” by implementing a method_missing method which I incorporated to cleanup my code.

Update on 12th Dec 2005

Today I decided that I needed to add reloading of ActiveForm subclasses and this is done with the following code chunk.

require 'dispatcher'
class Dispatcher
  class << self
    if ! method_defined?(:form_original_reset_application!) 
      alias :form_original_reset_application! :reset_application!
      def reset_application!
        form_original_reset_application!
        Dependencies.remove_subclasses_for(ActiveForm) if defined?(ActiveForm)
      end
    end
  end
end

Download: active_form.rb

Update on 1st March 2006

Available as a plugin at

svn: http://www.realityforge.org/svn/code/active-form/trunk

Posted in  | 3 comments | no trackbacks

Older posts: 1 2 3 4 5 6