devise-multi_email 2.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|