Commit aacd378904604b60850dd9245c52331ea2d629aa

  • Tree SHA1: 6f6dc44
  • Parent SHA1: ade6642 (authentication, I think it is best to remove restful authentication plugin from vendor/plugins, but it will be there for a while)
  • raw diff | raw patch
 removed generator plugin, will not be necessary later
  
11# == Schema Information
2# Schema version: 7
2# Schema version: 10
33#
44# Table name: movements
55#
  
11# == Schema Information
2# Schema version: 7
2# Schema version: 10
33#
44# Table name: realizations
55#
  
11# == Schema Information
2# Schema version: 7
2# Schema version: 10
33#
44# Table name: scheduled_debts
55#
66# id :integer not null, primary key
77# description :string(255) not null
8# scheduled_day :integer not null
9# scheduled_month :integer not null
10# confirmed_date :date not null
8# day :integer not null
9# month :integer not null
1110# scheduled_value :decimal(, ) not null
12# confirmed_value :decimal(, )
1311# comments :text
12# start_date :date
13# end_date :date
1414#
1515
1616class ScheduledDebt < ActiveRecord::Base
  
11# == Schema Information
2# Schema version: 7
2# Schema version: 10
33#
44# Table name: scheduled_profits
55#
66# id :integer not null, primary key
77# source :string(255) not null
88# scheduled_value :decimal(, ) not null
9# confirmed_value :decimal(, )
10# auto :boolean not null
119# month :integer not null
1210# day :integer default(10), not null
11# start_date :date
12# end_date :date
1313#
1414
1515class ScheduledProfit < ActiveRecord::Base
  
1# == Schema Information
2# Schema version: 10
3#
4# Table name: users
5#
6# id :integer not null, primary key
7# login :string(255)
8# email :string(255)
9# crypted_password :string(40)
10# salt :string(40)
11# created_at :datetime
12# updated_at :datetime
13# remember_token :string(255)
14# remember_token_expires_at :datetime
15#
16
117require 'digest/sha1'
218class User < ActiveRecord::Base
319 # Virtual attribute for the unencrypted password
  
11# == Schema Information
2# Schema version: 7
2# Schema version: 10
33#
44# Table name: movements
55#
  
1# == Schema Information
2# Schema version: 10
3#
4# Table name: users
5#
6# id :integer not null, primary key
7# login :string(255)
8# email :string(255)
9# crypted_password :string(40)
10# salt :string(40)
11# created_at :datetime
12# updated_at :datetime
13# remember_token :string(255)
14# remember_token_expires_at :datetime
15#
16
117quentin:
218 id: 1
319 login: quentin
  
1Restful Authentication Generator
2====
3
4This is a basic restful authentication generator for rails, taken
5from acts as authenticated. Currently it requires Rails 1.2.6 or above.
6
7To use:
8
9 ./script/generate authenticated user sessions \
10 --include-activation \
11 --stateful
12
13The first parameter specifies the model that gets created in signup
14(typically a user or account model). A model with migration is
15created, as well as a basic controller with the create method.
16
17The second parameter specifies the sessions controller name. This is
18the controller that handles the actual login/logout function on the
19site.
20
21The third parameter (--include-activation) generates the code for a
22ActionMailer and its respective Activation Code through email.
23
24The fourth (--stateful) builds in support for acts_as_state_machine
25and generates activation code. This was taken from:
26
27http://www.vaporbase.com/postings/stateful_authentication
28
29You can pass --skip-migration to skip the user migration.
30
31If you're using acts_as_state_machine, define your users resource like this:
32
33 map.resources :users, :member => { :suspend => :put,
34 :unsuspend => :put,
35 :purge => :delete }
36
37Also, add an observer to config/environment.rb if you chose the
38--include-activation option
39
40 config.active_record.observers = :user_observer # or whatever you
41 # named your model
42
43Security Alert
44====
45
46I introduced a change to the model controller that's been tripping
47folks up on Rails 2.0. The change was added as a suggestion to help
48combat session fixation attacks. However, this resets the Form
49Authentication token used by Request Forgery Protection. I've left
50it out now, since Rails 1.2.6 and Rails 2.0 will both stop session
51fixation attacks anyway.
  
1require 'rake'
2require 'rake/testtask'
3require 'rake/rdoctask'
4
5desc 'Default: run unit tests.'
6task :default => :test
7
8desc 'Test the restful_authentication plugin.'
9Rake::TestTask.new(:test) do |t|
10 t.libs << 'lib'
11 t.pattern = 'test/**/*_test.rb'
12 t.verbose = true
13end
14
15desc 'Generate documentation for the restful_authentication plugin.'
16Rake::RDocTask.new(:rdoc) do |rdoc|
17 rdoc.rdoc_dir = 'rdoc'
18 rdoc.title = 'RestfulAuthentication'
19 rdoc.options << '--line-numbers' << '--inline-source'
20 rdoc.rdoc_files.include('README')
21 rdoc.rdoc_files.include('lib/**/*.rb')
22end
  
1./script/generate authenticated USERMODEL CONTROLLERNAME
  
