Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Legacy table with column named "class" in Rails

I've got a legacy table that my rails application shares with another application. It has a column called "class". The first time I reference any attribute in that model, I get an error. Subsequent references to attributes work. Is there a good workaround for this, or should I just go modify the other application that uses this table (ugh)?

>> Member::Ssg.find(:first)
 => #<Member::Ssg ssg_key: #<BigDecimal:10b169688,'0.253E3',4(8)>, org_id: 2, academic_year: 2006, class: true, next_due_date: "2011-06-01", submitted_date: "2006-02-13", notes: nil, owner_id: "1">
 >> Member::Ssg.find(:first).notes
 NoMethodError: undefined method `generated_methods' for true:TrueClass
        from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/attribute_methods.rb:247:in `method_missing'
        from (irb):2
>> Member::Ssg.find(:first).notes
=> nil

SOLUTION: I went with a combination of the Bellmyer solution and adding the code below to my model

 class << self
   def instance_method_already_implemented?(method_name)
     return true if method_name == 'class'
     super
   end
 end
like image 447
Anna Avatar asked Oct 28 '10 14:10

Anna


1 Answers

NOTE: Please see the updated solution at the end of this answer. Leaving the original outdated solution for historic reasons.

This has come up often enough (legacy column names interfering with ruby/rails) that I might just make a plugin out of this. Here's how you can fix it right away, though. Create this file in your app:

# lib/bellmyer/create_alias.rb
module Bellmyer
  module CreateAlias
    def self.included(base)
      base.extend CreateAliasMethods
    end

    module CreateAliasMethods
      def create_alias old_name, new_name
        define_method new_name.to_s do
          self.read_attribute old_name.to_s
        end

        define_method new_name.to_s + "=" do |value|
          self.write_attribute old_name.to_s, value
        end
      end
    end
  end
end

And now, in your model:

class Member < ActiveRecord::Base
  include Bellmyer::CreateAlias
  create_alias 'class', 'class_name'
end

The first parameter to create_alias is the old method name, and the second parameter is the new name you want to call it, that won't interfere with rails. It basically uses the read_attribute and write_attribute methods to interact with the column instead of the ruby methods that get defined by ActiveRecord. Just be sure to use the new name for the field everywhere, like so:

member.class_name = 'helper'

This works with ruby 1.8, but I haven't tested with ruby 1.9 yet. I hope this helps!

UPDATE: I've found a better solution that works in Rails 3, the safe_attributes gem. I've written a blog post explaining how to use it, with example code snippets, and a full sample app you can download from github and play around with. Here's the link:

Legacy Database Column Names in Rails 3

like image 54
Jaime Bellmyer Avatar answered Nov 05 '22 13:11

Jaime Bellmyer