Grosse MàJ

This commit is contained in:
olivier
2008-11-25 22:11:16 +01:00
parent 53195fdfcd
commit 3e719157ea
2980 changed files with 343846 additions and 0 deletions

View File

@ -0,0 +1,29 @@
Restful Authentication Generator
====
This is a basic restful authentication generator for rails, taken from acts as authenticated. Currently it requires Rails 1.2 (or edge).
To use:
./script/generate authenticated user sessions --include-activation
The first parameter specifies the model that gets created in signup (typically a user or account model). A model with migration is created, as well as a basic controller with the create method.
The second parameter specifies the sessions controller name. This is the controller that handles the actual login/logout function on the site.
The third parameter (--include-activation) generates the code for a ActionMailer and its respective Activation Code through email.
You can pass --skip-migration to skip the user migration.
From here, you will need to add the resource routes in config/routes.rb.
map.resources :users
map.resource :session
If you're on rails 1.2.3 you may need to specify the controller name for the session singular resource:
map.resource :session, :controller => 'sessions'
Also, add an observer to config/environment.rb if you chose the --include-activation option
config.active_record.observers = :user_observer # or whatever you named your model

View File

@ -0,0 +1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the restful_authentication plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the restful_authentication plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'RestfulAuthentication'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View File

@ -0,0 +1 @@
./script/generate authenticated USERMODEL CONTROLLERNAME

View File

