Basics of presenter objects

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the architecture category.

Last Updated: 2021-05-15

Say if an ActiveRecord method is only used in views. Then it (arguably) belongs in a presenter.

require 'redcarpet/render_strip'

class ApplicationPresenter < SimpleDelegator
  def initialize(model)
    @model = model
    super(@model)
  end

  def short_description
    plaintext_renderer.render(@model.description)
      .truncate(80)
      .html_safe
  end

  private

  def plaintext_renderer
    Redcarpet::Markdown.new(
      Redcarpet::Render::StripDown
    )
  end
end

class EpisodePresenter < ApplicationPresenter
  def published_on
    super.to_date.to_formatted_s(:long)
  end
end

The way you'd use it is that the output of presenter would be assigned to an instance variable in the controllers.

class EpisodesController
  def show
    @episode = EpisodePresenter.new(episode)
  end
end

Observations from experience

The main issue I ran into was dealing with cases where the underlying object is nil / the empty array. For a while, I presented this "empty" object, but this was a bad idea since view logic that switches on truthiness breaks (e.g. if @episode) since you cannot redefine truthiness of a whole object in Ruby. Therefore in future I would avoid presenting at all if the object is "empty".