views:

683

answers:

3

I've got a legacy database with a bunch of idiotically named columns like:

some_field_c
some_other_field_c
a_third_field_c

I would very much like to make a Rails ActiveRecord sub-class that automatically aliases these attributes to their name minus the underscore and "c". However, when I tried:

attributes.each_key do | key |
  name = key
  alias_attribute key.to_sym, key[0, (key.length -2)].to_sym if key =~ /_c$/
end

in my class definition I got an "undefined local variable or method `attributes'" error. I also tried overwriting these methods:

method_missing
respond_to?

but I kept getting errors with that route too.

So my question (actually questions) is/are:

  1. Is what I'm trying to do even possible?
  2. If so, what's the best way to do it (iterative aliasing or overwriting method missing)?
  3. If it's not too much trouble, a very brief code sample of how to do #2 would be awesome.

Thanks in advance for any answers this post receives.

+2  A: 

Your problem is probably that attributes is an instance method and you're doing this in the context of the class. The class method that's closest to what you want is column_names.

Chuck
It worked, thank you! I did have to put the code in to a method that was called by the sub class to make it to work, and it would have been nice if instead there were some way to say "Run this re-aliasing code right after any sub-class attribute creation occurs", but *shrug* oh well. Thanks again!
machineghost
I tried to paste my working code in here, but the comment formatting ruined it :-( However, it was essentially the same code as in my question, except with "column_names" instead of "attributes", and no "name = key" (that line was garbage), all defined in a method that is called by the sub-class.
machineghost
+1  A: 

you could do something like:

methods.each do |method|
  if method.ends_with("_c") then
    self.send(:defind_method,method.slice(0,-2)){self.send(method)}
  end
end
LDomagala
+1  A: 

Hmm... I cooked up this little solution that I thought ended up pretty elegantly...

Not sure if this will work, but I aliased method_missing to still allow active_record to do it's thang:

module ShittyDatabaseMethods
  alias :old_method_missing :method_missing

  def method_missing(method)
    if methods.include?("#{method}_c")
      send("#{method}_c".to_sym)
    else
      old_method_missing(method)
    end
  end

end

class Test
  attr_accessor :test_c
  include ShittyDatabaseMethods
end

You may not get to name your module "ShittyDatabaseMethods", but you get the idea ;) Once you define that module and stuff it into lib, you just need to include this module and off you go :D

Would love to hear if this works out for you :)

Keith Hanson
Thanks, but unfortunately that won't work :-( If I do something likemy_shitty_object.some_attribute = 5that becomes a method call to "some_attribute="; if you append "_c" to that you get "some_attribute=_c" as your method name, instead of "some_attribute_c=".
machineghost
Ah, true true. Good catch :)
Keith Hanson