Class: Dry::View Abstract

Inherits:
Object
  • Object
show all
Extended by:
Configurable, Core::Cache
Defined in:
lib/dry/view.rb,
lib/dry/view/part.rb,
lib/dry/view/path.rb,
lib/dry/view/tilt.rb,
lib/dry/view/scope.rb,
lib/dry/view/context.rb,
lib/dry/view/version.rb,
lib/dry/view/exposure.rb,
lib/dry/view/rendered.rb,
lib/dry/view/renderer.rb,
lib/dry/view/tilt/erb.rb,
lib/dry/view/exposures.rb,
lib/dry/view/tilt/haml.rb,
lib/dry/view/tilt/erbse.rb,
lib/dry/view/part_builder.rb,
lib/dry/view/scope_builder.rb,
lib/dry/view/render_environment.rb,
lib/dry/view/decorated_attributes.rb,
lib/dry/view/render_environment_missing.rb

Overview

This class is abstract.

Subclass this and provide your own configuration and exposures to define your own view (along with a custom #initialize if you wish to inject dependencies into your subclass)

A standalone, template-based view rendering system that offers everything you need to write well-factored view code.

This represents a single view, holding the configuration and exposures necessary for rendering its template.

Defined Under Namespace

Modules: DecoratedAttributes, Tilt Classes: Context, Exposure, Exposures, Part, PartBuilder, Path, RenderEnvironment, RenderEnvironmentMissing, Rendered, Renderer, Scope, ScopeBuilder

Constant Summary collapse

UndefinedTemplateError =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Class.new(StandardError)
DEFAULT_RENDERER_OPTIONS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

{default_encoding: "utf-8"}.freeze
VERSION =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"0.6.0"

Instance Attribute Summary collapse

Configuration collapse

Exposures collapse

Render environment collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeView

Returns an instance of the view. This binds the defined exposures to the view instance.

Subclasses can define their own #initialize to accept injected dependencies, but must call super() to ensure the standard view initialization can proceed.



442
443
444
# File 'lib/dry/view.rb', line 442

def initialize
  @exposures = self.class.exposures.bind(self)
end

Instance Attribute Details

#exposuresExposures (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The view's bound exposures

Returns:



432
433
434
# File 'lib/dry/view.rb', line 432

def exposures
  @exposures
end

Class Method Details

.config.default_context=(context) ⇒ Object

Set the default context object to use when rendering. This will be used unless another context object is applied at render-time to View#call

Defaults to a frozen instance of Dry::View::Context.

Parameters:

See Also:



123
# File 'lib/dry/view.rb', line 123

setting :default_context, Context.new.freeze

.config.default_format=(format) ⇒ Object

Set the default format to use when rendering.

Defaults to :html.

Parameters:

  • format (Symbol)


133
# File 'lib/dry/view.rb', line 133

setting :default_format, :html

.expose(name, **options, &block) ⇒ Object .expose(name, **options) ⇒ Object .expose(name, **options) ⇒ Object .expose(*names, **options) ⇒ Object

Overloads:

  • .expose(name, **options, &block) ⇒ Object

    Define a value to be passed to the template. The return value of the block will be decorated by a matching Part and passed to the template.

    The block will be evaluated with the view instance as its self. The block's parameters will determine what it is given:

    • To receive other exposure values, provide positional parameters matching the exposure names. These exposures will already by decorated by their Parts.
    • To receive the view's input arguments (whatever is passed to View#call), provide matching keyword parameters. You can provide default values for these parameters to make the corresponding input keys optional
    • To receive the Context object, provide a context: keyword parameter
    • To receive the view's input arguments in their entirety, provide a keywords splat parameter (i.e. **input)

    Examples:

    Accessing input arguments

    expose :article do |slug:|
      article_repo.find_by_slug(slug)
    end
    

    Accessing other exposures

    expose :articles do
      article_repo.listing
    end
    
    expose :featured_articles do |articles|
      articles.select(&:featured?)
    end
    

    Parameters:

    • name (Symbol)

      name for the exposure

    • options (Hash)

      the exposure's options

    Options Hash (**options):

    • :layout (Boolean)

      expose this value to the layout (defaults to false)

    • :decorate (Boolean)

      decorate this value in a matching Part (defaults to true)

    • :as (Symbol, Class)

      an alternative name or class to use when finding a matching Part

  • .expose(name, **options) ⇒ Object

    Define a value to be passed to the template, provided by an instance method matching the name. The method's return value will be decorated by a matching Part and passed to the template.

    The method's parameters will determine what it is given:

    • To receive other exposure values, provide positional parameters matching the exposure names. These exposures will already by decorated by their Parts.
    • To receive the view's input arguments (whatever is passed to View#call), provide matching keyword parameters. You can provide default values for these parameters to make the corresponding input keys optional
    • To receive the Context object, provide a context: keyword parameter
    • To receive the view's input arguments in their entirey, provide a keywords splat parameter (i.e. **input)

    Examples:

    Accessing input arguments

    expose :article
    
    def article(slug:)
      article_repo.find_by_slug(slug)
    end
    

    Accessing other exposures

    expose :articles
    expose :featured_articles
    
    def articles
      article_repo.listing
    end
    
    def featured_articles
      articles.select(&:featured?)
    end
    

    Parameters:

    • name (Symbol)

      name for the exposure

    • options (Hash)

      the exposure's options

    Options Hash (**options):

    • :layout (Boolean)

      expose this value to the layout (defaults to false)

    • :decorate (Boolean)

      decorate this value in a matching Part (defaults to true)

    • :as (Symbol, Class)

      an alternative name or class to use when finding a matching Part

  • .expose(name, **options) ⇒ Object

    Define a single value to pass through from the input data (when there is no instance method matching the name). This value will be decorated by a matching Part and passed to the template.

    Parameters:

    • name (Symbol)

      name for the exposure

    • options (Hash)

      the exposure's options

    Options Hash (**options):

    • :layout (Boolean)

      expose this value to the layout (defaults to false)

    • :decorate (Boolean)

      decorate this value in a matching Part (defaults to true)

    • :as (Symbol, Class)

      an alternative name or class to use when finding a matching Part

    • :default (Boolean)

      a default value to provide if there is no matching input data

  • .expose(*names, **options) ⇒ Object

    Define multiple values to pass through from the input data (when there is no instance methods matching their names). These values will be decorated by matching Parts and passed through to the template.

    The provided options will be applied to all the exposures.

    Parameters:

    • names (Symbol)

      names for the exposures

    • options (Hash)

      the exposure's options

    Options Hash (**options):

    • :layout (Boolean)

      expose this value to the layout (defaults to false)

    • :decorate (Boolean)

      decorate this value in a matching Part (defaults to true)

    • :as (Symbol, Class)

      an alternative name or class to use when finding a matching Part

    • :default (Boolean)

      a default value to provide if there is no matching input data

See Also:



336
337
338
339
340
341
342
343
344
# File 'lib/dry/view.rb', line 336

def self.expose(*names, **options, &block)
  if names.length == 1
    exposures.add(names.first, block, options)
  else
    names.each do |name|
      exposures.add(name, options)
    end
  end
end

.exposuresExposures

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the defined exposures. These are unbound, since bound exposures are only created when initializing a View instance.

Returns:



356
357
358
# File 'lib/dry/view.rb', line 356

def self.exposures
  @exposures ||= Exposures.new
end

.config.inflector=(inflector) ⇒ Object

Set an inflector to provide to the part_builder and scope_builder.

Defaults to Dry::Inflector.new.

Parameters:

  • inflector


185
# File 'lib/dry/view.rb', line 185

setting :inflector, Dry::Inflector.new

.inherited(klass) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



223
224
225
226
227
228
# File 'lib/dry/view.rb', line 223

def self.inherited(klass)
  super
  exposures.each do |name, exposure|
    klass.exposures.import(name, exposure)
  end
end

.config.layout=(name) ⇒ Object

Set the name of the layout to render templates within. Layouts will be looked up within the configured layouts_dir, within the configured paths.

A false or nil value will use no layout. Defaults to nil.

Parameters:

  • name (String, FalseClass, nil)

    layout name, or false to indicate no layout



88
# File 'lib/dry/view.rb', line 88

setting :layout, false

.layout_env(format: config.default_format, context: config.default_context) ⇒ RenderEnvironment

Returns a render environment for the view and the given options, chdir'ed into the view's layout directory. This is the environment used when rendering the view's layout.

Parameters:

  • format (Symbol) (defaults to: config.default_format)

    template format to use (defaults to the default_format setting)

  • context (Context) (defaults to: config.default_context)

    context object to use (defaults to the default_context setting)

Returns:



403
404
405
# File 'lib/dry/view.rb', line 403

def self.layout_env(**args)
  render_env(**args).chdir(layout_path)
end

.layout_pathObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



422
423
424
# File 'lib/dry/view.rb', line 422

def self.layout_path
  File.join(config.layouts_dir, config.layout)
end

.config.layouts_dir=(dir) ⇒ Object

Set the name of the directory (within the configured paths) holding the layouts. Defaults to "layouts"

Parameters:

  • dir (String)

    directory name



97
# File 'lib/dry/view.rb', line 97

setting :layouts_dir, "layouts"

.config.part_builder=(part_builder) ⇒ Object

Set a custom part builder class

Parameters:

  • part_builder (Class)

See Also:



154
# File 'lib/dry/view.rb', line 154

setting :part_builder, PartBuilder

.config.scope_namespace=(namespace) ⇒ Object

Set a namespace that will be searched when building scope classes.

Parameters:

  • namespace (Module, Class)

See Also:



144
# File 'lib/dry/view.rb', line 144

setting :part_namespace

.config.paths=(paths) ⇒ Object

Set an array of directories that will be searched for all templates (templates, partials, and layouts).

These will be converted into Path objects and used for template lookup when rendering.

This is a required setting.

Parameters:

  • paths (String, Path, Array<String, Path>)

    the paths



63
64
65
# File 'lib/dry/view.rb', line 63

setting :paths do |paths|
  Array(paths).map { |path| Path[path] }
end

.private_expose(*names, **options, &block) ⇒ Object



347
348
349
# File 'lib/dry/view.rb', line 347

def self.private_expose(*names, **options, &block)
  expose(*names, **options, private: true, &block)
end

.render_env(format: config.default_format, context: config.default_context) ⇒ RenderEnvironment

Returns a render environment for the view and the given options. This environment isn't chdir'ed into any particular directory.

Parameters:

  • format (Symbol) (defaults to: config.default_format)

    template format to use (defaults to the default_format setting)

  • context (Context) (defaults to: config.default_context)

    context object to use (defaults to the default_context setting)

Returns:

See Also:



375
376
377
# File 'lib/dry/view.rb', line 375

def self.render_env(format: config.default_format, context: config.default_context)
  RenderEnvironment.prepare(renderer(format), config, context)
end

.renderer(format) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns renderer for the view and provided format



410
411
412
413
414
415
416
417
418
419
# File 'lib/dry/view.rb', line 410

def self.renderer(format)
  fetch_or_store(:renderer, config, format) {
    Renderer.new(
      config.paths,
      format: format,
      engine_mapping: config.renderer_engine_mapping,
      **config.renderer_options,
    )
  }
end

.config.renderer_engine_mapping=(mapping) ⇒ Object

A hash specifying the (Tilt-compatible) template engine class to use for a given format. Template engine detection is automatic based on format; use this setting only if you want to force a non-preferred engine.

Examples:

config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}

