There were a ton of contributions merged in master even before 0.24.2 was released. But since 0.24.2 was already changing the release packaging for linux, changing the CI and fixing 0.24.1, some features needed to wait their turn a little longer.

Once again, we have tested this release by compiling some of the most popular crystal shards. This helps us catch and fix unintended breaking changes earlier in the release cycle as well as submitting PRs to the shards and contributing a bit more with the community. This process is codified using the scripts in the test-ecosystem repository, which is still fairly new, but so far it’s working well.

The least visible work usually goes in infrastructure and there are always improvements and things waiting to be done. The latest news regarding this area are:

Also on docs, lots of improvements regarding navigation have been done in #5229.

The automated release process now cares about 32 bits linux releases. As a bonus point the packaging has been aligned again with respect to the 64 bits packages. So some paths have changed.

We’ve been contacted by Heroku to early register our buildpack. Stay tuned to future Heroku news to update to the crystal-lang/crystal buildpack in the registry. All in all it’s one more taste of the adoption of Crystal out there, and we are thrilled.

Nightly packages in nightly.crystal-lang.org are still down. The workaround for now it to use the docker image crystallang/crystal:nightly.

Exciting features

Shards is updated to 0.8.0

There are some performance improvements in shards for this release, by downloading less information when possible. A new global cache was added, so you don’t need to download your favorite shards over and over on all of your favorites projects. FYI you can use shards 0.8.0 with Crystal 0.24.2 if you want.

Automatic casts for literal values

If a method is defined as def foo(x : Int8) or def bar(color : Color) with

enumColorRedGreenBlueend

up to 0.24 you would need to call them as foo(1i8) or bar(Color::Blue). But since 0.25.0 you will be able to foo(1) and bar(:blue). A note of caution: this only work with literal values. If the value is saved in a variable and used as an argument it won’t work.

This feature allows cleaner code without sacrificing safety. Read more at #6074.

User defined annotations and [JSON|YAML]::Serializable

This new language construct allows the user to define their own annotations, like [Link]. Basically you will be able to annotate types declaration or instance variables, and later on query them to do something you wish in macros.

Before this feature metaprogramming usually involved calling one macro with all the information needed. From now on, a more decoupled mechanism between declaring and consuming can be used. Read more at #6063.

The new JSON::Serializable and YAML::Serializable modules use this annotations. Feedback is welcome since this feature is brand new. You can read more at #6082, JSON::Serializable, YAML::Serializable docs.

Another usage of annotations might be to declare a registry of classes, like the one used in DB drivers or frameworks handlers. And it could enable the removal of mutating values of constants during compilation time in favor of a more declarative code.

Do not collapse unions for sibling types

Although the previous code already compiled fine in 0.24.2 this changes allow the type system to deal with some cases that would have ended in a compile-time error but that actually make sense. At the end of the day the type system is about identifying which programs will safely run and cutting the ones that won’t.

The following program is an example of that. It won’t compile in 0.24.2 but it now does in 0.25.0.

classFooendclassBar<Foodefdo_itendendclassBaz<Foodefdo_itendendclassQux<Foo# there is no do_itendvar=rand<0.5?Bar.new:Baz.newvar.do_it

This is particularly useful in scenarios where there is a huge hierarchy of types but in a section of the code only a subset is used.

You can read more at #6024 and discover when the union of types are still collapsed to the common ancestor (spoiler, they need to not be siblings).

JSON::Any and YAML::Any changes

There were some subtle inconsistencies with JSON::Any and YAML::Any API. The bottom line is that over an ::Any value you can use #[] to traverse it and it will always return an ::Any value. If you need a specific type for the ::Any value (and be able to use Enumerable methods if it was an array) you need to call the already known #as_a, #as_h, #as_s methods.

We still encourage, when possible, the use of JSON.mapping, JSON::Serializable or JSON::PullParser when finer control is needed.

HTTP::Server can bind to multiple addresses

From now on if you use the built-in HTTP::Server you first need to configure it, then bind to one or more addresses, and lastly you listen to all of them. These addresses can be TCP ports or Unix sockets.

There is still a shortcut to bind and listen, but it doesn’t avoid a breaking change. Read more at #5776, #5959, and the HTTP::Server docs

Welcome to the TimeZone Jungle

There was a huge refactor in Time. If you hit a unicorn while opening the PR to read more about it, just try again.

Starting now Time has #location and #offset properties to know the timezone exactly. Time.now and Time.new will return by default information in the local timezone, while Time.utc_now and Time.utc will return information in UTC.

Methods like #to_local, #to_utc, #utc?, #local? and #in(location : Location) will help you to move around the globe faster than a plane.

The API even allows you to use custom timezones and fixed offsets with Time::Location.fixed.

Another change in the Time namespace are formatters. Better formatters for ISO 8601, RFC 3339, RFC 2822, HTTP enconded dates, YAML and other places where time was parsed or emitted now use a custom time formatter that deals with more cases as expected in each scenario.

Replace File::Stat with File::Info and other file API changes

Some time ago an abstraction for the running OS was introduced in the stdlib. The goal was to be able to run the Crystal compiler in a non POSIX platform and keep the stdlib as clean as possible. Feel free to check src/crystal/system, but keep in mind it is not intended as a public API.

This also required to pick names and abstractions in the stdlib that will fit everybody: POSIX and non POSIX.

The API was renamed and reworked for compare operations and accessing file properties and permissions. It is much clearer now. Hopefully it doesn’t affect too many users, since most of us use File.open, File.write and move on. Read more at #5584, #5333, #5553, #6161, File and File::Info docs.

Heredoc on every argument

If you use Heredoc a lot of you might be interested in this one. Up to 0.24.2 if you wanted to call a method on a string specified using Heredoc you would do:

puts<<-FOO
message
FOO.downcase

From now on the method needs to be at the initial delimiter

puts<<-FOO.downcase
message
FOO

It’s subtle but important, and it plays better with multiple Heredocs in a single call now that you can:

puts<<-ONE.upcase,<<-TWO.capitalize
hello world
ONE
second message
TWO

There were a ton of contributions merged in master even before 0.24.2 was released. But since 0.24.2 was already changing the release packaging for linux, changing the CI and fixing 0.24.1, some features needed to wait their turn a little longer.

Once again, we have tested this release by compiling some of the most popular crystal shards. This helps us catch and fix unintended breaking changes earlier in the release cycle as well as submitting PRs to the shards and contributing a bit more with the community. This process is codified using the scripts in the test-ecosystem repository, which is still fairly new, but so far it’s working well.

The least visible work usually goes in infrastructure and there are always improvements and things waiting to be done. The latest news regarding this area are:

Also on docs, lots of improvements regarding navigation have been done in #5229.

The automated release process now cares about 32 bits linux releases. As a bonus point the packaging has been aligned again with respect to the 64 bits packages. So some paths have changed.

We’ve been contacted by Heroku to early register our buildpack. Stay tuned to future Heroku news to update to the crystal-lang/crystal buildpack in the registry. All in all it’s one more taste of the adoption of Crystal out there, and we are thrilled.

Nightly packages in nightly.crystal-lang.org are still down. The workaround for now it to use the docker image crystallang/crystal:nightly.

Exciting features

Shards is updated to 0.8.0

There are some performance improvements in shards for this release, by downloading less information when possible. A new global cache was added, so you don’t need to download your favorite shards over and over on all of your favorites projects. FYI you can use shards 0.8.0 with Crystal 0.24.2 if you want.

Automatic casts for literal values

If a method is defined as def foo(x : Int8) or def bar(color : Color) with

enumColorRedGreenBlueend

up to 0.24 you would need to call them as foo(1i8) or bar(Color::Blue). But since 0.25.0 you will be able to foo(1) and bar(:blue). A note of caution: this only work with literal values. If the value is saved in a variable and used as an argument it won’t work.

This feature allows cleaner code without sacrificing safety. Read more at #6074.

User defined annotations and [JSON|YAML]::Serializable

This new language construct allows the user to define their own annotations, like [Link]. Basically you will be able to annotate types declaration or instance variables, and later on query them to do something you wish in macros.

