Rails static pages - Rails Tricks Issue 15

15 Aug 2023
Are you eager to elevate your security skills and safeguard your applications against cyber threats? I created a Rails Security course is designed specifically for developers like you who aim to build robust, secure Rails applications!
Buy my course: Security for Rails Developers.

Hi there,

This week I want to show you how I add static pages in a Rails application, since I just did this recently.

I like to put all of them into the same controller so they are nicely separated, so I generate a new controller called ‘StaticPagesController`. Then I add my routes:

get '/privacy-policy', to: 'static_pages#privacy'
get '/terms', to: 'static_pages#terms'

And I create my views in app/views/static_pages/. Since Rails 7.0, I can omit the controller methods, Rails will render the appropriate views regardless. That’s it. This would be today’s Rails trick, but since it would be super short, let’s dig into how Rails renders the views without the controller methods being defined.

As I found out, this feature called “implicit rendering”. What happens is, when Action Controller is processing a request, it is trying to find the appropriate method for the action it receives from the router:

# action_pack/lib/abstract_controller/base.rb
def method_for_action(action_name)
  if action_method?(action_name)
    action_name
  elsif respond_to?(:action_missing, true)
    "_handle_action_missing"
  end
end

This method is overridden in action_pack/lib/action_controller/metal.rb to check if there is any template available for the action, and if so, it renders the template, of not, it just renders a head: :no_content:

# action_pack/lib/action_controller/metal.rb
def method_for_action(action_name)
  super || if template_exists?(action_name.to_s, _prefixes)
    "default_render"
  end
end

def default_render
  if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
    render
  elsif any_templates?(action_name.to_s, _prefixes)
    message = "#{self.class.name}\##{action_name} is missing a template " \
      "for this request format and variant.\n" \
      "\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
      "\nrequest.variant: #{request.variant.inspect}"

    raise ActionController::UnknownFormat, message
  elsif interactive_browser_request?
    message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
    raise ActionController::MissingExactTemplate.new(message, self.class, action_name)
  else
    logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
    super
  end
end

Now you know how Rails can render implicit actions in your controller. Until next time!

Did you enjoy reading this? Sign up to the Rails Tricks newsletter for more content like this!

Or follow me on Twitter

Related posts