Ruby on Rails
SaltedLoginGeneratorQuickstart (Version #26)

This page is intended to help you get the SaltedHashLoginGenerator up and running quickly and easily. JoeHosteny’s generator is cool, but getting it set up has caused a lot of headaches on the mailing list.

In just a few steps, we’ll get the SaltedHashLoginGenerator up and running and get all the tests passing. I’m assuming that you have Ruby, Rails, and a database engine capable of transactions (MySQL 4.x with InnoDB, Postgres, etc)

Step One

Install the SaltedHashLoginGenerator and LocalizationGenerator gems:

gem install salted_login_generator
gem install localization_generator

Step Two

Create your application and get into its directory:

rails yourapp
cd yourapp

Step Three

Bring in the generator code:

script/generate salted_login User Localization

Note: you can of course choose a different name for these, but that’s outside the scope of this guide

Step Four

Edit your ApplicationController (yourapp/app/controllers/application.rb) to look like this

require 'localization'
require 'user_system'

# The filters added to this controller will be run for all controllers in the application.
# Likewise will all the methods added be available for all controllers.
class ApplicationController < ActionController::Base
  include Localization
  include UserSystem

  helper :user
  model  :user
end

Step Four-and-a-half

You must require login in the user controller. One way to do this is to add the following line to (yourapp/app/controllers/user_controller.rb)

before_filter :login_required

Or, alternately, if you want to protect your entire application, you can add that same line to your ApplicationController, making it unnecessary to specifically require login on the user controller.

Step Five

Add the following line to your ApplicationHelper (yourapp/app/helpers/application_helper.rb):

include Localization

Step Six

Add the following lines to the bottom of yourapp/config/environment.rb:

require 'environments/localization_environment'
require 'localization'
Localization::load_localized_strings
require 'environments/user_environment'

Step Seven

Edit yourapp/config/environments/localization_environment.rb to taste. Refer to yourapp/README_LOCALIZATION for more details.

Step Eight

Edit yourapp/config/environments/user_environment.rb to taste. Specifically, make sure you set the email addresses and URL, or else your notifications won’t work/will be funky. If you leave the FROM field blank, some SMTP servers will refuse your email.

Step Nine

Add appropriate mail configuration to your Rails environments (yourapp/config/environments/development.rb, yourapp/config/environments/test.rb, yourapp/config/environments/production.rb):

ActionMailer::Base.server_settings = {
  :address => "smtp.yourdomain.com",
  :port => 25
}

Note: For many people, more settings will be required. In particular, you may need to set the :domain key. Refer to the ActionMailer docs for more information about the ActionMailer::Base.server_settings variable.

Thanks to DavidCorbin? for pointing this out.

Step Ten

Ensure that your database settings are properly configured in yourapp/config/database.yml, and import the appropriate schema in yourapp/README_USER_LOGIN. Note, however, that the schema in the README (as of 2005-06-03) is slightly different that the one offered in yourapp/db/user_model.erbsql. You\u2019re only missing three fields, though\u2026 created_at, updated_at, logged_in_at which you can create by hand if you want.

If you have the db_structure gem installed, you can attempt to create the schema using it by running:

script/create_db

Note: db_structure v1.0.2 does not work with PostgreSQL. If you’re running Postgres, you’re going to have to create the database by hand.

Also note: If you run this command on an already existing application, it will destroy all tables in the databases referenced by your database.yml file.

Step Eleven

Run the tests and watch in amazement as they pass with flying colors:

yourbox ~/yourapp $ rake test_units
(in /home/you/yourapp)
ruby -Ilib:test "/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader.rb" \
"test/unit/localization_test.rb" "test/unit/user_test.rb"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader
Started
..............
Finished in 1.51912 seconds.
14 tests, 32 assertions, 0 failures, 0 errors

yourbox ~/yourapp $ rake test_functional
(in /home/you/yourapp)
ruby -Ilib:test "/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader.rb" \
"test/functional/user_controller_test.rb"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader
Started
.........
Finished in 4.013108 seconds.

9 tests, 93 assertions, 0 failures, 0 errors

If the functional tests fail with a warning: already initialized constant DEFAULT_HEAD_OPTIONS, edit yourapp/app/helpers/login_helper.rb. At the top change

@
module UserHelper?

DEFAULT_HEAD_OPTIONS = { :notice => true, :message => true, :error => false }.freeze

@

to

@
module <span class="newWikiWord">UserHelper<a href="http://wiki.rubyonrails.com/rails/pages/UserHelper">?</a></span>

  DEFAULT_HEAD_OPTIONS = {
    :notice => true,
    :message => true,
    :error => false
  }.freeze unless const_defined? "DEFAULT_HEAD_OPTIONS"
@

(there are other bug fixes on the SaltedHashLoginGenerator page, but you can get by without most of them).

Step Twelve

There is no step twelve!

Seriously, though, read the README so you understand how to use your new system.


Please post questions or comments here

Either I don’t know what I’m doing (quite possible, being a Rails and Ruby newbie), or this thing is broken. I keep getting errors like

NoMethodError in User#login 
WARNING: You have a nil object when you probably didn’t expect it! Odds are you
want an instance of Array instead.

Look in the callstack to see where you’re working with an object that could be nil.
Investigate your methods and make sure the object is what you expect!

/app/controllers/user_controller.rb:9:in `login’
script/server:49


In looking at the code, it seems that there are assumptions about @params‘user’ being set. When you come in fresh, this is nil, which causes this error. Why is there no exception handling in here???
So, is my application or configuration messed up, or is this generator the problem?

Possible Solution

I had some of the same problems. It turned out that the machine I was running an old version of rails. Double check yours?



Nope, it’s been rails-0.13.1 all along, but I even ran
gem install rails —remote
just to be sure. No change.

Even more puzzling, unit testing works fine:

C:>rake test_units
ruby -Ilib;test “c:/ruby/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_lo
ader.rb” “test/unit/localization_test.rb” “test/unit/user_test.rb”
Loaded suite c:/ruby/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader

Started
…………..
Finished in 1.072 seconds.

14 tests, 32 assertions, 0 failures, 0 errors

I’ve had no problems with the regular LoginGenerator, so I’m dropping back to that. sigh

Unit tests fail in PostgreSQL 8 because email is not allowed to be NULL.

Once I set the email field to be nullable the unit tests worked.

Shouldn’t :app_url be configurable per environment?

Perhaps it can but I have absolutely no idea how? :-)

Should DB column token_expiry be token_expires_at?

It was my understand that date/time columns were supposed to be named as such.

If the functional test fails, this might be why

If you changed :security_token_life_hours, the functional test might fail for your because of a hardcoded value in line 55. Be sure to modify this value to match your :security_token_life_hours setting in days.

No rhtml, rxml, or delegate template found for _form

One of the reasons that some people have more/less trouble with this generator than other people has to do with what they did before starting this quickstart. If you’ve already created most of your application, and you’re adding in this login system, you’ve got more of the ecostructure in place. If this login system is the first thing you’re creating for your app, you may find that some pieces are missing.

One item not generated in some cases is app/views/user/form.rhtml. Here’s a stub _form.rhtml that will suffice:


<%= error
messages_for ‘user’ %>




<%= text_field ‘user’, ‘login’ %>



<%= password_field ‘user’, ‘salted_password’ %>



<%= text_field ‘user’, ‘email’ %>



<%= text_field ‘user’, ‘firstname’ %>



<%= text_field ‘user’, ‘lastname’ %>



<%= text_field ‘user’, ‘salt’ %>



<%= text_field ‘user’, ‘verified’ %>



<%= text_field ‘user’, ‘role’ %>



<%= text_field ‘user’, ‘security_token’ %>



<%= datetime_select ‘user’, ‘token_expiry’ %>



<%= datetime_select ‘user’, ‘created_at’ %>



<%= datetime_select ‘user’, ‘updated_at’ %>



<%= datetime_select ‘user’, ‘logged_in_at’ %>



<%= text_field ‘user’, ‘deleted’ %>



<%= datetime_select ‘user’, ‘delete_after’ %>


users database keeps disappearing
If your user database keeps disappearing when you run your rake tests, maybe you tried to test out your changes on the test database before applying them to dev database. Make sure you apply the database changes to the dev database. The test database is re-created from the test database every time the tests are run.

*How to see what your SMTP server is reporting *
If you’ve identified that you have a problem sending your signup emails in UserNotify.deliver_signup (probably you’re seeing “Error creating account: confirmation email not sent”), one way to see what’s going on is to use the console.

ruby script/console
>>  UserNotify.deliver_signup(User.new, "", "")
Net::SMTPSyntaxError: 500 5.5.1 Command unrecognized: "AUTH CRAM-MD5"

NOTE for Windows Users
You need to have iconv support.

This page is intended to help you get the SaltedHashLoginGenerator up and running quickly and easily. JoeHosteny’s generator is cool, but getting it set up has caused a lot of headaches on the mailing list.

In just a few steps, we’ll get the SaltedHashLoginGenerator up and running and get all the tests passing. I’m assuming that you have Ruby, Rails, and a database engine capable of transactions (MySQL 4.x with InnoDB, Postgres, etc)

Step One

Install the SaltedHashLoginGenerator and LocalizationGenerator gems:

gem install salted_login_generator
gem install localization_generator

Step Two

Create your application and get into its directory:

rails yourapp
cd yourapp

Step Three

Bring in the generator code:

script/generate salted_login User Localization

Note: you can of course choose a different name for these, but that’s outside the scope of this guide

Step Four

Edit your ApplicationController (yourapp/app/controllers/application.rb) to look like this

require 'localization'
require 'user_system'

# The filters added to this controller will be run for all controllers in the application.
# Likewise will all the methods added be available for all controllers.
class ApplicationController < ActionController::Base
  include Localization
  include UserSystem

  helper :user
  model  :user
end

Step Four-and-a-half

You must require login in the user controller. One way to do this is to add the following line to (yourapp/app/controllers/user_controller.rb)

before_filter :login_required

Or, alternately, if you want to protect your entire application, you can add that same line to your ApplicationController, making it unnecessary to specifically require login on the user controller.

Step Five

Add the following line to your ApplicationHelper (yourapp/app/helpers/application_helper.rb):

include Localization

Step Six

Add the following lines to the bottom of yourapp/config/environment.rb:

require 'environments/localization_environment'
require 'localization'
Localization::load_localized_strings
require 'environments/user_environment'

Step Seven

Edit yourapp/config/environments/localization_environment.rb to taste. Refer to yourapp/README_LOCALIZATION for more details.

Step Eight

Edit yourapp/config/environments/user_environment.rb to taste. Specifically, make sure you set the email addresses and URL, or else your notifications won’t work/will be funky. If you leave the FROM field blank, some SMTP servers will refuse your email.

Step Nine

Add appropriate mail configuration to your Rails environments (yourapp/config/environments/development.rb, yourapp/config/environments/test.rb, yourapp/config/environments/production.rb):

ActionMailer::Base.server_settings = {
  :address => "smtp.yourdomain.com",
  :port => 25
}

Note: For many people, more settings will be required. In particular, you may need to set the :domain key. Refer to the ActionMailer docs for more information about the ActionMailer::Base.server_settings variable.

Thanks to DavidCorbin? for pointing this out.

Step Ten

Ensure that your database settings are properly configured in yourapp/config/database.yml, and import the appropriate schema in yourapp/README_USER_LOGIN. Note, however, that the schema in the README (as of 2005-06-03) is slightly different that the one offered in yourapp/db/user_model.erbsql. You\u2019re only missing three fields, though\u2026 created_at, updated_at, logged_in_at which you can create by hand if you want.

If you have the db_structure gem installed, you can attempt to create the schema using it by running:

script/create_db

Note: db_structure v1.0.2 does not work with PostgreSQL. If you’re running Postgres, you’re going to have to create the database by hand.

Also note: If you run this command on an already existing application, it will destroy all tables in the databases referenced by your database.yml file.

Step Eleven

Run the tests and watch in amazement as they pass with flying colors:

yourbox ~/yourapp $ rake test_units
(in /home/you/yourapp)
ruby -Ilib:test "/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader.rb" \
"test/unit/localization_test.rb" "test/unit/user_test.rb"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader
Started
..............
Finished in 1.51912 seconds.
14 tests, 32 assertions, 0 failures, 0 errors

yourbox ~/yourapp $ rake test_functional
(in /home/you/yourapp)
ruby -Ilib:test "/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader.rb" \
"test/functional/user_controller_test.rb"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader
Started
.........
Finished in 4.013108 seconds.

9 tests, 93 assertions, 0 failures, 0 errors

If the functional tests fail with a warning: already initialized constant DEFAULT_HEAD_OPTIONS, edit yourapp/app/helpers/login_helper.rb. At the top change

@
module UserHelper?

DEFAULT_HEAD_OPTIONS = { :notice => true, :message => true, :error => false }.freeze

@

to

@
module <span class="newWikiWord">UserHelper<a href="http://wiki.rubyonrails.com/rails/pages/UserHelper">?</a></span>

  DEFAULT_HEAD_OPTIONS = {
    :notice => true,
    :message => true,
    :error => false
  }.freeze unless const_defined? "DEFAULT_HEAD_OPTIONS"
@

(there are other bug fixes on the SaltedHashLoginGenerator page, but you can get by without most of them).

Step Twelve

There is no step twelve!

Seriously, though, read the README so you understand how to use your new system.


Please post questions or comments here

Either I don’t know what I’m doing (quite possible, being a Rails and Ruby newbie), or this thing is broken. I keep getting errors like

NoMethodError in User#login 
WARNING: You have a nil object when you probably didn’t expect it! Odds are you
want an instance of Array instead.

Look in the callstack to see where you’re working with an object that could be nil.
Investigate your methods and make sure the object is what you expect!

/app/controllers/user_controller.rb:9:in `login’
script/server:49


In looking at the code, it seems that there are assumptions about @params‘user’ being set. When you come in fresh, this is nil, which causes this error. Why is there no exception handling in here???
So, is my application or configuration messed up, or is this generator the problem?

Possible Solution

I had some of the same problems. It turned out that the machine I was running an old version of rails. Double check yours?



Nope, it’s been rails-0.13.1 all along, but I even ran
gem install rails —remote
just to be sure. No change.

Even more puzzling, unit testing works fine:

C:>rake test_units
ruby -Ilib;test “c:/ruby/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_lo
ader.rb” “test/unit/localization_test.rb” “test/unit/user_test.rb”
Loaded suite c:/ruby/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/rake_test_loader

Started
…………..
Finished in 1.072 seconds.

14 tests, 32 assertions, 0 failures, 0 errors

I’ve had no problems with the regular LoginGenerator, so I’m dropping back to that. sigh

Unit tests fail in PostgreSQL 8 because email is not allowed to be NULL.

Once I set the email field to be nullable the unit tests worked.

Shouldn’t :app_url be configurable per environment?

Perhaps it can but I have absolutely no idea how? :-)

