Enigo Homepage
Client Login
User ID:
Password:

Why Use Kansas?

There are already at least three readily available object/relational mapping systems available for Ruby:

Both Criteria and Lafcadio are fairly mature products, while SOP appears to be a functional but immature product. Of the three, SOP comes closest to the sort of thing that I want for my development work, but none of the three seemed to offer the combination of footprint and features that I was looking for. It is beyond this current document to do any sort of detailed comparison of the packages. I suggest that you do a little reading on each of those packages as one of them may suit your development needs better than Kansas does

Given that everyone's development needs, desires, and opinions differ, I won't presume to say why you should use Kansas. However, here are the reasons why I use it:

Lightweight
The codebase is small and efficient. It doesn't have a significant impact on the overall performance of the database interactions.
Narrowly Focused
Kansas endeavors to do only one thing -- map relational database table data to Ruby objects, allowing the code to interact with objects without being required to worry about the underlying SQL or database system.
Simple
Kansas lets the DBI layer provide the information about a table, so the developer does not need to maintain table descriptions within the code. Kansas can automatically map all of the tables in a database, or the developer can use it to map only specific tables. One-to-many and many-to-one relationships are easily defined. Queries, updates, and transactions are performed simply, and the developer can easily bypass Kansas to issue specific SQL statements directly if needed. It really does make coding database interaction simpler.

Here is a very simple example to illustrate how Kansas simplifies code. The assumption is that the code is running as part of a larger application. The code's job is to read a table, placing values in each record into objects stored in an array to be returned to the caller. The application provides a database connection pool for access to the database.

The ordinary way:

# Somewhere in the body of code, a structure is defined for our table
# data.

begin
	QDailyNav
rescue NameError
	QDailyNav = Struct.new('QDailyNav', :symbol, :name, :price, :change_price, :ytd)
end

#
# Elsewhere in the code, we want to access the data in a table a return an
# array of objects, one per table record.

def getData
  result = []
  application.dbpool.getConnection do |dbh|
    sql = 'select symbol, name, price, change_price, ytd from daily_nav'
    dbh.select_all(sql) do |row|
      result.push QDailyNav.new *row
    end
  end
  result
end
	

Okay. That wasn't too bad. We did, however, have to explicitly reiterate the database schema to specify the struct. And then again when we wrote the SQL statement (which we had to do because there's no guarantee of field return order when using select *, even though on a given database the result may be predictable.

There is a way to make this a bit simpler without using Kansas, and for a lot of simple stuff. Let's take a look at that before the Kansas example:

# Wherever the code brings in its libraries:

require 'ostruct'

#
# Elsewhere in the code....

def getData
  result = []
  application.dbpool.getConnection do |dbh|
    sql = 'select * from daily_nav'
    sth = dbh.execute(sql)
    while row = sth.fetch_hash do
      result.push OpenStruct.new(row)
    end
  end
  result
end
	

In that version, we traded predefining a struct to hold our data for the OpenStruct class. Using OpenStruct allowed the SQL to be simpler, as we no longer care what order the fields are returned in. However, OpenStruct can't be initialized with a DBI::Row object (which is what is passed into the block in a select_all() call), so we have to resort to the less idiomatic while loop to read the data. Still, overall, it is definitely an improvement.

Now the Kansas way:

# Wherever the code brings in its libraries:

require 'kansas'

#
# Elsewhere in the code....

def getData
  result = []
  application.dbpool.getConnection do |dbh|
    ksdbh = KSDatabase.new(dbh)
    ksdbh.class.table('DailyNav','daily_nav')
    ksdbh.select('DailyNav').each do |nav|
      result.push nav
    end
  end
  result
end
	

No structs were defined. No SQL was written. An instance of KSDatabase was created, using the database handle provided by the connection pool. It was told to map the table, daily_nav to a class named DailyNav (which actually means KSDatabase::DailyNav), and then the select method was invoked on that class, iterating over the results, which are all first class ruby objects encapsulating the row data.

That's not bad, but it's still really not a lot different from the previous example, even if we were shielded from writing SQL statements. However, an application that was written to use Kansas would probably do the KSDatabase object initialization when it created the connection pool. So, our getData() method would become:

def getData
  result = []
  application.dbpool.getConnection do |ksdbh|
    ksdbh.select('DailyNav').each do |nav|
      result.push nav
    end
  end
  result
end
	

That is nice. Sweet and simple. No messing with structs, and no messing with SQL. This is why Kansas is nice to use.