views:

37

answers:

2

Simple question that used to puzzle me about Rails:

Is it possible to describe a Model's structure from within the model rb file?

From what I understand a model's data structure is kept within the migration, and the model.rb file is supposed to contain only the business logic.

Why is it so? Why does it make more sense to migrate the database with a rake task than to extract it from the class?

+2  A: 

The reason migrations are stored separately is so that you can version your database. This would be unwieldy if done inline in the model.

Other ORMs (like DataMapper) do store the schema in the model definition. I think it's really convenient to be able to see model attributes right there, but it is unfortunate to not have the history of your database structure.

What I really wish is that running the migrations would just insert some comments at the top of the model file detailing the schema. That should be a simple hack.

cam
You could look at the annotate_models plugin/gem that does just that for you.
Steve Weet
From my point of view, database structure history is irrelevant if it is extracted from the model. Versioning of the database schema then becomes a natural extension of the model. [Edit:] Using a regular VCS, you would obtain the same advantages, I mean.
MrZombie
+1  A: 

Migrations do not simply show the state of the database schema.

They define the transitions from one state to another.

In a comment to cam's post, you said having the schema in the model would do the same thing, if you had the model's source stored in a VCS, you could look up the previous versions of the schema.

Here is why that is not equivalent to migrations:

Schema Version 1
string :name
string :password
string :token

Schema Version 2
string :username
string :displayname
string :password
string :token

So, what did I do here? What happened to "name"? Did I rename it to username? Or maybe I renamed it to displayname? Or did I drop it entirely?

You don't know. There's no way to tell. You only see the "before" and "after" of the schema. You don't see the transition.

Let's instead look at what I really did with this migration:

class UpdateNameFields < ActiveRecord::Migration
  def self.up
    rename_column :users, :name, :username
    add_column :users, :displayname
    User.update_all("displayname = username")
  end

  def self.down
    remove_column :users, :displayname
    rename_column :users, :username, :name
  end
end

See, I had been using "name" for usernames. But you wouldn't be able to tell that without the migration here. Plus, in an effort to not have my new displayname column be blank on all my existing records, I have seeded it with everyone's existing usernames. This lets me gently introduce this new feature - I can use it and know that existing records aren't going to just see a blank field.

Note that this is a trivial example. Because it was so trivial, you could take a guess that it was one of a couple possible options. But had it been a much more complex transition, you simply would not know.

Transitions from one schema to another can involve a more than just adding/deleting/renaming columns. I gave a little example above in my User.update_all. Whatever code you might need to execute to migrate data to the new schema, you can put in the migration.

When people say migrations are about "versioning the database", they don't just mean versioning the snapshot of the schema. They mean the ability to move between those schema states, and triggering all of the actions involved in going from one state to another.

Legion
Ah, I see the problem. Simply put, migrations are there so you can mess with the code without destroying the data. I can see where this is practical, but I'm still not convinced that it's a cure-all solution. Not much different than writing SQL scripts and/or procedures, only, it is contained within the Ruby code and is database-agnostic. Problem is, I still don't understand the applications in real life. I will have to read more on the question, and still very much dislike the idea of my model's structure being defined elsewhere than my model. :P
MrZombie
Migrations are very much in the spirit of rapid development, much like Rails in general. The idea is that they make it very easy to not only define transitions to new schema, but provide an easy and uniform way to go *backwards* in schema. As an app gets larger and more mature, migrations play less of a role, for sure. They're very handy for that frantic gotta-get-this-working development rush mode, though.
Legion