This is a working draft of proposal. Feel free to comment, express your opinions and suggest alternative solutions.
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)
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
endIt 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
endActionView::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
endThe 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
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
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:
Now you can just do this in your views: