In the Edge version of “Active Record Migrations,” section 6, “Using Models in Your Migrations,” we’re advised to create a local model. I see the sample code, but I don’t entirely understand what it’s doing, or how it achieves that. I can’t really suggest wording that would be clearer, since I’m not sure I get the point yet, but I can at least describe what I think I grok, and where it all falls off the rails (as it were):
The example “creates a local model,” declaring a class in-line that does nothing but extend ActiveRecord::Base
class Product < ActiveRecord::Base
Because it’s actually subclassed off ActiveRecord::Base, this definition does not extend, modify, or reuse our “real” Product model; it’s a completely unrelated model that happens to have the same name. Because it has no content, all manner of things that the real model would do don’t happen here.
Within the syntactic scope of the migration method, references to the Product model such as these:
add_column :products, :flag, :boolean
actually operate on our local model, not our real model. But migration methods “operate” by generating SQL statements, and the SQL statements refer to tables and columns by name, and the standard Rails name legerdemain applies, so the SQL statements generated by migration operations on the local model “just happen” to look exactly the same as they would have if generated against our real model. The database “does what I say, not what I mean,” and in this case that’s what’s wanted. Meanwhile, our strikingly rudimentary local model has no validators, so it doesn’t trip up on unsatisfiable validations.
It might be clearer with some addition like “This ensures that migration operations, operating on the local model, aren’t subject to the real model’s validators; at the same time, the actual generated SQL is the same, because the local model has the same name as the real one.”
Places where I’m still confused:
Sadly, our actual models by no means follow standard Rails name legerdemain. We’re grafting a Rails app onto a preexisting database, with non-Rails-y naming conventions, and we have a very substantial assortment of alternative name legerdemain. Do I have to reproduce that in the local model? I suppose so….
Name shell-games aside, I’m worried there might be cases where the Rails migration-to-SQL logic would actually need to know the current table definition, and would end up missing important info. For example, if I delete a column from the real model but, say, misspell its name, I seem to get a Ruby/Rails exception about my error. But with a local model, this would have to fall through to SQL’s “no such column” detection. Is that really the same thing? Is there something I should handle differently here? Am I risking more-obscure surprises?