tonkapark-clearance 0.6.9.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.textile +11 -5
  2. data/README.textile +38 -37
  3. data/Rakefile +41 -19
  4. data/TODO.textile +4 -3
  5. data/app/controllers/clearance/confirmations_controller.rb +27 -6
  6. data/app/controllers/clearance/passwords_controller.rb +1 -1
  7. data/app/controllers/clearance/sessions_controller.rb +1 -1
  8. data/app/controllers/clearance/users_controller.rb +3 -4
  9. data/app/models/clearance_mailer.rb +3 -25
  10. data/app/views/passwords/edit.html.erb +12 -14
  11. data/app/views/passwords/new.html.erb +9 -10
  12. data/app/views/sessions/new.html.erb +26 -1
  13. data/app/views/users/_form.html.erb +13 -0
  14. data/app/views/users/new.html.erb +5 -3
  15. data/clearance.gemspec +20 -31
  16. data/config/clearance_routes.rb +7 -9
  17. data/generators/clearance/clearance_generator.rb +3 -30
  18. data/generators/clearance/templates/README +17 -17
  19. data/generators/clearance/templates/migrations/create_users.rb +0 -3
  20. data/generators/clearance/templates/migrations/update_users.rb +1 -4
  21. data/generators/clearance/templates/user.rb +0 -1
  22. data/generators/clearance_features/clearance_features_generator.rb +4 -4
  23. data/generators/clearance_features/templates/features/sign_up.feature +17 -0
  24. data/generators/clearance_views/USAGE +0 -0
  25. data/generators/clearance_views/clearance_views_generator.rb +27 -0
  26. data/generators/clearance_views/templates/formtastic/passwords/edit.html.erb +21 -0
  27. data/generators/clearance_views/templates/formtastic/passwords/new.html.erb +15 -0
  28. data/generators/clearance_views/templates/formtastic/sessions/new.html.erb +22 -0
  29. data/generators/clearance_views/templates/formtastic/users/_inputs.html.erb +6 -0
  30. data/generators/clearance_views/templates/formtastic/users/new.html.erb +10 -0
  31. data/lib/clearance/authentication.rb +70 -32
  32. data/lib/clearance/user.rb +87 -32
  33. data/lib/clearance.rb +0 -1
  34. data/shoulda_macros/clearance.rb +6 -0
  35. metadata +23 -32
  36. data/app/controllers/clearance/invitations_controller.rb +0 -40
  37. data/app/views/clearance_mailer/invitation.html.erb +0 -3
  38. data/app/views/invitations/index.html.erb +0 -19
  39. data/app/views/invitations/new.html.erb +0 -15
  40. data/app/views/users/_user.html.erb +0 -28
  41. data/generators/clearance/templates/invitation.rb +0 -4
  42. data/generators/clearance/templates/migrations/create_invitations.rb +0 -20
  43. data/generators/clearance/templates/migrations/update_invitations.rb +0 -40
  44. data/lib/clearance/invitation.rb +0 -87
@@ -1,7 +1,7 @@
1
1
  ActionController::Routing::Routes.draw do |map|
2
2
  map.resources :passwords,
3
3
  :controller => 'clearance/passwords',
4
- :only => [:create]
4
+ :only => [:new, :create]
5
5
 
6
6
  map.resource :session,
7
7
  :controller => 'clearance/sessions',
@@ -17,19 +17,17 @@ ActionController::Routing::Routes.draw do |map|
17
17
  :only => [:new, :create]
18
18
  end
19
19
 
20
- map.resources :invitations,
21
- :controller => 'clearance/invitations',
22
- :only => [:index, :new, :create]
23
-
24
- map.recover_password '/recover_password', :controller =>'clearance/passwords', :action => 'new'
20
+ map.recover_password 'recover_password',
21
+ :controller => 'clearance/passwords',
22
+ :action => 'new'
25
23
 
26
- map.signup '/signup/:invitation_token',
24
+ map.sign_up 'sign_up',
27
25
  :controller => 'clearance/users',
28
26
  :action => 'new'
29
- map.signin 'login',
27
+ map.sign_in 'sign_in',
30
28
  :controller => 'clearance/sessions',
31
29
  :action => 'new'
32
- map.logout 'logout',
30
+ map.sign_out 'sign_out',
33
31
  :controller => 'clearance/sessions',
34
32
  :action => 'destroy',
35
33
  :method => :delete
@@ -12,7 +12,6 @@ class ClearanceGenerator < Rails::Generator::Base
12
12
  user_model = "app/models/user.rb"
13
13
  if File.exists?(user_model)
14
14
  m.insert_into user_model, "include Clearance::User"
15
- m.insert_into user_model, "belongs_to :invitation"
16
15
  else
17
16
  m.directory File.join("app", "models")
18
17
  m.file "user.rb", user_model
@@ -21,35 +20,17 @@ class ClearanceGenerator < Rails::Generator::Base
21
20
  m.directory File.join("test", "factories")
22
21
  m.file "factories.rb", "test/factories/clearance.rb"
23
22
 
24
- m.migration_template "migrations/#{user_migration_name}.rb",
23
+ m.migration_template "migrations/#{migration_name}.rb",
25
24
  'db/migrate',
26
- :migration_file_name => "clearance_#{user_migration_name}"
25
+ :migration_file_name => "clearance_#{migration_name}"
27
26
 
28
- m.directory File.join("config")
29
- m.file "clearance.yml", 'config/clearance.yml'
30
-
31
- invitation_model = "app/models/invitation.rb"
32
- if File.exists?(invitation_model)
33
- m.insert_into invitation_model, "include Clearance::Invitation"
34
- m.insert_into invitation_model, "belongs_to :sender, :class_name => 'User', :foreign_key => :sender_id"
35
- else
36
- m.directory File.join("app", "models")
37
- m.file "invitation.rb", invitation_model
38
- end
39
-
40
- m.migration_template "migrations/#{invitation_migration_name}.rb",
41
- 'db/migrate',
42
- :migration_file_name => "clearance_#{invitation_migration_name}"
43
-
44
-
45
-
46
27
  m.readme "README"
47
28
  end
48
29
  end
49
30
 
50
31
  private
51
32
 
52
- def user_migration_name
33
+ def migration_name
53
34
  if ActiveRecord::Base.connection.table_exists?(:users)
54
35
  'update_users'
55
36
  else
@@ -57,12 +38,4 @@ class ClearanceGenerator < Rails::Generator::Base
57
38
  end
58
39
  end
59
40
 
60
- def invitation_migration_name
61
- if ActiveRecord::Base.connection.table_exists?(:invitations)
62
- 'update_invitations'
63
- else
64
- 'create_invitations'
65
- end
66
- end
67
-
68
41
  end
@@ -1,22 +1,22 @@
1
1
 
2
2
  *******************************************************************************
3
-
3
+
4
4
  Ok, enough fancy automatic stuff. Time for some old school monkey copy-pasting.
5
-
6
- 1. A a clearance.yml config file has been created in your config/ directory.
7
-
8
- Use it to configure the host name and sender of clearance generated emails.
9
- In production it must be the actual host your application is deployed to.
10
- The config is used by mailers to generate URLs in emails.
11
-
12
- It can look like this:
13
-
14
- production:
15
- host: you_production_host.com
16
- do_not_reply: do_not_reply@you_production_host.com
17
-
18
- 2. Define root_url to *something* in your config/routes.rb:
19
-
5
+
6
+ 1. Define a HOST constant in your environments files.
7
+ In config/environments/test.rb and config/environments/development.rb it can be:
8
+
9
+ HOST = "localhost"
10
+
11
+ In production.rb it must be the actual host your application is deployed to.
12
+ The constant is used by mailers to generate URLs in emails.
13
+
14
+ 2. In config/environment.rb:
15
+
16
+ DO_NOT_REPLY = "donotreply@example.com"
17
+
18
+ 3. Define root_url to *something* in your config/routes.rb:
19
+
20
20
  map.root :controller => 'home'
21
-
21
+
22
22
  *******************************************************************************
@@ -7,9 +7,6 @@ class ClearanceCreateUsers < ActiveRecord::Migration
7
7
  t.string :token, :limit => 128
8
8
  t.datetime :token_expires_at
9
9
  t.boolean :email_confirmed, :default => false, :null => false
10
- t.boolean :admin, :default => false, :null => false
11
- t.integer :invitation_id
12
- t.integer :invitation_limit
13
10
  t.timestamps
14
11
  end
15
12
 
@@ -8,10 +8,7 @@ class ClearanceUpdateUsers < ActiveRecord::Migration
8
8
  [:salt, 't.string :salt, :limit => 128'],
9
9
  [:token, 't.string :token, :limit => 128'],
10
10
  [:token_expires_at, 't.datetime :token_expires_at'],
11
- [:email_confirmed, 't.boolean :email_confirmed, :default => false, :null => false'],
12
- [:admin, 't.boolean :admin, :default => false, :null => false'],
13
- [:invitation_id, 't.integer :invitation_id'],
14
- [:invitation_limit, 't.integer :invitation_limit']
11
+ [:email_confirmed, 't.boolean :email_confirmed, :default => false, :null => false']
15
12
  ].delete_if {|c| existing_columns.include?(c.first.to_s)}
16
13
  -%>
17
14
  change_table(:users) do |t|
@@ -1,4 +1,3 @@
1
1
  class User < ActiveRecord::Base
2
2
  include Clearance::User
3
- belongs_to :invitation
4
3
  end
@@ -1,20 +1,20 @@
1
1
  class ClearanceFeaturesGenerator < Rails::Generator::Base
2
-
2
+
3
3
  def manifest
4
4
  record do |m|
5
5
  m.directory File.join("features", "step_definitions")
6
6
  m.directory File.join("features", "support")
7
-
7
+
8
8
  ["features/step_definitions/clearance_steps.rb",
9
9
  "features/step_definitions/factory_girl_steps.rb",
10
10
  "features/support/paths.rb",
11
11
  "features/sign_in.feature",
12
- "features/sign_out.feature",
12
+ "features/sign_out.feature",
13
13
  "features/sign_up.feature",
14
14
  "features/password_reset.feature"].each do |file|
15
15
  m.file file, file
16
16
  end
17
17
  end
18
18
  end
19
-
19
+
20
20
  end
@@ -26,3 +26,20 @@ Feature: Sign up
26
26
  Then I should see "Confirmed email and signed in"
27
27
  And I should be signed in
28
28
 
29
+ Scenario: Signed in user clicks confirmation link again
30
+ Given I signed up with "email@person.com/password"
31
+ When I follow the confirmation link sent to "email@person.com"
32
+ Then I should be signed in
33
+ When I follow the confirmation link sent to "email@person.com"
34
+ Then I should see "Confirmed email and signed in"
35
+ And I should be signed in
36
+
37
+ Scenario: Signed out user clicks confirmation link again
38
+ Given I signed up with "email@person.com/password"
39
+ When I follow the confirmation link sent to "email@person.com"
40
+ Then I should be signed in
41
+ When I sign out
42
+ And I follow the confirmation link sent to "email@person.com"
43
+ Then I should see "Already confirmed email. Please sign in."
44
+ And I should be signed out
45
+
File without changes
@@ -0,0 +1,27 @@
1
+ class ClearanceViewsGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ strategy = "formtastic"
6
+ template_strategy = "erb"
7
+
8
+ m.directory File.join("app", "views", "users")
9
+ m.file "#{strategy}/users/new.html.#{template_strategy}",
10
+ "app/views/users/new.html.#{template_strategy}"
11
+ m.file "#{strategy}/users/_inputs.html.#{template_strategy}",
12
+ "app/views/users/_inputs.html.#{template_strategy}"
13
+
14
+ m.directory File.join("app", "views", "sessions")
15
+ m.file "#{strategy}/sessions/new.html.#{template_strategy}",
16
+ "app/views/sessions/new.html.#{template_strategy}"
17
+
18
+ m.directory File.join("app", "views", "passwords")
19
+ m.file "#{strategy}/passwords/new.html.#{template_strategy}",
20
+ "app/views/passwords/new.html.#{template_strategy}"
21
+ m.file "#{strategy}/passwords/edit.html.#{template_strategy}",
22
+ "app/views/passwords/edit.html.#{template_strategy}"
23
+ end
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,21 @@
1
+ <h2>Change your password</h2>
2
+
3
+ <p>
4
+ Your password has been reset. Choose a new password below.
5
+ </p>
6
+
7
+ <% semantic_form_for(:user,
8
+ :url => user_password_path(@user, :token => @user.token),
9
+ :html => { :method => :put }) do |form| %>
10
+ <%= form.error_messages %>
11
+ <% form.inputs do -%>
12
+ <%= form.input :password, :as => :password,
13
+ :label => "Choose password" %>
14
+ <%= form.input :password_confirmation, :as => :password,
15
+ :label => "Confirm password" %>
16
+ <% end -%>
17
+ <% form.buttons do -%>
18
+ <%= form.commit_button "Save this password" %>
19
+ <% end -%>
20
+ <% end %>
21
+
@@ -0,0 +1,15 @@
1
+ <h2>Reset your password</h2>
2
+
3
+ <p>
4
+ We will email you a link to reset your password.
5
+ </p>
6
+
7
+ <% semantic_form_for :password, :url => passwords_path do |form| -%>
8
+ <% form.inputs do -%>
9
+ <%= form.input :email, :label => "Email address" %>
10
+ <% end -%>
11
+ <% form.buttons do -%>
12
+ <%= form.commit_button "Reset password" %>
13
+ <% end -%>
14
+ <% end -%>
15
+
@@ -0,0 +1,22 @@
1
+ <h2>Sign in</h2>
2
+
3
+ <% semantic_form_for :session, :url => session_path do |form| %>
4
+ <% form.inputs do %>
5
+ <%= form.input :email %>
6
+ <%= form.input :password, :as => :password %>
7
+ <%= form.input :remember_me, :as => :boolean, :required => false %>
8
+ <% end %>
9
+ <% form.buttons do %>
10
+ <%= form.commit_button "Sign in" %>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <ul>
15
+ <li>
16
+ <%= link_to "Sign up", new_user_path %>
17
+ </li>
18
+ <li>
19
+ <%= link_to "Forgot password?", new_password_path %>
20
+ </li>
21
+ </ul>
22
+
@@ -0,0 +1,6 @@
1
+ <% form.inputs do %>
2
+ <%= form.input :email %>
3
+ <%= form.input :password %>
4
+ <%= form.input :password_confirmation, :label => "Confirm password" %>
5
+ <% end %>
6
+
@@ -0,0 +1,10 @@
1
+ <h2>Sign up</h2>
2
+
3
+ <% semantic_form_for @user do |form| %>
4
+ <%= form.error_messages %>
5
+ <%= render :partial => "/users/inputs", :locals => { :form => form } %>
6
+ <% form.buttons do %>
7
+ <%= form.commit_button "Sign up" %>
8
+ <% end %>
9
+ <% end %>
10
+
@@ -1,32 +1,94 @@
1
1
  module Clearance
2
2
  module Authentication
3
3
 
4
- def self.included(controller)
4
+ def self.included(controller) # :nodoc:
5
5
  controller.send(:include, InstanceMethods)
6
6
 
7
7
  controller.class_eval do
8
- helper_method :current_user
9
- helper_method :signed_in?
10
-
11
- hide_action :current_user, :signed_in?
8
+ helper_method :current_user, :signed_in?, :signed_out?
9
+ hide_action :current_user, :signed_in?, :signed_out?
12
10
  end
13
11
  end
14
12
 
15
13
  module InstanceMethods
14
+ # User in the current session or remember me cookie
15
+ #
16
+ # @return [User, nil]
16
17
  def current_user
17
18
  @_current_user ||= (user_from_cookie || user_from_session)
18
19
  end
19
20
 
21
+ # Is the current user signed in?
22
+ #
23
+ # @return [true, false]
20
24
  def signed_in?
21
25
  ! current_user.nil?
22
26
  end
23
27
 
24
- protected
28
+ # Is the current user signed out?
29
+ #
30
+ # @return [true, false]
31
+ def signed_out?
32
+ current_user.nil?
33
+ end
25
34
 
35
+ # Deny the user access if they are signed out.
36
+ #
37
+ # @example
38
+ # before_filter :authenticate
26
39
  def authenticate
27
40
  deny_access unless signed_in?
28
41
  end
29
42
 
43
+ # Sign user in to session.
44
+ #
45
+ # @param [User]
46
+ #
47
+ # @example
48
+ # sign_in(@user)
49
+ def sign_in(user)
50
+ if user
51
+ session[:user_id] = user.id
52
+ end
53
+ end
54
+
55
+ # Remember user with cookie.
56
+ #
57
+ # @param [User]
58
+ #
59
+ # @example
60
+ # remember(@user)
61
+ def remember(user)
62
+ user.remember_me!
63
+ cookies[:remember_token] = { :value => user.token,
64
+ :expires => user.token_expires_at }
65
+ end
66
+
67
+ # Forget user. Should only be used if developer overrides sign out.
68
+ #
69
+ # @param [User]
70
+ #
71
+ # @example
72
+ # forget(@user)
73
+ def forget(user)
74
+ user.forget_me! if user
75
+ cookies.delete(:remember_token)
76
+ reset_session
77
+ end
78
+
79
+ # Store the current location.
80
+ # Display a flash message if included.
81
+ # Redirect to sign in.
82
+ #
83
+ # @param [String] optional flash message to display to denied user
84
+ def deny_access(flash_message = nil)
85
+ store_location
86
+ flash[:failure] = flash_message if flash_message
87
+ redirect_to(sign_in_url)
88
+ end
89
+
90
+ protected
91
+
30
92
  def user_from_session