@ -0,0 +1,218 @@
class AuthenticatedGenerator < Rails::Generator::NamedBase
attr_reader :controller_name,
:controller_class_path,
:controller_file_path,
:controller_class_nesting,
:controller_class_nesting_depth,
:controller_class_name,
:controller_singular_name,
:controller_plural_name
alias_method :controller_file_name, :controller_singular_name
alias_method :controller_table_name, :controller_plural_name
attr_reader :model_controller_name,
:model_controller_class_path,
:model_controller_file_path,
:model_controller_class_nesting,
:model_controller_class_nesting_depth,
:model_controller_class_name,
:model_controller_singular_name,
:model_controller_plural_name
alias_method :model_controller_file_name, :model_controller_singular_name
alias_method :model_controller_table_name, :model_controller_plural_name
def initialize(runtime_args, runtime_options = {})
super
@controller_name = args.shift || 'sessions'
@model_controller_name = @name.pluralize
# sessions controller
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
@controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
if @controller_class_nesting.empty?
@controller_class_name = @controller_class_name_without_nesting
else
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
end
# model controller
base_name, @model_controller_class_path, @model_controller_file_path, @model_controller_class_nesting, @model_controller_class_nesting_depth = extract_modules(@model_controller_name)
@model_controller_class_name_without_nesting, @model_controller_singular_name, @model_controller_plural_name = inflect_names(base_name)
if @model_controller_class_nesting.empty?
@model_controller_class_name = @model_controller_class_name_without_nesting
else
@model_controller_class_name = "#{@model_controller_class_nesting}::#{@model_controller_class_name_without_nesting}"
end
end
def manifest
recorded_session = record do |m|
# Check for class naming collisions.
m.class_collisions controller_class_path, "#{controller_class_name}Controller", # Sessions Controller
"#{controller_class_name}Helper"
m.class_collisions model_controller_class_path, "#{model_controller_class_name}Controller", # Model Controller
"#{model_controller_class_name}Helper"
m.class_collisions class_path, "#{class_name}", "#{class_name}Mailer", "#{class_name}MailerTest", "#{class_name}Observer"
m.class_collisions [], 'AuthenticatedSystem', 'AuthenticatedTestHelper'
# Controller, helper, views, and test directories.
m.directory File.join('app/models', class_path)
m.directory File.join('app/controllers', controller_class_path)
m.directory File.join('app/controllers', model_controller_class_path)
m.directory File.join('app/helpers', controller_class_path)
m.directory File.join('app/views', controller_class_path, controller_file_name)
m.directory File.join('app/views', class_path, "#{file_name}_mailer") if options[:include_activation]
m.directory File.join('test/functional', controller_class_path)
m.directory File.join('app/controllers', model_controller_class_path)
m.directory File.join('app/helpers', model_controller_class_path)
m.directory File.join('app/views', model_controller_class_path, model_controller_file_name)
m.directory File.join('test/functional', model_controller_class_path)
m.directory File.join('test/unit', class_path)
m.template 'model.rb',
File.join('app/models',
class_path,
"#{file_name}.rb")
if options[:include_activation]
%w( mailer observer ).each do |model_type|
m.template "#{model_type}.rb", File.join('app/models',
class_path,
"#{file_name}_#{model_type}.rb")
end
end
m.template 'controller.rb',
File.join('app/controllers',
controller_class_path,
"#{controller_file_name}_controller.rb")
m.template 'model_controller.rb',
File.join('app/controllers',
model_controller_class_path,
"#{model_controller_file_name}_controller.rb")
m.template 'authenticated_system.rb',
File.join('lib', 'authenticated_system.rb')
m.template 'authenticated_test_helper.rb',
File.join('lib', 'authenticated_test_helper.rb')
m.template 'functional_test.rb',
File.join('test/functional',
controller_class_path,
"#{controller_file_name}_controller_test.rb")
m.template 'model_functional_test.rb',
File.join('test/functional',
model_controller_class_path,
"#{model_controller_file_name}_controller_test.rb")
m.template 'helper.rb',
File.join('app/helpers',
controller_class_path,
"#{controller_file_name}_helper.rb")
m.template 'model_helper.rb',
File.join('app/helpers',
model_controller_class_path,
"#{model_controller_file_name}_helper.rb")
m.template 'unit_test.rb',
File.join('test/unit',
class_path,
"#{file_name}_test.rb")
if options[:include_activation]
m.template 'mailer_test.rb', File.join('test/unit', class_path, "#{file_name}_mailer_test.rb")
end
m.template 'fixtures.yml',
File.join('test/fixtures',
"#{table_name}.yml")
# Controller templates
m.template 'login.rhtml', File.join('app/views', controller_class_path, controller_file_name, "new.rhtml")
m.template 'signup.rhtml', File.join('app/views', model_controller_class_path, model_controller_file_name, "new.rhtml")
if options[:include_activation]
# Mailer templates
%w( activation signup_notification ).each do |action|
m.template "#{action}.rhtml",
File.join('app/views', "#{file_name}_mailer", "#{action}.rhtml")
end
end
unless options[:skip_migration]
m.migration_template 'migration.rb', 'db/migrate', :assigns => {
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
}, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
end
end
action = nil
action = $0.split("/")[1]
case action
when "generate"
puts
puts ("-" * 70)
puts "Don't forget to:"
puts
puts " - add restful routes in config/routes.rb"
puts " map.resources :#{model_controller_file_name}"
puts " map.resource :#{controller_singular_name.singularize}"
puts
puts " Rails 1.2.3 may need a :controller option for the singular resource:"
puts " - map.resource :#{controller_singular_name.singularize}, :controller => '#{controller_file_name}'"
puts
if options[:include_activation]
puts " map.activate '/activate/:activation_code', :controller => '#{model_controller_file_name}', :action => 'activate'"
puts
puts " - add an observer to config/environment.rb"
puts " config.active_record.observers = :#{file_name}_observer"
end
puts
puts "Try these for some familiar login URLs if you like:"
puts
puts " map.signup '/signup', :controller => '#{model_controller_file_name}', :action => 'new'"
puts " map.login '/login', :controller => '#{controller_file_name}', :action => 'new'"
puts " map.logout '/logout', :controller => '#{controller_file_name}', :action => 'destroy'"
puts
puts ("-" * 70)
puts
when "destroy"
puts
puts ("-" * 70)
puts
puts "Thanks for using restful_authentication"
puts
puts "Don't forget to comment out the observer line in environment.rb"
puts " (This was optional so it may not even be there)"
puts " # config.active_record.observers = :#{file_name}_observer"
puts
puts ("-" * 70)
puts
else
puts
end
recorded_session
end
protected
# Override with your own usage banner.
def banner
"Usage: #{$0} authenticated ModelName [ControllerName]"
end
def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("--skip-migration",
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
opt.on("--include-activation",
"Generate signup 'activation code' confirmation via email") { |v| options[:include_activation] = v }
end
end

View File

@ -0,0 +1,3 @@
<%%= @<%= file_name %>.login %>, your account has been activated. You may now start adding your plugins:
<%%= @url %>

View File

@ -0,0 +1,127 @@
module AuthenticatedSystem
protected
# Returns true or false if the user is logged in.
# Preloads @current_<%= file_name %> with the user model if they're logged in.
def logged_in?
current_<%= file_name %> != :false
end
# Accesses the current <%= file_name %> from the session. Set it to :false if login fails
# so that future calls do not hit the database.
def current_<%= file_name %>
@current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
end
# Store the given <%= file_name %> in the session.
def current_<%= file_name %>=(new_<%= file_name %>)
session[:<%= file_name %>] = (new_<%= file_name %>.nil? || new_<%= file_name %>.is_a?(Symbol)) ? nil : new_<%= file_name %>.id
@current_<%= file_name %> = new_<%= file_name %>
end
# Check if the <%= file_name %> is authorized
#
# Override this method in your controllers if you want to restrict access
# to only a few actions or if you want to check if the <%= file_name %>
# has the correct rights.
#
# Example:
#
# # only allow nonbobs
# def authorized?
# current_<%= file_name %>.login != "bob"
# end
def authorized?
logged_in?
end
# Filter method to enforce a login requirement.
#
# To require logins for all actions, use this in your controllers:
#
# before_filter :login_required
#
# To require logins for specific actions, use this in your controllers:
#
# before_filter :login_required, :only => [ :edit, :update ]
#
# To skip this in a subclassed controller:
#
# skip_before_filter :login_required
#
def login_required
authorized? || access_denied
end
# Redirect as appropriate when an access request fails.
#
# The default action is to redirect to the login screen.
#
# Override this method in your controllers if you want to have special
# behavior in case the <%= file_name %> is not authorized
# to access the requested action. For example, a popup window might
# simply close itself.
def access_denied
respond_to do |accepts|
accepts.html do
store_location
redirect_to :controller => '/<%= controller_file_name %>', :action => 'new'
end
accepts.xml do
headers["Status"] = "Unauthorized"
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
render :text => "Could't authenticate you", :status => '401 Unauthorized'
end
end
false
end
# Store the URI of the current request in the session.
#
# We can return to this location by calling #redirect_back_or_default.
def store_location
session[:return_to] = request.request_uri
end
# Redirect to the URI stored by the most recent store_location call or
# to the passed default.
def redirect_back_or_default(default)
session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default)
session[:return_to] = nil
end
# Inclusion hook to make #current_<%= file_name %> and #logged_in?
# available as ActionView helper methods.
def self.included(base)
base.send :helper_method, :current_<%= file_name %>, :logged_in?
end
# Called from #current_user. First attempt to login by the user id stored in the session.
def login_from_session
self.current_<%= file_name %> = <%= class_name %>.find_by_id(session[:<%= file_name %>]) if session[:<%= file_name %>]
end
# Called from #current_user. Now, attempt to login by basic authentication information.
def login_from_basic_auth
username, passwd = get_auth_data
self.current_<%= file_name %> = <%= class_name %>.authenticate(username, passwd) if username && passwd
end
# Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
def login_from_cookie
<%= file_name %> = cookies[:auth_token] && <%= class_name %>.find_by_remember_token(cookies[:auth_token])
if <%= file_name %> && <%= file_name %>.remember_token?
<%= file_name %>.remember_me
cookies[:auth_token] = { :value => <%= file_name %>.remember_token, :expires => <%= file_name %>.remember_token_expires_at }
self.current_<%= file_name %> = <%= file_name %>
end
end
private
@@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
# gets BASIC auth info
def get_auth_data
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
end
end

View File

@ -0,0 +1,26 @@
module AuthenticatedTestHelper
# Sets the current <%= file_name %> in the session from the <%= file_name %> fixtures.
def login_as(<%= file_name %>)
@request.session[:<%= file_name %>] = <%= file_name %> ? <%= table_name %>(<%= file_name %>).id : nil
end
def authorize_as(user)
@request.env["HTTP_AUTHORIZATION"] = user ? "Basic #{Base64.encode64("#{users(user).login}:test")}" : nil
end
# taken from edge rails / rails 2.0. Only needed on Rails 1.2.3
def assert_difference(expressions, difference = 1, message = nil, &block)
expression_evaluations = [expressions].flatten.collect{|expression| lambda { eval(expression, block.binding) } }
original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
yield
expression_evaluations.each_with_index do |expression, i|
assert_equal original_values[i] + difference, expression.call, message
end
end
# taken from edge rails / rails 2.0. Only needed on Rails 1.2.3
def assert_no_difference(expressions, message = nil, &block)
assert_difference expressions, 0, message, &block
end
end

View File

@ -0,0 +1,31 @@
# This controller handles the login/logout function of the site.
class <%= controller_class_name %>Controller < ApplicationController
# Be sure to include AuthenticationSystem in Application Controller instead
include AuthenticatedSystem
# render new.rhtml
def new
end
def create
self.current_<%= file_name %> = <%= class_name %>.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me] == "1"
self.current_<%= file_name %>.remember_me
cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
end
redirect_back_or_default('/')
flash[:notice] = "Logged in successfully"
else
render :action => 'new'
end
end
def destroy
self.current_<%= file_name %>.forget_me if logged_in?
cookies.delete :auth_token
reset_session
flash[:notice] = "You have been logged out."
redirect_back_or_default('/')
end
end

View File

@ -0,0 +1,17 @@
quentin:
id: 1
login: quentin
email: quentin@example.com
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
created_at: <%%= 5.days.ago.to_s :db %>
<% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b <% end %>
<% if options[:include_activation] %> activated_at: <%%= 5.days.ago.to_s :db %> <% end %>
aaron:
id: 2
login: aaron
email: aaron@example.com
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
created_at: <%%= 1.days.ago.to_s :db %>
<% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9a <% end %>

View File

@ -0,0 +1,85 @@
require File.dirname(__FILE__) + '/../test_helper'
require '<%= controller_file_name %>_controller'
# Re-raise errors caught by the controller.
class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
# Then, you can remove it from this and the units test.
include AuthenticatedTestHelper
fixtures :<%= table_name %>
def setup
@controller = <%= controller_class_name %>Controller.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
def test_should_login_and_redirect
post :create, :login => 'quentin', :password => 'test'
assert session[:<%= file_name %>]
assert_response :redirect
end
def test_should_fail_login_and_not_redirect
post :create, :login => 'quentin', :password => 'bad password'
assert_nil session[:<%= file_name %>]
assert_response :success
end
def test_should_logout
login_as :quentin
get :destroy
assert_nil session[:<%= file_name %>]
assert_response :redirect
end
def test_should_remember_me
post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
assert_not_nil @response.cookies["auth_token"]
end
def test_should_not_remember_me
post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
assert_nil @response.cookies["auth_token"]
end
def test_should_delete_token_on_logout
login_as :quentin
get :destroy
assert_equal @response.cookies["auth_token"], []
end
def test_should_login_with_cookie
<%= table_name %>(:quentin).remember_me
@request.cookies["auth_token"] = cookie_for(:quentin)
get :new
assert @controller.send(:logged_in?)
end
def test_should_fail_expired_cookie_login
<%= table_name %>(:quentin).remember_me
<%= table_name %>(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
@request.cookies["auth_token"] = cookie_for(:quentin)
get :new
assert !@controller.send(:logged_in?)
end
def test_should_fail_cookie_login
<%= table_name %>(:quentin).remember_me
@request.cookies["auth_token"] = auth_token('invalid_auth_token')
get :new
assert !@controller.send(:logged_in?)
end
protected
def auth_token(token)
CGI::Cookie.new('name' => 'auth_token', 'value' => token)
end
def cookie_for(<%= file_name %>)
auth_token <%= table_name %>(<%= file_name %>).remember_token
end
end

View File

@ -0,0 +1,2 @@
module <%= controller_class_name %>Helper
end

View File

@ -0,0 +1,14 @@
<%% form_tag <%= controller_singular_name.singularize %>_path do -%>
<p><label for="login">Login</label><br/>
<%%= text_field_tag 'login' %></p>
<p><label for="password">Password</label><br/>
<%%= password_field_tag 'password' %></p>
<!-- Uncomment this if you want this functionality
<p><label for="remember_me">Remember me:</label>
<%%= check_box_tag 'remember_me' %></p>
-->
<p><%%= submit_tag 'Log in' %></p>
<%% end -%>

View File

@ -0,0 +1,25 @@
class <%= class_name %>Mailer < ActionMailer::Base
def signup_notification(<%= file_name %>)
setup_email(<%= file_name %>)
@subject += 'Please activate your new account'
<% if options[:include_activation] %>
@body[:url] = "http://YOURSITE/activate/#{<%= file_name %>.activation_code}"
<% else %>
@body[:url] = "http://YOURSITE/login/" <% end %>
end
def activation(<%= file_name %>)
setup_email(<%= file_name %>)
@subject += 'Your account has been activated!'
@body[:url] = "http://YOURSITE/"
end
protected
def setup_email(<%= file_name %>)
@recipients = "#{<%= file_name %>.email}"
@from = "ADMINEMAIL"
@subject = "[YOURSITE] "
@sent_on = Time.now
@body[:<%= file_name %>] = <%= file_name %>
end
end

View File

@ -0,0 +1,31 @@
require File.dirname(__FILE__) + '/../test_helper'
require '<%= file_name %>_mailer'
class <%= class_name %>MailerTest < Test::Unit::TestCase
FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
CHARSET = "utf-8"
include ActionMailer::Quoting
def setup
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
@expected = TMail::Mail.new
@expected.set_content_type "text", "plain", { "charset" => CHARSET }
end
def test_dummy_test
#do nothing
end
private
def read_fixture(action)
IO.readlines("#{FIXTURES_PATH}/<%= file_name %>_mailer/#{action}")
end
def encode(subject)
quoted_printable(subject, CHARSET)
end
end

View File

@ -0,0 +1,21 @@
class <%= migration_name %> < ActiveRecord::Migration
def self.up
create_table "<%= table_name %>", :force => true do |t|
t.column :login, :string
t.column :email, :string
t.column :crypted_password, :string, :limit => 40
t.column :salt, :string, :limit => 40
t.column :created_at, :datetime
t.column :updated_at, :datetime
t.column :remember_token, :string
t.column :remember_token_expires_at, :datetime
<% if options[:include_activation] %>
t.column :activation_code, :string, :limit => 40
t.column :activated_at, :datetime<% end %>
end
end
def self.down
drop_table "<%= table_name %>"
end
end

View File

@ -0,0 +1,98 @@
require 'digest/sha1'
class <%= class_name %> < ActiveRecord::Base
# Virtual attribute for the unencrypted password
attr_accessor :password
validates_presence_of :login, :email
validates_presence_of :password, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
validates_length_of :password, :within => 4..40, :if => :password_required?
validates_confirmation_of :password, :if => :password_required?
validates_length_of :login, :within => 3..40
validates_length_of :email, :within => 3..100
validates_uniqueness_of :login, :email, :case_sensitive => false
before_save :encrypt_password
<% if options[:include_activation] %>before_create :make_activation_code <% end %>
# prevents a user from submitting a crafted form that bypasses activation
# anything else you want your user to change should be added here.
attr_accessible :login, :email, :password, :password_confirmation
<% if options[:include_activation] %>
# Activates the user in the database.
def activate
@activated = true
self.activated_at = Time.now.utc
self.activation_code = nil
save(false)
end
def activated?
# the existence of an activation code means they have not activated yet
activation_code.nil?
end
# Returns true if the user has just been activated.
def recently_activated?
@activated
end
<% end %>
# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
def self.authenticate(login, password)
u = <% if options[:include_activation] %>find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login]<% else %>find_by_login(login)<% end %> # need to get the salt
u && u.authenticated?(password) ? u : nil
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
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
remember_me_for 2.weeks
end
def remember_me_for(time)
remember_me_until time.from_now.utc
end
def remember_me_until(time)
self.remember_token_expires_at = time
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
save(false)
end
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
protected
# before filter
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
def password_required?
crypted_password.blank? || !password.blank?
end
<% if options[:include_activation] %>
def make_activation_code
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
end <% end %>
end

View File

@ -0,0 +1,30 @@
class <%= model_controller_class_name %>Controller < ApplicationController
# Be sure to include AuthenticationSystem in Application Controller instead
include AuthenticatedSystem
# render new.rhtml
def new
end
def create
cookies.delete :auth_token
reset_session
@<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
@<%= file_name %>.save!
self.current_<%= file_name %> = @<%= file_name %>
redirect_back_or_default('/')
flash[:notice] = "Thanks for signing up!"
rescue ActiveRecord::RecordInvalid
render :action => 'new'
end
<% if options[:include_activation] %>
def activate
self.current_<%= file_name %> = params[:activation_code].blank? ? :false : <%= class_name %>.find_by_activation_code(params[:activation_code])
if logged_in? && !current_<%= file_name %>.activated?
current_<%= file_name %>.activate
flash[:notice] = "Signup complete!"
end
redirect_back_or_default('/')
end
<% end %>
end

View File

@ -0,0 +1,86 @@
require File.dirname(__FILE__) + '/../test_helper'
require '<%= model_controller_file_name %>_controller'
# Re-raise errors caught by the controller.
class <%= model_controller_class_name %>Controller; def rescue_action(e) raise e end; end
class <%= model_controller_class_name %>ControllerTest < Test::Unit::TestCase
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
# Then, you can remove it from this and the units test.
include AuthenticatedTestHelper
fixtures :<%= table_name %>
def setup
@controller = <%= model_controller_class_name %>Controller.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
def test_should_allow_signup
assert_difference '<%= class_name %>.count' do
create_<%= file_name %>
assert_response :redirect
end
end
def test_should_require_login_on_signup
assert_no_difference '<%= class_name %>.count' do
create_<%= file_name %>(:login => nil)
assert assigns(:<%= file_name %>).errors.on(:login)
assert_response :success
end
end
def test_should_require_password_on_signup
assert_no_difference '<%= class_name %>.count' do
create_<%= file_name %>(:password => nil)
assert assigns(:<%= file_name %>).errors.on(:password)
assert_response :success
end
end
def test_should_require_password_confirmation_on_signup
assert_no_difference '<%= class_name %>.count' do
create_<%= file_name %>(:password_confirmation => nil)
assert assigns(:<%= file_name %>).errors.on(:password_confirmation)
assert_response :success
end
end
def test_should_require_email_on_signup
assert_no_difference '<%= class_name %>.count' do
create_<%= file_name %>(:email => nil)
assert assigns(:<%= file_name %>).errors.on(:email)
assert_response :success
end
end
<% if options[:include_activation] %>
def test_should_activate_user
assert_nil <%= class_name %>.authenticate('aaron', 'test')
get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
assert_redirected_to '/'
assert_not_nil flash[:notice]
assert_equal <%= table_name %>(:aaron), <%= class_name %>.authenticate('aaron', 'test')
end
def test_should_not_activate_user_without_key
get :activate
assert_nil flash[:notice]
rescue ActionController::RoutingError
# in the event your routes deny this, we'll just bow out gracefully.
end
def test_should_not_activate_user_with_blank_key
get :activate, :activation_code => ''
assert_nil flash[:notice]
rescue ActionController::RoutingError
# well played, sir
end<% end %>
protected
def create_<%= file_name %>(options = {})
post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire@example.com',
:password => 'quire', :password_confirmation => 'quire' }.merge(options)
end
end

View File

@ -0,0 +1,2 @@
module <%= model_controller_class_name %>Helper
end

View File

@ -0,0 +1,11 @@
class <%= class_name %>Observer < ActiveRecord::Observer
def after_create(<%= file_name %>)
<%= class_name %>Mailer.deliver_signup_notification(<%= file_name %>)
end
def after_save(<%= file_name %>)
<% if options[:include_activation] %>
<%= class_name %>Mailer.deliver_activation(<%= file_name %>) if <%= file_name %>.recently_activated?
<% end %>
end
end

View File

@ -0,0 +1,16 @@
<%%= error_messages_for :<%= file_name %> %>
<%% form_for :<%= file_name %>, :url => <%= table_name %>_path do |f| -%>
<p><label for="login">Login</label><br/>
<%%= f.text_field :login %></p>
<p><label for="email">Email</label><br/>
<%%= f.text_field :email %></p>
<p><label for="password">Password</label><br/>
<%%= f.password_field :password %></p>
<p><label for="password_confirmation">Confirm Password</label><br/>
<%%= f.password_field :password_confirmation %></p>
<p><%%= submit_tag 'Sign up' %></p>
<%% end -%>

View File

@ -0,0 +1,8 @@
Your account has been created.
Username: <%%= @<%= file_name %>.login %>
Password: <%%= @<%= file_name %>.password %>
Visit this url to activate your account:
<%%= @url %>

