Should Rails have concrete ApplicationView class?

Rails for a long time had ApplicationController class. Since Rails 4, it also now has ApplicationModel class. But views are still anonymous classes built at runtime.

Debugger in view rendering phase:

self.class

=> #Class:0x007fc0096562f8 < ActionView::Base

This is hard to extend and understand what’s inside. For example, if I want to build a custom view renderer that can work independently of controller context (in our case, make it render stuff asynchronously in delayed job), but still have access to the application helpers, routes, and all the other custom code we have in our views.

How worthwhile would it be to have a concrete ApplicationView < ActionView::Base, returning something like:

self.class

=> #<ApplicationView:0x007fc79dd20b88 …>

Regular views would still work as before, but now if some custom class extends ApplicationView, they would get the routes, helpers, etc. out of the box in same configuration as the above anonymous class example has them, instead of having to subclass from ActionView::Base (a skeleton class without any app-specific configuration) and have configure it manually to match what their regular Rails views would have. Convention over Configuration?

Thanks!

This is hard to extend and understand what’s inside. For example, if I want to build a custom view renderer that can work independently of controller context (in our case, make it render stuff asynchronously in delayed job), but still have access to the application helpers, routes, and all the other custom code we have in our views.

You mean instead of having to override ::view_context_class in a controller you could just extend your ApplicationView class directly?

Nick

Didn’t hear of ::view_context_class before, but yes, I’d like to be able to subclass and use the ApplicationView class outside of regular controllers, while still using all the tools normally only available in the controller, like helper loading:

class MyRenderer < ApplicationView::Base
end

MyRenderer.render(’/users/index’)

The above should be as smart as regular view rendering is vis-a-vis which helpers/contex to load, for example:

  • All app route helper methods (_url and _path) should be available without having to custom-require them.
  • The same view paths should be able as those available from regular controller-base view renders.
  • It should load the same helpers that would be available had an action been rendered from UsersController#index. And custom helpers should be loadable in same way as in controller:
    class MyRenderer < ApplicationView::Base
    helper :all
    end

Assigns and methods should be settable directly on the renderer:

class MyRenderer < ApplicationView::Base
def initialize(user_id)
super # if needed

@user_id = user_id
@organizations = current_user.organizations

end

def current_user
User.find(@user_id)
end
end

Locals should be passed to render method as usual, and possible to set defaults from object:

class MyRenderer < ApplicationView::Base
def render(options={})
super.merge(some_local: ‘some_value’)
end
end

Then, the above renderer can be used anywhere, as well as be serialized and reused later in places like Delayed Job which doesn’t have request context.

In short, bring some reusable, object-oriented niceness to views, without having to reconfigure everything my app should already know about (because it’s already configured in regular controller-based views)

If there is existing way to do this kind of thing, please do tell.

Thanks.

Typo in above post, I meant < ApplicationView, not < ApplicationView::Base