Read-only Models in ActiveRecord
I recently worked on a project that was built around several hundred GB of data. Several large databases were populated by one team and then consumed by several other applications.
The project I was working on needed to access that data but would never need to modify it. Perhaps I was being overly paranoid, but I wanted to specify that everything from those databases was read-only to make sure nothing was accidently changed.
ActiveRecord is one of the coolest things about rails. With almost no work I get data models, including dead-simple CRUD, in my applications. But the inability to designate a model as being read-only is really frustrating to me.
I know that rails can mark individual instances with :readonly, but I wanted to ensure that every instance of these objects was read-only. Here’s what I came up with:
class FooBar < ActiveRecord::Base # Prevent creation of new records and modification to existing records def readonly? return true end # Prevent objects from being destroyed def before_destroy raise ActiveRecord::ReadOnlyRecord end end
And that’s all there is to it! FooBar objects are now read-only.
# You will not be able to create new records >> fb = FooBar.create(:name => "zork") ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord # You will not be able to save modified records >> fb = FooBar.find(:first) >> fb.name = "plugh" >> fb.save ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord # You will not be able to destroy existing records >> fb = FooBar.find(:first) >> fb.destroy ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
For the sake of simplicity the code here is in a regular rails model. But if you’re working with a large number of models like I was, you might consider extracting these methods into a new class “ReadOnlyActiveRecord” which can then used as the parent for the other models. DRY, right?
Depending on your situation, you may need to lock down the record more tightly. With this code you could still create and manipulate new FooBar objects in your code. The exception isn’t thrown until you try to persist to the database.
I certainly don’t consider myself an expert on rails. If anyone knows a better solution to this problem I’d love to hear it.