1require 'restful_authentication/rails_commands'
2class AuthenticatedGenerator < Rails::Generator::NamedBase
3 default_options :skip_migration => false,
4 :include_activation => false
5
6 attr_reader :controller_name,
7 :controller_class_path,
8 :controller_file_path,
9 :controller_class_nesting,
10 :controller_class_nesting_depth,
11 :controller_class_name,
12 :controller_singular_name,
13 :controller_plural_name,
14 :controller_file_name
15 alias_method :controller_table_name, :controller_plural_name
16 attr_reader :model_controller_name,
17 :model_controller_class_path,
18 :model_controller_file_path,
19 :model_controller_class_nesting,
20 :model_controller_class_nesting_depth,
21 :model_controller_class_name,
22 :model_controller_singular_name,
23 :model_controller_plural_name
24 alias_method :model_controller_file_name, :model_controller_singular_name
25 alias_method :model_controller_table_name, :model_controller_plural_name
26
27 def initialize(runtime_args, runtime_options = {})
28 super
29
30 @rspec = has_rspec?
31
32 @controller_name = args.shift || 'sessions'
33 @model_controller_name = @name.pluralize
34
35 # sessions controller
36 base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
37 @controller_class_name_without_nesting, @controller_file_name, @controller_plural_name = inflect_names(base_name)
38 @controller_singular_name = @controller_file_name.singularize
39
40 if @controller_class_nesting.empty?
41 @controller_class_name = @controller_class_name_without_nesting
42 else
43 @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
44 end
45
46 # model controller
47 base_name, @model_controller_class_path, @model_controller_file_path, @model_controller_class_nesting, @model_controller_class_nesting_depth = extract_modules(@model_controller_name)
48 @model_controller_class_name_without_nesting, @model_controller_singular_name, @model_controller_plural_name = inflect_names(base_name)
49
50 if @model_controller_class_nesting.empty?
51 @model_controller_class_name = @model_controller_class_name_without_nesting
52 else
53 @model_controller_class_name = "#{@model_controller_class_nesting}::#{@model_controller_class_name_without_nesting}"
54 end
55 end
56
57 def manifest
58 recorded_session = record do |m|
59 # Check for class naming collisions.
60 m.class_collisions controller_class_path, "#{controller_class_name}Controller", # Sessions Controller
61 "#{controller_class_name}Helper"
62 m.class_collisions model_controller_class_path, "#{model_controller_class_name}Controller", # Model Controller
63 "#{model_controller_class_name}Helper"
64 m.class_collisions class_path, "#{class_name}", "#{class_name}Mailer", "#{class_name}MailerTest", "#{class_name}Observer"
65 m.class_collisions [], 'AuthenticatedSystem', 'AuthenticatedTestHelper'
66
67 # Controller, helper, views, and test directories.
68 m.directory File.join('app/models', class_path)
69 m.directory File.join('app/controllers', controller_class_path)
70 m.directory File.join('app/controllers', model_controller_class_path)
71 m.directory File.join('app/helpers', controller_class_path)
72 m.directory File.join('app/views', controller_class_path, controller_file_name)
73 m.directory File.join('app/views', class_path, "#{file_name}_mailer") if options[:include_activation]
74
75 m.directory File.join('app/controllers', model_controller_class_path)
76 m.directory File.join('app/helpers', model_controller_class_path)
77 m.directory File.join('app/views', model_controller_class_path, model_controller_file_name)
78
79 if @rspec
80 m.directory File.join('spec/controllers', controller_class_path)
81 m.directory File.join('spec/controllers', model_controller_class_path)
82 m.directory File.join('spec/models', class_path)
83 m.directory File.join('spec/fixtures', class_path)
84 else
85 m.directory File.join('test/functional', controller_class_path)
86 m.directory File.join('test/functional', model_controller_class_path)
87 m.directory File.join('test/unit', class_path)
88 end
89
90 m.template 'model.rb',
91 File.join('app/models',
92 class_path,
93 "#{file_name}.rb")
94
95 if options[:include_activation]
96 %w( mailer observer ).each do |model_type|
97 m.template "#{model_type}.rb", File.join('app/models',
98 class_path,
99 "#{file_name}_#{model_type}.rb")
100 end
101 end
102
103 m.template 'controller.rb',
104 File.join('app/controllers',
105 controller_class_path,
106 "#{controller_file_name}_controller.rb")
107
108 m.template 'model_controller.rb',
109 File.join('app/controllers',
110 model_controller_class_path,
111 "#{model_controller_file_name}_controller.rb")
112
113 m.template 'authenticated_system.rb',
114 File.join('lib', 'authenticated_system.rb')
115
116 m.template 'authenticated_test_helper.rb',
117 File.join('lib', 'authenticated_test_helper.rb')
118
119 if @rspec
120 m.template 'functional_spec.rb',
121 File.join('spec/controllers',
122 controller_class_path,
123 "#{controller_file_name}_controller_spec.rb")
124 m.template 'model_functional_spec.rb',
125 File.join('spec/controllers',
126 model_controller_class_path,
127 "#{model_controller_file_name}_controller_spec.rb")
128 m.template 'unit_spec.rb',
129 File.join('spec/models',
130 class_path,
131 "#{file_name}_spec.rb")
132 m.template 'fixtures.yml',
133 File.join('spec/fixtures',
134 "#{table_name}.yml")
135 else
136 m.template 'functional_test.rb',
137 File.join('test/functional',
138 controller_class_path,
139 "#{controller_file_name}_controller_test.rb")
140 m.template 'model_functional_test.rb',
141 File.join('test/functional',
142 model_controller_class_path,
143 "#{model_controller_file_name}_controller_test.rb")
144 m.template 'unit_test.rb',
145 File.join('test/unit',
146 class_path,
147 "#{file_name}_test.rb")
148 if options[:include_activation]
149 m.template 'mailer_test.rb', File.join('test/unit', class_path, "#{file_name}_mailer_test.rb")
150 end
151 m.template 'fixtures.yml',
152 File.join('test/fixtures',
153 "#{table_name}.yml")
154 end
155
156 m.template 'helper.rb',
157 File.join('app/helpers',
158 controller_class_path,
159 "#{controller_file_name}_helper.rb")
160
161 m.template 'model_helper.rb',
162 File.join('app/helpers',
163 model_controller_class_path,
164 "#{model_controller_file_name}_helper.rb")
165
166
167 # Controller templates
168 m.template 'login.html.erb', File.join('app/views', controller_class_path, controller_file_name, "new.html.erb")
169 m.template 'signup.html.erb', File.join('app/views', model_controller_class_path, model_controller_file_name, "new.html.erb")
170
171 if options[:include_activation]
172 # Mailer templates
173 %w( activation signup_notification ).each do |action|
174 m.template "#{action}.html.erb",
175 File.join('app/views', "#{file_name}_mailer", "#{action}.html.erb")
176 end
177 end
178
179 unless options[:skip_migration]
180 m.migration_template 'migration.rb', 'db/migrate', :assigns => {
181 :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
182 }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
183 end
184
185 m.route_resource controller_singular_name
186 m.route_resources model_controller_plural_name
187 end
188
189 action = nil
190 action = $0.split("/")[1]
191 case action
192 when "generate"
193 puts
194 puts ("-" * 70)
195 puts "Don't forget to:"
196 puts
197 if options[:include_activation]
198 puts " map.activate '/activate/:activation_code', :controller => '#{model_controller_file_name}', :action => 'activate'"
199 puts
200 puts " - add an observer to config/environment.rb"
201 puts " config.active_record.observers = :#{file_name}_observer"
202 puts
203 end
204 if options[:stateful]
205 puts "Also, don't forget to install the acts_as_state_machine plugin and set your resource:"
206 puts
207 puts " svn co http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk vendor/plugins/acts_as_state_machine"
208 puts
209 puts %w(map.resources :#{model_controller_file_name}, :member => { :suspend => :put, :unsuspend => :put, :purge => :delete })
210 puts
211 end
212 puts "Try these for some familiar login URLs if you like:"
213 puts
214 puts %(map.activate '/activate/:activation_code', :controller => '#{model_controller_file_name}', :action => 'activate', :activation_code => nil)
215 puts %(map.signup '/signup', :controller => '#{model_controller_file_name}', :action => 'new')
216 puts %(map.login '/login', :controller => '#{controller_file_name}', :action => 'new')
217 puts %(map.logout '/logout', :controller => '#{controller_file_name}', :action => 'destroy')
218 puts
219 puts ("-" * 70)
220 puts
221 when "destroy"
222 puts
223 puts ("-" * 70)
224 puts
225 puts "Thanks for using restful_authentication"
226 puts
227 puts "Don't forget to comment out the observer line in environment.rb"
228 puts " (This was optional so it may not even be there)"
229 puts " # config.active_record.observers = :#{file_name}_observer"
230 puts
231 puts ("-" * 70)
232 puts
233 else
234 puts
235 end
236
237 recorded_session
238 end
239
240 def has_rspec?
241 options[:rspec] || (File.exist?('spec') && File.directory?('spec'))
242 end
243
244 protected
245 # Override with your own usage banner.
246 def banner
247 "Usage: #{$0} authenticated ModelName [ControllerName]"
248 end
249
250 def add_options!(opt)
251 opt.separator ''
252 opt.separator 'Options:'
253 opt.on("--skip-migration",
254 "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
255 opt.on("--include-activation",
256 "Generate signup 'activation code' confirmation via email") { |v| options[:include_activation] = true }
257 opt.on("--stateful",
258 "Use acts_as_state_machine. Assumes --include-activation") { |v| options[:include_activation] = options[:stateful] = true }
259 opt.on("--rspec",
260 "Force rspec mode (checks for RAILS_ROOT/spec by default)") { |v| options[:rspec] = true }
261 end
262end
  
1<%%= @<%= file_name %>.login %>, your account has been activated. You may now start adding your plugins:
2
3 <%%= @url %>
  
1module AuthenticatedSystem
2 protected
3 # Returns true or false if the <%= file_name %> is logged in.
4 # Preloads @current_<%= file_name %> with the <%= file_name %> model if they're logged in.
5 def logged_in?
6 current_<%= file_name %> != :false
7 end
8
9 # Accesses the current <%= file_name %> from the session. Set it to :false if login fails
10 # so that future calls do not hit the database.
11 def current_<%= file_name %>
12 @current_<%= file_name %> ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
13 end
14
15 # Store the given <%= file_name %> id in the session.
16 def current_<%= file_name %>=(new_<%= file_name %>)
17 session[:<%= file_name %>_id] = (new_<%= file_name %>.nil? || new_<%= file_name %>.is_a?(Symbol)) ? nil : new_<%= file_name %>.id
18 @current_<%= file_name %> = new_<%= file_name %> || :false
19 end
20
21 # Check if the <%= file_name %> is authorized
22 #
23 # Override this method in your controllers if you want to restrict access
24 # to only a few actions or if you want to check if the <%= file_name %>
25 # has the correct rights.
26 #
27 # Example:
28 #
29 # # only allow nonbobs
30 # def authorized?
31 # current_<%= file_name %>.login != "bob"
32 # end
33 def authorized?
34 logged_in?
35 end
36
37 # Filter method to enforce a login requirement.
38 #
39 # To require logins for all actions, use this in your controllers:
40 #
41 # before_filter :login_required
42 #
43 # To require logins for specific actions, use this in your controllers:
44 #
45 # before_filter :login_required, :only => [ :edit, :update ]
46 #
47 # To skip this in a subclassed controller:
48 #
49 # skip_before_filter :login_required
50 #
51 def login_required
52 authorized? || access_denied
53 end
54
55 # Redirect as appropriate when an access request fails.
56 #
57 # The default action is to redirect to the login screen.
58 #
59 # Override this method in your controllers if you want to have special
60 # behavior in case the <%= file_name %> is not authorized
61 # to access the requested action. For example, a popup window might
62 # simply close itself.
63 def access_denied
64 respond_to do |format|
65 format.html do
66 store_location
67 redirect_to new_<%= controller_singular_name %>_path
68 end
69 format.any do
70 request_http_basic_authentication 'Web Password'
71 end
72 end
73 end
74
75 # Store the URI of the current request in the session.
76 #
77 # We can return to this location by calling #redirect_back_or_default.
78 def store_location
79 session[:return_to] = request.request_uri
80 end
81
82 # Redirect to the URI stored by the most recent store_location call or
83 # to the passed default.
84 def redirect_back_or_default(default)
85 redirect_to(session[:return_to] || default)
86 session[:return_to] = nil
87 end
88
89 # Inclusion hook to make #current_<%= file_name %> and #logged_in?
90 # available as ActionView helper methods.
91 def self.included(base)
92 base.send :helper_method, :current_<%= file_name %>, :logged_in?
93 end
94
95 # Called from #current_<%= file_name %>. First attempt to login by the <%= file_name %> id stored in the session.
96 def login_from_session
97 self.current_<%= file_name %> = <%= class_name %>.find(session[:<%= file_name %>_id]) if session[:<%= file_name %>_id]
98 end
99
100 # Called from #current_<%= file_name %>. Now, attempt to login by basic authentication information.
101 def login_from_basic_auth
102 authenticate_with_http_basic do |username, password|
103 self.current_<%= file_name %> = <%= class_name %>.authenticate(username, password)
104 end
105 end
106
107 # Called from #current_<%= file_name %>. Finaly, attempt to login by an expiring token in the cookie.
108 def login_from_cookie
109 <%= file_name %> = cookies[:auth_token] && <%= class_name %>.find_by_remember_token(cookies[:auth_token])
110 if <%= file_name %> && <%= file_name %>.remember_token?
111 <%= file_name %>.remember_me
112 cookies[:auth_token] = { :value => <%= file_name %>.remember_token, :expires => <%= file_name %>.remember_token_expires_at }
113 self.current_<%= file_name %> = <%= file_name %>
114 end
115 end
116end
  
1module AuthenticatedTestHelper
2 # Sets the current <%= file_name %> in the session from the <%= file_name %> fixtures.
3 def login_as(<%= file_name %>)
4 @request.session[:<%= file_name %>_id] = <%= file_name %> ? <%= table_name %>(<%= file_name %>).id : nil
5 end
6
7 def authorize_as(user)
8 @request.env["HTTP_AUTHORIZATION"] = user ? ActionController::HttpAuthentication::Basic.encode_credentials(users(user).login, 'test') : nil
9 end
10end
  
1# This controller handles the login/logout function of the site.
2class <%= controller_class_name %>Controller < ApplicationController
3 # Be sure to include AuthenticationSystem in Application Controller instead
4 include AuthenticatedSystem
5
6 # render new.rhtml
7 def new
8 end
9
10 def create
11 self.current_<%= file_name %> = <%= class_name %>.authenticate(params[:login], params[:password])
12 if logged_in?
13 if params[:remember_me] == "1"
14 self.current_<%= file_name %>.remember_me
15 cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
16 end
17 redirect_back_or_default('/')
18 flash[:notice] = "Logged in successfully"
19 else
20 render :action => 'new'
21 end
22 end
23
24 def destroy
25 self.current_<%= file_name %>.forget_me if logged_in?
26 cookies.delete :auth_token
27 reset_session
28 flash[:notice] = "You have been logged out."
29 redirect_back_or_default('/')
30 end
31end
  
1quentin:
2 id: 1
3 login: quentin
4 email: quentin@example.com
5 salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
6 crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
7 created_at: <%%= 5.days.ago.to_s :db %>
8<% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b <% end %>
9<% if options[:include_activation] %> activated_at: <%%= 5.days.ago.to_s :db %> <% end %>
10<% if options[:stateful] %> state: active<% end %>
11aaron:
12 id: 2
13 login: aaron
14 email: aaron@example.com
15 salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
16 crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
17 created_at: <%%= 1.days.ago.to_s :db %>
18<% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9a <% end %>
19<% if options[:stateful] %> state: pending<% end %>
  
1require File.dirname(__FILE__) + '/../spec_helper'
2
3# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead
4# Then, you can remove it from this and the units test.
5include AuthenticatedTestHelper
6
7describe <%= controller_class_name %>Controller do
8 fixtures :<%= table_name %>
9
10 it 'logins and redirects' do
11 post :create, :login => 'quentin', :password => 'test'
12 session[:<%= file_name %>_id].should_not be_nil
13 response.should be_redirect
14 end
15
16 it 'fails login and does not redirect' do
17 post :create, :login => 'quentin', :password => 'bad password'
18 session[:<%= file_name %>_id].should be_nil
19 response.should be_success
20 end
21
22 it 'logs out' do
23 login_as :quentin
24 get :destroy
25 session[:<%= file_name %>_id].should be_nil
26 response.should be_redirect
27 end
28
29 it 'remembers me' do
30 post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
31 response.cookies["auth_token"].should_not be_nil
32 end
33
34 it 'does not remember me' do
35 post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
36 response.cookies["auth_token"].should be_nil
37 end
38
39 it 'deletes token on logout' do
40 login_as :quentin
41 get :destroy
42 response.cookies["auth_token"].should == []
43 end
44
45 it 'logs in with cookie' do
46 <%= table_name %>(:quentin).remember_me
47 request.cookies["auth_token"] = cookie_for(:quentin)
48 get :new
49 controller.send(:logged_in?).should be_true
50 end
51
52 it 'fails expired cookie login' do
53 <%= table_name %>(:quentin).remember_me
54 <%= table_name %>(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
55 request.cookies["auth_token"] = cookie_for(:quentin)
56 get :new
57 controller.send(:logged_in?).should_not be_true
58 end
59
60 it 'fails cookie login' do
61 <%= table_name %>(:quentin).remember_me
62 request.cookies["auth_token"] = auth_token('invalid_auth_token')
63 get :new
64 controller.send(:logged_in?).should_not be_true
65 end
66
67 def auth_token(token)
68 CGI::Cookie.new('name' => 'auth_token', 'value' => token)
69 end
70
71 def cookie_for(<%= file_name %>)
72 auth_token <%= table_name %>(<%= file_name %>).remember_token
73 end
74end
  
1require File.dirname(__FILE__) + '/../test_helper'
2require '<%= controller_file_name %>_controller'
3
4# Re-raise errors caught by the controller.
5class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
7class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
8 # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
9 # Then, you can remove it from this and the units test.
10 include AuthenticatedTestHelper
11
12 fixtures :<%= table_name %>
13
14 def setup
15 @controller = <%= controller_class_name %>Controller.new
16 @request = ActionController::TestRequest.new
17 @response = ActionController::TestResponse.new
18 end
19
20 def test_should_login_and_redirect
21 post :create, :login => 'quentin', :password => 'test'
22 assert session[:<%= file_name %>_id]
23 assert_response :redirect
24 end
25
26 def test_should_fail_login_and_not_redirect
27 post :create, :login => 'quentin', :password => 'bad password'
28 assert_nil session[:<%= file_name %>_id]
29 assert_response :success
30 end
31
32 def test_should_logout
33 login_as :quentin
34 get :destroy
35 assert_nil session[:<%= file_name %>_id]
36 assert_response :redirect
37 end
38
39 def test_should_remember_me
40 post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
41 assert_not_nil @response.cookies["auth_token"]
42 end
43
44 def test_should_not_remember_me
45 post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
46 assert_nil @response.cookies["auth_token"]
47 end
48
49 def test_should_delete_token_on_logout
50 login_as :quentin
51 get :destroy
52 assert_equal @response.cookies["auth_token"], []
53 end
54
55 def test_should_login_with_cookie
56 <%= table_name %>(:quentin).remember_me
57 @request.cookies["auth_token"] = cookie_for(:quentin)
58 get :new
59 assert @controller.send(:logged_in?)
60 end
61
62 def test_should_fail_expired_cookie_login
63 <%= table_name %>(:quentin).remember_me
64 <%= table_name %>(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
65 @request.cookies["auth_token"] = cookie_for(:quentin)
66 get :new
67 assert !@controller.send(:logged_in?)
68 end
69
70 def test_should_fail_cookie_login
71 <%= table_name %>(:quentin).remember_me
72 @request.cookies["auth_token"] = auth_token('invalid_auth_token')
73 get :new
74 assert !@controller.send(:logged_in?)
75 end
76
77 protected
78 def auth_token(token)
79 CGI::Cookie.new('name' => 'auth_token', 'value' => token)
80 end
81
82 def cookie_for(<%= file_name %>)
83 auth_token <%= table_name %>(<%= file_name %>).remember_token
84 end
85end
  
1module <%= controller_class_name %>Helper
2end
  
1<%% form_tag <%= controller_singular_name %>_path do -%>
2<p><label for="login">Login</label><br/>
3<%%= text_field_tag 'login' %></p>
4
5<p><label for="password">Password</label><br/>
6<%%= password_field_tag 'password' %></p>
7
8<!-- Uncomment this if you want this functionality
9<p><label for="remember_me">Remember me:</label>
10<%%= check_box_tag 'remember_me' %></p>
11-->
12
13<p><%%= submit_tag 'Log in' %></p>
14<%% end -%>
  
1class <%= class_name %>Mailer < ActionMailer::Base
2 def signup_notification(<%= file_name %>)
3 setup_email(<%= file_name %>)
4 @subject += 'Please activate your new account'
5 <% if options[:include_activation] %>
6 @body[:url] = "http://YOURSITE/activate/#{<%= file_name %>.activation_code}"
7 <% else %>
8 @body[:url] = "http://YOURSITE/login/" <% end %>
9 end
10
11 def activation(<%= file_name %>)
12 setup_email(<%= file_name %>)
13 @subject += 'Your account has been activated!'
14 @body[:url] = "http://YOURSITE/"
15 end
16
17 protected
18 def setup_email(<%= file_name %>)
19 @recipients = "#{<%= file_name %>.email}"
20 @from = "ADMINEMAIL"
21 @subject = "[YOURSITE] "
22 @sent_on = Time.now
23 @body[:<%= file_name %>] = <%= file_name %>
24 end
25end
  
1require File.dirname(__FILE__) + '/../test_helper'
2require '<%= file_name %>_mailer'
3
4class <%= class_name %>MailerTest < Test::Unit::TestCase
5 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
6 CHARSET = "utf-8"
7
8 include ActionMailer::Quoting
9
10 def setup
11 ActionMailer::Base.delivery_method = :test
12 ActionMailer::Base.perform_deliveries = true
13 ActionMailer::Base.deliveries = []
14
15 @expected = TMail::Mail.new
16 @expected.set_content_type "text", "plain", { "charset" => CHARSET }
17 end
18
19 def test_dummy_test
20 #do nothing
21 end
22
23 private
24 def read_fixture(action)
25 IO.readlines("#{FIXTURES_PATH}/<%= file_name %>_mailer/#{action}")
26 end
27
28 def encode(subject)
29 quoted_printable(subject, CHARSET)
30 end
31end
  
1class <%= migration_name %> < ActiveRecord::Migration
2 def self.up
3 create_table "<%= table_name %>", :force => true do |t|
4 t.column :login, :string
5 t.column :email, :string
6 t.column :crypted_password, :string, :limit => 40
7 t.column :salt, :string, :limit => 40
8 t.column :created_at, :datetime
9 t.column :updated_at, :datetime
10 t.column :remember_token, :string
11 t.column :remember_token_expires_at, :datetime
12 <% if options[:include_activation] %>t.column :activation_code, :string, :limit => 40
13 t.column :activated_at, :datetime<% end %>
14 <% if options[:stateful] %>t.column :state, :string, :null => :no, :default => 'passive'
15 t.column :deleted_at, :datetime<% end %>
16 end
17 end
18
19 def self.down
20 drop_table "<%= table_name %>"
21 end
22end
  
1require 'digest/sha1'
2class <%= class_name %> < ActiveRecord::Base
3 # Virtual attribute for the unencrypted password
4 attr_accessor :password
5
6 validates_presence_of :login, :email
7 validates_presence_of :password, :if => :password_required?
8 validates_presence_of :password_confirmation, :if => :password_required?
9 validates_length_of :password, :within => 4..40, :if => :password_required?
10 validates_confirmation_of :password, :if => :password_required?
11 validates_length_of :login, :within => 3..40
12 validates_length_of :email, :within => 3..100
13 validates_uniqueness_of :login, :email, :case_sensitive => false
14 before_save :encrypt_password
15 <% if options[:include_activation] && !options[:stateful] %>before_create :make_activation_code <% end %>
16 # prevents a user from submitting a crafted form that bypasses activation
17 # anything else you want your user to change should be added here.
18 attr_accessible :login, :email, :password, :password_confirmation
19<% if options[:stateful] %>
20 acts_as_state_machine :initial => :pending
21 state :passive
22 state :pending, :enter => :make_activation_code
23 state :active, :enter => :do_activate
24 state :suspended
25 state :deleted, :enter => :do_delete
26
27 event :register do
28 transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) }
29 end
30
31 event :activate do
32 transitions :from => :pending, :to => :active
33 end
34
35 event :suspend do
36 transitions :from => [:passive, :pending, :active], :to => :suspended
37 end
38
39 event :delete do
40 transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
41 end
42
43 event :unsuspend do
44 transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| !u.activated_at.blank? }
45 transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| !u.activation_code.blank? }
46 transitions :from => :suspended, :to => :passive
47 end
48<% elsif options[:include_activation] %>
49 # Activates the user in the database.
50 def activate
51 @activated = true
52 self.activated_at = Time.now.utc
53 self.activation_code = nil
54 save(false)
55 end
56
57 def active?
58 # the existence of an activation code means they have not activated yet
59 activation_code.nil?
60 end
61
62 # Returns true if the user has just been activated.
63 def pending?
64 @activated
65 end
66<% end %>
67 # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
68 def self.authenticate(login, password)
69 u = <%
70 if options[:stateful] %>find_in_state :first, :active, :conditions => {:login => login}<%
71 elsif options[:include_activation] %>find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login]<%
72 else %>find_by_login(login)<%
73 end %> # need to get the salt
74 u && u.authenticated?(password) ? u : nil
75 end
76
77 # Encrypts some data with the salt.
78 def self.encrypt(password, salt)
79 Digest::SHA1.hexdigest("--#{salt}--#{password}--")
80 end
81
82 # Encrypts the password with the user salt
83 def encrypt(password)
84 self.class.encrypt(password, salt)
85 end
86
87 def authenticated?(password)
88 crypted_password == encrypt(password)
89 end
90
91 def remember_token?
92 remember_token_expires_at && Time.now.utc < remember_token_expires_at
93 end
94
95 # These create and unset the fields required for remembering users between browser closes
96 def remember_me
97 remember_me_for 2.weeks
98 end
99
100 def remember_me_for(time)
101 remember_me_until time.from_now.utc
102 end
103
104 def remember_me_until(time)
105 self.remember_token_expires_at = time
106 self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
107 save(false)
108 end
109
110 def forget_me
111 self.remember_token_expires_at = nil
112 self.remember_token = nil
113 save(false)
114 end
115
116 protected
117 # before filter
118 def encrypt_password
119 return if password.blank?
120 self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
121 self.crypted_password = encrypt(password)
122 end
123
124 def password_required?
125 crypted_password.blank? || !password.blank?
126 end
127 <% if options[:include_activation] %>
128 def make_activation_code
129<% if options[:stateful] %> self.deleted_at = nil<% end %>
130 self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
131 end<% end %>
132 <% if options[:stateful] %>
133 def do_delete
134 self.deleted_at = Time.now.utc
135 end
136
137 def do_activate
138 self.activated_at = Time.now.utc
139 self.deleted_at = self.activation_code = nil
140 end<% end %>
141end
  
