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?
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.
Or you can copy a real working app with Lucky JumpStart.
To install Lucky, read the Installing Lucky guides for your Operating System. The guide will walk you through installing a command-line utility used for generating new Lucky applications.
Keep up to date by following @luckyframework on Twitter.
What's it look like?
class Api::Users::Show < ApiAction
get "/api/users/:user_id" do
user = UserQuery.find(user_id)
- If you want you can set up custom routes like
get "/sign_in"for non REST routes.
user_idmethod is generated because there is a
jsonto render JSON. Extract serializers for reusable JSON responses.
# Set up the model
class User < BaseModel
column last_active_at : Time
column last_name : String
column nickname : String?
- 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_namesince they are not marked as nilable).
Querying the database
# Add some methods to help query the database
class UserQuery < User::BaseQuery
# Query the database
User::BaseQueryis 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
lowermethod on a
Stringcolumn to make sure Postgres sorts everything in lowercase.
gtto get users last active greater than 1 week ago. Lucky has lots of powerful abstractions for creating complex queries, and type specific methods (like
class Users::Index < BrowserAction
get "/users" do
users = UserQuery.new.sorted_by_last_name
render IndexPage, users: users
class Users::IndexPage < MainLayout
needs users : UserQuery
private def render_new_user_button
link "New User", to: Users::New
private def render_user_list
ul class: "user-list" do
users.each do |user|
link user.name, to: Users::Show.with(user.id)
text " - "
text user.nickname || "No Nickname"
needs users : UserQuerytells the compiler that it must be passed users of the type
- 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
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.nicknamesince it disallows printing
nil. So instead we add a fallback
"No Nickname". No more accidentally printing empty text in HTML!
You need to make sure to install the Crystal dependencies.
crystal specfrom the project root.
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.
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 watchbased heavily on Sentry. Thanks @samueleaton!