devise-multi_email 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|