Skip to content

Instantly share code, notes, and snippets.

@bradgessler
Created December 8, 2025 18:20
Show Gist options
  • Select an option

  • Save bradgessler/2cfd814cc4ccc094c14fed2f57e464a7 to your computer and use it in GitHub Desktop.

Select an option

Save bradgessler/2cfd814cc4ccc094c14fed2f57e464a7 to your computer and use it in GitHub Desktop.
Dasherized Rails routes
# config/initializers/route_format.rb
#
# Global route formatting override for Rails routing.
#
# This hooks into ActionDispatch::Routing::Mapper::Mapping and mutates the
# Journey AST in place so that underscored literal path segments are
# dasherized. It affects *all* routes (get, resources, scope, etc.)
# because they all end up as a Journey AST passed into Mapping.
#
# IMPORTANT:
# - This does NOT distinguish between symbols and strings in the routes DSL.
# - Any literal segment like "linked_in" becomes "linked-in".
# - Dynamic params (e.g. :id) are not changed.
#
Rails.application.config.routes_format = :dasherize
module RouteFormatAstRewrite
private
# Recursively walk a Journey AST node and rewrite literal segments in-place.
#
# Only touches ActionDispatch::Journey::Nodes::Literal instances whose
# underlying string contains an underscore. Other node types are left as-is.
def rf_rewrite_ast(node)
formatter = Rails.application.config.routes_format
return unless formatter
return unless node
case node
when ActionDispatch::Journey::Nodes::Literal
value = node.left
if value.is_a?(String) && value.include?("_")
node.left = value.public_send(formatter)
# Clear cached to_s so Journey will recompute the string
node.instance_variable_set(:@to_s, nil)
end
when ActionDispatch::Journey::Nodes::Binary
rf_rewrite_ast(node.left)
rf_rewrite_ast(node.right)
when ActionDispatch::Journey::Nodes::Unary,
ActionDispatch::Journey::Nodes::Group,
ActionDispatch::Journey::Nodes::Star
rf_rewrite_ast(node.left)
when ActionDispatch::Journey::Nodes::Or
node.children.each { |child| rf_rewrite_ast(child) }
else
# leave other node types unchanged
end
end
# Hook into Mapping initialization to rewrite the AST before the
# Journey::Path::Pattern is built.
def initialize(*, ast:, **, &)
rf_rewrite_ast(ast)
super(*, ast:, **, &)
end
end
# Patch the core Mapping class used by all router macros.
ActionDispatch::Routing::Mapper::Mapping.prepend(RouteFormatAstRewrite)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment