Updates in the Athenaverse
This update is a bit unique in that there is no grandiose refactor that architecturally moves the framework forward, but is instead more focused on polishing/improving upon what is already there. There is however a major focus on the Console Component, with many exciting new features being introduced.
Demo Application
The Skeleton repository was introduced in the last update as a way to get started with the framework more quickly. I’m pleased to announce the introduction of the new Demo Application that expands on the idea of the skeleton, but with a fully runnable example application. The demo application is a simple blog REST API application that can be used as reference to see what an Athena Framework application, including tests, looks like. It also serves as a good somewhat real world example that can be used to play test proposed changes.
Console
The console component had quite a lot of development as of late. Both in regards to new features, as well as fixing some bugs. Let’s dive right in!
Native Tab Completion
One of the more exciting features is the console component now supports native tab completion for both built-in and user-defined commands. It is also fully integrated into the commands provided by the framework component.
NOTE: For performance reasons, this feature is currently limited to real compiled binaries only. This may possibly change when/if the interpreter becomes more robust.
Using the demo application as an example, which provides a bin/console
binary out of the box (if you want to follow along it can be built via shards build
). We first will want to generate and source the completion helper script:
$ ./bin/console completion > completion.sh
$ source completion.bash
The completion script will detect your shell based on $SHELL
and generate the proper file for your shell (bash, zsh, or fish). This file can then be sourced directly, or moved to a location where it would be sourced automatically for all new shells going forward. Run ./bin/console completion --help
for more information on this aspect.
And that’s it! The console
binary will now tab complete command names, option names, and option/argument values if possible as we can see here:
Plus, because this is native tab completion, performance is very speedy and is able to handle large amounts of data, so no need to do any filtering within your command itself.
Progress Bars and Indicators
While it’s hard to follow tab completion, another feature I’m elated to announce is the ability to render progress bars and progress indicators. These are a great way to keep the user informed of the progress of the currently running task. Or in the case of indicators, at least let the user know progress is still being made, even if it is not known when it will finish.
Progress Bars
Progress bars can be nearly fully customized. In the following example we customize the format to leverage some emojis, span multiple lines, what the characters that make up the bar should be, and various messages to show as things progress.
Code
bar = ACON::Helper::ProgressBar.new output, 15
bar.format = %(<fg=cyan> %title:-37s% </>\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:24s%)
bar.bar_character = "<fg=green>▓</>"
bar.empty_bar_character = "<fg=red>░</>"
bar.progress_character = ""
bar.set_message "Starting the demo... fingers crossed", "title"
bar.start
15.times do
bar.advance
case bar.progress
when 6 then bar.set_message "Looks good to me...", "title"
when 15 then bar.set_message "Thanks, bye", "title"
end
sleep 0.2
end
bar.finish
Custom formats and placeholders may also be defined as needed. The built-in Athena Style also exposes an API to use a progress bar in the Athena suggested style.
Progress Indicators
Progress indicators on the other hand, are a lot simpler. They simply spin showing that progress is not stalled, without any progress information.
The characters used for the animation can be controlled, as can the format.
Output Sections Max Height
Output sections are a neat little feature that’s been around for a while that enables having creating sections that can be written to and cleared independently of one another. This can be useful to display multiple progress bars for example.
Output sections now have a feature in which their maximum height can be limited, causing the contents to scroll, for example with a height of 3
:
The height can be changed at any time, and even removed which causes the scrolled text to display all at once.
Bug Fixes
Various bugs, mainly related to incorrect display alignment/formatting have also been squashed in the latest release. See the release notes for more information.
Framework
While most of the work has been focused on the console component, the framework component has also seen some nice new features/fixes.
Trailing Slashes
A common gotcha for those new to Athena Framework has been in how trailing slashes in routes are handled. Defining a route such as /user/{id}
IS different than /user/{id}/
and as such would cause a 404
if your route has a trailing slash but the request does not, or vice versa. This scenario is now more gracefully handled by redirecting GET
and HEAD
requests with a trailing slash to the route without one if it exists, and vice versa. The best practice is still pick one way and be consistent, given other HTTP methods do not have this same treatment.
Multiple Route Annotations
It is now possible for a single controller action to have more than one routing annotation:
class MultipleRoutesSingleMethod < ATH::Controller
@[ARTA::Get("/multiple-routes")]
@[ARTA::Post("/multiple-routes")]
@[ARTA::Route("/multiple-routes", methods: "PATCH")]
def action : Nil; end
end
This can be useful in certain cases, tho it is still a best practice to have a more 1:1
relationships between routes and methods.
Clock
Last but not least, a new component has been introduced and already integrated into the other components as needed. The Athena::Clock component decouples an application from the system clock, making testing time-sensitive logic much easier. It also provides the ability for different types of clocks to be used depending no the use case, while not having the change anything in the underlying implementation.
class ExpirationChecker
def initialize(@clock : Athena::Clock::Interface); end
def expired?(valid_until : Time) : Bool
@clock.now > valid_until
end
end
clock = ACLK::Spec::MockClock.new Time.utc 2023, 9, 16, 15, 20
expiration_checker = ExpirationChecker.new clock
valid_until = Time.utc 2023, 9, 16, 15, 25
# valid_until is in the future, so not expired
expiration_checker.expired?(valid_until).should be_false
# Sleep for 10 minutes, so time is now 2023-09-16 15:30:00,
# time is instantly changes as if 10 minutes really passed
clock.sleep 10.minutes
expiration_checker.expired?(valid_until).should be_true
# Time can also be shifted, either into the future or past
clock.shift minutes: -20
# valid_until is in the future again, so not expired
expiration_checker.expired?(valid_until).should be_false
Administrative
Taking a step back from the code, the Athena ecosystem now includes CONTRIBUTING.md
and UPGRADING.md
files as applicable. For example, for information on how to upgrade to this newest version of the console
component, checkout it’s UPGRADING.md. Or for more general information on how Athena handles versioning, checkout the monorepo’s UPGRADING.md. This text will be hidden