Before this feature metaprogramming usually involved calling one macro with all the information needed. From now on, a more decoupled mechanism between declaring and consuming can be used. Read more at #6063.

The new JSON::Serializable and YAML::Serializable modules use this annotations. Feedback is welcome since this feature is brand new. You can read more at #6082, JSON::Serializable, YAML::Serializable docs.

Another usage of annotations might be to declare a registry of classes, like the one used in DB drivers or frameworks handlers. And it could enable the removal of mutating values of constants during compilation time in favor of a more declarative code.

Do not collapse unions for sibling types

Although the previous code already compiled fine in 0.24.2 this changes allow the type system to deal with some cases that would have ended in a compile-time error but that actually make sense. At the end of the day the type system is about identifying which programs will safely run and cutting the ones that won’t.

The following program is an example of that. It won’t compile in 0.24.2 but it now does in 0.25.0.

classFooendclassBar<Foodefdo_itendendclassBaz<Foodefdo_itendendclassQux<Foo# there is no do_itendvar=rand<0.5?Bar.new:Baz.newvar.do_it

This is particularly useful in scenarios where there is a huge hierarchy of types but in a section of the code only a subset is used.

You can read more at #6024 and discover when the union of types are still collapsed to the common ancestor (spoiler, they need to not be siblings).

JSON::Any and YAML::Any changes

There were some subtle inconsistencies with JSON::Any and YAML::Any API. The bottom line is that over an ::Any value you can use #[] to traverse it and it will always return an ::Any value. If you need a specific type for the ::Any value (and be able to use Enumerable methods if it was an array) you need to call the already known #as_a, #as_h, #as_s methods.

We still encourage, when possible, the use of JSON.mapping, JSON::Serializable or JSON::PullParser when finer control is needed.

HTTP::Server can bind to multiple addresses

From now on if you use the built-in HTTP::Server you first need to configure it, then bind to one or more addresses, and lastly you listen to all of them. These addresses can be TCP ports or Unix sockets.

There is still a shortcut to bind and listen, but it doesn’t avoid a breaking change. Read more at #5776, #5959, and the HTTP::Server docs

Welcome to the TimeZone Jungle

There was a huge refactor in Time. If you hit a unicorn while opening the PR to read more about it, just try again.

Starting now Time has #location and #offset properties to know the timezone exactly. Time.now and Time.new will return by default information in the local timezone, while Time.utc_now and Time.utc will return information in UTC.

Methods like #to_local, #to_utc, #utc?, #local? and #in(location : Location) will help you to move around the globe faster than a plane.

The API even allows you to use custom timezones and fixed offsets with Time::Location.fixed.

Another change in the Time namespace are formatters. Better formatters for ISO 8601, RFC 3339, RFC 2822, HTTP enconded dates, YAML and other places where time was parsed or emitted now use a custom time formatter that deals with more cases as expected in each scenario.

Replace File::Stat with File::Info and other file API changes

Some time ago an abstraction for the running OS was introduced in the stdlib. The goal was to be able to run the Crystal compiler in a non POSIX platform and keep the stdlib as clean as possible. Feel free to check src/crystal/system, but keep in mind it is not intended as a public API.

This also required to pick names and abstractions in the stdlib that will fit everybody: POSIX and non POSIX.

The API was renamed and reworked for compare operations and accessing file properties and permissions. It is much clearer now. Hopefully it doesn’t affect too many users, since most of us use File.open, File.write and move on. Read more at #5584, #5333, #5553, #6161, File and File::Info docs.

Heredoc on every argument

If you use Heredoc a lot of you might be interested in this one. Up to 0.24.2 if you wanted to call a method on a string specified using Heredoc you would do:

puts<<-FOO
message
FOO.downcase

From now on the method needs to be at the initial delimiter

puts<<-FOO.downcase
message
FOO

It’s subtle but important, and it plays better with multiple Heredocs in a single call now that you can:

puts<<-ONE.upcase,<<-TWO.capitalize
hello world
ONE
second message
TWO