Exposures

Define exposures within your view to declare and prepare the values to be passed to the template, decorated as parts.

An exposure can take a block:

class MyView < Dry::View
  expose :users do
    user_repo.listing
  end
end

Or refer to an instance method:

class MyView < Dry::View
  expose :users

  private

  def users
    user_repo.listing
  end
end

Or allow a matching value from the input data to pass through to the view:

class MyView < Dry::View
  # With no matching instance method, passes the `users:` argument provided to
  # `#call` straight to the template
  expose :users
end

Accessing input data

If your exposure needs to work with input data (i.e. the arguments passed to the view’s #call), specify these as keyword arguments for your exposure block. Make this a required keyword argument if you require the data passed to the view’s #call:

class MyView < Dry::View
  expose :users do |page:|
    user_repo.listing(page: page)
  end
end

The same applies to instance methods acting as exposures:

class MyView < Dry::View
  expose :users

  private

  def users(page:)
    user_repo.listing(page: page)
  end
end

Specifying defaults

To make input data optional, provide a default value for the keyword argument (either nil or something more meaningful):

class MyView < Dry::View
  expose :users do |page: 1|
    user_repo.listing(page: page)
  end
end

If your exposure passes through input data directly, use the default: option:

class MyView < Dry::View
  # With no matching instance method, passes the `users:` argument to `#call`
  # straight to the template
  expose :users, default: []
end

Accessing the context

To access the context object from an exposure, include a context: keyword parameter:

expose :articles do |context:|
  article_repo.listing_for_user(context.current_user)
end

Depending on other exposures

Sometimes you may want to prepare data for other exposures to use. You can depend on another exposure by naming it as a positional argument for your exposure block or method.

class MyView < Dry::View::Controller
  expose :users do |page:|
    user_repo.listing(page: page)
  end

  expose :user_count do |users|
    users.to_a.length
  end
end

In this example, the user_count exposure has access to the value of the users value since it named the exposure as a positional argument. The users value is at this point will already be decorated by its part object.

Exposure dependencies (positional arguments) and input data (keyword arguments) can also be provided together:

expose :user_count do |users, count_title: "Admins count"|
  "#{count_title}: #{users.to_a.length}"
end

Layout exposures

Exposure values are made available only to the template by default. To make an exposure available to the layout, specify the layout: true option:

expose :users, layout: true do |page:|
  user_repo.listing(page: page)
end

Private exposures

You can create private exposures that are not passed to the template. This is helpful if you have an exposure that others will depend on, but is not otherwise needed in the template. Use private_expose for this:

class MyView < Dry::View::Controller
  private_expose :user_listing do
    user_repo.listing
  end

  expose :users do |user_listing|
    # does something with user_listing
  end

  expose :user_count do |user_listing|
    # also needs to work with user_listing
  end
end

In this example, only users and user_count will be passed to the template.

Undecorated exposures

You can create an exposure whose value is not decorated by a part. This may be helpful when your exposure returns a simpler "primitive" object that requires no extra behaviour, like a number or a string. To do this, pass the decorate: false option.

expose :page_number, decorate: false