Re: [Rails-core] Changing the way Rails renders views

112 views
Skip to first unread message

Jeremy Kemper

unread,
May 29, 2013, 1:38:13 PM5/29/13
to rubyonra...@googlegroups.com
Go for it, Daniel!

I think you'll find that handling references to local & instance vars is the fly in the ointment. Method-missing doesn't cut it since it's very low precedence compared to a real local var.


On Wed, May 29, 2013 at 7:41 AM, Daniel Schierbeck <daniel.s...@gmail.com> wrote:
Hi guys,

I think this may be too daunting a task, but I'd like to propose a change to the way views are rendered in Rails.

Currently, a template handler converts a template to a string containing Ruby code, which then gets evaluated in a context that has certain variables present. The code is actually stored as a method on a module as far as I can see.

This is all well and good, but I'm interested in using multi-gets to efficiently cache the rendering of a collection of objects. Right now I see no easy way to do it in PartialRenderer, since the state of the rendering is shared between each render. The current code is something like this:

  collection.map do |object|
    locals[as] = object
    template.render(view, locals) # returns a string.
  end

I think the code would be more robust, maintainable, and generally just easier to work with if the compiled views were instead classes that looked like this:

  class SomeAnonymousCompiledView
    def initialize(context, locals = {})
      @context, @locals = context, locals
    end

    def render
      output_buffer = ""
      output_buffer << "<h1>I was rendered by ERB!</h1>"
      output_buffer << some_local
      output_buffer
    end

    # Template handlers can optionally define a cache_key method. This could e.g.
    # encapsulate the stuff currently handled by Cache Digests, but could also use
    # the locals.
    def cache_key
      @locals[:fiddle]
    end

    def method_missing(*args)
      # This could be a way to handle local lookups.
    end
  end

The project I'm working on, Curly, is a template handler that has a built-in concept of presenters. Say there's a template "posts/show.html.curly" containing the following:

  <h1>{{title}}</h1>

It would have a matching presenter in "app/presenters/posts/show_presenter.rb":

  class Posts::ShowPresenter < Curly::Presenter
    presents :post  # the controller would assign @post

    def title # the return value is inserted in the template when rendered
      post.title
    end

    # Right now Curly generates code that checks the cache key and uses
    # Action View's existing cache system with the given key. This cannot work
    # when rendering a collection, unfortunately.
    def cache_key
      post
    end
  end

I'd love to have my template handler generate the following class:

  class CompiledPostsShowView
    def initialize(context, locals = {})
      @context, @locals = context, locals
    end

    def render
      @rendered ||= "<h1>#{presenter.title}</h1>"
    end

    # This would be used automatically by Rails if defined.
    def cache_key
      presenter.cache_key
    end

    private

    def presenter
      @presenter ||= Curly.presenter_for_path(@context.virtual_path).new(@context, @locals)
    end
  end

By wrapping each instance of a rendering in an object, we can encapsulate and carry around the state, reusing the cache key in multiple locations.

I'm not sure how easy it would be to retrofit this on top of ERB, but it should be possible.

I'd be willing to put in the work, I just want to know if this is something Core is interested in.

Cheers,
Daniel (@dasch)

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Daniel Schierbeck

unread,
May 29, 2013, 2:17:31 PM5/29/13
to rubyonra...@googlegroups.com
That's my biggest worry as well. I'll give it a shot - I have an idea for how I can cut the problem into smaller steps.

Daniel Schierbeck

unread,
May 30, 2013, 4:49:18 AM5/30/13
to rubyonra...@googlegroups.com
Actually, the assigns didn't seem to be a major issue. I'm struggling a bit with the output buffering though.

Can I start a Work In Progress PR on GitHub? I have a few ideas for how to proceed, but I'd like to get some feedback soon :-)

Jeremy Kemper

unread,
May 30, 2013, 8:36:23 AM5/30/13
to rubyonra...@googlegroups.com
By all means!
--

Steve Klabnik

unread,
May 30, 2013, 1:51:34 PM5/30/13
to rubyonra...@googlegroups.com
Yup, if you have some code, a WIP PR is a great way to do it.

Daniel Schierbeck

unread,
May 31, 2013, 4:03:53 AM5/31/13
to rubyonra...@googlegroups.com
Okay, I've started a PR at https://github.com/rails/rails/pull/10807

It's still a bit in flux, but I'd like to understand how output buffers are handled. Since each rendering is now scoped to a single object, there's a lot less to keep track of.

I also still have to copy some stuff over from Template, so it's very early.


On Thu, May 30, 2013 at 7:51 PM, Steve Klabnik <st...@steveklabnik.com> wrote:
Yup, if you have some code, a WIP PR is a great way to do it.

--
You received this message because you are subscribed to a topic in the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/rubyonrails-core/ecOTKuZG8_I/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to rubyonrails-co...@googlegroups.com.

To post to this group, send email to rubyonra...@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.





--
Med venlig hilsen
Daniel Schierbeck
Reply all
Reply to author
Forward
0 new messages