Skip to content

Instantly share code, notes, and snippets.

@johanlunds
Last active August 15, 2017 15:54
Show Gist options
  • Save johanlunds/f4b7ba8e788d66c6d8396789dcda30e9 to your computer and use it in GitHub Desktop.
Save johanlunds/f4b7ba8e788d66c6d8396789dcda30e9 to your computer and use it in GitHub Desktop.
A small mixin to get presenter functionality in any class.
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
module Presenter
extend ActiveSupport::Concern
included do
# class_attribute provides inheritance of the values
class_attribute :presenters
self.presenters = {}
end
class_methods do
def presents(klass)
presenters[klass] = self
end
def presenter(object)
presenters[object.class]
end
def present(object)
presenter(object)&.new(object) or raise NameError, "Presenter for #{object.class} is not registered"
end
end
attr_reader :object
def initialize(object)
@object = object
end
end
class BasePresenter
include Presenter
end
class Foo
end
class FooPresenter < BasePresenter
presents Foo
end
RSpec.describe 'presenters' do
it 'works' do
object = Foo.new
presenter = BasePresenter.presenter(object)
expect(presenter).to eq(FooPresenter)
result = BasePresenter.present(object)
expect(result.class).to eq(FooPresenter)
expect(result.object).to eq(object)
end
end
class AnotherPresenter
include Presenter
end
RSpec.describe 'presenter scoping' do
it 'has support for scopes' do
object = Foo.new
presenter = AnotherPresenter.presenter(object)
expect(presenter).to be_nil
expect { AnotherPresenter.present(object) }.to raise_error(NameError, "Presenter for Foo is not registered")
end
end
# Possible additional features:
#
# * use SimpleDelegator or Forwardable
# * support multiple levels of inheritance (class B < A, but use APresenter instead of BPresenter)
# * Support for global scopes and not only local scopes
# * Depending on taste: the API could instead be `presents Foo, scope: :models` or `presents Foo, scope: ModelPresenters`
# instead and avoid the subclassing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment