Rails Presenters: Filling the Model-View-Controller Gap

- - posted in Ruby on Rails, design patterns, presenters, programming, website | Comments

The Model-View-Controller design pattern as used by Ruby on Rails is very clear. Each part has its own role to play. But sometimes there is a bit of a gap. For instance, when determining specific attribute values of elements in a View, it is often not clear where these calculations should take place. We could create a set of helper methods, but that would clutter the namespace. Furthermore, the helper methods are hard to group by their relation, in contrast with Classes and Modules. Instead, we use the Presenter Design Pattern for this type of problems. This post shows how to use this pattern in Ruby on Rails and how to interact with it from the View.

TL;DR The Presenter Design Pattern is used to perform operations required by the View. Using a Model object, provided by the Controller, the Presenter pattern decouples calculations from structure, proving a better Separation of Concerns for the View. By following the naming conventions of Ruby on Rails, this pattern is easily implemented in an existing application.

This blog post discusses the Presenter Design Pattern and shows you an example how to implement it. The first part discusses the pattern and introduces a real-life use case example. We will then (manually) create the Presenter and populate it with some methods that take the responsibility of calculating (attribute) values. Doing so maintains the original purpose and responsibilities of the View and Controller. We will end with a full implementation and a View which sole concern is the structure of elements and delegates all attribute calculations to the Presenter. We will start with pointing out what the problem in the traditional MVC-pattern is.

What ‘gap’ will a Presenter fill?

Earlier we have discussed how to create SVG graphs with jQuery. In that example, we briefly looked at the calculations of the coordinates for all the elements, such as the grid, data points and graph surfaces. An option is to calculate them in the browser, using Javascript, after loading the raw data of the points to draw. Not only does it slow down the rendering of the whole page, but we want to generate the SVGs on server side too. This means that the calculations for the coordinates need to be performed somewhere in the Rails-universe; probably in the View (or partial) that generates the SVG. But is the View a good place to perform such calculations? It seems like logic, so maybe the Controller? That also smells wrong, since the coordinate calculations in the SVG has nothing to do with application logic. We could choose to incorporate the calculations in the Model, but that would require knowledge of the View and does not contribute to Separation Of Concerns. For this, one could use the Presenter Design Pattern.

What is a Presenter?

So, what does a Presenter do? Actually, the answer to what a Presenter exactly is, is not that straightforward. There are multiple interpretations of the Presenter pattern. In the context of MVP, it is almost a substitute for the Controller. There the Presenter handles events arising in the View or Model and mediates between the two. In this case we use it in a slightly different context, more like the MVCP, as described in this post which relates the Presenter pattern to Ruby on Rails.

The Presenter, as we use it, knows what the View needs, and using information from the Model, prepares this information for the View. The Presenter is created by the Controller, initialized with the Model, and passed to the View. The View in turn calls Presenter methods, for instance to calculate the coordinates of the n-th data point in a graph. The overall structure looks like the following diagram:

The diagram shows that the View delegates some tasks to the Presenter. The Presenter thereby fills the gap of client-side calculations performed server-side. There a a few ways to implement the Presenter pattern. We will show some gems which do the task for you, but eventually we create the pattern manually.

Gems

Of course, the first thing you do when introducing new functionality or structures is to search for a gem which provides it. There are a few, amongst others Active Presenter, Action Presenter and the feature-rich Draper (thanks to Alex Tretyakov for mentioning that last one). The second gem, Action Presenter, is very similar to the approach we have chosen. Both gems provide a nice set of methods to act with and incorporate the Presenter pattern in our Rails application. However, we do not need them all and we take the ability to learn a bit on the Ruby on Rails naming conventions and the practice of extending on it.

Manual Presenter

In this case we create a Presenter to perform the calculations of the interactive SVG graph. In that post we only made one type of graph (a line graph), but it could also be the case that there is a whole family of graphs. As we create a Presenter for each graph, it would be nice to organize them. We can do this, following the Rails naming convention, by creating a module GraphPresenters, and instantiate a GraphPresenters::LinegraphPresenter. The location for this file would be app/presenters/graph_presenters/line_graph_presenter.rb.

In the remainder of this post we create the Presenter, fill it with methods and create the View that uses them.

