Patch for Issue 1259 -- New #1297

Merged
merged 4 commits into from Aug 30, 2011
@@ -1,6 +1,7 @@
require 'rails/generators/active_record'
require 'generators/devise/orm_helpers'
+
module ActiveRecord
module Generators
class DeviseGenerator < ActiveRecord::Generators::Base
@@ -9,14 +10,27 @@ class DeviseGenerator < ActiveRecord::Generators::Base
include Devise::Generators::OrmHelpers
source_root File.expand_path("../templates", __FILE__)
- def generate_model
- invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
- end
-
def copy_devise_migration
- migration_template "migration.rb", "db/migrate/devise_create_#{table_name}"
+ exists = model_exists?
+ unless behavior == :revoke
+ unless exists
+ migration_template "migration.rb", "db/migrate/devise_create_#{table_name}"
+ else
+ migration_template "migration_existing.rb", "db/migrate/add_devise_to_#{table_name}"
+ end
+ else
+ if migration_exists?(table_name)
+ migration_template "migration_existing.rb", "db/migrate/add_devise_to_#{table_name}"
+ else
+ migration_template "migration.rb", "db/migrate/devise_create_#{table_name}"
+ end
+ end
end
+ def generate_model
+ invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
+ end
+
def inject_devise_content
inject_into_class(model_path, class_name, model_contents + <<CONTENT) if model_exists?
# Setup accessible (or protected) attributes for your model
@@ -0,0 +1,32 @@
+class AddDeviseTo<%= table_name.camelize %> < ActiveRecord::Migration
+ def self.up
+ change_table :<%= table_name %> do |t|
+ t.database_authenticatable :null => false
+ t.recoverable
+ t.rememberable
+ t.trackable
+
+ # t.encryptable
+ # t.confirmable
+ # t.lockable :lock_strategy => :<%= Devise.lock_strategy %>, :unlock_strategy => :<%= Devise.unlock_strategy %>
+ # t.token_authenticatable
+
+<% for attribute in attributes -%>
+ t.<%= attribute.type %> :<%= attribute.name %>
+<% end -%>
+ #Uncomment below if timestamps were not included in your original model.
+ t.timestamps
+ end
+ add_index :<%= table_name %>, :email, :unique => true
+ add_index :<%= table_name %>, :reset_password_token, :unique => true
+ # add_index :<%= table_name %>, :confirmation_token, :unique => true
+ # add_index :<%= table_name %>, :unlock_token, :unique => true
+ # add_index :<%= table_name %>, :authentication_token, :unique => true
+ end
+
+ def self.down
+ #By default, we don't want to make any assumption about how to roll back a migration when your
+ #model already existed. Please edit below which fields you would like to remove in this migration.
+ raise ActiveRecord::IrreversibleMigration

This comment has been minimized.

Show comment Hide comment
@daf

daf Aug 30, 2011

In my attempt at this, I had added "rm_" versions of each of the... well not sure what the word is, but the t.recoverable, t.trackable etc.. that way the down could be just the inverse of the things up top. So if you had t.recoverable in the up, you could put t.rm_recoverable in the down. Easy enough to mirror after editing.

Not sure what to do with the attribute section though, as I'm not sure what those are for.

@daf

daf Aug 30, 2011

In my attempt at this, I had added "rm_" versions of each of the... well not sure what the word is, but the t.recoverable, t.trackable etc.. that way the down could be just the inverse of the things up top. So if you had t.recoverable in the up, you could put t.rm_recoverable in the down. Easy enough to mirror after editing.

Not sure what to do with the attribute section though, as I'm not sure what those are for.

This comment has been minimized.

Show comment Hide comment
@seanski

seanski Aug 30, 2011

Contributor

That wouldn't be a bad thing to do; however, I'm worried what would happen if we had a user model with email, for example. It would be deleted when we the migration is rolled back, and this would not be ideal. I still think it's better for the user to write the rollback code. Maybe we could add the rollback code in the migration but leave it commented out?

@seanski

seanski Aug 30, 2011

Contributor

That wouldn't be a bad thing to do; however, I'm worried what would happen if we had a user model with email, for example. It would be deleted when we the migration is rolled back, and this would not be ideal. I still think it's better for the user to write the rollback code. Maybe we could add the rollback code in the migration but leave it commented out?

This comment has been minimized.

Show comment Hide comment
@daf

daf Aug 30, 2011

I agree - the existing email thing is very tricky. It's in database_authenticatable aleady and will blow up as it stands currently if the existing model has that field (as many will, I suspect).

I had thought to possibly handle that by detecting the existence of the model, and somehow checking its fields? Then instead of database_authenticatable, something else, like database_authenticatable_existing_email, and the complementary rm would follow suit? Getting very complicated, though...

@daf

daf Aug 30, 2011

I agree - the existing email thing is very tricky. It's in database_authenticatable aleady and will blow up as it stands currently if the existing model has that field (as many will, I suspect).

I had thought to possibly handle that by detecting the existence of the model, and somehow checking its fields? Then instead of database_authenticatable, something else, like database_authenticatable_existing_email, and the complementary rm would follow suit? Getting very complicated, though...

+ end
+end
@@ -14,6 +14,14 @@ def model_contents
def model_exists?
File.exists?(File.join(destination_root, model_path))
end
+
+ def migration_exists?(table_name)
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_devise_to_#{table_name}.rb$/).first
+ end
+
+ def migration_path
+ @migration_path ||= File.join("db", "migrate")
+ end
def model_path
@model_path ||= File.join("app", "models", "#{file_path}.rb")
@@ -13,9 +13,22 @@ class ActiveRecordGeneratorTest < Rails::Generators::TestCase
assert_file "app/models/monster.rb", /devise/, /attr_accessible (:[a-z_]+(, )?)+/
assert_migration "db/migrate/devise_create_monsters.rb"
end
+
+ test "update model migration when model exists" do
+ run_generator %w(monster)
+ assert_file "app/models/monster.rb"
+ run_generator %w(monster)
+ assert_migration "db/migrate/add_devise_to_monsters.rb"
+ end
test "all files are properly deleted" do
run_generator %w(monster)
+ run_generator %w(monster)
+ assert_migration "db/migrate/devise_create_monsters.rb"
+ assert_migration "db/migrate/add_devise_to_monsters.rb"
+ run_generator %w(monster), :behavior => :revoke
+ assert_no_migration "db/migrate/add_devise_to_monsters.rb"
+ assert_migration "db/migrate/devise_create_monsters.rb"
run_generator %w(monster), :behavior => :revoke
assert_no_file "app/models/monster.rb"
assert_no_migration "db/migrate/devise_create_monsters.rb"