31
93
  if session[:user_id]
32
94
  return nil unless user = ::User.find_by_id(session[:user_id])
@@ -46,26 +108,12 @@ module Clearance
46
108
  sign_in(user)
47
109
  end
48
110
 
49
- def sign_in(user)
50
- if user
51
- session[:user_id] = user.id
52
- end
53
- end
54
-
55
111
  def remember?
56
112
  params[:session] && params[:session][:remember_me] == "1"
57
113
  end
58
114
 
59
- def remember(user)
60
- user.remember_me!
61
- cookies[:remember_token] = { :value => user.token,
62
- :expires => user.token_expires_at }
63
- end
64
-
65
- def forget(user)
66
- user.forget_me! if user
67
- cookies.delete(:remember_token)
68
- reset_session
115
+ def store_location
116
+ session[:return_to] = request.request_uri if request.get?
69
117
  end
70
118
 
71
119
  def redirect_back_or(default)
@@ -84,16 +132,6 @@ module Clearance
84
132
  def redirect_to_root
85
133
  redirect_to(root_url)
86
134
  end
87
-
88
- def store_location
89
- session[:return_to] = request.request_uri if request.get?
90
- end
91
-
92
- def deny_access(flash_message = nil, opts = {})
93
- store_location
94
- flash[:failure] = flash_message if flash_message
95
- redirect_to(signin_url)
96
- end
97
135
  end
98
136
 
99
137
  end
@@ -3,6 +3,23 @@ require 'digest/sha1'
3
3
  module Clearance
4
4
  module User
5
5
 
6
+ # Hook for all Clearance::User modules.
7
+ #
8
+ # If you need to override parts of Clearance::User,
9
+ # extend and include à la carte.
10
+ #
11
+ # @example
12
+ # extend ClassMethods
13
+ # include InstanceMethods
14
+ # include AttrAccessor
15
+ # include Callbacks
16
+ #
17
+ # @see ClassMethods
18
+ # @see InstanceMethods
19
+ # @see AttrAccessible
20
+ # @see AttrAccessor
21
+ # @see Validations
22
+ # @see Callbacks
6
23
  def self.included(model)
7
24
  model.extend(ClassMethods)
8
25
 
@@ -14,14 +31,26 @@ module Clearance
14
31
  end
15
32
 
16
33
  module AttrAccessible
34
+ # Hook for attr_accessible white list.
35
+ #
36
+ # :email, :password, :password_confirmation
37
+ #
38
+ # Append other attributes that must be mass-assigned in your model.
39
+ #
40
+ # @example
41
+ # include Clearance::User
42
+ # attr_accessible :location, :gender
17
43
  def self.included(model)
18
44
  model.class_eval do
19
- attr_accessible :email, :password, :password_confirmation, :invitation_token
45
+ attr_accessible :email, :password, :password_confirmation
20
46
  end
21
47
  end
22
48
  end
23
49
 
24
50
  module AttrAccessor
51
+ # Hook for attr_accessor virtual attributes.
52
+ #
53
+ # :password, :password_confirmation
25
54
  def self.included(model)
26
55
  model.class_eval do
27
56
  attr_accessor :password, :password_confirmation
@@ -30,6 +59,12 @@ module Clearance
30
59
  end
31
60
 
32
61
  module Validations
62
+ # Hook for validations.
63
+ #
64
+ # :email must be present, unique, formatted
65
+ #
66
+ # If password is required,
67
+ # :password must be present, confirmed
33
68
  def self.included(model)
34
69
  model.class_eval do
35
70
  validates_presence_of :email
@@ -38,69 +73,91 @@ module Clearance
38
73
 
39
74
  validates_presence_of :password, :if => :password_required?
40
75
  validates_confirmation_of :password, :if => :password_required?
41
-
42
- validates_presence_of :invitation_id, :message => 'is required', :if => Proc.new { |u| u.new_record?}
43
- validates_uniqueness_of :invitation_id, :if => Proc.new { |u| u.new_record?}
44
76
  end
