[dry-view] Expose to layout


#1

I see that in dry-view exposures can’t be used in the layout. Is it the intended behaviour in order to have more separation of concerns between exposures and context?

This makes that, for example, in case you want to provide a custom HTML head title per page, having the <title> tag in the layout, you must configure a different context (child of a shared context) for each single view in order to override a title method.


#2

Thanks for raising this, @waiting-for-dev.

To be honest, not including the exposures in the layout’s scope just felt like the right thing to do, and it so happened to align with how we had always used dry-view, that is, to have a view controller superclass where the layout name is configured, inherited by a bunch of subclasses, one for each template. In this arrangement, if you had the layout expecting e.g. a title, you’d have to ensure every subclass provided its own title exposure, which feels pretty onerous. Also, it never seemed to make sense to me that a single layout file should be rendered with such a varying range of scopes (i.e. all the differing sets exposures for the view controller subclasses).

We of course wanted to do similar things around providing title information up into the layout, and we handled this by using a content_for-style approach, inspired by Rails’ so-named helper, e.g.

class MyContext
  def initialize
    @content_for = {}
  end

  def content_for(key, content = nil)
    if content || block_given?
      @content_for[key] =  block_given? ? yield : content
    else
      @content_for[key]
    end
  end
end

This allows us to set the title inside a template:

= content_for :title, "Checkout: Processing payment"

And have it available in the layout:

title
  - if content_for(:title)
    = "#{content_for(:title)} — "
  | My Cool Site

However, now that we support exposures being inherited, I think one could make an argument for including the exposures in the layout’s scope; if the layout was restricted only to work for the common exposures, you could define them as defaults in the superclass and only override them as necessary in the subclasses.

However, there’s part of me that’s still hesitant to provide such wildly varying scopes to a single template. I wonder if there’s another, better way we could handle this?

Either way, I do your use case is something we should make easier: let’s get something done before 1.0. Do you have any other ideas, @waiting-for-dev?


#3

In this arrangement, if you had the layout expecting e.g. a title , you’d have to ensure every subclass provided its own title exposure, which feels pretty onerous.

Are you sure? In my tests I have been trying to do that but it resulted in a missing method error.

We of course wanted to do similar things around providing title information up into the layout, and we handled this by using a content_for -style approach, inspired by Rails’ so-named helper, e.g.

I see. I didn’t think about this trick. But, to be honest, I don’t like it too much. Mainly because it is going beyond the functional object pattern, as it is using state for something more than dependencies and configuration. It also feels too much implicit for my taste. I mean, this relies on the fact that both template and layout use the same context instance and that it is used first in the template and later in the layout. I would say that it is kind of depending on an implementation detail.

Either way, I do your use case is something we should make easier: let’s get something done before 1.0. Do you have any other ideas, @waiting-for-dev

I see your point. I haven’t thought about it thoroughly, but I feel here we are mixing behaviour and design concepts. I mean, when I first saw it I liked the idea about having different things for working with template variables (exposures) and layout/template variables (context). But, besides that, there is a behavioural and intentional difference between both concepts: exposures are more convenient to work with thanks to its DSL, and context is intended to be used as “baseline render context” (as said in the docs). Maybe it would be nice to have both concepts working in the same way (maybe both DSL or class?), and leave out the intentional difference. After all, if we treat functions like actual first class citizens, as plain values, that intentional difference doesn’t make too much sense.