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
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
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
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
Thanks Łukasz for those thoughts on the evil view layer!
I like the idea of having the
context:
option which makes it easy to use an alternative context. In current Rails, this is a bit clumsy.Also, I like having all the helper context in a separate object. Love tilt's rendering simplicity
#render(locals, context)
but I believe this is similar in Rails now.What will feel clumsy for many people is that they need to define the
Context
class. IMO, the context should be the controller instance. In cells, this is done really easily and as the cell instance doesn't contain all the HTTP/Streaming/Model/... dependencies as anActionController
instance does, it's pretty safe to use it as the context.Your proposal would actually simplify our code in cells :-)
I don't understand where is the 2nd solution? Can you point me to it, please?