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-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.
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.schierbeck@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-core+unsubscribe@googlegroups.com. > To post to this group, send email to rubyonrails-core@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. > > >-- 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-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.
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. On Wednesday, May 29, 2013 7:38:13 PM UTC+2, Jeremy Kemper wrote:> > 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<javascript:> > > 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 <javascript:>. >> To post to this group, send email to rubyonra...@googlegroups.com<javascript:> >> . >> Visit this group at http://groups.google.com/group/rubyonrails-core?hl=en >> . >> For more options, visit https://groups.google.com/groups/opt_out. >> >> >> > >-- 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-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.
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 :-) On Wednesday, May 29, 2013 8:17:31 PM UTC+2, Daniel Schierbeck wrote:> > 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. > > On Wednesday, May 29, 2013 7:38:13 PM UTC+2, Jeremy Kemper wrote: >> >> 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. >>> >>> >>> >> >>-- 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-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.
By all means! On Thursday, May 30, 2013, Daniel Schierbeck wrote:> 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 :-) > > On Wednesday, May 29, 2013 8:17:31 PM UTC+2, Daniel Schierbeck wrote: > > 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. > > On Wednesday, May 29, 2013 7:38:13 PM UTC+2, Jeremy Kemper wrote: > > 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 i > > -- > 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-core+unsubscribe@googlegroups.com <javascript:_e({}, > ''cvml'', ''rubyonrails-core%2Bunsubscribe@googlegroups.com'');>. > To post to this group, send email to rubyonrails-core@googlegroups.com<javascript:_e({}, ''cvml'', ''rubyonrails-core@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. > > >-- 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-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.
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 the Google Groups "Ruby on Rails: Core" group. To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.
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 <steve@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-core+unsubscribe@googlegroups.com. > To post to this group, send email to rubyonrails-core@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 -- 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-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@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.