Dev Bootcamp: Phase 1, Week 3, Day 1

When I first tried learning rails, I was somewhat put off by the magic that seemed to happen under the hood: why was it that when I created a class User and a table users, they were somehow linked up and I could now call User.all to get all the users from my database, of user.find_by_first_name, even without writing a ‘find_by_first_name’ method?

Today we were tasked with creating a few ruby applications that supported CRUD operations (Create, Read, Update, Delete) on SQLite databases. While we’ll be looking into ActiveRecord in a day or two, today we were reliant on the sqlite3 ruby gem.

The first challenge was to create a single-table students database, but the second was to create a more complex address book that supported a many-many relationship between contacts and groups. Initially, my pair and I created CRUD methods for the individual classes that would represent tables in the database (contacts, groups and the join table group_members). However, due to the obvious repetition we decided to pull what we could into a parent class that the Contact, Group and GroupMembers classes could extend, but unfortunately a lot of the SQL queries and instantiation relied on knowledge of the table columns.

In the afternoon lecture, Shereef showed us how we could both create and set instance variables on an instantiated object from a hash using ‘instance_variable_set’, so we were able to pull the initialize for all three classes into the ObjectMapper parent class:

1
2
3
4
5
def initialize(args = {})
    args.each do |key, value|
      instance_variable_set("@#{key}", value) unless key.to_s =~ /^\d+$/
    end
  end

We also wanted to replicate the ActiveRecord #find_by_COLUMN, which we achieved with #method_missing:

1
2
3
4
5
6
7
8
9
10
  def self.where(column_name, value)
    map_query_result_to_objects($db.execute("SELECT * FROM #{get_table_name} WHERE #{column_name} = ?", value))
  end
 
  def self.method_missing(method_name, *args)
    if method_name =~ /^find_by_/
      column_name = method_name.to_s.gsub(/^find_by_(.*)/, '\\1')
      self.where(column_name, args)
    end
  end

In the end, we were able to trim our classes down to a mere declaration of the class itself and an extend of our ObjectMapper toolbox:

1
2
class Contact < ObjectMapper
end

Leave a Reply

Your email address will not be published. Required fields are marked *

*