The goal: prevent bugs, forget about most performance issues, and spend more
time on code instead of debugging and fixing tests.
In summary, make writing stunning web applications fast, fun, and easy.
Coming from Rails?
Try Lucky
Lucky has a fresh new set of guides that
make it easy to get started.
Feel free to say hi or ask questions on our
chat room.
Keep up-to-date
Keep up to date by following @luckyframework on Twitter.
Documentation
What's it look like?
JSON endpoint:
class Api::Users::Show < ApiAction
route do
json user_json
end
private def user_json
user = UserQuery.find(user_id)
{name:user.name, email:user.email}
end
end
route sets up a route for "/api/users/:user_id" automatically.
If you want you can set up custom routes like get "sign_in" for non REST routes.
A user_id method is generated because there is a user_id route parameter.
Use json to render JSON. Extract
serializers
for reusable JSON responses.
Database models
# Set up the model
class User < BaseModel
table :users do
column last_active_at : Time
column last_name : String
column nickname : String?
end
end
Sets up the columns that you’d like to use, along with their types
You can add ? to the type when the column can be nil . Crystal will then
help you remember not to call methods on it that won't work.
Lucky will set up presence validations for required fields
(last_active_at and last_name since they are not marked as nilable).
Querying the database
# Add some methods to help query the database
class UserQuery < User::BaseQuery
def recently_active
last_active_at.gt(1.week.ago)
end
def sorted_by_last_name
last_name.lower.desc_order
end
end
# Query the database
UserQuery.new.recently_active.sorted_by_last_name
User::BaseQuery is automatically generated when you define a model. Inherit
from it to customize queries.
Set up named scopes with instance methods.
Lucky sets up methods for all the columns so that if you mistype a column
name it will tell you at compile-time.
Use the lower method on a String column to make sure Postgres sorts
everything in lowercase.
Use gt to get users last active greater than 1 week ago. Lucky has lots
of powerful abstractions for creating complex queries, and type specific
methods (like lower).
Rendering HTML:
class Users::Index < BrowserAction
route do
users = UserQuery.new.sorted_by_last_name
render IndexPage, users:users
end
end
class Users::IndexPage < MainLayout
needs users : UserQuery
def content
render_new_user_button
render_user_list
end
private def render_new_user_button
link "New User", to:Users::New
end
private def render_user_list
ul class:"user-list" do
@users.each do |user|
li do
link user.name, to:Users::Show.with(user.id)
text " - "
text user.nickname || "No Nickname"
end
end
end
end
end
needs users : UserQuery tells the compiler that it must be passed users
of the type UserQuery.
If you forget to pass something that a page needs, it will let you know at
compile time. Fewer bugs and faster debugging.
Write tags with Crystal methods. Tags are automatically closed and
whitespace is removed.
Easily extract named methods since pages are made of regular classes and
methods. This makes your HTML pages incredibly easy to read.
Link to other pages with ease. Just use the action name: Users::New. Pass
params using with: Users::Show.with(user.id). No more trying to remember path
helpers and whether the helper is pluralized or not - If you forget to pass a
param to a route, Lucky will let you know at compile-time.
Since we defined column nickname : String? as nilable, Lucky would fail
to compile the page if you just did text user.nickname since it disallows
printing nil. So instead we add a fallback "No Nickname". No more
accidentally printing empty text in HTML!
Testing
You need to make sure to install the Crystal dependencies.
Run shards install
Run crystal spec from the project root.
Contributing
Create your feature branch (git checkout -b my-new-feature)
Run script/setup to build the Docker containers with everything you need.
Make your changes
Make sure specs pass: script/test.
Add a note to the CHANGELOG
Commit your changes (git commit -am 'Add some feature')
Push to the branch (git push origin my-new-feature)
Create a new Pull Request
Run specific tests with script/test
Lucky to have you!
We love all of the community members that have put in hard work to make Lucky better.
If you're one of those people, we want to give you a t-shirt!
To get a shirt, we ask that you have made a significant contribution to Lucky.
This includes things like submitting PRs with bug fixes and feature implementations, helping other members
work through problems, and deploying real world applications using Lucky!
To claim your shirt, fill in this form.
Contributors
paulcsmith Paul Smith - creator, maintainer
Thanks & attributions
SessionHandler, CookieHandler and FlashHandler are based on Amber. Thank you to the Amber team!
Thanks to Rails for inspiring many of the ideas that are easy to take for
granted. Convention over configuration, removing boilerplate, and most
importantly - focusing on developer happiness.
Thanks to Phoenix, Ecto and Elixir for inspiring Avram's save operations,
Lucky's single base actions and pipes, and focusing on helpful error
messages.
lucky watch based heavily on Sentry. Thanks @samueleaton!