Switching scaffolded Lucky Crystal apps to UUIDs
June 23, 2020 by Stephen Dolan
Introducing…
I’ve really been enjoying working with Lucky over the last few weeks, having developed on the web using only Rails for as long as I can remember. The community is fantastically welcoming and helpful, the Lucky framework is artfully crafted and intuitive, and it is fast without trying too hard or making things obscure.
Lucky has a CLI that can be used to generate apps using a very simple lucky init
command. If you generate an app with authentication built-in, you get a User
model with an ID column that’s defaulted to an Int64
. Here’s a (hopefully future-proof) set of steps to convert over to UUIDs in a scaffolded app!
Add the pgcrypto
extension to Postgres
The first action to take is straightforward, but requires a few steps.
We need to add the pgcrypto
extension to our database before the user model is created. To do that, we’ll rename that migration and insert a new one before it:
mv 00000000000001_create_users.cr 00000000000002_create_users.cr
I also went ahead and manually created the migration for adding the extension:
touch 00000000000001_enable_pgcrypto.cr`
Add this content to that file to create the extension in your database and remove it on rollback:
class EnablePgcrypto::V00000000000001 < Avram::Migrator::Migration::V1
def migrate
execute "CREATE EXTENSION IF NOT EXISTS pgcrypto"
end
def rollback
execute "DROP EXTENSION pgcrypto"
end
end
Tell Lucky to expect UUIDs by default for all model IDs
You can enable this on only specific modules, but if you’re planning on using UUIDs for all of your application’s records, go ahead and add this to src/models/base_model.cr
:
macro default_columns
primary_key id : UUID
timestamps
end
You can read more about your options for this step in the Lucky Guides.
Change some types!
Crystal is a typed language, so we need to tell the compiler that everywhere it previously expected a 64-bit integer for a User
’s ID, it now needs to expect a UUID
. We also need to change the code supplying that User
ID to supply a UUID
instead of an integer.
Rather than running through file-by-file, which may become out of date as the Lucky CLI and framework evolve, here are the two things to search for that you’ll need to replace in your generated application:
Int64
The only occurrences of this type in your app should be for typing User.id
, and the switch to UUIDs is as simple as replacing Int64
with UUID
. I’ll provide my changes from the PasswordResets::NewPage
as an example.
Before:
class PasswordResets::NewPage < AuthLayout
needs operation : ResetPassword
needs user_id : Int64
...
end
After:
class PasswordResets::NewPage < AuthLayout
needs operation : ResetPassword
needs user_id : UUID
...
end
.to_i64
This is the last change we need to make. We’ve covered converting type expectations in the previous section, and now we’ll convert the code that provides the User
ID.
The change to make to callers of to_i64
is to strip off to_i64
, and pass everything else to UUID.new
. I’ll provide two examples below.
From the PasswordResets::Create
action
Before:
html NewPage, operation: operation, user_id: user_id.to_i64`
After:
html NewPage, operation: operation, user_id: UUID.new(user_id)
From the UserToken
model:
Before:
payload["user_id"].to_s.to_i64
After:
UUID.new(payload["user_id"].to_s)
Summary
To convert a generated Lucky app to use UUIDs, we completed these actions:
- Add the
pgcrypto
extension to Postgres - Tell Lucky to use UUIDs by default for model IDs
- Rename type delcarations from
Int64
->UUID
- Refactor callers to pass
UUID.new
values instead ofString#to_i64
values
For further reading, head over to the Lucky Guides and search UUID
in the search bar at the top of the page!
Want some help with your personal productivity system?