MarkUs Blog

MarkUs Developers Blog About Their Project

Object Oriented Model Design In Rails

without comments

As a developer coming from a  traditional PHP/MySQL stack environment, representing Object Oriented models in Rails can be a difficult concept to comprehend using the built in ActiveRecord abstraction layer. In this post I will attempt to outline most common way to implement an inheritance hierarchy and as a result limit opportunities to write repeated Model validation code,  create attribute repitition, and finally to limit the introduction of refactoring bugs into your rails project.

For example purposes, we will use a MarkUs specific case. As part of the student code testing framework, the MarkUs application is required to keep track of a number of files submitted by either TA’s or professors which contain either test scripts themselves or test support data required to drive their test scripts.

Test Support File

File Name – String, Description – String, Assignment ID – Integer

Test Script – I’ve abstracted some of the fields in this object for purposes of this post

Script Name – String, Description – String, Assignment ID – Integer

Display Results – Boolean, Run On Submission – Boolean

As you can see, there are three fields in both models that overlap. On top of this, since File Name and Description require user input that we can’t control, we are required to validate/sanitize these attributes before we can allow them to enter our database for security reasons. As you might imagine, these requirements could easily be implemented using polymorphism allowing the base implementation of both Test Scripts and Test Support Files to be handled in one ruby Model. This would roughly cut our validation code in half, make database schemas much easier to change and implement, and finally fewer database queries would be necessary.

Unfortunately our current implementation consists of two completely separate ruby Models. This has created the need for redundant code in the following places:

Controllers – Automated_Tests_Helper.rb: add_test_script_link, add_test_support_file_link, and process_test_form

Models – Test_Support_File.rb & Test_Script.rb: write_file, delete_old_file, sanitize_filename, delete_file, and many of the validation statements

Views – _test_support_file_upload.html.erb & _test_script_upload.html.erb & _form.html.erb

As you can see, this makes refactoring, upgrading, and bug hunting much more difficult. For this reason I have outlined below the simplest way inheritance can be implemented in the MarkUs project.

Single Table Inheritance(STI)

The method we will be covering is that of STI. “In a nutshell, STI allows you to create subclasses of a particular database table. Using a single table, you can cast rows to specific objects that extend the base model.”(Water Cooler) As you may have inferred from the name, this supports inheritance from only a single super class as well. This works mainly by adding an inheritance column to the table(model) you’d like to extend from. By default Rails looks for a column named “type” that is of type string to store this information in.

Essentially this works by using column “type” to store  model names which are represented in that table allowing us to determine which type of model we are dealing with for the given row. One very important note, and downside to consider, is that any additional columns that are needed to represent subclasses will also be included in the same table. This means that there could be potentially many additional null columns that aren’t used by the base class but would still play a factor if a table join is performed for any operations.

Taking a MarkUs specific example:

create_table “test_file”, :force => true do |t|

t.integer              “assignment_id”

t.string                 “file_name”

t.string                 “description”

t.string                 “type”  //this handles the inheritance look ups

end

class TestFile < ActiveRecord::Base

#common file implementation here

end

test_script.rb:

class TestScript < TestFile

#specific implementation here

end

test_support_file.rb:

class TestSupportFile < TestFile

#specific implementation here

end

As you can see implementing inheritance is accomplished quite simply using this technique. Once you have implemented these model objects and have populated the database with different types of objects, you can access them using standard select or find calls using ActiveRecord. Rails will automatically convert the returned object into their specified model types.

That about concludes my basic overview of Single Table Inheritence. Hopefully this has been useful and will be considered for future versions of the MarkUs Testing Framework. For another great explanation, read: http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html

Written by deredowl

December 8th, 2012 at 11:38 pm

Posted in Uncategorized

Leave a Reply