Should DB column token_expiry be token_expires_at?

It was my understand that date/time columns were supposed to be named as such.

If the functional test fails, this might be why

If you changed :security_token_life_hours, the functional test might fail for your because of a hardcoded value in line 55. Be sure to modify this value to match your :security_token_life_hours setting in days.

No rhtml, rxml, or delegate template found for _form

One of the reasons that some people have more/less trouble with this generator than other people has to do with what they did before starting this quickstart. If you’ve already created most of your application, and you’re adding in this login system, you’ve got more of the ecostructure in place. If this login system is the first thing you’re creating for your app, you may find that some pieces are missing.

One item not generated in some cases is app/views/user/form.rhtml. Here’s a stub _form.rhtml that will suffice:


<%= error
messages_for ‘user’ %>




<%= text_field ‘user’, ‘login’ %>



<%= password_field ‘user’, ‘salted_password’ %>



<%= text_field ‘user’, ‘email’ %>



<%= text_field ‘user’, ‘firstname’ %>



<%= text_field ‘user’, ‘lastname’ %>



<%= text_field ‘user’, ‘salt’ %>



<%= text_field ‘user’, ‘verified’ %>



<%= text_field ‘user’, ‘role’ %>



<%= text_field ‘user’, ‘security_token’ %>



<%= datetime_select ‘user’, ‘token_expiry’ %>



<%= datetime_select ‘user’, ‘created_at’ %>



<%= datetime_select ‘user’, ‘updated_at’ %>



<%= datetime_select ‘user’, ‘logged_in_at’ %>



<%= text_field ‘user’, ‘deleted’ %>



<%= datetime_select ‘user’, ‘delete_after’ %>


users database keeps disappearing
If your user database keeps disappearing when you run your rake tests, maybe you tried to test out your changes on the test database before applying them to dev database. Make sure you apply the database changes to the dev database. The test database is re-created from the test database every time the tests are run.

*How to see what your SMTP server is reporting *
If you’ve identified that you have a problem sending your signup emails in UserNotify.deliver_signup (probably you’re seeing “Error creating account: confirmation email not sent”), one way to see what’s going on is to use the console.

ruby script/console
>>  UserNotify.deliver_signup(User.new, "", "")
Net::SMTPSyntaxError: 500 5.5.1 Command unrecognized: "AUTH CRAM-MD5"

NOTE for Windows Users
You need to have iconv support.