module Onyx::HTTP::View

Overview

A reusable HTTP view.

Views are usually rendered by the Middleware::Renderer invoking the #render method. You can use views either in raw endpoint procs or within Endpoint#call.

struct UserView
  include Onyx::HTTP::View

  def initialize(@user : User)
  end

  def render(context)
    context.response.content_type = "text/plain"
    context.response << "id: #{@user.id}, name: #{@user.name}"
  end
end

router.get "/user/:id" do |env|
  user = Onyx.query(User.where(id: env.request.path_params["id"].to_i))

  env.response.view = UserView.new(user)
  # or just return it
  UserView.new(user)
end

struct GetUser
  include Onyx::HTTP::Endpoint

  params do
    path do
      type id : Int32
    end
  end

  def call
    user = Onyx.query(User.where(id: params.path.id))
    return UserView.new(user)
  end
end

Apart from directly implementing the #render method, you can use the convenient macros:

If you have only one macro of these three called in a view, then the request's "Accept" header will be ignored and the view will always be rendered as specified:

# Will always be rendered as `{"foo":@foo}`
# with "Content-Type" set to `"application/json"`.
struct JSONView
  include Onyx::HTTP::View

  def initialize(@foo : String)
  end

  json foo: @foo
end

Otherwise, if you have multiple macros called, then the request's "Accept" header is considered and the rendering is determined by it. However, if the "Accept" header is absent or equals to "*/*", then the latter macro takes precendence:

# If `Accept` header has `application/json` entry with
# enough q-factor, then the JSON view would be rendered.
# Otherwise the same check will be made for `text/html` entry.
# If none succeeded, plain text is rendered then.
struct MultiView
  include Onyx::HTTP::View

  def initialize(@foo : String)
  end

  json foo: @foo
  template "./multi_view.html.ecr"
  text "foo: #{@foo}"
end

You can define multiple renderers of the same type, just alter the arguments. The accept argument is what defines the behaviour (it is stored as hash internally, that's why the latter takes precendence when "Accept" header is "*/*"):

struct TheView
  include Onyx::HTTP::View

  template("./view.html.ecr")
  template("./view.rss.ecr", content_type: "text/rss", accept: {"text/rss"})
end

Defined in:

onyx-http/view.cr

Instance Method Summary

Macro Summary

Instance Method Detail

abstract def render(context : HTTP::Server::Context) #

[View source]

Macro Detail

macro json(object, content_type = "application/json", accept = {"application/json"}, &block) #

Add JSON rendering to this view. It is expanded to this:

def render_to_json(io : IO)
  ({{object}}).to_json(io)
end

def to_json(builder : JSON::Builder)
  ({{object}}).to_json(builder)
end

[View source]
macro json(content_type = "application/json", accept = {"application/json"}, &block) #

Add JSON rendering with builder to this view.

struct TestView
  include Onyx::HTTP::View

  def initialize(@foo : String, @bar : Int32? = nil)
  end

  json do
    object do
      field "foo", @foo
      field "bar", @bar
    end
  end
end

[View source]
macro json(content_type = "application/json", accept = {"application/json"}, **object) #

Add JSON rendering to this view. It is expanded like this:

def to_application_json(io)
  ({{object}}).to_json(io)
end

[View source]
macro template(template, content_type = "text/html", accept = {"text/html"}) #

Add template rendering to this view. It is expanded like this:

def render_to_html(io : IO)
  Kilt.embed("#{__DIR__}/#{{{template}}}", io)
end

[View source]
macro text(value, content_type = "text/plain", accept = {"text/plain"}) #

Add plain text rendering to this view. It is expanded like this:

def render_to_plain(io : IO)
  io << ({{value}})
end

[View source]
macro xml(content_type = "application/xml", accept = {"application/xml"}, &block) #

Add XML rendering with builder to this view. See XML::Builder for methods.

struct TestView
  include Onyx::HTTP::View

  def initialize(@foo : String, @bar : Int32? = nil)
  end

  xml do
    element("foo", @foo) do
      attribute("bar", @bar)
    end
  end
end

[View source]