How to share variables in data migrations (up/down)

hi there,
How do we share variables in a given data migration?

For example,
the code below fails to work because “statuses” don’t seem to be in
scope for both up and down.

class AddDefaultValuesToStatuses < ActiveRecord::Migration
statuses = [
{
‘details’ => ‘details’,
},
{
‘fitting_profile’ => ‘fitting profile’,
},
{
‘live’ => ‘published on site’,
},
{
‘sold’ => ‘auction ended’,
},
]

def self.up
statuses.each do |status|
status.each do |name, display_title|
@new_status = Status.new(
:name => name,
:display_title => display_title,
:created_by => ‘admin’,
:updated_by => ‘admin’
).save
end
end
end

def self.down
statuses.each do |status|
status.each do |name, display_title|
@status = Status.searchlogic(
:name => name,
:display_title => display_title
)
@status.delete
end
end
end
end

How do we share variables in a given data migration?

Personally, in your case I’d declare it as a constant instead of a
variable
at the class level.

good idea but i would still like to learn how to share variables in a
given
migration.

Ideas, gents? :slight_smile: thanks :slight_smile:

Gordon Y. wrote:

good idea but i would still like to learn how to share variables in a
given
migration.

Ideas, gents? :slight_smile: thanks :slight_smile:

up and down are class methods. You share variables between them just
like you would between any class methods in Ruby.

However, your original example is problematic. You appear to be using
migrations for seed data. This is a terrible idea. Use seed.rb or
seed-fu instead.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Marnen,
thanks :slight_smile: Seed-fu and seed.rb - something for me to read on :smiley:
cheers :slight_smile:

On Thu, Mar 4, 2010 at 5:18 AM, Marnen Laibow-Koser
[email protected] wrote:

However, your original example is problematic. You appear to be using
migrations for seed data. This is a terrible idea.

Why?


Hassan S. ------------------------ [email protected]
twitter: @hassan

Because migration for definition structure and manage it.for fill data
use seeds , or SQL files.

Sent from my iPhone

On 04.03.2010, at 19:03, Hassan S. [email protected]

On Thu, Mar 4, 2010 at 8:31 AM, Ivan N. [email protected]
wrote:

Because migration for definition structure and manage it.

Just repeating it doesn’t make it valid. Again, why do you think that?


Hassan S. ------------------------ [email protected]
twitter: @hassan

On 4 March 2010 16:50, Hassan S. [email protected]
wrote:

On Thu, Mar 4, 2010 at 8:31 AM, Ivan N. [email protected] wrote:

Because migration for definition structure and manage it.

Just repeating it doesn’t make it valid. Again, why do you think that?

Migrations are a “point in time” reference about the structure of a
database.

If they alter data then they are binding tightly to the models - and
you can no longer have a later migration that adds/removes/renames
columns, because those columns will be set in the data in an earlier
migration.
Similarly, you can now no longer alter your models, because some
migrations rely on the operation of the model at that “point in time”

  • if you create a model, with all its validations, field
    specifications, etc, and a migration uses that model. If you later try
    to change the model by adding or changing a validation, your migration
    will no longer work, as it is tightly bound to the old model.

Is that bad enough for you?

Being “decoupled” (separating things from each other, so that they’re
all free to change without breaking other things) is the fundamental
principle of MVC - models do their thing, as do views, as do
controllers - and it’s made deliberately awkward to mix them up…
because it’s bad. So keep your migrations away from seed data, and
also separate your tests from fixtures.

Of course this assumes that you’re not going back and editing
migrations… but you wouldn’t do that, because that’s bad too! :slight_smile:

On Mar 4, 2010, at 12:14 PM, Michael P. wrote:

Migrations are a “point in time” reference about the structure of a
to change the model by adding or changing a validation, your migration
will no longer work, as it is tightly bound to the old model.

Is that bad enough for you?

So, the solution is to define a point-in-time version of your Model
that is just enough for you to use to accomplish the migration (incl.
seeding data).