45
77
  end
46
78
  end
47
79
 
48
80
  module Callbacks
81
+ # Hook for callbacks.
82
+ #
83
+ # salt, token, password encryption are handled before_save.
49
84
  def self.included(model)
50
85
  model.class_eval do
51
- before_save :initialize_salt, :encrypt_password, :initialize_token, :downcase_email
52
- before_create :set_invitation_limit, :redeemed
86
+ before_save :initialize_salt, :encrypt_password, :initialize_token
53
87
  end
54
88
  end
55
89
  end
56
90
 
57
91
  module InstanceMethods
92
+ # Am I authenticated with given password?
93
+ #
94
+ # @param [String] plain-text password
95
+ # @return [true, false]
96
+ # @example
97
+ # user.authenticated?('password')
58
98
  def authenticated?(password)
59
99
  encrypted_password == encrypt(password)
60
100
  end
61
101
 
62
- def encrypt(string)
63
- generate_hash("--#{salt}--#{string}--")
64
- end
65
-
102
+ # Am I remembered?
103
+ #
104
+ # @return [true, false]
105
+ # @example
106
+ # user.remember?
66
107
  def remember?
67
108
  token_expires_at && Time.now.utc < token_expires_at
68
109
  end
69
110
 
111
+ # Remember me for two weeks.
112
+ #
113
+ # @example
114
+ # user.remember_me!
115
+ # cookies[:remember_token] = { :value => user.token,
116
+ # :expires => user.token_expires_at }
70
117
  def remember_me!
71
118
  remember_me_until! 2.weeks.from_now.utc
72
119
  end
73
120
 
121
+ # Forget me.
122
+ #
123
+ # @example
124
+ # user.forget_me!
74
125
  def forget_me!
75
126
  clear_token
76
127
  save(false)
77
128
  end
78
129
 
130
+ # Confirm my email.
131
+ #
132
+ # @example
133
+ # user.confirm_email!
79
134
  def confirm_email!
80
- self.email_confirmed = true
81
- self.token = nil
135
+ self.email_confirmed = true
136
+ self.token = nil
82
137
  save(false)
83
138
  end
84
139
 
140
+ # Mark my account as forgotten password.
141
+ #
142
+ # @example
143
+ # user.forgot_password!
85
144
  def forgot_password!
86
145
  generate_token
87
146
  save(false)
88
147
  end
89
148
 
149
+ # Update my password.
150
+ #
151
+ # @param [String, String] password and password confirmation
152
+ # @return [true, false] password was updated or not
153
+ # @example
154
+ # user.update_password('new-password', 'new-password')
90
155
  def update_password(new_password, new_password_confirmation)
91
156
  self.password = new_password
92
157
  self.password_confirmation = new_password_confirmation
93
158
  clear_token if valid?
94
159
  save
95
160
  end
96
-
97
- def invitation_token
98
- invitation.token if invitation
99
- end
100
-
101
- def invitation_token=(token)
102
- self.invitation = ::Invitation.find_by_token(token)
103
- end
104
161
 
105
162
  protected
106
163
 
@@ -119,6 +176,10 @@ module Clearance
119
176
  self.encrypted_password = encrypt(password)
120
177
  end
121
178
 
179
+ def encrypt(string)
180
+ generate_hash("--#{salt}--#{string}--")
181
+ end
182
+
122
183
  def generate_token
123
184
  self.token = encrypt("--#{Time.now.utc.to_s}--#{password}--")
124
185
  self.token_expires_at = nil
@@ -142,21 +203,15 @@ module Clearance
142
203
  self.token = encrypt("--#{token_expires_at}--#{password}--")
143
204
  save(false)
144
205
  end
145
-
146
- def downcase_email
147
- self.email = email.to_s.downcase
148
- end
149
-
150
- def set_invitation_limit
151
- self.invitation_limit = 5
152
- end
153
-
154
- def redeemed
155
- invitation.redeemed! if invitation
156
- end
157
206
  end
158
207
 
159
208
  module ClassMethods
209
+ # Authenticate with email and password.
210
+ #
211
+ # @param [String, String] email and password
212
+ # @return [User, nil] authenticated user or nil
213
+ # @example
214
+ # User.authenticate("email@example.com", "password")
160
215
  def authenticate(email, password)
161
216
  return nil unless user = find_by_email(email)
162
217
  return user if user.authenticated?(password)