Athena 0.12.0
This release focused on polishing and improving existing core functionality. Highlights include a reworked parameter system, URL generation, and improved external documentation.
Website
As an ongoing effort to make Athena easier to use, I spent some time improving its documentation. Up until now everything has been documented within the generated API docs of each component, with some things here and there being more general. However, while the documentation for each component was quite good, there wasn’t anything to tie everything together.
In order to solve this, I created and launched a new website to include external Athena documentation. The website includes:
- An introduction to the Athena Framework, and to Athena itself; including high level feature overview and links to various resources
- An overview of how Athena is designed; following the flow that each request makes
- Subsections talk about how the various components are integrated into Athena
- A cookbook for sharing commonly useful types
- The getting started has also been moved to the external documentation
Beta Website
In addition to the official website, a beta version has also been launched as a means to beta test unified documentation. I.e. external documentation + API documentation within the same website. The goal of the beta site is to gather feedback around the concept to allow for improvements to be made without affecting the usability/processes already in place. It also serves as an example of what could be the future for Crystal API documentation, outside of the Athena Framework.
Shoutout to @oprypin for his ongoing mkdocs plugin project that powers the API documentation generation.
Enhanced Parameters
One of the more exciting features of this release is the work done to improve the usability/functionality of parameters, namely ART::QueryParam. Previously, the @[ART::QueryParam]
annotation did not actually do anything; nor did it offer any functionality other than serving as a marker that a given action uses one.
require "athena"
class ExampleController < ART::Controller
# A `requirements` field allows valdiating the parameter's value against a Regex pattern.
@[ART::Get("/regex")]
@[ART::QueryParam("page", requirements: /\d{2}/)]
def index(page : Int32) : Int32
page
end
# An `AVD::Constraint` may also be used.
@[ART::Get("/constraint")]
@[ART::QueryParam("page", requirements: @[Assert::PositiveOrZero])]
def index2(page : Int32) : Int32
page
end
# Arguments that are nilable (or have a default value) are considered optional.
@[ART::Get("/optional")]
@[ART::QueryParam("page")]
def index3(page : Int32?) : Int32?
page
end
end
ART.run
# GET /regex # => {"code":422,"message":"Parameter 'page' of value '' violated a constraint: 'This value should not be null.'\n"}
# GET /regex?page=10 # => 10
# GET /regex?page=bar # => {"code":400,"message":"Required parameter 'page' with value 'bar' could not be converted into a valid 'Int32'."}
# GET /regex?page=5 # => {"code":422,"message":"Parameter 'page' of value '5' violated a constraint: 'Parameter 'page' value does not match requirements: (?-imsx:^(?-imsx:\\d{2})$)'\n"}
# GET /constraint?page=-5 # => {"code":422,"message":"Parameter 'page' of value '-9' violated a constraint: 'This value should be positive or zero.'\n"}
# GET /optional # => null
# GET /optional?page=2 # => 2
Additional parameter arguments include:
- incompatibles which represent parameters that cannot be present at the same time.
- map allows providing arrays of values to a controller action, running validations against each individual value
- converter parameters can now have dedicated ART::ParamConverterInterfaces
In addition to these improvements, this release also introduces a new parameter type: ART::RequestParam which represents form data within the request body.
require "athena"
class ExampleController < ART::Controller
@[ART::Post(path: "/login")]
@[ART::RequestParam("username")]
@[ART::RequestParam("password")]
def login(username : String, password : String) : Nil
# ...
end
end
ART.run
# POST /login, body: "username=George&password=abc123"
See the related API documentation for more information and examples.
URL Generation
Last but not least, Athena went through some internal refactors that enable some more advanced routing related features. The first of which is the ability to generate URLs for the provided route and passed in parameters. A helper method was also added to ART::Controller to make this easier. See the related API documentation for more information.
require "athena"
class ExampleController < ART::Controller
@[ART::Get("/add/:value1/:value2", name: "add")]
def add(value1 : Int32, value2 : Int32, negative : Bool = false) : Int32
0
end
@[ART::Get("/")]
def get_link : String
self.generate_url "add", value1: 10, value2: 5 # => /add/10/5
# Another helper method also exists to redirect to another route
self.redirect_to_route "add", value1: 10, value2: 5
end
end
Honorable Mentions
A list of things worth mentioning but not big enough to warrant their own section:
- ART::Spec::APITestCase for integration testing controllers
- Responses are now written directly all at once, use ART::StreamedResponse in order to stream the response
- Include
charset=UTF-8
withincontent-type
header by default since strings are inherentlyUTF-8
- A few miscellaneous bug fixes
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.