Parameters:

  • mapping (Hash<Symbol, Class>)

    engine mapping

See Also:



218
# File 'lib/dry/view.rb', line 218

setting :renderer_engine_mapping

.config.renderer_options=(options) ⇒ Object

A hash of options to pass to the template engine. Template engines are provided by Tilt; see Tilt's documentation for what options your template engine may support.

Defaults to {default_encoding: "utf-8"}. Any options passed will be merged onto the defaults.

Parameters:

  • options (Hash)

    renderer options

See Also:



200
201
202
# File 'lib/dry/view.rb', line 200

setting :renderer_options, DEFAULT_RENDERER_OPTIONS do |options|
  DEFAULT_RENDERER_OPTIONS.merge(options.to_h).freeze
end

.config.scope=(scope_class) ⇒ Object

Set the scope class to use when rendering the view's template.

Configuring a custom scope class allows you to provide extra behaviour (alongside exposures) to the template.

Parameters:

  • scope_class (Class)

    scope class (inheriting from Dry::View::Scope)

See Also:



110
# File 'lib/dry/view.rb', line 110

setting :scope

.config.scope_builder=(scope_builder) ⇒ Object

Set a custom scope builder class

Parameters:

  • scope_builder (Class)

See Also:



175
# File 'lib/dry/view.rb', line 175

setting :scope_builder, ScopeBuilder

.config.scope_namespace=(namespace) ⇒ Object

Set a namespace that will be searched when building scope classes.

Parameters:

  • namespace (Module, Class)

See Also:



165
# File 'lib/dry/view.rb', line 165

setting :scope_namespace

.config.template=(name) ⇒ Object

Set the name of the template for rendering this view. Template name should be relative to the configured paths.

This is a required setting.

Parameters:

  • name (String)

    template name



76
# File 'lib/dry/view.rb', line 76

setting :template

.template_env(format: config.default_format, context: config.default_context) ⇒ RenderEnvironment

Returns a render environment for the view and the given options, chdir'ed into the view's template directory. This is the environment used when rendering the template, and is useful to to fetch independently when unit testing Parts and Scopes.

Parameters:

  • format (Symbol) (defaults to: config.default_format)

    template format to use (defaults to the default_format setting)

  • context (Context) (defaults to: config.default_context)

    context object to use (defaults to the default_context setting)

Returns:



390
391
392
# File 'lib/dry/view.rb', line 390

def self.template_env(**args)
  render_env(**args).chdir(config.template)
end

Instance Method Details

#call(format: config.default_format, context: config.default_context, **input) ⇒ Rendered

Render the view

Parameters:

  • format (Symbol) (defaults to: config.default_format)

    template format to use

  • context (Context) (defaults to: config.default_context)

    context object to use

  • input

    input data for preparing exposure values

Returns:

Raises:



461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# File 'lib/dry/view.rb', line 461

def call(format: config.default_format, context: config.default_context, **input)
  raise UndefinedTemplateError, "no +template+ configured" unless config.template

  env = self.class.render_env(format: format, context: context)
  template_env = self.class.template_env(format: format, context: context)

  locals = locals(template_env, input)
  output = env.template(config.template, template_env.scope(config.scope, locals))

  if layout?
    layout_env = self.class.layout_env(format: format, context: context)
    output = layout_env.template(self.class.layout_path, layout_env.scope(config.scope, layout_locals(locals))) { output }
  end

  Rendered.new(output: output, locals: locals)
end

#configObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The view's configuration



449
450
451
# File 'lib/dry/view.rb', line 449

def config
  self.class.config
end