I have a legacy db (oracle), in that db I have several tables that hold different data but are structurally the same. I am not allowed to change the DB schema in any way!
I wanted a DRY ActiveRecord Model to fetch the right data from the right tables. The problem was that I needed to dynamically overwrite self.table_name
in order for it work.
Here is my code:
ActiveRecord:Base Class which will be inherited by all similar tables
class ListenLoc < ActiveRecord::Base
@@table_name = nil
def self.table_name
@@table_name
end
default_scope { where(validated: 1).where("URL_OK >= 0") }
scope :random_order, -> { order('DBMS_RANDOM.VALUE') }
scope :unvalidated, -> { unscope(:where).where(validated: 0) }
def self.get_category(cat_id)
where("cat_id = ?", cat_id)
end
def self.rand_sample(cat_id, lim)
where("cat_id = ?", cat_id).random_order.limit(lim)
end
end
Child Classes look as such:
A
class ListenLocA < ListenLoc
@@table_name = 'LISTEN_ADDR_A'
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatA', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
B.
class ListenLocB < ListenLoc
@@table_name = 'LISTEN_ADDR_B'
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
The above works, however I have already noticed that there are some pitfalls when doing specific select lookups.
Is this a good approach? Is there a better way to pass the self.table_name
dynamically?
Update:
One would think that this should work, but I get an error that the table does not exist since ActiveRecord tries to validate the table before creating an Object, and self.table_name is not set on the ListenLoc Model dynamically.
class ListenLocB < ListenLoc
self.table_name = 'LISTEN_ADDR_B'
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
What I realized is, that I could just use superclass
without using globals, which I ended up using. This does not pose a race condition issue as with globals.
class ListenLocB < ListenLoc
superclass.table_name = 'LISTEN_ADDR_B' # or ListenLoc.table_name
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end