Overriding or aliasing name of column in legacy database using Rails/ActiveRecord

I’m writing a Rails application against a legacy database. One of the tables in this legacy database has a column named object_id. Unfortunately object_id is also an attribute of every object in Ruby, so when ActiveRecord is trying to use these objects to formulate a query it is using the Ruby defined object_id, rather than the value that is in the database.

The legacy application is immense at well over a million lines of code, so simply changing the name of the column in the database would be an option of last resort.

Questions:

  1. Is there any way to make ActiveRecord/Rails use an alias or synonym for this column? (i.e. such that in my app I can access the value using the alias, but when AR generates SQL it uses the actual column name)

  2. Is there any way in Ruby to make the object_id method behave differently, depending on who is calling it? (i.e. use database object_id when being called by activerecord, otherwise return ruby object_id)

  3. Can I simply override the behavior of the object_id method in my model (I assume this is a terrible idea, but had to ask)

Any suggestions are greatly appreciated.

Ouch. I don't have a definitive answer, but you maybe able to get away defining the following methods in your model...

def newobjid
  read_attribute(:object_id)
end

def newobjid=(val)
  write_atrribute(:object_id, val)
end

There may even be a short cut to the above... definitely take a look at the source of read_attribute/write_attribute to see what they are doing and figure out where the short comings in the above are...

-philip

Hi Mike,

Having a db table column named object_id is going to be a problem for
any ruby-based app, given object_id's significance as an object
attribute in ruby (http://www.ruby-doc.org/core/classes/
Object.html#M000339).

If your rails app only needs to read from (and not write to) that
legacy table, ... I'd create a view in the db which includes all the
columns in that table but with that object_id column renamed to
something like ob_id, and then read from that view instead of the
table in your rails app.

If your rails app needs to write to that table, and your rails app and
the legacy app are the only two apps that use that legacy db, and you
are able to modify that legacy app, .... I'd change that column name
in the db to something like ob_id, then I'd search for ($ grep -Erni
"object_id" /path/to/proj/root ) and replace all specific references
to that specific column in the legacy code.

Jeff

Thanks for all the replies.

Let me ask you this:

If I do set up a view to rename the columns, can I override the create
and update method in ActiveRecord::Base to make it insert into the
original table (rather than the view)? Having looked at
ActiveRecord::Base a little bit it would seem that I can do this
without any ill effects.

Thanks,
Mike

Why can't you just insert/update the view? As long as the view isn't
joining any other tables, you can treat it as a regular table (AFAIK
in MySQL, SQL Server, PostgreSQL).

I would like to be able to support mssql, postgres, oracle, and
sqlite3. My understanding is that updateable views wouldn't work for
sqlite3, but I'll have to do a bit more research.

Thanks for your suggestion.

Mike B wrote in post #966322:

I would like to be able to support mssql, postgres, oracle, and
sqlite3. My understanding is that updateable views wouldn't work for
sqlite3, but I'll have to do a bit more research.

Unless I'm badly mistaken, you should just need to use alias_attribute.
No DB view is necessary.

Thanks for your suggestion.

Best,

Marnen Laibow-Koser wrote in post #966357:

Mike B wrote in post #966322:

I would like to be able to support mssql, postgres, oracle, and
sqlite3. My understanding is that updateable views wouldn't work for
sqlite3, but I'll have to do a bit more research.

Unless I'm badly mistaken, you should just need to use alias_attribute.
No DB view is necessary.

...or not. I looked at the source of alias_attribute, and I'm not sure
if it will work. But do try it before messing with DB views.

Thanks for the suggestion. Gave it a try but it appears that support
for aliased columns does not run very deep in ActiveRecord.

For example, if you alias obj_id to object_id and then try to do:
Object.find_by_obj_id(3) it complains that the method is unknown.

I did a bit of digging on sqlite3 updateable views and it appears that
this can be accomplished by writing some triggers. Same applies for
postgres and others as well.

Barring an absolutely brilliant suggestion I'm going to pursue this
solution.

Thanks to all,
Mike

Mike B wrote in post #966542:

Thanks for the suggestion.

Please quote when replying -- I have no idea whom you're thanking for
what suggestion.

Gave it a try but it appears that support
for aliased columns does not run very deep in ActiveRecord.

For example, if you alias obj_id to object_id and then try to do:
Object.find_by_obj_id(3) it complains that the method is unknown.

But find_by_object_id should work.

I did a bit of digging on sqlite3 updateable views and it appears that
this can be accomplished by writing some triggers. Same applies for
postgres and others as well.

Barring an absolutely brilliant suggestion I'm going to pursue this
solution.

Please don't do that yet. Spend some more time with aliased attributes
-- you shouldn't need to write a view for something this simple. If it
were my DB, I'd either use alias_attribute or something like Philip's
solution. There should be no need for views here.

Actually, I'd probably change the name of the field. The legacy
application shouldn't be a concern: no two applications should directly
touch the same DB anyway IMHO. Another good idea might be to have the
legacy application expose a REST service (with any field names you
like!) that the Rails application can interact with by using
ActiveResource (this is sort of what we've done here at my job).

If you *do* need DB views, try the rails_sql_views plugin.

Thanks to all,
Mike

Best,

If you are using ActiveRecord 3, try using safe_attributes gem I
released recently. It will probably resolve your issue. If not, let
me know.

https://rubygems.org/gems/safe_attributes

Brian