From c0d9a7b400724195c642b8e036a4e7ad66abbb3d Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:01:25 +0200 Subject: [PATCH 01/19] Bump Ameba to the latest version (1.7.0-dev) --- shard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shard.yml b/shard.yml index f31d587cd..230b4d7c3 100644 --- a/shard.yml +++ b/shard.yml @@ -58,6 +58,6 @@ development_dependencies: version: ~> 0.3.0 ameba: github: crystal-ameba/ameba - version: ~> 1.6.4 + branch: master license: MIT From 60ae39b74c098633802bbfe19a93041c214b0a8f Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:02:04 +0200 Subject: [PATCH 02/19] Tweak Ameba configuration --- .ameba.yml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/.ameba.yml b/.ameba.yml index 82c244930..f143102ea 100644 --- a/.ameba.yml +++ b/.ameba.yml @@ -1,24 +1,7 @@ -# 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 + Enabled: true 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 From 8eb6f8863df10aca76305a9174160fc68442b4c4 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:02:16 +0200 Subject: [PATCH 03/19] Add Ameba CI workflow --- .github/workflows/ameba.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/ameba.yml diff --git a/.github/workflows/ameba.yml b/.github/workflows/ameba.yml new file mode 100644 index 000000000..013377843 --- /dev/null +++ b/.github/workflows/ameba.yml @@ -0,0 +1,19 @@ +name: Ameba + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Download source + uses: actions/checkout@v7 + + - name: Run Ameba Linter + uses: crystal-ameba/github-action@master From 7473cb8b1e74d2616423dce3f668c84eb3df471d Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:03:11 +0200 Subject: [PATCH 04/19] =?UTF-8?q?Remove=20`check=5Fformat`=20job=20as=20it?= =?UTF-8?q?=E2=80=99s=20redundant=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62889e8ad..5e5713e7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,25 +4,9 @@ on: push: branches: [main] pull_request: - branches: "*" + 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 From ebb1b996daa13a0ed7e2d5c9fbec615a51af56ff Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:04:16 +0200 Subject: [PATCH 05/19] Fix `Style/HeredocIndent` type of Ameba issues --- bin/lucky.gen.action.cr | 12 +-- spec/lucky/base_http_client_spec.cr | 4 +- spec/lucky/base_tags_spec.cr | 4 +- spec/lucky/component_spec.cr | 4 +- spec/lucky/cookies/flash_store_spec.cr | 8 +- spec/lucky/forgery_protection_helpers_spec.cr | 12 +-- spec/lucky/form_helpers_spec.cr | 60 ++++++------- spec/lucky/link_helpers_spec.cr | 36 ++++---- spec/lucky/specialty_tags_spec.cr | 48 +++++------ src/lucky/action_pipes.cr | 6 +- src/lucky/asset_helpers.cr | 22 ++--- src/lucky/enforce_underscored_route.cr | 28 +++---- src/lucky/errors.cr | 50 +++++------ src/lucky/exposable.cr | 12 +-- src/lucky/mount_component.cr | 40 ++++----- src/lucky/renderable.cr | 58 ++++++------- src/lucky/request_expectations.cr | 18 ++-- src/lucky/request_type_helpers.cr | 20 ++--- src/lucky/routable.cr | 10 +-- src/lucky/route_inferrer.cr | 2 +- src/lucky/secure_headers/set_frame_guard.cr | 10 +-- src/lucky/server_settings.cr | 10 +-- src/lucky/tags/_check_tag_content.cr | 12 +-- src/lucky/tags/bun_reload_tag.cr | 84 +++++++++---------- src/lucky/tags/link_helpers.cr | 44 +++++----- src/lucky/tags/tag_defaults.cr | 26 +++--- src/lucky/verify_accepts_format.cr | 14 ++-- src/lucky/welcome_page.cr | 4 +- src/run_macros/asset_manifest_builder.cr | 34 ++++---- tasks/exec.cr | 16 ++-- tasks/gen/action/api.cr | 8 +- tasks/gen/action/browser.cr | 12 +-- tasks/gen/component.cr | 8 +- tasks/gen/page.cr | 8 +- tasks/gen/task.cr | 18 ++-- tasks/routes.cr | 30 +++---- tasks/watch.cr | 12 +-- 37 files changed, 402 insertions(+), 402 deletions(-) diff --git a/bin/lucky.gen.action.cr b/bin/lucky.gen.action.cr index 43a3a11c9..6c0da41b9 100644 --- a/bin/lucky.gen.action.cr +++ b/bin/lucky.gen.action.cr @@ -1,15 +1,15 @@ require "colorize" puts <<-ERROR - Missing 'browser' or 'api' after 'gen.action' + Missing 'browser' or 'api' after 'gen.action' - For actions used in a browser (HTML, redirects)... + For actions used in a browser (HTML, redirects)... - #{"lucky gen.action.browser".colorize.green.bold} + #{"lucky gen.action.browser".colorize.green.bold} - For an API endpoint (JSON, XML, GraphQL)... + For an API endpoint (JSON, XML, GraphQL)... - #{"lucky gen.action.api".colorize.green.bold} + #{"lucky gen.action.api".colorize.green.bold} - ERROR + ERROR diff --git a/spec/lucky/base_http_client_spec.cr b/spec/lucky/base_http_client_spec.cr index fdc0160b0..a8ba04edc 100644 --- a/spec/lucky/base_http_client_spec.cr +++ b/spec/lucky/base_http_client_spec.cr @@ -103,7 +103,7 @@ describe Lucky::BaseHTTPClient do { "event_id": "1"} { "type": "event"} { "event_id": "2", "type": "event", "platform": ""} - JSON + JSON response = MyClient.new.exec_raw(HelloWorldAction, test_data) request = TestServer.last_request @@ -117,7 +117,7 @@ describe Lucky::BaseHTTPClient do { "event_id": "1"} { "type": "event"} { "event_id": "2", "type": "event", "platform": ""} - JSON + JSON response = MyClient.new.exec_raw(HelloWorldAction.route, test_data) request = TestServer.last_request diff --git a/spec/lucky/base_tags_spec.cr b/spec/lucky/base_tags_spec.cr index afe5bd65e..779fa6301 100644 --- a/spec/lucky/base_tags_spec.cr +++ b/spec/lucky/base_tags_spec.cr @@ -54,8 +54,8 @@ describe Lucky::BaseTags do describe "#style" do it "renders a style tag" do view(&.style("body { font-size: 2em; }")).should contain <<-HTML - - HTML + + HTML end end end diff --git a/spec/lucky/component_spec.cr b/spec/lucky/component_spec.cr index 8e5369b63..9f54dc5c8 100644 --- a/spec/lucky/component_spec.cr +++ b/spec/lucky/component_spec.cr @@ -171,8 +171,8 @@ describe "components rendering" do it "uses context from being mounted" do contents = TestMountPage.new(context_with_csrf).render.to_s contents.should contain <<-HTML - input type="hidden" name="_csrf" - HTML + input type="hidden" name="_csrf" + HTML end end diff --git a/spec/lucky/cookies/flash_store_spec.cr b/spec/lucky/cookies/flash_store_spec.cr index d2ac90c83..1419fa775 100644 --- a/spec/lucky/cookies/flash_store_spec.cr +++ b/spec/lucky/cookies/flash_store_spec.cr @@ -10,11 +10,11 @@ describe Lucky::FlashStore do it "raises an error when flash JSON is invalid" do message = <<-MESSAGE - The flash messages (stored as JSON) failed to parse in a JSON parser. - Here's what it tries to parse: + The flash messages (stored as JSON) failed to parse in a JSON parser. + Here's what it tries to parse: - not_valid_json=invalid - MESSAGE + not_valid_json=invalid + MESSAGE expect_raises(Lucky::InvalidFlashJSONError, message) do Lucky::FlashStore.from_session(build_invalid_session) diff --git a/spec/lucky/forgery_protection_helpers_spec.cr b/spec/lucky/forgery_protection_helpers_spec.cr index 933ba5a04..ff3d9e6f0 100644 --- a/spec/lucky/forgery_protection_helpers_spec.cr +++ b/spec/lucky/forgery_protection_helpers_spec.cr @@ -15,8 +15,8 @@ describe Lucky::ForgeryProtectionHelpers do context.session.set(Lucky::ProtectFromForgery::SESSION_KEY, "my_token") view(context, &.csrf_hidden_input).should contain <<-HTML - - HTML + + HTML end it "renders a meta tag for Rails UJS (and other JS that may need it)" do @@ -25,11 +25,11 @@ describe Lucky::ForgeryProtectionHelpers do rendered = view(context, &.csrf_meta_tags) rendered.should contain <<-HTML - - HTML + + HTML rendered.should contain <<-HTML - - HTML + + HTML end end diff --git a/spec/lucky/form_helpers_spec.cr b/spec/lucky/form_helpers_spec.cr index 0c2d110bf..f180bf827 100644 --- a/spec/lucky/form_helpers_spec.cr +++ b/spec/lucky/form_helpers_spec.cr @@ -73,34 +73,34 @@ describe Lucky::FormHelpers do it "renders a form tag" do without_csrf_protection do view(&.inferred_put_form).should contain <<-HTML -
foo
- HTML +
foo
+ HTML view(&.inferred_post_form).should contain <<-HTML -
foo
- HTML +
foo
+ HTML view(&.inferred_get_form).should contain <<-HTML -
foo
- HTML +
foo
+ HTML view(&.form_with_html_options).should contain <<-HTML -
foo
- HTML +
foo
+ HTML form = view(&.form_for(FormHelpers::Index) { }) form.should contain <<-HTML -
- HTML +
+ HTML form = view(&.form_for(FormHelpers::Index, class: "form-block") { }) form.should contain <<-HTML -
- HTML +
+ HTML view(&.form_with_bool_attr).should contain <<-HTML -
foo
- HTML +
foo
+ HTML form = view do |page| page.form_wrapper(FormHelpers::Create) do @@ -109,8 +109,8 @@ describe Lucky::FormHelpers do end form.should contain <<-HTML -
purple
- HTML +
purple
+ HTML end end @@ -121,40 +121,40 @@ describe Lucky::FormHelpers do form = view(context_with_csrf, &.form_for(FormHelpers::Index) { }) form.should contain <<-HTML -
- HTML +
+ HTML end it "converts the multipart argument" do without_csrf_protection do view(&.form_with_multipart).should contain <<-HTML -
foo
- HTML +
foo
+ HTML view(&.form_with_multipart_false).should contain <<-HTML -
foo
- HTML +
foo
+ HTML end end it "renders submit input" do view(&.submit("Save")).should contain <<-HTML - - HTML + + HTML view(&.submit("Save", class: "cool")).should contain <<-HTML - - HTML + + HTML end it "renders submit input with attributes" do view(&.submit("Save", attrs: [:disabled])).should contain <<-HTML - - HTML + + HTML view(&.submit("Save", class: "cool", attrs: [:hidden, :disabled])).should contain <<-HTML - - HTML + + HTML end end diff --git a/spec/lucky/link_helpers_spec.cr b/spec/lucky/link_helpers_spec.cr index 7a68dd07a..cb5bf21f4 100644 --- a/spec/lucky/link_helpers_spec.cr +++ b/spec/lucky/link_helpers_spec.cr @@ -61,26 +61,26 @@ describe Lucky::LinkHelpers do it "renders a link tag with an action" do view(&.link("Test", to: LinkHelpers::Index)).should contain <<-HTML - Test - HTML + Test + HTML link = view(&.link(to: LinkHelpers::Index, class: "link") { }) link.should contain <<-HTML - - HTML + + HTML end it "renders a link tag with a block" do view(&.http_get_route_with_block).should contain <<-HTML - Hello - HTML + Hello + HTML end it "renders a link tag without text" do view(&.http_get_route_without_text).should contain <<-HTML - - HTML + + HTML end it "renders a link with uuid" do @@ -90,26 +90,26 @@ describe Lucky::LinkHelpers do it "renders a link with a special data attribute" do view(&.link(to: LinkHelpers::Index, "data-is-useless": true)).should contain <<-HTML - - HTML + + HTML view(&.link(to: LinkHelpers::Index, "data-num": 4)).should contain <<-HTML - - HTML + + HTML end it "renders a link with boolean attrs" do view(&.http_get_route_with_text_and_attrs).should contain <<-HTML - Text - HTML + Text + HTML view(&.http_get_route_with_attrs_no_text).should contain <<-HTML - - HTML + + HTML view(&.http_get_route_with_block_and_attrs).should contain <<-HTML - Hello - HTML + Hello + HTML end end diff --git a/spec/lucky/specialty_tags_spec.cr b/spec/lucky/specialty_tags_spec.cr index bd74b5a08..395535bbd 100644 --- a/spec/lucky/specialty_tags_spec.cr +++ b/spec/lucky/specialty_tags_spec.cr @@ -12,18 +12,18 @@ end describe Lucky::SpecialtyTags do it "renders doctype" do view(&.html_doctype).should contain <<-HTML - - HTML + + HTML end it "renders css link tag" do view(&.css_link("app.css")).should eq <<-HTML - - HTML + + HTML view(&.css_link("app.css", rel: "preload", media: "print")).should eq <<-HTML - - HTML + + HTML end it "cache-busts non-fingerprinted local css links" do @@ -58,38 +58,38 @@ describe Lucky::SpecialtyTags do it "renders js link tag" do view(&.js_link("app.js")).should contain <<-HTML - - HTML + + HTML view(&.js_link("app.js", foo: "bar")).should contain <<-HTML - - HTML + + HTML end it "render utf8 meta tag" do view(&.utf8_charset).should contain <<-HTML - - HTML + + HTML end it "renders responsive meta tag" do view(&.responsive_meta_tag).should contain <<-HTML - - HTML + + HTML view(&.responsive_meta_tag(width: 600)).should contain <<-HTML - - HTML + + HTML view(&.responsive_meta_tag(height: 600)).should contain <<-HTML - - HTML + + HTML end it "renders canonical link tag" do view(&.canonical_link("https://it.is/here")).should contain <<-HTML - - HTML + + HTML end it "renders bun reload script in development" do @@ -117,12 +117,12 @@ describe Lucky::SpecialtyTags do it "renders proper non-breaking space entity" do view(&.nbsp).should contain <<-HTML -   - HTML +   + HTML view(&.nbsp(3)).should contain <<-HTML -     - HTML +     + HTML end end diff --git a/src/lucky/action_pipes.cr b/src/lucky/action_pipes.cr index 889853749..040025839 100644 --- a/src/lucky/action_pipes.cr +++ b/src/lucky/action_pipes.cr @@ -15,9 +15,9 @@ module Lucky::ActionPipes {% AFTER_PIPES[pipe.id] = false %} {% else %} {% pipe.raise <<-ERROR.lines.join(" ") - Can't skip '#{pipe}' because the pipe is not used. - Check the spelling of the pipe that you are trying to skip. - ERROR + Can't skip '#{pipe}' because the pipe is not used. + Check the spelling of the pipe that you are trying to skip. + ERROR %} {% end %} {% end %} diff --git a/src/lucky/asset_helpers.cr b/src/lucky/asset_helpers.cr index 293309b87..700054b4c 100644 --- a/src/lucky/asset_helpers.cr +++ b/src/lucky/asset_helpers.cr @@ -68,26 +68,26 @@ module Lucky::AssetHelpers {% end %} {% elsif path.is_a?(StringInterpolation) %} {% raise <<-ERROR - \n - The 'asset' macro doesn't work with string interpolation + \n + The 'asset' macro doesn't work with string interpolation - Try this... + Try this... - ▸ Use the 'dynamic_asset' method instead + ▸ Use the 'dynamic_asset' method instead - ERROR + ERROR %} {% else %} {% raise <<-ERROR - \n - The 'asset' macro requires a literal string like "my-logo.png", instead got: #{path} + \n + The 'asset' macro requires a literal string like "my-logo.png", instead got: #{path} - Try this... + Try this... - ▸ If you're using a variable, switch to a literal string - ▸ If you can't use a literal string, use the 'dynamic_asset' method instead + ▸ If you're using a variable, switch to a literal string + ▸ If you can't use a literal string, use the 'dynamic_asset' method instead - ERROR + ERROR %} {% end %} end diff --git a/src/lucky/enforce_underscored_route.cr b/src/lucky/enforce_underscored_route.cr index ff2a8b9a1..354ab5818 100644 --- a/src/lucky/enforce_underscored_route.cr +++ b/src/lucky/enforce_underscored_route.cr @@ -6,28 +6,28 @@ module Lucky::EnforceUnderscoredRoute macro enforce_route_style(path, action) {% if path.includes?("-") %} {% raise <<-ERROR - #{path} defined in '#{action}' should use an underscore. + #{path} defined in '#{action}' should use an underscore. - In '#{action}' + In '#{action}' - ▸ Change #{path} - ▸ To #{path.gsub(/-/, "_")} + ▸ Change #{path} + ▸ To #{path.gsub(/-/, "_")} - Or, skip the style check for this action + Or, skip the style check for this action - class #{action} - + include Lucky::SkipRouteStyleCheck - end + class #{action} + + include Lucky::SkipRouteStyleCheck + end - Or, skip checking all actions by removing 'Lucky::EnforceUnderscoredRoute' + Or, skip checking all actions by removing 'Lucky::EnforceUnderscoredRoute' - # Remove from both BrowserAction and ApiAction - class BrowserAction/ApiAction - - include Lucky::EnforceUnderscoredRoute - end + # Remove from both BrowserAction and ApiAction + class BrowserAction/ApiAction + - include Lucky::EnforceUnderscoredRoute + end - ERROR + ERROR %} {% end %} end diff --git a/src/lucky/errors.cr b/src/lucky/errors.cr index 67f6dbed9..44508f0ec 100644 --- a/src/lucky/errors.cr +++ b/src/lucky/errors.cr @@ -40,21 +40,21 @@ module Lucky def initialize(@request : HTTP::Request) accept_header = request.headers["accept"]? super <<-TEXT - Lucky couldn't figure out what format the client accepts. + Lucky couldn't figure out what format the client accepts. - The client's Accept header: '#{accept_header}' + The client's Accept header: '#{accept_header}' - You can teach Lucky how to handle this header: + You can teach Lucky how to handle this header: - #{"# Add this in config/mime_types.cr".colorize.dim} - Lucky::MimeType.register "#{accept_header}", :custom_format + #{"# Add this in config/mime_types.cr".colorize.dim} + Lucky::MimeType.register "#{accept_header}", :custom_format - Or use one of these headers Lucky knows about: + Or use one of these headers Lucky knows about: - #{Lucky::MimeType.known_accept_headers.join(", ")} + #{Lucky::MimeType.known_accept_headers.join(", ")} - TEXT + TEXT end def renderable_status : Int32 @@ -73,17 +73,17 @@ module Lucky def initialize(@request : HTTP::Request, action_name : String, format : Symbol, accepted_formats : Array(Symbol)) super <<-TEXT - The request wants :#{format}, but #{action_name} does not accept it. + The request wants :#{format}, but #{action_name} does not accept it. - Accepted formats: #{accepted_formats.map(&.to_s).join(", ")} + Accepted formats: #{accepted_formats.map(&.to_s).join(", ")} - Try this... + Try this... - ▸ Add :#{format} to 'accepted_formats' in #{action_name} or its parent class. - ▸ Make your request using one of the accepted formats. + ▸ Add :#{format} to 'accepted_formats' in #{action_name} or its parent class. + ▸ Make your request using one of the accepted formats. - TEXT + TEXT end def renderable_status : Int32 @@ -132,14 +132,14 @@ module Lucky private def _message <<-ERROR - Cookie value for '#{key}' is invalid. + Cookie value for '#{key}' is invalid. - Be sure the value does not contain any blank characters, - comma, double quote, semicolon, or double backslash. + Be sure the value does not contain any blank characters, + comma, double quote, semicolon, or double backslash. - See https://tools.ietf.org/html/rfc6265#section-4.1.1 for valid - characters - ERROR + See https://tools.ietf.org/html/rfc6265#section-4.1.1 for valid + characters + ERROR end end @@ -232,11 +232,11 @@ module Lucky private def _message <<-MESSAGE - The flash messages (stored as JSON) failed to parse in a JSON parser. - Here's what it tries to parse: + The flash messages (stored as JSON) failed to parse in a JSON parser. + Here's what it tries to parse: - #{bad_json} - MESSAGE + #{bad_json} + MESSAGE end end @@ -253,7 +253,7 @@ module Lucky <<-MESSAGE Expected subdomain matcher(s): #{@expected} Did not match host: #{@host} - MESSAGE + MESSAGE end end end diff --git a/src/lucky/exposable.cr b/src/lucky/exposable.cr index 4e59d14f6..633f16bb7 100644 --- a/src/lucky/exposable.cr +++ b/src/lucky/exposable.cr @@ -111,14 +111,14 @@ module Lucky::Exposable {% if method_name_str.ends_with?('?') || method_name_str.ends_with?('!') %} {% method_name.raise <<-ERROR - Methods ending in '?' or '!' cannot be exposed to pages. - #{@type.name} called `expose #{method_name_str.id}` + Methods ending in '?' or '!' cannot be exposed to pages. + #{@type.name} called `expose #{method_name_str.id}` - Try this... + Try this... - ▸ Define your method without ? or ! then... - ▸ expose #{method_name_str.gsub(/[!?]$/, "").id} - ERROR + ▸ Define your method without ? or ! then... + ▸ expose #{method_name_str.gsub(/[!?]$/, "").id} + ERROR %} {% end %} {% EXPOSURES << method_name.id %} diff --git a/src/lucky/mount_component.cr b/src/lucky/mount_component.cr index 4794fd8c5..68c3adfb8 100644 --- a/src/lucky/mount_component.cr +++ b/src/lucky/mount_component.cr @@ -38,52 +38,52 @@ module Lucky::MountComponent # :nodoc: def mount(_component : Lucky::BaseComponent, *args, **named_args) : Nil {% raise <<-ERROR - 'mount' requires a component class, not an instance of a component. + 'mount' requires a component class, not an instance of a component. - Try this... + Try this... - ▸ mount MyComponent - ▸ mount_instance MyComponent.new - ERROR + ▸ mount MyComponent + ▸ mount_instance MyComponent.new + ERROR %} end # :nodoc: def mount(_component : Lucky::BaseComponent, *args, **named_args, &) : Nil {% raise <<-ERROR - 'mount' requires a component class, not an instance of a component. + 'mount' requires a component class, not an instance of a component. - Try this... + Try this... - ▸ mount MyComponent - ▸ mount_instance MyComponent.new - ERROR + ▸ mount MyComponent + ▸ mount_instance MyComponent.new + ERROR %} end # :nodoc: def mount_instance(_component : Lucky::BaseComponent.class) : Nil {% raise <<-ERROR - 'mount_instance' requires an instance of a component, not component class. + 'mount_instance' requires an instance of a component, not component class. - Try this... + Try this... - ▸ mount MyComponent - ▸ mount_instance MyComponent.new - ERROR + ▸ mount MyComponent + ▸ mount_instance MyComponent.new + ERROR %} end # :nodoc: def mount_instance(_component : Lucky::BaseComponent.class, &) : Nil {% raise <<-ERROR - 'mount_instance' requires an instance of a component, not component class. + 'mount_instance' requires an instance of a component, not component class. - Try this... + Try this... - ▸ mount MyComponent - ▸ mount_instance MyComponent.new - ERROR + ▸ mount MyComponent + ▸ mount_instance MyComponent.new + ERROR %} end diff --git a/src/lucky/renderable.cr b/src/lucky/renderable.cr index 1f2e59be6..439edab7f 100644 --- a/src/lucky/renderable.cr +++ b/src/lucky/renderable.cr @@ -141,27 +141,27 @@ module Lucky::Renderable raise <<-ERROR - An action returned Nil + An action returned Nil - But it should return a Lucky::Response. + But it should return a Lucky::Response. - Try this... + Try this... - ▸ Return a response with html, redirect, or json at the end of your action. - ▸ Ensure all conditionals (like if/else) return a response with html, redirect, json, etc. + ▸ Return a response with html, redirect, or json at the end of your action. + ▸ Ensure all conditionals (like if/else) return a response with html, redirect, json, etc. - For example... + For example... - get "/admin/users" do - # Make sure there is a response in all conditional branches - if current_user.admin? - html IndexPage, users: UserQuery.new - else - redirect Home::Index + get "/admin/users" do + # Make sure there is a response in all conditional branches + if current_user.admin? + html IndexPage, users: UserQuery.new + else + redirect Home::Index + end end - end - ERROR + ERROR %} end @@ -170,23 +170,23 @@ module Lucky::Renderable raise <<-ERROR - An action returned #{T} + An action returned #{T} - But it should return a Lucky::Response + But it should return a Lucky::Response - Try this... + Try this... - ▸ Return a response with html, redirect, or json at the end of your action. - ▸ Ensure all conditionals (like if/else) return a response with html, redirect, json, etc. + ▸ Return a response with html, redirect, or json at the end of your action. + ▸ Ensure all conditionals (like if/else) return a response with html, redirect, json, etc. - For example... + For example... - get "/users" do - # Return a response with json, redirect, html, etc. - html IndexPage, users: UserQuery.new - end + get "/users" do + # Return a response with json, redirect, html, etc. + html IndexPage, users: UserQuery.new + end - ERROR + ERROR %} end @@ -290,13 +290,13 @@ module Lucky::Renderable {% raise <<-ERROR - Looks like your trying to pass a string to json response. + Looks like your trying to pass a string to json response. - Use `raw_json(body, ...)` instead. + Use `raw_json(body, ...)` instead. - NOTE: `raw_json` doesn't validate JSON string validity/integrity, use at your own risk. + NOTE: `raw_json` doesn't validate JSON string validity/integrity, use at your own risk. - ERROR + ERROR %} end diff --git a/src/lucky/request_expectations.cr b/src/lucky/request_expectations.cr index 2baa054b4..f55dbd37f 100644 --- a/src/lucky/request_expectations.cr +++ b/src/lucky/request_expectations.cr @@ -59,22 +59,22 @@ module Lucky::RequestExpectations expected_json.as_h.each { |expected_key, expected_value| if !actual_json.has_key?(expected_key) break <<-TEXT - Expected response to have JSON key #{expected_key.dump}, but it was not present. + Expected response to have JSON key #{expected_key.dump}, but it was not present. - Response keys: #{actual_json.keys.map(&.dump).join(", ")} - TEXT + Response keys: #{actual_json.keys.map(&.dump).join(", ")} + TEXT elsif actual_json[expected_key]? != expected_value break <<-TEXT - JSON response was incorrect. + JSON response was incorrect. - Expected #{expected_key.dump} to be: + Expected #{expected_key.dump} to be: - #{expected_value.inspect} + #{expected_value.inspect} - Instead got: + Instead got: - #{actual_json[expected_key].inspect} - TEXT + #{actual_json[expected_key].inspect} + TEXT end }.to_s end diff --git a/src/lucky/request_type_helpers.cr b/src/lucky/request_type_helpers.cr index 0fe043e5c..e13d27af8 100644 --- a/src/lucky/request_type_helpers.cr +++ b/src/lucky/request_type_helpers.cr @@ -4,22 +4,22 @@ module Lucky::RequestTypeHelpers private def default_format {% raise <<-TEXT - Must set 'accepted_formats' or 'default_format' in #{@type} (or its parent class). + Must set 'accepted_formats' or 'default_format' in #{@type} (or its parent class). - Example of 'accepted_formats' (recommended): + Example of 'accepted_formats' (recommended): - abstract class MyBaseAction < Lucky::Action - accepted_formats [:html, :json], default: :html - end + abstract class MyBaseAction < Lucky::Action + accepted_formats [:html, :json], default: :html + end - Example of 'default_format' (typically used only in Errors::Show): + Example of 'default_format' (typically used only in Errors::Show): - class Errors::Show < Lucky::ErrorAction - default_format :html - end + class Errors::Show < Lucky::ErrorAction + default_format :html + end - TEXT + TEXT %} end diff --git a/src/lucky/routable.cr b/src/lucky/routable.cr index efbe5fe42..52feb978a 100644 --- a/src/lucky/routable.cr +++ b/src/lucky/routable.cr @@ -149,14 +149,14 @@ module Lucky::Routable {% if already_used_route = NORMALIZED_ROUTES[normalized_key] %} {% raise <<-ERROR - #{original_path} in '#{@type.name}' collides with the path in '#{already_used_route[:action]}' + #{original_path} in '#{@type.name}' collides with the path in '#{already_used_route[:action]}' - Try this... + Try this... - ▸ Change the paths in one of the actions to something unique - ▸ Run `lucky routes` to verify all of your route paths + ▸ Change the paths in one of the actions to something unique + ▸ Run `lucky routes` to verify all of your route paths - ERROR + ERROR %} {% else %} {% NORMALIZED_ROUTES[normalized_key] = { diff --git a/src/lucky/route_inferrer.cr b/src/lucky/route_inferrer.cr index ce177bab0..a1c6824bf 100644 --- a/src/lucky/route_inferrer.cr +++ b/src/lucky/route_inferrer.cr @@ -79,7 +79,7 @@ class Lucky::RouteInferrer Expected something like: #{examples} - ERROR + ERROR end private def parent_resource_pieces : Array(String) diff --git a/src/lucky/secure_headers/set_frame_guard.cr b/src/lucky/secure_headers/set_frame_guard.cr index e9eba2e5c..b4621c983 100644 --- a/src/lucky/secure_headers/set_frame_guard.cr +++ b/src/lucky/secure_headers/set_frame_guard.cr @@ -44,12 +44,12 @@ module Lucky else raise <<-MESSAGE - You set frame_guard_value to #{value}, but it must be one of these options: + You set frame_guard_value to #{value}, but it must be one of these options: - - "sameorigin" - - "deny" - - A valid URL - MESSAGE + - "sameorigin" + - "deny" + - A valid URL + MESSAGE end end end diff --git a/src/lucky/server_settings.cr b/src/lucky/server_settings.cr index 1200737fd..8f3e4ef44 100644 --- a/src/lucky/server_settings.cr +++ b/src/lucky/server_settings.cr @@ -41,14 +41,14 @@ module Lucky::ServerSettings File.read YAML_SETTINGS_PATH else <<-ERROR - Expected config file for the watcher at #{YAML_SETTINGS_PATH}. + Expected config file for the watcher at #{YAML_SETTINGS_PATH}. - Try this... + Try this... - ▸ If this is Production, be sure to set LUCKY_ENV=production - ▸ If this is Development, ensure the #{YAML_SETTINGS_PATH} file exists + ▸ If this is Production, be sure to set LUCKY_ENV=production + ▸ If this is Development, ensure the #{YAML_SETTINGS_PATH} file exists - ERROR + ERROR end end end diff --git a/src/lucky/tags/_check_tag_content.cr b/src/lucky/tags/_check_tag_content.cr index 39ed06cf7..84290f1be 100644 --- a/src/lucky/tags/_check_tag_content.cr +++ b/src/lucky/tags/_check_tag_content.cr @@ -20,14 +20,14 @@ module Lucky::CheckTagContent {% raise <<-MESSAGE - A tag in #{@type} has a nested String, but it must return a tag or `text`. + A tag in #{@type} has a nested String, but it must return a tag or `text`. - If you want to display text, try this: + If you want to display text, try this: - div do - text "my string" - end - MESSAGE + div do + text "my string" + end + MESSAGE %} end end diff --git a/src/lucky/tags/bun_reload_tag.cr b/src/lucky/tags/bun_reload_tag.cr index b67b14a65..f6537c834 100644 --- a/src/lucky/tags/bun_reload_tag.cr +++ b/src/lucky/tags/bun_reload_tag.cr @@ -9,54 +9,54 @@ module Lucky::BunReloadTag tag "script" do raw <<-JS - (() => { - const cssPaths = #{bun_reload_connect_css_files.to_json}; - const ws = new WebSocket('#{LuckyBun::Config.instance.dev_server.ws_url}') - let connected = false + (() => { + const cssPaths = #{bun_reload_connect_css_files.to_json}; + const ws = new WebSocket('#{LuckyBun::Config.instance.dev_server.ws_url}') + let connected = false - const scrollKey = 'bun-scroll:' + location.pathname - addEventListener('load', () => { - const saved = sessionStorage.getItem(scrollKey) - if (saved !== null) { - sessionStorage.removeItem(scrollKey) - scrollTo(0, parseInt(saved, 10)) + const scrollKey = 'bun-scroll:' + location.pathname + addEventListener('load', () => { + const saved = sessionStorage.getItem(scrollKey) + if (saved !== null) { + sessionStorage.removeItem(scrollKey) + scrollTo(0, parseInt(saved, 10)) + } + }) + const reload = () => { + sessionStorage.setItem(scrollKey, String(scrollY)) + location.reload() } - }) - const reload = () => { - sessionStorage.setItem(scrollKey, String(scrollY)) - location.reload() - } - ws.onmessage = (event) => { - const data = JSON.parse(event.data) + ws.onmessage = (event) => { + const data = JSON.parse(event.data) - if (data.type === 'css') { - document.querySelectorAll('link[rel="stylesheet"]').forEach(link => { - const linkPath = new URL(link.href).pathname.split('?')[0] - if (cssPaths.some(p => linkPath.startsWith(p))) { - const url = new URL(link.href) - url.searchParams.set('bust', Date.now()) - link.href = url.toString() - } - }) - console.log('▸ CSS reloaded') - } else if (data.type === 'error') { - console.error('✖ Build error:', data.message) - } else { - console.log('▸ Reloading...') - reload() + if (data.type === 'css') { + document.querySelectorAll('link[rel="stylesheet"]').forEach(link => { + const linkPath = new URL(link.href).pathname.split('?')[0] + if (cssPaths.some(p => linkPath.startsWith(p))) { + const url = new URL(link.href) + url.searchParams.set('bust', Date.now()) + link.href = url.toString() + } + }) + console.log('▸ CSS reloaded') + } else if (data.type === 'error') { + console.error('✖ Build error:', data.message) + } else { + console.log('▸ Reloading...') + reload() + } } - } - ws.onopen = () => { - connected = true - console.log('▸ Live reload connected') - } - ws.onclose = () => { - if (connected) setTimeout(reload, 2000) - } - })() - JS + ws.onopen = () => { + connected = true + console.log('▸ Live reload connected') + } + ws.onclose = () => { + if (connected) setTimeout(reload, 2000) + } + })() + JS end end diff --git a/src/lucky/tags/link_helpers.cr b/src/lucky/tags/link_helpers.cr index 5154cc67d..fec0944ef 100644 --- a/src/lucky/tags/link_helpers.cr +++ b/src/lucky/tags/link_helpers.cr @@ -12,19 +12,19 @@ module Lucky::LinkHelpers def link(to : Lucky::RouteHelper, href : String, **html_options, &) : Nil {% raise <<-ERROR - 'link' cannot be called with an href. + 'link' cannot be called with an href. - Use 'a()' or remove the href argument. + Use 'a()' or remove the href argument. - Example: + Example: - a href: "/" do - end + a href: "/" do + end - link to: Home::Index do - end + link to: Home::Index do + end - ERROR + ERROR %} end @@ -61,35 +61,35 @@ module Lucky::LinkHelpers def link(text, to : String, attrs : Array(Symbol) = [] of Symbol, **html_options) {% raise <<-ERROR - 'link' no longer supports passing a String to 'to'. + 'link' no longer supports passing a String to 'to'. - Use 'a()' or pass an Action class instead. + Use 'a()' or pass an Action class instead. - Example: + Example: - a "Home", href: "/" - link "Home", to: Home::Index + a "Home", href: "/" + link "Home", to: Home::Index - ERROR + ERROR %} end def link(to : String, attrs : Array(Symbol) = [] of Symbol, **html_options, &) {% raise <<-ERROR - 'link' no longer supports passing a String to 'to'. + 'link' no longer supports passing a String to 'to'. - Use 'a()' or pass an Action class instead. + Use 'a()' or pass an Action class instead. - Example: + Example: - a href: "/" do - end + a href: "/" do + end - link to: Home::Index do - end + link to: Home::Index do + end - ERROR + ERROR %} yield end diff --git a/src/lucky/tags/tag_defaults.cr b/src/lucky/tags/tag_defaults.cr index 6a815fe6b..72cf70ddb 100644 --- a/src/lucky/tags/tag_defaults.cr +++ b/src/lucky/tags/tag_defaults.cr @@ -37,25 +37,25 @@ module Lucky::TagDefaults {% raise <<-ERROR - Use 'replace_class' or 'append_class' instead of 'class'. + Use 'replace_class' or 'append_class' instead of 'class'. - Correct example: + Correct example: - tag_defaults class: "default" do |tag_builder| - # Use 'replace_class' or 'append_class' here - tag_builder.div replace_class: "replaced" - end + tag_defaults class: "default" do |tag_builder| + # Use 'replace_class' or 'append_class' here + tag_builder.div replace_class: "replaced" + end - Incorrect example: + Incorrect example: - tag_defaults class: "default" do |tag_builder| - # Won't work with 'class' - tag_builder.div class: "replaced" - end + tag_defaults class: "default" do |tag_builder| + # Won't work with 'class' + tag_builder.div class: "replaced" + end - ----------------- + ----------------- - ERROR + ERROR %} {% end %} diff --git a/src/lucky/verify_accepts_format.cr b/src/lucky/verify_accepts_format.cr index ec41ef923..81ecb81a4 100644 --- a/src/lucky/verify_accepts_format.cr +++ b/src/lucky/verify_accepts_format.cr @@ -85,19 +85,19 @@ module Lucky::VerifyAcceptsFormat private def verify_all_formats_recognized! : Nil find_unrecognized_format.try do |unrecognized_format| raise <<-TEXT - #{self.class.name} accepts an unrecognized format :#{unrecognized_format} + #{self.class.name} accepts an unrecognized format :#{unrecognized_format} - You can teach Lucky how to handle this format: + You can teach Lucky how to handle this format: - # Add this in config/mime_types.cr - Lucky::MimeType.register "text/custom", :#{unrecognized_format} + # Add this in config/mime_types.cr + Lucky::MimeType.register "text/custom", :#{unrecognized_format} - Or use one of these formats Lucky knows about: + Or use one of these formats Lucky knows about: - #{Lucky::MimeType.known_formats.join(", ")} + #{Lucky::MimeType.known_formats.join(", ")} - TEXT + TEXT end end diff --git a/src/lucky/welcome_page.cr b/src/lucky/welcome_page.cr index 38e888f9d..1e185c538 100644 --- a/src/lucky/welcome_page.cr +++ b/src/lucky/welcome_page.cr @@ -191,7 +191,7 @@ class Lucky::WelcomePage background-image: linear-gradient(-180deg, #47c4ff 0%, #2ba4ec 100%) } - CSS + CSS end private def load_lato_font @@ -201,7 +201,7 @@ class Lucky::WelcomePage private def normalize_styles style <<-CSS /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none} - CSS + CSS end private def raw(string) diff --git a/src/run_macros/asset_manifest_builder.cr b/src/run_macros/asset_manifest_builder.cr index 8d8fde378..998b6f960 100644 --- a/src/run_macros/asset_manifest_builder.cr +++ b/src/run_macros/asset_manifest_builder.cr @@ -140,33 +140,33 @@ struct AssetManifestBuilder message = case @source in .bun? <<-ERROR - #{"Manifest not found:".colorize(:red)} #{@manifest_path} + #{"Manifest not found:".colorize(:red)} #{@manifest_path} - #{"Make sure you have compiled your assets:".colorize(:yellow)} - bun run dev # start development server with watcher - bun run build # normal build - bun run prod # minified and fingerprinted build + #{"Make sure you have compiled your assets:".colorize(:yellow)} + bun run dev # start development server with watcher + bun run build # normal build + bun run prod # minified and fingerprinted build - ERROR + ERROR in .mix? <<-ERROR - #{"Manifest not found:".colorize(:red)} #{@manifest_path} + #{"Manifest not found:".colorize(:red)} #{@manifest_path} - #{"Make sure you have compiled your assets:".colorize(:yellow)} - yarn run mix # development build - yarn run mix watch # development build with watcher - yarn run mix --production # production build + #{"Make sure you have compiled your assets:".colorize(:yellow)} + yarn run mix # development build + yarn run mix watch # development build with watcher + yarn run mix --production # production build - ERROR + ERROR in .vite? <<-ERROR - #{"Manifest not found:".colorize(:red)} #{@manifest_path} + #{"Manifest not found:".colorize(:red)} #{@manifest_path} - #{"Make sure you have compiled your assets:".colorize(:yellow)} - npx vite # start development server - npx vite build # production build + #{"Make sure you have compiled your assets:".colorize(:yellow)} + npx vite # start development server + npx vite build # production build - ERROR + ERROR end puts message diff --git a/tasks/exec.cr b/tasks/exec.cr index dd8f459bf..9760e3d2b 100644 --- a/tasks/exec.cr +++ b/tasks/exec.cr @@ -6,17 +6,17 @@ class Lucky::Exec < LuckyTask::Task name "exec" summary "Execute code. Use this in place of a console/REPL" help_message <<-TEXT - #{task_summary} + #{task_summary} - Options: - --editor=EDITOR, -e EDITOR Use the EDITOR for editing code - --back=NUMBER, -b NUMBER Load code NUMBER sessions back - --once, -o Only run this code once then exit + Options: + --editor=EDITOR, -e EDITOR Use the EDITOR for editing code + --back=NUMBER, -b NUMBER Load code NUMBER sessions back + --once, -o Only run this code once then exit - example: lucky exec -e emacs -b 3 -o + example: lucky exec -e emacs -b 3 -o - Run this task with 'lucky exec [OPTIONS]' - TEXT + Run this task with 'lucky exec [OPTIONS]' + TEXT arg :editor, "Which editor to use", shortcut: "-e", optional: true arg :back, "Load code from this many sessions back. Default is 1.", diff --git a/tasks/gen/action/api.cr b/tasks/gen/action/api.cr index 2a1d67b52..a7e7be860 100644 --- a/tasks/gen/action/api.cr +++ b/tasks/gen/action/api.cr @@ -6,12 +6,12 @@ class Gen::Action::Api < LuckyTask::Task summary "Generate a new api action" help_message <<-TEXT - #{task_summary} + #{task_summary} - Example: + Example: - lucky gen.action.api Api::Users::Index - TEXT + lucky gen.action.api Api::Users::Index + TEXT positional_arg :action_name, "The name of the action" switch :with_page, "This flag is used with gen.action.browser Only" diff --git a/tasks/gen/action/browser.cr b/tasks/gen/action/browser.cr index 3b2ce5d86..ab966f6f6 100644 --- a/tasks/gen/action/browser.cr +++ b/tasks/gen/action/browser.cr @@ -7,15 +7,15 @@ class Gen::Action::Browser < LuckyTask::Task summary "Generate a new browser action" help_message <<-TEXT - #{task_summary} + #{task_summary} - Optionally, you can pass the --with-page flag to generate - a page for the Action. + Optionally, you can pass the --with-page flag to generate + a page for the Action. - Example: + Example: - lucky gen.action.browser Users::Index --with-page - TEXT + lucky gen.action.browser Users::Index --with-page + TEXT positional_arg :action_name, "The name of the action" switch :with_page, "Generate a Page matching this Action" diff --git a/tasks/gen/component.cr b/tasks/gen/component.cr index 10e15561b..1eddf4072 100644 --- a/tasks/gen/component.cr +++ b/tasks/gen/component.cr @@ -27,12 +27,12 @@ end class Gen::Component < LuckyTask::Task summary "Generate a new HTML component" help_message <<-TEXT - #{task_summary} + #{task_summary} - Example: + Example: - lucky gen.component SettingsMenu - TEXT + lucky gen.component SettingsMenu + TEXT positional_arg :component_class, "The name of the component" diff --git a/tasks/gen/page.cr b/tasks/gen/page.cr index 576d4a6fa..3f695f3a4 100644 --- a/tasks/gen/page.cr +++ b/tasks/gen/page.cr @@ -27,12 +27,12 @@ end class Gen::Page < LuckyTask::Task summary "Generate a new HTML page" help_message <<-TEXT - #{task_summary} + #{task_summary} - Example: + Example: - lucky gen.page Users::IndexPage - TEXT + lucky gen.page Users::IndexPage + TEXT positional_arg :page_class, "The name of the page" diff --git a/tasks/gen/task.cr b/tasks/gen/task.cr index 182b506de..039439fc4 100644 --- a/tasks/gen/task.cr +++ b/tasks/gen/task.cr @@ -33,13 +33,13 @@ end class Gen::Task < LuckyTask::Task summary "Generate a lucky command line task" help_message <<-TEXT - #{task_summary} + #{task_summary} - Example: - lucky gen.task email.monthly_update + Example: + lucky gen.task email.monthly_update - See Also: https://luckyframework.org/guides/command-line-tasks/custom-tasks - TEXT + See Also: https://luckyframework.org/guides/command-line-tasks/custom-tasks + TEXT arg :task_summary, "The -h help text for the task", optional: true positional_arg :task_name, "The name of the task to generate" @@ -56,12 +56,12 @@ class Gen::Task < LuckyTask::Task .render(Path["."]) output.puts <<-TEXT - Generated #{output_path.join(task_filename).colorize.green} + Generated #{output_path.join(task_filename).colorize.green} - Run it with: + Run it with: - lucky #{task_name} - TEXT + lucky #{task_name} + TEXT end end diff --git a/tasks/routes.cr b/tasks/routes.cr index 67b6ae212..4da9a3df4 100644 --- a/tasks/routes.cr +++ b/tasks/routes.cr @@ -6,21 +6,21 @@ require "json" class Routes < LuckyTask::Task summary "Show all the routes for the app" help_message <<-TEXT - #{task_summary} + #{task_summary} - Optionally, you can pass the --with-params flag (-p) to print out - the available params for each Action. + Optionally, you can pass the --with-params flag (-p) to print out + the available params for each Action. - example: lucky routes --with-params + example: lucky routes --with-params - You can also output routes as JSON using the --format flag (-f). + You can also output routes as JSON using the --format flag (-f). - example: lucky routes --format=json + example: lucky routes --format=json - Routing documentation: + Routing documentation: - https://luckyframework.org/guides/http-and-routing/routing-and-params - TEXT + https://luckyframework.org/guides/http-and-routing/routing-and-params + TEXT switch :with_params, "Include action params with each route", shortcut: "-p" arg :format, "Output format (table or json)", shortcut: "-f", optional: true @@ -71,10 +71,10 @@ class Routes < LuckyTask::Task ) <<-TEXT - #{print_banner_message} + #{print_banner_message} - #{table} - TEXT + #{table} + TEXT end private def build_json_from_routes(routes : Array(Tuple(String, String, Lucky::Action.class))) : String @@ -118,9 +118,9 @@ class Routes < LuckyTask::Task private def print_banner_message : Colorize::Object(String) <<-TEXT.colorize.dim - Routing documentation: + Routing documentation: - https://luckyframework.org/guides/http-and-routing/routing-and-params - TEXT + https://luckyframework.org/guides/http-and-routing/routing-and-params + TEXT end end diff --git a/tasks/watch.cr b/tasks/watch.cr index 9a289d282..4fbb22c00 100644 --- a/tasks/watch.cr +++ b/tasks/watch.cr @@ -284,14 +284,14 @@ module LuckySentry if successful_compilations.zero? puts <<-ERROR - #{"---".colorize.dim} + #{"---".colorize.dim} - Feeling stuck? Try this... + Feeling stuck? Try this... - ▸ Run setup: #{"script/setup".colorize.bold} - ▸ Reinstall shards: #{"rm -rf lib bin && shards install".colorize.bold} - ▸ Ask for help: #{"https://luckyframework.org/chat".colorize.bold} - ERROR + ▸ Run setup: #{"script/setup".colorize.bold} + ▸ Reinstall shards: #{"rm -rf lib bin && shards install".colorize.bold} + ▸ Ask for help: #{"https://luckyframework.org/chat".colorize.bold} + ERROR end end From e16aaac30b05e19c14bce587ed8e3a256783b53c Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:04:50 +0200 Subject: [PATCH 06/19] Fix `Lint/WhitespaceAroundMacroExpression` type of Ameba issues --- spec/lucky/base_http_client_spec.cr | 4 +- src/lucky/memoizable.cr | 10 ++--- src/lucky/page_helpers/svg_inliner.cr | 2 +- src/lucky/rate_limit.cr | 2 +- src/lucky/routable.cr | 4 +- src/lucky/tags/base_tags.cr | 64 +++++++++++++-------------- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/spec/lucky/base_http_client_spec.cr b/spec/lucky/base_http_client_spec.cr index a8ba04edc..2cb38fb11 100644 --- a/spec/lucky/base_http_client_spec.cr +++ b/spec/lucky/base_http_client_spec.cr @@ -129,7 +129,7 @@ describe Lucky::BaseHTTPClient do {% for method in [:put, :patch, :post, :delete, :get, :options] %} describe "\#{{method.id}}" do it "sends correct request to correct uri and gives the correct response" do - response = MyClient.new.{{method.id}}( + response = MyClient.new.{{ method.id }}( path: "hello", foo: "bar" ) @@ -141,7 +141,7 @@ describe Lucky::BaseHTTPClient do end it "works without params" do - response = MyClient.new.{{method.id}}(path: "hello") + response = MyClient.new.{{ method.id }}(path: "hello") request = TestServer.last_request request.method.should eq({{ method.id.stringify }}.upcase) diff --git a/src/lucky/memoizable.cr b/src/lucky/memoizable.cr index c90c43c4c..7404b4db5 100644 --- a/src/lucky/memoizable.cr +++ b/src/lucky/memoizable.cr @@ -40,7 +40,7 @@ module Lucky::Memoizable end %} - @__memoized_{{safe_method_name}} : Tuple( + @__memoized_{{ safe_method_name }} : Tuple( {{ method_def.return_type }}, {% for arg in method_def.args %} {{ arg.restriction }}, @@ -78,18 +78,18 @@ module Lucky::Memoizable {% end %} ) {% for arg, index in method_def.args %} - @__memoized_{{ safe_method_name }} = nil if {{arg.internal_name}} != @__memoized_{{ safe_method_name }}.try &.at({{index}} + 1) + @__memoized_{{ safe_method_name }} = nil if {{ arg.internal_name }} != @__memoized_{{ safe_method_name }}.try &.at({{ index }} + 1) {% end %} @__memoized_{{ safe_method_name }} ||= -> do result = {{ safe_method_name }}__uncached{% if special_ending %}{{ special_ending.id }}{% end %}( {% for arg in method_def.args %} - {{arg.internal_name}}, + {{ arg.internal_name }}, {% end %} ) { result, {% for arg in method_def.args %} - {{arg.internal_name}}, + {{ arg.internal_name }}, {% end %} } end.call.not_nil! @@ -108,7 +108,7 @@ module Lucky::Memoizable ) : {{ method_def.return_type }} {{ safe_method_name }}__tuple_cached{% if special_ending %}{{ special_ending.id }}{% end %}( {% for arg in method_def.args %} - {{arg.internal_name}}, + {{ arg.internal_name }}, {% end %} ).first end diff --git a/src/lucky/page_helpers/svg_inliner.cr b/src/lucky/page_helpers/svg_inliner.cr index 940c84bf9..f85a6fc85 100644 --- a/src/lucky/page_helpers/svg_inliner.cr +++ b/src/lucky/page_helpers/svg_inliner.cr @@ -33,6 +33,6 @@ module Lucky::SvgInliner end %} - raw {{svg.gsub(/ String macro generate_tag_methods(method_name, tag) - # Generates a `<{{method_name.id}}></{{method_name.id}}>` tag. + # Generates a `<{{ method_name.id }}></{{ method_name.id }}>` tag. # # * The *content* argument is either a `String`, or any type that has included `Lucky::AllowedInTags`. This is the content that goes inside of the tag. # * The *options* argument is a `Hash(String, String)` of any HTML attribute that has a key/value like `class`, `id`, `type`, etc... # * The *attrs* argument is an `Array(Symbol)` for specifying [Boolean Attributes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes) such as `required`, `disabled`, `autofocus`, etc... # # ``` - # {{method_name.id}}("Sample", {"class" => "cls-1 red"}, [:required]) #=> <{{method_name.id}} class="cls-1 red" required>Sample + # {{ method_name.id }}("Sample", {"class" => "cls-1 red"}, [:required]) #=> <{{ method_name.id }} class="cls-1 red" required>Sample # ``` - def {{method_name.id}}( + def {{ method_name.id }}( content : Lucky::AllowedInTags | String = "", options = EMPTY_HTML_ATTRS, attrs : Array(Symbol) = [] of Symbol, @@ -122,14 +122,14 @@ module Lucky::BaseTags boolean_attrs = build_boolean_attrs(attrs) merged_options = merge_options(other_options, options) tag_attrs = build_tag_attrs(merged_options) - view << "<{{tag.id}}" << tag_attrs << boolean_attrs << ">" << HTML.escape(content.to_s) << "" + view << "<{{ tag.id }}" << tag_attrs << boolean_attrs << ">" << HTML.escape(content.to_s) << "" end - def {{method_name.id}}(content : Lucky::AllowedInTags | String) : Nil - view << "<{{tag.id}}>" << HTML.escape(content.to_s) << "" + def {{ method_name.id }}(content : Lucky::AllowedInTags | String) : Nil + view << "<{{ tag.id }}>" << HTML.escape(content.to_s) << "" end - def {{method_name.id}}( + def {{ method_name.id }}( content : Nil, options = EMPTY_HTML_ATTRS, attrs : Array(Symbol) = [] of Symbol, @@ -142,14 +142,14 @@ module Lucky::BaseTags Try this... if value = some_nilable_value - {{method_name.id}}(value, class: "header") + {{ method_name.id }}(value, class: "header") end ERROR %} end - def {{method_name.id}}( + def {{ method_name.id }}( content : Time, options = EMPTY_HTML_ATTRS, attrs : Array(Symbol) = [] of Symbol, @@ -158,75 +158,75 @@ module Lucky::BaseTags \{% raise <<-ERROR HTML tags content must be a String or Lucky::AllowedInTags object. - {{method_name.id}} received a Time object which has an ambiguous display format. + {{ method_name.id }} received a Time object which has an ambiguous display format. Try this... - {{method_name.id}}(current_time.to_s("%F"), html_opts) + {{ method_name.id }}(current_time.to_s("%F"), html_opts) ERROR %} end - def {{method_name.id}}(options, **other_options) : Nil + def {{ method_name.id }}(options, **other_options) : Nil {{ method_name.id }}("", options, **other_options) end - def {{method_name.id}}(options = EMPTY_HTML_ATTRS, **other_options) : Nil + def {{ method_name.id }}(options = EMPTY_HTML_ATTRS, **other_options) : Nil merged_options = merge_options(other_options, options) tag_attrs = build_tag_attrs(merged_options) - view << "<{{tag.id}}" << tag_attrs << ">" + view << "<{{ tag.id }}" << tag_attrs << ">" check_tag_content!(yield) - view << "" + view << "" end - def {{method_name.id}}(attrs : Array(Symbol), options = EMPTY_HTML_ATTRS, **other_options) : Nil + def {{ method_name.id }}(attrs : Array(Symbol), options = EMPTY_HTML_ATTRS, **other_options) : Nil boolean_attrs = build_boolean_attrs(attrs) merged_options = merge_options(other_options, options) tag_attrs = build_tag_attrs(merged_options) - view << "<{{tag.id}}" << tag_attrs << boolean_attrs << ">" + view << "<{{ tag.id }}" << tag_attrs << boolean_attrs << ">" check_tag_content!(yield) - view << "" + view << "" end - def {{method_name.id}} : Nil - view << "<{{tag.id}}>" + def {{ method_name.id }} : Nil + view << "<{{ tag.id }}>" check_tag_content!(yield) - view << "" + view << "" end end {% for tag in TAGS %} - generate_tag_methods(method_name: {{tag.id}}, tag: {{tag.id}}) + generate_tag_methods(method_name: {{ tag.id }}, tag: {{ tag.id }}) {% end %} {% for name, tag in RENAMED_TAGS %} - generate_tag_methods(method_name: {{name}}, tag: {{tag}}) + generate_tag_methods(method_name: {{ name }}, tag: {{ tag }}) {% end %} {% for tag in EMPTY_TAGS %} - # Generates a `<{{tag.id}}>` tag. - def {{tag.id}} : Nil - view << %(<{{tag.id}}>) + # Generates a `<{{ tag.id }}>` tag. + def {{ tag.id }} : Nil + view << %(<{{ tag.id }}>) end - def {{tag.id}}(options = EMPTY_HTML_ATTRS, **other_options) : Nil - {{tag.id}}([] of Symbol, options, **other_options) + def {{ tag.id }}(options = EMPTY_HTML_ATTRS, **other_options) : Nil + {{ tag.id }}([] of Symbol, options, **other_options) end - # Generates a `<{{tag.id}}>` tag. + # Generates a `<{{ tag.id }}>` tag. # # * The *attrs* argument is an `Array(Symbol)` for specifying [Boolean Attributes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes) such as `required`, `disabled`, `autofocus`, etc... # * The *options* argument is a `Hash(String, String)` of any HTML attribute that has a key/value like `class`, `id`, `type`, etc... # # ``` - # {{tag.id}}([:required], {"class" => "cls-1"}) #=> <{{tag.id}} class="cls-1" required> + # {{ tag.id }}([:required], {"class" => "cls-1"}) #=> <{{ tag.id }} class="cls-1" required> # ``` - def {{tag.id}}(attrs : Array(Symbol), options = EMPTY_HTML_ATTRS, **other_options) : Nil + def {{ tag.id }}(attrs : Array(Symbol), options = EMPTY_HTML_ATTRS, **other_options) : Nil bool_attrs = build_boolean_attrs(attrs) merged_options = merge_options(other_options, options) tag_attrs = build_tag_attrs(merged_options) - view << %(<{{tag.id}}#{tag_attrs}#{bool_attrs}>) + view << %(<{{ tag.id }}#{tag_attrs}#{bool_attrs}>) end {% end %} From 4d99e19d73f9636c869a919f0bb10b6332ab7fb9 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:05:42 +0200 Subject: [PATCH 07/19] Fix `Style/PercentLiteralDelimiters` type of Ameba issues --- spec/lucky/action_redirect_spec.cr | 2 +- spec/lucky/base_tags_spec.cr | 6 +++--- spec/lucky/static_compression_handler_spec.cr | 4 ++-- spec/lucky/text_helpers/highlight_spec.cr | 2 +- src/lucky/param_parser.cr | 4 ++-- src/lucky/protect_from_forgery.cr | 2 +- src/lucky/redirectable_turbolinks_support.cr | 2 +- src/lucky/server.cr | 4 ++-- src/lucky/static_compression_handler.cr | 2 +- src/lucky/tags/base_tags.cr | 6 +++--- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/lucky/action_redirect_spec.cr b/spec/lucky/action_redirect_spec.cr index 0df6e5afa..50c3366ad 100644 --- a/spec/lucky/action_redirect_spec.cr +++ b/spec/lucky/action_redirect_spec.cr @@ -165,7 +165,7 @@ describe Lucky::Action do response = action.redirect to: "/somewhere", status: 302 should_redirect(action, to: "/somewhere", status: 200) action.context.response.headers.has_key?("Turbolinks-Location").should be_false - response.body.should eq %[Turbolinks.clearCache();\nTurbolinks.visit("/somewhere", {"action": "replace"})] + response.body.should eq %(Turbolinks.clearCache();\nTurbolinks.visit("/somewhere", {"action": "replace"})) end it "set a cookie for redirects occurring during a turbolinks GET request" do diff --git a/spec/lucky/base_tags_spec.cr b/spec/lucky/base_tags_spec.cr index 779fa6301..cbdcdefae 100644 --- a/spec/lucky/base_tags_spec.cr +++ b/spec/lucky/base_tags_spec.cr @@ -38,9 +38,9 @@ describe Lucky::BaseTags do page.video(attrs: [:autoplay, :controls, :loop], poster: "https://luckyframework.org/nothing.png") do page.source(src: "https://luckyframework.org/nothing.mp4", type: "video/mp4") end - end.should contain %{} + end.should contain %() - view(&.video(id: "player", "data-stream": "https://luckyframework.org/demo.mp4")).should eq %{} + view(&.video(id: "player", "data-stream": "https://luckyframework.org/demo.mp4")).should eq %() end it "renders a button with a disabled boolean attribute" do @@ -48,7 +48,7 @@ describe Lucky::BaseTags do end it "renders an input with autofocus boolean attribute" do - view(&.input(attrs: [:autofocus], type: "text")).to_s.should contain %{\\1")).should eq %(wow em) + view(&.highlight("wow em", %w[wow em], highlighter: "\\1")).should eq %(wow em) end it "escapes HTML by default" do diff --git a/src/lucky/param_parser.cr b/src/lucky/param_parser.cr index 56984758e..440ca0eba 100644 --- a/src/lucky/param_parser.cr +++ b/src/lucky/param_parser.cr @@ -35,9 +35,9 @@ module Lucky::ParamParser end def self.parse(param : String, klass : Bool.class) : Bool? - if %w(true 1).includes? param + if %w[true 1].includes? param true - elsif %w(false 0).includes? param + elsif %w[false 0].includes? param false else nil diff --git a/src/lucky/protect_from_forgery.cr b/src/lucky/protect_from_forgery.cr index 05ffe9058..6eb1bac7f 100644 --- a/src/lucky/protect_from_forgery.cr +++ b/src/lucky/protect_from_forgery.cr @@ -3,7 +3,7 @@ # This module is automatically included in `BrowserAction` to protect from CSRF # attacks. module Lucky::ProtectFromForgery - ALLOWED_METHODS = %w(GET HEAD OPTIONS TRACE) + ALLOWED_METHODS = %w[GET HEAD OPTIONS TRACE] SESSION_KEY = "X-CSRF-TOKEN" PARAM_KEY = "_csrf" diff --git a/src/lucky/redirectable_turbolinks_support.cr b/src/lucky/redirectable_turbolinks_support.cr index dd4800a85..84e4c9a78 100644 --- a/src/lucky/redirectable_turbolinks_support.cr +++ b/src/lucky/redirectable_turbolinks_support.cr @@ -19,7 +19,7 @@ module Lucky::RedirectableTurbolinksSupport Lucky::TextResponse.new(context, "text/javascript", - %[Turbolinks.clearCache();\nTurbolinks.visit(#{path.to_json}, {"action": "replace"})], + %(Turbolinks.clearCache();\nTurbolinks.visit(#{path.to_json}, {"action": "replace"})), status: 200) else if request.headers["Turbolinks-Referrer"]? diff --git a/src/lucky/server.cr b/src/lucky/server.cr index 34c380337..86ed02867 100644 --- a/src/lucky/server.cr +++ b/src/lucky/server.cr @@ -8,7 +8,7 @@ class Lucky::Server setting port : Int32 setting asset_host : String = "" setting gzip_enabled : Bool = false - setting gzip_content_types : Array(String) = %w( + setting gzip_content_types : Array(String) = %w[ application/json application/javascript application/xml @@ -22,6 +22,6 @@ class Lucky::Server text/html text/javascript text/plain - ) + ] end end diff --git a/src/lucky/static_compression_handler.cr b/src/lucky/static_compression_handler.cr index 19a705032..2ae750db6 100644 --- a/src/lucky/static_compression_handler.cr +++ b/src/lucky/static_compression_handler.cr @@ -78,7 +78,7 @@ class Lucky::StaticCompressionHandler end private def etag(modification_time) - %{W/"#{modification_time.to_unix}"} + %(W/"#{modification_time.to_unix}") end private def modification_time(file_path) diff --git a/src/lucky/tags/base_tags.cr b/src/lucky/tags/base_tags.cr index d20a803e1..e71ea61c1 100644 --- a/src/lucky/tags/base_tags.cr +++ b/src/lucky/tags/base_tags.cr @@ -1,6 +1,6 @@ module Lucky::BaseTags include Lucky::CheckTagContent - TAGS = %i( + TAGS = %i[ a abbr address @@ -98,9 +98,9 @@ module Lucky::BaseTags ul video wbr - ) + ] RENAMED_TAGS = {"para": "p", "select_tag": "select"} - EMPTY_TAGS = %i(img br hr input meta source) + EMPTY_TAGS = %i[img br hr input meta source] EMPTY_HTML_ATTRS = {} of String => String macro generate_tag_methods(method_name, tag) From 99c8435ed49f24e438f5c29dc58dce37a8ce955e Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:06:08 +0200 Subject: [PATCH 08/19] Fix `Style/VerboseNilType` type of Ameba issues --- spec/lucky/html_page_spec.cr | 2 +- src/lucky/context_extensions.cr | 2 +- src/lucky/format_registry.cr | 4 ++-- src/lucky/mime_type.cr | 2 +- src/lucky/page_helpers/html_text_helpers.cr | 4 ++-- src/lucky/page_helpers/text_helpers.cr | 6 +++--- src/lucky/request_type_helpers.cr | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/lucky/html_page_spec.cr b/spec/lucky/html_page_spec.cr index d6908e9b9..602134d66 100644 --- a/spec/lucky/html_page_spec.cr +++ b/spec/lucky/html_page_spec.cr @@ -72,7 +72,7 @@ class LessNeedyDefaultsPage < MainLayout needs bool : Bool = false needs nil_default : String? = nil needs inferred_nil_default : String? - needs inferred_nil_default2 : String | Nil + needs inferred_nil_default2 : String? def inner div @a_string diff --git a/src/lucky/context_extensions.cr b/src/lucky/context_extensions.cr index 76391ce61..de945e5a0 100644 --- a/src/lucky/context_extensions.cr +++ b/src/lucky/context_extensions.cr @@ -11,7 +11,7 @@ class HTTP::Server::Context # # This stores the format extracted from the URL path (e.g., .csv, .json) # This takes precedence over Accept header-based format detection - property _url_format : Lucky::Format | Lucky::FormatRegistry::CustomFormat | Nil = nil + property _url_format : Lucky::Format | Lucky::FormatRegistry::CustomFormat? = nil # :nodoc: # diff --git a/src/lucky/format_registry.cr b/src/lucky/format_registry.cr index 0ef6029c6..7ff833960 100644 --- a/src/lucky/format_registry.cr +++ b/src/lucky/format_registry.cr @@ -23,7 +23,7 @@ module Lucky::FormatRegistry end # Find format by extension (checks both built-in and custom formats) - def self.from_extension(extension : String) : Lucky::Format | CustomFormat | Nil + def self.from_extension(extension : String) : Lucky::Format | CustomFormat? # Try built-in formats first if format = Lucky::Format.from_extension(extension) return format @@ -38,7 +38,7 @@ module Lucky::FormatRegistry end # Find format by MIME type (checks both built-in and custom formats) - def self.from_mime_type(mime_type : String) : Lucky::Format | CustomFormat | Nil + def self.from_mime_type(mime_type : String) : Lucky::Format | CustomFormat? # Try built-in formats first if format = Lucky::Format.from_mime_type(mime_type) return format diff --git a/src/lucky/mime_type.cr b/src/lucky/mime_type.cr index 342bde33f..ad743bae0 100644 --- a/src/lucky/mime_type.cr +++ b/src/lucky/mime_type.cr @@ -67,7 +67,7 @@ class Lucky::MimeType end # Extract format from URL path (e.g., "/reports/123.csv" -> Format::Csv) - def self.extract_format_from_path(path : String) : Lucky::Format | Lucky::FormatRegistry::CustomFormat | Nil + def self.extract_format_from_path(path : String) : Lucky::Format | Lucky::FormatRegistry::CustomFormat? # Only match extensions in the path portion (before any query string) if match = path.match(/^[^?]*\.([a-zA-Z0-9]+)(?:\?|$)/) extension = match[1] diff --git a/src/lucky/page_helpers/html_text_helpers.cr b/src/lucky/page_helpers/html_text_helpers.cr index 6fe13ee3d..7669d4ca2 100644 --- a/src/lucky/page_helpers/html_text_helpers.cr +++ b/src/lucky/page_helpers/html_text_helpers.cr @@ -30,13 +30,13 @@ module Lucky::HTMLTextHelpers # ```html # "Four score and se...Read more" # ``` - def truncate(text : String, length : Int32 = 30, omission : String = "...", separator : String | Nil = nil, escape : Bool = true, blk : Nil | Proc = nil) : Nil + def truncate(text : String, length : Int32 = 30, omission : String = "...", separator : String? = nil, escape : Bool = true, blk : Proc? = nil) : Nil content = truncate_text(text, length, omission, separator) raw(escape ? HTML.escape(content) : content) blk.call if !blk.nil? && text.size > length end - def truncate(text : String, length : Int32 = 30, omission : String = "...", separator : String | Nil = nil, escape : Bool = true, &block : -> _) : Nil + def truncate(text : String, length : Int32 = 30, omission : String = "...", separator : String? = nil, escape : Bool = true, &block : -> _) : Nil truncate(text, length, omission, separator, escape, blk: block) end diff --git a/src/lucky/page_helpers/text_helpers.cr b/src/lucky/page_helpers/text_helpers.cr index b0bf69ac6..1d8aa06b1 100644 --- a/src/lucky/page_helpers/text_helpers.cr +++ b/src/lucky/page_helpers/text_helpers.cr @@ -16,7 +16,7 @@ module Lucky::TextHelpers # ```html # Four score and se... # ``` - def truncate_text(text : String, length : Int32 = 30, omission : String = "...", separator : String | Nil = nil) : String + def truncate_text(text : String, length : Int32 = 30, omission : String = "...", separator : String? = nil) : String return text unless text.size > length length_with_room_for_omission = length - omission.size @@ -86,7 +86,7 @@ module Lucky::TextHelpers # It pluralizes `singular` unless `count` is 1. You can specify the `plural` option # to override the chosen plural word. - def pluralize(count : Int | String | Nil, singular : String, plural = nil) : String + def pluralize(count : Int | String?, singular : String, plural = nil) : String word = if (count == 1) || (count =~ /^1(\.0+)?$/) singular else @@ -237,7 +237,7 @@ module Lucky::TextHelpers @@_cycles[name] = cycle_object end - private def cut_excerpt_part(part_position : Symbol, part : String | Nil, separator : String, radius : Int32, omission : String) + private def cut_excerpt_part(part_position : Symbol, part : String?, separator : String, radius : Int32, omission : String) return "", "" if part.nil? part = part.split(separator) diff --git a/src/lucky/request_type_helpers.cr b/src/lucky/request_type_helpers.cr index e13d27af8..977024e55 100644 --- a/src/lucky/request_type_helpers.cr +++ b/src/lucky/request_type_helpers.cr @@ -116,7 +116,7 @@ module Lucky::RequestTypeHelpers # Get the detected format as an enum (if available) # Returns nil if the format came from Accept header or is unknown - def url_format : Lucky::Format | Lucky::FormatRegistry::CustomFormat | Nil + def url_format : Lucky::Format | Lucky::FormatRegistry::CustomFormat? context._url_format end From 63ed6dac5c295262d83bbafa96a5e0e7c54ad701 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:06:35 +0200 Subject: [PATCH 09/19] Fix `Lint/ElseNil` type of Ameba issues --- src/lucky/format.cr | 2 -- src/lucky/param_parser.cr | 2 -- src/lucky/params.cr | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/lucky/format.cr b/src/lucky/format.cr index 785d5f872..e8993edff 100644 --- a/src/lucky/format.cr +++ b/src/lucky/format.cr @@ -70,7 +70,6 @@ enum Lucky::Format when "atom" then Atom when "ics", "ical" then Ics when "css" then Css - else nil end end @@ -91,7 +90,6 @@ enum Lucky::Format when "text/css" then Css when "multipart/form-data" then MultipartForm when "application/x-www-form-urlencoded" then UrlEncodedForm - else nil end end end diff --git a/src/lucky/param_parser.cr b/src/lucky/param_parser.cr index 440ca0eba..a7bb501dc 100644 --- a/src/lucky/param_parser.cr +++ b/src/lucky/param_parser.cr @@ -39,8 +39,6 @@ module Lucky::ParamParser true elsif %w[false 0].includes? param false - else - nil end end diff --git a/src/lucky/params.cr b/src/lucky/params.cr index 47ef0da9e..171a3f559 100644 --- a/src/lucky/params.cr +++ b/src/lucky/params.cr @@ -672,8 +672,6 @@ class Lucky::Params vals = params.fetch_all(key + "[]") if !vals.empty? vals - else - nil end end From 2ab3daa3751d1da119cfaa239e31f1c2c9f850d0 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:06:58 +0200 Subject: [PATCH 10/19] Fix `Style/RedundantNilInControlExpression` type of Ameba issues --- src/lucky/mime_type.cr | 2 +- src/lucky/params.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lucky/mime_type.cr b/src/lucky/mime_type.cr index ad743bae0..6215664a5 100644 --- a/src/lucky/mime_type.cr +++ b/src/lucky/mime_type.cr @@ -161,7 +161,7 @@ class Lucky::MimeType end # No known formats match the ones requested - return nil + return end # Finally the client accepts anything so use the default format diff --git a/src/lucky/params.cr b/src/lucky/params.cr index 171a3f559..fe8d30cc2 100644 --- a/src/lucky/params.cr +++ b/src/lucky/params.cr @@ -663,7 +663,7 @@ class Lucky::Params private def get_all_json(key : String) : Array(String)? val = parsed_json[key]? - return nil if val.nil? + return if val.nil? val.as_a?.try(&.map(&.to_s)) || [val.to_s] end From 816e8e47cfba008a67e576f5e9a9e6981b5d287c Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:07:25 +0200 Subject: [PATCH 11/19] Fix `Style/RedundantSelf` type of Ameba issues --- src/charms/int16_extensions.cr | 2 +- src/charms/int32_extensions.cr | 2 +- src/charms/int64_extensions.cr | 2 +- src/charms/object.cr | 2 +- src/charms/uuid_extensions.cr | 2 +- src/lucky/base_http_client.cr | 2 +- src/lucky/mime_type.cr | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/charms/int16_extensions.cr b/src/charms/int16_extensions.cr index 2f78ead5c..01f195c5b 100644 --- a/src/charms/int16_extensions.cr +++ b/src/charms/int16_extensions.cr @@ -4,6 +4,6 @@ struct Int16 include ::Lucky::AllowedInTags def to_param : String - self.to_s + to_s end end diff --git a/src/charms/int32_extensions.cr b/src/charms/int32_extensions.cr index 7a2e7f985..b69b008d9 100644 --- a/src/charms/int32_extensions.cr +++ b/src/charms/int32_extensions.cr @@ -4,6 +4,6 @@ struct Int32 include ::Lucky::AllowedInTags def to_param : String - self.to_s + to_s end end diff --git a/src/charms/int64_extensions.cr b/src/charms/int64_extensions.cr index 40e80cfe8..e685da514 100644 --- a/src/charms/int64_extensions.cr +++ b/src/charms/int64_extensions.cr @@ -4,6 +4,6 @@ struct Int64 include ::Lucky::AllowedInTags def to_param : String - self.to_s + to_s end end diff --git a/src/charms/object.cr b/src/charms/object.cr index 0b2c115a1..37a05e6f6 100644 --- a/src/charms/object.cr +++ b/src/charms/object.cr @@ -4,7 +4,7 @@ class Object def blank? : Bool if self.responds_to?(:empty?) - self.empty? + self.empty? # ameba:disable Style/RedundantSelf else false end diff --git a/src/charms/uuid_extensions.cr b/src/charms/uuid_extensions.cr index 50bac0dad..c2447d231 100644 --- a/src/charms/uuid_extensions.cr +++ b/src/charms/uuid_extensions.cr @@ -2,6 +2,6 @@ struct UUID include ::Lucky::AllowedInTags def to_param : String - self.to_s + to_s end end diff --git a/src/lucky/base_http_client.cr b/src/lucky/base_http_client.cr index 2fc55258b..764090b57 100644 --- a/src/lucky/base_http_client.cr +++ b/src/lucky/base_http_client.cr @@ -133,7 +133,7 @@ abstract class Lucky::BaseHTTPClient @port = -1 def self.from_app(app : Lucky::BaseAppServer) : self - self.new(HTTP::Server.build_middleware(app.middleware)) + new(HTTP::Server.build_middleware(app.middleware)) end def initialize(@app : HTTP::Handler) diff --git a/src/lucky/mime_type.cr b/src/lucky/mime_type.cr index 6215664a5..a162ca226 100644 --- a/src/lucky/mime_type.cr +++ b/src/lucky/mime_type.cr @@ -144,7 +144,7 @@ class Lucky::MimeType # If we find a match in the things we accept then pick one of those formats_in_common = known_formats.select { |_media, format| accepted_formats.includes?(format) } unless formats_in_common.empty? - self.list.each do |media_range| + list.each do |media_range| if match = formats_in_common.find { |media, _format| media_range.matches?(media) } return match[1] end @@ -154,7 +154,7 @@ class Lucky::MimeType # Otherwise if the client doesn't just accept anything then try to find something they # do accept in the list of known formats unless includes_catch_all? - self.list.each do |media_range| + list.each do |media_range| if match = known_formats.find { |media, _format| media_range.matches?(media) } return match[1] end From 948679cf4b3294bec680eded3a68bfb928231b75 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:07:54 +0200 Subject: [PATCH 12/19] Fix `Lint/SpecEqWithBoolOrNilLiteral` type of Ameba issues --- spec/lucky/action_pipes_spec.cr | 8 ++++---- spec/lucky/action_route_params_spec.cr | 2 +- spec/lucky/action_spec.cr | 6 +++--- spec/lucky/cookies/cookie_jar_spec.cr | 2 +- spec/lucky/format_integration_spec.cr | 2 +- spec/lucky/memoize_spec.cr | 12 ++++++------ spec/lucky/paginator/paginator_spec.cr | 14 +++++++------- spec/lucky/params_spec.cr | 10 +++++----- spec/lucky/remote_ip_handler_spec.cr | 4 ++-- spec/tasks/gen/action_spec.cr | 2 +- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/spec/lucky/action_pipes_spec.cr b/spec/lucky/action_pipes_spec.cr index 3dd927370..ffd97732e 100644 --- a/spec/lucky/action_pipes_spec.cr +++ b/spec/lucky/action_pipes_spec.cr @@ -235,9 +235,9 @@ describe Lucky::Action do end Pipes::HaltedBefore.new(build_context, params).call halted_pipe = events.find! { |e| e.name == "redirect_me" } - halted_pipe.continued.should eq false + halted_pipe.continued.should be_false halted_pipe.position.to_s.should eq "Before" - halted_pipe.before?.should eq true + halted_pipe.before?.should be_true end it "publishes an event on after when halted" do @@ -247,9 +247,9 @@ describe Lucky::Action do end Pipes::HaltedAfter.new(build_context, params).call halted_pipe = events.find! { |e| e.name == "redirect_me" } - halted_pipe.continued.should eq false + halted_pipe.continued.should be_false halted_pipe.position.to_s.should eq "After" - halted_pipe.after?.should eq true + halted_pipe.after?.should be_true end end end diff --git a/spec/lucky/action_route_params_spec.cr b/spec/lucky/action_route_params_spec.cr index b91f11a97..0472e36bb 100644 --- a/spec/lucky/action_route_params_spec.cr +++ b/spec/lucky/action_route_params_spec.cr @@ -39,7 +39,7 @@ describe "Automatically generated param helpers" do action = TestOptionalParamAction.new(build_context, {"required" => "1", "optional_1" => "2"}) action.required.should eq "1" action.optional_1.should eq "2" - action.optional_2.should eq nil + action.optional_2.should be_nil typeof(action.optional_1).should eq String? typeof(action.optional_2).should eq String? end diff --git a/spec/lucky/action_spec.cr b/spec/lucky/action_spec.cr index 751141744..ae6a16055 100644 --- a/spec/lucky/action_spec.cr +++ b/spec/lucky/action_spec.cr @@ -537,7 +537,7 @@ describe Lucky::Action do it "is initialized to nil" do action = OptionalParams::Index.new(build_context(path: ""), params) - action.page.should eq nil + action.page.should be_nil end it "is fetched if present" do @@ -557,7 +557,7 @@ describe Lucky::Action do it "can specify nil as the default value" do action = OptionalParams::Index.new(build_context(path: ""), params) - action.nilable_with_explicit_nil.should eq nil + action.nilable_with_explicit_nil.should be_nil end it "overrides the default if present" do @@ -612,7 +612,7 @@ describe Lucky::Action do it "returns nil when the key is passed with no value for an optional param" do action = OptionalParams::Index.new(build_context(path: "/?optional_bool_with_no_default"), params) - action.optional_bool_with_no_default.should eq(nil) + action.optional_bool_with_no_default.should be_nil end end end diff --git a/spec/lucky/cookies/cookie_jar_spec.cr b/spec/lucky/cookies/cookie_jar_spec.cr index 7a5521fde..ea6511898 100644 --- a/spec/lucky/cookies/cookie_jar_spec.cr +++ b/spec/lucky/cookies/cookie_jar_spec.cr @@ -104,7 +104,7 @@ describe Lucky::CookieJar do cookies = HTTP::Cookies.new cookies[cookie_key] = cookie_value jar = Lucky::CookieJar.from_request_cookies(cookies) - jar.get?(cookie_key).should eq(nil) + jar.get?(cookie_key).should be_nil end describe "#set" do diff --git a/spec/lucky/format_integration_spec.cr b/spec/lucky/format_integration_spec.cr index a71c45ce3..ebce9cde6 100644 --- a/spec/lucky/format_integration_spec.cr +++ b/spec/lucky/format_integration_spec.cr @@ -63,7 +63,7 @@ describe "Format Integration" do ctx.request.path.should eq("/js/main.js") } result = handler.call(context) - result.should eq(nil) + result.should be_nil end it "supports multiple format extensions" do diff --git a/spec/lucky/memoize_spec.cr b/spec/lucky/memoize_spec.cr index 84d349d6b..6877b0d05 100644 --- a/spec/lucky/memoize_spec.cr +++ b/spec/lucky/memoize_spec.cr @@ -101,9 +101,9 @@ describe "memoizations" do it "works with predicate methods" do object = ObjectWithMemoizedMethods.new - object.method_4?.should eq(true) - object.method_4?.should eq(true) - object.method_4?.should eq(true) + object.method_4?.should be_true + object.method_4?.should be_true + object.method_4?.should be_true object.times_method_4_called.should eq(1) end @@ -119,9 +119,9 @@ describe "memoizations" do it "calls uncached with predicate and bang methods" do object = ObjectWithMemoizedMethods.new - object.method_4__uncached?.should eq(true) - object.method_4__uncached?.should eq(true) - object.method_4__uncached?.should eq(true) + object.method_4__uncached?.should be_true + object.method_4__uncached?.should be_true + object.method_4__uncached?.should be_true object.times_method_4_called.should eq(3) object.method_5__uncached!.should eq("Boom!") diff --git a/spec/lucky/paginator/paginator_spec.cr b/spec/lucky/paginator/paginator_spec.cr index d74a66996..ae00b0fc2 100644 --- a/spec/lucky/paginator/paginator_spec.cr +++ b/spec/lucky/paginator/paginator_spec.cr @@ -47,32 +47,32 @@ describe Lucky::Paginator do describe "#last_page?" do it "returns true if the current page is the last one" do - build_pages(page: 2, per_page: 1, item_count: 2).last_page?.should eq(true) - build_pages(page: 1, per_page: 1, item_count: 1).last_page?.should eq(true) + build_pages(page: 2, per_page: 1, item_count: 2).last_page?.should be_true + build_pages(page: 1, per_page: 1, item_count: 1).last_page?.should be_true end it "returns false if the current page is not the last one" do - build_pages(page: 1, per_page: 1, item_count: 2).last_page?.should eq(false) + build_pages(page: 1, per_page: 1, item_count: 2).last_page?.should be_false end end describe "#first_page?" do it "returns true if the current page is the first one" do - build_pages(page: 1, per_page: 1, item_count: 1).first_page?.should eq(true) + build_pages(page: 1, per_page: 1, item_count: 1).first_page?.should be_true end it "otherwise returns false" do - build_pages(page: 2, per_page: 1, item_count: 2).first_page?.should eq(false) + build_pages(page: 2, per_page: 1, item_count: 2).first_page?.should be_false end end describe "#overflowed?" do it "returns true if the current page is past the last page" do - build_pages(page: 2, per_page: 1, item_count: 1).overflowed?.should eq(true) + build_pages(page: 2, per_page: 1, item_count: 1).overflowed?.should be_true end it "otherwise returns false" do - build_pages(page: 1, per_page: 1, item_count: 1).overflowed?.should eq(false) + build_pages(page: 1, per_page: 1, item_count: 1).overflowed?.should be_false end end diff --git a/spec/lucky/params_spec.cr b/spec/lucky/params_spec.cr index 312321d3e..35cc2195f 100644 --- a/spec/lucky/params_spec.cr +++ b/spec/lucky/params_spec.cr @@ -420,7 +420,7 @@ describe Lucky::Params do request = build_request body: "", content_type: "" request.query = "a=1" params = Lucky::Params.new(request) - params.nested?("a").empty?.should eq true + params.nested?("a").empty?.should be_true end it "gets nested params after unescaping" do @@ -531,7 +531,7 @@ describe Lucky::Params do request = build_request body: "", content_type: "" request.query = "a[]=1" params = Lucky::Params.new(request) - params.nested_arrays?("a").empty?.should eq true + params.nested_arrays?("a").empty?.should be_true end it "gets nested array params after unescaping" do @@ -573,7 +573,7 @@ describe Lucky::Params do params = Lucky::Params.new(request) file = params.get_file(:welcome_file) - file.is_a?(Lucky::UploadedFile).should eq(true) + file.is_a?(Lucky::UploadedFile).should be_true File.read(file.path).should eq "welcome file contents" end @@ -772,7 +772,7 @@ describe Lucky::Params do request = build_request body: "", content_type: "" request.query = "a=1" params = Lucky::Params.new(request) - params.many_nested?("a").empty?.should eq true + params.many_nested?("a").empty?.should be_true end it "gets nested params after unescaping" do @@ -844,7 +844,7 @@ describe Lucky::Params do route_params = {"id" => "from_route"} params = Lucky::Params.new(request) - params.get?(:id).should eq nil + params.get?(:id).should be_nil params.route_params = route_params params.get?(:id).should eq "from_route" diff --git a/spec/lucky/remote_ip_handler_spec.cr b/spec/lucky/remote_ip_handler_spec.cr index 2d61ce834..7892bec73 100644 --- a/spec/lucky/remote_ip_handler_spec.cr +++ b/spec/lucky/remote_ip_handler_spec.cr @@ -8,7 +8,7 @@ describe Lucky::RemoteIpHandler do context = build_context(path: "/path") run_remote_ip_handler(context) - context.request.remote_address.should eq nil + context.request.remote_address.should be_nil context.request.remote_ip.should eq "" end @@ -44,7 +44,7 @@ describe Lucky::RemoteIpHandler do context = build_context(request) run_remote_ip_handler(context) - context.request.remote_address.should eq nil + context.request.remote_address.should be_nil context.request.remote_ip.should eq "" end diff --git a/spec/tasks/gen/action_spec.cr b/spec/tasks/gen/action_spec.cr index 01c2c3a38..aa2c8ad46 100644 --- a/spec/tasks/gen/action_spec.cr +++ b/spec/tasks/gen/action_spec.cr @@ -115,6 +115,6 @@ describe Gen::Action do route: %(get "/users") ) folder = template.template_folder - LuckyTemplate.snapshot(folder).has_key?("src/actions/user/index.cr").should eq(true) + LuckyTemplate.snapshot(folder).has_key?("src/actions/user/index.cr").should be_true end end From 5532eaa2c29fcb5fa9f572d801779a0c3284189d Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:09:10 +0200 Subject: [PATCH 13/19] Fix `Performance/AnyInsteadOfPresent` type of Ameba issues --- spec/lucky/cookies/flash_store_spec.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lucky/cookies/flash_store_spec.cr b/spec/lucky/cookies/flash_store_spec.cr index 1419fa775..3063510f3 100644 --- a/spec/lucky/cookies/flash_store_spec.cr +++ b/spec/lucky/cookies/flash_store_spec.cr @@ -92,14 +92,14 @@ describe Lucky::FlashStore do it "returns true if there are key/value pairs" do flash_store = build_flash_store({"some_key" => "some_value"}) - # ameba:disable Performance/AnyInsteadOfEmpty + # ameba:disable Performance/AnyInsteadOfPresent flash_store.any?.should be_true end it "returns false if there are no key/value pairs" do flash_store = build_flash_store - # ameba:disable Performance/AnyInsteadOfEmpty + # ameba:disable Performance/AnyInsteadOfPresent flash_store.any?.should be_false end end From bbe4f38303ea8bb9123f9b9fb23f50a10f2a9321 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:12:29 +0200 Subject: [PATCH 14/19] Fix `Lint/UnusedRescueVariable` type of Ameba issues --- src/lucky/cookies/cookie_jar.cr | 2 +- src/lucky/param_parser.cr | 2 +- src/lucky/support/message_verifier.cr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lucky/cookies/cookie_jar.cr b/src/lucky/cookies/cookie_jar.cr index 3db41a13c..5336745f4 100644 --- a/src/lucky/cookies/cookie_jar.cr +++ b/src/lucky/cookies/cookie_jar.cr @@ -151,7 +151,7 @@ class Lucky::CookieJar base_64_encrypted_part = cookie_value.lchop(LUCKY_ENCRYPTION_PREFIX) String.new(encryptor.verify_and_decrypt(base_64_encrypted_part)) - rescue e + rescue # an error happened while decrypting the cookie # we will treat that as if no cookie was passed end diff --git a/src/lucky/param_parser.cr b/src/lucky/param_parser.cr index a7bb501dc..a70cd3615 100644 --- a/src/lucky/param_parser.cr +++ b/src/lucky/param_parser.cr @@ -53,7 +53,7 @@ module Lucky::ParamParser begin parsed = format.parse(param) return parsed if parsed - rescue e : Time::Format::Error + rescue Time::Format::Error nil end end diff --git a/src/lucky/support/message_verifier.cr b/src/lucky/support/message_verifier.cr index 387084b79..85bfd8e90 100644 --- a/src/lucky/support/message_verifier.cr +++ b/src/lucky/support/message_verifier.cr @@ -17,7 +17,7 @@ module Lucky if valid_message?(data.to_s, digest.to_s) String.new(decode(data.to_s)) end - rescue e : Base64::Error | JSON::ParseException + rescue Base64::Error | JSON::ParseException nil end From bc9076f73b8ebc7074b66187b316e432b5a2567f Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:14:53 +0200 Subject: [PATCH 15/19] Fix `Lint/UnneededDisableDirective` type of Ameba issues --- src/lucky/format.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lucky/format.cr b/src/lucky/format.cr index e8993edff..2c60082e7 100644 --- a/src/lucky/format.cr +++ b/src/lucky/format.cr @@ -56,7 +56,6 @@ enum Lucky::Format end # Parse format from file extension - # ameba:disable Metrics/CyclomaticComplexity def self.from_extension(extension : String) : Format? case extension.downcase when "html", "htm" then Html From e8721b64806c6bb29aa4e7f8afdaa9029a06f1ac Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:16:55 +0200 Subject: [PATCH 16/19] Fix `Style/MultilineCurlyBlock` type of Ameba issues --- spec/lucky/text_helpers/highlight_spec.cr | 4 ++-- src/lucky/assignable.cr | 8 ++++---- src/lucky/request_expectations.cr | 4 ++-- src/lucky/secure_headers/set_xss_guard.cr | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/lucky/text_helpers/highlight_spec.cr b/spec/lucky/text_helpers/highlight_spec.cr index 8d4e031ea..f627a2b4e 100644 --- a/spec/lucky/text_helpers/highlight_spec.cr +++ b/spec/lucky/text_helpers/highlight_spec.cr @@ -6,11 +6,11 @@ class HighlightTestPage include Lucky::HTMLPage def test_highlight - highlight "This is a beautiful morning, but also a beautiful day", "beautiful" { |word| + highlight "This is a beautiful morning, but also a beautiful day", "beautiful" do |word| # you can't use HTMLPage here since they append to 'view' rather than return in-place # the block highlight expects is passed to gsub which expects to get a string returned "#{word}" - } + end end end diff --git a/src/lucky/assignable.cr b/src/lucky/assignable.cr index 92469e0c4..d71e00855 100644 --- a/src/lucky/assignable.cr +++ b/src/lucky/assignable.cr @@ -86,15 +86,15 @@ module Lucky::Assignable macro generate_needy_initializer {% if !@type.abstract? %} - {% sorted_assigns = ASSIGNS.sort_by { |dec| + {% sorted_assigns = ASSIGNS.sort_by do |dec| has_explicit_value = dec.type.is_a?(Metaclass) || - dec.type.types.any? { |type| + dec.type.types.any? do |type| (type.is_a?(Metaclass) || type.is_a?(ProcNotation) || type.is_a?(Generic)) ? false : type.names.includes?(Nil.id) - } || + end || !dec.value.is_a?(Nop) has_explicit_value ? 1 : 0 - } %} + end %} # Check if this is a BaseComponent - if so, don't accept unused exposures {% is_component = @type.ancestors.any? { |ancestor| ancestor.stringify == "Lucky::BaseComponent" } %} diff --git a/src/lucky/request_expectations.cr b/src/lucky/request_expectations.cr index f55dbd37f..4e84c11c5 100644 --- a/src/lucky/request_expectations.cr +++ b/src/lucky/request_expectations.cr @@ -56,7 +56,7 @@ module Lucky::RequestExpectations private def incorrect_response_body_message(actual_response : HTTP::Client::Response) : String actual_json = JSON.parse(actual_response.body).as_h - expected_json.as_h.each { |expected_key, expected_value| + expected_json.as_h.each do |expected_key, expected_value| if !actual_json.has_key?(expected_key) break <<-TEXT Expected response to have JSON key #{expected_key.dump}, but it was not present. @@ -76,7 +76,7 @@ module Lucky::RequestExpectations #{actual_json[expected_key].inspect} TEXT end - }.to_s + end.to_s end def negative_failure_message(actual_value) : String diff --git a/src/lucky/secure_headers/set_xss_guard.cr b/src/lucky/secure_headers/set_xss_guard.cr index bb3dae013..e183fa983 100644 --- a/src/lucky/secure_headers/set_xss_guard.cr +++ b/src/lucky/secure_headers/set_xss_guard.cr @@ -27,9 +27,9 @@ module Lucky private def xss_guard_value : String useragent = context.request.headers.fetch("User-Agent", "").downcase value = "1; mode=block" - useragent.match(/msie\s+(\d+)/).try { |match| + useragent.match(/msie\s+(\d+)/).try do |match| value = "0" if match[1].to_i < 9 - } + end value end end From be1b4aacbe467f6b79d9d3a084fe94a4a81c0f6f Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:17:36 +0200 Subject: [PATCH 17/19] Fix `Naming/RescuedExceptionsVariableName` type of Ameba issues --- src/lucky/cookies/cookie_jar.cr | 4 ++-- src/lucky/cookies/flash_store.cr | 4 ++-- src/lucky/json_body_parser.cr | 4 ++-- src/lucky/log_handler.cr | 6 +++--- src/lucky/mime_type.cr | 4 ++-- src/lucky/support/message_verifier.cr | 4 ++-- src/lucky/text_response.cr | 4 ++-- src/run_macros/asset_manifest_builder.cr | 6 +++--- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lucky/cookies/cookie_jar.cr b/src/lucky/cookies/cookie_jar.cr index 5336745f4..2646918cb 100644 --- a/src/lucky/cookies/cookie_jar.cr +++ b/src/lucky/cookies/cookie_jar.cr @@ -133,8 +133,8 @@ class Lucky::CookieJar raise Lucky::CookieOverflowError.new("size of '#{key}' cookie is too big") end cookies[key.to_s] = set_cookies[key.to_s] = raw_cookie - rescue e : IO::Error - raise InvalidCookieValueError.new(key, cause: e) + rescue ex : IO::Error + raise InvalidCookieValueError.new(key, cause: ex) end private def encrypt(raw_value : String) : String diff --git a/src/lucky/cookies/flash_store.cr b/src/lucky/cookies/flash_store.cr index 7fcd49187..9a616eb76 100644 --- a/src/lucky/cookies/flash_store.cr +++ b/src/lucky/cookies/flash_store.cr @@ -18,8 +18,8 @@ class Lucky::FlashStore end end self - rescue e : JSON::ParseException - raise Lucky::InvalidFlashJSONError.new(session.get?(SESSION_KEY), cause: e) + rescue ex : JSON::ParseException + raise Lucky::InvalidFlashJSONError.new(session.get?(SESSION_KEY), cause: ex) end def keep : Nil diff --git a/src/lucky/json_body_parser.cr b/src/lucky/json_body_parser.cr index 6587475ea..690e5f06b 100644 --- a/src/lucky/json_body_parser.cr +++ b/src/lucky/json_body_parser.cr @@ -12,7 +12,7 @@ class Lucky::JsonBodyParser else JSON.parse(body) end - rescue e : JSON::ParseException - raise Lucky::ParamParsingError.new(@request, cause: e) + rescue ex : JSON::ParseException + raise Lucky::ParamParsingError.new(@request, cause: ex) end end diff --git a/src/lucky/log_handler.cr b/src/lucky/log_handler.cr index 987d5dccc..6f4f141f2 100644 --- a/src/lucky/log_handler.cr +++ b/src/lucky/log_handler.cr @@ -37,9 +37,9 @@ class Lucky::LogHandler log_request_end(context, duration: duration) Lucky::Events::RequestCompleteEvent.publish(duration) end - rescue e - log_exception(context, Time.utc, e) - raise e + rescue ex + log_exception(context, Time.utc, ex) + raise ex end private def log_request_start(context : HTTP::Server::Context) : Nil diff --git a/src/lucky/mime_type.cr b/src/lucky/mime_type.cr index a162ca226..c94c2c63e 100644 --- a/src/lucky/mime_type.cr +++ b/src/lucky/mime_type.cr @@ -218,8 +218,8 @@ class Lucky::MimeType # multiplied by 1000 and then handled as an integer. begin ($1.to_f32 * 1000).round.to_u16 - rescue e : ArgumentError | OverflowError - raise InvalidMediaRange.new("#{parameter} is not a valid qvalue", cause: e) + rescue ex : ArgumentError | OverflowError + raise InvalidMediaRange.new("#{parameter} is not a valid qvalue", cause: ex) end else 1000u16 diff --git a/src/lucky/support/message_verifier.cr b/src/lucky/support/message_verifier.cr index 85bfd8e90..e5c4704e0 100644 --- a/src/lucky/support/message_verifier.cr +++ b/src/lucky/support/message_verifier.cr @@ -38,8 +38,8 @@ module Lucky else raise(InvalidSignatureError.new) end - rescue e : Base64::Error | JSON::ParseException - raise InvalidSignatureError.new(cause: e) + rescue ex : Base64::Error | JSON::ParseException + raise InvalidSignatureError.new(cause: ex) end def generate(value : String | Bytes) : String diff --git a/src/lucky/text_response.cr b/src/lucky/text_response.cr index b1f876975..3e1fae81d 100644 --- a/src/lucky/text_response.cr +++ b/src/lucky/text_response.cr @@ -32,8 +32,8 @@ class Lucky::TextResponse < Lucky::Response context.response.headers.add "Date", HTTP.format_time(Time.utc) gzip if should_gzip? context.response.print(body) if should_print? - rescue e : IO::Error - Lucky::Log.error(exception: e) { "Broken Pipe: Maybe the client navigated away?" } + rescue ex : IO::Error + Lucky::Log.error(exception: ex) { "Broken Pipe: Maybe the client navigated away?" } end def status : Int diff --git a/src/run_macros/asset_manifest_builder.cr b/src/run_macros/asset_manifest_builder.cr index 998b6f960..3762ea477 100644 --- a/src/run_macros/asset_manifest_builder.cr +++ b/src/run_macros/asset_manifest_builder.cr @@ -179,7 +179,7 @@ begin manifest_file = ARGV[1]? || "" AssetManifestBuilder.new(source, manifest_file).build_with_retry -rescue e - puts e.message.try(&.colorize(:red)) - raise e +rescue ex + puts ex.message.try(&.colorize(:red)) + raise ex end From 368cd0d8d331bfb3e49868a6584ec7b585a706d1 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:21:10 +0200 Subject: [PATCH 18/19] Ignore some of the `Lint/AssignmentInCallArgument` issues --- .ameba.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ameba.yml b/.ameba.yml index f143102ea..b7c7cdf74 100644 --- a/.ameba.yml +++ b/.ameba.yml @@ -5,3 +5,7 @@ Lint/Typos: - spec/lucky/text_helpers/truncate_spec.cr - spec/lucky/text_helpers/excerpts_spec.cr - spec/lucky/secure_headers_spec.cr + +Lint/AssignmentInCallArgument: + Excluded: + - spec/lucky/memoize_spec.cr From c5be1e5af15e5314585957efe442f84b3eebccd9 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 20 Jun 2026 16:13:14 +0200 Subject: [PATCH 19/19] =?UTF-8?q?Remove=20redundant=20`begin=20=E2=80=A6?= =?UTF-8?q?=20end`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lucky/mime_type.cr | 10 ++++------ src/lucky/param_parser.cr | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/lucky/mime_type.cr b/src/lucky/mime_type.cr index c94c2c63e..3a89f012e 100644 --- a/src/lucky/mime_type.cr +++ b/src/lucky/mime_type.cr @@ -121,12 +121,10 @@ class Lucky::MimeType # quality value. def self.parse(accept : String) : Array(MediaRange) list = accept.split(ACCEPT_SEP).compact_map do |range| - begin - MediaRange.parse(range) - rescue ex : InvalidMediaRange - Log.debug { "invalid media range in Accept: #{accept} - #{ex}" } - nil - end + MediaRange.parse(range) + rescue ex : InvalidMediaRange + Log.debug { "invalid media range in Accept: #{accept} - #{ex}" } + nil end list.unstable_sort_by! { |range| -range.qvalue.to_i32 } end diff --git a/src/lucky/param_parser.cr b/src/lucky/param_parser.cr index a70cd3615..1956ce195 100644 --- a/src/lucky/param_parser.cr +++ b/src/lucky/param_parser.cr @@ -50,12 +50,10 @@ module Lucky::ParamParser def self.parse(param : String, klass : Time.class) : Time? TIME_FORMATS.each do |format| - begin - parsed = format.parse(param) - return parsed if parsed - rescue Time::Format::Error - nil - end + parsed = format.parse(param) + return parsed if parsed + rescue Time::Format::Error + nil end end