1class <%= model_controller_class_name %>Controller < ApplicationController
2 # Be sure to include AuthenticationSystem in Application Controller instead
3 include AuthenticatedSystem
4 <% if options[:stateful] %>
5 # Protect these actions behind an admin login
6 # before_filter :admin_required, :only => [:suspend, :unsuspend, :destroy, :purge]
7 before_filter :find_<%= file_name %>, :only => [:suspend, :unsuspend, :destroy, :purge]
8 <% end %>
9
10 # render new.rhtml
11 def new
12 end
13
14 def create
15 cookies.delete :auth_token
16 # protects against session fixation attacks, wreaks havoc with
17 # request forgery protection.
18 # uncomment at your own risk
19 # reset_session
20 @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
21 @<%= file_name %>.<% if options[:stateful] %>register! if @<%= file_name %>.valid?<% else %>save<% end %>
22 if @<%= file_name %>.errors.empty?
23 self.current_<%= file_name %> = @<%= file_name %>
24 redirect_back_or_default('/')
25 flash[:notice] = "Thanks for signing up!"
26 else
27 render :action => 'new'
28 end
29 end
30<% if options[:include_activation] %>
31 def activate
32 self.current_<%= file_name %> = params[:activation_code].blank? ? :false : <%= class_name %>.find_by_activation_code(params[:activation_code])
33 if logged_in? && !current_<%= file_name %>.active?
34 current_<%= file_name %>.activate<% if options[:stateful] %>!<% end %>
35 flash[:notice] = "Signup complete!"
36 end
37 redirect_back_or_default('/')
38 end
39<% end %><% if options[:stateful] %>
40 def suspend
41 @<%= file_name %>.suspend!
42 redirect_to <%= table_name %>_path
43 end
44
45 def unsuspend
46 @<%= file_name %>.unsuspend!
47 redirect_to <%= table_name %>_path
48 end
49
50 def destroy
51 @<%= file_name %>.delete!
52 redirect_to <%= table_name %>_path
53 end
54
55 def purge
56 @<%= file_name %>.destroy
57 redirect_to <%= table_name %>_path
58 end
59
60protected
61 def find_<%= file_name %>
62 @<%= file_name %> = <%= class_name %>.find(params[:id])
63 end
64<% end %>
65end
  
1require File.dirname(__FILE__) + '/../spec_helper'
2
3# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead
4# Then, you can remove it from this and the units test.
5include AuthenticatedTestHelper
6
7describe <%= model_controller_class_name %>Controller do
8 fixtures :<%= table_name %>
9
10 it 'allows signup' do
11 lambda do
12 create_<%= file_name %>
13 response.should be_redirect
14 end.should change(<%= class_name %>, :count).by(1)
15 end
16
17 <% if options[:stateful] %>
18 it 'signs up user in pending state' do
19 create_user
20 assigns(:user).should be_pending
21 end<% end %>
22
23 <% if options[:include_activation] %>
24 it 'signs up user with activation code' do
25 create_user
26 assigns(:user).activation_code.should_not be_nil
27 end<% end %>
28
29 it 'requires login on signup' do
30 lambda do
31 create_<%= file_name %>(:login => nil)
32 assigns[:<%= file_name %>].errors.on(:login).should_not be_nil
33 response.should be_success
34 end.should_not change(<%= class_name %>, :count)
35 end
36
37 it 'requires password on signup' do
38 lambda do
39 create_<%= file_name %>(:password => nil)
40 assigns[:<%= file_name %>].errors.on(:password).should_not be_nil
41 response.should be_success
42 end.should_not change(<%= class_name %>, :count)
43 end
44
45 it 'requires password confirmation on signup' do
46 lambda do
47 create_<%= file_name %>(:password_confirmation => nil)
48 assigns[:<%= file_name %>].errors.on(:password_confirmation).should_not be_nil
49 response.should be_success
50 end.should_not change(<%= class_name %>, :count)
51 end
52
53 it 'requires email on signup' do
54 lambda do
55 create_<%= file_name %>(:email => nil)
56 assigns[:<%= file_name %>].errors.on(:email).should_not be_nil
57 response.should be_success
58 end.should_not change(<%= class_name %>, :count)
59 end
60
61 <% if options[:include_activation] %>
62 it 'activates user' do
63 <%= class_name %>.authenticate('aaron', 'test').should be_nil
64 get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
65 response.should redirect_to('/')
66 flash[:notice].should_not be_nil
67 <%= class_name %>.authenticate('aaron', 'test').should == <%= table_name %>(:aaron)
68 end
69
70 it 'does not activate user without key' do
71 get :activate
72 flash[:notice].should be_nil
73 end
74
75 it 'does not activate user with blank key' do
76 get :activate, :activation_code => ''
77 flash[:notice].should be_nil
78 end<% end %>
79
80 def create_<%= file_name %>(options = {})
81 post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire@example.com',
82 :password => 'quire', :password_confirmation => 'quire' }.merge(options)
83 end
84end
  
