Skip to content

Commit 6ecf1b4

Browse files
committed
Allows multiple messages
This commit adapts Devise to support multiple flash messages from the Models, utilizing the Warden multi-messages feature.
1 parent fec67f9 commit 6ecf1b4

16 files changed

+195
-65
lines changed

Earthfile

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
VERSION 0.7
2+
3+
# This allows one to change the running Ruby version with:
4+
#
5+
# `earthly --allow-privileged +test --EARTHLY_RUBY_VERSION=2.7`
6+
ARG --global EARTHLY_RUBY_VERSION=3.3
7+
ARG --global BUNDLER_VERSION=2.4.5
8+
9+
FROM ruby:$EARTHLY_RUBY_VERSION
10+
WORKDIR /gem
11+
12+
deps:
13+
# No need to keep a single `RUN` here since this target uses `SAVE ARTIFACT`
14+
# which means there's no Docker image created here.
15+
RUN apt update \
16+
&& apt install --yes \
17+
--no-install-recommends \
18+
build-essential \
19+
git \
20+
&& mkdir /gems \
21+
&& git clone https://github.com/Pharmony/warden.git /gems/warden \
22+
&& cd /gems/warden \
23+
&& git checkout features/support-multiple-messages \
24+
&& gem install bundler:${BUNDLER_VERSION}
25+
26+
COPY Gemfile /gem/Gemfile
27+
COPY Gemfile.lock /gem/Gemfile.lock
28+
COPY *.gemspec /gem
29+
COPY lib/devise/version.rb /gem/lib/devise/version.rb
30+
31+
RUN bundle install --jobs $(nproc)
32+
33+
SAVE ARTIFACT /gems git-gems
34+
SAVE ARTIFACT /usr/local/bundle bundler
35+
SAVE ARTIFACT /gem/Gemfile Gemfile
36+
SAVE ARTIFACT /gem/Gemfile.lock Gemfile.lock
37+
38+
dev:
39+
RUN apt update \
40+
&& apt install --yes \
41+
--no-install-recommends \
42+
git \
43+
&& gem install bundler:${BUNDLER_VERSION}
44+
45+
# Import cached gems
46+
COPY +deps/git-gems /gems
47+
COPY +deps/bundler /usr/local/bundle
48+
COPY +deps/Gemfile /gem/Gemfile
49+
COPY +deps/Gemfile.lock /gem/Gemfile.lock
50+
51+
# Import gem files
52+
FOR gem_folder IN app config lib test *.gemspec Rakefile
53+
COPY $gem_folder /gem/$gem_folder
54+
END
55+
56+
ENTRYPOINT ["bundle", "exec"]
57+
CMD ["rake"]
58+
59+
# Run `earthly +dev` in order to get the Docker image exported to your
60+
# Docker images.
61+
SAVE IMAGE heartcombo/devise:latest
62+
63+
#
64+
# This target runs the test suite.
65+
#
66+
# On you local machine you would likely use `docker compose run --rm gem`
67+
# instead, avoiding to refresh the Docker image which takes some seconds.
68+
#
69+
# Use the following command in order to run the tests suite:
70+
# earthly --allow-privileged +test [--TEST_COMMAND="rake test TEST=test/test_foobar.rb"]
71+
#
72+
# See the above `EARTHLY_RUBY_VERSION` variable.
73+
test:
74+
FROM earthly/dind:alpine
75+
76+
COPY docker-compose-earthly.yml ./docker-compose.yml
77+
78+
# Optionnal argument in the case you'd like to run something else than
79+
# `rake test`
80+
ARG TEST_COMMAND
81+
82+
# Creates a temporary Docker image using the output from the +dev target,
83+
# that will be used within the `WITH DOCKER ... END` block only.
84+
WITH DOCKER --load heartcombo/devise:latest=+dev
85+
RUN docker-compose run --rm gem $TEST_COMMAND
86+
END

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ end
3636
# group :mongoid do
3737
# gem "mongoid", "~> 4.0.0"
3838
# end
39+
40+
gem "warden", "~> 1.2.9", github: 'Pharmony/warden',
41+
branch: 'features/support-multiple-messages'

Gemfile.lock

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
GIT
2+
remote: https://github.com/Pharmony/warden.git
3+
revision: baf7b149b5322ac2b2f81cdc94d4e5331ff7ef1c
4+
branch: features/support-multiple-messages
5+
specs:
6+
warden (1.2.9)
7+
rack (>= 2.2.3)
8+
19
GIT
210
remote: https://github.com/rails/rails-controller-testing.git
311
revision: c203673f8011a7cdc2a8edf995ae6b3eec3417ca
@@ -236,8 +244,6 @@ GEM
236244
tzinfo (2.0.6)
237245
concurrent-ruby (~> 1.0)
238246
version_gem (1.1.3)
239-
warden (1.2.9)
240-
rack (>= 2.0.9)
241247
webrat (0.7.3)
242248
nokogiri (>= 1.2.0)
243249
rack (>= 1.0)
@@ -265,6 +271,7 @@ DEPENDENCIES
265271
rexml
266272
sqlite3 (~> 1.4)
267273
timecop
274+
warden (~> 1.2.9)!
268275
webrat (= 0.7.3)
269276

270277
BUNDLED WITH

