Skip to content

Instantly share code, notes, and snippets.

@agibralter
Created October 14, 2008 23:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agibralter/16815 to your computer and use it in GitHub Desktop.
Save agibralter/16815 to your computer and use it in GitHub Desktop.
Processing UsersController#create (for 127.0.0.1 at 2008-10-14 18:53:56) [POST]
Parameters: {"user"=>{"terms_and_conditions"=>"1", "password_confirmation"=>"password", "gender"=>"", "year"=>"", "password"=>"password", "news"=>"1", "email"=>"bob@fat.com"}, "commit"=>"join the project", "action"=>"create", "controller"=>"users"}
SQL (0.000238) SELECT `email` FROM `users` WHERE (`users`.email = 'bob@fat.com') 
SQL (0.000130) SELECT `email` FROM `users` WHERE (`users`.email = 'bob@fat.com') 
User Create (0.000205) INSERT INTO `users` (`salt`, `questions_count`, `updated_at`, `dob`, `crypted_password`, `state_password`, `gender`, `responses_count`, `multipurpose_code`, `email_verified_at`, `year`, `state_email`, `display_name`, `login`, `email`, `created_at`, `news`) VALUES('c0d1e5cfdsa159229f753baf5b72dbb865e249e2', 0, '2008-10-14 22:53:56', NULL, 'c8398e31f1d73249902dfds273abf165da1559a0', 'known', '', 0, '9fd5aded6db6b8fps0b7ea9a35035d5d36be38c2', NULL, '', 'unverified', NULL, NULL, 'bob@fat.com', '2008-10-14 22:53:56', 1)
User Load (0.000174) SELECT * FROM `users` WHERE (`users`.`id` = 1011) 
Sent mail to bob@fat.com
require 'digest/sha1'
class User < ActiveRecord::Base
###########################
# state
###########################
state_machine :state_email, :initial => 'fresh' do
before_transition :to => 'unverified', :do => :make_email_verification_code
before_transition :to => 'verified', :do => :set_email_verified_attributes
event :unverify do
transition :to => 'unverified', :from => %w(unverified verified fresh)
end
event :verify do
transition :to => 'verified', :from => %w(unverified verified fresh)
end
end
state_machine :state_password, :initial => 'known' do
before_transition :to => 'forgotten', :do => :make_forgot_password_code
before_transition :to => 'known', :do => :clear_forgot_password_code
event :forgets_password do
transition :to => 'forgotten', :from => 'known'
end
event :remembers_password do
transition :to => 'known', :from => 'forgotten'
end
end
def verified?
state_email == 'verified'
end
def unverified?
state_email == 'unverified'
end
def forgets_password?
state_password == 'forgotten'
end
def remembers_password?
state_password == 'known'
end
###########################
# attributes
###########################
# Virtual attribute for the unencrypted password
attr_accessor :password, :terms_and_conditions
attr_accessible :login,
:email,
:password,
:password_confirmation,
:terms_and_conditions,
:news,
:gender,
:display_name,
:year
###########################
# validations
###########################
validates_presence_of :email, :message => "missing email"
validates_length_of :login, :within => 3..40, :allow_blank => true, :message => "login must be between 3 and 40 characters"
validates_length_of :email, :within => 3..100, :allow_blank => true, :message => "email must be between 3 and 100 characters"
validates_uniqueness_of :login, :allow_blank => true, :message => "login is taken"
validates_uniqueness_of :email, :allow_blank => true, :message => "email is taken; please contact us if this seems like a mistake"
validates_format_of :login, :with => /^[^@]+$/, :allow_blank => true, :message => "invalid login (cannot be an email address)"
validates_format_of :email, :with => /(\S+)@(\S+)/, :allow_blank => true, :message => "invalid email"
validates_presence_of :password, :if => :password_required?, :message => "missing password"
validates_presence_of :password_confirmation, :if => :password_required?, :message => "missing password confirmation"
validates_length_of :password, :within => 4..40, :if => :password_required?, :missing => "password must be between 4 and 40 characters"
validates_confirmation_of :password, :if => :password_required?, :message => "passwords do not match"
validates_presence_of :terms_and_conditions, :on => :create, :message => "please read and agree to the terms and conditions"
validates_format_of :terms_and_conditions, :with => /^1$/, :on => :create, :message => "please read and agree to the terms and conditions"
validates_inclusion_of :news, :in => [true,false], :allow_blank => true
validates_inclusion_of :gender, :in => %w(m f), :allow_blank => true, :message => 'must select m or f'
validates_format_of :year, :with => /^\d{4}$/, :allow_blank => true, :message => 'must select a year'
###########################
# callbacks
###########################
before_save :encrypt_password, :nullify_blanks, :check_for_new_unverified_email
###########################
# public methods
###########################
# Updates attributes while also marking that the email address has been
# verified.
def verify_email_and_update_password_attributes(attributes)
self.verify(false) # the false flag tells SM not to save
self.remembers_password(false) # the false flag tells SM not to save
self.update_attributes(attributes.delete_if {|key, val| key.to_sym != :password && key.to_sym != :password_confirmation})
end
# Authenticates a user by their login name and unencrypted password. Returns
# the user or nil. Also calls remembers_password in CASE 1 of
# forgot_password?. This is the main (non-fatal) flaw of the combined
# email-verification/forgotten password system.
def self.authenticate(login_or_email, password)
u = find :first, :conditions => ["#{login_or_email =~ /@/ ? 'email' : 'login'} = ?", login_or_email]
if u && u.authenticated?(password)
u.remembers_password
u
end
end
# Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
# Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
end
def authenticated?(password)
crypted_password == encrypt(password)
end
# A customized attribute writer that sets whether or not the user is
# changing the email address.
def email=(new_email)
unless (self.email == new_email) || new_email.blank?
@email_updated = true
write_attribute(:email, new_email)
end
end
###########################
# protected methods
###########################
protected
# Before filter that encrypts a plain text password into a crypted_password.
def encrypt_password
return if password.blank?
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
self.crypted_password = encrypt(password)
end
# Before filter that ensures "NULL" values instead of blanks in DB.
def nullify_blanks
self.login = nil if login.blank?
write_attribute(:email, nil) if email.blank?
end
# If there's an email address and it's different than the one that existed
# when the record was loaded we need to validate it again.
def check_for_new_unverified_email
unverify(false) if @email_updated && !self.unverified?
end
def make_email_verification_code
self.multipurpose_code = new_multipurpose_code
self.email_verified_at = nil
end
def make_forgot_password_code
self.multipurpose_code = new_multipurpose_code
end
def clear_forgot_password_code
self.multipurpose_code = nil if self.verified?
end
# Creates a random hash code that has oh so many uses.
def new_multipurpose_code
Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
end
# Called to set the attributes associated with an email verification.
def set_email_verified_attributes
self.email_verified_at = Time.now.utc
self.multipurpose_code = nil
end
# Some helper methods used in validation.
def present_email?
!email.blank?
end
def present_login?
!login.blank?
end
def blank_email?
email.blank?
end
def blank_login?
login.blank?
end
def password_required?
crypted_password.blank? || !password.blank?
end
end
class UserObserver < ActiveRecord::Observer
# def after_save(user)
# MailerWorker.asynch_mail_forgot_password_message(:user_id => user.id) if user.recently_forgot_password?
# MailerWorker.asynch_mail_email_verification_message(:user_id => user.id) if user.new_unverified_email?
# end
def after_unverify(user, from_state, to_state)
MailerWorker.asynch_mail_email_verification_message(:user_id => user.id)
end
def after_forgets_password(user, from_state, to_state)
MailerWorker.asynch_mail_forgot_password_message(:user_id => user.id)
end
end
class UsersController < ApplicationController
before_filter :login_required, :except => [:new, :create]
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
self.current_user = @user
flash[:notice] = "thank you for joining; please check your email for a confirmation"
redirect_to home_path
else
flash[:warning] = "please fix the errors below"
render :action => 'new'
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment