diff --git a/app/models/notifier.rb b/app/models/notifier.rb index a220aae4..84f6ef15 100644 --- a/app/models/notifier.rb +++ b/app/models/notifier.rb @@ -23,7 +23,7 @@ class Notifier < ::ActionMailer::Base recipients record.email sent_on Time.now content_type 'text/html' - body record.class.name.downcase.to_sym => record + body record.class.name.downcase.to_sym => record, :resource => record end def translate(key, options={}) diff --git a/app/views/notifier/confirmation_instructions.html.erb b/app/views/notifier/confirmation_instructions.html.erb index 5af97418..4ba15cd7 100644 --- a/app/views/notifier/confirmation_instructions.html.erb +++ b/app/views/notifier/confirmation_instructions.html.erb @@ -1,5 +1,5 @@ -Welcome <%= @user.email %>! +Welcome <%= @resource.email %>! You can confirm your account through the link below: -<%#= link_to 'Confirm my account', confirmation_url(@user, :perishable_token => @user.perishable_token) %> +<%= link_to 'Confirm my account', confirmation_url(@resource, :perishable_token => @resource.perishable_token) %> diff --git a/app/views/notifier/reset_password_instructions.html.erb b/app/views/notifier/reset_password_instructions.html.erb index cf3a0a87..0d16ce5e 100644 --- a/app/views/notifier/reset_password_instructions.html.erb +++ b/app/views/notifier/reset_password_instructions.html.erb @@ -1,8 +1,8 @@ -Hello <%= @user.email %>! +Hello <%= @resource.email %>! Someone has requested a link to change your password, and you can do this through the link below. -<%#= link_to 'Change my password', edit_password_url(@user, :perishable_token => @user.perishable_token) %> +<%= link_to 'Change my password', edit_password_url(@resource, :perishable_token => @resource.perishable_token) %> If you didn't request this, please ignore this email. Your password won't change until you access the link above and create a new one. diff --git a/lib/devise/controllers/authenticable.rb b/lib/devise/controllers/authenticable.rb index 5ef42402..c9b3961c 100644 --- a/lib/devise/controllers/authenticable.rb +++ b/lib/devise/controllers/authenticable.rb @@ -24,7 +24,7 @@ module Devise # Proxy to the authenticated? method on warden # - def authenticated?(scope=resource_name) + def authenticated?(scope=:default) warden.authenticated?(scope.to_sym) end alias_method :logged_in?, :authenticated? diff --git a/lib/devise/controllers/filters.rb b/lib/devise/controllers/filters.rb index e6b19c8a..b9b86f91 100644 --- a/lib/devise/controllers/filters.rb +++ b/lib/devise/controllers/filters.rb @@ -25,9 +25,10 @@ module Devise end # Verify authenticated user and redirect to sign in if no authentication - # is found + # is found. By default resource_name is verified, but you can pass in any + # scope to verify whether there is a user authenticated within that scope. # - def authenticate!(scope) + def authenticate!(scope=resource_name) redirect_to new_session_path(scope) unless authenticated?(scope) end diff --git a/lib/devise/controllers/resources.rb b/lib/devise/controllers/resources.rb index a3f9bfb4..27664c29 100644 --- a/lib/devise/controllers/resources.rb +++ b/lib/devise/controllers/resources.rb @@ -2,12 +2,6 @@ module Devise module Controllers module Resources -# def self.included(base) -# base.class_eval do -# helper_method :resource, :resource_name, :resource_class -# end -# end - def resource @resource ||= instance_variable_get(:"@#{resource_name}") end @@ -27,7 +21,7 @@ module Devise private def resource_name_or_request_path(object=nil) - object ? (object.is_a?(::ActiveRecord::Base) ? object.class.name : object) : request.path + object ? (object.is_a?(::ActiveRecord::Base) ? object.class.name.downcase : object) : request.path end end end diff --git a/test/controllers/authenticable_test.rb b/test/controllers/authenticable_test.rb index 99d9ae28..900b6918 100644 --- a/test/controllers/authenticable_test.rb +++ b/test/controllers/authenticable_test.rb @@ -34,13 +34,13 @@ class ControllerAuthenticableTest < ActionController::TestCase end test 'run authenticate? with scope on warden' do - @mock_warden.expects(:authenticated?).with(:my_scope).returns(true) + @mock_warden.expects(:authenticated?).with(:my_scope) @controller.authenticated?(:my_scope) end test 'proxy logged_in? to authenticated' do - @mock_warden.expects(:authenticated?).returns(true) - @controller.logged_in? + @mock_warden.expects(:authenticated?).with(:my_scope) + @controller.logged_in?(:my_scope) end test 'run user on warden' do diff --git a/test/controllers/resources_test.rb b/test/controllers/resources_test.rb index 3f47db76..dcb0c8ed 100644 --- a/test/controllers/resources_test.rb +++ b/test/controllers/resources_test.rb @@ -13,6 +13,16 @@ class ResourcesTest < ActionController::TestCase assert_equal 'admin', @controller.resource_name end + test 'get resource name from an active_record object' do + user = Admin.new + assert_equal 'admin', @controller.resource_name(user) + end + + test 'get resource name from a symbol or string' do + assert_equal 'admin', @controller.resource_name(:admin) + assert_equal 'admin', @controller.resource_name('admin') + end + test 'get resource class from request path' do @request.path = '/users/session' assert_equal User, @controller.resource_class diff --git a/test/integration/admins/authentication_test.rb b/test/integration/admins/authentication_test.rb new file mode 100644 index 00000000..1c6b4cb4 --- /dev/null +++ b/test/integration/admins/authentication_test.rb @@ -0,0 +1,99 @@ +require 'test/test_helper' + +class AdminsAuthenticationTest < ActionController::IntegrationTest + + test 'home should be accessible without signed in admins' do + visit '/' + assert_response :success + assert_template 'home/index' + end + + test 'not signed in as admin should not be able to access admins actions' do + get admins_path + + assert_response :redirect + assert_redirected_to new_admin_session_path + assert_not warden.authenticated?(:admin) + end + + test 'signed in as user should not be able to access admins actions' do + sign_in_as_user + assert warden.authenticated?(:user) + assert_not warden.authenticated?(:admin) + + get admins_path + + assert_response :redirect + assert_redirected_to new_admin_session_path + + end + test 'signed in as admin should be able to access admin actions successfully' do + sign_in_as_admin + assert warden.authenticated?(:admin) + assert_not warden.authenticated?(:user) + + get admins_path + + assert_response :success + assert_template 'admins/index' + assert_contain 'Welcome Admin' + end + + test 'admin signing in with invalid email should return to sign in form with error message' do + sign_in_as_admin do + fill_in 'email', :with => 'wrongemail@test.com' + end + + assert_response :success + assert_template 'sessions/new' + assert_contain 'Invalid email or password' + assert_not warden.authenticated?(:admin) + end + + test 'admin signing in with invalid pasword should return to sign in form with error message' do + sign_in_as_admin do + fill_in 'password', :with => 'abcdef' + end + + assert_response :success + assert_template 'sessions/new' + assert_contain 'Invalid email or password' + assert_not warden.authenticated?(:admin) + end + + test 'not confirmed admin should not be able to login' do + sign_in_as_admin(:confirm => false) + + assert_contain 'Invalid email or password' + assert_not warden.authenticated?(:admin) + end + + test 'already confirmed admin should be able to sign in successfully' do + sign_in_as_admin + + assert_response :success + assert_template 'home/index' + assert_contain 'Signed in successfully' + assert_not_contain 'Sign In' + assert warden.authenticated?(:admin) + assert_not warden.authenticated?(:user) + end + + test 'not authenticated admin should not be able to sign out' do + delete admin_session_path + + assert_response :redirect + assert_redirected_to new_admin_session_path + assert_not warden.authenticated?(:admin) + end + + test 'authenticated admin should be able to sign out' do + sign_in_as_admin + assert warden.authenticated?(:admin) + + delete admin_session_path + assert_response :redirect + assert_redirected_to new_admin_session_path + assert_not warden.authenticated?(:admin) + end +end diff --git a/test/integration/admins/confirmation_test.rb b/test/integration/admins/confirmation_test.rb new file mode 100644 index 00000000..5501eeca --- /dev/null +++ b/test/integration/admins/confirmation_test.rb @@ -0,0 +1,49 @@ +require 'test/test_helper' + +class AdminsConfirmationTest < ActionController::IntegrationTest + + test 'admin should be able to request a new confirmation' do + admin = create_admin + ActionMailer::Base.deliveries.clear + + visit new_admin_session_path + click_link 'Didn\'t receive confirmation instructions?' + + fill_in 'email', :with => admin.email + click_button 'Resend confirmation instructions' + + assert_template 'sessions/new' + assert_contain 'You will receive an email with instructions about how to confirm your account in a few minutes' + assert_equal 1, ActionMailer::Base.deliveries.size + end + + test 'admin with invalid perishable token should not be able to confirm an account' do + visit user_confirmation_path(:perishable_token => 'invalid_perishable') + + assert_response :success + assert_template 'confirmations/new' + assert_have_selector '#errorExplanation' + assert_contain 'invalid confirmation' + end + + test 'admin with valid perishable token should be able to confirm an account' do + admin = create_admin(:confirm => false) + assert_not admin.confirmed? + + visit admin_confirmation_path(:perishable_token => admin.perishable_token) + + assert_template 'sessions/new' + assert_contain 'Your account was successfully confirmed!' + + assert admin.reload.confirmed? + end + + test 'admin already confirmed user should not be able to confirm the account again' do + admin = create_admin + visit admin_confirmation_path(:perishable_token => admin.perishable_token) + + assert_template 'confirmations/new' + assert_have_selector '#errorExplanation' + assert_contain 'already confirmed' + end +end diff --git a/test/integration/admins/password_recovery_test.rb b/test/integration/admins/password_recovery_test.rb new file mode 100644 index 00000000..079f3257 --- /dev/null +++ b/test/integration/admins/password_recovery_test.rb @@ -0,0 +1,104 @@ +require 'test/test_helper' + +class AdminsPasswordRecoveryTest < ActionController::IntegrationTest + + def visit_new_password_path + visit new_admin_session_path + click_link 'Forgot password?' + end + + def request_forgot_password(&block) + visit_new_password_path + + assert_response :success + assert_template 'passwords/new' + assert_not warden.authenticated?(:admin) + + fill_in 'email', :with => 'admin@test.com' + yield if block_given? + click_button 'Send me reset password instructions' + end + + def reset_password(options={}, &block) + visit edit_admin_password_path(:perishable_token => options[:perishable_token]) + assert_response :success + assert_template 'passwords/edit' + + fill_in 'Password', :with => '987654321' + fill_in 'Password confirmation', :with => '987654321' + yield if block_given? + click_button 'Change my password' + end + + test 'authenticated admin should not be able to visit forgot password page' do + sign_in_as_admin + assert warden.authenticated?(:admin) + + get new_admin_password_path + + assert_response :redirect + assert_redirected_to root_path + end + + test 'not authenticated admin should be able to request a forgot password' do + create_admin + request_forgot_password + + assert_template 'sessions/new' + assert_contain 'You will receive an email with instructions about how to reset your password in a few minutes.' + end + + test 'not authenticated admin with invalid email should receive an error message' do + request_forgot_password do + fill_in 'email', :with => 'invalid.test@test.com' + end + + assert_response :success + assert_template 'passwords/new' + assert_have_selector 'input[type=text][value=\'invalid.test@test.com\']' + assert_contain 'Email not found' + end + + test 'authenticated admin should not be able to visit edit password page' do + sign_in_as_admin + + get edit_admin_password_path + + assert_response :redirect + assert_redirected_to root_path + assert warden.authenticated?(:admin) + end + + test 'not authenticated admin with invalid perishable token should not be able to change his password' do + admin = create_admin + reset_password :perishable_token => 'invalid_perishable' + + assert_response :success + assert_template 'passwords/edit' + assert_have_selector '#errorExplanation' + assert_contain 'invalid confirmation' + assert_not admin.reload.valid_password?('987654321') + end + + test 'not authenticated admin with valid perisable token but invalid password should not be able to change his password' do + admin = create_admin + reset_password :perishable_token => admin.perishable_token do + fill_in 'Password confirmation', :with => 'other_password' + end + + assert_response :success + assert_template 'passwords/edit' + assert_have_selector '#errorExplanation' + assert_contain 'Password doesn\'t match confirmation' + assert_not admin.reload.valid_password?('987654321') + end + + test 'not authenticated admin with valid data should be able to change his password' do + admin = create_admin + reset_password :perishable_token => admin.perishable_token + + assert_template 'sessions/new' + assert_contain 'Your password was changed successfully.' + assert admin.reload.valid_password?('987654321') + end +end diff --git a/test/integration/authentication_test.rb b/test/integration/authentication_test.rb deleted file mode 100644 index afa4e8c5..00000000 --- a/test/integration/authentication_test.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'test/test_helper' - -class AuthenticationTest < ActionController::IntegrationTest - - test 'not authenticated user should load up sign in form' do - visit '/' - assert_response :success - assert_template 'sessions/new' - end - - test 'signing in with invalid email should return to sign in form with error message' do - sign_in do - fill_in 'email', :with => 'wrongemail@test.com' - end - - assert_response :success - assert_template 'sessions/new' - assert_contain 'Invalid email or password' - assert !warden.authenticated?(:user) - end - - test 'signing in with invalid pasword should return to sign in form with error message' do - sign_in do - fill_in 'password', :with => 'abcdef' - end - - assert_response :success - assert_template 'sessions/new' - assert_contain 'Invalid email or password' - assert !warden.authenticated? - end - - test 'not confirmed user should not be able to login' do - sign_in(:confirm => false) - - assert_contain 'Invalid email or password' - assert !warden.authenticated? - end - - test 'already confirmed user should be able to sign in successfully' do - sign_in - - assert_response :success - assert_template 'home/index' - assert_contain 'Signed in successfully' - assert_not_contain 'Sign In' - assert warden.authenticated?(:user) - end - - test 'not authenticated user should not be able to sign out' do - delete 'users/session' - - assert_response :redirect - assert_redirected_to new_user_session_path - assert !warden.authenticated?(:user) - end - - test 'authenticated user should be able to sign out' do - sign_in - assert warden.authenticated?(:user) - - delete 'users/session' - assert_response :redirect - assert_redirected_to new_user_session_path - assert !warden.authenticated?(:user) - end -end diff --git a/test/integration/users/authentication_test.rb b/test/integration/users/authentication_test.rb new file mode 100644 index 00000000..95022948 --- /dev/null +++ b/test/integration/users/authentication_test.rb @@ -0,0 +1,99 @@ +require 'test/test_helper' + +class UsersAuthenticationTest < ActionController::IntegrationTest + + test 'home should be accessible without signed in users' do + visit '/' + assert_response :success + assert_template 'home/index' + end + + test 'not signed in as user should not be able to access users actions' do + get users_path + + assert_response :redirect + assert_redirected_to new_user_session_path + assert_not warden.authenticated?(:user) + end + + test 'signed in as admin should not be able to access users actions' do + sign_in_as_admin + assert warden.authenticated?(:admin) + assert_not warden.authenticated?(:user) + + get users_path + + assert_response :redirect + assert_redirected_to new_user_session_path + + end + test 'signed in as user should be able to access users actions successfully' do + sign_in_as_user + assert warden.authenticated?(:user) + assert_not warden.authenticated?(:admin) + + get users_path + + assert_response :success + assert_template 'users/index' + assert_contain 'Welcome User' + end + + test 'user signing in with invalid email should return to sign in form with error message' do + sign_in_as_user do + fill_in 'email', :with => 'wrongemail@test.com' + end + + assert_response :success + assert_template 'sessions/new' + assert_contain 'Invalid email or password' + assert_not warden.authenticated?(:user) + end + + test 'user signing in with invalid pasword should return to sign in form with error message' do + sign_in_as_user do + fill_in 'password', :with => 'abcdef' + end + + assert_response :success + assert_template 'sessions/new' + assert_contain 'Invalid email or password' + assert_not warden.authenticated?(:user) + end + + test 'not confirmed user should not be able to login' do + sign_in_as_user(:confirm => false) + + assert_contain 'Invalid email or password' + assert_not warden.authenticated?(:user) + end + + test 'already confirmed user should be able to sign in successfully' do + sign_in_as_user + + assert_response :success + assert_template 'home/index' + assert_contain 'Signed in successfully' + assert_not_contain 'Sign In' + assert warden.authenticated?(:user) + assert_not warden.authenticated?(:admin) + end + + test 'not authenticated user should not be able to sign out' do + delete user_session_path + + assert_response :redirect + assert_redirected_to new_user_session_path + assert_not warden.authenticated?(:user) + end + + test 'authenticated user should be able to sign out' do + sign_in_as_user + assert warden.authenticated?(:user) + + delete user_session_path + assert_response :redirect + assert_redirected_to new_user_session_path + assert_not warden.authenticated?(:user) + end +end diff --git a/test/integration/confirmation_test.rb b/test/integration/users/confirmation_test.rb similarity index 70% rename from test/integration/confirmation_test.rb rename to test/integration/users/confirmation_test.rb index ab154c78..a09e1996 100644 --- a/test/integration/confirmation_test.rb +++ b/test/integration/users/confirmation_test.rb @@ -1,11 +1,12 @@ require 'test/test_helper' -class ConfirmationsTest < ActionController::IntegrationTest +class UsersConfirmationTest < ActionController::IntegrationTest - test 'should be able to request a new confirmation' do + test 'user should be able to request a new confirmation' do user = create_user + ActionMailer::Base.deliveries.clear - visit 'users/session/new' + visit new_user_session_path click_link 'Didn\'t receive confirmation instructions?' fill_in 'email', :with => user.email @@ -15,9 +16,10 @@ class ConfirmationsTest < ActionController::IntegrationTest # assert_redirected_to root_path assert_template 'sessions/new' assert_contain 'You will receive an email with instructions about how to confirm your account in a few minutes' + assert_equal 1, ActionMailer::Base.deliveries.size end - test 'with invalid perishable token should not be able to confirm an account' do + test 'user with invalid perishable token should not be able to confirm an account' do visit user_confirmation_path(:perishable_token => 'invalid_perishable') assert_response :success @@ -26,7 +28,7 @@ class ConfirmationsTest < ActionController::IntegrationTest assert_contain 'invalid confirmation' end - test 'with valid perishable token should be able to confirm an account' do + test 'user with valid perishable token should be able to confirm an account' do user = create_user(:confirm => false) assert_not user.confirmed? @@ -39,7 +41,7 @@ class ConfirmationsTest < ActionController::IntegrationTest assert user.reload.confirmed? end - test 'already confirmed user should not be able to confirm the account again' do + test 'user already confirmed user should not be able to confirm the account again' do user = create_user visit user_confirmation_path(:perishable_token => user.perishable_token) diff --git a/test/integration/password_recovery_test.rb b/test/integration/users/password_recovery_test.rb similarity index 60% rename from test/integration/password_recovery_test.rb rename to test/integration/users/password_recovery_test.rb index b9e15fff..78ccb8e9 100644 --- a/test/integration/password_recovery_test.rb +++ b/test/integration/users/password_recovery_test.rb @@ -1,36 +1,43 @@ require 'test/test_helper' -class PasswordRecoveryTest < ActionController::IntegrationTest +class UsersPasswordRecoveryTest < ActionController::IntegrationTest def visit_new_password_path - visit 'users/session/new' + visit new_user_session_path click_link 'Forgot password?' end def request_forgot_password(&block) visit_new_password_path - fill_in 'email', :with => 'test@test.com' + assert_response :success + assert_template 'passwords/new' + assert_not warden.authenticated?(:user) + + fill_in 'email', :with => 'user@test.com' yield if block_given? click_button 'Send me reset password instructions' end + def reset_password(options={}, &block) + visit edit_user_password_path(:perishable_token => options[:perishable_token]) + assert_response :success + assert_template 'passwords/edit' + + fill_in 'Password', :with => '987654321' + fill_in 'Password confirmation', :with => '987654321' + yield if block_given? + click_button 'Change my password' + end + test 'authenticated user should not be able to visit forgot password page' do - sign_in + sign_in_as_user + assert warden.authenticated?(:user) get new_user_password_path assert_response :redirect assert_redirected_to root_path - assert warden.authenticated?(:user) - end - - test 'not authenticated user should be able to visit forgot password page' do - visit_new_password_path - - assert_response :success - assert_template 'passwords/new' - assert !warden.authenticated?(:user) end test 'not authenticated user should be able to request a forgot password' do @@ -56,7 +63,7 @@ class PasswordRecoveryTest < ActionController::IntegrationTest end test 'authenticated user should not be able to visit edit password page' do - sign_in + sign_in_as_user get edit_user_password_path @@ -65,51 +72,36 @@ class PasswordRecoveryTest < ActionController::IntegrationTest assert warden.authenticated?(:user) end - test 'not authenticated with invalid perishable token should not be able to change his password' do - create_user - visit edit_user_password_path(:perishable_token => 'invalid_perishable') - assert_response :success - assert_template 'passwords/edit' - - fill_in 'Password', :with => '987654321' - fill_in 'Password confirmation', :with => '987654321' - click_button 'Change my password' + test 'not authenticated user with invalid perishable token should not be able to change his password' do + user = create_user + reset_password :perishable_token => 'invalid_perishable' assert_response :success assert_template 'passwords/edit' assert_have_selector '#errorExplanation' assert_contain 'invalid confirmation' - assert !@user.reload.valid_password?('987654321') + assert_not user.reload.valid_password?('987654321') end - test 'not authenticated with valid perisable token but invalid password should not be able to change his password' do - create_user - visit edit_user_password_path(:perishable_token => @user.perishable_token) - - fill_in 'Password', :with => '987654321' - fill_in 'Password confirmation', :with => 'other_password' - click_button 'Change my password' + test 'not authenticated user with valid perisable token but invalid password should not be able to change his password' do + user = create_user + reset_password :perishable_token => user.perishable_token do + fill_in 'Password confirmation', :with => 'other_password' + end assert_response :success assert_template 'passwords/edit' assert_have_selector '#errorExplanation' assert_contain 'Password doesn\'t match confirmation' - assert !@user.reload.valid_password?('987654321') + assert_not user.reload.valid_password?('987654321') end - test 'not authenticated with valid data should be able to change his password' do - create_user - visit edit_user_password_path(:perishable_token => @user.perishable_token) + test 'not authenticated user with valid data should be able to change his password' do + user = create_user + reset_password :perishable_token => user.perishable_token - fill_in 'Password', :with => '987654321' - fill_in 'Password confirmation', :with => '987654321' - click_button 'Change my password' - - # TODO: revisit this assert_template 'sessions/new' -# assert_response :redirect -# assert_redirected_to new_session_path assert_contain 'Your password was changed successfully.' - assert @user.reload.valid_password?('987654321') + assert user.reload.valid_password?('987654321') end end diff --git a/test/support/integration_tests_helper.rb b/test/support/integration_tests_helper.rb index 48d7bc33..6ef02650 100644 --- a/test/support/integration_tests_helper.rb +++ b/test/support/integration_tests_helper.rb @@ -7,17 +7,36 @@ class ActionController::IntegrationTest def create_user(options={}) @user ||= begin user = User.create!( - :email => 'test@test.com', :password => '123456', :password_confirmation => '123456' + :email => 'user@test.com', :password => '123456', :password_confirmation => '123456' ) user.confirm! unless options[:confirm] == false user end end - def sign_in(options={}, &block) + def create_admin(options={}) + @admin ||= begin + admin = Admin.create!( + :email => 'admin@test.com', :password => '123456', :password_confirmation => '123456' + ) + admin.confirm! unless options[:confirm] == false + admin + end + end + + def sign_in_as_user(options={}, &block) create_user(options) - visit 'users/session/new' - fill_in 'email', :with => 'test@test.com' + visit new_user_session_path + fill_in 'email', :with => 'user@test.com' + fill_in 'password', :with => '123456' + yield if block_given? + click_button 'Sign In' + end + + def sign_in_as_admin(options={}, &block) + create_admin(options) + visit new_admin_session_path + fill_in 'email', :with => 'admin@test.com' fill_in 'password', :with => '123456' yield if block_given? click_button 'Sign In' diff --git a/test/test_helper.rb b/test/test_helper.rb index ab6d493f..e69355b1 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -14,18 +14,14 @@ ActiveRecord::Migration.verbose = false ActiveRecord::Base.logger = Logger.new(nil) ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") ActiveRecord::Schema.define(:version => 1) do - create_table :users do |t| - t.string :email, :null => false - t.string :encrypted_password, :null => false - t.string :password_salt, :null => false - t.string :perishable_token - t.datetime :confirmed_at - end - - create_table :admins do |t| - t.string :email, :null => false - t.string :encrypted_password, :null => false - t.string :password_salt, :null => false + [:users, :admins].each do |table| + create_table table do |t| + t.string :email, :null => false + t.string :encrypted_password, :null => false + t.string :password_salt, :null => false + t.string :perishable_token + t.datetime :confirmed_at + end end end