# Athena Framework 0.15.0

**URL:** <https://forum.crystal-lang.org/t/athena-framework-0-15-0/4026>\
**Category:** News\
**Created:** [October 30, 2021, 6:05pm UTC](https://forum.crystal-lang.org/t/athena-framework-0-15-0/4026 "2021-10-30T18:05:33Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [October 30, 2021, 6:05pm UTC](https://forum.crystal-lang.org/t/athena-framework-0-15-0/4026/1 "2021-10-30T18:05:33Z")

</div>

# Athena Framework 0.15.0

This [release](https://github.com/athena-framework/athena/releases/tag/v0.15.0) focused on polishing existing features, preparing the Athena ecosystem for the future. With highlights including:

- Improved param converters
- Namespace renaming
- Console component

## Improved Param Converters

[ATH::ParamConverters](https://athenaframework.org/Framework/ParamConverter) are a unique and powerful feature of Athena Framework that has been made even more powerful with this release. A common use case for param converters is deserializing a request body into an object that can be provided to your controller action. However, in the past, such a feature needed to be manually defined within each application’s code. This is no longer the case and a [ATH::RequestBodyConverter](https://athenaframework.org/Framework/RequestBodyConverter) now comes built into the framework!

The implementation has also been made much more robust. You no longer need to manually specify the type to deserialize into as it is inferred from the parameter’s type restriction. Additionally, the built in converter handles both [ASR::Serializable](https://athenaframework.org/Serializer/Serializable) and [JSON::Serializable](https://crystal-lang.org/api/JSON/Serializable.html) types as well as removing the need to `include ASR::Model` in some contexts. A compile time error is raised if you try to use with a type that is not serializable.

> **Before**
>
> ```crystal
> # Define a base type so that it can be stored in an ivar.
> abstract struct DTO
> include ASR::Model
> end
> 
> struct UserCreate < DTO
> include AVD::Validatable
> include ASR::Serializable # CANNOT be JSON::Serializable
> 
> # Getters with validation constraints for the user's name, email, etc.
> end
> 
> @[ADI::Register]
> struct RequestBody < ART::ParamConverterInterface
> # A bunch of code from the cookbook.
> end
> 
> class UserController < ART::Controller
> @[ARTA::Post("/user")]
> @[ARTA::ParamConverter("user_create", converter: RequestBody, type: UserCreate)]
> def new_user(user_create : UserCreate) : UserCreate
> user_create
> end
> end
> 
> ```

> **After**
>
> ```crystal
> struct UserCreate
> include AVD::Validatable
> include ASR::Serializable # OR JSON::Serializable
> 
> # Getters with validation constraints for the user's name, email, etc.
> end
> 
> class UserController < ATH::Controller
> @[ATHA::Post("/user")]
> @[ATHA::ParamConverter("user", converter: ATH::RequestBodyConverter)]
> def new_user(user : User) : User
> user
> end
> end
> 
> ```

Behind the scenes, each param converter uses generics to know the type of the related action parameter. This can be used to provide compile time type safety. I.e. if the converter only can handle parameters of a specific type:

> **Type Safe Converters**
>
> ```crystal
> @[ADI::Register]
> class MultiplyConverter < ATH::ParamConverter
> configuration multiplier : Int32
> 
> # :inherit:
> def apply(request : ATH::Request, configuration : Configuration(Int32)) : Nil
> # Multiply and re-set the attribute by some multiplier defined in the configuration
> end
> 
> # :inherit:
> def apply(request : ATH::Request, configuration : Configuration(T)) : Nil forall T
> {% T.raise "MultiplyConverter does not support arguments of type '#{T}'." %}
> end
> end
> 
> ```

In this example the second argument of the first `#apply` method is set to an `Int32` generic argument. This will restrict that method to only handle instances where the param converter was applied to an argument of type `Int32`. The second overload handles all other types as it is a free variable. This overload will raise a compile time error if the converter is applied to an argument that is _NOT_ an `Int32`. Ultimately, this makes the converter compile time safe.

See the API documentation for more information/examples.

## Namespace changes

Another major change of this release is renaming of the namespace the framework lives in. The main reason for this is that previously the framework code was living in the `Routing` namespace. This would conflict with the future `Routing` component. It also didn’t make sense to have framework level code living in that namespace anyway, given the majority of it does not have anything to do with routing persay.

There are no functional changes, so find & replace all should be sufficient to upgrade:

- `ARTA` =\> `ATHA`
- `ART` =\> `ATH`
- `Athena::Routing` =\> `Athena::Framework`

> **NOTE:** Routing/URL generation types still use `Athena::Routing`/`ART` names as they will eventually be moved into the proper `Routing` component

The actual `Routing` component is currently in a prototype stage, but the preliminary results are looking pretty good 🙂:

> **Prototype Router Benchmark**
>
> ```sh
> /get
> retour 3.06M (326.45ns) (± 1.73%) 576B/op 7.25× slower
> amber 3.48M (287.22ns) (± 0.72%) 386B/op 6.38× slower
> radix 17.72M ( 56.42ns) (± 0.87%) 112B/op 1.25× slower
> Athena 22.21M ( 45.02ns) (± 0.87%) 64.0B/op fastest
> 
> /get/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z
> retour 1.38M (722.22ns) (± 0.68%) 576B/op 15.30× slower
> amber 353.68k ( 2.83µs) (± 4.54%) 4.03kB/op 59.90× slower
> radix 2.28M (438.36ns) (± 0.85%) 273B/op 9.29× slower
> Athena 21.18M ( 47.20ns) (± 3.68%) 64.0B/op fastest
> 
> /get/books/23/chapters
> retour 1.13M (886.38ns) (± 2.78%) 944B/op 6.20× slower
> amber 1.06M (946.37ns) (± 3.46%) 1.23kB/op 6.62× slower
> radix 1.70M (587.39ns) (± 1.51%) 432B/op 4.11× slower
> Athena 6.99M (143.04ns) (± 2.15%) 97.0B/op fastest
> 
> ```

## Console Component

In other news from the Athena Ecosystem, I’m elated to announce initial release of the [Athena::Console](https://athenaframework.org/Console/) component! It allows the creation of CLI based [commands](https://athenaframework.org/Console/Command), with plenty of abstractions, extension points, features, and helpers to make that process easier. Both to create and to maintain.

> **Example Command**
>
> ```crystal
> class AddCommand < ACON::Command
> @@default_name = "add"
> @@default_description = "Sums two numbers, optionally making making the sum negative"
> 
> protected def configure : Nil
> self
> .argument("value1", :required, "The first value")
> .argument("value2", :required, "The second value")
> .option("negative", description: "If the sum should be made negative")
> end
> 
> protected def execute(input : ACON::Input::Interface, output : ACON::Output::Interface) : ACON::Command::Status
> sum = input.argument("value1", Int32) + input.argument("value2", Int32)
> 
> sum = -sum if input.option "negative", Bool
> 
> output.puts "The sum of the values is: #{sum}"
> 
> ACON::Command::Status::SUCCESS
> end
> end
> 
> ```

While there is too much to cover in this thread, a few highlights include:

- Robust [input](https://athenaframework.org/Console/Input/Interface/) parsing, that includes:
  - Type safe conversion logic of input values to their desired type
  - Optional, required, and array arguments
  - no-value, optional/required value, negatable, and array options
    - Both short and long flags with stacked short flag option support

- Independently managed output [sections](https://athenaframework.org/Console/Output/Section)
- Asking [`ACON::Question`](https://athenaframework.org/Console/Question/#Athena::Console::Question)s
  - Such as confirmation, single answer, and (multiple)choice

- Output [styling](https://athenaframework.org/Console/Formatter/OutputStyleInterface/#Athena::Console::Formatter::OutputStyleInterface), with custom style support
  - `output.puts "<error>foo</error>"`

- High level reusable formatting [styles](https://athenaframework.org/Console/Style/Interface/#Athena::Console::Style::Interface)
  - Such as for printing blocks, titles, lists, etc.

- [Testing abstractions](https://athenaframework.org/Console/Spec/#Athena::Console::Spec)

> **Example Test**
>
> ```crystal
> require "spec"
> require "athena-spec"
> 
> describe AddCommand do
> describe "#execute" do
> it "without negative option" do
> tester = ACON::Spec::CommandTester.new AddCommand.new
> tester.execute value1: 10, value2: 7
> tester.display.should eq "The sum of the values is: 17\n"
> end
> 
> it "with negative option" do
> tester = ACON::Spec::CommandTester.new AddCommand.new
> tester.execute value1: -10, value2: 5, "--negative": nil
> tester.display.should eq "The sum of the values is: 5\n"
> end
> end
> end
> 
> ```

The component is already quite feature rich, but there is still more to do, so keep an eye out for new versions that could contain things like:

- Progress bars
- Tables
- Eventing
- Signal aware commands
- And much more!

* * *

Checkout the [release](https://github.com/athena-framework/athena/releases/tag/v0.15.0) notes for a complete list of changes. As usual feel free to join me in the Athena [Gitter](https://gitter.im/athena-frameworkcr/community) channel if you have any suggestions, questions, or ideas. I’m also available on Discord (`Blacksmoke16#0016`) or via [Email](mailto:george@dietrich.app).

P.S. Athena now has a [Discord server](https://discord.gg/TmDVPb3dmr)!