View File

@ -0,0 +1,101 @@
require File.dirname(__FILE__) + '/../test_helper'
class <%= class_name %>Test < Test::Unit::TestCase
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead.
# Then, you can remove it from this and the functional test.
include AuthenticatedTestHelper
fixtures :<%= table_name %>
def test_should_create_<%= file_name %>
assert_difference '<%= class_name %>.count' do
<%= file_name %> = create_<%= file_name %>
assert !<%= file_name %>.new_record?, "#{<%= file_name %>.errors.full_messages.to_sentence}"
end
end
def test_should_require_login
assert_no_difference '<%= class_name %>.count' do
u = create_<%= file_name %>(:login => nil)
assert u.errors.on(:login)
end
end
def test_should_require_password
assert_no_difference '<%= class_name %>.count' do
u = create_<%= file_name %>(:password => nil)
assert u.errors.on(:password)
end
end
def test_should_require_password_confirmation
assert_no_difference '<%= class_name %>.count' do
u = create_<%= file_name %>(:password_confirmation => nil)
assert u.errors.on(:password_confirmation)
end
end
def test_should_require_email
assert_no_difference '<%= class_name %>.count' do
u = create_<%= file_name %>(:email => nil)
assert u.errors.on(:email)
end
end
def test_should_reset_password
<%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'new password')
end
def test_should_not_rehash_password
<%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin2', 'test')
end
def test_should_authenticate_<%= file_name %>
assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
end
def test_should_set_remember_token
<%= table_name %>(:quentin).remember_me
assert_not_nil <%= table_name %>(:quentin).remember_token
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
end
def test_should_unset_remember_token
<%= table_name %>(:quentin).remember_me
assert_not_nil <%= table_name %>(:quentin).remember_token
<%= table_name %>(:quentin).forget_me
assert_nil <%= table_name %>(:quentin).remember_token
end
def test_should_remember_me_for_one_week
before = 1.week.from_now.utc
<%= table_name %>(:quentin).remember_me_for 1.week
after = 1.week.from_now.utc
assert_not_nil <%= table_name %>(:quentin).remember_token
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
end
def test_should_remember_me_until_one_week
time = 1.week.from_now.utc
<%= table_name %>(:quentin).remember_me_until time
assert_not_nil <%= table_name %>(:quentin).remember_token
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
assert_equal <%= table_name %>(:quentin).remember_token_expires_at, time
end
def test_should_remember_me_default_two_weeks
before = 2.weeks.from_now.utc
<%= table_name %>(:quentin).remember_me
after = 2.weeks.from_now.utc
assert_not_nil <%= table_name %>(:quentin).remember_token
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
end
protected
def create_<%= file_name %>(options = {})
<%= class_name %>.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
end
end

View File

@ -0,0 +1 @@
puts IO.read(File.join(File.dirname(__FILE__), 'README'))