The Crystal Programming Language Forum

Athena 0.14.0

Athena 0.14.0

This release focused improving the view layer of Athena.

View Layer Improvements

As outlined in the external documentation, Athena is powered by events. A core part of the framework is the view event, which is invoked when a controller action does NOT return an ART::Response. For example:

require "athena"

class ExampleController < ART::Controller
  @[ARTA::Get("/response")]
  def get_response : ART::Response
    # This gets returned as is without invoking the view layer.
    ART::Response.new {greeting: "Hello!"}.to_json
  end
  
  @[ARTA::Get("/non-response")]
  def get_response : NamedTuple(greeting: String)
    # This invokes the view layer in order to turn the NamedTuple into an ART::Response.
    # By default will be JSON serialized.
    {greeting: "Hello!"}
  end
end

ART.run

# GET /response      # => {"greeting":"Hello!"}
# GET /non-response  # => {"greeting":"Hello!"}

Previously there was not a way to customize the response headers while still invoking the view layer. There was also not a real nice way to handle multiple format responses. This releases introduces some new concepts/features to address these limitations.

ART::View

An ART::View represents an ART::Response, but in a format agnostic way. It is essentially the same as returning the data directly but allows customizing the response status/format/headers, etc. without needing to directly render the response content in the controller.

require "athena"

class HelloController < ART::Controller
  @[ARTA::Get("/:name")]
  def say_hello(name : String) : NamedTuple(greeting: String)
    {greeting: "Hello #{name}"}
  end

  @[ARTA::Get("/view/:name")]
  def say_hello_view(name : String) : ART::View(NamedTuple(greeting: String))
    # A convenience method is provided to easily create an `ART::View`.
    # The status could have also been set via the `ARTA::View` annotation.
    self.view({greeting: "Hello #{name}"}, :im_a_teapot)
  end
end

ART.run

# GET /Fred      # => 200 {"greeting":"Hello Fred"}
# GET /view/Fred # => 418 {"greeting":"Hello Fred"}

Content Negotiation

As mentioned in the last release thread, this release integrates the Negotiation component into Athena. The main feature this brings is content negotiation support; or in other words allowing the request’s format to be determined based on the request’s Accept HTTP header. This feature allows developing format agnostic controllers, deferring the rendering of the response content to outside of the controller.

By default only JSON is natively renderable by Athena. Additional formats may be made renderable via implementing an ART::View::FormatHandlerInterface for the desired format.

require "athena"
require "csv"

# Register a format handler for the `CSV` format,
# and register it as a service.
@[ADI::Register]
class CSVFormatHandler
  # Implement the interface.
  include Athena::Routing::View::FormatHandlerInterface

  # :inherit:
  def call(view_handler : ART::View::ViewHandlerInterface, view : ART::ViewBase, request : ART::Request, format : String) : ART::Response
    # Implement logic to transform the view data into an ART::Response in the CSV format.
  end

  # :inherit:
  def format : String
    "csv"
  end
end

See the negotiation component in the external documentation for more information; as well as a full example of how everything fits together.

ART::Request

Athena previously was monkey patching methods/functionality into the stdlib’s HTTP::Request type. However as more was added it started to prove hard to document and started to smell. The extended functionality has been extracted into a dedicated ART::Request type. Existing type restrictions will need to be updated.

This new type includes previously existing methods as well as some new ones. The new methods are mainly focused with getting/setting the request’s format, MIME type, or hostname.

Honorable Mentions

  • Various documentation improvements
  • Deprecated ACF::ConfigurationResolver in favor of directly injecting configuration types
  • Can now use Regexs as the ART::Config::CORS#allow_origin value
  • Introduced a dedicated ART::Response::Headers type to support additional functionality, and to make it more visible/easier to document
    • Also exposes an HTTP::Cookies to better support adding cookies to the response.

Checkout the release notes for a complete list of changes. As usual feel free to join me in the Athena Gitter channel if you have any suggestions, questions, or ideas. I’m also available on Discord (Blacksmoke16#0016) or via Email.

6 Likes