devise-multi_email 2.0.0 → 3.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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +13 -5
- data/CHANGELOG.md +14 -2
- data/README.md +32 -8
- data/devise-multi_email.gemspec +3 -3
- data/examples/rails5_app/Gemfile.lock +30 -26
- data/examples/rails5_app/app/models/email.rb +2 -0
- data/examples/rails5_app/db/migrate/20170307140813_devise_create_users.rb +2 -2
- data/examples/rails5_app/db/schema.rb +1 -1
- data/gemfiles/{rails_4_2.gemfile → rails_5_2.gemfile} +1 -1
- data/gemfiles/{rails_5_0.gemfile → rails_6_0.gemfile} +1 -1
- 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 +17 -12
- data/lib/devise/multi_email/models/validatable.rb +28 -11
- 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
- data/lib/devise/multi_email.rb +42 -20
- metadata +11 -11
- data/.simplecov +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2565382823ed8ab4f837160c54a053df8076a441e2f197802e152932b77634f6
|
4
|
+
data.tar.gz: 385ae0a6a352ce1343bea96e30a26fe1d8adf4939615deaf963df87c964b1909
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5faba7eb1f3f7e04d862954f7cc40d1bf94a08ad7ccbdff2383d6419cb4d5d13f02f3cdf98876275faf3a70be39f6345215440add7cbe888818ead45d05c3855
|
7
|
+
data.tar.gz: 73cf0ce8bff419342f769e61f0c3c7455fec01a3733c17afb6337e7e15e798b5de244971e5dd02889ba7c60cbf7c3f53c35e45ca8bca853f0548362353093529
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
language: ruby
|
2
|
+
|
2
3
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.
|
4
|
+
- 2.4
|
5
|
+
- 2.5
|
6
|
+
- 2.6
|
5
7
|
gemfile:
|
6
|
-
- gemfiles/
|
7
|
-
- gemfiles/
|
8
|
-
|
8
|
+
- gemfiles/rails_5_2.gemfile
|
9
|
+
- gemfiles/rails_5_1.gemfile
|
10
|
+
- gemfiles/rails_6_0.gemfile
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
exclude:
|
14
|
+
- rvm: 2.4
|
15
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
16
|
+
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,20 @@
|
|
1
|
+
### Unreleased
|
2
|
+
* Fix ActiveModel::Errors#keys deprecation in Rails 6.1
|
3
|
+
|
4
|
+
### 3.0.0 - 2019-11-06
|
5
|
+
* Deprecate the support of Rails 4 (although it might still work)
|
6
|
+
* Fix warnings in Rails 6
|
7
|
+
|
8
|
+
### 2.0.1 - 2017-05-16
|
9
|
+
|
10
|
+
* Refactored to simplify some logic and start moving toward mimicking default Devise lifecycle behavior
|
11
|
+
* Added `Devise::MultiEmail.only_login_with_primary_email` option to restrict login to only primary emails
|
12
|
+
* Added `Devise::MultiEmail.autosave_emails` option to automatically enable `autosave` on "emails" association
|
13
|
+
|
1
14
|
### 2.0.0 - 2017-05-12
|
2
15
|
|
3
|
-
* New `Devise::MultiEmail#configure` setup with options for `user` and `emails` associations and `
|
16
|
+
* New `Devise::MultiEmail#configure` setup with options for `user` and `emails` associations and `primary_email_record` method names
|
4
17
|
* 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
18
|
* Changed logic when changing an email address to look up existing email record, otherwise creating a new one, then marking it "primary"
|
7
19
|
* Changed logic when changing an email address to mark all others as `primary = false`
|
8
20
|
* Changed logic when changing an email address to `nil` to mark as `primary = false` rather than deleting records
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Letting [Devise](https://github.com/plataformatec/devise) support multiple email
|
|
10
10
|
|
11
11
|
## Getting Started
|
12
12
|
|
13
|
-
Add this line to your application's `Gemfile
|
13
|
+
Add this line to your application's `Gemfile`:
|
14
14
|
|
15
15
|
```ruby
|
16
16
|
gem 'devise-multi_email'
|
@@ -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
|
@@ -152,17 +176,17 @@ You can do `email.send_confirmation_instructions` for each email individually, b
|
|
152
176
|
|
153
177
|
## Wiki
|
154
178
|
|
155
|
-
[Migrating
|
179
|
+
[Migrating existing user records](https://github.com/allenwq/devise-multi_email/wiki/Migrating-existing-user-records)
|
156
180
|
|
157
181
|
## Development
|
158
182
|
|
159
|
-
After checking out the repo, run `
|
183
|
+
After checking out the repo, run `bundle install` to install dependencies.
|
160
184
|
|
161
|
-
|
185
|
+
Then, run `bundle exec rake` to run the RSpec test suite.
|
162
186
|
|
163
187
|
## Contributing
|
164
188
|
|
165
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
189
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/allenwq/devise-multi_email. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
166
190
|
|
167
191
|
|
168
192
|
## License
|
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.}
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_runtime_dependency 'devise'
|
23
23
|
|
24
|
-
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'bundler'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
26
|
spec.add_development_dependency 'rspec'
|
27
27
|
spec.add_development_dependency 'sqlite3'
|
@@ -39,15 +39,16 @@ GEM
|
|
39
39
|
minitest (~> 5.1)
|
40
40
|
tzinfo (~> 1.1)
|
41
41
|
arel (7.1.4)
|
42
|
-
bcrypt (3.1.
|
42
|
+
bcrypt (3.1.13)
|
43
43
|
builder (3.2.3)
|
44
44
|
byebug (9.0.6)
|
45
|
-
concurrent-ruby (1.
|
45
|
+
concurrent-ruby (1.1.5)
|
46
|
+
crass (1.0.5)
|
46
47
|
debug_inspector (0.0.2)
|
47
|
-
devise (4.
|
48
|
+
devise (4.7.1)
|
48
49
|
bcrypt (~> 3.0)
|
49
50
|
orm_adapter (~> 0.1)
|
50
|
-
railties (>= 4.1.0
|
51
|
+
railties (>= 4.1.0)
|
51
52
|
responders
|
52
53
|
warden (~> 1.2.3)
|
53
54
|
devise-encryptable (0.2.0)
|
@@ -56,10 +57,11 @@ GEM
|
|
56
57
|
devise
|
57
58
|
erubis (2.7.0)
|
58
59
|
execjs (2.7.0)
|
59
|
-
ffi (1.
|
60
|
+
ffi (1.11.1)
|
60
61
|
globalid (0.3.7)
|
61
62
|
activesupport (>= 4.1.0)
|
62
|
-
i18n (0.
|
63
|
+
i18n (0.9.5)
|
64
|
+
concurrent-ruby (~> 1.0)
|
63
65
|
jbuilder (2.6.3)
|
64
66
|
activesupport (>= 3.0.0, < 5.2)
|
65
67
|
multi_json (~> 1.2)
|
@@ -70,23 +72,24 @@ GEM
|
|
70
72
|
listen (3.0.8)
|
71
73
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
72
74
|
rb-inotify (~> 0.9, >= 0.9.7)
|
73
|
-
loofah (2.
|
75
|
+
loofah (2.3.1)
|
76
|
+
crass (~> 1.0.2)
|
74
77
|
nokogiri (>= 1.5.9)
|
75
78
|
mail (2.6.4)
|
76
79
|
mime-types (>= 1.16, < 4)
|
77
|
-
method_source (0.
|
80
|
+
method_source (0.9.2)
|
78
81
|
mime-types (3.1)
|
79
82
|
mime-types-data (~> 3.2015)
|
80
83
|
mime-types-data (3.2016.0521)
|
81
|
-
mini_portile2 (2.
|
82
|
-
minitest (5.
|
84
|
+
mini_portile2 (2.4.0)
|
85
|
+
minitest (5.12.2)
|
83
86
|
multi_json (1.12.1)
|
84
87
|
nio4r (2.0.0)
|
85
|
-
nokogiri (1.
|
86
|
-
mini_portile2 (~> 2.
|
88
|
+
nokogiri (1.10.5)
|
89
|
+
mini_portile2 (~> 2.4.0)
|
87
90
|
orm_adapter (0.5.0)
|
88
91
|
puma (3.7.1)
|
89
|
-
rack (2.0.
|
92
|
+
rack (2.0.7)
|
90
93
|
rack-test (0.6.3)
|
91
94
|
rack (>= 1.0)
|
92
95
|
rails (5.0.2)
|
@@ -101,23 +104,24 @@ GEM
|
|
101
104
|
bundler (>= 1.3.0, < 2.0)
|
102
105
|
railties (= 5.0.2)
|
103
106
|
sprockets-rails (>= 2.0.0)
|
104
|
-
rails-dom-testing (2.0.
|
105
|
-
activesupport (>= 4.2.0
|
106
|
-
nokogiri (
|
107
|
-
rails-html-sanitizer (1.0
|
108
|
-
loofah (~> 2.
|
107
|
+
rails-dom-testing (2.0.3)
|
108
|
+
activesupport (>= 4.2.0)
|
109
|
+
nokogiri (>= 1.6)
|
110
|
+
rails-html-sanitizer (1.3.0)
|
111
|
+
loofah (~> 2.3)
|
109
112
|
railties (5.0.2)
|
110
113
|
actionpack (= 5.0.2)
|
111
114
|
activesupport (= 5.0.2)
|
112
115
|
method_source
|
113
116
|
rake (>= 0.8.7)
|
114
117
|
thor (>= 0.18.1, < 2.0)
|
115
|
-
rake (
|
118
|
+
rake (13.0.0)
|
116
119
|
rb-fsevent (0.9.8)
|
117
120
|
rb-inotify (0.9.8)
|
118
121
|
ffi (>= 0.5.0)
|
119
|
-
responders (
|
120
|
-
|
122
|
+
responders (3.0.0)
|
123
|
+
actionpack (>= 5.0)
|
124
|
+
railties (>= 5.0)
|
121
125
|
sass (3.4.23)
|
122
126
|
sass-rails (5.0.6)
|
123
127
|
railties (>= 4.0.0, < 6)
|
@@ -130,7 +134,7 @@ GEM
|
|
130
134
|
spring-watcher-listen (2.0.1)
|
131
135
|
listen (>= 2.7, < 4.0)
|
132
136
|
spring (>= 1.2, < 3.0)
|
133
|
-
sprockets (3.7.
|
137
|
+
sprockets (3.7.2)
|
134
138
|
concurrent-ruby (~> 1.0)
|
135
139
|
rack (> 1, < 3)
|
136
140
|
sprockets-rails (3.2.0)
|
@@ -138,18 +142,18 @@ GEM
|
|
138
142
|
activesupport (>= 4.0)
|
139
143
|
sprockets (>= 3.0.0)
|
140
144
|
sqlite3 (1.3.13)
|
141
|
-
thor (0.
|
145
|
+
thor (0.20.3)
|
142
146
|
thread_safe (0.3.6)
|
143
147
|
tilt (2.0.6)
|
144
148
|
turbolinks (5.0.1)
|
145
149
|
turbolinks-source (~> 5)
|
146
150
|
turbolinks-source (5.0.0)
|
147
|
-
tzinfo (1.2.
|
151
|
+
tzinfo (1.2.5)
|
148
152
|
thread_safe (~> 0.1)
|
149
153
|
uglifier (3.1.4)
|
150
154
|
execjs (>= 0.3.0, < 3)
|
151
|
-
warden (1.2.
|
152
|
-
rack (>=
|
155
|
+
warden (1.2.8)
|
156
|
+
rack (>= 2.0.6)
|
153
157
|
web-console (3.4.0)
|
154
158
|
actionview (>= 5.0)
|
155
159
|
activemodel (>= 5.0)
|
@@ -30,7 +30,7 @@ class DeviseCreateUsers < ActiveRecord::Migration[5.0]
|
|
30
30
|
add_index :users, :reset_password_token, unique: true
|
31
31
|
# add_index :users, :unlock_token, unique: true
|
32
32
|
|
33
|
-
create_table :
|
33
|
+
create_table :user_emails do |t|
|
34
34
|
t.integer :user_id, null: false
|
35
35
|
t.string :email, null: false
|
36
36
|
t.string :unconfirmed_email
|
@@ -44,6 +44,6 @@ class DeviseCreateUsers < ActiveRecord::Migration[5.0]
|
|
44
44
|
t.timestamps null: false
|
45
45
|
end
|
46
46
|
|
47
|
-
add_index :
|
47
|
+
add_index :user_emails, :confirmation_token, unique: true
|
48
48
|
end
|
49
49
|
end
|
@@ -12,7 +12,7 @@
|
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 20170307145547) do
|
14
14
|
|
15
|
-
create_table "
|
15
|
+
create_table "user_emails", force: :cascade do |t|
|
16
16
|
t.integer "user_id", null: false
|
17
17
|
t.string "email", null: false
|
18
18
|
t.string "unconfirmed_email"
|
@@ -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
|
@@ -42,12 +42,18 @@ module Devise
|
|
42
42
|
|
43
43
|
# delegate before creating overriding methods
|
44
44
|
delegate :skip_confirmation!, :skip_confirmation_notification!, :skip_reconfirmation!, :confirmation_required?,
|
45
|
-
:confirmation_token, :confirmed_at, :confirmation_sent_at, :confirm, :confirmed?, :unconfirmed_email,
|
46
|
-
:reconfirmation_required?, :pending_reconfirmation?, to:
|
45
|
+
:confirmation_token, :confirmed_at, :confirmed_at=, :confirmation_sent_at, :confirm, :confirmed?, :unconfirmed_email,
|
46
|
+
:reconfirmation_required?, :pending_reconfirmation?, to: Devise::MultiEmail.primary_email_method_name, allow_nil: true
|
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: unconfirmed_access_possible?)
|
52
|
+
end
|
47
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,10 +94,9 @@ module Devise
|
|
88
94
|
|
89
95
|
private
|
90
96
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
end
|
97
|
+
def unconfirmed_access_possible?
|
98
|
+
Devise.allow_unconfirmed_access_for.nil? || \
|
99
|
+
Devise.allow_unconfirmed_access_for > 0.days
|
95
100
|
end
|
96
101
|
|
97
102
|
module ClassMethods
|
@@ -7,8 +7,13 @@ module Devise
|
|
7
7
|
|
8
8
|
included do
|
9
9
|
validates_presence_of :email, if: :email_required?
|
10
|
-
|
11
|
-
|
10
|
+
if Devise.activerecord51?
|
11
|
+
validates_uniqueness_of :email, allow_blank: true, case_sensitive: true, if: :will_save_change_to_email?
|
12
|
+
validates_format_of :email, with: email_regexp, allow_blank: true, if: :will_save_change_to_email?
|
13
|
+
else
|
14
|
+
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
|
15
|
+
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
def email_required?
|
@@ -60,19 +65,31 @@ module Devise
|
|
60
65
|
private
|
61
66
|
|
62
67
|
def propagate_email_errors
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
email_error_key = "#{email_error_key}.email".to_sym
|
68
|
+
association_name = self.class.multi_email_association.name
|
69
|
+
email_error_key = errors_attribute_names.detect do |key|
|
70
|
+
[association_name.to_s, "#{association_name}.email"].include?(key.to_s)
|
67
71
|
end
|
72
|
+
return unless email_error_key.present?
|
73
|
+
|
74
|
+
email_errors =
|
75
|
+
if errors.respond_to?(:details)
|
76
|
+
errors
|
77
|
+
.details[email_error_key]
|
78
|
+
.map { |e| e[:error] }
|
79
|
+
.zip(errors.delete(email_error_key) || [])
|
80
|
+
else
|
81
|
+
errors.delete(email_error_key)
|
82
|
+
end
|
68
83
|
|
69
|
-
email_errors
|
70
|
-
|
71
|
-
email_errors.each do |error|
|
72
|
-
errors.add(:email, error)
|
84
|
+
email_errors.each do |type, message|
|
85
|
+
errors.add(:email, type, message: message)
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
89
|
+
def errors_attribute_names
|
90
|
+
errors.respond_to?(:attribute_names) ? errors.attribute_names : errors.keys
|
91
|
+
end
|
92
|
+
|
76
93
|
module ClassMethods
|
77
94
|
|
78
95
|
# All validations used by this module.
|
@@ -80,7 +97,7 @@ module Devise
|
|
80
97
|
:validates_confirmation_of, :validates_length_of].freeze
|
81
98
|
|
82
99
|
def assert_validations_api!(base) #:nodoc:
|
83
|
-
unavailable_validations = VALIDATIONS.select{ |v| !base.respond_to?(v) }
|
100
|
+
unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
|
84
101
|
|
85
102
|
unless unavailable_validations.empty?
|
86
103
|
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.to_a.reject(&:destroyed?).reject(&:marked_for_destruction?)
|
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
|
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
|
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:
|
4
|
+
version: 3.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:
|
12
|
+
date: 2022-04-27 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: devise
|
@@ -28,16 +29,16 @@ dependencies:
|
|
28
29
|
name: bundler
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
30
31
|
requirements:
|
31
|
-
- - "
|
32
|
+
- - ">="
|
32
33
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
+
version: '0'
|
34
35
|
type: :development
|
35
36
|
prerelease: false
|
36
37
|
version_requirements: !ruby/object:Gem::Requirement
|
37
38
|
requirements:
|
38
|
-
- - "
|
39
|
+
- - ">="
|
39
40
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
+
version: '0'
|
41
42
|
- !ruby/object:Gem::Dependency
|
42
43
|
name: rake
|
43
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,13 +112,13 @@ 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: []
|
117
119
|
files:
|
118
120
|
- ".gitignore"
|
119
121
|
- ".rspec"
|
120
|
-
- ".simplecov"
|
121
122
|
- ".travis.yml"
|
122
123
|
- CHANGELOG.md
|
123
124
|
- CODE_OF_CONDUCT.md
|
@@ -209,9 +210,9 @@ files:
|
|
209
210
|
- examples/rails5_app/tmp/.keep
|
210
211
|
- examples/rails5_app/vendor/assets/javascripts/.keep
|
211
212
|
- examples/rails5_app/vendor/assets/stylesheets/.keep
|
212
|
-
- gemfiles/rails_4_2.gemfile
|
213
|
-
- gemfiles/rails_5_0.gemfile
|
214
213
|
- gemfiles/rails_5_1.gemfile
|
214
|
+
- gemfiles/rails_5_2.gemfile
|
215
|
+
- gemfiles/rails_6_0.gemfile
|
215
216
|
- lib/devise/multi_email.rb
|
216
217
|
- lib/devise/multi_email/association_manager.rb
|
217
218
|
- lib/devise/multi_email/email_model_extensions.rb
|
@@ -241,8 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
241
242
|
- !ruby/object:Gem::Version
|
242
243
|
version: '0'
|
243
244
|
requirements: []
|
244
|
-
|
245
|
-
rubygems_version: 2.6.8
|
245
|
+
rubygems_version: 3.0.3.1
|
246
246
|
signing_key:
|
247
247
|
specification_version: 4
|
248
248
|
summary: Let devise support multiple emails.
|