Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### Unreleased

* enhancements
* Compute each strategy's `authenticatable_name` once per strategy class instead of on every request: Warden builds new strategy instances per request, so the per-instance memo never hit.

### 5.0.4 - 2026-05-08

* security fixes
Expand Down
8 changes: 7 additions & 1 deletion lib/devise/strategies/authenticatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,14 @@ def parse_authentication_key_values(hash, keys)
# Holds the authenticatable name for this class. Devise::Strategies::DatabaseAuthenticatable
# becomes simply :database.
def authenticatable_name
@authenticatable_name ||= self.class.authenticatable_name
end

# Computed once per strategy class: Warden builds new strategy
# instances on every request.
def self.authenticatable_name
@authenticatable_name ||=
ActiveSupport::Inflector.underscore(self.class.name.split("::").last).
ActiveSupport::Inflector.underscore(name.split("::").last).
sub("_authenticatable", "").to_sym
end
end
Expand Down
19 changes: 19 additions & 0 deletions test/strategies/authenticatable_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require 'test_helper'
require 'devise/strategies/database_authenticatable'
require 'devise/strategies/rememberable'

class AuthenticatableStrategyTest < ActiveSupport::TestCase
test 'authenticatable_name comes from the strategy class name' do
assert_equal :database, Devise::Strategies::DatabaseAuthenticatable.authenticatable_name
assert_equal :rememberable, Devise::Strategies::Rememberable.authenticatable_name
assert_equal :database, Devise::Strategies::DatabaseAuthenticatable.new({}).send(:authenticatable_name)
end

test 'authenticatable_name is computed once per strategy class, not per instance' do
Devise::Strategies::DatabaseAuthenticatable.authenticatable_name
ActiveSupport::Inflector.expects(:underscore).never
2.times { Devise::Strategies::DatabaseAuthenticatable.new({}).send(:authenticatable_name) }
end
end