Skip to content

Instantly share code, notes, and snippets.

@mur-wtag
Created December 20, 2020 04:59
Show Gist options
  • Select an option

  • Save mur-wtag/da9a1ad24bc0b39904e26c0cb8ad22db to your computer and use it in GitHub Desktop.

Select an option

Save mur-wtag/da9a1ad24bc0b39904e26c0cb8ad22db to your computer and use it in GitHub Desktop.
Presenter in MVC pattern

In view we are uing Presenter like this:

# app/views/galleries/show.html

<% present(object: @gallery, shop: @shop, gallery_items: @gallery_items) do |presenter| %>
  <div class="shop-name"><%= presenter.shop_name %></div>
    <div class="post-filter btn-filter d-inline-block">
     <div class="dropdown">
       <a class="filter-dropdown dropdown-toggle" href="#" role="button" id="postFilter" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
         <%= presenter.active_filter.present? ? presenter.active_filter[:name] : 'All Photos' %> <%= embedded_svg 'icon-down-arrow.svg', class: 'icon' %>
       </a>
       <div class="dropdown-menu" aria-labelledby="postFilter">
         <a class="dropdown-item <%= 'active' unless params[:view] %>" href="<%= gallery_path(@gallery, channel: params[:channel]) %>">All Photos</a>
         <% presenter.view_filters.each do |filter| %>
           <%= link_to(filter.path, class: filter.css_classes) do %>
             <%= filter.name %>
           <% end %>
         <% end %>
       </div>
     </div>
  </div>
<% end %>

And we are accessing presenter, through helper module:

# app/helpers/application_helper.rb

def present(klass = nil, object:, **objects)
  klass ||= "#{object.class}Presenter".constantize
  presenter = klass.new(object, self) # we are passing application helper object, so view context goes along with it
  presenter.define_objects(objects)

  yield(presenter) if block_given? # passing ruby code block and yielding modifying it with `presenter` object

  presenter
end

And gallery presenter look like this:

# app/presenters/application_presenter.rb

# frozen_string_literal: true
class ApplicationPresenter
  def initialize(object, template)
    @object = object
    @template = template # view templete
  end

  def define_objects(objects_hash)
    objects_hash.each do |name, obj|
      define_singleton_method(name) do
        obj
      end
    end
  end

  private

  def self.presents(name)
    define_method(name) do
      @object
    end
  end

  def h
    @template
  end
end

# app/presenters/gallery_presenter.rb

class GalleryPresenter < ApplicationPresenter
  presents :gallery

  def view_filters
    @filters = VIEW_FILTERS
    @filters.map do |filter|
      filter = filter.dup
      css_classes = 'dropdown-item'
      css_classes += ' active' if h.params[:view] == filter[:param]
      filter.store(:css_classes, css_classes)
      filter.store(:path, h.gallery_path(gallery, view: filter[:param], channel: h.params[:channel])) # using view template object

      Hashie::Mash.new(filter)
    end
  end

  def active_filter
    view_filters.find { |filter| h.params[:view] == filter[:param] }
  end

  def newest_gallery
    @newest_gallery ||= shop.galleries.order(:updated_at).last # using other object like `shop` which passed from view (from controller).
  end
  
  def shop_name
    shop.name
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment