How do you set HABTM values during migration?

Hi All,

I’m a bit perplexed as to how to set up certain values during
migration. My issue is similar to the following post:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/95e65b9f701a8f95/8bce8ff26f996da6?lnk=gst&q=admin+migration#8bce8ff26f996da6

…however, I have a setup with Users and Roles, and I need to assign
a role to an administrative user during migration. My setup uses
HABTM via join tables. Any ideas?

Thanks in advance.

Michael Williams wrote:

Hi All,

I’m a bit perplexed as to how to set up certain values during
migration. My issue is similar to the following post:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/95e65b9f701a8f95/8bce8ff26f996da6?lnk=gst&q=admin+migration#8bce8ff26f996da6

…however, I have a setup with Users and Roles, and I need to assign
a role to an administrative user during migration. My setup uses
HABTM via join tables. Any ideas?

Thanks in advance.

I have Users, Roles, and Permissions.

migration 001

def self.up

create your tables

create_table :users
# user fields
end
create_table :roles
# role fields
end
create_table :permissions
# permission fields
end

then the data (I know that this is a new DB, and these are

the first two records created, so a 1, and 1 for record IDs works.

User.create( set user fields)
Role.create( set role fields)

I suppose I could do a find to get the IDs for User and

Role, but I don’t need to

Permission.create(:user_id => 1, :role_id => 1)
end

gberz3 wrote:

…however, I have a setup with Users and Roles, and I need to assign
a role to an administrative user during migration. My setup uses
HABTM via join tables. Any ideas?

Someone may suggest a specific technical fix.

My favorite general recommendation here is something called “deprecation
refactor”. That means you leave an old system online while developing a
new one.
Some deployments go out with both systems online.

So suppose we have Foo 1<–>* Bar, and we need Foo <–> Bar.
Temporarily leave
the foo_id in Bar, and add a bars_foos table. Get your tests and code
passing
with both has_many and habtm in the Foo table. (This might require some
hacks
that belie ActiveRecord’s usual elegance, such as a Foo#get_bars method
that
conglomerates both its owned and linked Bars.) Any new records should
start
using the new system.

Wall-to-wall unit tests are critical here, because they allow your logic
to
temporarily survive a bad design.

Now deploy this intermediate design, and let it “soak” into the
database.

After you have confidence the habtm itself works - such as after a week
of
working on other features within the new design - now you write the
migration
that moves records out of the has_many association and into the habtm.
The
migration takes advantage of your experience with the new habtm working
in practice.

Write a unit test for a migration like this (and any cleaner way would
interest
me!):

def test_migration_convert_has_many_to_habtm
# assemble the database up here
require RailsRoot + ‘db/migrate/042_has_many_to_habtm.rb’
ConvertHasManyToHabtm.verbose = false
ConvertHasManyToHabtm.up # activate the migration
# assert here that foo_id variables are cleared, and
# that bars_foos is populated
end

Now deploy the migration, run the site for a while, and observe that all
foo_id
fields remain empty.

Finally, write another migration that retires the Bar.foo_id field. At
this
time, disable test_migration_convert_has_many_to_habtm (unless you want
to start
it by adding that field back!).

This post describes a very large, multi-phase “deprecation refactor”.
Only use
this beast if your data records are very complex. (Some of ours are
still in
progress!:wink: For the simple matter of upgrading Foo.has_many :bars, you
should
still do all the steps in this order (per books like /Refactoring/), but
you
don’t need to deploy and wait a while between each step.


Phlip