1require File.dirname(__FILE__) + '/../test_helper'
2require '<%= model_controller_file_name %>_controller'
3
4# Re-raise errors caught by the controller.
5class <%= model_controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
7class <%= model_controller_class_name %>ControllerTest < Test::Unit::TestCase
8 # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
9 # Then, you can remove it from this and the units test.
10 include AuthenticatedTestHelper
11
12 fixtures :<%= table_name %>
13
14 def setup
15 @controller = <%= model_controller_class_name %>Controller.new
16 @request = ActionController::TestRequest.new
17 @response = ActionController::TestResponse.new
18 end
19
20 def test_should_allow_signup
21 assert_difference '<%= class_name %>.count' do
22 create_<%= file_name %>
23 assert_response :redirect
24 end
25 end
26
27 def test_should_require_login_on_signup
28 assert_no_difference '<%= class_name %>.count' do
29 create_<%= file_name %>(:login => nil)
30 assert assigns(:<%= file_name %>).errors.on(:login)
31 assert_response :success
32 end
33 end
34
35 def test_should_require_password_on_signup
36 assert_no_difference '<%= class_name %>.count' do
37 create_<%= file_name %>(:password => nil)
38 assert assigns(:<%= file_name %>).errors.on(:password)
39 assert_response :success
40 end
41 end
42
43 def test_should_require_password_confirmation_on_signup
44 assert_no_difference '<%= class_name %>.count' do
45 create_<%= file_name %>(:password_confirmation => nil)
46 assert assigns(:<%= file_name %>).errors.on(:password_confirmation)
47 assert_response :success
48 end
49 end
50
51 def test_should_require_email_on_signup
52 assert_no_difference '<%= class_name %>.count' do
53 create_<%= file_name %>(:email => nil)
54 assert assigns(:<%= file_name %>).errors.on(:email)
55 assert_response :success
56 end
57 end
58 <% if options[:include_activation] %>
59 def test_should_activate_user
60 assert_nil <%= class_name %>.authenticate('aaron', 'test')
61 get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
62 assert_redirected_to '/'
63 assert_not_nil flash[:notice]
64 assert_equal <%= table_name %>(:aaron), <%= class_name %>.authenticate('aaron', 'test')
65 end
66
67 def test_should_not_activate_user_without_key
68 get :activate
69 assert_nil flash[:notice]
70 rescue ActionController::RoutingError
71 # in the event your routes deny this, we'll just bow out gracefully.
72 end
73
74 def test_should_not_activate_user_with_blank_key
75 get :activate, :activation_code => ''
76 assert_nil flash[:notice]
77 rescue ActionController::RoutingError
78 # well played, sir
79 end<% end %>
80
81 protected
82 def create_<%= file_name %>(options = {})
83 post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire@example.com',
84 :password => 'quire', :password_confirmation => 'quire' }.merge(options)
85 end
86end
  
