devise-multi_email 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -2
- data/README.md +27 -3
- data/devise-multi_email.gemspec +2 -2
- data/lib/devise/multi_email.rb +42 -20
- data/lib/devise/multi_email/association_manager.rb +17 -4
- data/lib/devise/multi_email/email_model_manager.rb +3 -5
- data/lib/devise/multi_email/models/authenticatable.rb +32 -7
- data/lib/devise/multi_email/models/confirmable.rb +12 -12
- data/lib/devise/multi_email/models/validatable.rb +3 -7
- data/lib/devise/multi_email/parent_model_extensions.rb +2 -1
- data/lib/devise/multi_email/parent_model_manager.rb +58 -23
- data/lib/devise/multi_email/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ba4375a3b35995bdfa11564cc3f2a4e879d9731
|
4
|
+
data.tar.gz: 32c1c262c80fbfa87ef8819e40e0821ecc8ce822
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9926f6af62664391396203089a60f9a0876d8b3548898ee5148626e963d4c5bd818ff8db4d20c934cf3d7672f3886274fe68e75465a08cf1fa560b84060cdd61
|
7
|
+
data.tar.gz: 711a2674cee47fb189a46220adf4d32b334609f74ad490016bc4cd87d387ac9c836b5e1eae2165d5478d747e6b0c58971b75c54bad3f522487ca47de349ac063
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
### 2.0.1 - 2017-05-16
|
2
|
+
|
3
|
+
* Refactored to simplify some logic and start moving toward mimicking default Devise lifecycle behavior
|
4
|
+
* Added `Devise::MultiEmail.only_login_with_primary_email` option to restrict login to only primary emails
|
5
|
+
* Added `Devise::MultiEmail.autosave_emails` option to automatically enable `autosave` on "emails" association
|
6
|
+
|
1
7
|
### 2.0.0 - 2017-05-12
|
2
8
|
|
3
|
-
* New `Devise::MultiEmail#configure` setup with options for `user` and `emails` associations and `
|
9
|
+
* New `Devise::MultiEmail#configure` setup with options for `user` and `emails` associations and `primary_email_record` method names
|
4
10
|
* Refactor to expose `_multi_email_*` prefixed methods on models
|
5
|
-
* New `primary_email` method to get primary email record (however, can be configured as `primary_email_record` for backwards-compatibility)
|
6
11
|
* Changed logic when changing an email address to look up existing email record, otherwise creating a new one, then marking it "primary"
|
7
12
|
* Changed logic when changing an email address to mark all others as `primary = false`
|
8
13
|
* Changed logic when changing an email address to `nil` to mark as `primary = false` rather than deleting records
|
data/README.md
CHANGED
@@ -49,6 +49,31 @@ create_table :emails do |t|
|
|
49
49
|
end
|
50
50
|
```
|
51
51
|
|
52
|
+
You can choose whether or not users can login with an email address that is not the primary email address.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
Devise::MultiEmail.configure do |config|
|
56
|
+
# Default is `false`
|
57
|
+
config.only_login_with_primary_email = true
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
The `autosave` is automatically enabled on the `emails` association by default. This is to ensure the `primary`
|
62
|
+
flag is persisted for all emails when the primary email is changed. When `autosave` is not enabled on the association,
|
63
|
+
only new emails are saved when the parent (e.g. `User`) record is saved. (Updates to already-persisted email records
|
64
|
+
are not saved.)
|
65
|
+
|
66
|
+
If you don't want `autosave` to be enabled automatically, you can disable this feature. What this will do is
|
67
|
+
enable alternative behavior, which adds an `after_save` callback to the parent record and calls `email.save` on each email
|
68
|
+
record where the `primary` value has changed.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
Devise::MultiEmail.configure do |config|
|
72
|
+
# Default is `true`
|
73
|
+
config.autosave_emails = false
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
52
77
|
### Configure custom association names
|
53
78
|
|
54
79
|
You may not want to use the association `user.emails` or `email.users`. You can customize the name of the associations used. Add your custom configurations to an initializer file such as `config/initializers/devise-multi_email.rb`.
|
@@ -61,9 +86,8 @@ Devise::MultiEmail.configure do |config|
|
|
61
86
|
config.parent_association_name = :team
|
62
87
|
# Default is :emails for parent (e.g. User) model
|
63
88
|
config.emails_association_name = :email_addresses
|
64
|
-
#
|
65
|
-
|
66
|
-
config.primary_email_method_name = :primary_email_record
|
89
|
+
# Default is :primary_email_record
|
90
|
+
config.primary_email_method_name = :primary_email
|
67
91
|
end
|
68
92
|
|
69
93
|
# Example use of custom association names
|
data/devise-multi_email.gemspec
CHANGED
@@ -6,8 +6,8 @@ require 'devise/multi_email/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'devise-multi_email'
|
8
8
|
spec.version = Devise::MultiEmail::VERSION
|
9
|
-
spec.authors = ['ALLEN WANG QIANG']
|
10
|
-
spec.email = ['rovingbreeze@gmail.com']
|
9
|
+
spec.authors = ['ALLEN WANG QIANG', 'Joel Van Horn']
|
10
|
+
spec.email = ['rovingbreeze@gmail.com', 'joel@joelvanhorn.com']
|
11
11
|
|
12
12
|
spec.summary = %q{Let devise support multiple emails.}
|
13
13
|
spec.description = %q{Devise authenticatable, confirmable and validatable with multiple emails.}
|
data/lib/devise/multi_email.rb
CHANGED
@@ -3,32 +3,54 @@ require 'devise'
|
|
3
3
|
|
4
4
|
module Devise
|
5
5
|
module MultiEmail
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class << self
|
7
|
+
def configure(&block)
|
8
|
+
yield self
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
@parent_association_name ||= :user
|
12
|
-
end
|
11
|
+
@autosave_emails = false
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
def autosave_emails?
|
14
|
+
@autosave_emails == true
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
def autosave_emails=(value)
|
18
|
+
@autosave_emails = (value == true)
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
@emails_association_name = name.try(:to_sym)
|
24
|
-
end
|
21
|
+
@only_login_with_primary_email = false
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
def only_login_with_primary_email?
|
24
|
+
@only_login_with_primary_email == true
|
25
|
+
end
|
26
|
+
|
27
|
+
def only_login_with_primary_email=(value)
|
28
|
+
@only_login_with_primary_email = (value == true)
|
29
|
+
end
|
30
|
+
|
31
|
+
def parent_association_name
|
32
|
+
@parent_association_name ||= :user
|
33
|
+
end
|
34
|
+
|
35
|
+
def parent_association_name=(name)
|
36
|
+
@parent_association_name = name.try(:to_sym)
|
37
|
+
end
|
38
|
+
|
39
|
+
def emails_association_name
|
40
|
+
@emails_association_name ||= :emails
|
41
|
+
end
|
42
|
+
|
43
|
+
def emails_association_name=(name)
|
44
|
+
@emails_association_name = name.try(:to_sym)
|
45
|
+
end
|
46
|
+
|
47
|
+
def primary_email_method_name
|
48
|
+
@primary_email_method_name ||= :primary_email_record
|
49
|
+
end
|
29
50
|
|
30
|
-
|
31
|
-
|
51
|
+
def primary_email_method_name=(name)
|
52
|
+
@primary_email_method_name = name.try(:to_sym)
|
53
|
+
end
|
32
54
|
end
|
33
55
|
end
|
34
56
|
end
|
@@ -14,16 +14,29 @@ module Devise
|
|
14
14
|
model_class.__send__ :include, mod
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
# Specify a block with alternative behavior which should be
|
18
|
+
# run when `autosave` is not enabled.
|
19
|
+
def configure_autosave!(&block)
|
20
|
+
unless autosave_enabled?
|
21
|
+
if Devise::MultiEmail.autosave_emails?
|
22
|
+
reflection.autosave = true
|
23
|
+
else
|
24
|
+
yield if block_given?
|
25
|
+
end
|
20
26
|
end
|
27
|
+
end
|
21
28
|
|
29
|
+
def autosave_enabled?
|
30
|
+
reflection.options[:autosave] == true
|
31
|
+
end
|
32
|
+
|
33
|
+
def model_class
|
22
34
|
@model_class ||= reflection.class_name.constantize
|
23
35
|
end
|
24
36
|
|
25
37
|
def reflection
|
26
|
-
@reflection ||= @klass.reflect_on_association(name)
|
38
|
+
@reflection ||= @klass.reflect_on_association(name) ||
|
39
|
+
raise("#{@klass}##{name} association not found: It might be because your declaration is after `devise :multi_email_confirmable`.")
|
27
40
|
end
|
28
41
|
end
|
29
42
|
end
|
@@ -4,14 +4,12 @@ module Devise
|
|
4
4
|
module MultiEmail
|
5
5
|
class EmailModelManager
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(record)
|
10
|
-
@record = record
|
7
|
+
def initialize(email_record)
|
8
|
+
@email_record = email_record
|
11
9
|
end
|
12
10
|
|
13
11
|
def parent
|
14
|
-
|
12
|
+
@email_record.__send__(@email_record.class.multi_email_association.name)
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
@@ -29,6 +29,7 @@ module Devise
|
|
29
29
|
extend ActiveSupport::Concern
|
30
30
|
|
31
31
|
included do
|
32
|
+
multi_email_association.configure_autosave!{ include AuthenticatableAutosaveExtensions }
|
32
33
|
multi_email_association.include_module(EmailAuthenticatable)
|
33
34
|
end
|
34
35
|
|
@@ -36,26 +37,40 @@ module Devise
|
|
36
37
|
|
37
38
|
# Gets the primary email address of the user.
|
38
39
|
def email
|
39
|
-
multi_email.
|
40
|
+
multi_email.primary_email_record.try(:email)
|
40
41
|
end
|
41
42
|
|
42
43
|
# Sets the default email address of the user.
|
43
44
|
def email=(new_email)
|
44
|
-
multi_email.change_primary_email_to(new_email)
|
45
|
+
multi_email.change_primary_email_to(new_email, allow_unconfirmed: true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module AuthenticatableAutosaveExtensions
|
50
|
+
extend ActiveSupport::Concern
|
51
|
+
|
52
|
+
included do
|
53
|
+
# Toggle `primary` value for all emails if `autosave` is not on
|
54
|
+
after_save do
|
55
|
+
multi_email.filtered_emails.each do |email|
|
56
|
+
# update value in database without persisting any other changes
|
57
|
+
email.save if email.changes.key?(:primary)
|
58
|
+
end
|
59
|
+
end
|
45
60
|
end
|
46
61
|
end
|
47
62
|
|
48
63
|
module ClassMethods
|
49
64
|
def find_first_by_auth_conditions(tainted_conditions, opts = {})
|
50
65
|
filtered_conditions = devise_parameter_filter.filter(tainted_conditions.dup)
|
51
|
-
|
66
|
+
criteria = filtered_conditions.extract!(:email, :unconfirmed_email)
|
52
67
|
|
53
|
-
if
|
68
|
+
if criteria.keys.any?
|
54
69
|
conditions = filtered_conditions.to_h.merge(opts).
|
55
|
-
reverse_merge(
|
70
|
+
reverse_merge(build_conditions(criteria))
|
56
71
|
|
57
72
|
resource = joins(multi_email_association.name).find_by(conditions)
|
58
|
-
resource.current_login_email =
|
73
|
+
resource.current_login_email = criteria.values.first if resource
|
59
74
|
resource
|
60
75
|
else
|
61
76
|
super(tainted_conditions, opts)
|
@@ -63,7 +78,17 @@ module Devise
|
|
63
78
|
end
|
64
79
|
|
65
80
|
def find_by_email(email)
|
66
|
-
joins(multi_email_association.name).where(
|
81
|
+
joins(multi_email_association.name).where(build_conditions email: email).first
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_conditions(criteria)
|
85
|
+
criteria = devise_parameter_filter.filter(criteria)
|
86
|
+
# match the primary email record if the `unconfirmed_email` column is specified
|
87
|
+
if Devise::MultiEmail.only_login_with_primary_email? || criteria[:unconfirmed_email]
|
88
|
+
criteria.merge!(primary: true)
|
89
|
+
end
|
90
|
+
|
91
|
+
{ multi_email_association.reflection.table_name.to_sym => criteria }
|
67
92
|
end
|
68
93
|
end
|
69
94
|
end
|
@@ -8,12 +8,12 @@ module Devise
|
|
8
8
|
included do
|
9
9
|
devise :confirmable
|
10
10
|
|
11
|
-
|
11
|
+
include ConfirmableExtensions
|
12
12
|
end
|
13
13
|
|
14
|
-
module
|
15
|
-
def
|
16
|
-
|
14
|
+
module ConfirmableExtensions
|
15
|
+
def confirmation_period_valid?
|
16
|
+
primary? ? super : false
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -45,9 +45,15 @@ module Devise
|
|
45
45
|
:confirmation_token, :confirmed_at, :confirmation_sent_at, :confirm, :confirmed?, :unconfirmed_email,
|
46
46
|
:reconfirmation_required?, :pending_reconfirmation?, to: :primary_email_record, allow_nil: true
|
47
47
|
|
48
|
+
# In case email updates are being postponed, don't change anything
|
49
|
+
# when the postpone feature tries to switch things back
|
50
|
+
def email=(new_email)
|
51
|
+
multi_email.change_primary_email_to(new_email, allow_unconfirmed: false)
|
52
|
+
end
|
53
|
+
|
48
54
|
# This need to be forwarded to the email that the user logged in with
|
49
55
|
def active_for_authentication?
|
50
|
-
login_email =
|
56
|
+
login_email = multi_email.login_email_record
|
51
57
|
|
52
58
|
if login_email && !login_email.primary?
|
53
59
|
super && login_email.active_for_authentication?
|
@@ -58,7 +64,7 @@ module Devise
|
|
58
64
|
|
59
65
|
# Shows email not confirmed instead of account inactive when the email that user used to login is not confirmed
|
60
66
|
def inactive_message
|
61
|
-
login_email =
|
67
|
+
login_email = multi_email.login_email_record
|
62
68
|
|
63
69
|
if login_email && !login_email.primary? && !login_email.confirmed?
|
64
70
|
:unconfirmed
|
@@ -88,12 +94,6 @@ module Devise
|
|
88
94
|
|
89
95
|
private
|
90
96
|
|
91
|
-
def current_login_email_record
|
92
|
-
if respond_to?(:current_login_email) && current_login_email
|
93
|
-
multi_email.emails.find_by(email: current_login_email)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
97
|
module ClassMethods
|
98
98
|
delegate :confirm_by_token, :send_confirmation_instructions, to: 'multi_email_association.model_class', allow_nil: false
|
99
99
|
end
|
@@ -61,12 +61,8 @@ module Devise
|
|
61
61
|
|
62
62
|
def propagate_email_errors
|
63
63
|
email_error_key = self.class.multi_email_association.name
|
64
|
-
|
65
|
-
|
66
|
-
email_error_key = "#{email_error_key}.email".to_sym
|
67
|
-
end
|
68
|
-
|
69
|
-
email_errors = errors.delete(email_error_key) || []
|
64
|
+
email_errors = errors.delete(email_error_key) ||
|
65
|
+
errors.delete("#{email_error_key}.email".to_sym) || []
|
70
66
|
|
71
67
|
email_errors.each do |error|
|
72
68
|
errors.add(:email, error)
|
@@ -80,7 +76,7 @@ module Devise
|
|
80
76
|
:validates_confirmation_of, :validates_length_of].freeze
|
81
77
|
|
82
78
|
def assert_validations_api!(base) #:nodoc:
|
83
|
-
unavailable_validations = VALIDATIONS.select{ |v| !base.respond_to?(v) }
|
79
|
+
unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
|
84
80
|
|
85
81
|
unless unavailable_validations.empty?
|
86
82
|
raise "Could not use :validatable module since #{base} does not respond " <<
|
@@ -8,9 +8,10 @@ module Devise
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
included do
|
11
|
+
multi_email_association.configure_autosave!
|
11
12
|
multi_email_association.include_module(EmailModelExtensions)
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
delegate Devise::MultiEmail.primary_email_method_name, to: :multi_email, allow_nil: false
|
15
16
|
|
16
17
|
def multi_email
|
@@ -4,51 +4,86 @@ module Devise
|
|
4
4
|
module MultiEmail
|
5
5
|
class ParentModelManager
|
6
6
|
|
7
|
-
|
7
|
+
def initialize(parent_record)
|
8
|
+
@parent_record = parent_record
|
9
|
+
end
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
+
def current_email_record
|
12
|
+
login_email_record || primary_email_record
|
11
13
|
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
def login_email_record
|
16
|
+
if @parent_record.current_login_email.present?
|
17
|
+
formatted_email = format_email(@parent_record.current_login_email)
|
18
|
+
filtered_emails.find { |item| item.email == formatted_email }
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
# Gets the primary email record.
|
19
|
-
def
|
23
|
+
def primary_email_record
|
20
24
|
filtered_emails.find(&:primary?)
|
21
25
|
end
|
22
|
-
alias_method Devise::MultiEmail.primary_email_method_name, :
|
23
|
-
|
24
|
-
def change_primary_email_to(new_email)
|
25
|
-
# Use Devise formatting settings for emails
|
26
|
-
formatted_email = record.class.send(:devise_parameter_filter).filter(email: new_email)[:email]
|
27
|
-
|
28
|
-
valid_emails = filtered_emails
|
26
|
+
alias_method Devise::MultiEmail.primary_email_method_name, :primary_email_record
|
29
27
|
|
28
|
+
# :allow_unconfirmed option sets this email record to primary
|
29
|
+
# :skip_confirmations option confirms this email record (without saving)
|
30
|
+
# @see `set_primary_record_to`
|
31
|
+
def change_primary_email_to(new_email, options = {})
|
30
32
|
# mark none as primary when set to nil
|
31
33
|
if new_email.nil?
|
32
|
-
|
34
|
+
filtered_emails.each { |item| item.primary = false }
|
33
35
|
|
34
36
|
# select or build an email record
|
35
37
|
else
|
36
|
-
record =
|
38
|
+
record = find_or_build_for_email(new_email)
|
37
39
|
|
38
|
-
|
39
|
-
record
|
40
|
-
valid_emails << record
|
40
|
+
if record.try(:confirmed?) || primary_email_record.nil? || options[:allow_unconfirmed]
|
41
|
+
set_primary_record_to(record, options)
|
41
42
|
end
|
42
|
-
|
43
|
-
# toggle the selected record as primary and others as not
|
44
|
-
valid_emails.each{ |other| other.primary = (other == record) }
|
45
43
|
end
|
46
44
|
|
47
45
|
record
|
48
46
|
end
|
49
47
|
|
48
|
+
# Use Devise formatting settings for emails
|
49
|
+
def format_email(email)
|
50
|
+
@parent_record.class.__send__(:devise_parameter_filter).filter(email: email)[:email]
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_or_build_for_email(email)
|
54
|
+
formatted_email = format_email(email)
|
55
|
+
record = filtered_emails.find { |item| item.email == formatted_email }
|
56
|
+
record || emails.build(email: formatted_email)
|
57
|
+
end
|
58
|
+
|
50
59
|
def emails
|
51
|
-
|
60
|
+
@parent_record.__send__(@parent_record.class.multi_email_association.name)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Gets the email records that have not been deleted
|
64
|
+
def filtered_emails(options = {})
|
65
|
+
emails.lazy.reject(&:destroyed?).reject(&:marked_for_destruction?).to_a
|
66
|
+
end
|
67
|
+
|
68
|
+
def confirmed_emails
|
69
|
+
filtered_emails.select { |record| record.try(:confirmed?) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def unconfirmed_emails
|
73
|
+
filtered_emails.reject { |record| record.try(:confirmed?) }
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
# :skip_confirmations option confirms this email record (without saving)
|
79
|
+
def set_primary_record_to(record, options = {})
|
80
|
+
# Toggle primary flag for all emails
|
81
|
+
filtered_emails.each { |other| other.primary = (other.email == record.email) }
|
82
|
+
|
83
|
+
if options[:skip_confirmations]
|
84
|
+
record.try(:skip_confirmation!)
|
85
|
+
record.try(:skip_reconfirmation!)
|
86
|
+
end
|
52
87
|
end
|
53
88
|
end
|
54
89
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devise-multi_email
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ALLEN WANG QIANG
|
8
|
+
- Joel Van Horn
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date: 2017-
|
12
|
+
date: 2017-06-27 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: devise
|
@@ -111,6 +112,7 @@ dependencies:
|
|
111
112
|
description: Devise authenticatable, confirmable and validatable with multiple emails.
|
112
113
|
email:
|
113
114
|
- rovingbreeze@gmail.com
|
115
|
+
- joel@joelvanhorn.com
|
114
116
|
executables: []
|
115
117
|
extensions: []
|
116
118
|
extra_rdoc_files: []
|