Skip to content

Instantly share code, notes, and snippets.

@lukaszx0
Last active December 21, 2015 06:59
Show Gist options
  • Save lukaszx0/6267840 to your computer and use it in GitHub Desktop.
Save lukaszx0/6267840 to your computer and use it in GitHub Desktop.
View Classes implementation proposal

View Classes

This is a working draft of proposal. Feel free to comment, express your opinions and suggest alternative solutions.

Problem

As a second part of my Google Summer of Code, I planned to introduce View Classes to Action View.

There was ongoing discussion about this and extracting AV from AP (which is done already: rails/rails#11396) here: https://groups.google.com/forum/#!topic/rubyonrails-gsoc/N7uDY_6513I

The "problem" is becoming more common and people are looking for solution to ERB view templates bloated with too much logic (http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/#view-objects)

Solution

Example

I'd like to propose as simple as possible solution which will introduce context argument to render method like following:

class PagesController < ApplicationController
  def index
    @post = Post.find(params[:post])

    render context: PostsShowViewContext.new(@post)
  end
end

It will also work for partials in the same way:

render partial: "page_content", context: PostsShowViewContext.new(@post)

The render will expect context class instance as an argument. It'll have reference to controller.

The ViewContext class will look like this:

# app/view_models/posts/show.rb
class PostsShowViewContext
  include ActionView::ViewContext # pseudo code, something like this will be including AV's stuff.

  attr_reader :post

  def initialize(post)
    @post = post
  end

  def title
    post.title
  end
end

ActionView::ViewContext module will deliver all boilerplate: view helpers, assigns and route helpers

The whole thing would be implemented somewhere in place where ActionView makes rendering happen

# somewhere in ActionView depths
class ActionView::Rendering
  def render(options)
    if context = options[:context]
      context.controller = self
    end
  end
end

How it'll work

The intention of this change is to introduce "view classes", the object in which context's template would be evaluted. The way it works in tilt for example: https://github.com/rtomayko/tilt#basic-usage

Naming

As we all know, it's hard. For me, the best name for this "feature" would be ViewModel (view_model). However there are other candidates:

  • View Context
  • View Class
  • Context
@lukaszx0
Copy link
Author

lukaszx0 commented Sep 2, 2013

This gist i just a regular git repo. You can find both solutions under the link I've posted in my comment above: https://gist.github.com/strzalek/6267840/249c76563dfb3d2fcf97c76d7546614dbf84d214. You have "first" and "second" solution in there of which I've picked second one and only this one remained in the document.

@dhh
Copy link

dhh commented Oct 31, 2013

I'm not a fan of adding this additional layer of chrome. It doesn't offer much if anything over using the existing mechanisms to achieve the same. If you want to use a context or presenter object, it couldn't be easier:

class PagesController < ApplicationController
  def index
    @context = PostsShowViewContext.new(Post.find(params[:post]))
  end
end

Now you can just do this in your views:

<%= @context.title %>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment