#lucky
- If you want to follow along, install Lucky
- Learn some Crystal from the official docs if you’re not already familiar
You can also just read through this to see if Lucky looks interesting.
We’ll be building an application that allows users to create new forums, start and comment in discussions, and upvote answers. It’ll also send email notifications and go over how to test the application. We’ll hit on most of what it takes to build an application in Lucky.
Let’s call our new app “Chatter”. To create a new application, run lucky init chatter
. This will create a new Lucky application with files and folders that will help you create server rendered HTML and JSON APIs.
Follow the install instructions that appear after the application is created and you should see the Lucky name and logo after running lucky dev
Want to build just an API with no front end? Use the
—-api
option when creating the app. Check out the “building a JSON API” guide.
Lucky generates files for signing in, signing up, and resetting your password. To do that, Lucky also generates a User
model. By default it only has an email
, and encrypted_password
.
In our application, we also want to require people to set a username, and optionally add their full name.
The generated User
model can be customized by adding a few new columns with the column
macro:
# in src/models/user.cr
class User < BaseModel
table :users do
# These columns are in the generated file
column email : String
column encrypted_password : String
# These are the new ones we’ll add
column username : String
column name : String?
end
end
You’ll note that we add a ?
to the type for the user’s name. That tells Lucky that the name
column might be nil
. The username
on the other hand does not have a ?
because it is a required field.
Lucky uses “migrations” to manage tables, columns, and indices your app will need. Lucky generates a migration that creates the user table. Let’s modify it to add our new columns. You can find this migration in the db/migrations folder
:
# in db/migrations/create_users_xxxxxx.cr
class CreateUsers < LuckyMigrator::Migration
table :users do
# These columns are in the auto generated file
add email : String, unique: true
add encrypted_password : String
# These are the new ones
add username : String, unique: true
add name : String?
end
end
You’ll note that the migration definition is similar to the model. This is done to minimize the amount of syntax you need to learn.
When Lucky sees a migration with a type that ends with ?
, it will allow the column to have null
values in the database.
When the column type does not have a ?
Lucky will set the column to NOT NULL
.
Lucky can do a lot. This guide will skip many of the options available in Lucky so that it’s easier to get going. Check out the migrations guide for an in-depth look at migrations and the available options.
When we create the app, Lucky runs all migrations when we run bin/setup
. That means our users table was already created. Since we don’t have any records in the database, let’s rollback our migration and rerun it. his is such a common thing to do during development that we have a task that does this: lucky db.redo
. This runs lucky do.rollback
and lucky db.migrate.one
.
Our forum will allow people to post multiple topics. First, let’s create a new Topic
model and database table.
First, run lucky gen.migration CreateTopics
to generate a migration.
The generator will create a default migration. Let’s modify it to create a topics table
class CreateTopics::V11111 < LuckyMigrator::Migration
def migrate
create :topics do
add title : String
add_belongs_to creator : User
end
end
def rollback
drop :topics
end
end
create
will automatically addid
,created_at
andupdated_at
columns.
Since we want our topics to be associated with the user that created them, we use add_belongs_to
. We give the name of the relation (creator
) and the type (User
). Lucky will then create an indexed column called creator_id
that has a foreign key to theid
column on the users
table.
Now that the migration is written, run lucky db.migrate
to run the migration and create the table.
Let’s create our model with lucky gen.model Topic
. This command will create 3 files: model, form, and query. For now we’ll focus on the model:
# in src/models/topic.cr
class Topic < BaseModel
table :topics do
column title : String
belongs_to creator : User
end
end
You’ll notice that we use the same syntax in migrations as we do in the migration. This tells Lucky what column to use and what model type it should be.
Let’s create an action with lucky gen.action.browser Topics::Index
. This will create a bare bones Topics::Index
. Let’s make it do something:
class Topics::Index < BrowserAction
action do
render IndexPage, topics: TopicQuery.new
end
end
The action
macro will automatically create a route to this action. I’m this case, /topics
.
The render
method will render the IndexPage
and pass all the topics to it.
Link to more in-depth guides
Let’s create a new page with lucky gen.page Topics::IndexPage
. We’ll edit it to render some topics:
class Topics::IndexPage < MainLayout
needs topics : TopicQuery
def content
h1 “All Topics”
ul do
@topics.each do |topic|
li topic.title
end
end
end
end