For example, if you are only working with the new columns of a table,
you can (and I’ll argue you SHOULD!) define an empty class inside the
migration.

class CreateGames < ActiveRecord::Migration
class Game < ActiveRecord::Base
end
def self.up
create_table :games do |t|
t.string :name
t.integer :prize
t.timestamps
end
Game.reset_column_information
Game.create(:name => “Jackpot”, :prize => 1000000)
Game.create(:name => “Instant Win”, :prize => 100)
Game.create(:name => “Pick 4”, :prize => 5000)
end

def self.down
drop_table :games
end
end

If you have later code (and another migration) that, say, validates
that prizes must be a minimum of 500, your migration can find and fix
existing data, but THIS migration will still work.

This reduces the coupling to the current schema which is what
ActiveRecord is going to reflect upon. If you need some of the
functionality from the model, then it should be duplicated here in the
migration.

-Rob


You received this message because you are subscribed to the Google
Groups “Ruby on Rails: Talk” group.
To post to this group, send email to rubyonrails-
[email protected].
To unsubscribe from this group, send email to [email protected]
.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en
.

Rob B. http://agileconsultingllc.com
[email protected]
+1 513-295-4739
Skype: rob.biedenharn

On 4 March 2010 17:30, Hassan S. [email protected]
wrote:

Similarly, you can now no longer alter your models, because some
migrations rely on the operation of the model at that “point in time”

Likewise, sorry, that makes zero sense to me. I can’t alter a model
because it was altered before? What??

Maybe an example would help…

Yesterday I created this model and it’s migration:

class Post < ActiveRecord::Base
end

class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :summary
t.string :user_id
t.timestamps
end

default record for Posts

Post.reset_column_information
Post.create(:name => “My First Post”, :user_id => 1)
end

def self.down
drop_table :posts
end
end

Today I decide that I need to have an “expired flag” on my posts, and
it’s required, so I create a new migration and edit my model:
class Post < ActiveRecord::Base
validates_presence_of :expires
end

class AlterPostsAddExpired < ActiveRecord::Migration
def self.up
add_column :posts, :expires, :datetime
end

def self.down
remove_column :posts, :expires
end
end

But now, when I roll back my migrations and try to roll them forward
again, the “create” breaks because the “point in time” Post record
didn’t require an “expires” value (or indeed, even have a field for
it!).

As Rob says, the way around this is to duplicate the model code (or at
least the minimum you need to achieve your result) in the migration -
but this is a smelly, non-DRY, and non-testable way of putting data in
the tables (remember where I said it was “deliberately awkward” to do)

  • when the alternative is to use a seed or seed-fu (like, 30 seconds
    work), I can’t believe you’re making me go through these hoops to show
    you how bad a practice it is to put data in your migrations
    :-/

On Thu, Mar 4, 2010 at 9:14 AM, Michael P. [email protected]
wrote:

Migrations are a “point in time” reference about the structure of a database.

If they alter data then they are binding tightly to the models - and
you can no longer have a later migration that adds/removes/renames
columns, because those columns will be set in the data in an earlier
migration.

Huh? I can’t even parse that last part - “columns set in the data in
an earlier migration”?? What does that mean?

I can’t see how a db column being populated with data is going to
keep you from using a migration to update/change it, structure- or
content-wise.

Similarly, you can now no longer alter your models, because some
migrations rely on the operation of the model at that “point in time”

Likewise, sorry, that makes zero sense to me. I can’t alter a model
because it was altered before? What??

Maybe an example would help…


Hassan S. ------------------------ [email protected]
twitter: @hassan

On Thu, Mar 4, 2010 at 9:58 AM, Michael P. [email protected]
wrote:

Similarly, you can now no longer alter your models, because some
migrations rely on the operation of the model at that “point in time”

Today I decide that I need to have an “expired flag” on my posts, and
it’s required, so I create a new migration …

But now, when I roll back my migrations and try to roll them forward
again, …

Well, sorry, why are you “rolling back” at all? Why not just run the
new migration? I can’t remember every needing to do a rollback for
other than a single most recent migration (due to some obvious
d’oh! screwup like a typo).

I can’t believe you’re making me go through these hoops to show
you how bad a practice it is to put data in your migrations

This seems to be a lesson in “at some point old migrations will fail if
(when) your models change enough” rather than anything to do with
migrations and data.


Hassan S. ------------------------ [email protected]
twitter: @hassan

On Mar 4, 2010, at 12:58 PM, Michael P. wrote:

Yesterday I created this model and it’s migration:

class Post < ActiveRecord::Base
end

take that model…

class CreatePosts < ActiveRecord::Migration

and put it right here INSIDE the migration class

But now, when I roll back my migrations and try to roll them forward
you how bad a practice it is to put data in your migrations
:-/

You can have the same kind of problems in migrations that aren’t
trying to seed data, but just do the kind of things to change existing
data that happen in the real world.

I’ve helped projects that got themselves into trouble with too many
migrations happening between production deployments and the set of
migrations can’t even be run forward during the deploy.

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On 4 March 2010 18:22, Hassan S. [email protected]
wrote:

Well, sorry, why are you “rolling back” at all? Why not just run the
new migration? I can’t remember every needing to do a rollback for
other than a single most recent migration (due to some obvious
d’oh! screwup like a typo).

So why are you using migrations at all? And why have data in them?!

I work in a team with other developers, on code that gets deployed
onto test, staging and live servers - migrations get run all the time.
Of course, on my “bedroom” projects where I’m the only person doing
anything on the code, I almost never re-run migrations.

I can’t believe you’re making me go through these hoops to show
you how bad a practice it is to put data in your migrations

This seems to be a lesson in “at some point old migrations will fail if
(when) your models change enough” rather than anything to do with
migrations and data.

No - old migrations only fail if you access your models from them.

You asked for an example… you got one. If you choose to ignore the
advice (or you think it doesn’t apply to you) that’s okay - it’s only
advice; a convention; a recommendation. Not a command :slight_smile: That’s the
beauty of Rails; convention over configuration. Stick to the
conventions and you make everyone’s life easier (including your
own)… but hey ho, it’s your choice.

On Thu, Mar 4, 2010 at 11:28 AM, Michael P. [email protected]
wrote:

I work in a team with other developers, on code that gets deployed
onto test, staging and live servers - migrations get run all the time.

So do I – but migrating down multiple levels and back up has never
entered the picture. What circumstances require that?

You asked for an example… you got one. If you choose to ignore the
advice (or you think it doesn’t apply to you) that’s okay - it’s only
advice; a convention; a recommendation.

And yet I still fail to see the evidence supporting this as a
“convention”,
especially given that the API includes examples of using migrations to
change data, not just structure.

http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

Hence my question :slight_smile:


Hassan S. ------------------------ [email protected]
twitter: @hassan

On 4 March 2010 18:14, Rob B. [email protected]
wrote:

take that model…
and put it right here INSIDE the migration class

Yes, I did agree with you that that’s possible. It’s just not very
pleasant, or the simplest thing to do.

Hassan S. wrote:

And yet I still fail to see the evidence supporting this as a
“convention”,
especially given that the API includes examples of using migrations to
change data, not just structure.

ActiveRecord::Migration

Well… Alright - one migration creates an index, and another one
removes empty tags. This is a one-time change to the data, which would
be necessary as a migration but would not show up in the schema.rb file.
The original comment was “Don’t seed with migrations - seed with the
seeding process”. This is still a good idea. :slight_smile:

On Thu, Mar 4, 2010 at 12:00 PM, Aldric G. [email protected]
wrote:

The original comment was “Don’t seed with migrations - seed with the
seeding process”. This is still a good idea. :slight_smile:

Just because. Sweet ${DEITY} I give up.

Thanks anyway.

Hassan S. ------------------------ [email protected]
twitter: @hassan

well, in one of my other migrations (which was large), i populated FACT
data
with raw sqls. That worked really well but this time round, I wanted to
use
ruby all the way.