An Endless Method Use Case

An Endless Method Use Case

An example of where I think the endless method helps with making the code concise and easy to read

ยท

3 min read

While preparing my article for the series that I started about Open Source Ruby, I found an example of code that I think could benefit from using the endless method.

Here is a video version of this article:

or continue reading for the text version ๐Ÿ‘‡

The original code

The original code is based on Mastodon ApplicationController:

# https://github.com/mastodon/mastodon/blob/main/app/controllers/application_controller.rb#L3

class ApplicationController < ActionController::Base
  # other code ...

  def forbidden
    respond_with_error(403)
  end

  def not_found
    respond_with_error(404)
  end

  def gone
    respond_with_error(410)
  end

  def unprocessable_entity
    respond_with_error(422)
  end

  def not_acceptable
    respond_with_error(406)
  end

  def bad_request
    respond_with_error(400)
  end

  def internal_server_error
    respond_with_error(500)
  end

  def service_unavailable
    respond_with_error(503)
  end

  def too_many_requests
    respond_with_error(429)
  end
  # other code ...
end

Refactoring

Use the endless method

# https://github.com/mastodon/mastodon/blob/main/app/controllers/application_controller.rb#L3

class ApplicationController < ActionController::Base
  # other code ...

  def forbidden = respond_with_error(403)

  def not_found = respond_with_error(404)

  def gone = respond_with_error(410)

  def unprocessable_entity = respond_with_error(422)

  def not_acceptable = respond_with_error(406)

  def bad_request = respond_with_error(400)

  def internal_server_error = respond_with_error(500)

  def service_unavailable = respond_with_error(503)

  def too_many_requests = respond_with_error(429)

  # other code ...
end

In this specific case, the endless method is the best to be used here.

It is almost like aliasing the same method but giving it different names based on the parameters passed.

Let's take a look at a side-by-side comparison:

Extract to a concern

I think we can take this one step further and extract it all to a concern or simple module if you want to:

module RespondWithError
  extend ActiveSupport::Concern

  private

  def forbidden = respond_with_error(403)

  def not_found = respond_with_error(404)

  def gone = respond_with_error(410)

  def unprocessable_entity = respond_with_error(422)

  def not_acceptable = respond_with_error(406)

  def bad_request = respond_with_error(400)

  def internal_server_error = respond_with_error(500)

  def service_unavailable = respond_with_error(503)

  def too_many_requests = respond_with_error(429)

  def respond_with_error(code)
    respond_to do |format|
      format.any  { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }
      format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
    end
  end
end

This way all behavior about responding with error status code is contained in a single file. Adding or changing this behaviour could be made in this one single place.

Group together by status code

Based on the reply from Russell Garner and Bradley Schaefer we can refactor this more by removing new lines between endless methods and grouping them by status:

module RespondWithError
  extend ActiveSupport::Concern

  private

  # Client error responses 4xx
  def bad_request           = respond_with_error(400)
  def forbidden             = respond_with_error(403)
  def not_found             = respond_with_error(404)
  def not_acceptable        = respond_with_error(406)
  def gone                  = respond_with_error(410)
  def unprocessable_entity  = respond_with_error(422)
  def too_many_requests     = respond_with_error(429)

  # Server error responses 5xx
  def internal_server_error = respond_with_error(500)
  def service_unavailable   = respond_with_error(503)

  def respond_with_error(code)
    respond_to do |format|
      format.any  { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }
      format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
    end
  end
end

Read more about the endless method

I strongly recommend this article from Victor Shepelev about the endless method:

https://zverok.substack.com/p/useless-ruby-sugar-endless-one-line

You will find there more cases and examples about when to use and when not to use this construct.


Enjoyed this article?

๐Ÿ‘‰ Join my Short Ruby News newsletter for weekly Ruby updates from the community and visit rubyandrails.info, a directory with learning content about Ruby.

๐Ÿ‘ Subscribe to my Ruby and Ruby on rails courses over email at learn.shortruby.com - effortless learning anytime, anywhere

๐Ÿค Let's connect on Ruby.social or Linkedin or Twitter where I post mainly about Ruby and Rails.

๐ŸŽฅ Follow me on my YouTube channel for short videos about Ruby


Updates

  • 2023-12-06: Added a new refactoring variant based on replies to my share on Ruby.social - see Group together by status code

Did you find this article valuable?

Support Lucian Ghinda by becoming a sponsor. Any amount is appreciated!