mongoid-devise 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/CHANGELOG.rdoc +333 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +260 -0
  4. data/Rakefile +53 -0
  5. data/TODO +2 -0
  6. data/app/controllers/confirmations_controller.rb +33 -0
  7. data/app/controllers/passwords_controller.rb +42 -0
  8. data/app/controllers/registrations_controller.rb +55 -0
  9. data/app/controllers/sessions_controller.rb +45 -0
  10. data/app/controllers/unlocks_controller.rb +33 -0
  11. data/app/models/devise_mailer.rb +68 -0
  12. data/app/views/confirmations/new.html.erb +12 -0
  13. data/app/views/devise_mailer/confirmation_instructions.html.erb +5 -0
  14. data/app/views/devise_mailer/reset_password_instructions.html.erb +8 -0
  15. data/app/views/devise_mailer/unlock_instructions.html.erb +7 -0
  16. data/app/views/passwords/edit.html.erb +16 -0
  17. data/app/views/passwords/new.html.erb +12 -0
  18. data/app/views/registrations/edit.html.erb +25 -0
  19. data/app/views/registrations/new.html.erb +17 -0
  20. data/app/views/sessions/new.html.erb +17 -0
  21. data/app/views/shared/_devise_links.erb +19 -0
  22. data/app/views/unlocks/new.html.erb +12 -0
  23. data/generators/devise/USAGE +5 -0
  24. data/generators/devise/devise_generator.rb +15 -0
  25. data/generators/devise/lib/route_devise.rb +32 -0
  26. data/generators/devise/templates/migration.rb +23 -0
  27. data/generators/devise/templates/model.rb +9 -0
  28. data/generators/devise_install/USAGE +3 -0
  29. data/generators/devise_install/devise_install_generator.rb +15 -0
  30. data/generators/devise_install/templates/README +18 -0
  31. data/generators/devise_install/templates/devise.rb +102 -0
  32. data/generators/devise_views/USAGE +3 -0
  33. data/generators/devise_views/devise_views_generator.rb +21 -0
  34. data/init.rb +2 -0
  35. data/lib/devise.rb +253 -0
  36. data/lib/devise/controllers/helpers.rb +200 -0
  37. data/lib/devise/controllers/internal_helpers.rb +129 -0
  38. data/lib/devise/controllers/url_helpers.rb +41 -0
  39. data/lib/devise/encryptors/authlogic_sha512.rb +21 -0
  40. data/lib/devise/encryptors/base.rb +20 -0
  41. data/lib/devise/encryptors/bcrypt.rb +21 -0
  42. data/lib/devise/encryptors/clearance_sha1.rb +19 -0
  43. data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
  44. data/lib/devise/encryptors/sha1.rb +27 -0
  45. data/lib/devise/encryptors/sha512.rb +27 -0
  46. data/lib/devise/failure_app.rb +65 -0
  47. data/lib/devise/hooks/activatable.rb +15 -0
  48. data/lib/devise/hooks/rememberable.rb +30 -0
  49. data/lib/devise/hooks/timeoutable.rb +18 -0
  50. data/lib/devise/hooks/trackable.rb +18 -0
  51. data/lib/devise/locales/en.yml +35 -0
  52. data/lib/devise/mapping.rb +131 -0
  53. data/lib/devise/models.rb +112 -0
  54. data/lib/devise/models/activatable.rb +16 -0
  55. data/lib/devise/models/authenticatable.rb +146 -0
  56. data/lib/devise/models/confirmable.rb +172 -0
  57. data/lib/devise/models/http_authenticatable.rb +21 -0
  58. data/lib/devise/models/lockable.rb +160 -0
  59. data/lib/devise/models/recoverable.rb +80 -0
  60. data/lib/devise/models/registerable.rb +8 -0
  61. data/lib/devise/models/rememberable.rb +94 -0
  62. data/lib/devise/models/timeoutable.rb +28 -0
  63. data/lib/devise/models/token_authenticatable.rb +89 -0
  64. data/lib/devise/models/trackable.rb +16 -0
  65. data/lib/devise/models/validatable.rb +48 -0
  66. data/lib/devise/orm/active_record.rb +41 -0
  67. data/lib/devise/orm/data_mapper.rb +83 -0
  68. data/lib/devise/orm/mongo_mapper.rb +51 -0
  69. data/lib/devise/orm/mongoid.rb +60 -0
  70. data/lib/devise/rails.rb +14 -0
  71. data/lib/devise/rails/routes.rb +125 -0
  72. data/lib/devise/rails/warden_compat.rb +25 -0
  73. data/lib/devise/schema.rb +65 -0
  74. data/lib/devise/strategies/authenticatable.rb +36 -0
  75. data/lib/devise/strategies/base.rb +16 -0
  76. data/lib/devise/strategies/http_authenticatable.rb +49 -0
  77. data/lib/devise/strategies/rememberable.rb +37 -0
  78. data/lib/devise/strategies/token_authenticatable.rb +37 -0
  79. data/lib/devise/test_helpers.rb +86 -0
  80. data/lib/devise/version.rb +3 -0
  81. data/test/controllers/helpers_test.rb +177 -0
  82. data/test/controllers/internal_helpers_test.rb +55 -0
  83. data/test/controllers/url_helpers_test.rb +47 -0
  84. data/test/devise_test.rb +69 -0
  85. data/test/encryptors_test.rb +31 -0
  86. data/test/failure_app_test.rb +44 -0
  87. data/test/integration/authenticatable_test.rb +271 -0
  88. data/test/integration/confirmable_test.rb +97 -0
  89. data/test/integration/http_authenticatable_test.rb +44 -0
  90. data/test/integration/lockable_test.rb +83 -0
  91. data/test/integration/recoverable_test.rb +141 -0
  92. data/test/integration/registerable_test.rb +130 -0
  93. data/test/integration/rememberable_test.rb +63 -0
  94. data/test/integration/timeoutable_test.rb +68 -0
  95. data/test/integration/token_authenticatable_test.rb +55 -0
  96. data/test/integration/trackable_test.rb +64 -0
  97. data/test/mailers/confirmation_instructions_test.rb +80 -0
  98. data/test/mailers/reset_password_instructions_test.rb +68 -0
  99. data/test/mailers/unlock_instructions_test.rb +62 -0
  100. data/test/mapping_test.rb +153 -0
  101. data/test/models/authenticatable_test.rb +180 -0
  102. data/test/models/confirmable_test.rb +228 -0
  103. data/test/models/lockable_test.rb +202 -0
  104. data/test/models/recoverable_test.rb +138 -0
  105. data/test/models/rememberable_test.rb +135 -0
  106. data/test/models/timeoutable_test.rb +28 -0
  107. data/test/models/token_authenticatable_test.rb +51 -0
  108. data/test/models/trackable_test.rb +5 -0
  109. data/test/models/validatable_test.rb +106 -0
  110. data/test/models_test.rb +56 -0
  111. data/test/orm/active_record.rb +31 -0
  112. data/test/orm/mongo_mapper.rb +20 -0
  113. data/test/orm/mongoid.rb +22 -0
  114. data/test/rails_app/app/active_record/admin.rb +7 -0
  115. data/test/rails_app/app/active_record/user.rb +7 -0
  116. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  117. data/test/rails_app/app/controllers/application_controller.rb +10 -0
  118. data/test/rails_app/app/controllers/home_controller.rb +4 -0
  119. data/test/rails_app/app/controllers/users_controller.rb +16 -0
  120. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  121. data/test/rails_app/app/mongo_mapper/admin.rb +9 -0
  122. data/test/rails_app/app/mongo_mapper/user.rb +8 -0
  123. data/test/rails_app/app/mongoid/admin.rb +9 -0
  124. data/test/rails_app/app/mongoid/user.rb +8 -0
  125. data/test/rails_app/config/boot.rb +110 -0
  126. data/test/rails_app/config/environment.rb +42 -0
  127. data/test/rails_app/config/environments/development.rb +17 -0
  128. data/test/rails_app/config/environments/production.rb +28 -0
  129. data/test/rails_app/config/environments/test.rb +28 -0
  130. data/test/rails_app/config/initializers/devise.rb +79 -0
  131. data/test/rails_app/config/initializers/inflections.rb +2 -0
  132. data/test/rails_app/config/initializers/new_rails_defaults.rb +24 -0
  133. data/test/rails_app/config/initializers/session_store.rb +15 -0
  134. data/test/rails_app/config/routes.rb +21 -0
  135. data/test/routes_test.rb +110 -0
  136. data/test/support/assertions_helper.rb +37 -0
  137. data/test/support/integration_tests_helper.rb +71 -0
  138. data/test/support/test_silencer.rb +5 -0
  139. data/test/support/tests_helper.rb +39 -0
  140. data/test/test_helper.rb +21 -0
  141. data/test/test_helpers_test.rb +57 -0
  142. metadata +216 -0
@@ -0,0 +1,22 @@
1
+ require "digest/sha1"
2
+
3
+ module Devise
4
+ module Encryptors
5
+ # = RestfulAuthenticationSha1
6
+ # Simulates Restful Authentication's default encryption mechanism.
7
+ # Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
8
+ # Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES. Should be set to 10 in
9
+ # the initializer to silumate the default behavior.
10
+ class RestfulAuthenticationSha1 < Base
11
+
12
+ # Gererates a default password digest based on salt, pepper and the
13
+ # incoming password.
14
+ def self.digest(password, stretches, salt, pepper)
15
+ digest = pepper
16
+ stretches.times { digest = Digest::SHA1.hexdigest([digest, salt, password, pepper].flatten.join('--')) }
17
+ digest
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ require "digest/sha1"
2
+
3
+ module Devise
4
+ module Encryptors
5
+ # = Sha1
6
+ # Uses the Sha1 hash algorithm to encrypt passwords.
7
+ class Sha1 < Base
8
+
9
+ # Gererates a default password digest based on stretches, salt, pepper and the
10
+ # incoming password.
11
+ def self.digest(password, stretches, salt, pepper)
12
+ digest = pepper
13
+ stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
14
+ digest
15
+ end
16
+
17
+ private
18
+
19
+ # Generate a SHA1 digest joining args. Generated token is something like
20
+ # --arg1--arg2--arg3--argN--
21
+ def self.secure_digest(*tokens)
22
+ ::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require "digest/sha2"
2
+
3
+ module Devise
4
+ module Encryptors
5
+ # = Sha512
6
+ # Uses the Sha512 hash algorithm to encrypt passwords.
7
+ class Sha512 < Base
8
+
9
+ # Gererates a default password digest based on salt, pepper and the
10
+ # incoming password.
11
+ def self.digest(password, stretches, salt, pepper)
12
+ digest = pepper
13
+ stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
14
+ digest
15
+ end
16
+
17
+ private
18
+
19
+ # Generate a Sha512 digest joining args. Generated token is something like
20
+ # --arg1--arg2--arg3--argN--
21
+ def self.secure_digest(*tokens)
22
+ ::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,65 @@
1
+ module Devise
2
+ # Failure application that will be called every time :warden is thrown from
3
+ # any strategy or hook. Responsible for redirect the user to the sign in
4
+ # page based on current scope and mapping. If no scope is given, redirect
5
+ # to the default_url.
6
+ class FailureApp
7
+ attr_reader :env
8
+ include Warden::Mixins::Common
9
+
10
+ cattr_accessor :default_url, :default_message, :instance_writer => false
11
+ @@default_message = :unauthenticated
12
+
13
+ def self.call(env)
14
+ new(env).respond!
15
+ end
16
+
17
+ def initialize(env)
18
+ @env = env
19
+ end
20
+
21
+ def respond!
22
+ options = @env['warden.options']
23
+ scope = options[:scope]
24
+
25
+ redirect_path = if mapping = Devise.mappings[scope]
26
+ "#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
27
+ else
28
+ "/#{default_url}"
29
+ end
30
+ query_string = query_string_for(options)
31
+ store_location!(scope)
32
+
33
+ headers = {}
34
+ headers["Location"] = redirect_path
35
+ headers["Location"] << "?" << query_string unless query_string.empty?
36
+ headers["Content-Type"] = 'text/plain'
37
+
38
+ [302, headers, ["You are being redirected to #{redirect_path}"]]
39
+ end
40
+
41
+ # Build the proper query string based on the given message.
42
+ def query_string_for(options)
43
+ message = @env['warden'].try(:message) || options[:message] || default_message
44
+
45
+ params = case message
46
+ when Symbol
47
+ { message => true }
48
+ when String
49
+ { :message => message }
50
+ else
51
+ {}
52
+ end
53
+
54
+ Rack::Utils.build_query(params)
55
+ end
56
+
57
+ # Stores requested uri to redirect the user after signing in. We cannot use
58
+ # scoped session provided by warden here, since the user is not authenticated
59
+ # yet, but we still need to store the uri based on scope, so different scopes
60
+ # would never use the same uri to redirect.
61
+ def store_location!(scope)
62
+ session[:"#{scope}.return_to"] = request.request_uri if request && request.get?
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ # Deny user access whenever his account is not active yet.
2
+ Warden::Manager.after_set_user do |record, warden, options|
3
+ if record && record.respond_to?(:active?) && !record.active?
4
+ scope = options[:scope]
5
+ warden.logout(scope)
6
+
7
+ # If winning strategy was set, this is being called after authenticate and
8
+ # there is no need to force a redirect.
9
+ if warden.winning_strategy
10
+ warden.winning_strategy.fail!(record.inactive_message)
11
+ else
12
+ throw :warden, :scope => scope, :message => record.inactive_message
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ # After authenticate hook to verify if the user in the given scope asked to be
2
+ # remembered while he does not sign out. Generates a new remember token for
3
+ # that specific user and adds a cookie with this user info to sign in this user
4
+ # automatically without asking for credentials. Refer to rememberable strategy
5
+ # for more info.
6
+ Warden::Manager.after_authentication do |record, warden, options|
7
+ scope = options[:scope]
8
+ remember_me = warden.params[scope].try(:fetch, :remember_me, nil)
9
+
10
+ if Devise::TRUE_VALUES.include?(remember_me) &&
11
+ warden.authenticated?(scope) && record.respond_to?(:remember_me!)
12
+ record.remember_me!
13
+
14
+ warden.response.set_cookie "remember_#{scope}_token", {
15
+ :value => record.class.serialize_into_cookie(record),
16
+ :expires => record.remember_expires_at,
17
+ :path => "/"
18
+ }
19
+ end
20
+ end
21
+
22
+ # Before logout hook to forget the user in the given scope, only if rememberable
23
+ # is activated for this scope. Also clear remember token to ensure the user
24
+ # won't be remembered again.
25
+ Warden::Manager.before_logout do |record, warden, scope|
26
+ if record.respond_to?(:forget_me!)
27
+ record.forget_me!
28
+ warden.response.delete_cookie "remember_#{scope}_token"
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ # Each time a record is set we check whether it's session has already timed out
2
+ # or not, based on last request time. If so, the record is logged out and
3
+ # redirected to the sign in page. Also, each time the request comes and the
4
+ # record is set, we set the last request time inside it's scoped session to
5
+ # verify timeout in the following request.
6
+ Warden::Manager.after_set_user do |record, warden, options|
7
+ scope = options[:scope]
8
+ if record && record.respond_to?(:timedout?) && warden.authenticated?(scope)
9
+ last_request_at = warden.session(scope)['last_request_at']
10
+
11
+ if record.timedout?(last_request_at)
12
+ warden.logout(scope)
13
+ throw :warden, :scope => scope, :message => :timeout
14
+ end
15
+
16
+ warden.session(scope)['last_request_at'] = Time.now.utc
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # After each sign in, update sign in time, sign in count and sign in IP.
2
+ Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
3
+ scope = options[:scope]
4
+ if Devise.mappings[scope].try(:trackable?) && warden.authenticated?(scope)
5
+ old_current, new_current = record.current_sign_in_at, Time.now
6
+ record.last_sign_in_at = old_current || new_current
7
+ record.current_sign_in_at = new_current
8
+
9
+ old_current, new_current = record.current_sign_in_ip, warden.request.remote_ip
10
+ record.last_sign_in_ip = old_current || new_current
11
+ record.current_sign_in_ip = new_current
12
+
13
+ record.sign_in_count ||= 0
14
+ record.sign_in_count += 1
15
+
16
+ record.save(false)
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ en:
2
+ devise:
3
+ sessions:
4
+ link: 'Sign in'
5
+ signed_in: 'Signed in successfully.'
6
+ signed_out: 'Signed out successfully.'
7
+ unauthenticated: 'You need to sign in or sign up before continuing.'
8
+ unconfirmed: 'You have to confirm your account before continuing.'
9
+ locked: 'Your account is locked.'
10
+ invalid: 'Invalid email or password.'
11
+ invalid_token: 'Invalid authentication token.'
12
+ timeout: 'Your session expired, please sign in again to continue.'
13
+ inactive: 'Your account was not activated yet.'
14
+ passwords:
15
+ link: 'Forgot password?'
16
+ send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
17
+ updated: 'Your password was changed successfully. You are now signed in.'
18
+ confirmations:
19
+ link: "Didn't receive confirmation instructions?"
20
+ send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
21
+ confirmed: 'Your account was successfully confirmed. You are now signed in.'
22
+ registrations:
23
+ link: 'Sign up'
24
+ signed_up: 'You have signed up successfully.'
25
+ updated: 'You updated your account successfully.'
26
+ destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
27
+ unlocks:
28
+ link: "Didn't receive unlock instructions?"
29
+ send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
30
+ unlocked: 'Your account was successfully unlocked. You are now signed in.'
31
+ mailer:
32
+ confirmation_instructions: 'Confirmation instructions'
33
+ reset_password_instructions: 'Reset password instructions'
34
+ unlock_instructions: 'Unlock Instructions'
35
+
@@ -0,0 +1,131 @@
1
+ module Devise
2
+ # Responsible for handling devise mappings and routes configuration. Each
3
+ # resource configured by devise_for in routes is actually creating a mapping
4
+ # object. You can refer to devise_for in routes for usage options.
5
+ #
6
+ # The required value in devise_for is actually not used internally, but it's
7
+ # inflected to find all other values.
8
+ #
9
+ # map.devise_for :users
10
+ # mapping = Devise.mappings[:user]
11
+ #
12
+ # mapping.name #=> :user
13
+ # # is the scope used in controllers and warden, given in the route as :singular.
14
+ #
15
+ # mapping.as #=> "users"
16
+ # # how the mapping should be search in the path, given in the route as :as.
17
+ #
18
+ # mapping.to #=> User
19
+ # # is the class to be loaded from routes, given in the route as :class_name.
20
+ #
21
+ # mapping.for #=> [:authenticatable]
22
+ # # is the modules included in the class
23
+ #
24
+ class Mapping #:nodoc:
25
+ attr_reader :name, :as, :path_names, :path_prefix, :route_options
26
+
27
+ # Loop through all mappings looking for a map that matches with the requested
28
+ # path (ie /users/sign_in). If a path prefix is given, it's taken into account.
29
+ def self.find_by_path(path)
30
+ Devise.mappings.each_value do |mapping|
31
+ route = path.split("/")[mapping.as_position]
32
+ return mapping if route && mapping.as == route.to_sym
33
+ end
34
+ nil
35
+ end
36
+
37
+ # Find a mapping by a given class. It takes into account single table inheritance as well.
38
+ def self.find_by_class(klass)
39
+ Devise.mappings.each_value do |mapping|
40
+ return mapping if klass <= mapping.to
41
+ end
42
+ nil
43
+ end
44
+
45
+ # Receives an object and find a scope for it. If a scope cannot be found,
46
+ # raises an error. If a symbol is given, it's considered to be the scope.
47
+ def self.find_scope!(duck)
48
+ case duck
49
+ when String, Symbol
50
+ duck
51
+ else
52
+ klass = duck.is_a?(Class) ? duck : duck.class
53
+ mapping = Devise::Mapping.find_by_class(klass)
54
+ raise "Could not find a valid mapping for #{duck}" unless mapping
55
+ mapping.name
56
+ end
57
+ end
58
+
59
+ # Default url options which can be used as prefix.
60
+ def self.default_url_options
61
+ {}
62
+ end
63
+
64
+ def initialize(name, options) #:nodoc:
65
+ @as = (options.delete(:as) || name).to_sym
66
+ @klass = (options.delete(:class_name) || name.to_s.classify).to_s
67
+ @name = (options.delete(:scope) || name.to_s.singularize).to_sym
68
+
69
+ @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
70
+ @route_options = options || {}
71
+
72
+ @path_names = Hash.new { |h,k| h[k] = k.to_s }
73
+ @path_names.merge!(options.delete(:path_names) || {})
74
+ end
75
+
76
+ # Return modules for the mapping.
77
+ def for
78
+ @for ||= to.devise_modules
79
+ end
80
+
81
+ # Reload mapped class each time when cache_classes is false.
82
+ def to
83
+ return @to if @to
84
+ klass = @klass.constantize
85
+ @to = klass if Rails.configuration.cache_classes
86
+ klass
87
+ end
88
+
89
+ # Check if the respective controller has a module in the mapping class.
90
+ def allows?(controller)
91
+ (self.for & CONTROLLERS[controller.to_sym]).present?
92
+ end
93
+
94
+ # Return in which position in the path prefix devise should find the as mapping.
95
+ def as_position
96
+ self.path_prefix.count("/")
97
+ end
98
+
99
+ # Returns the raw path using path_prefix and as.
100
+ def raw_path
101
+ path_prefix + as.to_s
102
+ end
103
+
104
+ # Returns the parsed path taking into account the relative url root and raw path.
105
+ def parsed_path
106
+ returning (ActionController::Base.relative_url_root.to_s + raw_path) do |path|
107
+ self.class.default_url_options.each do |key, value|
108
+ path.gsub!(key.inspect, value.to_param)
109
+ end
110
+ end
111
+ end
112
+
113
+ # Create magic predicates for verifying what module is activated by this map.
114
+ # Example:
115
+ #
116
+ # def confirmable?
117
+ # self.for.include?(:confirmable)
118
+ # end
119
+ #
120
+ def self.register(*modules)
121
+ modules.each do |m|
122
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
123
+ def #{m}?
124
+ self.for.include?(:#{m})
125
+ end
126
+ METHOD
127
+ end
128
+ end
129
+ Devise::Mapping.register *ALL
130
+ end
131
+ end
@@ -0,0 +1,112 @@
1
+ module Devise
2
+ module Models
3
+ autoload :Activatable, 'devise/models/activatable'
4
+ autoload :Authenticatable, 'devise/models/authenticatable'
5
+ autoload :Confirmable, 'devise/models/confirmable'
6
+ autoload :Lockable, 'devise/models/lockable'
7
+ autoload :Recoverable, 'devise/models/recoverable'
8
+ autoload :Rememberable, 'devise/models/rememberable'
9
+ autoload :Registerable, 'devise/models/registerable'
10
+ autoload :Timeoutable, 'devise/models/timeoutable'
11
+ autoload :Trackable, 'devise/models/trackable'
12
+ autoload :Validatable, 'devise/models/validatable'
13
+
14
+ # Creates configuration values for Devise and for the given module.
15
+ #
16
+ # Devise::Models.config(Devise::Authenticable, :stretches, 10)
17
+ #
18
+ # The line above creates:
19
+ #
20
+ # 1) An accessor called Devise.stretches, which value is used by default;
21
+ #
22
+ # 2) Some class methods for your model Model.stretches and Model.stretches=
23
+ # which have higher priority than Devise.stretches;
24
+ #
25
+ # 3) And an instance method stretches.
26
+ #
27
+ # To add the class methods you need to have a module ClassMethods defined
28
+ # inside the given class.
29
+ #
30
+ def self.config(mod, *accessors) #:nodoc:
31
+ accessors.each do |accessor|
32
+ mod.class_eval <<-METHOD, __FILE__, __LINE__ + 1
33
+ def #{accessor}
34
+ if defined?(@#{accessor})
35
+ @#{accessor}
36
+ elsif superclass.respond_to?(:#{accessor})
37
+ superclass.#{accessor}
38
+ else
39
+ Devise.#{accessor}
40
+ end
41
+ end
42
+
43
+ def #{accessor}=(value)
44
+ @#{accessor} = value
45
+ end
46
+ METHOD
47
+ end
48
+ end
49
+
50
+ # Include the chosen devise modules in your model:
51
+ #
52
+ # devise :authenticatable, :confirmable, :recoverable
53
+ #
54
+ # You can also give any of the devise configuration values in form of a hash,
55
+ # with specific values for this model. Please check your Devise initializer
56
+ # for a complete description on those values.
57
+ #
58
+ def devise(*modules)
59
+ raise "You need to give at least one Devise module" if modules.empty?
60
+ options = modules.extract_options!
61
+
62
+ @devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
63
+
64
+ Devise.orm_class.included_modules_hook(self) do
65
+ devise_modules.each do |m|
66
+ include Devise::Models.const_get(m.to_s.classify)
67
+ end
68
+
69
+ options.each { |key, value| send(:"#{key}=", value) }
70
+ end
71
+ end
72
+
73
+ # Stores all modules included inside the model, so we are able to verify
74
+ # which routes are needed.
75
+ def devise_modules
76
+ @devise_modules ||= []
77
+ end
78
+
79
+ # Find an initialize a record setting an error if it can't be found.
80
+ def find_or_initialize_with_error_by(attribute, value, error=:invalid)
81
+ if value.present?
82
+ conditions = { attribute => value }
83
+ record = find(:first, :conditions => conditions)
84
+ end
85
+
86
+ unless record
87
+ record = new
88
+
89
+ if value.present?
90
+ record.send(:"#{attribute}=", value)
91
+ else
92
+ error, skip_default = :blank, true
93
+ end
94
+
95
+ add_error_on(record, attribute, error, !skip_default)
96
+ end
97
+
98
+ record
99
+ end
100
+
101
+ # Wraps add error logic in a method that works for different frameworks.
102
+ def add_error_on(record, attribute, error, add_default=true)
103
+ options = add_default ? { :default => error.to_s.gsub("_", " ") } : {}
104
+
105
+ begin
106
+ record.errors.add(attribute, error, options)
107
+ rescue ArgumentError
108
+ record.errors.add(attribute, error.to_s.gsub("_", " "))
109
+ end
110
+ end
111
+ end
112
+ end