Initializing the Presenter

The Controller instantiates the Presenter and provides it with a Graph model object. The Graph model has_many data points and knows how to calculate the domain (the number of years to plot) and value ranges (the maximum and minimum height of the graph).

We begin our Presenter with the following code:

app/presenters/graph_presenters/line_graph_presenter.rb Presenter, initializing methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# line_graph_presenter.rb
module GraphPresenters
  class LineGraphPresenter
    public

    def initialize( graph )
      @graph = graph

      @options = {
        :number_of_vertical_lines = 11,
        :range_top_value = @graph.max_value
      }
      @options[:range_step_size] = @options[:range_top_value] / @options[:number_of_vertical_lines]
    end

    def configure( settings = {} )
      settings.assert_valid_keys( :width, :height )
      @options.merge! settings
      @options.merge! {
        x_start: 113, # Leave space for the vertical labels
        y_start: 10,  # Leave space for the year-labels
        radius: 5,    # The radius of each data point
      }
      @options.merge! {
        x_end: @options[:width] - @options[:radius],
        y_end: @options[:height] - @options[:radius]
      }
    end
  end
end

Here we see the two methods of the Presenter that can collect information. The first is the initialize method, which receives a Graph model object when it’s created by the Controller.

The initialize method provides the basic configuration of the graph we would like to draw, such as the :number_of_vertical_lines in our grid and the value-increase each line represents. The second method, configure is called from the View to set the width and height (in pixels) of the resulting SVG, resulting in x and y coordinates of the container’s border. By using these two methods, we also separate the initializing and configurations steps related to the Controller and View, respectively. (Note that you need to do a bit more mathematics for the calculation of the above metrics, but that is left out of this tutorial)

Coordinate calculations

With the initialization and configuration methods in place, we can start with the coordinate calculations. We need two methods to determine the x- and y-coordinates, representing the years on the x-axis and the value of the data point on the y-axis. To do that, we first need methods to calculate the value, in our coordinate system, for each pixel we have available. And we need to know the distance between every year-line on the x-axis. The following methods are used for these coordinate calculations:

app/presenters/graph_presenters/line_graph_presenter.rb Presenter, coordinate calculations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# line_graph_presenter.rb

...

public
def vertical_line(n)
{
  x1: vertical_line_position_x(n),
  y1: @options[:y_start],
  x2: vertical_line_position_x(n),
  y2: @options[:y_end]
}

def horizontal_line(n)
{
  x1: @options[:x_start],
  y1: horizontal_line_position_y(n),
  x2: @options[:x_end],
  y2: horizontal_line_position_y(n)
}
end

private
def distance_between_vertical_lines
  pixels_available = (@options[:x_end] - @options[:x_start])
  pixels_available / @graph.number_of_datapoints
end

def distance_between_horizontal_lines
  pixels_available = @options[:y_end] - @options[:y_start]
  pixels_available / @options[:number_of_vertical_lines]
end

# Vertical lines are aligned with each year on the x-axis
def vertical_line_position_x(n)
  @options[:x_start] + (distance_between_vertical_lines * n)
end

# Horizontal lines provide visual guidance for the value of data points
def horizontal_line_position_y(n)
  @options[:y_start] + (distance_between_horizontal_lines * n)
end

The grid View

With these methods available in the Presenter, we create our View that outputs the actual SVG. We start with the grid, as explained in the first section of the post about creating interactive graphs. Let’s assume that the Controller has made an instance variable @presenter available to the View (later on we will show how). The view can now make use of the methods of @presenter (to delegate the calculation of coordinates) and focus on the structure of the SVG. The following View (expressed in Haml, which I find very convenient for structural content, but less for textual content) constructs the same grid as in the above mentioned post, but with much less code.

app/views/graphs/show.html.haml View for the SVG
1
2
3
4
5
6
7
8
9
# show.html.haml
%svg{xmlns: 'http://www.w3.org/2000/svg','xmlns:xlink' => 'http://www.w3.org/1999/xlink', version: '1.1', class: 'graph'}
  %g.grid#xGrid
    - @presenter.number_of_datapoints.times do |n|
      %line{ @presenter.vertical_line n }

  %g.grid#yGrid
    - @presenter.number_of_vertical_lines.times do |n|
      %line{ @presenter.horizontal_line n }