1module <%= model_controller_class_name %>Helper
2end
  
1class <%= class_name %>Observer < ActiveRecord::Observer
2 def after_create(<%= file_name %>)
3 <%= class_name %>Mailer.deliver_signup_notification(<%= file_name %>)
4 end
5
6 def after_save(<%= file_name %>)
7 <% if options[:include_activation] %>
8 <%= class_name %>Mailer.deliver_activation(<%= file_name %>) if <%= file_name %>.pending?
9 <% end %>
10 end
11end
  
1<%%= error_messages_for :<%= file_name %> %>
2<%% form_for :<%= file_name %>, :url => <%= table_name %>_path do |f| -%>
3<p><label for="login">Login</label><br/>
4<%%= f.text_field :login %></p>
5
6<p><label for="email">Email</label><br/>
7<%%= f.text_field :email %></p>
8
9<p><label for="password">Password</label><br/>
10<%%= f.password_field :password %></p>
11
12<p><label for="password_confirmation">Confirm Password</label><br/>
13<%%= f.password_field :password_confirmation %></p>
14
15<p><%%= submit_tag 'Sign up' %></p>
16<%% end -%>
  
1Your account has been created.
2
3 Username: <%%= @<%= file_name %>.login %>
4 Password: <%%= @<%= file_name %>.password %>
5
6Visit this url to activate your account:
7
8 <%%= @url %>
  
1require File.dirname(__FILE__) + '/../spec_helper'
2
3# Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead.
4# Then, you can remove it from this and the functional test.
5include AuthenticatedTestHelper
6
7describe <%= class_name %> do
8 fixtures :<%= table_name %>
9
10 describe 'being created' do
11 before do
12 @<%= file_name %> = nil
13 @creating_<%= file_name %> = lambda do
14 @<%= file_name %> = create_<%= file_name %>
15 violated "#{@<%= file_name %>.errors.full_messages.to_sentence}" if @<%= file_name %>.new_record?
16 end
17 end
18
19 it 'increments User#count' do
20 @creating_<%= file_name %>.should change(<%= class_name %>, :count).by(1)
21 end
22<% if options[:include_activation] %>
23 it 'initializes #activation_code' do
24 @creating_<%= file_name %>.call
25 @<%= file_name %>.reload.activation_code.should_not be_nil
26 end
27<% end %><% if options[:stateful] %>
28 it 'starts in pending state' do
29 @creating_<%= file_name %>.call
30 @<%= file_name %>.should be_pending
31 end
32<% end %> end
33
34 it 'requires login' do
35 lambda do
36 u = create_<%= file_name %>(:login => nil)
37 u.errors.on(:login).should_not be_nil
38 end.should_not change(<%= class_name %>, :count)
39 end
40
41 it 'requires password' do
42 lambda do
43 u = create_<%= file_name %>(:password => nil)
44 u.errors.on(:password).should_not be_nil
45 end.should_not change(<%= class_name %>, :count)
46 end
47
48 it 'requires password confirmation' do
49 lambda do
50 u = create_<%= file_name %>(:password_confirmation => nil)
51 u.errors.on(:password_confirmation).should_not be_nil
52 end.should_not change(<%= class_name %>, :count)
53 end
54
55 it 'requires email' do
56 lambda do
57 u = create_<%= file_name %>(:email => nil)
58 u.errors.on(:email).should_not be_nil
59 end.should_not change(<%= class_name %>, :count)
60 end
61
62 it 'resets password' do
63 <%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
64 <%= class_name %>.authenticate('quentin', 'new password').should == <%= table_name %>(:quentin)
65 end
66
67 it 'does not rehash password' do
68 <%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
69 <%= class_name %>.authenticate('quentin2', 'test').should == <%= table_name %>(:quentin)
70 end
71
72 it 'authenticates <%= file_name %>' do
73 <%= class_name %>.authenticate('quentin', 'test').should == <%= table_name %>(:quentin)
74 end
75
76 it 'sets remember token' do
77 <%= table_name %>(:quentin).remember_me
78 <%= table_name %>(:quentin).remember_token.should_not be_nil
79 <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
80 end
81
82 it 'unsets remember token' do
83 <%= table_name %>(:quentin).remember_me
84 <%= table_name %>(:quentin).remember_token.should_not be_nil
85 <%= table_name %>(:quentin).forget_me
86 <%= table_name %>(:quentin).remember_token.should be_nil
87 end
88
89 it 'remembers me for one week' do
90 before = 1.week.from_now.utc
91 <%= table_name %>(:quentin).remember_me_for 1.week
92 after = 1.week.from_now.utc
93 <%= table_name %>(:quentin).remember_token.should_not be_nil
94 <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
95 <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after).should be_true
96 end
97
98 it 'remembers me until one week' do
99 time = 1.week.from_now.utc
100 <%= table_name %>(:quentin).remember_me_until time
101 <%= table_name %>(:quentin).remember_token.should_not be_nil
102 <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
103 <%= table_name %>(:quentin).remember_token_expires_at.should == time
104 end
105
106 it 'remembers me default two weeks' do
107 before = 2.weeks.from_now.utc
108 <%= table_name %>(:quentin).remember_me
109 after = 2.weeks.from_now.utc
110 <%= table_name %>(:quentin).remember_token.should_not be_nil
111 <%= table_name %>(:quentin).remember_token_expires_at.should_not be_nil
112 <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after).should be_true
113 end
114<% if options[:stateful] %>
115 it 'registers passive <%= file_name %>' do
116 <%= file_name %> = create_<%= file_name %>(:password => nil, :password_confirmation => nil)
117 <%= file_name %>.should be_passive
118 <%= file_name %>.update_attributes(:password => 'new password', :password_confirmation => 'new password')
119 <%= file_name %>.register!
120 <%= file_name %>.should be_pending
121 end
122
123 it 'suspends <%= file_name %>' do
124 <%= table_name %>(:quentin).suspend!
125 <%= table_name %>(:quentin).should be_suspended
126 end
127
128 it 'does not authenticate suspended <%= file_name %>' do
129 <%= table_name %>(:quentin).suspend!
130 <%= class_name %>.authenticate('quentin', 'test').should_not == <%= table_name %>(:quentin)
131 end
132
133 it 'deletes <%= file_name %>' do
134 <%= table_name %>(:quentin).deleted_at.should be_nil
135 <%= table_name %>(:quentin).delete!
136 <%= table_name %>(:quentin).deleted_at.should_not be_nil
137 <%= table_name %>(:quentin).should be_deleted
138 end
139
140 describe "being unsuspended" do
141 fixtures :<%= table_name %>
142
143 before do
144 @<%= file_name %> = <%= table_name %>(:quentin)
145 @<%= file_name %>.suspend!
146 end
147
148 it 'reverts to active state' do
149 @<%= file_name %>.unsuspend!
150 @<%= file_name %>.should be_active
151 end
152
153 it 'reverts to passive state if activation_code and activated_at are nil' do
154 <%= class_name %>.update_all :activation_code => nil, :activated_at => nil
155 @<%= file_name %>.reload.unsuspend!
156 @<%= file_name %>.should be_passive
157 end
158
159 it 'reverts to pending state if activation_code is set and activated_at is nil' do
160 <%= class_name %>.update_all :activation_code => 'foo-bar', :activated_at => nil
161 @<%= file_name %>.reload.unsuspend!
162 @<%= file_name %>.should be_pending
163 end
164 end
165<% end %>
166protected
167 def create_<%= file_name %>(options = {})
168 record = <%= class_name %>.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
169 record.<% if options[:stateful] %>register! if record.valid?<% else %>save<% end %>
170 record
171 end
172end
  
1require File.dirname(__FILE__) + '/../test_helper'
2
3class <%= class_name %>Test < Test::Unit::TestCase
4 # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead.
5 # Then, you can remove it from this and the functional test.
6 include AuthenticatedTestHelper
7 fixtures :<%= table_name %>
8
9 def test_should_create_<%= file_name %>
10 assert_difference '<%= class_name %>.count' do
11 <%= file_name %> = create_<%= file_name %>
12 assert !<%= file_name %>.new_record?, "#{<%= file_name %>.errors.full_messages.to_sentence}"
13 end
14 end
15<% if options[:include_activation] %>
16 def test_should_initialize_activation_code_upon_creation
17 <%= file_name %> = create_<%= file_name %>
18 assert_not_nil <%= file_name %>.reload.activation_code
19 end
20<% end %><% if options[:stateful] %>
21 def test_should_create_and_start_in_pending_state
22 <%= file_name %> = create_<%= file_name %>
23 assert <%= file_name %>.pending?
24 end
25
26<% end %>
27 def test_should_require_login
28 assert_no_difference '<%= class_name %>.count' do
29 u = create_<%= file_name %>(:login => nil)
30 assert u.errors.on(:login)
31 end
32 end
33
34 def test_should_require_password
35 assert_no_difference '<%= class_name %>.count' do
36 u = create_<%= file_name %>(:password => nil)
37 assert u.errors.on(:password)
38 end
39 end
40
41 def test_should_require_password_confirmation
42 assert_no_difference '<%= class_name %>.count' do
43 u = create_<%= file_name %>(:password_confirmation => nil)
44 assert u.errors.on(:password_confirmation)
45 end
46 end
47
48 def test_should_require_email
49 assert_no_difference '<%= class_name %>.count' do
50 u = create_<%= file_name %>(:email => nil)
51 assert u.errors.on(:email)
52 end
53 end
54
55 def test_should_reset_password
56 <%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
57 assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'new password')
58 end
59
60 def test_should_not_rehash_password
61 <%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
62 assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin2', 'test')
63 end
64
65 def test_should_authenticate_<%= file_name %>
66 assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
67 end
68
69 def test_should_set_remember_token
70 <%= table_name %>(:quentin).remember_me
71 assert_not_nil <%= table_name %>(:quentin).remember_token
72 assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
73 end
74
75 def test_should_unset_remember_token
76 <%= table_name %>(:quentin).remember_me
77 assert_not_nil <%= table_name %>(:quentin).remember_token
78 <%= table_name %>(:quentin).forget_me
79 assert_nil <%= table_name %>(:quentin).remember_token
80 end
81
82 def test_should_remember_me_for_one_week
83 before = 1.week.from_now.utc
84 <%= table_name %>(:quentin).remember_me_for 1.week
85 after = 1.week.from_now.utc
86 assert_not_nil <%= table_name %>(:quentin).remember_token
87 assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
88 assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
89 end
90
91 def test_should_remember_me_until_one_week
92 time = 1.week.from_now.utc
93 <%= table_name %>(:quentin).remember_me_until time
94 assert_not_nil <%= table_name %>(:quentin).remember_token
95 assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
96 assert_equal <%= table_name %>(:quentin).remember_token_expires_at, time
97 end
98
99 def test_should_remember_me_default_two_weeks
100 before = 2.weeks.from_now.utc
101 <%= table_name %>(:quentin).remember_me
102 after = 2.weeks.from_now.utc
103 assert_not_nil <%= table_name %>(:quentin).remember_token
104 assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
105 assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
106 end
107<% if options[:stateful] %>
108 def test_should_register_passive_<%= file_name %>
109 <%= file_name %> = create_<%= file_name %>(:password => nil, :password_confirmation => nil)
110 assert <%= file_name %>.passive?
111 <%= file_name %>.update_attributes(:password => 'new password', :password_confirmation => 'new password')
112 <%= file_name %>.register!
113 assert <%= file_name %>.pending?
114 end
115
116 def test_should_suspend_<%= file_name %>
117 <%= table_name %>(:quentin).suspend!
118 assert <%= table_name %>(:quentin).suspended?
119 end
120
121 def test_suspended_<%= file_name %>_should_not_authenticate
122 <%= table_name %>(:quentin).suspend!
123 assert_not_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
124 end
125
126 def test_should_unsuspend_<%= file_name %>_to_active_state
127 <%= table_name %>(:quentin).suspend!
128 assert <%= table_name %>(:quentin).suspended?
129 <%= table_name %>(:quentin).unsuspend!
130 assert <%= table_name %>(:quentin).active?
131 end
132
133 def test_should_unsuspend_<%= file_name %>_with_nil_activation_code_and_activated_at_to_passive_state
134 <%= table_name %>(:quentin).suspend!
135 <%= class_name %>.update_all :activation_code => nil, :activated_at => nil
136 assert <%= table_name %>(:quentin).suspended?
137 <%= table_name %>(:quentin).reload.unsuspend!
138 assert <%= table_name %>(:quentin).passive?
139 end
140
141 def test_should_unsuspend_<%= file_name %>_with_activation_code_and_nil_activated_at_to_pending_state
142 <%= table_name %>(:quentin).suspend!
143 <%= class_name %>.update_all :activation_code => 'foo-bar', :activated_at => nil
144 assert <%= table_name %>(:quentin).suspended?
145 <%= table_name %>(:quentin).reload.unsuspend!
146 assert <%= table_name %>(:quentin).pending?
147 end
148
149 def test_should_delete_<%= file_name %>
150 assert_nil <%= table_name %>(:quentin).deleted_at
151 <%= table_name %>(:quentin).delete!
152 assert_not_nil <%= table_name %>(:quentin).deleted_at
153 assert <%= table_name %>(:quentin).deleted?
154 end
155<% end %>
156protected
157 def create_<%= file_name %>(options = {})
158 <%= class_name %>.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
159 end
160end
  
1puts IO.read(File.join(File.dirname(__FILE__), 'README'))
  
1Rails::Generator::Commands::Create.class_eval do
2 def route_resource(*resources)
3 resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
4 sentinel = 'ActionController::Routing::Routes.draw do |map|'
5
6 logger.route "map.resource #{resource_list}"
7 unless options[:pretend]
8 gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
9 "#{match}\n map.resource #{resource_list}\n"
10 end
11 end
12 end
13end
14
15Rails::Generator::Commands::Destroy.class_eval do
16 def route_resource(*resources)
17 resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
18 look_for = "\n map.resource #{resource_list}\n"
19 logger.route "map.resource #{resource_list}"
20 gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
21 end
22end
23
24Rails::Generator::Commands::List.class_eval do
25 def route_resource(*resources)
26 resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
27 logger.route "map.resource #{resource_list}"
28 end
29end