You effectively do specify your fields before runtime in Rails, though, in the form of your migrations. Furthermore all of the associations (belongs_to, etc), are arguably also specified before runtime since those helpers run when the class is parsed.
I frequently don't use Ruby-based migrations. I use datastores defined elsewhere and use Sequel as a convenient and easy interface for tasks against those datastores.
You're trying to slice too fine. Crystal can't do this. That's OK. Own it, don't try to hide it.
At that point, you may (should) just use an object mapper like ROM that has no model-class hooks at all. (Which I actually like. But the Rails world disagrees.)