Lucky 1.0.0-rc1 has been released, and it's a huge milestone for the framework and community.
As many developers know, reaching a 1.0 milestone is huge, and something to be celebrated. We’re excited to publish the first release candidate of Lucky 1.0. We will be using this and the next few release candidates to clean up the rough edges of the framework, and make sure all of the dependencies in the Lucky ecosystem are solid and ready to take us to the next level.
We will cover a few notable differences, but you can view all the changes from the CHANGELOG
See the UPGRADE_NOTES.
You can also view the changes using LuckyDiff. This tool helps see what has changed between versions and will be included in upgrade notes from now on.
This version releases several breaking changes that were needed in order to make the framework more extensible for the future.
Many sites (including this one) have no need for a database. In some cases, your data comes from a 3rd party API, or maybe you need a custom database engine other than PostgreSQL.
In this release, Avram is no longer a dependency of Lucky, but instead,
Lucky is now an optional dependency of Avram. If you’re using Avram outside
of Lucky, then nothing changes for you. However, for existing Lucky users upgrading to rc1
that need Avram,
you’ll now have to include it as an additional dependency. Be sure to add it
to your shard.yml
file, and require it in your src/shards.cr
.
Support for Arrays in Lucky and Avram have existed for a few versions now. This includes support for arrays in params. However, one bit that has been missing was the ability to pass those array params through to an operation.
For example:
# before this release
# post:tags[]=lucky&post:tags[]=crystal
tags = params.nested_arrays("post")["tags"]
SavePost.create(params, tags: tags) do |operation, post|
# ...
end
# after this release
SavePost.create(params) do |operation, post|
# ...
end
This ended up being quite a big breaking change. The original Params implementation
assumed the type was Hash(String, String)
, but this meant you couldn’t have an
array value. Now the type assumes Hash(String, Array(String))
which is actually
the same type as URI::Params
.
As we all know, the javascript ecosystem changes in leaps and bounds daily. This makes it very difficult to keep up.
In 2017, when Lucky began, the Rails core team had a neat library called Turbolinks. This library helped to make pages feel even faster, and who doesn’t love speed? Lucky included this library by default, but Rails has since deprecated Turbolinks in favor of Turbo. Although Lucky doesn’t include Turbo yet, we would still like to encourage users to give it a shot, and let us know if this is something you’d like to see included by default.
Along those same lines, Lucky also came out-of-the-box with Laravel-Mix which is “An elegant wrapper around Webpack”. This made bringing javascript to Lucky seamless, and easy. However, the Laravel framework has moved to Vite as their default front-end. Laravel-Mix may or may not stick around, but with the uncertainty, the 1.0.0-rc1 release opens up the possibility to use Vite while keeping with the type-safe traditions Lucky brings.
If you’d like to experiment with it, updated your src/app.cr
, with this code
# src/app.cr
Lucky::AssetHelpers.load_manifest "public/manifest.json", use_vite: true
Then you’ll just need to setup Vite as your build system. For some examples, you can visit this Vite Lucky test app
So why even include a javascript build system with Lucky?
One of Lucky’s goals is to be type-safe and help catch bugs in development. This
is done in many ways, but one of the most important ways is when it comes to loading
your assets in production. By utilizing a generated manifest file, Lucky can
generate an internal NamedTuple
at compile-time that can be referenced in the app.
When you forget to add an asset (i.e. image, style, script, etc…), a compile-time
error will raise letting you know.
Carbon 0.3.0 has been released with a few neat updates. The first being the ability to set email layouts. Just like your standard views, you may have a very similar wrapper on your emails like contact footer info, or your logo.
To specify your layout, you’ll add the layout
macro, and pass the name
of the folder where your layout template file is located.
# src/emails/welcome_email.cr
class WelcomeEmail < BaseEmail
def initialize(@recipient : Carbon::Emailable)
end
to @recipient
from "info@myapp.com"
subject "Welcome to the site!"
layout my_email_layout
templates html, text
end
Then in your templates, you can add my_email_layout/layout.ecr
,
and use the content
method to yield your email body
# src/emails/templates/my_email_layout/layout.ecr
<h1>Our Email</h1>
<%= content %>
<div>footer</div>
# src/emails/templates/welcome_email/html.ecr
<p>Welcome to the site!</p>
Emailing can be tricky. Sometimes it’s hard to track emails,
and other times you’re dealing with unsubscribes. To help with
these, we’ve added before_send
and after_send
callbacks,
as well as a deliverable : Bool
property. When deliverable
is set to false
, the email won’t be sent.
class FriendIsLiveEmail < BaseEmail
def initialize(@recipient : Carbon::Emailable, @friend : User)
end
before_send do
if !@recipient.wants_email_from_friend?(@friend)
self.deliverable = false
end
end
# the response is from the email service (i.e. sendgrid, etc...)
after_send do |response|
MarkEmailAsRecentlySent.run(self, sent_at: Time.utc)
end
end
Working towards better compatibility with CockroachDB
enums now support both Int64
values, as well as the @[Flags]
annotations making bitwise
permissions super easy to achieve in Lucky!
Another amazing update is the addition of the extract()
query method for timestamp columns.
This allows you to query for date parts of a Time
object. For example, query for
all users that signed up in July.
UserQuery.new.created_at.extract(:month).eq(7)
This extract
method accepts any of the following chronounits:
enum ChronoUnits
Century
Day
Decade
Dow
Doy
Epoch
Hour
Isodow
Isoyear
Julian
Microseconds
Millennium
Milliseconds
Minute
Month
Quarter
Second
Timezone
TimezoneHour
TimezoneMinute
Week
Year
end
We’ve also added support for the bytea
column with Crystal’s Byte
(Slice(UInt8)
) alias.
# In your model
table do
column data : Byte
end
Thank you to everyone that helped with this release, and welcome to all of the new people that have joined our Discord recently. It’s fun and exciting seeing all of the growth happening.
Please report any issues, and as always, PRs are greatly appreciated!
If you haven’t already, give us a star on GitHub, and be sure to follow us on Twitter.
Learn tips and tricks with LuckyCasts.
If you have any questions, or just want to chat, please join us on Discord.