To add, the above code is a pretty near approximation of the literal code inside the devise codebase, which is a very standard Ruby auth system.
See here:
https://github.com/heartcombo/devise/blob/main/lib/devise/co...
def self.define_helpers(mapping) #:nodoc:
mapping = mapping.name
class_eval <<-METHODS, __FILE__, __LINE__ + 1
def authenticate_#{mapping}!(opts = {})
That code is *literally* calling class_eval with a multi-line string parameter, where it inlines the helper name (like admin, user, whatever), to grow the class at runtime.It hurts my soul.
Thank you both for the time you spent explaining this.
It's been widely understood in the Ruby community for some time now that metaprogramming—like in the example above—should generally be limited to framework or library code, and avoided in regular application code.
Dynamically generated methods can provide amazing DX when used appropriately. A classic example from Rails is belongs_to, which dynamically defines methods based on the arguments provided:
class Post < ApplicationRecord belongs_to :user end
This generates methods like:
post.user - retrieves the associated user
post.user=(user) - sets the associated user
post.user_changed? - returns true if the user foreign key has changed.