We make use of Haml’s functionality by letting all the Presenter methods return a hash, which can be used directly as the HTML/SVG-attributes. To draw the lines of the base grid, we iterate over the domain points and number of vertical lines. The attributes of each line are calculated by the Presenter. With the base grid set up, the methods to display the data points on that grid are next.

Data points positioning

Every data point has a position on the horizontal axis, which is calculated by vertical_line(n), and a position on the vertical axis. To calculate these positions we need to add the following methods to the Presenter.

app/presenters/graph_presenters/line_graph_presenter.rb Presenter, coordinate methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# line_graph_presenter.rb

...

public
def circle(n)
  value = @graph.datapoint n
  line = vertical_line n

  {
    cx: line[:x1],
    cy: y_position value,
    r: 5
  }
end

def surface
  point = circle 0
  first = point
  path = "M#{point[:cx]},#{@options[:y_end]}"

  @graph.number_of_datapoints.times do |n|
    point = circle n
    path += "L#{point[:cx]},#{point[:cy]}"
  end

  path << "L#{point[:cx]},#{@options[:y_end}"
  path << "M#{first[:cx]},#{@options[:y_end}"

  { d: path }
end

private
def y_position(value)
  # Assume the y-axis always starts from 0
  pixels_per_value = (@options[:end] - @options[:start]) / @options[:range_top_value]
  @options[:y_end] - (value * pixels_per_value)
end

Display the data points

In the above code block we have created a circle(n) method, which returns a hash of attributes to create a circle in SVG. It uses vertical_line(n) to get the correct position on the x-axis and y_position(value) to represent the correct value on the y-axis. The surface method in turn uses the result of multiple circle(n) calls to construct a SVG-path spanning all the data points and axes as boundaries. For a more in-depth explanation of the workings of this constructed path you can read more in the previous post on graphs. The View calls these methods, using the following Haml code.

/app/views/graphs/show.html.haml Add surface and points to the SVG
1
2
3
4
5
6
7
8
9
10
# show.html.haml

...

  %g.surfaces
    %path{ @presenter.surface }

  %g.points
    - @graph.number_of_datapoints.times do |n|
        %circle{ @presenter.circle(n) }

Since all the other elements of the SVG are more of the same, that is left the reader as an exercise to implement. With the methods vertical_line(n) and y_position(value), all elements can be positioned. The main part of the Presenter does not change. What is left is the instantiation of the Presenter.

Create the Presenter object

AS stated earlier, the Controller constructs the Presenter object and passes the Model instance to it and makes the Presenter available to the view.

app/controllers/graphs_controller.rb Construction the Presenter
1
2
3
4
5
# graphs_controller.rb
def show
  ...
  @presenter = GraphPresenters::LineGraphPresenter.new graph
end

That’s it. The graph object is already available, since that is the ActiveRecord model under consideration. By making @presenter and instance variable it becomes available to the View. Of course, in case of multiple types of graphs you need to dynamically determine which Presenter class to instantiate.

Round-up: Model-View-Controller-Presenter

So, what have we done? We have created a Module to group all our GraphPresenters. Each of those is constructed by the Controller, which passes them a Graph model Object when they are initialized. Therefor the Presenter knows about the Model, performs calculations on the data in the Model object and prepares the information to be used by the View. The View’s only concern is the structure of the elements, whilst the positioning is delegated to the Presenter. The following diagram puts it all in place.

By following the naming conventions of Ruby on Rails, we are able the extend the Model-View-Controller pattern with a new pattern, the Presenter. It plays nicely with the MVC-pattern and keeps our View clean of logic. Along the way we saw a bit of HAML and worked out the coordinate calculations for the graph of the previous posts on SVG Graphs. The final files can be viewed together in this Gist.

Since there are always more solutions possible, I would love to hear yours, or any modifications to the one presented here. In this post we used the Presenter mainly for coordinate calculations, but in many more use-cases the Presenter can be useful. How would you use it?

This article also appeared on the Inspire.nl Blog. Inspire is a Dutch software studio that develops innovative Ruby on Rails web applications for computer, tablet and mobile.

Comments