Showing preview only (969K chars total). Download the full file or copy to clipboard to get everything.
Repository: luckyframework/web
Branch: main
Commit: d72fc3385eb0
Files: 291
Total size: 894.9 KB
Directory structure:
gitextract_eam4n9mt/
├── .ameba.yml
├── .crystal-version
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── config.yml
│ └── workflows/
│ ├── ci.yml
│ └── docs.yml
├── .gitignore
├── .prettierrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── UPGRADE_NOTES.md
├── bin/
│ ├── lucky.exec.cr
│ ├── lucky.gen.action.api.cr
│ ├── lucky.gen.action.browser.cr
│ ├── lucky.gen.action.cr
│ ├── lucky.gen.component.cr
│ ├── lucky.gen.page.cr
│ ├── lucky.gen.secret_key.cr
│ ├── lucky.gen.task.cr
│ └── lucky.watch.cr
├── bunfig.toml
├── docker-compose.yml
├── public/
│ ├── bun-manifest.json
│ ├── mix-manifest.json
│ └── vite-manifest.json
├── script/
│ ├── docs
│ ├── setup
│ └── test
├── shard.edge.yml
├── shard.override.yml
├── shard.yml
├── spec/
│ ├── bun/
│ │ ├── config_spec.cr
│ │ └── lucky.test.js
│ ├── charms/
│ │ ├── cookie_spec.cr
│ │ ├── hash_spec.cr
│ │ ├── string_spec.cr
│ │ └── uuid_spec.cr
│ ├── fixtures/
│ │ └── plain_text
│ ├── lucky/
│ │ ├── action_cookies_and_sessions_spec.cr
│ │ ├── action_pipes_spec.cr
│ │ ├── action_redirect_spec.cr
│ │ ├── action_rendering_spec.cr
│ │ ├── action_route_params_spec.cr
│ │ ├── action_spec.cr
│ │ ├── asset_helpers_spec.cr
│ │ ├── assignable_spec.cr
│ │ ├── base_http_client_spec.cr
│ │ ├── base_tags_spec.cr
│ │ ├── component_spec.cr
│ │ ├── cookies/
│ │ │ ├── cookie_jar_spec.cr
│ │ │ ├── flash_store_spec.cr
│ │ │ └── session_spec.cr
│ │ ├── custom_tags_spec.cr
│ │ ├── data_response_spec.cr
│ │ ├── dev_asset_cache_handler_spec.cr
│ │ ├── error_handling_spec.cr
│ │ ├── errors_spec.cr
│ │ ├── expose_spec.cr
│ │ ├── fallback_action_spec.cr
│ │ ├── file_response_spec.cr
│ │ ├── force_ssl_handler_spec.cr
│ │ ├── forgery_protection_helpers_spec.cr
│ │ ├── form_helpers_spec.cr
│ │ ├── format_edge_cases_spec.cr
│ │ ├── format_integration_spec.cr
│ │ ├── html_page_spec.cr
│ │ ├── http_method_override_handler_spec.cr
│ │ ├── infer_page_spec.cr
│ │ ├── link_helpers_spec.cr
│ │ ├── log_handler_spec.cr
│ │ ├── maximum_request_size_handler_spec.cr
│ │ ├── memoize_spec.cr
│ │ ├── mime_type_spec.cr
│ │ ├── namespaced_action_spec.cr
│ │ ├── number_to_currency_spec.cr
│ │ ├── paginator/
│ │ │ ├── backend_helpers_spec.cr
│ │ │ ├── components_spec.cr
│ │ │ └── paginator_spec.cr
│ │ ├── param_parser_spec.cr
│ │ ├── params_spec.cr
│ │ ├── pretty_log_formatter_spec.cr
│ │ ├── protect_from_forgery_spec.cr
│ │ ├── quick_def_spec.cr
│ │ ├── rate_limit_spec.cr
│ │ ├── remote_ip_handler_spec.cr
│ │ ├── render_if_defined_spec.cr
│ │ ├── request_expectations_spec.cr
│ │ ├── request_type_helper_spec.cr
│ │ ├── root_spec.cr
│ │ ├── route_handler_format_spec.cr
│ │ ├── route_helper_format_spec.cr
│ │ ├── route_helper_spec.cr
│ │ ├── route_not_found_error_spec.cr
│ │ ├── route_not_found_handler_spec.cr
│ │ ├── route_prefix_spec.cr
│ │ ├── router_spec.cr
│ │ ├── secure_headers_spec.cr
│ │ ├── serializable_spec.cr
│ │ ├── specialty_tags_spec.cr
│ │ ├── static_compression_handler_spec.cr
│ │ ├── static_file_handler_spec.cr
│ │ ├── subdomain_spec.cr
│ │ ├── support/
│ │ │ ├── message_encrypter_spec.cr
│ │ │ └── message_verifier_spec.cr
│ │ ├── tag_defaults_spec.cr
│ │ ├── text_helpers/
│ │ │ ├── cycle_spec.cr
│ │ │ ├── excerpts_spec.cr
│ │ │ ├── highlight_spec.cr
│ │ │ ├── pluralize_spec.cr
│ │ │ ├── simple_format_spec.cr
│ │ │ ├── svg_inliner_spec.cr
│ │ │ ├── text_helpers_spec.cr
│ │ │ ├── to_sentence_spec.cr
│ │ │ ├── truncate_spec.cr
│ │ │ └── word_wrap_spec.cr
│ │ ├── text_response_spec.cr
│ │ ├── time_helpers_spec.cr
│ │ ├── uploaded_file_spec.cr
│ │ ├── url_format_spec.cr
│ │ ├── url_helpers_spec.cr
│ │ ├── verify_accepts_format_spec.cr
│ │ └── welcome_page_spec.cr
│ ├── spec_helper.cr
│ ├── support/
│ │ ├── cleanup_helper.cr
│ │ ├── context_helper.cr
│ │ ├── exec_template.cr.template
│ │ ├── generator_helper.cr
│ │ ├── multipart_helper.cr
│ │ ├── raw_log_formatter.cr
│ │ ├── routes_helper.cr
│ │ ├── test_action.cr
│ │ ├── test_fallback_action.cr
│ │ └── test_server.cr
│ └── tasks/
│ ├── exec_spec.cr
│ ├── gen/
│ │ ├── action_spec.cr
│ │ ├── component_spec.cr
│ │ ├── page_spec.cr
│ │ ├── secret_key_base_spec.cr
│ │ └── task_spec.cr
│ └── routes_spec.cr
├── src/
│ ├── bun/
│ │ ├── bake.js
│ │ ├── config.cr
│ │ ├── lucky.js
│ │ └── plugins/
│ │ ├── aliases.js
│ │ ├── cssGlobs.js
│ │ ├── index.js
│ │ └── jsGlobs.js
│ ├── charms/
│ │ ├── bool_extensions.cr
│ │ ├── cookie.cr
│ │ ├── hash_extensions.cr
│ │ ├── int16_extensions.cr
│ │ ├── int32_extensions.cr
│ │ ├── int64_extensions.cr
│ │ ├── object.cr
│ │ ├── request_extensions.cr
│ │ ├── static_file_handler.cr
│ │ ├── string_extensions.cr
│ │ └── uuid_extensions.cr
│ ├── lucky/
│ │ ├── action.cr
│ │ ├── action_delegates.cr
│ │ ├── action_pipes.cr
│ │ ├── allowed_in_tags.cr
│ │ ├── asset_helpers.cr
│ │ ├── assignable.cr
│ │ ├── base_app_server.cr
│ │ ├── base_component.cr
│ │ ├── base_http_client.cr
│ │ ├── base_log_formatter.cr
│ │ ├── context_extensions.cr
│ │ ├── cookies/
│ │ │ ├── cookie_jar.cr
│ │ │ ├── flash_store.cr
│ │ │ └── session.cr
│ │ ├── data_response.cr
│ │ ├── dev_asset_cache_handler.cr
│ │ ├── enforce_underscored_route.cr
│ │ ├── error_action.cr
│ │ ├── error_handler.cr
│ │ ├── errors.cr
│ │ ├── events/
│ │ │ ├── pipe_event.cr
│ │ │ └── request_complete_event.cr
│ │ ├── exception_page.cr
│ │ ├── exposable.cr
│ │ ├── file_response.cr
│ │ ├── force_ssl_handler.cr
│ │ ├── form_data.cr
│ │ ├── form_data_parser.cr
│ │ ├── format.cr
│ │ ├── format_registry.cr
│ │ ├── html_builder.cr
│ │ ├── html_page.cr
│ │ ├── http_method_override_handler.cr
│ │ ├── json_body_parser.cr
│ │ ├── log_handler.cr
│ │ ├── logger_helpers.cr
│ │ ├── maximum_request_size_handler.cr
│ │ ├── memoizable.cr
│ │ ├── mime_type.cr
│ │ ├── mount_component.cr
│ │ ├── page_helpers/
│ │ │ ├── helperful_paragraph_error.cr
│ │ │ ├── html_text_helpers.cr
│ │ │ ├── number_to_currency.cr
│ │ │ ├── render_if_defined.cr
│ │ │ ├── svg_inliner.cr
│ │ │ ├── text_helpers.cr
│ │ │ ├── time_helpers.cr
│ │ │ └── url_helpers.cr
│ │ ├── paginator/
│ │ │ ├── backend_helpers.cr
│ │ │ ├── components/
│ │ │ │ ├── bootstrap_nav.cr
│ │ │ │ ├── bulma_nav.cr
│ │ │ │ └── simple_nav.cr
│ │ │ ├── current_page.cr
│ │ │ ├── gap.cr
│ │ │ ├── page.cr
│ │ │ └── paginator.cr
│ │ ├── param_helpers.cr
│ │ ├── param_parser.cr
│ │ ├── params.cr
│ │ ├── pretty_log_formatter.cr
│ │ ├── protect_from_forgery.cr
│ │ ├── quick_def.cr
│ │ ├── rate_limit.cr
│ │ ├── redirectable.cr
│ │ ├── redirectable_turbolinks_support.cr
│ │ ├── remote_ip_handler.cr
│ │ ├── renderable.cr
│ │ ├── renderable_error.cr
│ │ ├── request_body_limit.cr
│ │ ├── request_body_reader.cr
│ │ ├── request_expectations.cr
│ │ ├── request_id_handler.cr
│ │ ├── request_type_helpers.cr
│ │ ├── response.cr
│ │ ├── routable.cr
│ │ ├── route_handler.cr
│ │ ├── route_helper.cr
│ │ ├── route_inferrer.cr
│ │ ├── route_not_found_handler.cr
│ │ ├── router.cr
│ │ ├── secure_headers/
│ │ │ ├── disable_floc.cr
│ │ │ ├── set_csp_guard.cr
│ │ │ ├── set_frame_guard.cr
│ │ │ ├── set_sniff_guard.cr
│ │ │ └── set_xss_guard.cr
│ │ ├── serializable.cr
│ │ ├── server.cr
│ │ ├── server_settings.cr
│ │ ├── skip_route_style_check.cr
│ │ ├── static_compression_handler.cr
│ │ ├── subdomain.cr
│ │ ├── support/
│ │ │ ├── message_encryptor.cr
│ │ │ └── message_verifier.cr
│ │ ├── tags/
│ │ │ ├── _check_tag_content.cr
│ │ │ ├── base_tags.cr
│ │ │ ├── bun_reload_tag.cr
│ │ │ ├── custom_tags.cr
│ │ │ ├── forgery_protection_helpers.cr
│ │ │ ├── form_helpers.cr
│ │ │ ├── link_helpers.cr
│ │ │ ├── live_reload_tag.cr
│ │ │ ├── specialty_tags.cr
│ │ │ └── tag_defaults.cr
│ │ ├── text_response.cr
│ │ ├── uploaded_file.cr
│ │ ├── verify_accepts_format.cr
│ │ ├── version.cr
│ │ └── welcome_page.cr
│ ├── lucky.cr
│ └── run_macros/
│ ├── asset_manifest_builder.cr
│ └── missing_asset.cr
├── tasks/
│ ├── exec.cr
│ ├── exec_template.cr.template
│ ├── gen/
│ │ ├── action/
│ │ │ ├── action_generator.cr
│ │ │ ├── api.cr
│ │ │ └── browser.cr
│ │ ├── component.cr
│ │ ├── page.cr
│ │ ├── secret_key.cr
│ │ ├── task.cr
│ │ └── templates/
│ │ ├── action/
│ │ │ └── action.cr.ecr
│ │ ├── component/
│ │ │ └── component.cr.ecr
│ │ ├── page/
│ │ │ └── page.cr.ecr
│ │ └── task/
│ │ └── task.cr.ecr
│ ├── routes.cr
│ └── watch.cr
└── tasks.cr
================================================
FILE CONTENTS
================================================
================================================
FILE: .ameba.yml
================================================
# This configuration file was generated by `ameba --gen-config`
# on 2026-04-02 23:47:16 UTC using Ameba version 1.6.4.
# The point is for the user to remove these configuration records
# one by one as the reported problems are removed from the code base.
# Problems found: 92
# Run `ameba --only Lint/UselessAssign` for details
Lint/UselessAssign:
Description: Disallows useless variable assignments
ExcludeTypeDeclarations: true
Enabled: true
Severity: Warning
# Problems found: 9
# Run `ameba --only Lint/Typos` for details
Lint/Typos:
Description: Reports typos found in source files
FailOnError: false
Excluded:
- spec/lucky/text_helpers/truncate_spec.cr
- spec/lucky/text_helpers/excerpts_spec.cr
- spec/lucky/secure_headers_spec.cr
Enabled: true
Severity: Warning
================================================
FILE: .crystal-version
================================================
1.15.1
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [paulcsmith, jwoertink]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
> Example:
> 1. Generate a Lucky project '...'
> 2. Generate a route with '...'
> 3. See error 'paste error message'
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots/code**
If applicable, add screenshots/code samples to help explain your problem.
**Versions (please complete the following information):**
- Lucky version (check in shard.lock):
- Crystal version (`crystal --version`):
- OS:
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: Request Feature
url: https://github.com/luckyframework/lucky/discussions/new?category_id=66231
about: Request a feature in the discussion forum
- name: Visit Forum
url: https://github.com/luckyframework/lucky/discussions
about: Ask questions and discuss with other community members
- name: Chat
url: https://luckyframework.org/chat
about: Chat with the community
================================================
FILE: .github/workflows/ci.yml
================================================
name: Lucky CI
on:
push:
branches: [main]
pull_request:
branches: "*"
jobs:
check_format:
strategy:
fail-fast: false
runs-on: ubuntu-latest
continue-on-error: false
steps:
- uses: actions/checkout@v6
- uses: crystal-lang/install-crystal@v1
with:
crystal: latest
- name: Install shards
run: shards install
- name: Format
run: crystal tool format --check
- name: Lint
run: ./bin/ameba
specs:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
shard_file:
- shard.yml
crystal_version:
- 1.16.3
- latest
experimental:
- false
include:
- shard_file: shard.edge.yml
crystal_version: latest
experimental: true
os: ubuntu-latest
- shard_file: shard.override.yml
crystal_version: nightly
experimental: true
os: ubuntu-latest
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
steps:
- uses: actions/checkout@v6
- uses: crystal-lang/install-crystal@v1
with:
crystal: ${{matrix.crystal_version}}
- name: Install shards
run: shards install --skip-postinstall --skip-executables
env:
SHARDS_OVERRIDE: ${{ matrix.shard_file }}
- name: Run tests
run: crystal spec
================================================
FILE: .github/workflows/docs.yml
================================================
name: Deploy docs
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: crystal-lang/install-crystal@v1
with:
crystal: latest
- name: "Install shards"
run: shards install
- name: "Generate docs"
run: crystal docs
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
================================================
FILE: .gitignore
================================================
/docs/
/libs/
/lib/
/bin/ameba
/bin/ameba.*
/tmp/
/.shards/
server
# Libraries don't need dependency lock
# Dependencies will be locked in application that uses them
/shard.lock
.DS_Store
================================================
FILE: .prettierrc
================================================
{
"printWidth": 80,
"tabWidth": 2,
"singleQuote": true,
"semi": false,
"bracketSpacing": false,
"quoteProps": "consistent",
"trailingComma": "none",
"arrowParens": "avoid"
}
================================================
FILE: CHANGELOG.md
================================================
### Changes in 1.4.0
- Fixed: `Lucky::Response` require for defining `debug_message` on custom response types. [#1927](https://github.com/luckyframework/lucky/pull/1927)
- Fixed: Compilation error on Windows. [#1929](https://github.com/luckyframework/lucky/pull/1929)
- Updated: `Lucky::UploadedFile` now checks for the file being empty. [#1942](https://github.com/luckyframework/lucky/pull/1942)
- Updated: `Lucky::BaseHTTPClient` now allows `IO` in `exec_raw`. [#1944](https://github.com/luckyframework/lucky/pull/1944)
- Added: new Rate Limiting for actions. [#1945](https://github.com/luckyframework/lucky/pull/1945)
- Fixed: params nil value when passing a query param key without a value. [#1946](https://github.com/luckyframework/lucky/pull/1946)
- Refactor: added new `Lucky::Serialzable` module to replace `Lucky::Serializer` class allowing you to make your serializers structs if you wanted. [#1947](https://github.com/luckyframework/lucky/pull/1947)
- Fixed: params no longer raise when passing empty nested params. [#1950](https://github.com/luckyframework/lucky/pull/1950)
- Updated: Added method override to allow passing a NamedTuple to `Lucky::BaseHTTPClient#exec`. [#1952](https://github.com/luckyframework/lucky/pull/1952)
- Fixed: error with `send_json` spec method when your response had the `status` key. [#1954](https://github.com/luckyframework/lucky/pull/1954)
- Updated: content-type check to match the RFC with performance improvement. [#1955](https://github.com/luckyframework/lucky/pull/1955)
- Updated: avoid intermediate strings for performance improvement. [#1958](https://github.com/luckyframework/lucky/pull/1958)
- Updated: added lots of extra types throughout the codebase. [#1959](https://github.com/luckyframework/lucky/pull/1959)
- Updated: All bash scripts are now written in Crystal for better cross-platform support. [#875 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/875)
- Fixed: PG issue on Windows. [#1071 in Avram](https://github.com/luckyframework/avram/pull/1071)
- Added: support for Crystal 1.16 by removing variables that use `?`. [#1083 in Avram](https://github.com/luckyframework/avram/pull/1083)
- Added: null sorting to `order_by`. [#1081 in Avram](https://github.com/luckyframework/avram/pull/1081)
- Fixed: issue with nested array params passed to operation attributes. [#1085 in Avram](https://github.com/luckyframework/avram/pull/1085)
- Fixed: operations now raise an error when using the wrong callback for that operation. [#1086 in Avram](https://github.com/luckyframework/avram/pull/1086)
- Updated: Using the query bulk update will now update `updated_at`. [#1091 in Avram](https://github.com/luckyframework/avram/pull/1091)
- Refactor: all query `where_*` methods are now moved to the `join_*` methods. [#1090 in Avram](https://github.com/luckyframework/avram/pull/1090)
- Added: ability to set separate reader and writer databases. [#1089 in Avram](https://github.com/luckyframework/avram/pull/1089)
- Added: new table locking mechanism. [#1076 in Avram](https://github.com/luckyframework/avram/pull/1076)
- Added: more performance improvements by avoiding intermediate strings in Avram. [#1095 in Avram](https://github.com/luckyframework/avram/pull/1095)
- Added: more types in Avram. [#1094 in Avram](https://github.com/luckyframework/avram/pull/1094)
- Added: types to Wordsmith. [#28 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/28)
- Updated: Wordsmith codebase to remove redundant to_s calls. [#31 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/31)
- Added: new `IO` method overloads to Wordsmith. [#32 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/32)
- Fixed: `LuckyEnv` now raises when it catches duplicate keys in your .env file. [#36 in LuckyEnv](https://github.com/luckyframework/lucky_env/pull/36)
- Added: dynamic environment aware .env loading. [#37 in LuckyEnv](https://github.com/luckyframework/lucky_env/pull/37)
- Added: type-safe ENV method generation. [#39 in LuckyEnv](https://github.com/luckyframework/lucky_env/pull/39)
### Changes in 1.3.0 (2024-11-02)
- Fixed: re-compilation time not resetting. [#1894](https://github.com/luckyframework/lucky/pull/1894)
- Fixed: missing `--with-page` flag in `gen.action.browser` task help text. [#1895](https://github.com/luckyframework/lucky/pull/1895)
- Refactor: lucky routes to pull from the router instead of holding a second array in memory. [#1898](https://github.com/luckyframework/lucky/pull/1898)
- Updated: error message when booting Lucky and the watcher config is missing. [#1896](https://github.com/luckyframework/lucky/pull/1896)
- Fixed: compatibility with Crystal 1.13.x and later. [#1900](https://github.com/luckyframework/lucky/pull/1900)
- Fixed: invalid query string for array params. [#1908](https://github.com/luckyframework/lucky/pull/1908)
- Refactor: make `form_method` public. [#1915](https://github.com/luckyframework/lucky/pull/1915)
- Added: new `MaximumRequestSizeHandler`. [#1916](https://github.com/luckyframework/lucky/pull/1916)
- Fixed: deprecation warning for Crystal 1.13. [#1918](https://github.com/luckyframework/lucky/pull/1918)
- Fixed: compilation error with updated ExceptionPage. [#1910](https://github.com/luckyframework/lucky/pull/1910)
- Updated: to latest ExceptionPage. [#1921](https://github.com/luckyframework/lucky/pull/1921)
- Added: support for Windows with the core `Lucky` shard. [#1919](https://github.com/luckyframework/lucky/pull/1919)
- Refactor: all built-in tasks are no longer precompiled. [#1919](https://github.com/luckyframework/lucky/pull/1919)
- Updated: url link to PostCSS in generated webpack.mix. [65f2d3df in LuckyCLI](https://github.com/luckyframework/lucky_cli/commit/65f2d3dfd0c572ba8a8015c36499ca2f61c742a8)
- Added: ability to run uncompiled tasks from Crystal files. [#871 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/871)
- Refactor: generated page templates with clear args. [#935 in Avram](https://github.com/luckyframework/avram/pull/935)
- Fixed: issue with type casting on `select_min/max`. [#1040 in Avram](https://github.com/luckyframework/avram/pull/1040)
- Added: `have_any` criteria method for array queries. [#1042 in Avram](https://github.com/luckyframework/avram/pull/1042)
- Added: `if_exists` option to DROP TABLE queries. [#1043 in Avram](https://github.com/luckyframework/avram/pull/1043)
- Added: support for camel case or snake case on `gen.migration` task. [#1046 in Avram](https://github.com/luckyframework/avram/pull/1046)
- Added: array param options for raw where query values. [#1044 in Avram](https://github.com/luckyframework/avram/pull/1044)
- Updated: error message when there's issues connecting to your DB. [#1047 in Avram](https://github.com/luckyframework/avram/pull/1047)
- Updated: DeleteOperations to run in a transaction. [#1055 in Avram](https://github.com/luckyframework/avram/pull/1055)
- Added: `have_error` expectation for specs with SaveOperations. [#1062 in Avram](https://github.com/luckyframework/avram/pull/1062)
- Refactor: use `postgres` as the default database for `db.create` and `db.drop` tasks. [#1058 in Avram](https://github.com/luckyframework/avram/pull/1058)
- Refactor: all calls to tables and column names are now quoted in constructed SQL queries. Allows for defining columns that may conflict with reserved SQL words. [#1059 in Avram](https://github.com/luckyframework/avram/pull/1059)
- Added: `validate_url_format` for operation validations. [#1065 in Avram](https://github.com/luckyframework/avram/pull/1065)
- Refactor: built-in db related tasks are no longer precompiled. [#1069 in Avram](https://github.com/luckyframework/avram/pull/1069)
- Refacor: built-in `gen.email` task is no longer precompiled. [#95 in Carbon](https://github.com/luckyframework/carbon/pull/95)
### Changes in 1.2.0 (2024-04-21)
- Updated: exception page which includes syntax highlighting now. [#1850](https://github.com/luckyframework/lucky/pull/1850)
- Refactor: Action call body with a clear compile error and ability to do short-circuit returns. [#1857](https://github.com/luckyframework/lucky/pull/1857)
- Added: new `-n` flag to `lucky gen.secret_key` to configure key size. [#1856](https://github.com/luckyframework/lucky/pull/1856)
- Refactor: routes with query params optimization. [#1854](https://github.com/luckyframework/lucky/pull/1854)
- Deprecated: Lucky now prefers Crystal 1.10 or later
- Added: overload to `distance_of_time_in_words` that takes a `Time::Span`. [#1860](https://github.com/luckyframework/lucky/pull/1860)
- Added: compilation time to terminal output during development. [#1855](https://github.com/luckyframework/lucky/pull/1855)
- Refactor: lucky gen tasks are now using [LuckyTemplate](https://github.com/luckyframework/lucky_template) for Windows compatibility. [#1861](https://github.com/luckyframework/lucky/pull/1861)
- Added: `Date` header to response output. [#1866](https://github.com/luckyframework/lucky/pull/1866)
- Added: LuckyCLI releases now include binary packages in the release since v1.1.1
- Fixed: entering `Y` or `y` in LuckyCLI Wizard. [#857 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/857)
- Updated: Generated Github Actions CI file with latest actions versions. Nexploit NPM is no longer required for SecTester. [#859 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/859)
- Added: new Scoop package installer for installing LuckyCLI on Windows. [Scoop it up](https://github.com/luckyframework/scoop-bucket)
- Fixed: a few compatibility issues with CockroachDB. [#980 in Avram](https://github.com/luckyframework/avram/pull/980)
- Added: `attrs` override for boolean attributes on `select_input` and `multi_select_input`. [#981 in Avram](https://github.com/luckyframework/avram/pull/981)
- Updated: DB connections now support a retry. [#990 in Avram](https://github.com/luckyframework/avram/pull/990)
- Updated: `select_input` and `multi_select_input` to include the `id` attribute. [#992 in Avram](https://github.com/luckyframework/avram/pull/992)
- Added: `if_not_exists` and `if_exists` options to migration `create` and `alter` macros. [#993 in Avram](https://github.com/luckyframework/avram/pull/993)
- Added: block overload for `preload` `has_one` methods. [#994 in Avram](https://github.com/luckyframework/avram/pull/994)
- Fixed: how the `verify_connection` task runs to catch some edge cases of it not working. [#995 in Avram](https://github.com/luckyframework/avram/pull/995)
- Updated: migrator commands no longer use `Process.run` with the `shell` option. [#997 in Avram](https://github.com/luckyframework/avram/pull/997)
- **Breaking change** Refactor: all Avram models now use `DB::Serializable` instead of the deprecated `DB.mapping`. [#996 in Avram](https://github.com/luckyframework/avram/pull/996)
- Updated: all "UPDATE" statements with a subquery are now wrapped allowing bulk updates when a join exists. [#998 in Avram](https://github.com/luckyframework/avram/pull/998)
- Added: an option to `add_belongs_to` to disable adding an index. [#1002 in Avram](https://github.com/luckyframework/avram/pull/1002)
- Added: `create_sequence` and `drop_sequence` migration methods. [#1003 in Avram](https://github.com/luckyframework/avram/pull/1003)
- Added: `refresh_view` query method and materialized view option for materialized view models. [#1004 in Avram](https://github.com/luckyframework/avram/pull/1004)
- Added: support for `String` primary keys in models. [#1000 in Avram](https://github.com/luckyframework/avram/pull/1000)
- Added: new `String`, `Int`, and `Float` criteria methods. `length`, `reverse`, `abs`, `ceil`, and `floor` respectively. [#1010 in Avram](https://github.com/luckyframework/avram/pull/1010)
- Added: an option to specify an alias for join tables with `Avram::Join`. [#1013 in Avram](https://github.com/luckyframework/avram/pull/1013)
- Added: support for `Array(SomeEnum)` columns. [#1009 in Avram](https://github.com/luckyframework/avram/pull/1009)
- Added: support for custom column converters. [#1009 in Avram](https://github.com/luckyframework/avram/pull/1009)
- Added: new `base_query_class` arg for `has_many` associations to specify which query class to use when an association is called. [#1006 in Avram](https://github.com/luckyframework/avram/pull/1006)
- Added: new `JSON::Any` critera methods `has_key`, `has_any_keys`, and `has_all_keys`. [#1015 in Avram](https://github.com/luckyframework/avram/pull/1015)
- Added: more new `JSON::Any` criteria methods `includes` and `in`. [#1016 in Avram](https://github.com/luckyframework/avram/pull/1016)
- Added: new `IGNORE` constant which is an instance of `Avram::Nothing` used for ignoring column updates in operations. [#1018 in Avram](https://github.com/luckyframework/avram/pull/1018)
- Added: new `String` criteria methods to convert to `to_tsquery` and `to_tsvector` along with a `match` for simple full-text search. [#1019 in Avram](https://github.com/luckyframework/avram/pull/1019)
- Added: new Firefox driver for LuckyFlow. [#162 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/162)
- Added: support for attachments in Carbon. [#88 in Carbon](https://github.com/luckyframework/carbon/pull/88)
- Fixed: Carbon emails can be JSON serialized. [#92 in Carbon](https://github.com/luckyframework/carbon/pull/92)
- Fixed: the location where Carbon emails are generated with `lucky gen.email`. [#93 in Carbon](https://github.com/luckyframework/carbon/pull/93)
- Updated: CarbonSMTP Adapter support on Email shard dependency. [#18 in CarbonSMTPAdapter](https://github.com/luckyframework/carbon_smtp_adapter/pull/18)
- Added: support for attachments in CarbonSMTPAdapter. [#20 in CarbonSMTPAdapter](https://github.com/luckyframework/carbon_smtp_adapter/pull/20)
### Changes in 1.1.0 (2023-10-29)
- Fixed: generated docs on `match` macro. [#1790](https://github.com/luckyframework/lucky/pull/1790)
- Fixed: memoized memthods that use an external arg name. [#1817](https://github.com/luckyframework/lucky/pull/1817)
- Added: configurable timeout for browser reload in development. [#1822](https://github.com/luckyframework/lucky/pull/1822)
- Added: config option to silence stacktraces from logs. [#1821](https://github.com/luckyframework/lucky/pull/1821)
- Added: new `--with-page` flag for `lucky gen.action.browser` task. [#1819](https://github.com/luckyframework/lucky/pull/1819)
- Refactor: `RequestBodyReader` with a performance boost in actions. [#1826](https://github.com/luckyframework/lucky/pull/1826)
- Refactor: action route generation with a performance boost. [#1829](https://github.com/luckyframework/lucky/pull/1829)
- Refactor: `html` macro for actions with simpler code. [#1836](https://github.com/luckyframework/lucky/pull/1836)
- Added: config option to configure default redirect statuses. [#1838](https://github.com/luckyframework/lucky/pull/1838)
- Refactor: The `lucky` CLI no longer compiles when running `-h`. [#771 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/771)
- Added: A new `lucky tasks` CLI command added to display available tasks.
- Fixed: The `lucky dev` command now shows in the CLI help menu.
- Updated: generated apps `docker-compose.yml`. [#799 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/799)
- Refactor: LuckyCLI app generation no longer ueses the `crystal init app` command. [#800 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/800)
- Added: initial work on getting the LuckyCLI to work on Windows. [#804 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/804)
- Refactor: pg client tools are no longer a requirement for Avram. [#942 in Avram](https://github.com/luckyframework/avram/pull/942)
- Fixed: queries not logging when turning up logger level. [#945 in Avram](https://github.com/luckyframework/avram/pull/945)
- Fixed: error message when using `validate_numeric` and passing a float. [#948 in Avram](https://github.com/luckyframework/avram/pull/948)
- Added: JSON serialized Array columns. [#949 in Avram](https://github.com/luckyframework/avram/pull/949)
- Updated: to PG v0.27.0. [#951 in Avram](https://github.com/luckyframework/avram/pull/951)
- Fixed: saving `nil` values when using Upsert operations. [#957 in Avram](https://github.com/luckyframework/avram/pull/957)
- Refactor: escape hatch to allow storing empty strings from param values. [#956 in Avram](https://github.com/luckyframework/avram/pull/956)
- Updated: the `validate_size_of` validation can now validate arrays. [#960 in Avram](https://github.com/luckyframework/avram/pull/960)
- Added: PG extension `PG::NumericFloatConverter` for converting `Float64` values from custom SQL. [#958 in Avram](https://github.com/luckyframework/avram/pull/958)
- Refactor: SaveOperation `insert` and `update` with small performance boost. [#962 in Avram](https://github.com/luckyframework/avram/pull/962)
- Added: new `X_preloaded?` methods to check if an association has already been preloaded. [#961 in Avram](https://github.com/luckyframework/avram/pull/961)
- **Breaking change** Removed: Avram's initialized logging and subscribing to Pulsar events by default. Each app will add this in to control when/if it happens. [#967 in Avram](https://github.com/luckyframework/avram/pull/967)
- Fixed: handing bytes outside of ASCII range for `bytea` columns. [#975 in Avram](https://github.com/luckyframework/avram/pull/975)
- Added: support for other postgres index types other than btree. [#971 in Avram](https://github.com/luckyframework/avram/pull/971)
- Updated: SecTester to v1.6 with new Crystal repeater. [#30 in LuckySecTester](https://github.com/luckyframework/lucky_sec_tester/pull/30)
- Updated: Carbon is now supported on Windows. [#82 in Carbon](https://github.com/luckyframework/carbon/pull/82)
- Updated: LuckyFlow is now supported on Windows. [#156 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/156)
- Fixed: support for Chrome v115+ with chromedriver in LuckyFlow. [#159 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/159)
- Added: Brand new `LuckyHXML` shard for generating [Hyperview](https://hyperview.org/) apps from Lucky. [View Shard](https://github.com/luckyframework/lucky_hxml)
- Added: Brand new `LuckyTemplate` shard for better template generation across platforms. [View Shard](https://github.com/luckyframework/lucky_template)
- Updated: LuckyTask is now supported on Windows. [#14 in LuckyTask](https://github.com/luckyframework/lucky_task/pull/14)
- **Breaking change** Refacor: LuckyTask API to avoid conflicts with common names used for CLI args. [#25 in LuckyTask](https://github.com/luckyframework/lucky_task/pull/25)
- Updated: Habitat is now supported on Windows. [#80 in Habitat](https://github.com/luckyframework/habitat/pull/80)
- Updated: LuckyEnv is now supported on Windows. [#28 in LuckyEnv](https://github.com/luckyframework/lucky_env/pull/28)
- Added: ability to reference previous ENV vars as variables in the `.env` file. [#31 in LuckyEnv](https://github.com/luckyframework/lucky_env/pull/31)
- Updated: LuckyRouter is now supported on Windows. [#63 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/63)
- Updated: LuckyCache is now supported on Windows. [#13 in LuckyCache](https://github.com/luckyframework/lucky_cache/pull/13)
- Updated: Pulsar is now supported on Windows. [#22 in Pulsar](https://github.com/luckyframework/pulsar/pull/22)
- Updated: Wordsmith is now supported on Windows. [#25 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/25)
### Changes in 1.0.0 (2023-03-12)
- Added: Inline SVG files. [#1761](https://github.com/luckyframework/lucky/pull/1761)
- Added: `attrs` option to `sumbit` tag helper. [#1759](https://github.com/luckyframework/lucky/pull/1759)
- Fixed: `previous_url` helper when the referrer is the current page. [#1743](https://github.com/luckyframework/lucky/pull/1743)
- Fixed: issue with SSE Watcher for live reloading. [#1748](https://github.com/luckyframework/lucky/pull/1748)
- Fixed: issue with nil values in JSON params. [#1762](https://github.com/luckyframework/lucky/pull/1762)
- Fixed: issue with block HTML tags using a custom wrapper. [#1763](https://github.com/luckyframework/lucky/pull/1763)
- Removed: the optional `SkipUniqueRoute` check. All routes must be unique. [#1764](https://github.com/luckyframework/lucky/pull/1764)
- Updated: Accept header handling for more robustness. [#1769](https://github.com/luckyframework/lucky/pull/1769)
- Updated: Crystal minimum supported version to be 1.6.0 or later. [#1774](https://github.com/luckyframework/lucky/pull/1774)
- Updated: the Lucky watcher to always use the specified host in development. [#1786](https://github.com/luckyframework/lucky/pull/1786)
- Added: ENV `DEV_HOST` for overriding the development host with an ENV var. [#1788](https://github.com/luckyframework/lucky/pull/1788)
- Updated: generated Dockerfile for development with several fixes. [#787 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/787), [#793 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/793)
- Updated: generated `current_user` method is memoized by default now. [#508 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/508)
- Updated: generated apps are compliant with Crystal 1.7+ [#790 in LuckyCLI](https://github.com/luckyframework/lucky_cli/pull/790)
- Fixed: casting issue with AvramParams and Array values. [#895 in Avram](https://github.com/luckyframework/avram/pull/895)
- Fixed: multi select options with Arrays from columns. [#897 in Avram](https://github.com/luckyframework/avram/pull/897)
- Fixed: submitting only a file in a form with no other permitted params. [#894 in Avram](https://github.com/luckyframework/avram/pull/894)
- Updated: avoid refetching associations that have already been preloaded. [#901 in Avram](https://github.com/luckyframework/avram/pull/901)
- **Breaking change** Updated: Always pass the record to DeleteOperation blocks. [#887 in Avram](https://github.com/luckyframework/avram/pull/887)
- Updated: more support for CockroachDB with `.any?` query method. [#900 in Avram](https://github.com/luckyframework/avram/pull/900)
- Added: new `updated?` and `created?` SaveOperation methods. [#904 in Avram](https://github.com/luckyframework/avram/pull/904)
- Added: new function criteria with new `trim`, and `as_date` query methods. [#912 in Avram](https://github.com/luckyframework/avram/pull/912)
- Updated: more support for CockroachDB with `select_sum` query method. [#921 in Avram](https://github.com/luckyframework/avram/pull/921)
- Updated: better Int column support for CockroachDB. [#920 in Avram](https://github.com/luckyframework/avram/pull/920)
- Fixed: compilation issue with preloading optional associations. [#925 in Avram](https://github.com/luckyframework/avram/pull/925)
- Updated: Authentic with better primary key agnostic support. [#77 in Authentic](https://github.com/luckyframework/authentic/pull/77)
- Updated: Small performance boost with the router. [#60 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/60)
- Updated: to lastest Bright security SecTester. [#27 in LuckySecTester](https://github.com/luckyframework/lucky_sec_tester/pull/27)
### Changes in 1.0.0-rc1 (2022-10-2)
- **Breaking change** Removed: Avram is no longer a dependency of Lucky. [#1620](https://github.com/luckyframework/lucky/pull/1620)
- Fixed: path output when generating a component. [#1694](https://github.com/luckyframework/lucky/pull/1694)
- Updated: Lucky to run on Crystal 1.4 or later as a minimum. [#1696](https://github.com/luckyframework/lucky/pull/1696)
- Removed: the deprecated verifier code. [#1697](https://github.com/luckyframework/lucky/pull/1697)
- Added: new Live-Reload for `lucky watch` command as an alternative to browsersync. [#1693](https://github.com/luckyframework/lucky/pull/1693)
- Updated: Error actions now have access to `params`. [#1716](https://github.com/luckyframework/lucky/pull/1716)
- Updated: `lucky exec` allowing you to set your editor by `$EDITOR` env. [#1715](https://github.com/luckyframework/lucky/pull/1715)
- Added: ability to set custom content-type responses in actions. [#1719](https://github.com/luckyframework/lucky/pull/1719)
- Updated: exception page so javascript is no longer required for it. [#1723](https://github.com/luckyframework/lucky/pull/1723)
- Updated: the error message when you forget to add a type to args in a `memoize` method. [#1726](https://github.com/luckyframework/lucky/pull/1726)
- Added: new `exec_raw` escape hatch method for `ApiClient` to submit raw param data. [#1728](https://github.com/luckyframework/lucky/pull/1728)
- Fixed: `memoize` methods can now end in `?` or `!`. [#1727](https://github.com/luckyframework/lucky/pull/1727)
- Added: Experimental option to have type-safe assets built using [Vitejs](https://vitejs.dev/). [#1729](https://github.com/luckyframework/lucky/pull/1729)
- **Breaking change** Removed: default integration with Turbolinks. [#1737](https://github.com/luckyframework/lucky/pull/1737)
- Updated: generated apps using LuckyFlow are compatible with latest Flow updated. [#760 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/760)
- Updated: generated apps that specify "no auth" no longer include Authentic. [#761 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/761)
- Updated: generated apps now have the ability to remove Avram. [#764 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/764)
- Removed: the tasks file generation task. [#770 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/770)
- Fixed: issue with generated apps that would try to encrypt bad passwords. [#773 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/773)
- Updated: the error message when generating an app in a directory that doesn't exist. [#772 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/772)
- Fixed: issue with generated Dockerfile causing it to not boot. [#775 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/775)
- Updated: generated apps no longer include Turbolinks by default. [#779 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/779)
- **Breaking change** Added: Lucky as an optional extension to Avram. [#772 in Avram](https://github.com/luckyframework/avram/pull/772)
- **Breaking change** Added: Array param support to operations and changed how `Avram::Params` is constructed. [#847 in Avram](https://github.com/luckyframework/avram/pull/847)
- Updated: compile-time catch when specifying both `default` and `fill_existing_with` values in migrations. [#852 in Avram](https://github.com/luckyframework/avram/pull/852)
- Added: new `change_default` method for migrations to change the default value of a column. [#853 in Avram](https://github.com/luckyframework/avram/pull/853)
- Updated: references to `uuid-ossp` in favor of `pgcrypto`. [#855 in Avram](https://github.com/luckyframework/avram/pull/855)
- Added: the ability to use the `@[Flags]` annotation and `Int64` for enums. [#856 in Avram](https://github.com/luckyframework/avram/pull/856)
- Added: new `extract()` method for timestamp queries. [#861 in Avram](https://github.com/luckyframework/avram/pull/861)
- Added: new `Bytes` column support for `bytea` columns. [#860 in Avram](https://github.com/luckyframework/avram/pull/860)
- **Breaking change** Updated: named args to take precedence over param values when using Operations. [#862 in Avram](https://github.com/luckyframework/avram/pull/862)
- Added: new `grouped_checkbox` method for use with Array attributes. [#863 in Avram](https://github.com/luckyframework/avram/pull/863)
- Fixed: `change_type` so it can change from one nilable type to another nilable type. [#869 in Avram](https://github.com/luckyframework/avram/pull/869)
- Updated: Factory callbacks now return `self`. [#866 in Avram](https://github.com/luckyframework/avram/pull/866)
- Fixed: JSON::Any columns that are nilable to return nil properly. [#878 in Avram](https://github.com/luckyframework/avram/pull/878)
- Updated: the arg names to `validate_numeric` to avoid confusion. [#867 in Avram](https://github.com/luckyframework/avram/pull/867)
- Added: the ability to customize the session key used for sign-in with Authentic. [#75 in Authentic](https://github.com/luckyframework/authentic/pull/75)
- **Breaking change** Added: new Driver registration for LuckyFlow allowing for custom web drivers. [#133 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/133)
- **Breaking change** Updated: LuckyFlow to not depend directly on Selenium. [#135 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/135)
- Added: `find_xpath` method for LuckyFlow drivers. [#141 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/141)
- Added: "Webless" driver support to LuckyFlow. [#137 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/137)
- **Breaking change** Updated: and abstracted LuckyFlow extensions for Lucky, Avram, and Authentic. [#152 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/152)
- Added: the ability to add mailer layouts for HTML templates. [#70 in Carbon](https://github.com/luckyframework/carbon/pull/70)
- Added: `before_send` and `after_send` callbacks for emails with delivery short circuiting. [#72 in Carbon](https://github.com/luckyframework/carbon/pull/72)
- Fixed: issue when generating a new email and using the word "Email" duplicating it. [#74 in Carbon](https://github.com/luckyframework/carbon/pull/74)
- Updated: LuckySecTester is officially released with v0.1.0. [View Shard](https://github.com/luckyframework/lucky_sec_tester)
- Added: new `ws` CLI utility with Wordsmith allowing you to process words from the CLI. [#23 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/23)
### Changes in 0.30.1 (2022-04-17)
- Fixed: compilation issue when using the `Lucky::Subdomain` module. [#1686](https://github.com/luckyframework/lucky/pull/1686)
### Changes in 0.30.0 (2022-04-07)
- Fixed: logging empty request_id [#1630](https://github.com/luckyframework/lucky/pull/1630)
- Updated: references to community chat and general housekeeping [#1633](https://github.com/luckyframework/lucky/pull/1633)
- Updated: error message on incorrect usage of `default_format` [#1638](https://github.com/luckyframework/lucky/pull/1638)
- Updated: error message on incorrect usage of routes [#1639](https://github.com/luckyframework/lucky/pull/1639)
- Added: new `previous_url` page helper method. [#1641](https://github.com/luckyframework/lucky/pull/1641)
- Added: ability to make requests to actions directly in specs. [#1644](https://github.com/luckyframework/lucky/pull/1644)
- Fixed: `time_ago_in_words` always rounding down. [#1651](https://github.com/luckyframework/lucky/pull/1651)
- Updated: Turbolinks redirect support to allow for replace in the future. [#1650](https://github.com/luckyframework/lucky/pull/1650)
- Updated: compiling "done" notice to be more prominent. [#1653](https://github.com/luckyframework/lucky/pull/1653)
- Added: ability to enable/disable forgery protection app-wide. [#1657](https://github.com/luckyframework/lucky/pull/1657)
- **Breaking change** Fixed: parsing JSON values for params. [#1661](https://github.com/luckyframework/lucky/pull/1661)
- **Breaking change** Updated: Lucky::ForceSSLHandler middleware to match X-Forwarded-Proto == https exactly, which previously accepted uppercase and only required https to be part of the value [#1662](https://github.com/luckyframework/lucky/pull/1662)
- **Breaking change** Updated: the primary branch name from "master" to "main". [#1667](https://github.com/luckyframework/lucky/pull/1667)
- Added: new Content-Security-Policy header module. [#1673](https://github.com/luckyframework/lucky/pull/1673)
- Added: new `remote_ip` method with a config to customize the header lookup. [#1675](https://github.com/luckyframework/lucky/pull/1675)
- **Breaking change** Updated: the remote_ip lookup to use the last valid IP instead of the first in the `X-Forward-For` list. [also #1675](https://github.com/luckyframework/lucky/pull/1675)
- Updated: `MessageVerifier` to handle different types of token data. [#1674](https://github.com/luckyframework/lucky/pull/1674)
- Added: `[]` and `[]=` Methods to `Lucky::CookieJar`. [#1678](https://github.com/luckyframework/lucky/pull/1678)
- Fixed: issue with generated CI not connecting to postgres. [#719 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/719)
- Added: built-in process runner [Nox](https://github.com/matthewmcgarvey/nox). [#710 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/710)
- **Breaking change** Removed: built-in support for other process runners like Overmind through `lucky dev`. [#720](https://github.com/luckyframework/lucky_cli/pull/720)
- Removed: references to TravisCI from old Crystal setup. [#722 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/722)
- Updated: default watcher port to use 3000 instead of 5000. [#727 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/727)
- Added: Docker setup for local development out-of-the-box. [#738 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/738)
- Fixed: ameba linting issue with new Lucky apps. [#739 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/739)
- Updated: default target name to be `app` instead of your app's name for easier building with Docker. [#742 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/742)
- Added: new integration with Bright Security (formerly Neuralegion) SecTester for Security specs out-of-the-box. [#743 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/743)
- Added: `random_order` for queries. [#765 in Avram](https://github.com/luckyframework/avram/pull/765)
- Removed: old compile-time deprecation errors. [#744 in Avram](https://github.com/luckyframework/avram/pull/774)
- Updated: running DB specs in a transaction instead of truncating. [#780 in Avram](https://github.com/luckyframework/avram/pull/780)
- Added: `group_count` method for grouped count queries. [#778 in Avram](https://github.com/luckyframework/avram/pull/778)
- Added: `AvramSlugify` is now integrated as `Avram::Slugify`. [#786 in Avram](https://github.com/luckyframework/avram/pull/786)
- Fixed: JSON parsing to/from the DB when the value was a String but should have been a JSON object. [#806 in Avram](https://github.com/luckyframework/avram/pull/806)
- Fixed: issue with some query cache methods not returning correctly. [#810 in Avram](https://github.com/luckyframework/avram/pull/810)
- Added: helpful error when required association is not loaded in preload. [#812 in Avram](https://github.com/luckyframework/avram/pull/812)
- Added: default time values in SQL for the default timestamps. [#820 in Avram](https://github.com/luckyframework/avram/pull/820)
- Added: a unique index to the migrations table to avoid duplicate migration versions. [#815 in Avram](https://github.com/luckyframework/avram/pull/815)
- Added: properties to error classes for better error customization with I18n. [#823 in Avram](https://github.com/luckyframework/avram/pull/823)
- Added: new `generate` method for `Avram::Slugify` to generate a slug without setting a value. [#821 in Avram](https://github.com/luckyframework/avram/pull/821)
- Added: `validate_format_of` and `validate_uniqueness_of` to `Avram::I18Backend` for better error message support. [#830 in Avram](https://github.com/luckyframework/avram/pull/830)
- Added: new [LuckySecTester](https://github.com/luckyframework/lucky_sec_tester) shard as a thing wrapper around the [NeuraLegion SecTester](https://github.com/NeuraLegion/sec_tester)
- Added: new task to generate email templates. [#60 in Carbon](https://github.com/luckyframework/carbon/pull/60)
- Added: new `size` method to LuckyCache. [#10 in LuckyCache](https://github.com/luckyframework/lucky_cache/pull/10)
- Added: `clear_subscribers` method to Pulsar to clear events of subscribers. [#18 in Pulsar](https://github.com/luckyframework/pulsar/pull/18)
- Added: extra log metadata when using `emit()` with Dexter. [#45 in Dexter](https://github.com/luckyframework/dexter/pull/45)
- Added: support for passing unsubscribe group data to Sendgrid. [#8 in Carbon Sendgrid Adapter](https://github.com/luckyframework/carbon_sendgrid_adapter/pull/8)
### v0.29 (2021-11-30)
- Fixed: the binary name generated for the `gen.secret_key` task. [#1556](https://github.com/luckyframework/lucky/pull/1556)
- Updated: the `html_with_status` to allow Numbers, Symbols, and HTTP::Status. [#1568](https://github.com/luckyframework/lucky/pull/1568)
- Updated: the `lucky routes` task to print within a table (again). Added a new flag option to print params. [#1569](https://github.com/luckyframework/lucky/pull/1569)
- Added: new `path_without_query_params` Action method. [#1572](https://github.com/luckyframework/lucky/pull/1572)
- Added: option to set a different manifest filename when not using laravel-mix. [#1578](https://github.com/luckyframework/lucky/pull/1578)
- Fixed: an issue when passing optional params that were not a String. [#1582](https://github.com/luckyframework/lucky/pull/1582)
- Fixed: Ameba always being installed even when your app doesn't depend on it. [#1589](https://github.com/luckyframework/lucky/pull/1589), [#736 in Avram](https://github.com/luckyframework/avram/pull/736), [#62 in Carbon](https://github.com/luckyframework/carbon/pull/62)
- Added: support for using namespaces when generating resources from the CLI. [#1588](https://github.com/luckyframework/lucky/pull/1588)
- Added: DeleteOperation when generating a model from the CLI. [#1594](https://github.com/luckyframework/lucky/pull/1594)
- Fixed: verifying secret messages that may contain "--". [#1596](https://github.com/luckyframework/lucky/pull/1596)
- Removed: `route` and `nested_route` macro helpers. [#1597](https://github.com/luckyframework/lucky/pull/1597)
- Fixed: the use of boolean attributes rendering invalid values in HTML. [#1598](https://github.com/luckyframework/lucky/pull/1598)
- Added: new Pulsar event to fire when an HTTP request is fully completed. [#1601](https://github.com/luckyframework/lucky/pull/1601)
- Updated: the `lucky watch` task to be less greedy on recompiles. [#1604](https://github.com/luckyframework/lucky/pull/1604)
- Added: the `ajax` desired client format option for actions. [#1603](https://github.com/luckyframework/lucky/pull/1603)
- Added: a better help message for `lucky exec` to include options. [#1602](https://github.com/luckyframework/lucky/pull/1602)
- Added: new `previous_page`, and `next_page` Paginator helper methods. [#1611](https://github.com/luckyframework/lucky/pull/1611)
- Fixed: writing to the response body when making a HEAD call. [#1609](https://github.com/luckyframework/lucky/pull/1609)
- **Breaking change** Removed: the `lucky build.release` task. [#1612](https://github.com/luckyframework/lucky/pull/1612)
- Updated: Avram is no longer required for parsing params. [#1616](https://github.com/luckyframework/lucky/pull/1616)
- **Breaking change** Removed: support for Crystal versions below 1.0.0. [#1618](https://github.com/luckyframework/lucky/pull/1618)
- **Breaking change** Updated: the `Lucky::BaseAppServer#listen` method to be abstract. [#1622](https://github.com/luckyframework/lucky/pull/1622)
- Added: new HTTP `context.request_id` and `RequestIdHandler`. [#1610](https://github.com/luckyframework/lucky/pull/1610)
- Added: documentation clarity for the route style checking. [#668 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/668)
- Updated: require position for the config directory to be higher in the stack. [#676 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/676)
- Updated: the `load_manifest` to use "public/mix-manifest.json" by default. [#679 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/679)
- Fixed: issue when booting a Lucky app would clash with Elixir's `mix`. [#682 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/682)
- Fixed: the error page returning a 200 response. [#684 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/684)
- Updated: the GithubActions CI for generated apps to allow for testing multiple versions of Crystal. [#685 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/685)
- Added: a max limit for password sized in Authentic password validations. [#692 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/692)
- Added: special "flow" tags to a generated app's specs. [#693 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/693)
- Added: new sample "app config" file for generated apps. [#694 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/694)
- Updated: the `AppServer#listen` code to be a bit more concise. [#699 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/699)
- Added: new `Lucky::RequestIdHandler` to generated apps. [#700 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/700)
- Fixed: issue when `select_count` was called before an update causing a failure. [#715 in Avram](https://github.com/luckyframework/avram/pull/715)
- Fixed: issue when inheritng from a SaveOperation that had attributes. [#718 in Avram](https://github.com/luckyframework/avram/pull/718)
- Fixed: issue when passing an enum number as a String. [#720 in Avram](https://github.com/luckyframework/avram/pull/720)
- **Breaking change** Removed: UUID generation on the Crystal side for the `id` column. [#725 in Avram](https://github.com/luckyframework/avram/pull/725)
- Updated: the error message for unpermitted columns to be a little clearer where they come from. [#723 in Avram](https://github.com/luckyframework/avram/pull/723)
- Added: new `includes()` query method for Array columns. [#733 in Avram](https://github.com/luckyframework/avram/pull/733)
- Added: compile-time catches for common mistakes with attribute usage. [#738 in Avram](https://github.com/luckyframework/avram/pull/738)
- Updated: the pending migration notice to be a lot more clear in development. [#737 in Avram](https://github.com/luckyframework/avram/pull/737)
- Updated: `add_belongs_to` to require the `references` option when using a namespace. [#742 in Avram](https://github.com/luckyframework/avram/pull/742)
- Updated: all validation methods to now return a Bool based on if they passed or not. [#744 in Avram](https://github.com/luckyframework/avram/pull/744)
- Updated: the `lucky db.schema.dump` task to include the migrations table data. [#743 in Avram](https://github.com/luckyframework/avram/pull/743)
- Fixed: missing low level DB methods. [#750 in Avram](https://github.com/luckyframework/avram/pull/750)
- Added: new escape hatch to skip default validations, and allow blank strings to be saved. [#746](https://github.com/luckyframework/avram/pull/746)
- Added: new `default_validations` block macro. [#751 in Avram](https://github.com/luckyframework/avram/pull/751)
- Added: new `validate_format_of` validation method. [#752 in Avram](https://github.com/luckyframework/avram/pull/752)
- Fixed: sort order of migrations. [#756 in Avram](https://github.com/luckyframework/avram/pull/756)
- Added: new `Avram::I18nBackend` for setting language translations. [#757 in Avram](https://github.com/luckyframework/avram/pull/757)
- **Breaking change** Renamed: `Operation::Status` enums to `Operation::OperationStatus`. [#759 in Avram](https://github.com/luckyframework/avram/pull/759)
- Added: new query cache mechanism. [#763 in Avram](https://github.com/luckyframework/avram/pull/763)
- Added: brand new [LuckyCache](https://github.com/luckyframework/lucky_cache) shard.
- Added: Dynamic Email templates with Carbon SendGrid adapter. [#5 in Carbon SendGrid](https://github.com/luckyframework/carbon_sendgrid_adapter/pull/5)
- Added: new `int32` and `float64` task args. [#3 in LuckyTask](https://github.com/luckyframework/lucky_task/pull/3)
- Added: faster routing! [#54 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/54), [#55 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/55)
- Updated: `ENV["LUCKY_TASK"]` to be nilable. [#21 in LuckyTask](https://github.com/luckyframework/lucky_env/pull/21)
### v0.28.0 (2021-07-22)
- Updated: The exception page to use new backtrace shard. [#1465](https://github.com/luckyframework/lucky/pull/1465)
- Updated: some Lucky internals with "Spring Cleaning". #1478, #1481, #1483, #1489, #1496, #1511, #1513, #1514, #1521, #1522, #1529, #1532, #1535, #1540, #1542, #1544, #1543
- Updated: components so they no longer require `context` to be passed in. [#1488](https://github.com/luckyframework/lucky/pull/1488)
- Added: new `time_from_now_in_words` text helper. [#1493](https://github.com/luckyframework/lucky/pull/1493)
- Added: new `multipart?` action request helper. [#1495](https://github.com/luckyframework/lucky/pull/1495)
- Updated: the generated delete action resource to use DeleteOperation. [#1497](https://github.com/luckyframework/lucky/pull/1497)
- Added: new `raw_json` Action response method. [#1492](https://github.com/luckyframework/lucky/pull/1492)
- Updated: `form_for` to allow passing boolean attributes. [#1506](https://github.com/luckyframework/lucky/pull/1506)
- Added: new module to disable FLoC. [#1508](https://github.com/luckyframework/lucky/pull/1508)
- Deprecated: `route` and `nested_route` from actions. [#1510](https://github.com/luckyframework/lucky/pull/1510)
- Updated: performance with `params`. It's now attached to `context`. [#1509](https://github.com/luckyframework/lucky/pull/1509)
- Updated: api actions generated will always be in the `api/` namespace. [#1512](https://github.com/luckyframework/lucky/pull/1512)
- Updated: `Lucky::Serializer` with abstract `render` method. [#1516](https://github.com/luckyframework/lucky/pull/1516)
- Added: new `html_with_status` method to render HTML pages with non-200 status. [#1507](https://github.com/luckyframework/lucky/pull/1507)
- **Breaking change**: HTML fields that require an Operation Array attribute will now generate a param name appended with `[]`. [#1523](https://github.com/luckyframework/lucky/pull/1523)
- Added: new `multi_select_input`. [#1518](https://github.com/luckyframework/lucky/pull/1518)
- Added: the ability to define Array query params in actions. [#1527](https://github.com/luckyframework/lucky/pull/1527)
- Added: standardized route style checked. [#1536](https://github.com/luckyframework/lucky/pull/1536)
- Added: native subdomain support for actions. [#1537](https://github.com/luckyframework/lucky/pull/1537)
- Added: new unique route enforcer. [#1538](https://github.com/luckyframework/lucky/pull/1538)
- Updated: Carbon sendgrid is now a separate adapter. [#624 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/624)
- Updated: default components no longer need `context`. [#641 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/641)
- Updated: some LuckyCli internals with "Spring Cleaning". #646, #647, #648, #651, #649, #666 (in Lucky CLI)
- Updated: generated apps will use `LuckyEnv` now. [#650 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/650)
- Removed: deprecated `normalize-css` package and replaced with `modern-normalize`. [#652 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/652)
- Added: new generated apps will enforce route styles by default. [#653 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/653)
- Updated: new generated apps will use case-insensitive email columns for the `User` model. [#657 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/657)
- Fixed: support for Crystal 1.1.0 when generating a new app. [#644 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/644)
- Removed: the `Lucky::Env` module and replaced with the `LuckyEnv` shard. [#655 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/655)
- Fixed: passing special args to precompiled tasks. [#656 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/656)
- Updated: some Avram internals with "Spring Cleaning". #669, #681, #683, #704, #705, #708 (in Avram)
- Updated: `validate_numeric` and `validate_size_of` methods to allow for a custom error message. [#670 in Avram](https://github.com/luckyframework/avram/pull/670)
- Updated: `db.drop` and `db.reset` tasks with a "quiet" option. [#675 in Avram](https://github.com/luckyframework/avram/pull/675)
- Fixed: edge case with migrations using `fill_existing_with: false`. [#676 in Avram](https://github.com/luckyframework/avram/pull/676)
- Added: new `Appdatabase.listen` method to listen for `pg_notify()` calls to any number of channels. [#677 in Avram](https://github.com/luckyframework/avram/pull/677)
- Added: `before_save` and `after_save` callbacks to `Avram::Factory`. [#678 in Avram](https://github.com/luckyframework/avram/pull/678)
- Fixed: `add_belongs_to` to support namespaced types. [#685 in Avram](https://github.com/luckyframework/avram/pull/685)
- Updated: how `select_count` works to allow counting with `distinct_on`. [#687 in Avram](https://github.com/luckyframework/avram/pull/687)
- Fixed: datetime parsing from form inputs. [#693 in Avram](https://github.com/luckyframework/avram/pull/693)
- Fixed: compile-time error when you have a typo in your constant name (i.e. Boolean instead of Bool). [#700 in Avram](https://github.com/luckyframework/avram/pull/700)
- Updated: the `validate_uniqueness_of` method to take a full query object. [#701 in Avram](https://github.com/luckyframework/avram/pull/701)
- Added: new `any?` and `none?` query methods to return a `Bool` value based on records existing. [#703 in Avram](https://github.com/luckyframework/avram/pull/703)
- **Breaking change**: removed `avram_enum`. You can now just use the native Crystal `enum` type. [#698 in Avram](https://github.com/luckyframework/avram/pull/698)
- Added: ability to use `CASCADE` when calling `truncate`. [#702 in Avram](https://github.com/luckyframework/avram/pull/702)
- Added: new `SaveOperation.upsert` methods. [#334 in Avram](https://github.com/luckyframework/avram/pull/334)
- **Breaking change**: renamed `DeleteOperation.destroy` to `DeleteOperation.delete`. [#707 in Avram](https://github.com/luckyframework/avram/pull/707)
- Fixed: compile-time error when no columns are defined in a model. [#706 in Avram](https://github.com/luckyframework/avram/pull/706)
- Added: new JSON serializable object columns. [#695 in Avram](https://github.com/luckyframework/avram/pull/695)
- Updated: the `current_user?` method to be memoized for performance. [#64 in Authentic](https://github.com/luckyframework/authentic/pull/64)
- Updated: Authentic to catch potential development issues when setting the `secret_key`. [#65 in Authentic](https://github.com/luckyframework/authentic/pull/65)
- Fixed: minor performance issue when setting a slug in AvramSlugify. [#10 in AvramSlugify](https://github.com/luckyframework/avram_slugify/pull/10)
- Updated: irregular inflects with `human -> humans`. [#14 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/14)
- Fixed: customizing inflections with Wordsmith. [#18 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/18)
- Fixed: URI decoding path parts in the Lucky Router, also a small performance gain. [#51 in Lucky Router](https://github.com/luckyframework/lucky_router/pull/51)
- Added: the environment predicate methods from `Lucky::Env` in to `LuckyEnv`. [#13 in LuckyEnv](https://github.com/luckyframework/lucky_env/pull/13)
- Updated: Email Previews in Breeze are easier to add in. [#41 in Breeze](https://github.com/luckyframework/breeze/pull/41)
- Fixed: using file uploads with Breeze requests. [#43 in Breeze](https://github.com/luckyframework/breeze/pull/43)
- Added: storing before/after pipes in Breeze. [#36 in Breeze](https://github.com/luckyframework/breeze/pull/36)
- Fixed: issue when running specs on an app using Breeze. [#42 in Breeze](https://github.com/luckyframework/breeze/pull/42)
### v0.27.2 (2021-04-12)
- Removed: legacy ecrypted cookies handling. [#1470](https://github.com/luckyframework/lucky/pull/1470)
- Updated: Cookies resulting in encryption failure are ignored. [#1470](https://github.com/luckyframework/lucky/pull/1470)
### v0.27.1 (2021-04-09)
- Fixed: support for previous versions (<= 0.26.0) of encrypted cookies. [#1467](https://github.com/luckyframework/lucky/pull/1467)
### v0.27.0 (2021-04-09)
- Added: support for Crystal 1.0 🥳 [#1445](https://github.com/luckyframework/lucky/pull/1445)
- Added: Pulsar events for tracking before/after action pipes. [#1423](https://github.com/luckyframework/lucky/pull/1423)
- Updated: `link` to raise an exception when passing both `to` and `href`. [#1428](https://github.com/luckyframework/lucky/pull/1428)
- Added: support for more HTML standard tags. [#1433](https://github.com/luckyframework/lucky/pull/1433)
- Added: new `mount_instance` method to mount an instance of a Component. [#1446](https://github.com/luckyframework/lucky/pull/1446)
- Fixed: bug when using VueJS type attributes on HTML tags. [#1452](https://github.com/luckyframework/lucky/pull/1452)
- Updated: the exception page to look like it belongs in Lucky. [#1451](https://github.com/luckyframework/lucky/pull/1451)
- Updated: HTML tags to allow passing in a raw hash of attributes. [#1453](https://github.com/luckyframework/lucky/pull/1453)
- Updated: the `lucky build.release` task to build in to the `bin` directory. [#1454](https://github.com/luckyframework/lucky/pull/1454)
- Fixed: error message when passing wrong type values to HTML tags. [#1456](https://github.com/luckyframework/lucky/pull/1456)
- Updated: `lucky_cli` dependency with new `lucky_task` shard. [#1459](https://github.com/luckyframework/lucky/pull/1459)
- Updated: generated apps so `LuckyFlow` is only a development dependency. [#608 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/608)
- Updated: Auth modules in generated apps to be in an `auth/` directory. [#618 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/618)
- Removed: `LuckyCli::Task` in to a separate shard. [#622 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/622)
- Added: new `LuckyTask` shard to the ecosystem. [View LuckyTask](https://github.com/luckyframework/lucky_task)
- Updated: `avram_enum` to allow parsing a String value. [#639 in Avram](https://github.com/luckyframework/avram/pull/639)
- Fixed: bug when a column is defined as a `DOUBLE`, and using `Float64`. [#637 in Avram](https://github.com/luckyframework/avram/pull/637)
- Updated: migrations to use `Int64` by default allowing for better CockroachDB support. [#641 in Avram](https://github.com/luckyframework/avram/pull/641)
- Fixed: database transactions to not require a `Bool` return type. [#626 in Avram](https://github.com/luckyframework/avram/pull/626)
- Removed: deprecated `raw_where` methods. [#653 in Avram](https://github.com/luckyframework/avram/pull/653)
- Added: new `where(&)` method for wrapping queries in parenthesis. [#652 in Avram](https://github.com/luckyframework/avram/pull/652)
- Added: new `drop_foreign_key` method. [#654 in Avram](https://github.com/luckyframework/avram/pull/654)
- Updated: how table names are stored internally paving the way for faster compilation. [#660 in Avram](https://github.com/luckyframework/avram/pull/660)
- Added: new `parameterize` method for string helpers. [#9 in Wordsmith](https://github.com/luckyframework/wordsmith/pull/9)
- Added: new `Habitat.extend` macro for extending existing configuration settings. [#59 in Habitat](https://github.com/luckyframework/habitat/pull/59)
- Added: new development dashboard shard `Breeze`. [View Breeze](https://github.com/luckyframework/breeze)
- Added: new .env parsing shard `LuckyEnv`. [View LuckyEnv](https://github.com/luckyframework/lucky_env)
### v0.26.0 (2021-02-06)
- Updated: the compile-error for missing Page args. [#1373](https://github.com/luckyframework/lucky/pull/1373)
- Fixed: flash messages to be discarded unless specifically kept. [#1374](https://github.com/luckyframework/lucky/pull/1374)
- Added: generating `JSON::Any` columns from `lucky gen.model` task. [#1375](https://github.com/luckyframework/lucky/pull/1375)
- Added: creating empty HTML tags passing in a Hash for options. [#1377](https://github.com/luckyframework/lucky/pull/1377)
- Updated: action generators to use actual route instead of the `route` or `nested_route` methods. [#1378](https://github.com/luckyframework/lucky/pull/1378)
- Updated: the compile-error for incorrect route helper usage. [#1372](https://github.com/luckyframework/lucky/pull/1372)
- Updated: `lucky gen.*.resource` task to not shadow outer local variable. [#1379](https://github.com/luckyframework/lucky/pull/1379)
- Fixed: uploaded files now have access to the tempfile before it's closed. [#1381](https://github.com/luckyframework/lucky/pull/1381)
- Added: new `lucky gen.task` task for generating a new Cli Task. [#1322](https://github.com/luckyframework/lucky/pull/1322)
- Added: new `params.get_all` method to get an Array of values from params. [#1389](https://github.com/luckyframework/lucky/pull/1389)
- Removed: broken `tag()` overloads. [#1394](https://github.com/luckyframework/lucky/pull/1394)
- Added: support for Crystal 0.36. [#1398](https://github.com/luckyframework/lucky/pull/1398)
- Fixed: empty HTML tags adding an extra space when generated. [#1400](https://github.com/luckyframework/lucky/pull/1400)
- Fixed: issue with wrong types for form HTML methods when passing in an Operation Attribute. [#1405](https://github.com/luckyframework/lucky/pull/1405)
- Updated: DB logging to be a little less excited. [#589 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/589)
- Removed: usage of the `route` and `nested_route` methods in generated apps. [#594 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/594)
- Added: some inline docs to using `ENV["LUCKY_TASK"]`. [#595 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/595/files)
- Added: ability to pass `--error-trace` flag to precompiled lucky tasks. [#596 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/596)
- Updated: to Laravel Mix 6 for generated apps. [#592 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/592)
- Fixed: issue with generated apps Github Actions CI failing. [#600 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/600)
- Updated: `Procfile` for generated apps to call a binary named by the project instead of "app". [#601 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/601)
- Added: a new Heroku Buildpack to build all proper targets. Accompanies #601 in Lucky CLI. [view Heroku Buildpack](https://github.com/luckyframework/heroku-buildpack-lucky)
- Added: equality operators (`==`, `===`) to `avram_enum`. [#566 in Avram](https://github.com/luckyframework/avram/pull/566)
- Fixed: bug in associations that returned more records than they should have. [#574 in Avram](https://github.com/luckyframework/avram/pull/574)
- Updated: error messages during migrations that were obscured. [#577 in Avram](https://github.com/luckyframework/avram/pull/577)
- Added: `UUID` primary keys are now generated in the database with a fallback using Crystal. [#578 in Avram](https://github.com/luckyframework/avram/pull/578)
- Updated: `validate_size_of` to only take a `String` attribute. [#579 in Avram](https://github.com/luckyframework/avram/pull/579)
- Added: new `validate_numeric` validation for validating numbers. [#580 in Avram](https://github.com/luckyframework/avram/pull/580)
- Fixed: issue with `has_one` preloads not loading the correct records. [#581 in Avram](https://github.com/luckyframework/avram/pull/581)
- Updated: compile-error for attempting to use nilable attributes. [#583 in Avram](https://github.com/luckyframework/avram/pull/583)
- Added: new `Avram::DeleteOperation` objects for handling complex delete logic. [#573 in Avram](https://github.com/luckyframework/avram/pull/573)
- Updated: how generics work in `Avram::Attribute`. [#586 in Avram](https://github.com/luckyframework/avram/pull/586)
- Updated: custom type support to allow for better 3rd-party support in the future. [#587 in Avram](https://github.com/luckyframework/avram/pull/587)
- Added: new `lucky db.console` task to enter PSQL for your app. [#592 in Avram](https://github.com/luckyframework/avram/pull/592)
- Updated: more support and transparency for custom DB types with new `criteria` method. [#591 in Avram](https://github.com/luckyframework/avram/pull/591)
- Fixed: using the `datetime-local` tag to persist the Time. [#603 in Avram](https://github.com/luckyframework/avram/pull/603)
- Updated: to the latest (0.23.x) crystal-pg version. [#605 in Avram](https://github.com/luckyframework/avram/pull/605)
- Added: support for using `citext` columns with new `case_sensitive: false` option. [#608 in Avram](https://github.com/luckyframework/avram/pull/608)
- Added: support for `Array(UUID)` columns. [#609 in Avram](https://github.com/luckyframework/avram/pull/609)
- Updated: SaveOperation `after_save` and `after_commit` callbacks to run even if no changes to the record are made. [#612 in Avram](https://github.com/luckyframework/avram/pull/612)
- Removed: the `after_completed` callback in SaveOperations. [also #612 in Avram](https://github.com/luckyframework/avram/pull/612)
- Rename: `Avram::Box` to `Avram::Factory`. [#614 in Avram](https://github.com/luckyframework/avram/pull/614)
- Added: new composite primary keys for migrations. [#616 in Avram](https://github.com/luckyframework/avram/pull/616)
- Fixed: issues with `has_one` in `SaveOperation` not updating the associated record when doing updates. [#596 in Avram](https://github.com/luckyframework/avram/pull/596)
- Updated: `Avram::Operation` to not call `run` if the operation is not `valid?`. [#621 in Avram](https://github.com/luckyframework/avram/pull/621)
- Updated: LuckyFlow ChromeDriver to run in non-headless mode (head mode?). [#112 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/112)
- Added: ability to use a non Chrome browser in LuckyFlow. [#112 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/113)
- Added: the `browser_binary` option back in which lets use specify a different Chrome based browser. [#114 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/114)
- Added: new `pause` method for LuckyFlow to pause execution of flow for debugging. [#117 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/117)
- Updated: runtime-error for duplicate defined routes. [#45 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/45)
- Added: `have_delivered_emails` spec expectation method for `Carbo`. [#45 in Carbon](https://github.com/luckyframework/carbon/pull/45)
### v0.25.0 (2020-12-18)
- Rename: component `with_defaults` renamed to `tag_defaults` [#1262](https://github.com/luckyframework/lucky/pull/1262)
- Fixed: send HSTS headers over HTTPS. [#1268](https://github.com/luckyframework/lucky/pull/1268)
- Updated: `memoize` can be used on any `Object` [#1270](https://github.com/luckyframework/lucky/pull/1270)
- Added: `tfoot()` tag method. [#1296](https://github.com/luckyframework/lucky/pull/1296)
- Added: routes now support glob routing [#1294](https://github.com/luckyframework/lucky/pull/1294)
- Fixed: passing a `UUID` in to a tag for text [#1280](https://github.com/luckyframework/lucky/pull/1280)
- Fixed: calling route helper methods on actions with `route_prefix` set. [#1298](https://github.com/luckyframework/lucky/pull/1298)
- Added: clearing cookies with specific options passed in [#966](https://github.com/luckyframework/lucky/pull/966)
- Fixed: passing a `name` prop to a custom tag. [#1309](https://github.com/luckyframework/lucky/pull/1309)
- Added: `blockquote()` and `cite()` tag methods. [#1317](https://github.com/luckyframework/lucky/pull/1317)
- Added: type name in error message for action classes [#1321](https://github.com/luckyframework/lucky/pull/1321)
- Fixed: params that use `Bool` with a default value of `false` [#1352](https://github.com/luckyframework/lucky/pull/1352)
- Updated: generated `start_server` binary is now output to the `bin` directory instead of top-level. [#1358](https://github.com/luckyframework/lucky/pull/1358)
- Fixed: HTTP status description in the log output. [#1362](https://github.com/luckyframework/lucky/pull/1362)
- Updated: reverted the `DATABASE_URL` ENV. [#551 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/551)
- Updated: emails will print to the log in development for easier debugging. [#555 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/555)
- Updated: Tasks can use the `output` property for easier testing. Added an `example` option to task args. [#557 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/557)
- Added: New generated Lucky projects will come with Github Actions out of the box. [#559 In Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/559)
- Updated: front-end `package.json` dependencies. [#553](https://github.com/luckyframework/lucky_cli/pull/553)
- Fixed: Signal trap is properly caught when running `lucky dev`. [#572 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/572)
- Updated: the built-in seed tasks to better match the common structure. [#584 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/584)
- Added: new `Lucky::Env.task?` method will return true if `ENV["LUCKY_TASK"] = "true"` is set. [#576 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/576)
- Updated: Query objects no longer mutate which fixes calling aggregate methods without needing to `clone`. [#411 in Avram](https://github.com/luckyframework/avram/pull/411)
- Updated: the error message when a required primary key is missing. [#454 in Avram](https://github.com/luckyframework/avram/pull/454)
- Updated: `fill_existing_with` to be used with nilable columns. [#452 in Avram](https://github.com/luckyframework/avram/pull/452)
- Fixed: `Bool` columns with a default `false` value. [#461 in Avram](https://github.com/luckyframework/avram/pull/461)
- Fixed: `belongs_to` using the wrong key in some cases. [#465 in Avram](https://github.com/luckyframework/avram/pull/465)
- Fixed: using optional Arrays in columns and migrations. [#471 in Avram](https://github.com/luckyframework/avram/pull/471)
- Fixed: calling `to_s` or `to_i` on an enum column to get the enum's proper value. [#474 in Avram](https://github.com/luckyframework/avram/pull/474)
- Updated: using `raw_where` will now be deprecated in favor of a unified `where`. [#460 in Avram](https://github.com/luckyframework/avram/pull/460)
- Fixed: issues with invalid SQL with joins. [#451 in Avram](https://github.com/luckyframework/avram/pull/451)
- Added: a whole new interface for `Avram::Operation`. [#469 in Avram](https://github.com/luckyframework/avram/pull/469)
- Updated: `Avram::SaveOperation` callback methods `after_save` and `after_commit` work with blocks, and more. [#481 in Avram](https://github.com/luckyframework/avram/pull/481)
- Added: a compile-time error catch when passing a raw hash in to a `SaveOperation`. [#485 in Avram](https://github.com/luckyframework/avram/pull/485)
- Removed: `register_setup_step` macro used for hooking in to the Avram model setup. [#486 in Avram](https://github.com/luckyframework/avram/pull/486)
- Added: new `or()` query method to perform `WHERE x OR y` SQL calls. [#442 in Avram](https://github.com/luckyframework/avram/pull/442)
- Updated: database calls to be optimized for speed. [#491 in Avram](https://github.com/luckyframework/avram/pull/491)
- Added: `params.has_key_for?` to check if params contains a key for an operation. [#500 in Avram](https://github.com/luckyframework/avram/pull/500)
- Added: conditional callbacks for `Avram::SaveOperation`. [#495 in Avram](https://github.com/luckyframework/avram/pull/495)
- Updated: the `has_many` count method to not preload when just a number is being returned. [#509 in Avram](https://github.com/luckyframework/avram/pull/509)
- Fixed: passing a `file_attribute` as a named arg to an operation. [#514 in Avram](https://github.com/luckyframework/avram/pull/514)
- Removed: unique filtering on `WHERE` clauses. [#518](https://github.com/luckyframework/avram/pull/518)
- Updated: error message when using `remove` incorrectly in migrations. [#524 in Avram](https://github.com/luckyframework/avram/pull/524)
- Added: error message when trying to generate a migration by a name that already exists. [#528 in Avram](https://github.com/luckyframework/avram/pull/528)
- Added: new custom errors for Operation objects. [#534 in Avram](https://github.com/luckyframework/avram/pull/534)
- Updated: `add_belongs_to` can now set a unique index. [#536 in Avram](https://github.com/luckyframework/avram/pull/536)
- Fixed: creating records by passing in values that match the default. [#540 in Avram](https://github.com/luckyframework/avram/pull/540)
- Updated: how `has_many through` associations are defined to fix has_many through a has_many through association. [#525 in Avram](https://github.com/luckyframework/avram/pull/525)
- Added: new `after_completed` callback on `Avram::SaveOperation` which is called even if no updates are made. [#544 in Avram](https://github.com/luckyframework/avram/pull/544)
- Added: `UUID` primary key checks to the SchemaEnforcer. [#546 in Avram](https://github.com/luckyframework/avram/pull/546)
- Added: records already loaded in to memory can now preload associations. [#542 in Avram](https://github.com/luckyframework/avram/pull/542), [#553 in Avram](https://github.com/luckyframework/avram/pull/553), [#561 in Avram](https://github.com/luckyframework/avram/pull/561)
- Added: support for models to use `VIEW`. [#555 in Avram](https://github.com/luckyframework/avram/pull/555)
- Added: new `defaults` method for defining default query methods on Query objects. [#564 in Avram](https://github.com/luckyframework/avram/pull/564)
- Fixed: setting two routes that use different path variable names. [#38 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/38)
- Added: route globbing. [#40 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/40)
- Fixed: catching when duplicate routes are defined. [#42 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/42)
- Added: flow spec matcher method `have_current_path`. [#96 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/96)
- Fixed: flow spec `have_text` matcher method to check if the text is included and not exact. [#99 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/99)
- Added: flow method to confirm and accept javascript modal boxes. [#101 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/101)
- Added: flow to fill a select field. [#104 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/104)
- Added: flow to select multiple values from a select field. [#106 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/106)
- Added: flow method `element.hover` to hover over an element. [#108 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/108)
### v0.24.0 (2020-09-05)
- Fixed: `send_text_response` default status to nil [#1214](https://github.com/luckyframework/lucky/pull/1214)
- Added: `data` method for Actions to return file contents [#1220](https://github.com/luckyframework/lucky/pull/1220)
- Updated: Component `m` is renamed to `mount` [#1226](https://github.com/luckyframework/lucky/pull/1226)
- Updated: Components with UrlHelpers like `current_page?` [#1228](https://github.com/luckyframework/lucky/pull/1228)
- Added: optional param routing [#1229](https://github.com/luckyframework/lucky/pull/1229)
- Updated: docs on `accept_format` [#1234](https://github.com/luckyframework/lucky/pull/1234)
- Updated: generator templates to use getter methods over instance variables [#1236](https://github.com/luckyframework/lucky/pull/1236)
- Updated: our community to use Discord for community [chat room](https://discord.gg/HeqJUcb) [#1237](https://github.com/luckyframework/lucky/pull/1237)
- Updated: compile-time error when path params are defined with dashes [#1238](https://github.com/luckyframework/lucky/pull/1238)
- Updated: path helpers to render query params even if default value is passed [#1239](https://github.com/luckyframework/lucky/pull/1239)
- Updated: `redirect_back` to disallow external referrers by default with config option [#1241](https://github.com/luckyframework/lucky/pull/1241)
- Updated: generated api apps will use `disable_cookies` by default [#535 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/535)
- Fixed: generating an app with the name "app" will raise an error [#543 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/543)
- Updated: `AppClient` renamed to `ApiClient` [#534 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/534)
- Updated: generated projects to use `--ignore-crystal-version` flag when running `shards install`. NOTE: this is a temporary update, and will be reverted in a future release. [Read Crystal Blog](https://crystal-lang.org/2020/08/20/preparing-our-shards-for-crystal-1.0.html) [See commit](https://github.com/luckyframework/lucky_cli/pull/547/files#diff-154806d6e0faa0b1aa1e518f3bbd3647R25)
- Added: ability to set default values on model columns [#424 in Avram](https://github.com/luckyframework/avram/pull/424)
- Added: `file_attribute` for operations to specify a file from params [#428 in Avram](https://github.com/luckyframework/avram/pull/428)
- Added: new `Database.delete` strategy for cleaning up data in specs [#426 in Avram](https://github.com/luckyframework/avram/pull/426)
- Added: `create_function` and `drop_function` to create SQL functions [#427 in Avram](https://github.com/luckyframework/avram/pull/427)
- Updated: `Avram::PostgresURL` renamed to `Avram::Credentials` with a new interface [#433 in Avram](https://github.com/luckyframework/avram/pull/433)
- Added: `create_trigger` and `drop_trigger` to create SQL triggers [#436 in Avram](https://github.com/luckyframework/avram/pull/436)
- Added: association `_count` method to easily return a count of a has_many association [#392 in Avram](https://github.com/luckyframework/avram/pull/392)
- Added: new `Pulsar` shard for pub/sub style communication in Lucky [See Pulsar](https://github.com/luckyframework/pulsar)
- Added: Pulsar instrumentation to Avram for subscribing to queries [#441 in Avram](https://github.com/luckyframework/avram/pull/441)
- Added: support for `Array(Float64)` in databases [#443 in Avram](https://github.com/luckyframework/avram/pull/443)
- Updated: `fill_existing_with` option on `add_belongs_to` in migrations [#444 in Avram](https://github.com/luckyframework/avram/pull/444)
- Added: `Box.build_attributes` method to build the attributes of a model in specs [#449 in Avram](https://github.com/luckyframework/avram/pull/449)
- Fixed: blank strings causing parse exceptions in save operations [#448 in Avram](https://github.com/luckyframework/avram/pull/448)
- Updated: LuckyRouter with many performance and structural refactors [#28](https://github.com/luckyframework/lucky_router/pull/28), [#30](https://github.com/luckyframework/lucky_router/pull/30), [#31](https://github.com/luckyframework/lucky_router/pull/31), [#32](https://github.com/luckyframework/lucky_router/pull/32)
### v0.23.1 (2020-07-07)
- Fixed: generated apps using deprecated `mount` instead of `m` [#531 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/531)
### v0.23.0 (2020-06-26)
- Updated: password reset tokens to be URL safe [#1118](https://github.com/luckyframework/lucky/pull/1118)
- Added: `radio` input helper [#1125](https://github.com/luckyframework/lucky/pull/1125)
- Added: component file paths to rendered comments in markup for development [#1126](https://github.com/luckyframework/lucky/pull/1126)
- Added: `query_param_declarations` method to Action classes [#1122](https://github.com/luckyframework/lucky/pull/1122)
- Fixed: generating a model that already exists now raises an error [#1127](https://github.com/luckyframework/lucky/pull/1127)
- Added: `select_prompt` helper method [#1124](https://github.com/luckyframework/lucky/pull/1124)
- Updated: `lucky routes` UI to now include query params [#1128](https://github.com/luckyframework/lucky/pull/1128)
- Added: `route_prefix` method for Actions to prefix all routes [#1121](https://github.com/luckyframework/lucky/pull/1121)
- Fixed: error when deleting cookies that don't exist [#1132](https://github.com/luckyframework/lucky/pull/1132)
- Fixed: handling ajax form submissions with TurboLinks [#1133](https://github.com/luckyframework/lucky/pull/1133)
- Fixed: issue with `ajax?` method not returning correct value [#1134](https://github.com/luckyframework/lucky/pull/1134)
- Fixed: security issue by escaping HTML helpers by default [#1135](https://github.com/luckyframework/lucky/pull/1135)
- Updated: `memoize` to allow for arguments, and `nil` and `false` values [#1139](https://github.com/luckyframework/lucky/pull/1139)
- Updated: model generator to provide more helpful error messages [#1140](https://github.com/luckyframework/lucky/pull/1140)
- Added: `get_raw` method to params along with striping blankspace on param `get` calls [#1144](https://github.com/luckyframework/lucky/pull/1144)
- Removed: `mount` with deprecation in favor of new `m` method.
- Added: `m` helper method as a `mount` replacement with a new interface. [#1151](https://github.com/luckyframework/lucky/pull/1151)
- Updated: `String#squish` method to be faster [#1159](https://github.com/luckyframework/lucky/pull/1159)
- Removed: `Lucky::SessionHandler` and `Lucky::FlashHandler`. [#518 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/518)
- Fixed: issue with session cookies not being written at the right time. [#1160](https://github.com/luckyframework/lucky/pull/1160)
- Added: `template` HTML method for `<template>` tags. [#1164](https://github.com/luckyframework/lucky/pull/1164)
- Fixed: flash messages being lost during multiple redirects. [#1169](https://github.com/luckyframework/lucky/pull/1169)
- Added: `redirect_back` for actions to redirect back to previous referrer [#1168](https://github.com/luckyframework/lucky/pull/1168)
- Added: `component` method to render a Component directly from an Action [#1172](https://github.com/luckyframework/lucky/pull/1172)
- Added: `canonical_link` HTML helper method. [#1182](https://github.com/luckyframework/lucky/pull/1182)
- Added: `disable_cookies` macro to stop cookies from being written on a specific action [#1180](https://github.com/luckyframework/lucky/pull/1180)
- Fixed: setting `samesite` on cookies in your `Lucky::CookieJar` `on_set` [#1183](https://github.com/luckyframework/lucky/pull/1183)
- Fixed: compilation bug in generated page when running `lucky gen.page` [#1191](https://github.com/luckyframework/lucky/pull/1191)
- Added: `multipart: true` option to `form_for` to set multipart enctype [#1200](https://github.com/luckyframework/lucky/pull/1200)
- Added: `Lucky.root` method to raise compile-time error directing people to use `Dir.current` instead. [#1206](https://github.com/luckyframework/lucky/pull/1206)
- Added: native CLI args to `LuckyCli::Task`. [#466 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/466)
- Updated: generated projects to disable StaticFileHandler directory listing by default. [#510 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/510)
- Updated: error action to return a 404 for `Avra::RecordNotFoundError` [#524 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/524)
- Fixed: `select_count` failing when postgres returns no counts. [#357 in Avram](https://github.com/luckyframework/avram/pull/357)
- Added: support for postgres extensions with `enable_extension`, `disable_extension`, and `update_extension`. [#356 in Avram](https://github.com/luckyframework/avram/pull/356)
- Added: enum support for models with `avram_enum` macro. [#339 in Avram](https://github.com/luckyframework/avram/pull/339)
- Fixed: the error message when using `remove` in migrations, and not passing a Symbol.
- Added: `rename` and `rename_belongs_to` in migrations [#366 in Avram](https://github.com/luckyframework/avram/pull/366)
- Added: new `lucky db.setup` task which runs `db.create` and `db.migrate`. [#361 in Avram](https://github.com/luckyframework/avram/pull/361)
- Added: ability to set a custom index name for table indices. [#386 in Avram](https://github.com/luckyframework/avram/pull/386)
- Fixed: using a custom primary key name of type `UUID`. [#401 in Avram](https://github.com/luckyframework/avram/pull/401)
- Added: checking for a connection to the PostgreSQL engine before running the `lucky db.create` task. [#397 in Avram](https://github.com/luckyframework/avram/pull/397)
- Fixed: logging issues related to Crystal 0.35.0. [#31 in Dexter](https://github.com/luckyframework/dexter/pull/31)
- Updated: which selenium library was being used for LuckyFlow. [#76 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/76)
- Added: initial work to support using other browsers aside from Chrome in LuckyFlow. [#79 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/79), [#88 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/88)
- Added: support to auto fetch latest webdrivers in LuckyFlow. [#80 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/80)
- Fixed: issue with really long stacktrace in LuckyFlow. [#83 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/83)
- Added: `have_text` expectation method for Flow specs. [#87 in LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/87)
- Added: optional path param routing. [#18 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/18)
- Update: routing to ensure matching dynamic fragments all work. [#23 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/23)
- Added: a little bit of speed to the routing lookup. [#26 in LuckyRouter](https://github.com/luckyframework/lucky_router/pull/26)
- Added: a new `validation` option to Habitat settings. [#49 in Habitat](https://github.com/luckyframework/habitat/pull/49)
- Renamed: the internal Habitat `Settings` class to `HabitatSettings` to avoid name conflicts in some Lucky apps. [#48 in Habitat](https://github.com/luckyframework/habitat/pull/48)
- Fixed: bug when setting a default value in a Habitat setting that could potentially raise an exception. [#51 in Habitat](https://github.com/luckyframework/habitat/pull/51)
### v0.22.0 (2020-06-17)
- Added: support for Crystal 0.35.0
### v0.21.0 (2020-04-19)
- Added: support for Crystal 0.34.0 `Log` class [#506 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/506/files)
- Added: `paginate_array` for paginating Arrays [#1108](https://github.com/luckyframework/lucky/pull/1108)
- Improve error logging [#1114](https://github.com/luckyframework/lucky/pull/1114)
- Improve http status logging [#1114](https://github.com/luckyframework/lucky/pull/1114)
- Upgraded: Dexter to v0.2.0
- Type-safe log configuration
- New JSON formatter
- Helpers for testing logs
- Fix for issues with the system check in Procfile.dev [#505 in Lucky CLI](https://github.com/luckyframework/lucky_cli/pull/505)
### v0.20.0 (2020-04-08)
- Added: support for Crystal 0.34.0
- Fixed: error on some generated pages from missing sourcemap [#1019](https://github.com/luckyframework/lucky/pull/1019)
- Updated: `options_for_select` to accept more types [#295](https://github.com/luckyframework/lucky/pull/295)
- Added: ability to pass boolean attrs in link helper methods [#1032](https://github.com/luckyframework/lucky/pull/1032)
- Removed: setting `needs` with `?`. Lucky now generates a method ending in `?` for you when the type is `Bool` [#1034](https://github.com/luckyframework/lucky/pull/1034)
- Added: `needs` on pages can now be accessed by a method and not just instance variable [#1034](https://github.com/luckyframework/lucky/pull/1034)
- Removed: `link` helper method with a `String` path. [#1035](https://github.com/luckyframework/lucky/pull/1035)
- Added: new `Lucky::CookieNotFoundError` class. [#1038](https://github.com/luckyframework/lucky/pull/1038)
- Added: `cookies.deleted?()` method for checking if a cookie has been deleted. [#1040](https://github.com/luckyframework/lucky/pull/1040)
- Added: new `Lucky::Paginator` component with built-in styles for different different CSS frameworks. [#1020](https://github.com/luckyframework/lucky/pull/1020)
- Fixed: `needs` accidentally overwriting methods of the same name. [#1046](https://github.com/luckyframework/lucky/pull/1046)
- Updated: `label_for` to be a little more flexible with `nil` text. [#1047](https://github.com/luckyframework/lucky/pull/1047)
- Updated: resource generator to be a little easier to read and digest. [#1050](https://github.com/luckyframework/lucky/pull/1050)
- Updated: development `ENV` now uses `ENV["DEV_PORT"]` instead of `ENV["PORT"]` to fix issues with process managers. [#1051](https://github.com/luckyframework/lucky/pull/1051)
- Added: new `Lucky::CatchUnpermittedAttribute` mixin for `Shared::Field` component. [#1052](https://github.com/luckyframework/lucky/pull/1052)
- Added: new methods in Actions for accessing params from different sources like `from_json`, `from_query`, `from_form`, and `from_multipart`. [#1053](https://github.com/luckyframework/lucky/pull/1053)
- Updated: generated pages to have some default text pointing to the location of the file to edit. [#1057](https://github.com/luckyframework/lucky/pull/1057)
- Fixed: incorrect pluralization of resources on `NewPage`. [#1058](https://github.com/luckyframework/lucky/pull/1058)
- Updated: all action "callbacks" are officially named "pipes". All pipes only log when halted by default. [#1062](https://github.com/luckyframework/lucky/pull/1062)
- Updated: the `lucky dev` watcher does not print which file changes because you know you just changed that file. [#1065](https://github.com/luckyframework/lucky/pull/1065)
- Added: a new HTTP handler to set the `request.remote_address` if the `X-Forwarded-For` header is set. [#1059](https://github.com/luckyframework/lucky/pull/1059)
- Added: a `current_page?` helper method for pages. [#1074](https://github.com/luckyframework/lucky/pull/1074)
- Added: `FormFields` component for generated resources. [#1081](https://github.com/luckyframework/lucky/pull/1081)
- Updated: all HTML tag methods explicitly return `Nil` now. [#1083](https://github.com/luckyframework/lucky/pull/1083)
- Updated: page markup to render directly to the IO instead of creating an additional string. [#1084](https://github.com/luckyframework/lucky/pull/1084)
- Added: `String#squish` method. [#1085](https://github.com/luckyframework/lucky/pull/1085)
- Updated: error message from returning invalid type in Actions. [#1086](https://github.com/luckyframework/lucky/pull/1086)
- Added: ability to set custom directory when generating a new Lucky project [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/464)
- Added: ability to set your postgres DB port with ENV var. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/469)
- Added: a `robots.txt` file to generated web apps by default. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/472)
- Added: new compiling spinner graphic for a cleaner UX. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/481)
- Updated: some comments on the generated main app file. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/484)
- Added: lots of internal documentation. (many small commits to LuckyCli)
- Updated: generated `UserSerializer` to inherit from `BaseSerializer`. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/489)
- Updated: cookies to default to `http_only`. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/491)
- Updated: node dependencies in generated web apps. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/493)
- Added: new `system_check` script along with some refactors to make checking that your app is setup a lot easier. [See LuckyCli](https://github.com/luckyframework/lucky_cli/pull/482)
- Removed: ability to pass a raw hash to an `Avram::SaveOperation`. [See Avram](https://github.com/luckyframework/avram/pull/312)
- Added: ability to `skip_schema_enforcer` for certain models. [See Avram](https://github.com/luckyframework/avram/pull/314)
- Added: `Avram::Model#reload` to reload all of the attributes that may have been updated since the instance was created. [See Avram](https://github.com/luckyframework/avram/pull/324)
- Added: `Query#reset_where` to reset the WHERE clause on a specific column. [See Avram](https://github.com/luckyframework/avram/pull/325)
- Added: logging queries that fail. [See Avram](https://github.com/luckyframework/avram/pull/326)
- Fixed: using `fill_existing_with` when you already had data in your table. [See Avram](https://github.com/luckyframework/avram/pull/328)
- Added: bulk updating records straight from a query object. [See Avram](https://github.com/luckyframework/avram/pull/329)
- Added: new "soft delete" feature. [See Avram](https://github.com/luckyframework/avram/pull/323)
- Fixed: saving empty array columns when the column can't be `nil`, but it can be `[]`. [See Avram](https://github.com/luckyframework/avram/pull/330)
- Updated: `SaveOperation.new` to set attributes directly. [See Avram](https://github.com/luckyframework/avram/pull/332)
- Removed: the `on` option for `needs` in `SaveOperation`. [See Avram](https://github.com/luckyframework/avram/pull/332)
- Fixed: connecting to databases running on a unix domain socket. [See Avram](https://github.com/luckyframework/avram/pull/333)
- Added: new shard for turning an Avram column in to a URL slug. [AvramSlugify](https://github.com/luckyframework/avram_slugify)
### v0.19.0 (2020-02-29)
- Added: missing docs for time helpers [#943](https://github.com/luckyframework/lucky/pull/943)
- Added: HTML boolean attributes to checkbox and textarea helpers [#955](https://github.com/luckyframework/lucky/pull/955)
- Fixed: generated templates with proper naming conventions [#956](https://github.com/luckyframework/lucky/pull/956)
- Added: `to_param` for `UUID` allowing UUID to be passed in params [#945](https://github.com/luckyframework/lucky/pull/945)
- Updated: watcher error message to be a little less abrupt [#968](https://github.com/luckyframework/lucky/pull/968)
- Updated: generated migrations using the `table_for` macro [#970](https://github.com/luckyframework/lucky/pull/970)
- Fixed: using `with_defaults` when the tag has content [#972](https://github.com/luckyframework/lucky/pull/972)
- Added: `any?` and `empty?` to `flash` [#977](https://github.com/luckyframework/lucky/pull/977)
- Fixed: allowing `false` values for `needs` [#979](https://github.com/luckyframework/lucky/pull/979)
- Updated: `needs` to now infer a value of `nil` when the type is nilable [#980](https://github.com/luckyframework/lucky/pull/980)
- Fixed: allowing the `-h` flag for the watch task [#958](https://github.com/luckyframework/lucky/pull/958)
- Added: gzip response for assets when it's configured [#983](https://github.com/luckyframework/lucky/pull/983)
- Added: Lucky API docs are now generated from the CI which is deployed to Github pages [#989](https://github.com/luckyframework/lucky/pull/989)
- Fixed: when using `needs` with different values in random order and Lucky would not compile [#993](https://github.com/luckyframework/lucky/pull/993)
- Added: more context to the resource generator [See commit](https://github.com/luckyframework/lucky/commit/ae7301750c9b49c99d5b530ddc93cda91e73f288)
- Added: ability to pass Crystal's `--error-tace` flag to `lucky watch` [#957](https://github.com/luckyframework/lucky/pull/957)
- Fixed: generating resource.browser when using a `JSON::Any` column type [#997](https://github.com/luckyframework/lucky/pull/997)
- Fixed: issue when using HTML boolean attributes with custom tags [#1010](https://github.com/luckyframework/lucky/pull/1010)
- Added: the option to define columns in the model generator [#1009](https://github.com/luckyframework/lucky/pull/1009)
- Updated: permitting columns generated from the resource generator [#1014](https://github.com/luckyframework/lucky/pull/1014)
- Added: new `to_prepared_sql` method to generate fully prepared sql for debugging [See Avram](https://github.com/luckyframework/avram/pull/264)
- Fixed: cloning distinct queries [See Avram](https://github.com/luckyframework/avram/pull/285)
- Added: new predicate methods variants for boolean columns [See Avram](https://github.com/luckyframework/avram/pull/300)
- Added: new `changed?`, `changes`, and `original_value` methods for attributes in Operations [See Avram](https://github.com/luckyframework/avram/pull/295)
- Updated: `validate_size_of` and `validate_inclusion_of` to allow `nil` values [See Avram](https://github.com/luckyframework/avram/pull/299)
- Updated: error messages on some callbacks [See Avram](https://github.com/luckyframework/avram/pull/282)
- Fixed: `select_sum` when the column is any number type [See Avram](https://github.com/luckyframework/avram/pull/304)
- Fixed: issues with `has_one` when your model is namespaced, and how it's queried [See Avram](https://github.com/luckyframework/avram/pull/263)
- Fixed: aggregate query methods to work on all number types [See Avram](https://github.com/luckyframework/avram/pull/307)
- Fixed: bug when using a Box that had no columns [See Avram](https://github.com/luckyframework/avram/pull/310)
- Updated: preloads to only call when there are parent records. This is a query optimization update. [See Avram](https://github.com/luckyframework/avram/pull/306)
### v0.18.3 (2020-02-17)
- Added: support for Crystal 0.33.0
### v0.18.2 (2019-12-13)
- Added: support for Crystal 0.32.0
### v0.18.1 (2019-10-18)
- Fixed: debug page in development with reset context
- Updated: lucky exec works more like a REPL
- Updated: Log time measured with monotonic
- Fixed: Record deletion when primary key is UUID
- Fixed: Setting empty array as default to array column
- Added: Overflow cast catch from Int64 to Int32
- Fixed: UUID primary key issue in SaveOperation
- Fixed: required attribute validations on custom before_save callbacks
- Added: New `reset_limit` query method
- Added: New `reset_offset` query method
### v0.18.0 (2019-10-03)
- Added: support for Crystal 0.31.1
- Fixed: how accept / content-type headers are handled [#869](https://github.com/luckyframework/lucky/pull/869)
- Added: `ParamParsingError` for when parsing JSON params fails [#874](https://github.com/luckyframework/lucky/pull/874)
- Updated: `Lucky::BaseHTTPClient` [#875](https://github.com/luckyframework/lucky/pull/875)
- Updated: shell scripts for POSIX compliance [#879](https://github.com/luckyframework/lucky/pull/879)
- Added: `date_input`, `time_input`, `datetime_input` [#877](https://github.com/luckyframework/lucky/pull/877)
- Added: support for HTTP `PATCH` [#885](https://github.com/luckyframework/lucky/pull/885)
- Added: `abbr` HTML tag [#886](https://github.com/luckyframework/lucky/pull/886)
- Fixed: missing primary_key and timestamps in generated migrations [#888](https://github.com/luckyframework/lucky/pull/888)
- Fixed: `pluralize` to take any Int [#890](https://github.com/luckyframework/lucky/pull/890)
- Fixed: generation of migrations with resource [see Commit](https://github.com/luckyframework/lucky/commit/31848d916bdba9d2e6333e508ae2e95d9788263a)
- Rename: `Lucky::HttpRespondable` to `Lucky::RenderableError` [see Commit](https://github.com/luckyframework/lucky/commit/026f2e3bf9c1085376537c27bc2a28bfde590eb1)
- Fixed: `accepts_format`, and a few other mime type issues [#896](https://github.com/luckyframework/lucky/pull/896)
- Fixed: default curl requests to server not responding properly [#899](https://github.com/luckyframework/lucky/pull/899)
- Rename: `handle_error` to `render` in `ErrorAction` [#903](https://github.com/luckyframework/lucky/pull/903)
- Rename: `render` to `html` in Actions [#905](https://github.com/luckyframework/lucky/pull/905)
- Update: error message when missing type declaration for `needs` [#907](https://github.com/luckyframework/lucky/pull/907)
- Fixed: model generation allowing for non alphanumeric characters [#910](https://github.com/luckyframework/lucky/pull/910)
- Updated: make more errors renderable [#911](https://github.com/luckyframework/lucky/pull/911)
- Fixed: help messages now display for precompiled tasks [#923](https://github.com/luckyframework/lucky/pull/923)
- Updated: default help messages for tasks [#923](https://github.com/luckyframework/lucky/pull/923)
- Fixed: issue with precompile tasks running in some directories [#924](https://github.com/luckyframework/lucky/pull/924)
- Added: SQL logging [see Avram](https://github.com/luckyframework/avram/pull/213)
- Updated: error message when postgres isn't running [see Avram](https://github.com/luckyframework/avram/pull/218)
- Updated: `Box.create_pair` allows for setting attributes, and returns instances [see Avram](https://github.com/luckyframework/avram/pull/215)
- Added: ability to `clone` a query [see Avram](https://github.com/luckyframework/avram/pull/214)
- Fixed: `add_belongs_to` in alter statement using wrong Int size [see Avram](https://github.com/luckyframework/avram/pull/224)
- Fixed: incorrect error message from `SaveOperation` updates in 0.17 [see Avram](https://github.com/luckyframework/avram/pull/225)
- Added: `between` query method [see Avram](https://github.com/luckyframework/avram/pull/227)
- Added: ordering queries by `NULLS FIRST` and `NULLS LAST` [see Avram](https://github.com/luckyframework/avram/pull/228)
- Fixed: missing attributes from SaveOperation [see Avram](https://github.com/luckyframework/avram/pull/232)
- Added: `db.schema.restore` and `db.schema.dump` tasks [see Avram](https://github.com/luckyframework/avram/pull/216)
- Added: `group` query method for doing GROUP BY [see Avram](https://github.com/luckyframework/avram/pull/234)
- Updated: SchemaEnforcer [see Avram](https://github.com/luckyframework/avram/pull/237)
- Fixed: issue when calling `before` in SaveOperation [see Avram](https://github.com/luckyframework/avram/pull/240)
- Added: JWT auth generation for API apps [see LuckyCli](https://github.com/luckyframework/lucky_cli/pull/395)
- Updated: Serializers to be smarter with collections [see LuckyCli](https://github.com/luckyframework/lucky_cli/pull/397)
- Updated: webpack to ignore `node_modules` directory [see LuckyCli](https://github.com/luckyframework/lucky_cli/pull/401)
- Removed: cli `lucky init` task args [see LuckyCli](https://github.com/luckyframework/lucky_cli/pull/420)
- Added: new `lucky init.custom` task to take args as `init` did before.
- Fixed: `lucky init` to catch invalid project names properly.
- Added: support for `browser_binary` in LuckyFlow [see LuckyFlow](https://github.com/luckyframework/lucky_flow/pull/59)
### v0.17 (2019-08-13)
- Rename: `Avram::BaseForm` to `Avram::SaveOperation` [see Avram](https://github.com/luckyframework/avram/pull/104)
- Rename: `Avram::Field` to `Avram::Attribute` [see Avram](https://github.com/luckyframework/avram/commit/d3503a161670077c1d7b14484382132ea3ab423d)
- Update: `number_to_currency` now returns `String` instead of writing to the view directly. [#809](https://github.com/luckyframework/lucky/pull/809)
- Fixed: bug in running `build.release` task.
- Update: mounted components render comments to show start and end of component. [#817](https://github.com/luckyframework/lucky/pull/817)
- Revert: returning `String` for `highlight` helper. [#818](https://github.com/luckyframework/lucky/pull/818)
- Update: text helpers that write to the view moved to their own module. [#820](https://github.com/luckyframework/lucky/pull/820)
- Rename: `fillable` to `permit_columns`. [see Avram](https://github.com/luckyframework/avram/commit/b32b5a9b53688762e22c063ebad9f858cba636c0)
- Added: `skip_if` option to `LogHandler`. [#824](https://github.com/luckyframework/lucky/pull/824)
- Rename: `Lucky::Exposeable` to `Lucky::Exposable`. [#827](https://github.com/luckyframework/lucky/pull/827)
- Rename: `Lucky::Routeable` to `Lucky::Routable`. [#827](https://github.com/luckyframework/lucky/pull/827)
- Added: `memoize` macro. [#832](https://github.com/luckyframework/lucky/pull/832)
- Added: `table_for` macro. [see Avram](https://github.com/luckyframework/avram/pull/127)
- Added: `xml` render method for Actions. [#838](https://github.com/luckyframework/lucky/pull/838)
- Rename: `text` render action to `plain_text`. [#838](https://github.com/luckyframework/lucky/pull/838)
- Update: `responsive_meta_tag` to be flexible. [#835](https://github.com/luckyframework/lucky/pull/835)
- Added: `Int16#to_param` and `Int64#to_param`.
- Fixed: `append/replace_class` with no default. [#842](https://github.com/luckyframework/lucky/pull/842)
- Added: multi database support. [see Avram](https://github.com/luckyframework/avram/pull/136)
- Rename: `form_name` to `param_key`. [see Avram](https://github.com/luckyframework/avram/pull/140)
- Fixed: 3rd party shards versions. [#855](https://github.com/luckyframework/lucky/pull/855)
- Added: JSON support. [see Avram](https://github.com/luckyframework/avram/pull/108)
- Update: calling `first` ensures proper order by. [see Avram](https://github.com/luckyframework/avram/pull/118)
- Update: specifying primary keys is more explicit now. [see Avram](https://github.com/luckyframework/avram/commit/c6fe426a455fc1bf397d0b3b32069a97cd89d2df)
- Added: custom primary key name support. [see Avram](https://github.com/luckyframework/avram/commit/a97c2b7dba359dda775bc587458a3d00571979e9)
- Added: column and primary key support for `Int16`. [see Avram](https://github.com/luckyframework/avram/pull/131)
- Rename: `Query.destroy_all` to `Query.truncate`. [see Avram](https://github.com/luckyframework/avram/pull/134)
- Fixed: model inference with table names. [see Avram](https://github.com/luckyframework/avram/pull/144)
- Rename: `virtual` to `attribute`. [see Avram](https://github.com/luckyframework/avram/pull/112)
- Rename: `VirtualForm` to `Operation`. [see Avram](https://github.com/luckyframework/avram/commit/daaf55955c8131dea8533584720257ca444f23a7)
- Added: support for `Array` fields. [see Avram](https://github.com/luckyframework/avram/pull/151)
- Rename: association query methods now prefixed with `where_`. [see Avram](https://github.com/luckyframework/avram/commit/f298b8a2be2b0d9b753f33517093c72c261cd148)
- Added: query method to bulk delete. [see Avram](https://github.com/luckyframework/avram/pull/169)
- Update: association query methods no longer take a block. [see Avram](https://github.com/luckyframework/avram/commit/a8112f3b0abca05c06da0c3ba3f599dc6b06110b)
- Added: support for polymorphic associations. [see Avram](https://github.com/luckyframework/avram/pull/165)
- Added: `db.rollback_to` task. [see Avram](https://github.com/luckyframework/avram/pull/133)
- Added: `db.migrations.status` task. [see Avram](https://github.com/luckyframework/avram/pull/135)
- Added: `db.verify_connection` task. [see Avram](https://github.com/luckyframework/avram/pull/167)
- Fixed: calling `lucky -v` from a lucky project failed. [see CLI](https://github.com/luckyframework/lucky_cli/pull/387)
- Update: name convention for operations to be `VerbNoun`. [see CLI](https://github.com/luckyframework/lucky_cli/pull/386)
- Added: `change_type` macro for migrations. [see Avram](https://github.com/luckyframework/avram/pull/209)
### v0.16 (2019-08-03)
- Added: support for Crystal 0.30.0
### v0.15 (2019-06-12)
- Removed `Lucky::Action::Status`. Use Crystal's `HTTP::Status` enum. [#769](https://github.com/luckyframework/lucky/pull/769)
- CookieOverflowError is now checked when the cookie is set instead of later in middleware. [#761](https://github.com/luckyframework/lucky/pull/761)
- Crystal 0.29.0 support added
- Rename `Lucky::BaseApp` to `Lucky::BaseAppServer`
- Rename `Sentry` to `LuckySentry`
- **Breaking change** - Many text helpers now return a `String` instead of appending to the view (`cycle`, `excerpt`, `highlight`, `pluralize`, `time_ago_in_words`, `to_sentence`, `word_wrap`) [#781](https://github.com/luckyframework/lucky/pull/781)
- Added new asset host option [#795](https://github.com/luckyframework/lucky/pull/795)
- Added new secure header modules [#735](https://github.com/luckyframework/lucky/pull/735)
- Added fallback routing [#731](https://github.com/luckyframework/lucky/pull/731)
- Updated SSL Handler with HSTS option [#734](https://github.com/luckyframework/lucky/pull/734)
- Components are now classes instead of modules [#714](https://github.com/luckyframework/lucky/pull/714)
- Fixed `BaseHTTPClient` params [#726](https://github.com/luckyframework/lucky/pull/726)
- Fixed passing `Symbol` for statuses in redirects [#730](https://github.com/luckyframework/lucky/pull/730)
- More helpful errors [#733](https://github.com/luckyframework/lucky/pull/733), [#732](https://github.com/luckyframework/lucky/pull/732)
### v.0.14 (2019-04-18)
- Crystal 0.28.0 support added
### v0.13 (2019-02-27)
- Use [`Dexter`](https://github.com/luckyframework/dexter) as the logger. https://github.com/luckyframework/lucky_cli/pull/300 and https://github.com/luckyframework/lucky_cli/pull/299
- Move scripts from `bin` to `script`. Ignore all of `bin` directory in `.gitignore`. See https://github.com/luckyframework/lucky_cli/pull/288 and https://github.com/luckyframework/lucky_cli/pull/301
- `App` in `src/app.cr` should now inherit from `Lucky::BaseApp`. See https://github.com/luckyframework/lucky_cli/pull/287/files for an example.
- Prefix id params with the resource name [#659](https://github.com/luckyframework/lucky/issues/659)
- Added Action#url_without_query_params [#662](https://github.com/luckyframework/lucky/pull/662)
- Added `Lucky::AssetHelpers.load_manifest` so that API apps don't need a blank manifest to compile.
- Pages ignore unused exposures [#666](https://github.com/luckyframework/lucky/issues/666)
- `unexpose` and `unexpose_if_exposed` have been removed because they are no
longer necessary now that pages ignore unused exposures.
- `is` in queries has been renamed to `eq`. For example: `UserQuery.new.name.not.is("Emily")` should now be `UserQuery.new.name.not.eq("Emily")`. If passing in something that could be `Nil`, one must use `nilable_eq` instead. [avram#46](https://github.com/luckyframework/avram/pull/46)
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Open Source Code of Conduct
In order to foster an inclusive, kind, harassment-free, and cooperative community, the Lucky team enforces this code of conduct on our open source projects.
## Summary
Harassment in code and discussion or violation of physical boundaries is completely unacceptable anywhere in Lucky's codebases, issue trackers, chatrooms, mailing lists, meetups, and other events. Violators will be warned by the core team. Repeat violations will result in being blocked or banned by the core team at or before the 3rd violation.
## In detail
Harassment includes offensive verbal comments related to gender identity, gender expression, sexual orientation, disability, physical appearance, body size, race, religion, sexual images, deliberate intimidation, stalking, sustained disruption, and unwelcome sexual attention.
Individuals asked to stop any harassing behavior are expected to comply immediately.
Maintainers are also subject to the anti-harassment policy.
If anyone engages in harassing behavior, including maintainers, we may take appropriate action, up to and including warning the offender, deletion of comments, removal from the project’s codebase and communication systems, and escalation to GitHub support.
If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact a member of the core team on Twitter on privately on Discord (@Paul Smith).
We expect everyone to follow these rules anywhere in Lucky's codebases, issue trackers, chatrooms, and mailing lists.
Finally, don't forget that it is human to make mistakes! We all do. Let’s work together to help each other, resolve issues, and learn from the mistakes that we will all inevitably make from time to time.
## Avoid using negative emoji reactions
Avoid using the 👎 or 😕 emoji for comments or PRs. It does not provide
enough value because it is not clear
**why** you are confused or what you disagree with. Instead, write a comment
explaining what you found confusing or that you don't like.
Some example responses instead of emoji:
- "I understand what you're trying to say, but I think that it will cause X problem."
- "I'm not sure I understand, could you include a gist explaining the use-case?"
## Thanks
Thanks to the CocoaPods Code of Conduct, Bundler Code of Conduct, JSConf Code of Conduct, and Contributor Covenant for inspiration and ideas.
## License
To the extent possible under law, the Lucky team has waived all copyright and related or neighboring rights to the Code of Conduct. This work is published from the United States.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Lucky
We love pull requests from everyone. By participating in this project, you
agree to abide by the project [code of conduct].
[code of conduct]: https://github.com/luckyframework/lucky/blob/main/CODE_OF_CONDUCT.md
Here are some ways _you_ can contribute:
- by using alpha, beta, and prerelease versions
- by reporting bugs
- by suggesting new features
- by writing or editing documentation
- by writing specifications
- by writing code ( **no patch is too small** : fix typos, add comments, clean up inconsistent whitespace )
- by refactoring code
- by closing [issues][]
- by reviewing patches
[issues]: https://github.com/luckyframework/lucky/issues
## Submitting an Issue
- We use the [GitHub issue tracker][issues] to track bugs and features.
- Before submitting a bug report or feature request, check to make sure it hasn't
already been submitted.
- When submitting a bug report, please include a [Gist][] that includes a stack
trace and any details that may be necessary to reproduce the bug, including
your Crystal version, and operating system. Ideally, a bug report
should include a pull request with failing specs.
[gist]: https://gist.github.com/
## Cleaning Up Issues
- Issues that have no response from the submitter will be closed after 30 days.
- Issues will be closed once they're assumed to be fixed or answered. If the
maintainer is wrong, it can be opened again.
- If your issue is closed by mistake, please understand and explain the issue.
We will happily reopen the issue.
## Setting Up Local Environment
1. Fork it ( <https://github.com/luckyframework/lucky/fork> )
1. Create your feature branch (git checkout -b my-new-feature)
1. Install docker: <https://docs.docker.com/compose/install/>
1. Run `script/setup` to build the Docker containers with everything you need.
1. Make your changes
1. Make sure specs pass: `script/test`.
1. Add a note to the CHANGELOG
1. Commit your changes (git commit -am 'Add some feature')
1. Push to the branch (git push origin my-new-feature)
1. Create a new Pull Request
> Run specific tests with `script/test <path_to_spec>`
## Submitting a Pull Request
1. [Fork][fork] the [official repository][repo].
2. [Create a topic branch.][branch]
3. Implement your feature or bug fix.
4. Add, commit, and push your changes.
5. [Submit a pull request.][pr]
## Notes
- Please add tests if you changed code. Contributions without tests won't be accepted.
- If you don't know how to add tests, please put in a PR and leave a comment
asking for help. We love helping!
- Please don't update the Gem version.
[repo]: https://github.com/luckyframework/lucky/
[fork]: https://help.github.com/articles/fork-a-repo/
[branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/
[pr]: https://help.github.com/articles/using-pull-requests/
Inspired by <https://github.com/middleman/middleman-heroku/blob/master/CONTRIBUTING.md>
================================================
FILE: Dockerfile
================================================
FROM crystallang/crystal:latest
WORKDIR /data
RUN apt-get update && \
apt-get install -y curl libreadline-dev unzip && \
curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun && \
# Cleanup leftovers
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY . /data
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2017 Paul Smith
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: PULL_REQUEST_TEMPLATE.md
================================================
## Purpose
Describe the feature or issue and link to the related issue.
If no issue has been opened about this, be sure to open an issue first to discuss the need for this PR.
## Description
Please include any relevant code samples or screen shots that may help to overview of this PR.
Link to specific lines of code, or examples if you need to.
## Checklist
* [ ] - An issue already exists detailing the issue/or feature request that this PR fixes
* [ ] - All specs are formatted with `crystal tool format spec src`
* [ ] - Inline documentation has been added and/or updated
* [ ] - Lucky builds on docker with `./script/setup`
* [ ] - All builds and specs pass on docker with `./script/test`
================================================
FILE: README.md
================================================
[](http://luckyframework.org)
[](https://github.com/luckyframework/lucky/releases/latest)
[](https://github.com/luckyframework/lucky/blob/main/LICENSE)
[](https://luckyframework.github.io/lucky)
[](https://luckyframework.org/guides)
[](https://discord.gg/HeqJUcb)
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?
- [Ruby on Rails to Lucky on Crystal: Blazing fast, fewer bugs, and even more fun.
](https://hackernoon.com/ruby-on-rails-to-lucky-on-crystal-blazing-fast-fewer-bugs-and-even-more-fun-104010913fec)
## Try Lucky
Lucky has a [fresh new set of guides](https://luckyframework.org/guides/) that
make it easy to get started.
Feel free to say hi or ask questions on our
[chat room](https://luckyframework.org/chat).
Or you can copy a real working app with [Lucky JumpStart](https://github.com/stephendolan/lucky_jumpstart/).
## Installing Lucky
To install Lucky, read the [Installing Lucky](https://luckyframework.org/guides/getting-started/installing) 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
Keep up to date by following [@luckyframework](https://twitter.com/luckyframework) on Twitter.
## Documentation
[API (main)](https://luckyframework.github.io/lucky/)
## What's it look like?
### JSON endpoint:
```crystal
class Api::Users::Show < ApiAction
get "/api/users/:user_id" do
user = UserQuery.find(user_id)
json UserSerializer.new(user)
end
end
```
- 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](https://luckyframework.org/guides/writing-json-apis/#respond-with-json)
for reusable JSON responses.
### Database models
```crystal
# Set up the model
class User < BaseModel
table 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
```crystal
# 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:
```crystal
class Users::Index < BrowserAction
get "/users" 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.
1. Run `shards install`
1. Run `crystal spec` from the project root.
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
### 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](https://forms.gle/w3PJ4pww8WDAuJov5).
## Contributors
[paulcsmith](https://github.com/paulcsmith) Paul Smith - Original Creator of Lucky
<a href="https://github.com/luckyframework/lucky/graphs/contributors">
<img src="https://contrib.rocks/image?repo=luckyframework/lucky" />
</a>
Made with [contrib.rocks](https://contrib.rocks).
## Thanks & attributions
- SessionHandler, CookieHandler and FlashHandler are based on [Amber](https://github.com/amberframework/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](https://github.com/samueleaton/sentry). Thanks [@samueleaton](https://github.com/samueleaton)!
================================================
FILE: UPGRADE_NOTES.md
================================================
## Upgrading from 1.3.0 to 1.4.0
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=1.3.0&to=1.4.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 1.4.0`
- Avram should be `~> 1.4.0`
- Authentic should be `~> 1.0.2`
- LuckyEnv should be `~> 0.3.0`
- LuckySecTester should be `~> 0.3.3`
- Run `shards update`
- Upgrade Lucky CLI on Windows (Scoop)
```
scoop bucket add lucky https://github.com/luckyframework/scoop-bucket
scoop install lucky
```
### General updates
- Update: your BaseSerializer and remove inheritance then add `include Lucky::Serializable`. [See PR](https://github.com/luckyframework/lucky/pull/1947)
- Remove: all setup bash scripts in favor of Crystal versions. [See PR](https://github.com/luckyframework/lucky_cli/pull/875)
- Remove: any local variable, or argument passed to a method that has a `?` for Crystal 1.16 compatibility. [See PR](https://github.com/luckyframework/avram/pull/1083)
- Update: any call to `where_*` join methods in favor of `join_*` join methods. [See PR](https://github.com/luckyframework/avram/pull/1090)
```crystal
# Update this
UserQuery.new.where_subscriptions(SubscriptionQuery.new)
# to this
UserQuery.new.join_subscriptions(SubscriptionQuery.new)
```
### Optional update
- Update: to Crystal 1.16 or later and use type-safe ENV methods. [See PR](https://github.com/luckyframework/lucky_env/pull/39)
```crystal
LuckyEnv.init_env(".env")
LuckyEnv.load(".env")
ENV["DEV_PORT"] #=> "3000"
LuckyEnv.dev_port #=> 3000
```
## Upgrading from 1.2.0 to 1.3.0
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=1.2.0&to=1.3.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 1.3.0`
- Avram should be `~> 1.3.0`
- Authentic should be `~> 1.0.1`
- Carbon should be `~> 0.6.0`
- Carbon Adapter should be `~> 0.6.0`
- Run `shards update`
- Upgrade Lucky CLI on Windows (Scoop)
```
scoop bucket add lucky https://github.com/luckyframework/scoop-bucket
scoop install lucky
```
### General updates
No required updates needed for this release.
### Optional update
- Update: to Crystal 1.14
- Update: All previously pre-compiled tasks should now show in your `./bin/` as Crystal files. Build these to run them as compiled.
```
crystal build --release bin/lucky.gen.secret_key.cr -o bin/lucky.gen.secret_key
crystal build --release bin/lucky.watch.cr -o bin/lucky.watch
crystal build --release bin/lucky.exec.cr -o bin/lucky.exec
# ... etc...
```
## Upgrading from 1.1.0 to 1.2.0
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=1.1.0&to=1.2.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 1.2.0`
- Avram should be `~> 1.2.0`
- Carbon should be `~> 0.5.1`
- Carbon Adapter should be `~> 0.5.0`
- LuckyFlow should be `~> 0.10.0`
- Run `shards update`
- Upgrade Lucky CLI on Windows (Scoop)
```
scoop bucket add lucky https://github.com/luckyframework/scoop-bucket
scoop install lucky
```
### General updates
- Add: the annotation `@[DB::Field(ignore: true)]` to any instance variables you've added to your models. [See PR](https://github.com/luckyframework/avram/pull/996)
- Remove: the Nexploit NPM package from your Github Actions if you're using SecTester. [See PR](https://github.com/luckyframework/lucky_sec_tester/pull/34)
### Optional update
- Replace: any use of `Avram::Nothing.new` with `IGNORE`. [See PR](https://github.com/luckyframework/avram/pull/1018)
## Upgrading from 1.0.0 to 1.1.0
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=1.0.0&to=1.1.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 1.1.0`
- Avram should be `~> 1.1.0`
- Authentic should be `~> 1.0.0`
- Carbon should be `~> 0.4.0`
- LuckyTask should be `~> 0.3.0`
- LuckyFlow should be `~> 0.9.2`
- LuckySecTester should be `~> 0.3.2`
- Run `shards update`
### General updates
- Add: `Avram.initialize_logging` to your `config/log.cr` file near the bottom. [See PR](https://github.com/luckyframework/avram/pull/967)
- Update: all LuckyTask tasks. [See PR](https://github.com/luckyframework/lucky_task/pull/25)
```crystal
# All help_message instance methods are macro calls
def help_message
"my help message"
end
# is now
help_message "my help message"
# Calls to `name`, `summary`, or `help_message` from your task `call` method are now classes.
def call
# `name` is now
self.class.task_name
# `summary` is now
self.class.task_summary
# `help_message` is now
self.class.task_help_message
end
```
### Optional update
- Add: `allow_blank: true` on String columns you want to allow empty strings to be saved. [See PR](https://github.com/luckyframework/avram/pull/956)
```crystal
class Post < BaseModel
table do
column title : String
# Field is required, but storing "" is ok
column sub_title : String, allow_blank: true
end
end
```
## Upgrading from 1.0.0-rc1 to 1.0.0
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=1.0.0-rc1&to=1.0.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 1.0.0`
- Avram should be `~> 1.0.0`
- Authentic should be `~> 1.0.0`
- Run `shards update`
### General updates
- Update: to at least Crystal 1.6 or later.
- Update: Any use of a `DeleteOperation` that assumed the record could be nil should now assume the record will always exist in the block. [See PR for more details](https://github.com/luckyframework/avram/pull/887)
### Optional update
## Upgrading from 0.30 to 1.0.0-rc1
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.30.0&to=1.0.0-rc1).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 1.0.0-rc1`
- Avram should be `~> 1.0.0-rc1`
- Authentic should be `~> 0.9.0`
- Carbon (and carbon_sendgrid_adapter) should be `~> 0.3.0`
- LuckyFlow should be `~> 0.9.0`
- Run `shards update`
### General updates
- Add: Avram to your `shard.yml` as a dependency.
- Add: `require "avram/lucky"` to `src/shards.cr` right below `require "lucky"`. [See PR](https://github.com/luckyframework/avram/pull/772)
- Add: `require "avram/lucky/tasks"` to `tasks.cr` right below `require "lucky/tasks/**"`. [See PR](https://github.com/luckyframework/lucky_cli/pull/764)
- Update: to Crystal 1.4 or later.
- Add: `include Lucky::RedirectableTurbolinksSupport` in your `BrowserAction` if you are using turbolinks.
- Add: `live_reload_connect_tag` to your `src/components/shared/layout_head.cr` and `reload_port: 3001` to your `config/watch.yml` file for live browser reloading. [See this PR](https://github.com/luckyframework/lucky_cli/pull/767) and [this PR](https://github.com/luckyframework/lucky/pull/1693)
- Update: `Avram::Params.new()` now takes `Hash(String, Array(String))` instead of `Hash(String, String)`. [See PR](https://github.com/luckyframework/avram/pull/847)
- Update: arg names in `validate_numeric` from `less_than` and `greater_than` to `at_least` and `no_more_than`. [See PR](https://github.com/luckyframework/avram/pull/867)
- Update: your LuckyFlow configuration...
```crystal
# spec/spec_helper.cr
# ...
require "spec"
# ...
require "lucky_flow"
require "lucky_flow/ext/lucky"
require "lucky_flow/ext/avram"
# ...
# spec/setup/configure_lucky_flow.cr
# ...
LuckyFlow::Spec.setup
```
### Optional updates
- Update the `lucky_sec_tester` shard to version `0.1.0`
- Replace turbolinks with [Turbo](https://turbo.hotwired.dev/)
- Replace laravel-mix with [Vite](https://vitejs.dev/)
## Upgrading from 0.29 to 0.30
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.29.0&to=0.30.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 0.30.0`
- Authentic should be `~> 0.8.2`
- LuckyFlow should be `~> 0.7.3` *NOTE*: 0.8.0 is released, but may not be compatible yet
- Run `shards update`
### General updates
- Update: `spec/support/api_client.cr` with `app AppServer.new` defined in the class.
```crystal
class ApiClient < Lucky::BaseHTTPClient
app AppServer.new
def initialize
super
headers("Content-Type": "application/json")
end
def self.auth(user : User)
new.headers("Authorization": UserToken.generate(user))
end
end
```
- Update: the `request.remote_ip` method now pulls from the last (instead of first) valid IP in the `X-Forwarded-For` list. [See PR for details](https://github.com/luckyframework/lucky/pull/1675)
- Update: All primary repo branches are now `main`. Adjust any references accordingly.
- Update: `./script/system_check` and remove mentions of `ensure_process_runner_installed`. Nox is built-in [See PR for details](https://github.com/luckyframework/lucky_cli/pull/720)
### Optional updates
- Update: uses of `AvramSlugify` to `Avram::Slugify`. [See PR for details](https://github.com/luckyframework/avram/pull/786)
- Update: specs to use transactions instead of truncate. [See PR for details](https://github.com/luckyframework/avram/pull/780)
```crystal
# in spec/spec_helper.cr
require "./setup/**"
# Add this line
Avram::SpecHelper.use_transactional_specs(AppDatabase)
include Carbon::Expectations
include Lucky::RequestExpectations
include LuckyFlow::Expectations
```
- Remove: the `spec/setup/clean_database.cr` file. This accompanies the transactional specs update
- Update: the `spec/setup/start_app_server.cr` file. This file is no longer needed if your action specs make standard calls, and are not using LuckyFlow. [See PR for details](https://github.com/luckyframework/lucky/pull/1644)
## Upgrading from 0.28 to 0.29
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.28.2&to=0.29.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `">= 1.0.0"`
- Lucky should be `~> 0.29.0`
- Authentic should be `~> 0.8.1`
- Caron SendgidAdapter should be `~> 0.2.0` if you're using SendGrid
- LuckyEnv should be `~> 0.1.4`
- LuckyTask should be `~> 0.1.1`
- JWT should be `~> 1.6.0`
- Run `shards update`
### General updates
- Remove: any usage of the `lucky build.release` task. Use `shards build --release --production` instead. [See PR for details](https://github.com/luckyframework/lucky/pull/1612)
- Update: to Crystal version 1.0.0 or greater. Versions below 1.0 are no longer supported. [See PR for details](https://github.com/luckyframework/lucky/pull/1618)
- Update: your `AppServer` in `src/app_server.cr` to have a `listen` method defined. This method is now abstract on `Lucky::BaseAppServer`. [See PR for details](https://github.com/luckyframework/lucky/pull/1622)
- Update: if you use UUID for primary keys in your models, ensure you've added the "pgcrypto" extension to your DB. The `id` value will no longer be generated on the Crystal side. [See PR for details](https://github.com/luckyframework/avram/pull/725)
- Update: any usage of the `Status` enums in your SaveOperations to be `OperationStatus`. [See PR for details](https://github.com/luckyframework/avram/pull/759)
- Remove: any usage of `route` or `nested_route` from your actions, and replace them with the actual route. (Optionally, you can use the [Legacy Routing Shard](https://github.com/matthewmcgarvey/lucky_legacy_routing)) [See PR for details](https://github.com/luckyframework/lucky/pull/1597)
- Update: your `src/app.cr`, and move the requires for `config/server` and `config/**` to the top of the require stack. [See PR for details](https://github.com/luckyframework/lucky_cli/pull/676)
- Update: your `package.json` (Full Apps only) to use `yarn run mix` instead of just `mix`. [See PR for details](https://github.com/luckyframework/lucky_cli/pull/682)
- Update: your `src/app_server.cr` middleware stack with `Lucky::RequestIdHandler.new` at the top of the stack before `Lucky::ForceSSLHandler.new`. [See PR for details](https://github.com/luckyframework/lucky_cli/pull/700)
- Update: any usage of `add_belongs_to` with namespaced models to specify the `references` option. [See PR for details](https://github.com/luckyframework/avram/pull/742)
- Update: the `error_html` method in `src/actions/errors/show.cr`. Replace the following code
```diff
- html Errors::ShowPage, message: message, status: status
+ html_with_status Errors::ShowPage, status, message: message, status_code: status
```
- Rename: the `status` variable to `status_code` in `src/pages/errors/show_page.cr`
### Optional updates
- Add: a new config `Lucky::RequestIdHandler` in `config/server.cr` to set a request ID.
```crystal
#...
Lucky::RequestIdHandler.configure do |settings|
settings.set_request_id = ->(context : HTTP::Server::Context) {
UUID.random.to_s
}
end
```
- Add: query cache to `config/database.cr`. [See PR for details](https://github.com/luckyframework/avram/pull/763)
```crystal
Avram.configure do |settings|
settings.database_to_migrate = AppDatabase
# In production, allow lazy loading (N+1).
# In development and test, raise an error if you forget to preload associations
settings.lazy_load_enabled = LuckyEnv.production?
# Disable query cache during tests
settings.query_cache_enabled = !LuckyEnv.test?
end
```
## Upgrading from 0.27 to 0.28
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.27.2&to=0.28.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `">= 1.0.0"`
- Lucky should be `~> 0.28.0`
- Authentic should be `~> 0.8.0`
- Carbon should be `~> 0.2.0`
- Caron SendgidAdapter should be `~> 0.1.0` if you're using SendGrid
- Dotenv should be replaced with [LuckyEnv ~> 0.1.3](https://github.com/luckyframework/lucky_env)
- Run `shards update`
### General updates
- Remove: `needs context : HTTP::Server::Context` from any component, as well as passing it in to the `mount()` for the components. [See PR for details](https://github.com/luckyframework/lucky/pull/1488)
- Rename: all `DeleteOperation.destroy` calls to `DeleteOperation.delete`
- Update: `avram_enum` to use the Crystal `enum`. [See PR for details](https://github.com/luckyframework/avram/pull/698)
```diff
# Models get this update
- avram_enum State do
+ enum State
Started
Ended
end
# Factories get this update
- state Thing::State.new(:started)
+ state Thing::State::Started
# Operations get this update
- SaveThing.create(state: Thing::State.new(:started)) do |op, t|
+ SaveThing.create(state: Thing::State::Started) do |op, t|
# Queries get this update
- ThingQuery.new.state(Thing::State.new(:started).value)
+ ThingQuery.new.state(Thing::State::Started)
```
- Update: your `config/env.cr` to this.
```crystal
# Environments are managed using `LuckyEnv`. By default, development, production
# and test are supported.
# If you need additional environment support, add it here
# LuckyEnv.add_env :staging
```
- Update: any use of `Lucky::Env` to use `LuckyEnv`. (e.g. `Lucky::Env.test?` -> `LuckyEnv.test?`). [See PR for details](https://github.com/luckyframework/lucky_cli/pull/655)
- Update: any use of `Lucky::Env.name` to use `LuckyEnv.environment`.
- Update: any use of `route` or `nested_route`, and replace them with the generated routes. Use `lucky routes` to view all generated routes. If you still need this, you can use the [Lucky Legacy Routing](https://github.com/matthewmcgarvey/lucky_legacy_routing) shard.
- Add: the [luckyframework/carbon_sendgrid_adapter](https://github.com/luckyframework/carbon_sendgrid_adapter) shard if you're using Sendgrid to send mail. Be sure to `require "carbon_sendgrid_adapter"` in `config/email.cr`.
### Optional updates
- Update: all routes to use underscore (`_`) instead of dash (`-`) as word separator. Include the `Lucky::EnforceUnderscoredRoute` module in your base actions. (e.g. `/this-route` -> `/this_route`)
```crystal
class BrowserAction < Lucky::Action
include Lucky::EnforceUnderscoredRoute
# ...
end
```
- Update: `send_text_response()` responses if you're passing a raw JSON string to use `raw_json()` instead.
- Add: `include Lucky::SecureHeaders::DisableFLoC` to your `BrowserAction` to disable FLoC.
```crystal
class BrowserAction < Lucky::Action
include Lucky::SecureHeaders::DisableFLoC
# ...
end
```
- Remove: `normalize-scss` from your `package.json` and replace with `modern-normalize` if you're using `normalize-scss`.
- Update: any query where you write code like `if SomeQuery.new.first?` to `if SomeQuery.new.any?`. `.any?` returns a Bool instead of loading the whole object which has a small performance gain.
- Add: the [Breeze](https://github.com/luckyframework/breeze) shard to your development workflow!
## Upgrading from 0.26 to 0.27
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.26.0&to=0.27.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `">= 0.36.1, < 2.0.0"`
- Lucky should be `~> 0.27.0`
- Authentic should be `~> 0.7.3`
- Carbon should be `~> 0.1.4`
- Dotenv should be `~> 1.0.0` or replace with [LuckyEnv 0.1.0](https://github.com/luckyframework/lucky_env)
- LuckyFlow should be `~> 0.7.3`
- JWT (if you use Auth) should be `~> 1.5.1`
- LuckyTask needs to be added as a dependency
```
lucky_task:
github: luckyframework/lucky_task
version: ~> 0.1.0
```
- Run `shards update`
### General updates
- Add: the new `lucky_task` shard as a dependency.
- Update: your `tasks.cr` file with the new require, and module name change:
```crystal
# tasks.cr
ENV["LUCKY_TASK"] = "true"
# Load Lucky and the app (actions, models, etc.)
require "./src/app"
require "lucky_task"
require "./tasks/**"
require "./db/migrations/**"
require "lucky/tasks/**"
LuckyTask::Runner.run
```
- Update: all tasks in your `tasks/` directory to inherit from `LuckyTask::Task` instead of `LuckyCli::Task`. (e.g. `Db::Seed::RequiredData < LuckyCli::Task` -> `Db::Seed::RequiredData < LuckyTask::Task`)
- Update: your `config/cookies.cr` with a default cookie path of `"/"`.
```crystal
Lucky::CookieJar.configure do |settings|
settings.on_set = ->(cookie : HTTP::Cookie) {
# ... other defaults
# Add this line. See ref: https://github.com/crystal-lang/crystal/pull/10491
cookie.path("/")
}
end
```
### Optional updates
- Update: to Crystal 1.0.0. You can continue to use Crystal 0.36.1 if you need.
- Update: `LuckyFlow` to be a `development_dependency`.
## Upgrading from 0.25 to 0.26
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.25.0&to=0.26.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.36.1`
- Lucky should be `~> 0.26.0`
- Authentic should be `~> 0.7.2`
- LuckyFlow should be `~> 0.7.2`
- Run `shards update`
### General updates
- Update: your `Procfile` web to point to `./bin/YOUR APP NAME` instead of `./app`. NOTE: this is dependant on how you deploy your app, so only required if you use the heroku_buildpack for Lucky. [read more](https://github.com/luckyframework/lucky_cli/pull/601) and [more](https://github.com/luckyframework/heroku-buildpack-crystal/pull/11)
- Update: any references directly to an `Avram::Attribute(T)` generic. e.g. `Avram::Attribute(String?)` -> `Avram::Attribute(String)`. [read more](https://github.com/luckyframework/avram/pull/586)
- Update: any custom database types to include the class method `adapter` that returns the `Lucky` constant. [read more](https://github.com/luckyframework/avram/pull/587)
- Update: any custom database types to include the class method `criteria(query : T, column) forall T`. [read more](https://github.com/luckyframework/avram/pull/591)
- Remove: any call to `after_completed` in a SaveOperation. The `after_save` and `after_commit` now run even if no change is updated. [read more](https://github.com/luckyframework/avram/pull/612)
- Rename: all `Avram::Box` classes, filenames, and the `spec/support/boxes` directory (sorry 😬) to `Avram::Factory`, etc.... e.g. `UserBox` -> `UserFactory` [read more](https://github.com/luckyframework/avram/pull/614). [view discussion](https://github.com/luckyframework/lucky/discussions/1282)
- Notice: the `Avram::Operation` now avoids calling `run` if there were validation errors in any `before_run`. This may change some of your logic, or create surprised. [read more](https://github.com/luckyframework/avram/pull/621)
### Optional updates
- Update: any calls made in Github CI config to `lucky db.create_required_seeds` to `lucky db.seed.required_data`. [read more](https://github.com/luckyframework/lucky_cli/pull/600)
- Update: any use of `route` or `nested_route` in your actions to explicitly specify the route. This isn't deprecated, yet, but will be in a future version and eventually removed.
- Add: `DB::Log.level = :info` to your `config/log.cr` file to quiet the excessive "Executing query" notices
- Update: your Laravel Mix to version 6. [read more](https://github.com/luckyframework/lucky_cli/pull/592)
- Add: a new migration to have UUID primary keys generated from the database for existing tables. [read more](https://github.com/luckyframework/avram/pull/578)
```crystal
# in a new migration file
def migrate
enable_extension "pgcrypto"
execute("ALTER TABLE products ALTER COLUMN id SET DEFAULT gen_random_uuid();")
execute("ALTER TABLE users ALTER COLUMN id SET DEFAULT gen_random_uuid();")
end
```
- Remove: all calls to `flash.keep` in your actions. [read more](https://github.com/luckyframework/lucky/pull/1374)
## Upgrading from 0.24 to 0.25
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.24.0&to=0.25.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.35.1`
- Lucky should be `~> 0.25.0`
- Authentic should be `~> 0.7.1`
- LuckyFlow should be `~> 0.7.1`
- Run `shards update`
### General updates
- Update: all `Avram::Operation` to implement the new interface.
- Your main instance method should be called `run`
- The `run` method should return just the value you need. No more `yield self, thing` / `yield self, nil`.
- Call the operation with `MyOperation.run(params)` instead of `MyOperation.new(params).submit`
- The `MyOperation.run` class method takes a block that yields the operation, and your return value. Similar to `SaveOperation`.
```crystal
# Before Update
class RequestPasswordReset < Avram::Operation
#...
def submit
if valid?
yield self, user
else
yield self, nil
end
end
end
# Use in your Action
RequestPasswordReset.new(params).submit do |operation, user|
end
# After Update
class RequestPasswordReset < Avram::Operation
#...
def run
if valid?
user
else
nil
end
end
end
# Use in your Action
RequestPasswordReset.run(params) do |operation, user|
end
```
- Rename: all usage of `with_defaults` to `tag_defaults`
- Update: query objects to no longer rely on mutating the query.
```crystal
# Before update
q = UserQuery.new
q.age.gte(21)
q.to_sql #=> SELECT * FROM users WHERE age >= 21
# After update
q = UserQuery.new
q.age.gte(21)
q.to_sql #=> SELECT * FROM users
```
- Rename: all usage of `raw_where` to `where`
- Update: query objects that set a default query in the initializer to use the `defaults` method.
```crystal
# Before update
class UserQuery < User::BaseQuery
def initialize
admin(false)
end
end
UserQuery.new.to_sql #=> SELECT * FROM users WHERE admin = false
# After update
class UserQuery < User::BaseQuery
def initialize
defaults &.admin(false)
end
end
UserQuery.new.to_sql #=> SELECT * FROM users WHERE admin = false
```
- Update: any `has_many through` model association to include the new assocation chain.
```crystal
# Before update
has_many posts : Post
has_many comments : Comment, through: :posts
# After update
# The first in the array is the association you're going through
# The second is that through's association.
has_many posts : Post
has_many comments : Comment, through: [:posts, :comments]
```
- Update: any query that used a `where_XXX` on a `belongs_to` from the pluralized name to singularized.
```crystal
# assuming Post belongs_to User
# Before update
PostQuery.new.where_users(UserQuery.new)
# After update
PostQuery.new.where_user(UserQuery.new) # Notice the 'where_user' is single now
```
### Optional updates
- Update: any mention of `DB_URL` that we told you to use should actually be `DATABASE_URL`
- Remove: any include for `include Lucky::Memoizable`. This is now included in `Object` and available everywhere
- Update: HTML tags that display a `UUID` no longer need to cast to String. `link uuid, to: Whatever`
- Remove: any `start_server` or `start_server.dwarf` files in the top-level directory. These are now built to your `bin/`
- Update: `config/email.cr` to include a case for development to print emails.
```crystal
# config/email.cr
BaseEmail.configure do |settings|
if Lucky::Env.production?
# ...
elsif Lucky::Env.development?
settings.adapter = Carbon::DevAdapter.new(print_emails: true)
else
# ...
end
end
```
- Update: any `call(io : IO)` method in your tasks, and use the `output` property instead for testing. [read more](https://github.com/luckyframework/lucky_cli/pull/557)
- Update: your `package.json` with all the latest front-end updates. [read more](https://github.com/luckyframework/lucky_cli/pull/553)
- Rename: your seed tasks `tasks/create_required_seeds.cr` -> `tasks/db/seed/required_data.cr`, and `tasks/create_sample_seeds.cr` -> `tasks/db/seed/sample_data.cr`
- Update: `config/log.cr` to silence some of the query logging with `DB::Log.level = :info`.
## Upgrading from 0.23 to 0.24
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.23.0&to=0.24.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.35.1`
- Lucky should be `~> 0.24.0`
- Authentic should be `~> 0.7.0`
- Run `shards update`
### General updates
- Rename: all instances of the `m` method to `mount`. e.g. `m Shared::Footer, year: 2020` -> `mount Shared::Footer, year: 2020`.
- Update: `config/database.cr` with new `Avram::Credentials`.
```crystal
AppDatabase.configure do |settings|
if Lucky::Env.production?
settings.credentials = Avram::Credentials.parse(ENV["DATABASE_URL"])
else
settings.credentials = Avram::Credentials.parse?(ENV["DATABASE_URL"]?) || Avram::Credentials.new(
database: database_name,
hostname: ENV["DB_HOST"]? || "localhost",
# NOTE: This was changed from `String` to `Int32`
port: ENV["DB_PORT"]?.try(&.to_i) || 5432,
username: ENV["DB_USERNAME"]? || "postgres",
password: ENV["DB_PASSWORD"]? || "postgres"
)
end
end
```
- Rename: all instances of `AppClient` to `ApiClient` in your `spec/` directory.
- Update: `script/setup` with `shards install --ignore-crystal-version`. Alternatively, you can set a global `SHARDS_OPTS=--ignore-crystal-version` environment variable
### Optional updates
- Update: `redirect_back` with `allow_external: true` argument if you need to allow external referers
- Update: your database credentials with the new `query` option to pass query string options
```crystal
# config/database.cr
settings.credentials = Avram::Credentials.new(
database: database_name,
hostname: ENV["DB_HOST"]? || "localhost",
port: ENV["DB_PORT"]?.try(&.to_i) || 5432,
username: ENV["DB_USERNAME"]? || "postgres",
password: ENV["DB_PASSWORD"]? || "postgres",
# This option is new
query: "initial_pool_size=5&max_pool_size=20"
)
```
- Add: `disable_cookies` to `ApiAction` in `src/actions/api_action.cr`.
## Upgrading from 0.22 to 0.23
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.22.0&to=0.23.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.35.0`
- Lucky should be `~> 0.23.0`
- Authentic should be `~> 0.6.1`
- LuckyFlow should be `~> 0.7.0`
- jwt should be `~> 1.4.2`
- Run `shards update`
### General updates
- Update: `params.get` now strips white space. If you need the raw value, use `params.get_raw`.
- Rename: `mount` to `m` in all pages that use components. **Note: This was reverted in the next version**
- Update: all mounted components to use new signature `mount(MyComponent.new(x: 1, y: 2))` -> `m(MyComponent, x: 1, y:2)`.
- Remove: `Lucky::SessionHandler` and `Lucky::FlashHandler` from `src/app_server.cr`
### Optional updates
- Add: `Avram::RecordNotFoundError` to the `dont_report` array in `src/actions/errors/show.cr`
- Update: `def render(error : Lucky::RouteNotFoundError` to `def render(error : Lucky::RouteNotFoundError | Avram::RecordNotFoundError)` in `src/actions/errors/show.cr`.
- Update: any CLI tasks that use `ARGV` to use the native args [See implementation](https://github.com/luckyframework/lucky_cli/pull/466)
## Upgrading from 0.21 to 0.22
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.21.0&to=0.22.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.35.0`
- Lucky should be `~> 0.22.0`
- Authentic should be `~> 0.6.0`
- jwt should be `~> 1.4.2`
- Run `shards update`
## Upgrading from 0.20 to 0.21
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.20.0&to=0.21.0).
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.34.0`
- Lucky should be `~> 0.21.0`
- Authentic should be `~> 0.5.4`
- LuckyFlow should be `~> 0.6.3`
- Run `shards update`
### General updates
- Rename: `config/logger.cr` to `config/log.cr`
- Update: `config/log.cr` to use the new `Log`. [See implementation](https://github.com/luckyframework/lucky_cli/blob/v0.21.0/src/web_app_skeleton/config/log.cr#L1)
- Update: `Procfile.dev` and update the `system_check` to `script/system_check && sleep 100000`.
- Update: all `Lucky.logger.{level}("message")` calls to use the new Crystal Log `Log.{level} { "message" }`
- Remove: the following lines from `config/database.cr`
```crystal
# Uncomment the next line to log all SQL queries
# settings.query_log_level = ::Logger::Severity::DEBUG
```
### Updating `Lucky.logger`
Before this version, you would log data like this:
```crystal
Lucky.logger.debug("Logging some message")
Lucky.logger.info({path: @context.request.path})
```
Now, you would write this like:
```crystal
# Use the Crystal std-lib log for simple String messages
Log.debug { "Logging some message" }
# Use the Dexter extension for logging key/value data
Log.dexter.info { {path: @context.request.path} }
```
## Upgrading from 0.19 to 0.20
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.19.0&to=0.20.0).
- Update `.crystal-version` file to `0.34.0`
- Upgrade to crystal 0.34.0
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.34.0
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.34.0`
- Lucky should be `~> 0.20.0`
- Authentic should be `~> 0.5.2`
- LuckyFlow should be `~> 0.6.2`
- Run `shards update`
### General updates
- Update: `link` no longer accepts a `String` path or URL, it must be an Action. Change `link()` to an `a` tag with an `href` (`a "Google", href: "https://google.com"`), or use an action class with `link` (`link "Home", to: "/" ` becomes `link("Home", to: Home::Index)`.
- Remove: the `?` from any `needs` using a predicate method. e.g. `needs signed_in? : Bool` -> `needs signed_in : Bool`. Lucky now automatically creates a method ending with `?` for `needs` with a `Bool` type.
- Update: your development `ENV["PORT"]` to be `ENV["DEV_PORT"]` if you need to customize the port your local server is running on.
- Update: all `SaveOperation` classes where a raw hash is being passed in. e.g. `MyOperation.new({"name" => "Gary"})` -> `MyOperation.new(name: "Gary")`, or if you must use a hash, wrap it in params first: `MyOperation.new(Avram::Params.new({"name" => "Gary"})`
- Remove: the `on:` option from `needs` inside every Operation class. e.g. `needs created_by : String, on: :create` -> `needs created_by : String`. You will need to explicitly pass these when calling `new`, `create`, and `update`.
### Optional updates
- Update: all instance variables called from a `needs` on a page or component can now just use the method of that name. e.g. `@current_user` -> `current_user`
- Add: `include Lucky::CatchUnpermittedAttribute` to the `class Shared::Field(T)` in `src/components/shared/field.cr`. This will raise a nicer error if you forget to permit a column in your SaveOperation
- Add: the new `Lucky::RemoteIpHandler.new` to your app handlers in `src/app_server.cr` just before `Lucky::RouteHandler.new`.
- Add: `robots.txt` to your `public/` directory.
```
User-agent: *
Disallow:
```
- Update: `UserSerializer` to inherit from the `BaseSerializer` if it doesn't already.
- Add: `cookie.http_only(true)` to your `config/cookies.cr` file. This goes inside your `settings.on_set` block.
- Update: your node dependencies where needed
- Update: the `setup` script in `script/setup`. [See implementation](https://github.com/luckyframework/lucky_cli/tree/ee7699bddde50b80e495a89edb442b754f627239/src/web_app_skeleton/script/setup.ecr). Be sure to remove the ECR tags.
- Add: this line `system_check: script/system_check && $SHELL` to your `Procfile.dev`
- Add: the new `system_check` script in `script/system_check`. Note: you may need to `chmod +x script/system_check`. [See implementation](https://github.com/luckyframework/lucky_cli/tree/ee7699bddde50b80e495a89edb442b754f627239/src/web_app_skeleton/script/system_check.ecr). Be sure to remove the ECR tags.
- Add: the new `function_helpers` script in `script/helpers/function_helpers`. [See implementation](https://github.com/luckyframework/lucky_cli/tree/ee7699bddde50b80e495a89edb442b754f627239/src/web_app_skeleton/script/helpers/function_helpers)
- Add: the new `text_helpers` script in `script/helpers/text_helpers`. [See implementation](https://github.com/luckyframework/lucky_cli/tree/ee7699bddde50b80e495a89edb442b754f627239/src/web_app_skeleton/script/helpers/text_helpers)
## Upgrading from 0.18 to 0.19
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.18.0&to=0.19.0).
- Update `.crystal-version` file to `0.33.0`
- Upgrade to crystal 0.33.0
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.33.0
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Crystal should be `0.33.0`
- Lucky should be `~> 0.19.0`
- Authentic should be `~> 0.5.1`
- LuckyFlow should be `~> 0.6.2`
- Run `shards update`
### Recommended (but optional) changes
#### GZip static assets
* [Add the compression plugin](https://github.com/luckyframework/lucky_cli/commit/8bc002ab51cb13e67f515c4de977766f96825a18#diff-73db280623fcd1a64ac1ab76c8700dbc) to `package.json`
* Make [these changes](https://github.com/luckyframework/lucky_cli/commit/8bc002ab51cb13e67f515c4de977766f96825a18#diff-cd19e42e70bfbcf2a12480b0b6b1f590)
to your `webpack.mix.js` file
* In `src/app_server.cr` add `Lucky::StaticCompressionHandler.new("./public", file_ext: "gz", content_encoding: "gzip")` above the `Lucky::StaticFileHandler.new`.
#### GZip text responses
* Make [these changes](https://github.com/luckyframework/lucky_cli/commit/8bc002ab51cb13e67f515c4de977766f96825a18#diff-83ca1a783e82ef6f0d38f400b7c1eaa1) to `config/server.cr` to gzip text responses.
## Upgrading from 0.17 to 0.18
For a full diff of necessary changes, please see [LuckyDiff](https://luckydiff.com?from=0.17.0&to=0.18.0).
- Upgrade to crystal 0.31.1
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.31.1
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update `.crystal-version` to `0.31.1`
- Update versions in `shard.yml`
- Crystal should be `0.31.1`
- Lucky should be `~> 0.18`
- Authentic should be `~> 0.5.0`
- LuckyFlow should be `~> 0.6.0`
- Run `shards update`
### General updates
- Rename: all `render` calls in actions to `html`.
- Update: the `src/actions/errors/show.cr` file to the new format:
```crystal
class Errors::Show < Lucky::ErrorAction
DEFAULT_MESSAGE = "Something went wrong."
default_format :html
dont_report [Lucky::RouteNotFoundError]
def render(error : Lucky::RouteNotFoundError)
if html?
error_html "Sorry, we couldn't find that page.", status: 404
else
error_json "Not found", status: 404
end
end
# When the request is JSON and an InvalidOperationError is raised, show a
# helpful error with the param that is invalid, and what was wrong with it.
def render(error : Avram::InvalidOperationError)
if html?
error_html DEFAULT_MESSAGE, status: 500
else
error_json \
message: error.renderable_message,
details: error.renderable_details,
param: error.invalid_attribute_name,
status: 400
end
end
# Always keep this below other 'render' methods or it may override your
# custom 'render' methods.
def render(error : Lucky::RenderableError)
if html?
error_html DEFAULT_MESSAGE, status: error.renderable_status
else
error_json error.renderable_message, status: error.renderable_status
end
end
# If none of the 'render' methods return a response for the raised Exception,
# Lucky will use this method.
def default_render(error : Exception) : Lucky::Response
if html?
error_html DEFAULT_MESSAGE, status: 500
else
error_json DEFAULT_MESSAGE, status: 500
end
end
private def error_html(message : String, status : Int)
context.response.status_code = status
html Errors::ShowPage, message: message, status: status
end
private def error_json(message : String, status : Int, details = nil, param = nil)
json ErrorSerializer.new(message: message, details: details, param: param), status: status
end
private def report(error : Exception) : Nil
# Send to Rollbar, send an email, etc.
end
end
```
- Rename: `title` to `message` in `src/pages/errors/show_page.cr`.
- Add: `BaseSerializer` to `src/serializers/`.
```crystal
abstract class BaseSerializer < Lucky::Serializer
def self.for_collection(collection : Enumerable, *args, **named_args)
collection.map do |object|
new(object, *args, **named_args)
end
end
end
```
- Add: `require "./serializers/base_serializer"` to your `src/app.cr` above `require "./serializers/**"`
- Optional: Update all serializers to inherit from `BaseSerializer`. Also merge Show/Index serializers in to a single class.
```crystal
# Merge these two classes
class Users::IndexSerializer < Lucky::Serializer
end
class Users::ShowSerializers < Lucky::Serializer
end
# in to this class
class UserSerializer < BaseSerializer
# Same contents as Users::ShowSerializer
# Calls to Users::IndexSerializer now become:
#
# UserSerializer.for_collection(users)
end
```
- Rename: `Errors::ShowSerializer` to `ErrorSerializer`
- Update: `ErrorSerializer` to inherit from the new `BaseSerializer`
- Update: `ErrorSerializer` contents with:
```crystal
class ErrorSerializer < BaseSerializer
def initialize(
@message : String,
@details : String? = nil,
@param : String? = nil # If there was a problem with a specific param
)
end
def render
{message: @message, param: @param, details: @details}
end
end
```
- Add: `Avram::SchemaEnforcer.ensure_correct_column_mappings!` to `src/start_server.cr` below `Avram::Migrator::Runner.new.ensure_migrated!`.
- Update: any mention to renamed errors in [this commit](https://github.com/luckyframework/lucky/pull/911/files#diff-02d01a64649367eb50f82f303c2d07e2R248). You can likely ignore this as most people do not rescue these specific errors.
- Add: `accepted_formats [:json]` to `ApiAction` in `src/actions/api_action.cr`.
```crystal
abstract class ApiAction < Lucky::Action
accepted_formats [:json]
end
```
- Add: `accepted_formats [:html, :json], default: :html` to `BrowserAction` in `src/actions/browser_action.cr`
```crystal
abstract class BrowserAction < Lucky::Action
accepted_formats [:html, :json], default: :html
end
```
- Update: `src/app_server.cr` with explicit return type on the `middleware` method.
```crystal
# Add return type here
def middleware : Array(HTTP::Handler)
[
# ...
] of HTTP::Handler # Add this or app will fail to compile
end
```
- Add: `include Lucky::RequestExpectations` to `spec/spec_helper.cr` below `include Carbon::Expectations`
- Add: `Avram::SchemaEnforcer.ensure_correct_column_mappings!` to `spec/spec_helper.cr` below `Avram::Migrator::Runner.new.ensure_migrated!`
- Update: Change `at_exit do` in `spec/setup/start_app_server.cr` to `Spec.after_suite do`
## Upgrading from 0.16 to 0.17
- Ensure you've upgraded to crystal 0.30.1
- Upgrade Lucky CLI (homebrew)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.30.1
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
- Update `.crystal-version` to `0.30.1`
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 0.17`
- Run `shards update`
### Example upgrade
If you're not sure about an upgrade step, or simply want to look at an example, see the [lucky_bits upgrade](https://github.com/edwardloveall/lucky_bits/commit/47473a19084f1781062ce3767e3fbcf527c11e4d).
### General updates
- Rename: Action rendering method `text` to `plain_text`.
- Update: use of `number_to_currency` now returns a String instead of writing to the view directly.
- Delete: `config/static_file_handler.cr`.
- Add: a new `Lucky::LogHandler` configure to the bottom of `config/logger.cr`.
- Update: `Avram::Repo.configure` to `Avram.configure` in `config/logger.cr`.
<details>
<summary>config/logger.cr</summary>
```crystal
require "file_utils"
logger =
if Lucky::Env.test?
# Logs to `tmp/test.log` so you can see what's happening without having
# a bunch of log output in your specs results.
FileUtils.mkdir_p("tmp")
Dexter::Logger.new(
io: File.new("tmp/test.log", mode: "w"),
level: Logger::Severity::DEBUG,
log_formatter: Lucky::PrettyLogFormatter
)
elsif Lucky::Env.production?
# This sets the log formatter to JSON so you can parse the logs with
# services like Logentries or Logstash.
#
# If you want logs like in development use `Lucky::PrettyLogFormatter`.
Dexter::Logger.new(
io: STDOUT,
level: Logger::Severity::INFO,
log_formatter: Dexter::Formatters::JsonLogFormatter
)
else
# For development, log everything to STDOUT with the pretty formatter.
Dexter::Logger.new(
io: STDOUT,
level: Logger::Severity::DEBUG,
log_formatter: Lucky::PrettyLogFormatter
)
end
Lucky.configure do |settings|
settings.logger = logger
end
Lucky::LogHandler.configure do |settings|
# Skip logging static assets in development
if Lucky::Env.development?
settings.skip_if = ->(context : HTTP::Server::Context) {
context.request.method.downcase == "get" &&
context.request.resource.starts_with?(/\/css\/|\/js\/|\/assets\//)
}
end
end
Avram.configure do |settings|
settings.logger = logger
end
```
</details>
- Update: `script/setup` to include the new postgres checks.
```diff
# This must go *after* the 'shards install' step
+ printf "\n▸ Checking that postgres is installed\n"
+ check_postgres | indent
+ printf "✔ Done\n" | indent
+ printf "\n▸ Verifying postgres connection\n"
+ lucky db.verify_connection | indent
printf "\n▸ Setting up the database\n"
lucky db.create | indent
```
### Database updates
- Add: a new `AppDatabase` class in `src/app_database.cr` that inherits from `Avram::Database`.
```crystal
class AppDatabase < Avram::Database
end
```
- Add: `require "./app_database"` to `src/app.cr` right below the `require "./shards"`.
- Rename: `Avram::Repo.configure` to `AppDatabase.configure` in `config/database.cr`.
- Add: `Avram.configure` block.
<details>
<summary>config/database.cr</summary>
```crystal
database_name = "..."
AppDatabase.configure do |settings|
if Lucky::Env.production?
settings.url = ENV.fetch("DATABASE_URL")
else
settings.url = ENV["DATABASE_URL"]? || Avram::PostgresURL.build(
database: database_name,
hostname: ENV["DB_HOST"]? || "localhost",
# Some common usernames are "postgres", "root", or your system username (run 'whoami')
username: ENV["DB_USERNAME"]? || "postgres",
# Some Postgres installations require no password. Use "" if that is the case.
password: ENV["DB_PASSWORD"]? || "postgres"
)
end
end
Avram.configure do |settings|
settings.database_to_migrate = AppDatabase
# this is moved from your old `Avram::Repo.configure` block.
settings.lazy_load_enabled = Lucky::Env.production?
end
```
</details>
- Move: the `settings.lazy_load_enabled` from `AppDatabase.configure` to `Avram.configure` block.
- Add: a `database` class method to `src/models/base_model.cr` that returns `AppDatabase`.
```crystal
abstract class BaseModel < Avram::Model
def self.database : Avram::Database.class
AppDatabase
end
end
```
- Update: `Avram::Repo` to `AppDatabase` in `spec/setup/clean_database.cr`.
- Avram no longer automatically adds a timestamp and primary key to migrations.
Add a primary key and timestamps to your old migrations.
> Also note that the syntax for a UUID has changed. You use
> `primary_key id : UUID` instead of an option on 'create'
```crystal
def migrate
create :users do
# Add these to your 'create' statements in your migrations
primary_key id : Int64 # Or 'UUID' if using UUID
add_timestamps
end
end
```
- Note: Avram now defaults primary keys to `Int64` instead of `Int32`. You
can use the `change_type` macro to migrate your **primary keys and foreign keys**
to `Int64` if you need. Run `lucky gen.migration UpdatePrimaryKeyTypes`.
```crystal
class UpdatePrimaryKeyTypesV20190723233131 < Avram::Migrator::Migration::V1
def migrate
alter table_for(User) do
change_type id : Int64
end
alter table_for(Post) do
change_type id : Int64
change_type user_id : Int64
end
end
end
```
- Update: models now default the primary key to `Int64`. This can be
overridden if your tables uses a different column type for your primary keys,
such as Int32 or UUID
```crystal
abstract class BaseModel < Avram::Model
macro default_columns
primary_key id : UUID
timestamps
end
end
```
This also means that any model that uses `UUID` for a primary key can remove the `primary_key_type` option
```crystal
class User < BaseModel
# 0.16 and earlier
table :users, primary_key_type: :uuid do
column email : String
end
# Now with 0.17 it will use the 'default_columns' from the 'BaseModel'
table :users do
column email : String
end
end
```
### Updating queries
- Rename: `Query.new.destroy_all` to `Query.truncate`. (e.g. `UserQuery.new.destroy_all` => `UserQuery.truncate`)
- Rename: all association query methods from the association name to `where_{association_name}`. (e.g. `UserQuery.new.posts` => `UserQuery.new.where_posts`)
- Update: all association query methods no longer take a block. Pass the query in as an argument. (e.g. `UserQuery.new.posts { |post_query| }` => `UserQuery.new.where_posts(PostQuery.new)`)
- Update: `where_{association_name}` methods no longer need to be preceded by a `join_{assoc}`, unless you need a custom join (i.e. `left_join_{assoc}`). If you use a custom join, you will need to add the `auto_inner_join: false` option to your `where_{assoc}` method.
### Moving forms to operations
- Rename: the `src/forms` directory to `src/operations`.
- Update: `require "./forms/mixins/**"` and `require "./forms/**"` to `require "./operations/mixins/**"` and `require "./operations/**"` respectively in `src/app.cr`
- Rename: `BaseForm` to `SaveOperation` in `src/operations`. (e.g. `User::BaseForm` => `User::SaveOperation`)
- Rename: `fillable` to `permit_columns`
- Rename: form class names to new naming convention. (e.g. `class UserForm < User::SaveOperation` => `class SaveUser < User::SaveOperation`). This step is optional, but still recommended to avoid future confusion.
- Rename: `Avram::VirtualForm` to `Avram::Operation`.
- Rename: virtual form class names to new naming convention VerbNoun. (e.g. `class SignInForm < Avram::Operation` => `class SignInUser < Avram::Operation`).
- Rename: `virtual` to `attribute`.
- Update: all `SaveOperation` classes to call `before_save prepare`. The `prepare` method is no longer called by default, which allows you to rename this method as well.
- Update: `FillableField` to `PermittedAttribute` in `src/components/shared/`. Check `field.cr` and `field_errors.cr`.
- Update: all authentic classes and modules to use new operation setup. This may require renaming some files to fit the `VerbNoun` `verb_noun.cr` convention.
<details>
<summary>Files in src/operations/</summary>
```diff
# src/operations/mixins/password_validations.cr
module PasswordValidations
+ macro included
+ before_save run_password_validations
+ end
#...
end
# src/operations/request_password_reset.cr
- class RequestPasswordReset < Avram::VirtualForm
+ class RequestPasswordReset < Avram::Operation
#...
end
# src/operations/reset_password.cr
- def prepare
- run_password_validations
+ before do
Authentic.copy_and_encrypt password, to: encrypted_password
# src/operations/sign_in_user.cr
- class SignInUser < Avram::VirtualOperation
+ class SignInUser < Avram::Operation
# src/operations/sign_up_user.cr
- def prepare
+ before_save do
validate_uniqueness_of email
- run_password_validations
```
</details>
- Update `sign_in_user.cr` to match [the new template](https://github.com/luckyframework/lucky_cli/blob/c45e1860751bba25a16120402f93e7537c0be5b5/src/base_authentication_app_skeleton/src/operations/sign_in_user.cr).
- Rename the `FindAuthenticatable` mixin to `UserFromEmail`, again the Lucky CLI [template](https://github.com/luckyframework/lucky_cli/blob/c45e1860751bba25a16120402f93e7537c0be5b5/src/base_authentication_app_skeleton/src/operations/mixins/user_from_email.cr) is a helpful guide.
## Upgrading from 0.15 to 0.16
- Upgrade to crystal 0.30.0
No updates to Lucky itself are required. There may be Crystal 0.30.0 related changes you may need to make.
## Upgrading from 0.14 to 0.15
- Upgrade to crystal 0.29.0
- Upgrade Lucky CLI (macOS)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.29.0
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
- Update `.crystal-version` to `0.29.0`
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/getting-started/installing#on-linux
- Update versions in `shard.yml`
- Lucky should be `~> 0.15`
- Run `shards update`
- Rename `src/server.cr` to `src/start_server.cr`.
- Edit `src/start_server.cr` by changing
* `app` to `app_server` and `App` to `AppServer`.
* delete the line that starts with `puts "Listening on`
- Update `src/{your app name}.cr` to require `./start_server`
- Rename `src/dependencies.cr` to `src/shards.cr`
- Move the `App` class to a new file in `src/app_server.cr`
- Rename `App` to `AppServer` and rename `Lucky::BaseApp` to `Lucky::BaseAppServer` in your new `src/app_server.cr`
- Update `src/app.cr` to require new `./app_server` file
- Update `src/app.cr` to require new `./shards` file
- Replace usages of `Lucky::Action::Status::` with the respective crystal `HTTP::Status::`
## Upgrading from 0.13 to 0.14
- Upgrade to crystal 0.28.0
- Create new file `config/force_ssl_handler.cr` with the following content:
```crystal
Lucky::ForceSSLHandler.configure do |settings|
settings.enabled = Lucky::Env.production?
end
```
## Upgrading from 0.12 to 0.13
- Upgrade Lucky CLI (macOS)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.27.2
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
- Update `.crystal-version` to `0.27.2`
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/installing/#install-lucky
- Update versions in `shard.yml`
- Lucky should be `~> 0.13`
- LuckyFlow should be `~> 0.4`
- Authentic should be `~> 0.3`
- Run `shards update`
- Find and replace `LuckyRecord` with `Avram`
- Add `Lucky::AssetHelpers.load_manifest` below `require "dependencies"` in `src/app.cr` for browser apps. Skip for API only apps.
- `Query#preload` with a query now includes the association name -> [`Query#preload_{{ assoc_name }}`](https://github.com/luckyframework/lucky_record/pull/307)
- Remove `unexpose` and `unexpose_if_exposed` from your actions. Pages now
ignore unused exposures so these methods have been removed.
- Change `require "lucky_record"` to `require "avram"` in `src/dependencies`
- Rename `config/log_handler.cr` to `config/logger.cr`
- Replace `config/logger.cr` with this:
```crystal
require "file_utils"
logger =
if Lucky::Env.test?
# Logs to `tmp/test.log` so you can see what's happening without having
# a bunch of log output in your specs results.
FileUtils.mkdir_p("tmp")
Dexter::Logger.new(
io: File.new("tmp/test.log", mode: "w"),
level: Logger::Severity::DEBUG,
log_formatter: Lucky::PrettyLogFormatter
)
elsif Lucky::Env.production?
# This sets the log formatter to JSON so you can parse the logs with
# services like Logentries or Logstash.
#
# If you want logs like in development use `Lucky::PrettyLogFormatter`.
Dexter::Logger.new(
io: STDOUT,
level: Logger::Severity::INFO,
log_formatter: Dexter::Formatters::JsonLogFormatter
)
else
# For development, log everything to STDOUT with the pretty formatter.
Dexter::Logger.new(
io: STDOUT,
level: Logger::Severity::DEBUG,
log_formatter: Lucky::PrettyLogFormatter
)
end
Lucky.configure do |settings|
settings.logger = logger
end
Avram::Repo.configure do |settings|
settings.logger = logger
end
```
- If using `is` in queries, rename the calls to `eq`
- App in `src/app.cr` should now inherit from `Lucky::BaseApp`. See [the changes you need to make](https://github.com/luckyframework/lucky_cli/commit/7794306c55b8e00ded0d816def5cd62dc6fe4367).
- Move `bin/setup` to `script/setup`
- In your `README` replace `bin/setup` with `script/setup`
- Replace `bin/lucky` in your `.gitignore` with just `/bin/`. Lucky projects
should now put bash scripts in `/script`. Binaries go in `/bin/` and are
ignored.
- `id` in actions using `route` now have the underscored version of the
resource name prepended. You'll need to rename your `id` calls to
`<resource_name>_id`.
```crystal
# Example from v0.12
class Users::Show < BrowserAction
route do
# Using the 'id' param
UserQuery.find(id)
end
end
# Would now be
class Users::Show < BrowserAction
route do
# Now it is 'user_id'
UserQuery.find(user_id)
end
end
```
- Make changes to [laravel.mix](https://github.com/luckyframework/lucky_cli/commit/88ad5af5b40f3a29c4abcb0581db505019d7003f#diff-cd19e42e70bfbcf2a12480b0b6b1f590)
- Make changes to [package.json](https://github.com/luckyframework/lucky_cli/commit/88ad5af5b40f3a29c4abcb0581db505019d7003f#diff-73db280623fcd1a64ac1ab76c8700dbc)
- Run `yarn install`
And you should now be good to go!
## Upgrading from 0.11 to 0.12
- Upgrade Lucky CLI (macOS)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date. Requires 0.27
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/installing/#install-lucky
> Use your package manager to update Crystal to v0.27
- In `db/migrations`, change `LuckyMigrator::Migration` -> `LuckyRecord::Migrator::Migration` for every migration
- Remove `lucky_migrator` from `shard.yml`
- Remove `lucky_migrator` from `src/dependencies`
- Remove the `LuckyMigrator.configure` block from `config/database.cr`
- Configuration now requires passing an argument. Find and replace `.configure do` with `.configure do |settings|` in all files in `config`
- Update `config/session.cr`
- Change `Lucky::Session::Store.configure` to `Lucky::Session.configure do |settings|`
- Change your session key because signing/encryption has changed. For example: add `_0_12_0` to the end of the key.
- Remove `settings.secret = Lucky::Server.settings.secret_key_base`
- If using `cookies[]` anywhere in your app, change the key you use. Lucky now signs and encrypts all cookies. Old cookies will not decrypt properly.
- Change `session[]=` and `cookies[]=` to `session|cookies.set|get`
- Change `session|cookies.destroy` to `session/cookies.clear`
- `cookies.unset(:key)` and `delete.unset(:key)` should be `cookies|session.delete(:key)`
- Remove `unexpose current_user` from `src/actions/home/index.cr`
- `Query#count` has been renamed to `Query#select_count`. For example: `UserQuery.new.count` is now `UserQuery.new.select_count`
- Change `flash.danger` to `flash.failure` in your actions.
- Update `Lucky::Flash::Handler` to `Lucky::FlashHandler` in `src/app.cr`
- Update usages of `Lucky::Response` to `Lucky::TextResponse`
- Update usages of `LuckyInflector::Inflector` to `Wordsmith::Inflector`
- Remove `config/session.cr` and copy [`config/cookies.cr`](https://github.com/luckyframework/lucky_cli/blob/baaeeb0b8c7a410625320af394437f8665442664/src/web_app_skeleton/config/cookies.cr.ecr)
- Replace `config/email.cr` with [this one](https://github.com/luckyframework/lucky_cli/blob/baaeeb0b8c7a410625320af394437f8665442664/src/web_app_skeleton/config/email.cr).
- Add this line to `spec_helper.cr` (around line 19) -> `LuckyRecord::Migrator::Runner.new.ensure_migrated!`
- In `config/server.cr`, copy the new block starting at [`line 15`](https://github.com/luckyframework/lucky_cli/blob/baaeeb0b8c7a410625320af394437f8665442664/src/web_app_skeleton/config/server.cr.ecr#L15-L23).
- Update shard versions in `shard.yml`:
- Lucky `~> 0.12`
- LuckyRecord `~> 0.7`
- Authentic `~> 0.2`
- LuckyFlow `~> 0.3`
- Change `.crystal-version` to `0.27.0`
- Run `shards update` to install the new shards
## Upgrading from 0.10 to 0.11
- Upgrade Lucky CLI (macOS)
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date
brew upgrade lucky
```
- Upgrade Lucky CLI (Linux)
> Remove the existing Lucky binary and follow the Linux
> instructions in this section
> https://luckyframework.org/guides/installing/#install-lucky
> Use your package manager to update Crystal to v0.25
- Update `.crystal-version` to `0.25.0`
- Change `crystal deps` to `shards install` in `bin/setup`
- Update `lucky_flow` and `lucky_migrator` in `shard.yml`
- `lucky_flow` should now be `0.2`
- `lucky_migrator` should now be `0.6`
- Remove any cached shards: rm -rf ~/.cache/shards
> This is to address a bug in shards: https://github.com/crystal-lang/shards/issues/211
- Run `shards update`
- Find all instances of `nested_action` and replace with `nested_route`
- Find all instances of `action` and replace with `route` in your actions
> To make it easier to only change the right thing, search for `action do` and
> replace with `route do`. This will make it fairly easy to find and replace
> across your whole project.
- Move static assets from `static/assets` to `public/assets`
- Move `static/js` to `src/js`
- Move `static/css` to `src/css`
- Remove `/public` from `.gitignore`
- Add these to `.gitignore`
- `/public/mix-manifest.json`
- `/public/js`
- `/public/css`
- Update `src/app.cr` lines:
- Remove host and port: https://github.com/luckyframework/lucky_cli/blob/ce677b8aefbbef2f06587d835795cbb59c5801dd/src/web_app_skeleton/src/app.cr.ecr#L25
- Add `bind_tcp` with host and port: https://github.com/luckyframework/lucky_cli/blob/ce677b8aefbbef2f06587d835795cbb59c5801dd/src/web_app_skeleton/src/app.cr.ecr#L50
- Update webpack config to match this: https://github.com/luckyframework/lucky_cli/blob/ce677b8aefbbef2f06587d835795cbb59c5801dd/src/browser_app_skeleton/webpack.mix.js#L12-L37
- Calls to the `asset` method no longer require prefixing `/assets`. You may not
be using this. The compiler will complain and help you find the right asset if
you need to update this.
### Upgrading from 0.8 to 0.10
> Note: Lucky skipped version 0.9 so that Lucky and Lucky CLI are on the same version.
- Upgrade Lucky CLI
On macOS:
```
brew update
brew upgrade crystal-lang # Make sure you're up-to-date
brew upgrade lucky
```
If you are on Linux, remove the existing Lucky binary and follow the Linux
instructions in this section
https://luckyframework.org/guides/installing/#install-lucky
- View the upgrade diff and make changes to your app
In previous upgrade guides (below) every change is listed individually. This was
time consuming and error-prone. Now,
you can [view all changes in this GitHub commit](https://github.com/luckyframework/upgrade-diffs/commit/c279b0d0c0b9936301c5ea93fd25a549c9cd4c06).
- Ensure node version is at least 6.0 `node -v`. Install a newer version if
yours is older.
- Move files in `src/pipes` to `src/actions/mixins`
- Change `allow` to `fillable` in forms
- Change `allow_virtual` to `virtual` in forms
- Run `shards update`
- Run `bin/setup` to run new migrations, Laravel Mix and seeds file
> If you have any problems or want to add extra details please open an issue or
> Pull Request. Thank you!
### Upgrading from 0.7 to 0.8
- Upgrade Lucky CLI
On macOS:
```
brew update
brew upgrade crystal-lang
brew upgrade lucky
```
If you are on Linux, remove the existing Lucky binary and follow the Linux
instructions in this section:
https://luckyframework.org/guides/installing/#install-lucky
- Update dependencies in `shard.yml`
```yml
dependencies:
lucky:
github: luckyframework/lucky
version: "~> 0.8.0"
lucky_migrator:
github: luckyframework/lucky_migrator
version: ~> 0.4.0
```
Then run `shards update`
- Update `config/server.cr`
You can probably copy this as-is, but if you have made customizations to your
`config/server.cr` then you'll need to customize this:
```crystal
Lucky::Server.configure do |settings|
if Lucky::Env.production?
settings.secret_key_base = secret_key_from_env
settings.host = "0.0.0.0"
settings.port = ENV["PORT"].to_i
else
settings.secret_key_base = "<%= secret_key_base %>"
# Change host/port in config/watch.yml
# Alternatively, you can set the PORT env to set the port
settings.host = Lucky::ServerSettings.host
settings.port = Lucky::ServerSettings.port
end
end
private def secret_key_from_env
ENV["SECRET_KEY_BASE"]? || raise_missing_secret_key_in_production
end
private def raise_missing_secret_key_in_production
raise "Please set the SECRET_KEY_BASE environment variable. You can generate a secret key with 'lucky gen.secret_key'"
end
```
- Add `config/watch.yml`
This is used by the watcher so it knows what port the server is running on.
```yaml
host: 0.0.0.0
port: 5000
```
- Update `config/database.cr`
Put this inside of the `LuckyRecord::Repo.configure do |settings|` block:
```
# In development and test, raise an error if you forget to preload associations
settings.lazy_load_enabled = Lucky::Env.production?
```
See a full example here: https://github.com/luckyframework/lucky_cli/blob/a25472cc7461b1803735d086e57a632f92f93a1c/src/web_app_skeleton/config/database.cr.ecr
- You will need to preload associations now:
This will make N+1 queries a thing of the past.
```crystal
# Will now raise a runtime error in dev/test
post = PostQuery.new.find(id)
post.comments # Must preload comments
# Now, you need to preload the comments
post = PostQuery.new.preload_comments.find(id)
post.comments
```
- Rename `field` to `column` in your models. For example
```crystal
class Post < BaseModel
table :posts do
column title : String # was "field title : String" previously
end
end
```
- Optionally include `responsive_meta_tag` in `MainLayout`
You can include this in `head` to make your app layout responsive.
- Change `abstract def inner` to `abstract def content` in `MainLayout`
- Change method call to `inner` to `content` in the render method of `MainLayout`
- Change instances of `def inner` to `def content` in Pages
- Change form `needs` to use `on: :create`
`needs` in forms should now use `on: :save` if you want the old behavior.
See https://luckyframework.org/guides/saving-with-forms/#passing-extra-data-to-forms for more info
- Must pass extra params using `create` or `update`
You can no longer pass params to `Form#new`. You must pass them in the
`create` or `update`.
```crystal
UserForm.new(name: "Jane").save!
UserForm.create!(name: "Jane")
```
More info at https://luckyframework.org/guides/saving-with-forms/#passing-data-without-route-params
- Change calls from `form.save_succeeded?` to `form.saved?`
- Trap int in src/server.cr
Add this to your `src/server.cr` before `server.listen`
```crystal
Signal::INT.trap do
server.close
end
```
- Add `bin/lucky/` to `.gitignore`
```
# Add to .gitignore
bin/lucky/
```
- Add nice HTML error page
Copy contents of the linked file to `src/pages/errors/show_page.cr`
https://github.com/luckyframework/lucky_cli/blob/a2547
gitextract_eam4n9mt/ ├── .ameba.yml ├── .crystal-version ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── config.yml │ └── workflows/ │ ├── ci.yml │ └── docs.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── UPGRADE_NOTES.md ├── bin/ │ ├── lucky.exec.cr │ ├── lucky.gen.action.api.cr │ ├── lucky.gen.action.browser.cr │ ├── lucky.gen.action.cr │ ├── lucky.gen.component.cr │ ├── lucky.gen.page.cr │ ├── lucky.gen.secret_key.cr │ ├── lucky.gen.task.cr │ └── lucky.watch.cr ├── bunfig.toml ├── docker-compose.yml ├── public/ │ ├── bun-manifest.json │ ├── mix-manifest.json │ └── vite-manifest.json ├── script/ │ ├── docs │ ├── setup │ └── test ├── shard.edge.yml ├── shard.override.yml ├── shard.yml ├── spec/ │ ├── bun/ │ │ ├── config_spec.cr │ │ └── lucky.test.js │ ├── charms/ │ │ ├── cookie_spec.cr │ │ ├── hash_spec.cr │ │ ├── string_spec.cr │ │ └── uuid_spec.cr │ ├── fixtures/ │ │ └── plain_text │ ├── lucky/ │ │ ├── action_cookies_and_sessions_spec.cr │ │ ├── action_pipes_spec.cr │ │ ├── action_redirect_spec.cr │ │ ├── action_rendering_spec.cr │ │ ├── action_route_params_spec.cr │ │ ├── action_spec.cr │ │ ├── asset_helpers_spec.cr │ │ ├── assignable_spec.cr │ │ ├── base_http_client_spec.cr │ │ ├── base_tags_spec.cr │ │ ├── component_spec.cr │ │ ├── cookies/ │ │ │ ├── cookie_jar_spec.cr │ │ │ ├── flash_store_spec.cr │ │ │ └── session_spec.cr │ │ ├── custom_tags_spec.cr │ │ ├── data_response_spec.cr │ │ ├── dev_asset_cache_handler_spec.cr │ │ ├── error_handling_spec.cr │ │ ├── errors_spec.cr │ │ ├── expose_spec.cr │ │ ├── fallback_action_spec.cr │ │ ├── file_response_spec.cr │ │ ├── force_ssl_handler_spec.cr │ │ ├── forgery_protection_helpers_spec.cr │ │ ├── form_helpers_spec.cr │ │ ├── format_edge_cases_spec.cr │ │ ├── format_integration_spec.cr │ │ ├── html_page_spec.cr │ │ ├── http_method_override_handler_spec.cr │ │ ├── infer_page_spec.cr │ │ ├── link_helpers_spec.cr │ │ ├── log_handler_spec.cr │ │ ├── maximum_request_size_handler_spec.cr │ │ ├── memoize_spec.cr │ │ ├── mime_type_spec.cr │ │ ├── namespaced_action_spec.cr │ │ ├── number_to_currency_spec.cr │ │ ├── paginator/ │ │ │ ├── backend_helpers_spec.cr │ │ │ ├── components_spec.cr │ │ │ └── paginator_spec.cr │ │ ├── param_parser_spec.cr │ │ ├── params_spec.cr │ │ ├── pretty_log_formatter_spec.cr │ │ ├── protect_from_forgery_spec.cr │ │ ├── quick_def_spec.cr │ │ ├── rate_limit_spec.cr │ │ ├── remote_ip_handler_spec.cr │ │ ├── render_if_defined_spec.cr │ │ ├── request_expectations_spec.cr │ │ ├── request_type_helper_spec.cr │ │ ├── root_spec.cr │ │ ├── route_handler_format_spec.cr │ │ ├── route_helper_format_spec.cr │ │ ├── route_helper_spec.cr │ │ ├── route_not_found_error_spec.cr │ │ ├── route_not_found_handler_spec.cr │ │ ├── route_prefix_spec.cr │ │ ├── router_spec.cr │ │ ├── secure_headers_spec.cr │ │ ├── serializable_spec.cr │ │ ├── specialty_tags_spec.cr │ │ ├── static_compression_handler_spec.cr │ │ ├── static_file_handler_spec.cr │ │ ├── subdomain_spec.cr │ │ ├── support/ │ │ │ ├── message_encrypter_spec.cr │ │ │ └── message_verifier_spec.cr │ │ ├── tag_defaults_spec.cr │ │ ├── text_helpers/ │ │ │ ├── cycle_spec.cr │ │ │ ├── excerpts_spec.cr │ │ │ ├── highlight_spec.cr │ │ │ ├── pluralize_spec.cr │ │ │ ├── simple_format_spec.cr │ │ │ ├── svg_inliner_spec.cr │ │ │ ├── text_helpers_spec.cr │ │ │ ├── to_sentence_spec.cr │ │ │ ├── truncate_spec.cr │ │ │ └── word_wrap_spec.cr │ │ ├── text_response_spec.cr │ │ ├── time_helpers_spec.cr │ │ ├── uploaded_file_spec.cr │ │ ├── url_format_spec.cr │ │ ├── url_helpers_spec.cr │ │ ├── verify_accepts_format_spec.cr │ │ └── welcome_page_spec.cr │ ├── spec_helper.cr │ ├── support/ │ │ ├── cleanup_helper.cr │ │ ├── context_helper.cr │ │ ├── exec_template.cr.template │ │ ├── generator_helper.cr │ │ ├── multipart_helper.cr │ │ ├── raw_log_formatter.cr │ │ ├── routes_helper.cr │ │ ├── test_action.cr │ │ ├── test_fallback_action.cr │ │ └── test_server.cr │ └── tasks/ │ ├── exec_spec.cr │ ├── gen/ │ │ ├── action_spec.cr │ │ ├── component_spec.cr │ │ ├── page_spec.cr │ │ ├── secret_key_base_spec.cr │ │ └── task_spec.cr │ └── routes_spec.cr ├── src/ │ ├── bun/ │ │ ├── bake.js │ │ ├── config.cr │ │ ├── lucky.js │ │ └── plugins/ │ │ ├── aliases.js │ │ ├── cssGlobs.js │ │ ├── index.js │ │ └── jsGlobs.js │ ├── charms/ │ │ ├── bool_extensions.cr │ │ ├── cookie.cr │ │ ├── hash_extensions.cr │ │ ├── int16_extensions.cr │ │ ├── int32_extensions.cr │ │ ├── int64_extensions.cr │ │ ├── object.cr │ │ ├── request_extensions.cr │ │ ├── static_file_handler.cr │ │ ├── string_extensions.cr │ │ └── uuid_extensions.cr │ ├── lucky/ │ │ ├── action.cr │ │ ├── action_delegates.cr │ │ ├── action_pipes.cr │ │ ├── allowed_in_tags.cr │ │ ├── asset_helpers.cr │ │ ├── assignable.cr │ │ ├── base_app_server.cr │ │ ├── base_component.cr │ │ ├── base_http_client.cr │ │ ├── base_log_formatter.cr │ │ ├── context_extensions.cr │ │ ├── cookies/ │ │ │ ├── cookie_jar.cr │ │ │ ├── flash_store.cr │ │ │ └── session.cr │ │ ├── data_response.cr │ │ ├── dev_asset_cache_handler.cr │ │ ├── enforce_underscored_route.cr │ │ ├── error_action.cr │ │ ├── error_handler.cr │ │ ├── errors.cr │ │ ├── events/ │ │ │ ├── pipe_event.cr │ │ │ └── request_complete_event.cr │ │ ├── exception_page.cr │ │ ├── exposable.cr │ │ ├── file_response.cr │ │ ├── force_ssl_handler.cr │ │ ├── form_data.cr │ │ ├── form_data_parser.cr │ │ ├── format.cr │ │ ├── format_registry.cr │ │ ├── html_builder.cr │ │ ├── html_page.cr │ │ ├── http_method_override_handler.cr │ │ ├── json_body_parser.cr │ │ ├── log_handler.cr │ │ ├── logger_helpers.cr │ │ ├── maximum_request_size_handler.cr │ │ ├── memoizable.cr │ │ ├── mime_type.cr │ │ ├── mount_component.cr │ │ ├── page_helpers/ │ │ │ ├── helperful_paragraph_error.cr │ │ │ ├── html_text_helpers.cr │ │ │ ├── number_to_currency.cr │ │ │ ├── render_if_defined.cr │ │ │ ├── svg_inliner.cr │ │ │ ├── text_helpers.cr │ │ │ ├── time_helpers.cr │ │ │ └── url_helpers.cr │ │ ├── paginator/ │ │ │ ├── backend_helpers.cr │ │ │ ├── components/ │ │ │ │ ├── bootstrap_nav.cr │ │ │ │ ├── bulma_nav.cr │ │ │ │ └── simple_nav.cr │ │ │ ├── current_page.cr │ │ │ ├── gap.cr │ │ │ ├── page.cr │ │ │ └── paginator.cr │ │ ├── param_helpers.cr │ │ ├── param_parser.cr │ │ ├── params.cr │ │ ├── pretty_log_formatter.cr │ │ ├── protect_from_forgery.cr │ │ ├── quick_def.cr │ │ ├── rate_limit.cr │ │ ├── redirectable.cr │ │ ├── redirectable_turbolinks_support.cr │ │ ├── remote_ip_handler.cr │ │ ├── renderable.cr │ │ ├── renderable_error.cr │ │ ├── request_body_limit.cr │ │ ├── request_body_reader.cr │ │ ├── request_expectations.cr │ │ ├── request_id_handler.cr │ │ ├── request_type_helpers.cr │ │ ├── response.cr │ │ ├── routable.cr │ │ ├── route_handler.cr │ │ ├── route_helper.cr │ │ ├── route_inferrer.cr │ │ ├── route_not_found_handler.cr │ │ ├── router.cr │ │ ├── secure_headers/ │ │ │ ├── disable_floc.cr │ │ │ ├── set_csp_guard.cr │ │ │ ├── set_frame_guard.cr │ │ │ ├── set_sniff_guard.cr │ │ │ └── set_xss_guard.cr │ │ ├── serializable.cr │ │ ├── server.cr │ │ ├── server_settings.cr │ │ ├── skip_route_style_check.cr │ │ ├── static_compression_handler.cr │ │ ├── subdomain.cr │ │ ├── support/ │ │ │ ├── message_encryptor.cr │ │ │ └── message_verifier.cr │ │ ├── tags/ │ │ │ ├── _check_tag_content.cr │ │ │ ├── base_tags.cr │ │ │ ├── bun_reload_tag.cr │ │ │ ├── custom_tags.cr │ │ │ ├── forgery_protection_helpers.cr │ │ │ ├── form_helpers.cr │ │ │ ├── link_helpers.cr │ │ │ ├── live_reload_tag.cr │ │ │ ├── specialty_tags.cr │ │ │ └── tag_defaults.cr │ │ ├── text_response.cr │ │ ├── uploaded_file.cr │ │ ├── verify_accepts_format.cr │ │ ├── version.cr │ │ └── welcome_page.cr │ ├── lucky.cr │ └── run_macros/ │ ├── asset_manifest_builder.cr │ └── missing_asset.cr ├── tasks/ │ ├── exec.cr │ ├── exec_template.cr.template │ ├── gen/ │ │ ├── action/ │ │ │ ├── action_generator.cr │ │ │ ├── api.cr │ │ │ └── browser.cr │ │ ├── component.cr │ │ ├── page.cr │ │ ├── secret_key.cr │ │ ├── task.cr │ │ └── templates/ │ │ ├── action/ │ │ │ └── action.cr.ecr │ │ ├── component/ │ │ │ └── component.cr.ecr │ │ ├── page/ │ │ │ └── page.cr.ecr │ │ └── task/ │ │ └── task.cr.ecr │ ├── routes.cr │ └── watch.cr └── tasks.cr
SYMBOL INDEX (38 symbols across 6 files)
FILE: spec/bun/lucky.test.js
constant TEST_DIR (line 6) | const TEST_DIR = join(process.cwd(), '.test-tmp')
function createFile (line 24) | function createFile(relativePath, content = '') {
function readOutput (line 31) | function readOutput(relativePath) {
function setupProject (line 35) | async function setupProject(files = {}, configOverrides = {}) {
function buildCSS (line 43) | async function buildCSS(files, configOverrides) {
function buildJS (line 49) | async function buildJS(files, configOverrides) {
function copyAssets (line 284) | async function copyAssets(files = {}, config = {}) {
function jsApp (line 583) | function jsApp(...lines) {
function buildJSGlobs (line 587) | async function buildJSGlobs(files) {
FILE: src/bun/lucky.js
method flags (line 28) | flags({debug, dev, prod}) {
method deepMerge (line 34) | deepMerge(target, source) {
method loadConfig (line 44) | loadConfig() {
method loadPlugins (line 66) | async loadPlugins() {
method outDir (line 76) | get outDir() {
method fingerprint (line 82) | fingerprint(name, ext, content) {
method buildAssets (line 89) | async buildAssets(type, options = {}) {
method buildJS (line 141) | async buildJS() {
method buildCSS (line 149) | async buildCSS() {
method copyStaticAssets (line 153) | async copyStaticAssets() {
method cleanOutDir (line 180) | cleanOutDir() {
method writeManifest (line 184) | async writeManifest() {
method build (line 190) | async build() {
method prettyManifest (line 205) | prettyManifest() {
method reload (line 212) | reload(type = 'full') {
method watch (line 225) | async watch() {
method serve (line 278) | async serve() {
method bake (line 311) | async bake() {
FILE: src/bun/plugins/aliases.js
constant REGEX (line 1) | const REGEX = /(url\(\s*['"]?|(?<!\w)['"](?:glob:)?)\$\//g
function aliases (line 7) | function aliases({root}) {
FILE: src/bun/plugins/cssGlobs.js
constant REGEX (line 4) | const REGEX = /@import\s+['"]([^'"]*\*[^'"]*)['"]\s*;/g
function cssGlobs (line 8) | function cssGlobs() {
FILE: src/bun/plugins/index.js
constant TYPE_REGEXES (line 7) | const TYPE_REGEXES = {
function transformPipeline (line 13) | function transformPipeline(type, transforms) {
function loadFactory (line 31) | async function loadFactory(name, root) {
function resolveType (line 44) | async function resolveType(names, context) {
function resolvePlugins (line 66) | async function resolvePlugins(pluginConfig, context) {
FILE: src/bun/plugins/jsGlobs.js
constant REGEX (line 4) | const REGEX = /import\s+(\w+)\s+from\s+['"]glob:([^'"]+)['"]/g
function jsGlobs (line 18) | function jsGlobs() {
Condensed preview — 291 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (973K chars).
[
{
"path": ".ameba.yml",
"chars": 803,
"preview": "# This configuration file was generated by `ameba --gen-config`\n# on 2026-04-02 23:47:16 UTC using Ameba version 1.6.4.\n"
},
{
"path": ".crystal-version",
"chars": 7,
"preview": "1.15.1\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 79,
"preview": "# These are supported funding model platforms\n\ngithub: [paulcsmith, jwoertink]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 749,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 444,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: Request Feature\n url: https://github.com/luckyframework/lucky/dis"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1524,
"preview": "name: Lucky CI\n\non:\n push:\n branches: [main]\n pull_request:\n branches: \"*\"\n\njobs:\n check_format:\n strategy:\n"
},
{
"path": ".github/workflows/docs.yml",
"chars": 570,
"preview": "name: Deploy docs\n\non:\n push:\n branches: [main]\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - uses:"
},
{
"path": ".gitignore",
"chars": 191,
"preview": "/docs/\n/libs/\n/lib/\n/bin/ameba\n/bin/ameba.*\n/tmp/\n/.shards/\nserver\n\n# Libraries don't need dependency lock\n# Dependencie"
},
{
"path": ".prettierrc",
"chars": 190,
"preview": "{\n \"printWidth\": 80,\n \"tabWidth\": 2,\n \"singleQuote\": true,\n \"semi\": false,\n \"bracketSpacing\": false,\n \"quoteProps\""
},
{
"path": "CHANGELOG.md",
"chars": 107110,
"preview": "### Changes in 1.4.0\n\n- Fixed: `Lucky::Response` require for defining `debug_message` on custom response types. [#1927]("
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 2601,
"preview": "# Open Source Code of Conduct\n\nIn order to foster an inclusive, kind, harassment-free, and cooperative community, the Lu"
},
{
"path": "CONTRIBUTING.md",
"chars": 2956,
"preview": "# Contributing to Lucky\n\nWe love pull requests from everyone. By participating in this project, you\nagree to abide by th"
},
{
"path": "Dockerfile",
"chars": 324,
"preview": "FROM crystallang/crystal:latest\nWORKDIR /data\n\nRUN apt-get update && \\\n apt-get install -y curl libreadline-dev unzip &"
},
{
"path": "LICENSE",
"chars": 1077,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2017 Paul Smith\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "PULL_REQUEST_TEMPLATE.md",
"chars": 696,
"preview": "## Purpose\nDescribe the feature or issue and link to the related issue.\nIf no issue has been opened about this, be sure "
},
{
"path": "README.md",
"chars": 7529,
"preview": "[\n"
},
{
"path": "bin/lucky.gen.action.api.cr",
"chars": 84,
"preview": "require \"lucky/tasks/gen/action/api\"\n\nGen::Action::Api.new.print_help_or_call(ARGV)\n"
},
{
"path": "bin/lucky.gen.action.browser.cr",
"chars": 92,
"preview": "require \"lucky/tasks/gen/action/browser\"\n\nGen::Action::Browser.new.print_help_or_call(ARGV)\n"
},
{
"path": "bin/lucky.gen.action.cr",
"chars": 315,
"preview": "require \"colorize\"\n\nputs <<-ERROR\n Missing 'browser' or 'api' after 'gen.action'\n\n For actions used in a browser ("
},
{
"path": "bin/lucky.gen.component.cr",
"chars": 81,
"preview": "require \"lucky/tasks/gen/component\"\n\nGen::Component.new.print_help_or_call(ARGV)\n"
},
{
"path": "bin/lucky.gen.page.cr",
"chars": 71,
"preview": "require \"lucky/tasks/gen/page\"\n\nGen::Page.new.print_help_or_call(ARGV)\n"
},
{
"path": "bin/lucky.gen.secret_key.cr",
"chars": 82,
"preview": "require \"lucky/tasks/gen/secret_key\"\n\nGen::SecretKey.new.print_help_or_call(ARGV)\n"
},
{
"path": "bin/lucky.gen.task.cr",
"chars": 71,
"preview": "require \"lucky/tasks/gen/task\"\n\nGen::Task.new.print_help_or_call(ARGV)\n"
},
{
"path": "bin/lucky.watch.cr",
"chars": 64,
"preview": "require \"lucky/tasks/watch\"\n\nWatch.new.print_help_or_call(ARGV)\n"
},
{
"path": "bunfig.toml",
"chars": 27,
"preview": "[test]\nroot = \"./spec/bun\"\n"
},
{
"path": "docker-compose.yml",
"chars": 187,
"preview": "services:\n app:\n build:\n context: .\n dockerfile: Dockerfile\n environment:\n SHARDS_CACHE_PATH: /dat"
},
{
"path": "public/bun-manifest.json",
"chars": 191,
"preview": "{\n \"images/logo.png\": \"images/logo-8dc912a1.png\",\n \"images/lucky_logo.png\": \"images/lucky_logo-8dc912a1.png\",\n \"js/ap"
},
{
"path": "public/mix-manifest.json",
"chars": 147,
"preview": "{\n \"/images/logo.png\": \"/images/logo-with-hash.png\",\n \"/assets/images/inside-assets-folder.png\":\n \"/assets/images/i"
},
{
"path": "public/vite-manifest.json",
"chars": 234,
"preview": "{\n \"src/images/lucky_logo.png\": {\n \"file\": \"images/lucky_logo.a54cc67e.png\",\n \"src\": \"src/images/lucky_logo.png\"\n"
},
{
"path": "script/docs",
"chars": 127,
"preview": "#!/bin/sh -eu\n\nCOMPOSE=\"docker compose run --rm app\"\n\nprintf \"\\ngenerating docs with 'crystal docs'\\n\\n\"\n$COMPOSE crysta"
},
{
"path": "script/setup",
"chars": 277,
"preview": "#!/bin/sh -eu\n\nif ! command -v docker compose >/dev/null; then\n printf 'Docker and docker compose are not installed.\\n'"
},
{
"path": "script/test",
"chars": 583,
"preview": "#!/bin/sh -eu\n\nCOMPOSE=\"docker compose run --remove-orphans --rm app\"\n\nprintf \"\\nrunning specs with 'crystal spec'\\n\\n\"\n"
},
{
"path": "shard.edge.yml",
"chars": 738,
"preview": "dependencies:\n lucky_task:\n github: luckyframework/lucky_task\n branch: main\n habitat:\n github: luckyframework"
},
{
"path": "shard.override.yml",
"chars": 85,
"preview": "# dependencies:\n# habitat:\n# github: luckyframework/habitat\n# branch: main\n"
},
{
"path": "shard.yml",
"chars": 1327,
"preview": "name: lucky\nversion: 1.4.0\n\ncrystal: '>= 1.16.3'\n\nauthors:\n - Paul Smith <paulcsmith0218@gmail.com>\n\nexecutables:\n - l"
},
{
"path": "spec/bun/config_spec.cr",
"chars": 1443,
"preview": "require \"../spec_helper\"\n\ndescribe LuckyBun::Config do\n it \"uses defaults without a config file\" do\n config = LuckyB"
},
{
"path": "spec/bun/lucky.test.js",
"chars": 24221,
"preview": "import {describe, test, expect, beforeEach, afterAll} from 'bun:test'\nimport {mkdirSync, writeFileSync, rmSync, existsSy"
},
{
"path": "spec/charms/cookie_spec.cr",
"chars": 1025,
"preview": "require \"../spec_helper\"\n\ndescribe HTTP::Cookie do\n describe \"setters\" do\n it \"can chain and set values\" do\n ti"
},
{
"path": "spec/charms/hash_spec.cr",
"chars": 873,
"preview": "require \"../spec_helper\"\n\ndescribe \"Hash charm\" do\n describe \"get\" do\n it \"gets the string key whether you pass a sy"
},
{
"path": "spec/charms/string_spec.cr",
"chars": 540,
"preview": "require \"../spec_helper\"\n\ndescribe \"String charm\" do\n describe \"squish\" do\n it \"squishes the text by removing newlin"
},
{
"path": "spec/charms/uuid_spec.cr",
"chars": 307,
"preview": "require \"../spec_helper\"\n\ndescribe \"UUID charm\" do\n describe \"to_param\" do\n it \"gets the value as a string\" do\n "
},
{
"path": "spec/fixtures/plain_text",
"chars": 6,
"preview": "lucky\n"
},
{
"path": "spec/lucky/action_cookies_and_sessions_spec.cr",
"chars": 1917,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass Cookies::Index < TestAction\n get \"/cookies\" do\n cookies.set :"
},
{
"path": "spec/lucky/action_pipes_spec.cr",
"chars": 6348,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass PipeFromActionMacro::Index < TestAction\n before set_before_cooki"
},
{
"path": "spec/lucky/action_redirect_spec.cr",
"chars": 8507,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass RedirectAction < TestAction\n include Lucky::RedirectableTurbolin"
},
{
"path": "spec/lucky/action_rendering_spec.cr",
"chars": 8660,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass Rendering::IndexPage\n include Lucky::HTMLPage\n\n needs title : S"
},
{
"path": "spec/lucky/action_route_params_spec.cr",
"chars": 4033,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestParamAction < TestAction\n get \"/test/:param_1/:param"
},
{
"path": "spec/lucky/action_spec.cr",
"chars": 20684,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass CustomRoutes::Index < TestAction\n get \"/so_custom\" do\n plain_"
},
{
"path": "spec/lucky/asset_helpers_spec.cr",
"chars": 3907,
"preview": "require \"../spec_helper\"\n\nmodule Shared::ComponentWithAsset\n def asset_inside_component\n asset(\"images/logo.png\")\n "
},
{
"path": "spec/lucky/assignable_spec.cr",
"chars": 2377,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass BasePage\n include Lucky::HTMLPage\n\n needs name : String\nend\n\ncl"
},
{
"path": "spec/lucky/base_http_client_spec.cr",
"chars": 4930,
"preview": "require \"../spec_helper\"\n\nclass HelloWorldAction < TestAction\n accepted_formats [:plain_text]\n\n param codes : Array(St"
},
{
"path": "spec/lucky/base_tags_spec.cr",
"chars": 2105,
"preview": "require \"../spec_helper\"\ninclude ContextHelper\n\nprivate class TestPage\n include Lucky::HTMLPage\n\n def render\n end\nend"
},
{
"path": "spec/lucky/component_spec.cr",
"chars": 5295,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestComponent < Lucky::BaseComponent\n def render\n tex"
},
{
"path": "spec/lucky/cookies/cookie_jar_spec.cr",
"chars": 7943,
"preview": "require \"../../spec_helper\"\n\ndescribe Lucky::CookieJar do\n it \"sets and gets with indifferent access\" do\n jar = Luck"
},
{
"path": "spec/lucky/cookies/flash_store_spec.cr",
"chars": 6049,
"preview": "require \"../../spec_helper\"\n\ndescribe Lucky::FlashStore do\n describe \".from_session\" do\n it \"creates a flash store f"
},
{
"path": "spec/lucky/cookies/session_spec.cr",
"chars": 892,
"preview": "require \"../../spec_helper\"\n\ndescribe Lucky::Session do\n it \"gets and sets with indifferent access\" do\n store = Luck"
},
{
"path": "spec/lucky/custom_tags_spec.cr",
"chars": 1991,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestPage\n include Lucky::HTMLPage\n\n def render\n end\nen"
},
{
"path": "spec/lucky/data_response_spec.cr",
"chars": 2907,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::FileResponse do\n describe \"#print\" do\n describe \"st"
},
{
"path": "spec/lucky/dev_asset_cache_handler_spec.cr",
"chars": 1365,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::DevAssetCacheHandler do\n it \"sets no-cache headers for"
},
{
"path": "spec/lucky/error_handling_spec.cr",
"chars": 4712,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class CustomError < Exception\nend\n\nprivate class StealthError <"
},
{
"path": "spec/lucky/errors_spec.cr",
"chars": 2526,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe \"Errors\" do\n describe Lucky::InvalidParamError do\n it \"is "
},
{
"path": "spec/lucky/expose_spec.cr",
"chars": 1177,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass OnlyExpose < TestAction\n expose name\n\n get \"/expose\" do\n htm"
},
{
"path": "spec/lucky/fallback_action_spec.cr",
"chars": 819,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\n# TestFallbackAction::Index is defined in spec/support/test_fallback_ac"
},
{
"path": "spec/lucky/file_response_spec.cr",
"chars": 3329,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::FileResponse do\n describe \"#print\" do\n describe \"fi"
},
{
"path": "spec/lucky/force_ssl_handler_spec.cr",
"chars": 3282,
"preview": "require \"../spec_helper\"\nrequire \"http/server\"\n\ninclude ContextHelper\n\ndescribe Lucky::ForceSSLHandler do\n context \"whe"
},
{
"path": "spec/lucky/forgery_protection_helpers_spec.cr",
"chars": 1030,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestPage\n include Lucky::HTMLPage\n\n def render\n end\nen"
},
{
"path": "spec/lucky/form_helpers_spec.cr",
"chars": 4416,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass FormHelpers::Index < TestAction\n get \"/form_helpers\" { plain_tex"
},
{
"path": "spec/lucky/format_edge_cases_spec.cr",
"chars": 12724,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class EdgeCaseFormatAction < TestAction\n accepted_formats [:ht"
},
{
"path": "spec/lucky/format_integration_spec.cr",
"chars": 2510,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestReportsAction < TestAction\n accepted_formats [:html,"
},
{
"path": "spec/lucky/html_page_spec.cr",
"chars": 4539,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass TestRender\n include Lucky::HTMLPage\n\n def render : String\n r"
},
{
"path": "spec/lucky/http_method_override_handler_spec.cr",
"chars": 1582,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::HttpMethodOverrideHandler do\n describe \"#call\" do\n "
},
{
"path": "spec/lucky/infer_page_spec.cr",
"chars": 848,
"preview": "require \"../spec_helper\"\n\nclass Rendering::CustomPage\n include Lucky::HTMLPage\n\n needs title : String\n needs arg2 : S"
},
{
"path": "spec/lucky/link_helpers_spec.cr",
"chars": 3109,
"preview": "require \"../spec_helper\"\n\nclass LinkHelpers::Index < TestAction\n get \"/link_helpers\" { plain_text \"foo\" }\nend\n\nclass Li"
},
{
"path": "spec/lucky/log_handler_spec.cr",
"chars": 1847,
"preview": "require \"../spec_helper\"\nrequire \"http/server\"\n\ninclude ContextHelper\n\ndescribe Lucky::LogHandler do\n it \"logs the star"
},
{
"path": "spec/lucky/maximum_request_size_handler_spec.cr",
"chars": 3160,
"preview": "require \"../spec_helper\"\nrequire \"http/server\"\n\ninclude ContextHelper\n\ndescribe Lucky::MaximumRequestSizeHandler do\n co"
},
{
"path": "spec/lucky/memoize_spec.cr",
"chars": 4183,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\n# NOTE: The Memoizable module is included in the Object charm\n# which g"
},
{
"path": "spec/lucky/mime_type_spec.cr",
"chars": 7819,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::MimeType do\n describe \"determine_clients_desired_forma"
},
{
"path": "spec/lucky/namespaced_action_spec.cr",
"chars": 644,
"preview": "require \"../spec_helper\"\n\nclass Admin::MultiWord::Users::Show < TestAction\n get \"/admin/multi_word/users/:user_id\" do\n "
},
{
"path": "spec/lucky/number_to_currency_spec.cr",
"chars": 1649,
"preview": "require \"../spec_helper\"\nrequire \"html\"\n\nprivate class TestPage\n include Lucky::HTMLPage\n\n def render\n end\nend\n\ndescr"
},
{
"path": "spec/lucky/paginator/backend_helpers_spec.cr",
"chars": 1586,
"preview": "require \"../../spec_helper\"\n\nclass Paginatable\n include Lucky::Paginator::BackendHelpers\n include ContextHelper\n\n def"
},
{
"path": "spec/lucky/paginator/components_spec.cr",
"chars": 573,
"preview": "require \"../../spec_helper\"\n\ndescribe \"Lucky::Paginator Components\" do\n it \"compiles and renders the components success"
},
{
"path": "spec/lucky/paginator/paginator_spec.cr",
"chars": 10113,
"preview": "require \"../../spec_helper\"\n\nprivate def build_pages(page = 1, per_page = 1, item_count = 1, full_path = \"/items\") : Luc"
},
{
"path": "spec/lucky/param_parser_spec.cr",
"chars": 3989,
"preview": "require \"../spec_helper\"\n\nenum TimeComponent\n Year\n Month\n Day\n Hour\n Minute\n Second\nend\n\nstruct FormattedTime\n p"
},
{
"path": "spec/lucky/params_spec.cr",
"chars": 26206,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\ninclude MultipartHelper\n\ndescribe Lucky::Params do\n describe \"#from_que"
},
{
"path": "spec/lucky/pretty_log_formatter_spec.cr",
"chars": 3688,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::PrettyLogFormatter do\n context \"special cases\" do\n "
},
{
"path": "spec/lucky/protect_from_forgery_spec.cr",
"chars": 2683,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass ProtectedAction::Index < TestAction\n include Lucky::ProtectFromF"
},
{
"path": "spec/lucky/quick_def_spec.cr",
"chars": 366,
"preview": "require \"../spec_helper\"\n\nprivate class QuickDefClass\n quick_def :page_title, \"My page title\"\n quick_def page_title_wi"
},
{
"path": "spec/lucky/rate_limit_spec.cr",
"chars": 1787,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass RateLimitRoutes::Index < TestAction\n include Lucky::RateLimit\n\n "
},
{
"path": "spec/lucky/remote_ip_handler_spec.cr",
"chars": 2638,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::RemoteIpHandler do\n describe \"getting the remote_addre"
},
{
"path": "spec/lucky/render_if_defined_spec.cr",
"chars": 718,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nabstract class LayoutWithOptionalSidebar\n include Lucky::HTMLPage\n\n d"
},
{
"path": "spec/lucky/request_expectations_spec.cr",
"chars": 2727,
"preview": "require \"../spec_helper\"\n\ninclude Lucky::RequestExpectations\n\ndescribe Lucky::RequestExpectations do\n it \"fails if the "
},
{
"path": "spec/lucky/request_type_helper_spec.cr",
"chars": 2565,
"preview": "require \"../spec_helper\"\n\nprivate class FakeAction\n include Lucky::RequestTypeHelpers\n default_format :my_default_form"
},
{
"path": "spec/lucky/root_spec.cr",
"chars": 243,
"preview": "require \"../spec_helper\"\n\nclass RootAction < TestAction\n get \"/\" do\n plain_text \"Hello there\"\n end\nend\n\ndescribe \"r"
},
{
"path": "spec/lucky/route_handler_format_spec.cr",
"chars": 6806,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe \"Route Handler Format Integration\" do\n describe \"path manipul"
},
{
"path": "spec/lucky/route_helper_format_spec.cr",
"chars": 2298,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass FormatBlog::Index < TestAction\n accepted_formats [:html, :rss], "
},
{
"path": "spec/lucky/route_helper_spec.cr",
"chars": 3330,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass TestSubdomainAction < TestAction\n get \"/dashboard\" do\n plain_"
},
{
"path": "spec/lucky/route_not_found_error_spec.cr",
"chars": 269,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::RouteNotFoundError do\n it \"has getter for the context\""
},
{
"path": "spec/lucky/route_not_found_handler_spec.cr",
"chars": 1528,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\n# TestFallbackAction::Index is defined in spec/support/test_fallback_ac"
},
{
"path": "spec/lucky/route_prefix_spec.cr",
"chars": 2417,
"preview": "require \"../spec_helper\"\n\nclass PrefixedActions < TestAction\n route_prefix \"/api/v1\"\n\n get \"/prefixed_get\" do\n plai"
},
{
"path": "spec/lucky/router_spec.cr",
"chars": 1397,
"preview": "require \"../spec_helper\"\n\ndescribe Lucky::Router do\n it \"routes based on the method name and path\" do\n Lucky.router."
},
{
"path": "spec/lucky/secure_headers_spec.cr",
"chars": 3940,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nclass FrameGuardRoutes::WithSameorigin < TestAction\n include Lucky::Se"
},
{
"path": "spec/lucky/serializable_spec.cr",
"chars": 895,
"preview": "require \"../spec_helper\"\n\nprivate abstract struct BaseSerializerStruct\n include Lucky::Serializable\nend\n\nprivate struct"
},
{
"path": "spec/lucky/specialty_tags_spec.cr",
"chars": 3295,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestPage\n include Lucky::HTMLPage\n\n def render\n end\nen"
},
{
"path": "spec/lucky/static_compression_handler_spec.cr",
"chars": 3319,
"preview": "require \"../spec_helper\"\nrequire \"http\"\n\ninclude ContextHelper\n\nprivate PATH = \"example.css\"\n\ndescribe Lucky::StaticComp"
},
{
"path": "spec/lucky/static_file_handler_spec.cr",
"chars": 493,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::StaticFileHandler do\n it \"shows static files in logs\" "
},
{
"path": "spec/lucky/subdomain_spec.cr",
"chars": 4426,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nabstract class BaseAction < Lucky::Action\n # https://github.com/luckyf"
},
{
"path": "spec/lucky/support/message_encrypter_spec.cr",
"chars": 814,
"preview": "require \"../../spec_helper\"\n\ndescribe Lucky::MessageEncryptor do\n describe \"#encrypt\" do\n it \"raises a helpful error"
},
{
"path": "spec/lucky/support/message_verifier_spec.cr",
"chars": 2831,
"preview": "require \"../../spec_helper\"\n\ndescribe Lucky::MessageVerifier do\n describe \"#verify\" do\n it \"is valid\" do\n verif"
},
{
"path": "spec/lucky/tag_defaults_spec.cr",
"chars": 1835,
"preview": "require \"../spec_helper\"\n\nprivate class TestTagDefaultsPage\n include Lucky::HTMLPage\n\n def render\n tag_defaults do "
},
{
"path": "spec/lucky/text_helpers/cycle_spec.cr",
"chars": 3798,
"preview": "require \"./text_helpers_spec\"\n\ndescribe Lucky::TextHelpers do\n Spec.before_each do\n view.reset_cycles\n end\n\n descr"
},
{
"path": "spec/lucky/text_helpers/excerpts_spec.cr",
"chars": 3707,
"preview": "require \"./text_helpers_spec\"\n\ndescribe Lucky::TextHelpers do\n describe \"excerpt\" do\n it \"excerpts\" do\n view.ex"
},
{
"path": "spec/lucky/text_helpers/highlight_spec.cr",
"chars": 4600,
"preview": "require \"../../spec_helper\"\n\ninclude ContextHelper\n\nclass HighlightTestPage\n include Lucky::HTMLPage\n\n def test_highli"
},
{
"path": "spec/lucky/text_helpers/pluralize_spec.cr",
"chars": 1195,
"preview": "require \"./text_helpers_spec\"\n\ndescribe Lucky::TextHelpers do\n describe \"pluralize\" do\n it \"pluralizes words\" do\n "
},
{
"path": "spec/lucky/text_helpers/simple_format_spec.cr",
"chars": 3063,
"preview": "require \"./text_helpers_spec\"\n\nclass TextHelperTestPage\n def test_simple_format_with_block\n simple_format(\"my cool t"
},
{
"path": "spec/lucky/text_helpers/svg_inliner_spec.cr",
"chars": 2747,
"preview": "require \"./text_helpers_spec\"\n\n@[Lucky::SvgInliner::Path(\"spec/fixtures\")]\nmodule Lucky::SvgInliner\nend\n\nclass TextHelpe"
},
{
"path": "spec/lucky/text_helpers/text_helpers_spec.cr",
"chars": 195,
"preview": "require \"../../spec_helper\"\n\ninclude ContextHelper\n\nclass TextHelperTestPage\n include Lucky::HTMLPage\n\n def render\n "
},
{
"path": "spec/lucky/text_helpers/to_sentence_spec.cr",
"chars": 1703,
"preview": "require \"./text_helpers_spec\"\n\nclass CustomEnumerable\n include Enumerable(Int32)\n\n def each(&)\n yield 1\n yield 2"
},
{
"path": "spec/lucky/text_helpers/truncate_spec.cr",
"chars": 2431,
"preview": "require \"./text_helpers_spec\"\n\nclass TextHelperTestPage\n def test_truncate\n truncate \"Hello World\", length: 8 do\n "
},
{
"path": "spec/lucky/text_helpers/word_wrap_spec.cr",
"chars": 641,
"preview": "require \"./text_helpers_spec\"\n\ndescribe Lucky::TextHelpers do\n describe \"word_word\" do\n it \"word wraps\" do\n vie"
},
{
"path": "spec/lucky/text_response_spec.cr",
"chars": 7698,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\n# Monkey patch HTTP::Server::Response to allow accessing the response b"
},
{
"path": "spec/lucky/time_helpers_spec.cr",
"chars": 2557,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class TestPage\n include Lucky::HTMLPage\n include Lucky::TimeH"
},
{
"path": "spec/lucky/uploaded_file_spec.cr",
"chars": 1611,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\ninclude MultipartHelper\n\ndescribe Lucky::UploadedFile do\n describe \"#na"
},
{
"path": "spec/lucky/url_format_spec.cr",
"chars": 1972,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class FakeActionWithFormat\n include Lucky::RequestTypeHelpers\n"
},
{
"path": "spec/lucky/url_helpers_spec.cr",
"chars": 7855,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::UrlHelpers do\n describe \"#current_page?\" do\n contex"
},
{
"path": "spec/lucky/verify_accepts_format_spec.cr",
"chars": 2266,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\nprivate class ActionThatAcceptsHtml < Lucky::Action\n accepted_formats "
},
{
"path": "spec/lucky/welcome_page_spec.cr",
"chars": 239,
"preview": "require \"../spec_helper\"\n\ninclude ContextHelper\n\ndescribe Lucky::WelcomePage do\n it \"compiles successfully\" do\n Luck"
},
{
"path": "spec/spec_helper.cr",
"chars": 1022,
"preview": "require \"spec\"\nrequire \"lucky_env\"\nrequire \"../src/lucky\"\nrequire \"../tasks/**\"\nrequire \"./support/**\"\n\ninclude RoutesHe"
},
{
"path": "spec/support/cleanup_helper.cr",
"chars": 234,
"preview": "require \"file_utils\"\n\nmodule CleanupHelper\n private def cleanup\n FileUtils.rm_rf(\"./tmp\")\n end\n\n private def with_"
},
{
"path": "spec/support/context_helper.cr",
"chars": 1471,
"preview": "module ContextHelper\n extend self\n\n private def build_request(\n method = \"GET\",\n body = \"\",\n content_type = \""
},
{
"path": "spec/support/exec_template.cr.template",
"chars": 19,
"preview": "# Just for testing\n"
},
{
"path": "spec/support/generator_helper.cr",
"chars": 1059,
"preview": "module GeneratorHelper\n private def generate(generator : Class, args : Array(String) = [] of String) : IO\n task = ge"
},
{
"path": "spec/support/multipart_helper.cr",
"chars": 2499,
"preview": "module MultipartHelper\n alias Parts = Hash(String, String | Hash(String, String) | Array(Hash(String, String)) | Array("
},
{
"path": "spec/support/raw_log_formatter.cr",
"chars": 89,
"preview": "struct RawLogFormatter < Dexter::BaseFormatter\n def call : Nil\n io << data\n end\nend\n"
},
{
"path": "spec/support/routes_helper.cr",
"chars": 380,
"preview": "module RoutesHelper\n private def assert_route_added?(method, path, expected_action)\n result = Lucky.router.find_acti"
},
{
"path": "spec/support/test_action.cr",
"chars": 130,
"preview": "abstract class TestAction < Lucky::Action\n include Lucky::EnforceUnderscoredRoute\n accepted_formats [:html], default: "
},
{
"path": "spec/support/test_fallback_action.cr",
"chars": 254,
"preview": "# This file depends on spec/test_action.cr being available first, so the name matters.\n# If the name changes you may nee"
},
{
"path": "spec/support/test_server.cr",
"chars": 608,
"preview": "class TestServer < Lucky::BaseAppServer\n class_setter last_request : HTTP::Request?\n\n def self.last_request : HTTP::Re"
},
{
"path": "spec/tasks/exec_spec.cr",
"chars": 596,
"preview": "require \"../spec_helper\"\n\ndescribe Lucky::Exec do\n {% if flag?(:win32) %}\n pending \"Cry on Windows needs updating\"\n "
},
{
"path": "spec/tasks/gen/action_spec.cr",
"chars": 4262,
"preview": "require \"../../spec_helper\"\n\ninclude CleanupHelper\ninclude GeneratorHelper\n\ndescribe Gen::Action do\n it \"generates a ba"
},
{
"path": "spec/tasks/gen/component_spec.cr",
"chars": 1264,
"preview": "require \"../../spec_helper\"\n\ninclude CleanupHelper\ninclude GeneratorHelper\n\ndescribe Gen::Component do\n it \"generates a"
},
{
"path": "spec/tasks/gen/page_spec.cr",
"chars": 1251,
"preview": "require \"../../spec_helper\"\n\ninclude CleanupHelper\ninclude GeneratorHelper\n\ndescribe Gen::Page do\n it \"generates a page"
},
{
"path": "spec/tasks/gen/secret_key_base_spec.cr",
"chars": 480,
"preview": "require \"../../spec_helper\"\n\ndescribe Gen::SecretKey do\n it \"outputs a new secret key base\" do\n task = Gen::SecretKe"
},
{
"path": "spec/tasks/gen/task_spec.cr",
"chars": 1510,
"preview": "require \"../../spec_helper\"\n\ninclude CleanupHelper\ninclude GeneratorHelper\n\nprivate def run_with_args(args : Array(Strin"
},
{
"path": "spec/tasks/routes_spec.cr",
"chars": 2240,
"preview": "require \"../spec_helper\"\n\n# Test action with params for routes JSON output\nprivate class RoutesTaskTestAction < TestActi"
},
{
"path": "src/bun/bake.js",
"chars": 200,
"preview": "import LuckyBun from './lucky.js'\n\nLuckyBun.flags({\n debug: process.argv.includes('--debug'),\n dev: process.argv.inclu"
},
{
"path": "src/bun/config.cr",
"chars": 1901,
"preview": "require \"json\"\n\nmodule LuckyBun\n struct Config\n include JSON::Serializable\n\n CONFIG_PATH = \"./config/bun.json\"\n\n "
},
{
"path": "src/bun/lucky.js",
"chars": 8946,
"preview": "import {mkdirSync, readFileSync, existsSync, rmSync, watch} from 'fs'\nimport {join, dirname, basename, extname} from 'pa"
},
{
"path": "src/bun/plugins/aliases.js",
"chars": 482,
"preview": "const REGEX = /(url\\(\\s*['\"]?|(?<!\\w)['\"](?:glob:)?)\\$\\//g\n\n// Resolves `$/` root aliases in CSS url() references and JS"
},
{
"path": "src/bun/plugins/cssGlobs.js",
"chars": 1567,
"preview": "import {dirname, relative, resolve, join} from 'path'\nimport {Glob} from 'bun'\n\nconst REGEX = /@import\\s+['\"]([^'\"]*\\*[^"
},
{
"path": "src/bun/plugins/index.js",
"chars": 2472,
"preview": "import {join} from 'path'\nimport aliases from './aliases.js'\nimport cssGlobs from './cssGlobs.js'\nimport jsGlobs from '."
},
{
"path": "src/bun/plugins/jsGlobs.js",
"chars": 1704,
"preview": "import {dirname, extname} from 'path'\nimport {Glob} from 'bun'\n\nconst REGEX = /import\\s+(\\w+)\\s+from\\s+['\"]glob:([^'\"]+)"
},
{
"path": "src/charms/bool_extensions.cr",
"chars": 85,
"preview": "require \"../lucky/allowed_in_tags\"\n\nstruct Bool\n include ::Lucky::AllowedInTags\nend\n"
},
{
"path": "src/charms/cookie.cr",
"chars": 777,
"preview": "class HTTP::Cookie\n def name(value : String) : HTTP::Cookie\n self.name = value\n self\n end\n\n def value(string : "
},
{
"path": "src/charms/hash_extensions.cr",
"chars": 1091,
"preview": "class Hash(K, V)\n # Return the **nilable** value of a hash key\n #\n # This returns a value stored in a hash. The key c"
},
{
"path": "src/charms/int16_extensions.cr",
"chars": 131,
"preview": "require \"../lucky/allowed_in_tags\"\n\nstruct Int16\n include ::Lucky::AllowedInTags\n\n def to_param : String\n self.to_s"
},
{
"path": "src/charms/int32_extensions.cr",
"chars": 131,
"preview": "require \"../lucky/allowed_in_tags\"\n\nstruct Int32\n include ::Lucky::AllowedInTags\n\n def to_param : String\n self.to_s"
},
{
"path": "src/charms/int64_extensions.cr",
"chars": 131,
"preview": "require \"../lucky/allowed_in_tags\"\n\nstruct Int64\n include ::Lucky::AllowedInTags\n\n def to_param : String\n self.to_s"
},
{
"path": "src/charms/object.cr",
"chars": 591,
"preview": "class Object\n include ::Lucky::QuickDef\n include ::Lucky::Memoizable\n\n def blank? : Bool\n if self.responds_to?(:em"
},
{
"path": "src/charms/request_extensions.cr",
"chars": 305,
"preview": "class HTTP::Request\n # This is an alternative to `remote_address`\n # since that casts to `Socket::Address`, and not al"
},
{
"path": "src/charms/static_file_handler.cr",
"chars": 153,
"preview": "require \"http/server\"\n\nclass Lucky::StaticFileHandler < HTTP::StaticFileHandler\n def call(context : HTTP::Server::Conte"
},
{
"path": "src/charms/string_extensions.cr",
"chars": 1244,
"preview": "class String\n def to_param : String\n self\n end\n\n # Returns a new string with whitespace and newlines squish to a s"
},
{
"path": "src/charms/uuid_extensions.cr",
"chars": 94,
"preview": "struct UUID\n include ::Lucky::AllowedInTags\n\n def to_param : String\n self.to_s\n end\nend\n"
},
{
"path": "src/lucky/action.cr",
"chars": 610,
"preview": "require \"./*\"\n\nabstract class Lucky::Action\n getter context : HTTP::Server::Context\n getter route_params : Hash(String"
},
{
"path": "src/lucky/action_delegates.cr",
"chars": 136,
"preview": "# :nodoc:\nmodule Lucky::ActionDelegates\n macro included\n delegate flash, cookies, session, response, request, to: co"
},
{
"path": "src/lucky/action_pipes.cr",
"chars": 5685,
"preview": "module Lucky::ActionPipes\n # :nodoc:\n class Continue\n end\n\n # Skips before or after pipes\n #\n # ```\n # skip requi"
},
{
"path": "src/lucky/allowed_in_tags.cr",
"chars": 582,
"preview": "# Include this module in a type to allow it to be output in tags\n#\n# Lucky already includes this in a few common types l"
},
{
"path": "src/lucky/asset_helpers.cr",
"chars": 4012,
"preview": "# Methods for returning the path to assets\n#\n# These methods will return fingerprinted paths, check assets at compile ti"
},
{
"path": "src/lucky/assignable.cr",
"chars": 3798,
"preview": "module Lucky::Assignable\n # Declare what a class needs in order to be initialized.\n #\n # This will declare an instanc"
},
{
"path": "src/lucky/base_app_server.cr",
"chars": 451,
"preview": "# The Base class for creating an app server in Lucky\nabstract class Lucky::BaseAppServer\n private getter server\n\n abst"
},
{
"path": "src/lucky/base_component.cr",
"chars": 958,
"preview": "require \"./html_builder\"\n\nabstract class Lucky::BaseComponent\n include Lucky::HTMLBuilder\n\n macro inherited\n # Retu"
},
{
"path": "src/lucky/base_http_client.cr",
"chars": 4615,
"preview": "require \"http/client\"\n\n# A client for making HTTP requests\n#\n# Makes it easy to pass params, use Lucky route helpers, an"
},
{
"path": "src/lucky/base_log_formatter.cr",
"chars": 192,
"preview": "abstract class Lucky::BaseLogFormatter\n abstract def format(\n severity : ::Logger::Severity,\n timestamp : Time,\n "
},
{
"path": "src/lucky/context_extensions.cr",
"chars": 1301,
"preview": "class HTTP::Server::Context\n # :nodoc:\n #\n # This is used to store the client's accepted/desired format\n # That way "
},
{
"path": "src/lucky/cookies/cookie_jar.cr",
"chars": 4363,
"preview": "class Lucky::CookieJar\n MAX_COOKIE_SIZE = 4096\n LUCKY_ENCRYPTION_PREFIX = Base64.strict_encode(\"lucky\") + \"--\""
},
{
"path": "src/lucky/cookies/flash_store.cr",
"chars": 1371,
"preview": "class Lucky::FlashStore\n SESSION_KEY = \"_flash\"\n alias Key = String | Symbol\n\n private getter flashes = {} of String "
},
{
"path": "src/lucky/cookies/session.cr",
"chars": 780,
"preview": "class Lucky::Session\n alias Key = String | Symbol\n private property store = {} of String => String\n\n delegate to_json"
},
{
"path": "src/lucky/data_response.cr",
"chars": 2237,
"preview": "# Return a data for the request.\n#\n# `data` can be used to return contents of the IO as a file to the browser, or\n# rend"
},
{
"path": "src/lucky/dev_asset_cache_handler.cr",
"chars": 741,
"preview": "# Makes sure browser cache for assets is busted at every request in development.\nclass Lucky::DevAssetCacheHandler\n inc"
},
{
"path": "src/lucky/enforce_underscored_route.cr",
"chars": 880,
"preview": "# Include this in your actions to enforce underscores are used in your paths\n#\n# This is purely to help maintain consist"
},
{
"path": "src/lucky/error_action.cr",
"chars": 2655,
"preview": "require \"./*\"\n\nabstract class Lucky::ErrorAction\n include Lucky::ActionDelegates\n include Lucky::ParamHelpers\n includ"
},
{
"path": "src/lucky/error_handler.cr",
"chars": 639,
"preview": "class Lucky::ErrorHandler\n include HTTP::Handler\n\n Habitat.create do\n setting show_debug_output : Bool\n setting "
},
{
"path": "src/lucky/errors.cr",
"chars": 5698,
"preview": "module Lucky\n # = Lucky Errors\n #\n # Generic Lucky Record exception class.\n class Error < Exception\n end\n\n # Raise"
},
{
"path": "src/lucky/events/pipe_event.cr",
"chars": 351,
"preview": "class Lucky::Events::PipeEvent < Pulsar::Event\n getter :name, :position, :continued\n\n enum Position\n Before\n Aft"
},
{
"path": "src/lucky/events/request_complete_event.cr",
"chars": 141,
"preview": "class Lucky::Events::RequestCompleteEvent < Pulsar::Event\n getter duration : Time::Span\n\n def initialize(@duration : T"
},
{
"path": "src/lucky/exception_page.cr",
"chars": 7026,
"preview": "class Lucky::ExceptionPage < ExceptionPage\n def styles : ExceptionPage::Styles\n Styles.new(accent: lucky_green, logo"
},
{
"path": "src/lucky/exposable.cr",
"chars": 3001,
"preview": "module Lucky::Exposable\n macro included\n EXPOSURES = [] of Symbol\n\n macro inherited\n EXPOSURES = [] of Symbo"
},
{
"path": "src/lucky/file_response.cr",
"chars": 3691,
"preview": "# Return a file's contents for the request.\n#\n# `file` can be used to return a file and it's contents to the browser, or"
},
{
"path": "src/lucky/force_ssl_handler.cr",
"chars": 2910,
"preview": "# Redirects HTTP requests to HTTPS\n#\n# Uses the `X-Forwarded-Proto` header to determine whether the request was\n# made s"
},
{
"path": "src/lucky/form_data.cr",
"chars": 1049,
"preview": "class Lucky::FormData\n getter params = MultiValueStorage(String).new\n getter files = MultiValueStorage(Lucky::Uploaded"
},
{
"path": "src/lucky/form_data_parser.cr",
"chars": 463,
"preview": "# :nodoc:\nclass Lucky::FormDataParser\n getter body : String\n getter request : HTTP::Request\n\n def initialize(@body : "
},
{
"path": "src/lucky/format.cr",
"chars": 3254,
"preview": "# Format enum for handling different content types and file extensions\nenum Lucky::Format\n Html\n Json\n Xml\n Csv\n Js"
},
{
"path": "src/lucky/format_registry.cr",
"chars": 2104,
"preview": "# Registry for custom formats that extends the built-in Format enum\nmodule Lucky::FormatRegistry\n # Storage for custom "
},
{
"path": "src/lucky/html_builder.cr",
"chars": 804,
"preview": "require \"./tags/**\"\nrequire \"./page_helpers/**\"\nrequire \"./mount_component\"\n\nmodule Lucky::HTMLBuilder\n include Lucky::"
},
{
"path": "src/lucky/html_page.cr",
"chars": 276,
"preview": "require \"./html_builder\"\n\nmodule Lucky::HTMLPage\n include Lucky::HTMLBuilder\n\n Habitat.create do\n setting render_co"
},
{
"path": "src/lucky/http_method_override_handler.cr",
"chars": 676,
"preview": "class Lucky::HttpMethodOverrideHandler\n include HTTP::Handler\n\n def call(context : HTTP::Server::Context)\n http_met"
},
{
"path": "src/lucky/json_body_parser.cr",
"chars": 386,
"preview": "# :nodoc:\nclass Lucky::JsonBodyParser\n getter body : String\n getter request : HTTP::Request\n\n def initialize(@body : "
},
{
"path": "src/lucky/log_handler.cr",
"chars": 1842,
"preview": "require \"colorize\"\n\nclass Lucky::LogHandler\n include HTTP::Handler\n # These constants are used here and in the PrettyL"
},
{
"path": "src/lucky/logger_helpers.cr",
"chars": 765,
"preview": "module Lucky::LoggerHelpers\n def self.colored_http_status(status_code : Int32) : String\n http_status = HTTP::Status."
}
]
// ... and 91 more files (download for full content)
About this extraction
This page contains the full source code of the luckyframework/web GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 291 files (894.9 KB), approximately 264.1k tokens, and a symbol index with 38 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.