{"id":13424480,"url":"https://github.com/luckyframework/lucky","last_synced_at":"2025-05-14T00:09:53.098Z","repository":{"id":16847756,"uuid":"78238350","full_name":"luckyframework/lucky","owner":"luckyframework","description":"A full-featured Crystal web framework that catches bugs for you, runs incredibly fast, and helps you write code that lasts.","archived":false,"fork":false,"pushed_at":"2025-05-11T23:42:38.000Z","size":14104,"stargazers_count":2636,"open_issues_count":97,"forks_count":162,"subscribers_count":44,"default_branch":"main","last_synced_at":"2025-05-12T00:25:36.732Z","etag":null,"topics":["crystal","hacktoberfest","lucky-framework","web","web-framework"],"latest_commit_sha":null,"homepage":"https://luckyframework.org","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luckyframework.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["paulcsmith","jwoertink"]}},"created_at":"2017-01-06T20:58:57.000Z","updated_at":"2025-05-11T23:41:22.000Z","dependencies_parsed_at":"2024-04-01T20:45:53.455Z","dependency_job_id":"638262f4-642e-473f-a90c-0b49c2feb7bc","html_url":"https://github.com/luckyframework/lucky","commit_stats":{"total_commits":1183,"total_committers":92,"mean_commits":"12.858695652173912","dds":"0.48605240912933223","last_synced_commit":"5607fe69ce631569bed96376cc5f36654f95fcce"},"previous_names":[],"tags_count":80,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckyframework%2Flucky/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luckyframework","download_url":"https://codeload.github.com/luckyframework/lucky/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254044212,"owners_count":22005103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crystal","hacktoberfest","lucky-framework","web","web-framework"],"created_at":"2024-07-31T00:00:54.964Z","updated_at":"2025-05-14T00:09:48.084Z","avatar_url":"https://github.com/luckyframework.png","language":"Crystal","readme":"[![github banner-short](https://user-images.githubusercontent.com/22394/26989908-dd99cc2c-4d22-11e7-9576-c6aeada2bd63.png)](http://luckyframework.org)\n\n[![Version](https://img.shields.io/github/tag/luckyframework/lucky.svg?maxAge=360\u0026label=version)](https://github.com/luckyframework/lucky/releases/latest)\n[![License](https://img.shields.io/github/license/luckyframework/lucky.svg)](https://github.com/luckyframework/lucky/blob/main/LICENSE)\n\n[![API Documentation Website](https://img.shields.io/website?down_color=red\u0026down_message=Offline\u0026label=API%20Documentation\u0026up_message=Online\u0026url=https%3A%2F%2Fluckyframework.github.io%2Flucky%2F)](https://luckyframework.github.io/lucky)\n[![Lucky Guides Website](https://img.shields.io/website?down_color=red\u0026down_message=Offline\u0026label=Lucky%20Guides\u0026up_message=Online\u0026url=https%3A%2F%2Fluckyframework.org%2Fguides)](https://luckyframework.org/guides)\n\n[![Discord](https://img.shields.io/discord/743896265057632256)](https://discord.gg/HeqJUcb)\n\nThe goal: prevent bugs, forget about most performance issues, and spend more\ntime on code instead of debugging and fixing tests.\n\nIn summary, make writing stunning web applications fast, fun, and easy.\n\n## Coming from Rails?\n\n- [Ruby on Rails to Lucky on Crystal: Blazing fast, fewer bugs, and even more fun.\n  ](https://hackernoon.com/ruby-on-rails-to-lucky-on-crystal-blazing-fast-fewer-bugs-and-even-more-fun-104010913fec)\n\n## Try Lucky\n\nLucky has a [fresh new set of guides](https://luckyframework.org/guides/) that\nmake it easy to get started.\n\nFeel free to say hi or ask questions on our\n[chat room](https://luckyframework.org/chat).\n\nOr you can copy a real working app with [Lucky JumpStart](https://github.com/stephendolan/lucky_jumpstart/).\n\n## Installing Lucky\n\nTo install Lucky, read the [Installing Lucky](https://luckyframework.org/guides/getting-started/installing) guides for your Operating System.\nThe guide will walk you through installing a command-line utility used for generating new Lucky applications.\n\n## Keep up-to-date\n\nKeep up to date by following [@luckyframework](https://twitter.com/luckyframework) on Twitter.\n\n## Documentation\n\n[API (main)](https://luckyframework.github.io/lucky/)\n\n## What's it look like?\n\n### JSON endpoint:\n\n```crystal\nclass Api::Users::Show \u003c ApiAction\n  get \"/api/users/:user_id\" do\n    user = UserQuery.find(user_id)\n    json UserSerializer.new(user)\n  end\nend\n```\n\n- If you want you can set up custom routes like `get \"/sign_in\"` for non REST routes.\n- A `user_id` method is generated because there is a `user_id` route parameter.\n- Use `json` to render JSON. [Extract\n  serializers](https://luckyframework.org/guides/writing-json-apis/#respond-with-json)\n  for reusable JSON responses.\n\n### Database models\n\n```crystal\n# Set up the model\nclass User \u003c BaseModel\n  table do\n    column last_active_at : Time\n    column last_name : String\n    column nickname : String?\n  end\nend\n```\n\n- Sets up the columns that you’d like to use, along with their types\n- You can add `?` to the type when the column can be `nil` . Crystal will then\n  help you remember not to call methods on it that won't work.\n- Lucky will set up presence validations for required fields\n  (`last_active_at` and `last_name` since they are not marked as nilable).\n\n### Querying the database\n\n```crystal\n# Add some methods to help query the database\nclass UserQuery \u003c User::BaseQuery\n  def recently_active\n    last_active_at.gt(1.week.ago)\n  end\n\n  def sorted_by_last_name\n    last_name.lower.desc_order\n  end\nend\n\n# Query the database\nUserQuery.new.recently_active.sorted_by_last_name\n```\n\n- `User::BaseQuery` is automatically generated when you define a model. Inherit\n  from it to customize queries.\n- Set up named scopes with instance methods.\n- Lucky sets up methods for all the columns so that if you mistype a column\n  name it will tell you at compile-time.\n- Use the `lower` method on a `String` column to make sure Postgres sorts\n  everything in lowercase.\n- Use `gt` to get users last active greater than 1 week ago. Lucky has lots\n  of powerful abstractions for creating complex queries, and type specific\n  methods (like `lower`).\n\n### Rendering HTML:\n\n```crystal\nclass Users::Index \u003c BrowserAction\n  get \"/users\" do\n    users = UserQuery.new.sorted_by_last_name\n    render IndexPage, users: users\n  end\nend\n\nclass Users::IndexPage \u003c MainLayout\n  needs users : UserQuery\n\n  def content\n    render_new_user_button\n    render_user_list\n  end\n\n  private def render_new_user_button\n    link \"New User\", to: Users::New\n  end\n\n  private def render_user_list\n    ul class: \"user-list\" do\n      users.each do |user|\n        li do\n          link user.name, to: Users::Show.with(user.id)\n          text \" - \"\n          text user.nickname || \"No Nickname\"\n        end\n      end\n    end\n  end\nend\n```\n\n- `needs users : UserQuery` tells the compiler that it must be passed users\n  of the type `UserQuery`.\n- If you forget to pass something that a page needs, it will let you know at\n  compile time. **Fewer bugs and faster debugging**.\n- Write tags with Crystal methods. Tags are automatically closed and\n  whitespace is removed.\n- Easily extract named methods since pages are made of regular classes and\n  methods. **This makes your HTML pages incredibly easy to read.**\n- Link to other pages with ease. Just use the action name: `Users::New`. Pass\n  params using `with`: `Users::Show.with(user.id)`. No more trying to remember path\n  helpers and whether the helper is pluralized or not - If you forget to pass a\n  param to a route, Lucky will let you know at compile-time.\n- Since we defined `column nickname : String?` as nilable, Lucky would fail\n  to compile the page if you just did `text user.nickname` since it disallows\n  printing `nil`. So instead we add a fallback `\"No Nickname\"`. **No more\n  accidentally printing empty text in HTML!**\n\n## Testing\n\nYou need to make sure to install the Crystal dependencies.\n\n1. Run `shards install`\n1. Run `crystal spec` from the project root.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n### Lucky to have you!\n\nWe love all of the community members that have put in hard work to make Lucky better.\nIf you're one of those people, we want to give you a t-shirt!\n\nTo get a shirt, we ask that you have made a significant contribution to Lucky.\nThis includes things like submitting PRs with bug fixes and feature implementations, helping other members\nwork through problems, and deploying real world applications using Lucky!\n\nTo claim your shirt, [fill in this form](https://forms.gle/w3PJ4pww8WDAuJov5).\n\n## Contributors\n\n[paulcsmith](https://github.com/paulcsmith) Paul Smith - Original Creator of Lucky\n\n\u003ca href=\"https://github.com/luckyframework/lucky/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=luckyframework/lucky\" /\u003e\n\u003c/a\u003e\n\nMade with [contrib.rocks](https://contrib.rocks).\n\n## Thanks \u0026 attributions\n\n- SessionHandler, CookieHandler and FlashHandler are based on [Amber](https://github.com/amberframework/amber). Thank you to the Amber team!\n- Thanks to Rails for inspiring many of the ideas that are easy to take for\n  granted. Convention over configuration, removing boilerplate, and most\n  importantly - focusing on developer happiness.\n- Thanks to Phoenix, Ecto and Elixir for inspiring Avram's save operations,\n  Lucky's single base actions and pipes, and focusing on helpful error\n  messages.\n- `lucky watch` based heavily on [Sentry](https://github.com/samueleaton/sentry). Thanks [@samueleaton](https://github.com/samueleaton)!\n","funding_links":["https://github.com/sponsors/paulcsmith","https://github.com/sponsors/jwoertink"],"categories":["Crystal",":page_facing_up: Project Pages","Web Frameworks","\u003ca name=\"Crystal\"\u003e\u003c/a\u003eCrystal"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckyframework%2Flucky","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluckyframework%2Flucky","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckyframework%2Flucky/lists"}