docker-compose-earthly.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This file is only used by Earthly
2+
3+
name: devise
4+
5+
services:
6+
gem:
7+
image: heartcombo/devise:latest

docker-compose.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This file allows you to use the `docker compose` command in order to run Ruby
2+
# commands or tests.
3+
4+
name: devise
5+
6+
services:
7+
# docker compose run --rm gem [rspec [path to spec file]]
8+
gem:
9+
image: heartcombo/devise:latest
10+
volumes:
11+
- $PWD:/gem/

lib/devise/failure_app.rb

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def recall
7575
end
7676
end
7777

78-
flash.now[:alert] = i18n_message(:invalid) if is_flashing_format?
78+
flash.now[:alert] = i18n_messages(:invalid) if is_flashing_format?
7979
self.response = recall_app(warden_options[:recall]).call(request.env).tap { |response|
8080
response[0] = Rack::Utils.status_code(
8181
response[0].in?(300..399) ? Devise.responder.redirect_status : Devise.responder.error_status
@@ -90,7 +90,7 @@ def redirect
9090
flash.keep(:timedout)
9191
flash.keep(:alert)
9292
else
93-
flash[:alert] = i18n_message
93+
flash[:alert] = i18n_messages
9494
end
9595
end
9696
redirect_to redirect_url
@@ -102,9 +102,13 @@ def i18n_options(options)
102102
options
103103
end
104104

105-
def i18n_message(default = nil)
106-
message = warden_message || default || :unauthenticated
105+
def i18n_messages(default = nil)
106+
Array(warden_messages || default || :unauthenticated).map do |message|
107+
i18n_message(message)
108+
end
109+
end
107110

111+
def i18n_message(message)
108112
if message.is_a?(Symbol)
109113
options = {}
110114
options[:resource_name] = scope
@@ -126,7 +130,7 @@ def i18n_locale
126130
end
127131

128132
def redirect_url
129-
if warden_message == :timeout
133+
if Array(warden_messages).include?(:timeout)
130134
flash[:timedout] = true if is_flashing_format?
131135

132136
path = if request.get?
@@ -199,14 +203,14 @@ def http_auth_header?
199203
end
200204

201205
def http_auth_body
202-
return i18n_message unless request_format
206+
return i18n_messages unless request_format
203207
method = "to_#{request_format}"
204208
if method == "to_xml"
205-
{ error: i18n_message }.to_xml(root: "errors")
209+
i18n_messages.to_xml(root: 'errors', skip_types: true)
206210
elsif {}.respond_to?(method)
207-
{ error: i18n_message }.send(method)
211+
{ errors: i18n_messages }.send(method)
208212
else
209-
i18n_message
213+
i18n_messages
210214
end
211215
end
212216

@@ -225,8 +229,8 @@ def warden_options
225229
request.respond_to?(:get_header) ? request.get_header("warden.options") : request.env["warden.options"]
226230
end
227231

228-
def warden_message
229-
@message ||= warden.message || warden_options[:message]
232+
def warden_messages
233+
@messages ||= warden.messages.presence || warden_options[:messages].presence
230234
end
231235

232236
def scope

lib/devise/hooks/activatable.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
88
scope = options[:scope]
99
warden.logout(scope)
10-
throw :warden, scope: scope, message: record.inactive_message
10+
throw :warden, scope: scope, messages: record.inactive_message
1111
end
1212
end

lib/devise/hooks/timeoutable.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
record.timedout?(last_request_at) &&
2626
!proxy.remember_me_is_active?(record)
2727
Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
28-
throw :warden, scope: scope, message: :timeout
28+
throw :warden, scope: scope, messages: [:timeout]
2929
end
3030

3131
unless env['devise.skip_trackable']

lib/devise/models/authenticatable.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ module Authenticatable
6464
class_attribute :devise_modules, instance_writer: false
6565
self.devise_modules ||= []
6666

67+
class_attribute :devise_messages
68+
self.devise_messages = []
69+
6770
before_validation :downcase_keys
6871
before_validation :strip_whitespace
6972
end
@@ -82,10 +85,6 @@ def valid_for_authentication?
8285
block_given? ? yield : true
8386
end
8487

85-
def unauthenticated_message
86-
:invalid
87-
end
88-
8988
def active_for_authentication?
9089
true
9190
end
@@ -124,6 +123,10 @@ def inspect
124123
"#<#{self.class} #{inspection.join(", ")}>"
125124
end
126125

126+
def reset_devise_messages!
127+
self.devise_messages = []
128+
end
129+
127130
protected
128131

129132
def devise_mailer

lib/devise/models/confirmable.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,15 @@ def resend_confirmation_instructions
142142
# is already confirmed, it should never be blocked. Otherwise we need to
143143
# calculate if the confirm time has not expired for this user.
144144
def active_for_authentication?
145-
super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
145+
valid = super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
146+
147+
devise_messages << :unconfirmed unless valid
148+
149+
valid
146150
end
147151

148-
# The message to be shown if the account is inactive.
152+
# Devise::RegistrationsController uses this method to determine the flash
153+
# message to be shown to the user with `signed_up_but_#{inactive_message}`
149154
def inactive_message
150155
!confirmed? ? :unconfirmed : super
151156
end

0 commit comments

Comments
 (0)