From 2ccfc144a409a85f1480ee66e47a05738f17eaf1 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 00:07:29 +0800 Subject: [PATCH 01/26] add crytsal client generator --- bin/configs/crystal.yaml | 9 + .../languages/CrystalClientCodegen.java | 891 ++++++++++++++++ .../mustache/PrefixWithHashLambda.java | 49 + .../org.openapitools.codegen.CodegenConfig | 1 + .../main/resources/crystal/Gemfile.mustache | 9 + .../main/resources/crystal/README.mustache | 46 + .../main/resources/crystal/Rakefile.mustache | 10 + .../src/main/resources/crystal/api.mustache | 185 ++++ .../resources/crystal/api_client.mustache | 396 +++++++ .../api_client_faraday_partial.mustache | 138 +++ .../api_client_typhoeus_partial.mustache | 153 +++ .../main/resources/crystal/api_doc.mustache | 118 +++ .../main/resources/crystal/api_error.mustache | 33 + .../main/resources/crystal/api_info.mustache | 12 + .../main/resources/crystal/api_test.mustache | 38 + .../resources/crystal/base_object.mustache | 120 +++ .../resources/crystal/configuration.mustache | 356 +++++++ .../crystal/configuration_spec.mustache | 34 + ...configuration_tls_faraday_partial.mustache | 29 + ...onfiguration_tls_typhoeus_partial.mustache | 34 + .../resources/crystal/git_push.sh.mustache | 58 ++ .../main/resources/crystal/gitignore.mustache | 39 + .../src/main/resources/crystal/model.mustache | 23 + .../main/resources/crystal/model_doc.mustache | 12 + .../resources/crystal/model_test.mustache | 75 ++ .../crystal/partial_model_enum_class.mustache | 20 + .../crystal/partial_model_generic.mustache | 296 ++++++ .../partial_model_generic_doc.mustache | 28 + .../crystal/partial_oneof_module.mustache | 137 +++ .../crystal/partial_oneof_module_doc.mustache | 92 ++ .../src/main/resources/crystal/rspec.mustache | 2 + .../main/resources/crystal/rubocop.mustache | 148 +++ .../src/main/resources/crystal/shard.mustache | 20 + .../resources/crystal/shard_name.mustache | 25 + .../resources/crystal/spec_helper.mustache | 6 + .../main/resources/crystal/travis.mustache | 8 + .../main/resources/crystal/version.mustache | 5 + samples/client/petstore/crystal/.gitignore | 39 + .../crystal/.openapi-generator-ignore | 23 + .../petstore/crystal/.openapi-generator/FILES | 19 + .../crystal/.openapi-generator/VERSION | 1 + samples/client/petstore/crystal/.rspec | 2 + samples/client/petstore/crystal/.rubocop.yml | 148 +++ samples/client/petstore/crystal/.travis.yml | 16 + samples/client/petstore/crystal/README.md | 38 + samples/client/petstore/crystal/bin/ameba | Bin 0 -> 7907132 bytes samples/client/petstore/crystal/bin/ameba.cr | 7 + samples/client/petstore/crystal/git_push.sh | 58 ++ .../client/petstore/crystal/lib/.shards.info | 27 + .../petstore/crystal/lib/ameba/.dockerignore | 4 + .../petstore/crystal/lib/ameba/.editorconfig | 7 + .../crystal/lib/ameba/.github/FUNDING.yml | 9 + .../petstore/crystal/lib/ameba/.gitignore | 11 + .../petstore/crystal/lib/ameba/.travis.yml | 28 + .../petstore/crystal/lib/ameba/Dockerfile | 13 + .../client/petstore/crystal/lib/ameba/LICENSE | 21 + .../petstore/crystal/lib/ameba/Makefile | 21 + .../petstore/crystal/lib/ameba/README.md | 249 +++++ .../crystal/lib/ameba/bench/check_sources.cr | 30 + .../petstore/crystal/lib/ameba/bin/.keep | 0 .../petstore/crystal/lib/ameba/bin/ameba.cr | 7 + samples/client/petstore/crystal/lib/ameba/lib | 1 + .../petstore/crystal/lib/ameba/shard.yml | 20 + .../lib/ameba/spec/ameba/ast/branch_spec.cr | 363 +++++++ .../ameba/spec/ameba/ast/branchable_spec.cr | 49 + .../spec/ameba/ast/flow_expression_spec.cr | 56 + .../lib/ameba/spec/ameba/ast/scope_spec.cr | 143 +++ .../lib/ameba/spec/ameba/ast/util_spec.cr | 326 ++++++ .../ameba/ast/variabling/argument_spec.cr | 41 + .../ameba/ast/variabling/assignment_spec.cr | 118 +++ .../ameba/ast/variabling/reference_spec.cr | 10 + .../ameba/ast/variabling/variable_spec.cr | 218 ++++ .../ast/visitors/counting_visitor_spec.cr | 65 ++ .../visitors/flow_expression_visitor_spec.cr | 66 ++ .../ameba/ast/visitors/node_visitor_spec.cr | 16 + ...dundant_control_expression_visitor_spec.cr | 26 + .../ameba/ast/visitors/scope_visitor_spec.cr | 58 ++ .../crystal/lib/ameba/spec/ameba/base_spec.cr | 75 ++ .../lib/ameba/spec/ameba/cli/cmd_spec.cr | 165 +++ .../lib/ameba/spec/ameba/config_spec.cr | 215 ++++ .../formatter/disabled_formatter_spec.cr | 42 + .../ameba/formatter/dot_formatter_spec.cr | 107 ++ .../ameba/formatter/explain_formatter_spec.cr | 71 ++ .../formatter/flycheck_formatter_spec.cr | 48 + .../ameba/formatter/json_formatter_spec.cr | 99 ++ .../ameba/formatter/todo_formatter_spec.cr | 127 +++ .../ameba/spec/ameba/formatter/util_spec.cr | 29 + .../lib/ameba/spec/ameba/glob_utils_spec.cr | 50 + .../ameba/spec/ameba/inline_comments_spec.cr | 161 +++ .../lib/ameba/spec/ameba/issue_spec.cr | 50 + .../lib/ameba/spec/ameba/reportable_spec.cr | 40 + .../lib/ameba/spec/ameba/rule/base_spec.cr | 32 + .../ameba/rule/layout/line_length_spec.cr | 43 + .../rule/layout/trailing_blank_lines_spec.cr | 58 ++ .../rule/layout/trailing_whitespace_spec.cr | 28 + .../ameba/rule/lint/bad_directive_spec.cr | 66 ++ .../rule/lint/comparison_to_boolean_spec.cr | 114 ++ .../rule/lint/debugger_statement_spec.cr | 45 + .../spec/ameba/rule/lint/empty_ensure_spec.cr | 69 ++ .../ameba/rule/lint/empty_expression_spec.cr | 127 +++ .../spec/ameba/rule/lint/empty_loop_spec.cr | 88 ++ .../rule/lint/hash_duplicated_key_spec.cr | 51 + .../rule/lint/literal_in_condition_spec.cr | 179 ++++ .../lint/literal_in_interpolation_spec.cr | 51 + .../ameba/rule/lint/percent_arrays_spec.cr | 86 ++ .../spec/ameba/rule/lint/rand_zero_spec.cr | 37 + .../lint/redundant_string_cercion_spec.cr | 97 ++ .../rule/lint/redundant_with_index_spec.cr | 163 +++ .../rule/lint/redundant_with_object_spec.cr | 83 ++ .../ameba/rule/lint/shadowed_argument_spec.cr | 166 +++ .../rule/lint/shadowed_exception_spec.cr | 176 ++++ .../lint/shadowing_local_outer_var_spec.cr | 241 +++++ .../rule/lint/shared_var_in_fiber_spec.cr | 236 +++++ .../ameba/spec/ameba/rule/lint/syntax_spec.cr | 41 + .../lint/unneded_disable_directive_spec.cr | 102 ++ .../ameba/rule/lint/unreachable_code_spec.cr | 701 +++++++++++++ .../ameba/rule/lint/unused_argument_spec.cr | 306 ++++++ .../ameba/rule/lint/useless_assign_spec.cr | 977 ++++++++++++++++++ .../lint/useless_condition_in_when_spec.cr | 46 + .../metrics/cyclomatic_complexity_spec.cr | 47 + .../rule/performance/any_after_filter_spec.cr | 73 ++ .../first_last_after_filter_spec.cr | 141 +++ .../performance/size_after_filter_spec.cr | 74 ++ .../ameba/rule/style/constant_names_spec.cr | 54 + .../spec/ameba/rule/style/is_a_nil_spec.cr | 45 + .../ameba/rule/style/large_numbers_spec.cr | 134 +++ .../ameba/rule/style/method_names_spec.cr | 56 + .../negated_conditions_in_unless_spec.cr | 69 ++ .../ameba/rule/style/predicate_name_spec.cr | 60 ++ .../ameba/rule/style/redundant_begin_spec.cr | 227 ++++ .../ameba/rule/style/redundant_next_spec.cr | 219 ++++ .../ameba/rule/style/redundant_return_spec.cr | 266 +++++ .../spec/ameba/rule/style/type_names_spec.cr | 61 ++ .../spec/ameba/rule/style/unless_else_spec.cr | 45 + .../ameba/rule/style/variable_names_spec.cr | 62 ++ .../spec/ameba/rule/style/while_true_spec.cr | 43 + .../lib/ameba/spec/ameba/runner_spec.cr | 183 ++++ .../lib/ameba/spec/ameba/severity_spec.cr | 108 ++ .../lib/ameba/spec/ameba/source_spec.cr | 38 + .../lib/ameba/spec/ameba/tokenizer_spec.cr | 44 + .../crystal/lib/ameba/spec/ameba_spec.cr | 4 + .../crystal/lib/ameba/spec/spec_helper.cr | 200 ++++ .../petstore/crystal/lib/ameba/src/ameba.cr | 42 + .../crystal/lib/ameba/src/ameba/ast/branch.cr | 194 ++++ .../lib/ameba/src/ameba/ast/branchable.cr | 44 + .../ameba/src/ameba/ast/flow_expression.cr | 69 ++ .../crystal/lib/ameba/src/ameba/ast/scope.cr | 180 ++++ .../crystal/lib/ameba/src/ameba/ast/util.cr | 157 +++ .../src/ameba/ast/variabling/argument.cr | 49 + .../src/ameba/ast/variabling/assignment.cr | 106 ++ .../src/ameba/ast/variabling/ivariable.cr | 13 + .../src/ameba/ast/variabling/reference.cr | 10 + .../src/ameba/ast/variabling/variable.cr | 198 ++++ .../src/ameba/ast/visitors/base_visitor.cr | 29 + .../ameba/ast/visitors/counting_visitor.cr | 39 + .../ast/visitors/flow_expression_visitor.cr | 67 ++ .../src/ameba/ast/visitors/node_visitor.cr | 61 ++ .../redundant_control_expression_visitor.cr | 62 ++ .../src/ameba/ast/visitors/scope_visitor.cr | 185 ++++ .../crystal/lib/ameba/src/ameba/cli/cmd.cr | 152 +++ .../crystal/lib/ameba/src/ameba/config.cr | 280 +++++ .../src/ameba/formatter/base_formatter.cr | 32 + .../src/ameba/formatter/disabled_formatter.cr | 17 + .../src/ameba/formatter/dot_formatter.cr | 96 ++ .../src/ameba/formatter/explain_formatter.cr | 83 ++ .../src/ameba/formatter/flycheck_formatter.cr | 18 + .../src/ameba/formatter/json_formatter.cr | 152 +++ .../src/ameba/formatter/todo_formatter.cr | 68 ++ .../lib/ameba/src/ameba/formatter/util.cr | 23 + .../crystal/lib/ameba/src/ameba/glob_utils.cr | 37 + .../lib/ameba/src/ameba/inline_comments.cr | 104 ++ .../crystal/lib/ameba/src/ameba/issue.cr | 26 + .../crystal/lib/ameba/src/ameba/reportable.cr | 41 + .../crystal/lib/ameba/src/ameba/rule/base.cr | 197 ++++ .../src/ameba/rule/layout/line_length.cr | 29 + .../ameba/rule/layout/trailing_blank_lines.cr | 33 + .../ameba/rule/layout/trailing_whitespace.cr | 25 + .../src/ameba/rule/lint/bad_directive.cr | 54 + .../ameba/rule/lint/comparison_to_boolean.cr | 41 + .../src/ameba/rule/lint/debugger_statement.cr | 29 + .../ameba/src/ameba/rule/lint/empty_ensure.cr | 56 + .../src/ameba/rule/lint/empty_expression.cr | 56 + .../ameba/src/ameba/rule/lint/empty_loop.cr | 68 ++ .../ameba/rule/lint/hash_duplicated_key.cr | 43 + .../ameba/rule/lint/literal_in_condition.cr | 50 + .../rule/lint/literal_in_interpolation.cr | 34 + .../src/ameba/rule/lint/percent_array.cr | 74 ++ .../ameba/src/ameba/rule/lint/rand_zero.cr | 44 + .../rule/lint/redundant_string_coercion.cr | 47 + .../ameba/rule/lint/redundant_with_index.cr | 58 ++ .../ameba/rule/lint/redundant_with_object.cr | 51 + .../src/ameba/rule/lint/shadowed_argument.cr | 58 ++ .../src/ameba/rule/lint/shadowed_exception.cr | 77 ++ .../rule/lint/shadowing_local_outer_var.cr | 69 ++ .../ameba/rule/lint/shared_var_in_fiber.cr | 86 ++ .../lib/ameba/src/ameba/rule/lint/syntax.cr | 34 + .../rule/lint/unneeded_disable_directive.cr | 67 ++ .../src/ameba/rule/lint/unreachable_code.cr | 64 ++ .../src/ameba/rule/lint/unused_argument.cr | 65 ++ .../src/ameba/rule/lint/useless_assign.cr | 51 + .../rule/lint/useless_condition_in_when.cr | 83 ++ .../rule/metrics/cyclomatic_complexity.cr | 38 + .../rule/performance/any_after_filter.cr | 46 + .../performance/first_last_after_filter.cr | 57 + .../rule/performance/size_after_filter.cr | 61 ++ .../src/ameba/rule/style/constant_names.cr | 43 + .../ameba/src/ameba/rule/style/is_a_nil.cr | 38 + .../src/ameba/rule/style/large_numbers.cr | 109 ++ .../src/ameba/rule/style/method_names.cr | 63 ++ .../style/negated_conditions_in_unless.cr | 55 + .../src/ameba/rule/style/predicate_name.cr | 49 + .../src/ameba/rule/style/redundant_begin.cr | 131 +++ .../src/ameba/rule/style/redundant_next.cr | 119 +++ .../src/ameba/rule/style/redundant_return.cr | 116 +++ .../ameba/src/ameba/rule/style/type_names.cr | 90 ++ .../ameba/src/ameba/rule/style/unless_else.cr | 58 ++ .../src/ameba/rule/style/variable_names.cr | 51 + .../ameba/src/ameba/rule/style/while_true.cr | 41 + .../crystal/lib/ameba/src/ameba/runner.cr | 145 +++ .../crystal/lib/ameba/src/ameba/severity.cr | 49 + .../crystal/lib/ameba/src/ameba/source.cr | 69 ++ .../crystal/lib/ameba/src/ameba/tokenizer.cr | 111 ++ .../petstore/crystal/lib/ameba/src/cli.cr | 3 + .../lib/crest/.github/workflows/crystal.yml | 49 + .../petstore/crystal/lib/crest/.gitignore | 13 + .../petstore/crystal/lib/crest/.travis.yml | 24 + .../petstore/crystal/lib/crest/CHANGELOG.md | 237 +++++ .../client/petstore/crystal/lib/crest/LICENSE | 21 + .../petstore/crystal/lib/crest/README.md | 614 +++++++++++ samples/client/petstore/crystal/lib/crest/lib | 1 + .../petstore/crystal/lib/crest/logo/icon.png | Bin 0 -> 2985 bytes .../petstore/crystal/lib/crest/logo/icon.svg | 1 + .../lib/crest/logo/logotype_horizontal.png | Bin 0 -> 7246 bytes .../lib/crest/logo/logotype_horizontal.svg | 1 + .../crystal/lib/crest/playground/requests.md | 38 + .../petstore/crystal/lib/crest/shard.yml | 27 + .../crest/spec/integration/basic_auth_spec.cr | 75 ++ .../crest/spec/integration/cookies_spec.cr | 41 + .../lib/crest/spec/integration/crest_spec.cr | 73 ++ .../crest/spec/integration/exception_spec.cr | 82 ++ .../crest/spec/integration/headers_spec.cr | 20 + .../lib/crest/spec/integration/logger_spec.cr | 18 + .../lib/crest/spec/integration/proxy_spec.cr | 61 ++ .../spec/integration/redirection_spec.cr | 77 ++ .../crest/spec/integration/request_spec.cr | 175 ++++ .../crest/spec/integration/resource_spec.cr | 210 ++++ .../crest/spec/integration/response_spec.cr | 57 + .../crest/spec/integration/streaming_spec.cr | 120 +++ .../crystal/lib/crest/spec/spec_helper.cr | 40 + .../spec/support/ext/kemal/ext/context.cr | 10 + .../crystal/lib/crest/spec/support/fff.png | Bin 0 -> 106 bytes .../crest/spec/support/kemal_basic_auth.cr | 35 + .../crystal/lib/crest/spec/support/server.cr | 229 ++++ .../lib/crest/spec/support/server_cli.cr | 3 + .../crystal/lib/crest/spec/unit/crest_spec.cr | 7 + .../lib/crest/spec/unit/curlify_spec.cr | 95 ++ .../lib/crest/spec/unit/data_form_spec.cr | 31 + .../lib/crest/spec/unit/exceptions_spec.cr | 11 + .../crest/spec/unit/params_encoder_spec.cr | 138 +++ .../lib/crest/spec/unit/request_spec.cr | 141 +++ .../lib/crest/spec/unit/resource_spec.cr | 48 + .../crest/spec/unit/urlencoded_form_spec.cr | 29 + .../petstore/crystal/lib/crest/src/crest.cr | 75 ++ .../crystal/lib/crest/src/crest/curlify.cr | 93 ++ .../crystal/lib/crest/src/crest/exceptions.cr | 125 +++ .../crystal/lib/crest/src/crest/form.cr | 17 + .../lib/crest/src/crest/forms/data_form.cr | 43 + .../crest/src/crest/forms/urlencoded_form.cr | 21 + .../crystal/lib/crest/src/crest/logger.cr | 53 + .../crest/src/crest/loggers/common_logger.cr | 77 ++ .../lib/crest/src/crest/params_encoder.cr | 130 +++ .../crystal/lib/crest/src/crest/redirector.cr | 89 ++ .../crystal/lib/crest/src/crest/request.cr | 327 ++++++ .../crystal/lib/crest/src/crest/resource.cr | 176 ++++ .../crystal/lib/crest/src/crest/response.cr | 118 +++ .../crystal/lib/exception_page/.editorconfig | 9 + .../crystal/lib/exception_page/.gitignore | 9 + .../crystal/lib/exception_page/.travis.yml | 28 + .../crystal/lib/exception_page/LICENSE | 21 + .../crystal/lib/exception_page/README.md | 111 ++ .../petstore/crystal/lib/exception_page/lib | 1 + .../crystal/lib/exception_page/shard.yml | 18 + .../spec/exception_page_spec.cr | 34 + .../lib/exception_page/spec/frame_spec.cr | 27 + .../lib/exception_page/spec/spec_helper.cr | 26 + .../spec/support/app_exception_page.cr | 19 + .../spec/support/test_server.cr | 29 + .../lib/exception_page/src/exception_page.cr | 54 + .../src/exception_page/exception_page.ecr | 857 +++++++++++++++ .../src/exception_page/frame.cr | 77 ++ .../src/exception_page/frame_generator.cr | 13 + .../src/exception_page/styles.cr | 18 + .../src/exception_page/version.cr | 3 + .../lib/http-client-digest_auth/.editorconfig | 9 + .../lib/http-client-digest_auth/.gitignore | 5 + .../lib/http-client-digest_auth/.travis.yml | 5 + .../lib/http-client-digest_auth/LICENSE | 21 + .../lib/http-client-digest_auth/README.md | 69 ++ .../crystal/lib/http-client-digest_auth/lib | 1 + .../samples/http_client_digest_auth.cr | 32 + .../lib/http-client-digest_auth/shard.yml | 13 + .../spec/http/client/digest_auth_spec.cr | 116 +++ .../spec/spec_helper.cr | 2 + .../src/http-client-digest_auth.cr | 5 + .../src/http/client/digest_auth.cr | 168 +++ .../http_proxy/.github/workflows/crystal.yml | 49 + .../crystal/lib/http_proxy/.gitignore | 10 + .../crystal/lib/http_proxy/CHANGELOG.md | 66 ++ .../petstore/crystal/lib/http_proxy/LICENSE | 22 + .../petstore/crystal/lib/http_proxy/README.md | 135 +++ .../petstore/crystal/lib/http_proxy/lib | 1 + .../crystal/lib/http_proxy/samples/client.cr | 40 + .../crystal/lib/http_proxy/samples/server.cr | 23 + .../samples/server_with_authentication.cr | 38 + .../http_proxy/samples/with_proxy_server.cr | 60 ++ .../petstore/crystal/lib/http_proxy/shard.yml | 14 + .../lib/http_proxy/spec/client_spec.cr | 53 + .../lib/http_proxy/spec/server_spec.cr | 17 + .../lib/http_proxy/spec/spec_helper.cr | 27 + .../lib/http_proxy/src/http/proxy/client.cr | 176 ++++ .../lib/http_proxy/src/http/proxy/server.cr | 53 + .../src/http/proxy/server/basic_auth.cr | 27 + .../src/http/proxy/server/context.cr | 59 ++ .../src/http/proxy/server/handler.cr | 17 + .../crystal/lib/http_proxy/src/http_proxy.cr | 12 + .../petstore/crystal/lib/kemal/.ameba.yml | 13 + .../crystal/lib/kemal/.github/FUNDING.yml | 8 + .../lib/kemal/.github/ISSUE_TEMPLATE.md | 23 + .../kemal/.github/PULL_REQUEST_TEMPLATE.md | 15 + .../petstore/crystal/lib/kemal/.gitignore | 8 + .../petstore/crystal/lib/kemal/.travis.yml | 14 + .../petstore/crystal/lib/kemal/CHANGELOG.md | 385 +++++++ .../client/petstore/crystal/lib/kemal/LICENSE | 19 + .../petstore/crystal/lib/kemal/README.md | 67 ++ samples/client/petstore/crystal/lib/kemal/lib | 1 + .../crystal/lib/kemal/samples/hello_world.cr | 8 + .../crystal/lib/kemal/samples/json_api.cr | 11 + .../lib/kemal/samples/websocket_server.cr | 11 + .../petstore/crystal/lib/kemal/shard.yml | 25 + .../crystal/lib/kemal/spec/all_spec.cr | 1 + .../crystal/lib/kemal/spec/asset/hello.ecr | 1 + .../spec/asset/hello_with_content_for.ecr | 5 + .../crystal/lib/kemal/spec/asset/layout.ecr | 1 + .../kemal/spec/asset/layout_with_yield.ecr | 6 + .../spec/asset/layout_with_yield_and_vars.ecr | 8 + .../crystal/lib/kemal/spec/config_spec.cr | 61 ++ .../crystal/lib/kemal/spec/context_spec.cr | 107 ++ .../lib/kemal/spec/exception_handler_spec.cr | 115 +++ .../crystal/lib/kemal/spec/handler_spec.cr | 161 +++ .../crystal/lib/kemal/spec/helpers_spec.cr | 173 ++++ .../lib/kemal/spec/init_handler_spec.cr | 32 + .../lib/kemal/spec/log_handler_spec.cr | 21 + .../lib/kemal/spec/middleware/filters_spec.cr | 190 ++++ .../lib/kemal/spec/param_parser_spec.cr | 204 ++++ .../lib/kemal/spec/route_handler_spec.cr | 146 +++ .../crystal/lib/kemal/spec/route_spec.cr | 25 + .../crystal/lib/kemal/spec/run_spec.cr | 46 + .../crystal/lib/kemal/spec/spec_helper.cr | 91 ++ .../lib/kemal/spec/static/dir/bigger.txt | 9 + .../lib/kemal/spec/static/dir/index.html | 12 + .../lib/kemal/spec/static/dir/test.txt | 2 + .../kemal/spec/static_file_handler_spec.cr | 153 +++ .../crystal/lib/kemal/spec/view_spec.cr | 62 ++ .../lib/kemal/spec/websocket_handler_spec.cr | 68 ++ .../petstore/crystal/lib/kemal/src/kemal.cr | 98 ++ .../lib/kemal/src/kemal/base_log_handler.cr | 9 + .../crystal/lib/kemal/src/kemal/cli.cr | 55 + .../crystal/lib/kemal/src/kemal/config.cr | 168 +++ .../crystal/lib/kemal/src/kemal/dsl.cr | 37 + .../lib/kemal/src/kemal/exception_handler.cr | 30 + .../lib/kemal/src/kemal/ext/context.cr | 62 ++ .../lib/kemal/src/kemal/ext/response.cr | 14 + .../lib/kemal/src/kemal/file_upload.cr | 24 + .../lib/kemal/src/kemal/filter_handler.cr | 90 ++ .../crystal/lib/kemal/src/kemal/handler.cr | 80 ++ .../kemal/src/kemal/helpers/exception_page.cr | 38 + .../lib/kemal/src/kemal/helpers/exceptions.cr | 20 + .../lib/kemal/src/kemal/helpers/helpers.cr | 270 +++++ .../lib/kemal/src/kemal/helpers/macros.cr | 98 ++ .../lib/kemal/src/kemal/helpers/templates.cr | 35 + .../lib/kemal/src/kemal/helpers/utils.cr | 13 + .../lib/kemal/src/kemal/init_handler.cr | 15 + .../lib/kemal/src/kemal/log_handler.cr | 28 + .../lib/kemal/src/kemal/null_log_handler.cr | 11 + .../lib/kemal/src/kemal/param_parser.cr | 113 ++ .../crystal/lib/kemal/src/kemal/route.cr | 17 + .../lib/kemal/src/kemal/route_handler.cr | 68 ++ .../crystal/lib/kemal/src/kemal/ssl.cr | 17 + .../kemal/src/kemal/static_file_handler.cr | 68 ++ .../crystal/lib/kemal/src/kemal/websocket.cr | 14 + .../lib/kemal/src/kemal/websocket_handler.cr | 41 + .../petstore/crystal/lib/kilt/.gitignore | 10 + .../petstore/crystal/lib/kilt/.travis.yml | 1 + .../client/petstore/crystal/lib/kilt/LICENSE | 21 + .../petstore/crystal/lib/kilt/README.md | 105 ++ samples/client/petstore/crystal/lib/kilt/lib | 1 + .../petstore/crystal/lib/kilt/shard.yml | 15 + .../crystal/lib/kilt/spec/fixtures/test.ecr | 1 + .../lib/kilt/spec/fixtures/test.mustache | 1 + .../crystal/lib/kilt/spec/fixtures/test.raw | 1 + .../crystal/lib/kilt/spec/fixtures/test.slang | 1 + .../crystal/lib/kilt/spec/fixtures/test.temel | 1 + .../lib/kilt/spec/kilt/crustache_spec.cr | 26 + .../crystal/lib/kilt/spec/kilt/slang_spec.cr | 18 + .../crystal/lib/kilt/spec/kilt/temel_spec.cr | 18 + .../crystal/lib/kilt/spec/kilt_spec.cr | 28 + .../crystal/lib/kilt/spec/spec_helper.cr | 3 + .../lib/kilt/spec/support/raw_engine.cr | 5 + .../petstore/crystal/lib/kilt/src/crikey.cr | 4 + .../crystal/lib/kilt/src/crustache.cr | 4 + .../petstore/crystal/lib/kilt/src/ecr.cr | 3 + .../petstore/crystal/lib/kilt/src/kilt.cr | 35 + .../crystal/lib/kilt/src/kilt/exception.cr | 5 + .../kilt/src/kilt/helpers/temel_embedder.cr | 3 + .../crystal/lib/kilt/src/kilt/version.cr | 3 + .../petstore/crystal/lib/kilt/src/slang.cr | 4 + .../petstore/crystal/lib/kilt/src/temel.cr | 9 + .../petstore/crystal/lib/radix/.gitignore | 8 + .../petstore/crystal/lib/radix/.travis.yml | 11 + .../petstore/crystal/lib/radix/CHANGELOG.md | 102 ++ .../client/petstore/crystal/lib/radix/LICENSE | 21 + .../petstore/crystal/lib/radix/Makefile | 18 + .../petstore/crystal/lib/radix/README.md | 129 +++ samples/client/petstore/crystal/lib/radix/lib | 1 + .../petstore/crystal/lib/radix/shard.yml | 7 + .../crystal/lib/radix/spec/radix/node_spec.cr | 150 +++ .../lib/radix/spec/radix/result_spec.cr | 76 ++ .../crystal/lib/radix/spec/radix/tree_spec.cr | 626 +++++++++++ .../lib/radix/spec/radix/version_spec.cr | 12 + .../crystal/lib/radix/spec/spec_helper.cr | 2 + .../petstore/crystal/lib/radix/src/radix.cr | 2 + .../crystal/lib/radix/src/radix/node.cr | 207 ++++ .../crystal/lib/radix/src/radix/result.cr | 88 ++ .../crystal/lib/radix/src/radix/tree.cr | 472 +++++++++ .../crystal/lib/radix/src/radix/version.cr | 3 + samples/client/petstore/crystal/shard.lock | 34 + samples/client/petstore/crystal/shard.yml | 20 + .../petstore/crystal/spec/api/pet_api_spec.cr | 129 +++ .../crystal/spec/api/store_api_spec.cr | 72 ++ .../crystal/spec/api/user_api_spec.cr | 118 +++ .../crystal/spec/models/api_response_spec.cr | 44 + .../crystal/spec/models/category_spec.cr | 38 + .../crystal/spec/models/order_spec.cr | 66 ++ .../petstore/crystal/spec/models/pet_spec.cr | 66 ++ .../petstore/crystal/spec/models/tag_spec.cr | 38 + .../petstore/crystal/spec/models/user_spec.cr | 74 ++ .../petstore/crystal/spec/spec_helper.cr | 14 + .../client/petstore/crystal/src/petstore.cr | 33 + .../crystal/src/petstore/api/pet_api.cr | 493 +++++++++ .../crystal/src/petstore/api/store_api.cr | 256 +++++ .../crystal/src/petstore/api/user_api.cr | 491 +++++++++ .../crystal/src/petstore/api_client.cr | 404 ++++++++ .../crystal/src/petstore/api_error.cr | 41 + .../crystal/src/petstore/configuration.cr | 271 +++++ .../src/petstore/models/api_response.cr | 188 ++++ .../crystal/src/petstore/models/category.cr | 200 ++++ .../crystal/src/petstore/models/order.cr | 239 +++++ .../crystal/src/petstore/models/pet.cr | 249 +++++ .../crystal/src/petstore/models/tag.cr | 183 ++++ .../crystal/src/petstore/models/user.cr | 214 ++++ 460 files changed, 36741 insertions(+) create mode 100644 bin/configs/crystal.yaml create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/PrefixWithHashLambda.java create mode 100644 modules/openapi-generator/src/main/resources/crystal/Gemfile.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/Rakefile.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_client.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_client_faraday_partial.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_client_typhoeus_partial.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_error.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_info.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/api_test.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/base_object.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/configuration.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/configuration_spec.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/configuration_tls_faraday_partial.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/configuration_tls_typhoeus_partial.mustache create mode 100755 modules/openapi-generator/src/main/resources/crystal/git_push.sh.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/gitignore.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/model.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/model_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/model_test.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/partial_model_enum_class.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/partial_model_generic.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/partial_model_generic_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/partial_oneof_module.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/partial_oneof_module_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/rspec.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/rubocop.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/shard.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/shard_name.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/spec_helper.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/travis.mustache create mode 100644 modules/openapi-generator/src/main/resources/crystal/version.mustache create mode 100644 samples/client/petstore/crystal/.gitignore create mode 100644 samples/client/petstore/crystal/.openapi-generator-ignore create mode 100644 samples/client/petstore/crystal/.openapi-generator/FILES create mode 100644 samples/client/petstore/crystal/.openapi-generator/VERSION create mode 100644 samples/client/petstore/crystal/.rspec create mode 100644 samples/client/petstore/crystal/.rubocop.yml create mode 100644 samples/client/petstore/crystal/.travis.yml create mode 100644 samples/client/petstore/crystal/README.md create mode 100755 samples/client/petstore/crystal/bin/ameba create mode 100644 samples/client/petstore/crystal/bin/ameba.cr create mode 100644 samples/client/petstore/crystal/git_push.sh create mode 100644 samples/client/petstore/crystal/lib/.shards.info create mode 100644 samples/client/petstore/crystal/lib/ameba/.dockerignore create mode 100644 samples/client/petstore/crystal/lib/ameba/.editorconfig create mode 100644 samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml create mode 100644 samples/client/petstore/crystal/lib/ameba/.gitignore create mode 100644 samples/client/petstore/crystal/lib/ameba/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/ameba/Dockerfile create mode 100644 samples/client/petstore/crystal/lib/ameba/LICENSE create mode 100644 samples/client/petstore/crystal/lib/ameba/Makefile create mode 100644 samples/client/petstore/crystal/lib/ameba/README.md create mode 100644 samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/bin/.keep create mode 100644 samples/client/petstore/crystal/lib/ameba/bin/ameba.cr create mode 120000 samples/client/petstore/crystal/lib/ameba/lib create mode 100644 samples/client/petstore/crystal/lib/ameba/shard.yml create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr create mode 100644 samples/client/petstore/crystal/lib/ameba/src/cli.cr create mode 100644 samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml create mode 100644 samples/client/petstore/crystal/lib/crest/.gitignore create mode 100644 samples/client/petstore/crystal/lib/crest/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/crest/CHANGELOG.md create mode 100644 samples/client/petstore/crystal/lib/crest/LICENSE create mode 100644 samples/client/petstore/crystal/lib/crest/README.md create mode 120000 samples/client/petstore/crystal/lib/crest/lib create mode 100644 samples/client/petstore/crystal/lib/crest/logo/icon.png create mode 100644 samples/client/petstore/crystal/lib/crest/logo/icon.svg create mode 100644 samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.png create mode 100644 samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg create mode 100644 samples/client/petstore/crystal/lib/crest/playground/requests.md create mode 100644 samples/client/petstore/crystal/lib/crest/shard.yml create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/fff.png create mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/server.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/form.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/logger.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/request.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/resource.cr create mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/response.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/.editorconfig create mode 100644 samples/client/petstore/crystal/lib/exception_page/.gitignore create mode 100644 samples/client/petstore/crystal/lib/exception_page/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/exception_page/LICENSE create mode 100644 samples/client/petstore/crystal/lib/exception_page/README.md create mode 120000 samples/client/petstore/crystal/lib/exception_page/lib create mode 100644 samples/client/petstore/crystal/lib/exception_page/shard.yml create mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr create mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr create mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/README.md create mode 120000 samples/client/petstore/crystal/lib/http-client-digest_auth/lib create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr create mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml create mode 100644 samples/client/petstore/crystal/lib/http_proxy/.gitignore create mode 100644 samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md create mode 100644 samples/client/petstore/crystal/lib/http_proxy/LICENSE create mode 100644 samples/client/petstore/crystal/lib/http_proxy/README.md create mode 120000 samples/client/petstore/crystal/lib/http_proxy/lib create mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/client.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/server.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/shard.yml create mode 100644 samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr create mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/.ameba.yml create mode 100644 samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml create mode 100644 samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md create mode 100644 samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 samples/client/petstore/crystal/lib/kemal/.gitignore create mode 100644 samples/client/petstore/crystal/lib/kemal/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/kemal/CHANGELOG.md create mode 100644 samples/client/petstore/crystal/lib/kemal/LICENSE create mode 100644 samples/client/petstore/crystal/lib/kemal/README.md create mode 120000 samples/client/petstore/crystal/lib/kemal/lib create mode 100644 samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/samples/json_api.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/shard.yml create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr create mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/.gitignore create mode 100644 samples/client/petstore/crystal/lib/kilt/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/kilt/LICENSE create mode 100644 samples/client/petstore/crystal/lib/kilt/README.md create mode 120000 samples/client/petstore/crystal/lib/kilt/lib create mode 100644 samples/client/petstore/crystal/lib/kilt/shard.yml create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/crikey.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/crustache.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/ecr.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/slang.cr create mode 100644 samples/client/petstore/crystal/lib/kilt/src/temel.cr create mode 100644 samples/client/petstore/crystal/lib/radix/.gitignore create mode 100644 samples/client/petstore/crystal/lib/radix/.travis.yml create mode 100644 samples/client/petstore/crystal/lib/radix/CHANGELOG.md create mode 100644 samples/client/petstore/crystal/lib/radix/LICENSE create mode 100644 samples/client/petstore/crystal/lib/radix/Makefile create mode 100644 samples/client/petstore/crystal/lib/radix/README.md create mode 120000 samples/client/petstore/crystal/lib/radix/lib create mode 100644 samples/client/petstore/crystal/lib/radix/shard.yml create mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr create mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr create mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr create mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr create mode 100644 samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/lib/radix/src/radix.cr create mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/node.cr create mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/result.cr create mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/tree.cr create mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/version.cr create mode 100644 samples/client/petstore/crystal/shard.lock create mode 100644 samples/client/petstore/crystal/shard.yml create mode 100644 samples/client/petstore/crystal/spec/api/pet_api_spec.cr create mode 100644 samples/client/petstore/crystal/spec/api/store_api_spec.cr create mode 100644 samples/client/petstore/crystal/spec/api/user_api_spec.cr create mode 100644 samples/client/petstore/crystal/spec/models/api_response_spec.cr create mode 100644 samples/client/petstore/crystal/spec/models/category_spec.cr create mode 100644 samples/client/petstore/crystal/spec/models/order_spec.cr create mode 100644 samples/client/petstore/crystal/spec/models/pet_spec.cr create mode 100644 samples/client/petstore/crystal/spec/models/tag_spec.cr create mode 100644 samples/client/petstore/crystal/spec/models/user_spec.cr create mode 100644 samples/client/petstore/crystal/spec/spec_helper.cr create mode 100644 samples/client/petstore/crystal/src/petstore.cr create mode 100644 samples/client/petstore/crystal/src/petstore/api/pet_api.cr create mode 100644 samples/client/petstore/crystal/src/petstore/api/store_api.cr create mode 100644 samples/client/petstore/crystal/src/petstore/api/user_api.cr create mode 100644 samples/client/petstore/crystal/src/petstore/api_client.cr create mode 100644 samples/client/petstore/crystal/src/petstore/api_error.cr create mode 100644 samples/client/petstore/crystal/src/petstore/configuration.cr create mode 100644 samples/client/petstore/crystal/src/petstore/models/api_response.cr create mode 100644 samples/client/petstore/crystal/src/petstore/models/category.cr create mode 100644 samples/client/petstore/crystal/src/petstore/models/order.cr create mode 100644 samples/client/petstore/crystal/src/petstore/models/pet.cr create mode 100644 samples/client/petstore/crystal/src/petstore/models/tag.cr create mode 100644 samples/client/petstore/crystal/src/petstore/models/user.cr diff --git a/bin/configs/crystal.yaml b/bin/configs/crystal.yaml new file mode 100644 index 000000000000..74badcce662d --- /dev/null +++ b/bin/configs/crystal.yaml @@ -0,0 +1,9 @@ +generatorName: crystal +outputDir: samples/client/petstore/crystal +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/crystal +additionalProperties: + shardVersion: 1.0.0 + moduleName: Petstore + shardName: petstore +strictSpecBehavior: false diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java new file mode 100644 index 000000000000..7a5b347180d2 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CrystalClientCodegen.java @@ -0,0 +1,891 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.GeneratorMetadata; +import org.openapitools.codegen.meta.Stability; +import org.openapitools.codegen.meta.features.*; +import org.openapitools.codegen.templating.mustache.PrefixWithHashLambda; +import org.openapitools.codegen.templating.mustache.TrimWhitespaceLambda; +import org.openapitools.codegen.utils.ModelUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.*; + +import static org.openapitools.codegen.utils.StringUtils.camelize; +import static org.openapitools.codegen.utils.StringUtils.underscore; + +public class CrystalClientCodegen extends DefaultCodegen { + private static final Logger LOGGER = LoggerFactory.getLogger(CrystalClientCodegen.class); + private static final String NUMERIC_ENUM_PREFIX = "N"; + protected static int emptyMethodNameCounter = 0; + + protected String shardName; + protected String moduleName; + protected String shardVersion = "1.0.0"; + protected String specFolder = "spec"; + protected String srcFolder = "src"; + protected String shardLicense = "unlicense"; + protected String shardHomepage = "https://openapitools.org"; + protected String shardSummary = "A Crystal SDK for the REST API"; + protected String shardDescription = "This shard maps to a REST API"; + protected String shardAuthor = ""; + protected String shardAuthorEmail = ""; + protected String apiDocPath = "docs/"; + protected String modelDocPath = "docs/"; + + public static final String SHARD_NAME = "shardName"; + public static final String SHARD_VERSION = "shardVersion"; + public static final String SHARD_LICENSE = "shardLicense"; + public static final String SHARD_HOMEPAGE = "shardHomepage"; + public static final String SHARD_SUMMARY = "shardSummary"; + public static final String SHARD_DESCRIPTION = "shardDescription"; + public static final String SHARD_AUTHOR = "shardAuthor"; + public static final String SHARD_AUTHOR_EMAIL = "shardAuthorEmail"; + + public CrystalClientCodegen() { + super(); + + modifyFeatureSet(features -> features + .includeDocumentationFeatures(DocumentationFeature.Readme) + .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom)) + .securityFeatures(EnumSet.of( + SecurityFeature.BasicAuth, + SecurityFeature.BearerToken, + SecurityFeature.ApiKey, + SecurityFeature.OAuth2_Implicit + )) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling, + GlobalFeature.ParameterizedServer, + GlobalFeature.MultiServer + ) + .includeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .excludeParameterFeatures( + ParameterFeature.Cookie + ) + .includeClientModificationFeatures( + ClientModificationFeature.BasePath, + ClientModificationFeature.UserAgent + ) + ); + + generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) + .stability(Stability.BETA) + .build(); + + supportsInheritance = true; + + // clear import mapping (from default generator) as crystal does not use it + // at the moment + importMapping.clear(); + + embeddedTemplateDir = templateDir = "crystal"; + outputFolder = "generated-code" + File.separator + "crystal"; + + modelPackage = "models"; + apiPackage = "api"; + modelTemplateFiles.put("model.mustache", ".cr"); + apiTemplateFiles.put("api.mustache", ".cr"); + + modelTestTemplateFiles.put("model_test.mustache", ".cr"); + apiTestTemplateFiles.put("api_test.mustache", ".cr"); + + // TODO support auto-generated doc + //modelDocTemplateFiles.put("model_doc.mustache", ".md"); + //apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + // default HIDE_GENERATION_TIMESTAMP to true + hideGenerationTimestamp = Boolean.TRUE; + + // reserved word. Ref: https://github.com/crystal-lang/crystal/wiki/Crystal-for-Rubyists#available-keywords + reservedWords = new HashSet( + Arrays.asList( + "abstract", "do", "if", "nil?", "select", "union", + "alias", "else", "in", "of", "self", "unless", + "as", "elsif", "include", "out", "sizeof", "until", + "as?", "end", "instance", "sizeof", "pointerof", "struct", "verbatim", + "asm", "ensure", "is_a?", "private", "super", "when", + "begin", "enum", "lib", "protected", "then", "while", + "break", "extend", "macro", "require", "true", "with", + "case", "false", "module", "rescue", "type", "yield", + "class", "for", "next", "responds_to?", "typeof", + "def", "fun", "nil", "return", "uninitialized") + ); + + languageSpecificPrimitives.clear(); + languageSpecificPrimitives.add("String"); + languageSpecificPrimitives.add("Boolean"); + languageSpecificPrimitives.add("Integer"); + languageSpecificPrimitives.add("Float"); + languageSpecificPrimitives.add("Date"); + languageSpecificPrimitives.add("Time"); + languageSpecificPrimitives.add("Array"); + languageSpecificPrimitives.add("Hash"); + languageSpecificPrimitives.add("File"); + languageSpecificPrimitives.add("Object"); + + typeMapping.clear(); + typeMapping.put("string", "String"); + typeMapping.put("boolean", "Bool"); + typeMapping.put("char", "Char"); + typeMapping.put("int", "Int32"); + typeMapping.put("integer", "Int32"); + typeMapping.put("long", "Int64"); + typeMapping.put("short", "Int32"); + typeMapping.put("float", "Float32"); + typeMapping.put("double", "Float64"); + typeMapping.put("number", "Float64"); + typeMapping.put("date", "Time"); + typeMapping.put("DateTime", "Time"); + typeMapping.put("array", "Array"); + typeMapping.put("List", "Array"); + typeMapping.put("set", "Set"); + typeMapping.put("map", "Hash"); + typeMapping.put("object", "Object"); + typeMapping.put("file", "File"); + typeMapping.put("binary", "String"); + typeMapping.put("ByteArray", "String"); + typeMapping.put("UUID", "String"); + typeMapping.put("URI", "String"); + + instantiationTypes.put("map", "Hash"); + instantiationTypes.put("array", "Array"); + instantiationTypes.put("set", "Set"); + + // remove modelPackage and apiPackage added by default + cliOptions.removeIf(opt -> CodegenConstants.MODEL_PACKAGE.equals(opt.getOpt()) || + CodegenConstants.API_PACKAGE.equals(opt.getOpt())); + + cliOptions.add(new CliOption(SHARD_NAME, "shard name (e.g. twitter_client"). + defaultValue("openapi_client")); + + cliOptions.add(new CliOption(SHARD_VERSION, "shard version.").defaultValue("1.0.0")); + + cliOptions.add(new CliOption(SHARD_LICENSE, "shard license."). + defaultValue("unlicense")); + + cliOptions.add(new CliOption(SHARD_HOMEPAGE, "shard homepage."). + defaultValue("http://org.openapitools")); + + cliOptions.add(new CliOption(SHARD_DESCRIPTION, "shard description."). + defaultValue("This shard maps to a REST API")); + + cliOptions.add(new CliOption(SHARD_AUTHOR, "shard author (only one is supported).")); + + cliOptions.add(new CliOption(SHARD_AUTHOR_EMAIL, "shard author email (only one is supported).")); + + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC). + defaultValue(Boolean.TRUE.toString())); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (StringUtils.isEmpty(System.getenv("CRYSTAL_POST_PROCESS_FILE"))) { + LOGGER.info("Hint: Environment variable 'CRYSTAL_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export CRYSTAL_POST_PROCESS_FILE=\"/usr/local/bin/crystal tool format\"' (Linux/Mac)"); + } + + if (additionalProperties.containsKey(SHARD_NAME)) { + setShardName((String) additionalProperties.get(SHARD_NAME)); + } + additionalProperties.put(SHARD_NAME, shardName); + + if (additionalProperties.containsKey(SHARD_VERSION)) { + setShardVersion((String) additionalProperties.get(SHARD_VERSION)); + } else { + // not set, pass the default value to template + additionalProperties.put(SHARD_VERSION, shardVersion); + } + + if (additionalProperties.containsKey(SHARD_LICENSE)) { + setShardLicense((String) additionalProperties.get(SHARD_LICENSE)); + } + + if (additionalProperties.containsKey(SHARD_HOMEPAGE)) { + setShardHomepage((String) additionalProperties.get(SHARD_HOMEPAGE)); + } + + if (additionalProperties.containsKey(SHARD_SUMMARY)) { + setShardSummary((String) additionalProperties.get(SHARD_SUMMARY)); + } + + if (additionalProperties.containsKey(SHARD_DESCRIPTION)) { + setShardDescription((String) additionalProperties.get(SHARD_DESCRIPTION)); + } + + if (additionalProperties.containsKey(SHARD_AUTHOR)) { + setShardAuthor((String) additionalProperties.get(SHARD_AUTHOR)); + } + + if (additionalProperties.containsKey(SHARD_AUTHOR_EMAIL)) { + setShardAuthorEmail((String) additionalProperties.get(SHARD_AUTHOR_EMAIL)); + } + + // make api and model doc path available in mustache template + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); + + // use constant model/api package (folder path) + setModelPackage("models"); + setApiPackage("api"); + + supportingFiles.add(new SupportingFile("shard_name.mustache", srcFolder, shardName + ".cr")); + String shardFolder = srcFolder + File.separator + shardName; + supportingFiles.add(new SupportingFile("api_error.mustache", shardFolder, "api_error.cr")); + supportingFiles.add(new SupportingFile("configuration.mustache", shardFolder, "configuration.cr")); + supportingFiles.add(new SupportingFile("api_client.mustache", shardFolder, "api_client.cr")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); + supportingFiles.add(new SupportingFile("shard.mustache", "", "shard.yml")); + + // crystal spec files + supportingFiles.add(new SupportingFile("spec_helper.mustache", specFolder, "spec_helper.cr") + .doNotOverwrite()); + + // add lambda for mustache templates + additionalProperties.put("lambdaPrefixWithHash", new PrefixWithHashLambda()); + + } + + @Override + public String getHelp() { + return "Generates a Crystal client library (beta)."; + } + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "crystal"; + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator + apiPackage.replace("/", File.separator); + } + + @Override + public String modelFileFolder() { + return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator + modelPackage.replace("/", File.separator); + } + + @Override + public String apiTestFileFolder() { + return outputFolder + File.separator + specFolder + File.separator + apiPackage.replace("/", File.separator); + } + + @Override + public String modelTestFileFolder() { + return outputFolder + File.separator + specFolder + File.separator + modelPackage.replace("/", File.separator); + } + + @Override + public String apiDocFileFolder() { + return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); + } + + @Override + public String modelDocFileFolder() { + return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar); + } + + @Override + public String getSchemaType(Schema schema) { + String openAPIType = super.getSchemaType(schema); + String type = null; + if (typeMapping.containsKey(openAPIType)) { + type = typeMapping.get(openAPIType); + if (languageSpecificPrimitives.contains(type)) { + return type; + } + } else { + type = openAPIType; + } + + if (type == null) { + return null; + } + + return toModelName(type); + } + + @Override + public String toModelName(final String name) { + String modelName; + modelName = sanitizeName(name); + + if (!StringUtils.isEmpty(modelNamePrefix)) { + modelName = modelNamePrefix + "_" + modelName; + } + + if (!StringUtils.isEmpty(modelNameSuffix)) { + modelName = modelName + "_" + modelNameSuffix; + } + + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(modelName)) { + modelName = camelize("Model" + modelName); + LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName); + return modelName; + } + + // model name starts with number + if (modelName.matches("^\\d.*")) { + LOGGER.warn(modelName + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + modelName)); + modelName = "model_" + modelName; // e.g. 200Response => Model200Response (after camelize) + } + + // camelize the model name + // phone_number => PhoneNumber + return camelize(modelName); + } + + @Override + public String toModelFilename(String name) { + return underscore(toModelName(name)); + } + + @Override + public String toModelDocFilename(String name) { + return toModelName(name); + } + + @Override + public String toApiFilename(final String name) { + // replace - with _ e.g. created-at => created_at + String filename = name; + if (apiNameSuffix != null && apiNameSuffix.length() > 0) { + filename = filename + "_" + apiNameSuffix; + } + + filename = filename.replaceAll("-", "_"); + + // e.g. PhoneNumberApi.cr => phone_number_api.cr + return underscore(filename); + } + + @Override + public String toApiDocFilename(String name) { + return toApiName(name); + } + + @Override + public String toApiTestFilename(String name) { + return toApiFilename(name) + "_spec"; + } + + @Override + public String toModelTestFilename(String name) { + return toModelFilename(name) + "_spec"; + } + + @Override + public String toApiName(String name) { + return super.toApiName(name); + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("Integer".equals(datatype) || "Float".equals(datatype)) { + return value; + } else { + return "\"" + escapeText(value) + "\""; + } + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("Integer".equals(datatype) || "Float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return NUMERIC_ENUM_PREFIX + varName; + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase(Locale.ROOT)); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return NUMERIC_ENUM_PREFIX + enumName; + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(Locale.ROOT); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return NUMERIC_ENUM_PREFIX + enumName; + } else { + return enumName; + } + } + + @Override + public Map postProcessModels(Map objs) { + // process enum in models + return postProcessModelsEnum(objs); + } + + @Override + public String toOperationId(String operationId) { + // rename to empty_method_name_1 (e.g.) if method name is empty + if (StringUtils.isEmpty(operationId)) { + operationId = underscore("empty_method_name_" + emptyMethodNameCounter++); + LOGGER.warn("Empty method name (operationId) found. Renamed to " + operationId); + return operationId; + } + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + String newOperationId = underscore("call_" + operationId); + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + newOperationId); + return newOperationId; + } + + // operationId starts with a number + if (operationId.matches("^\\d.*")) { + LOGGER.warn(operationId + " (starting with a number) cannot be used as method name. Renamed to " + underscore(sanitizeName("call_" + operationId))); + operationId = "call_" + operationId; + } + + return underscore(sanitizeName(operationId)); + } + + @Override + public String toApiImport(String name) { + return shardName + "/" + apiPackage() + "/" + toApiFilename(name); + } + + public void setShardName(String shardName) { + this.shardName = shardName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public void setShardVersion(String shardVersion) { + this.shardVersion = shardVersion; + } + + public void setShardDescription(String shardDescription) { + this.shardDescription = shardDescription; + } + + public void setShardSummary(String shardSummary) { + this.shardSummary = shardSummary; + } + + public void setShardLicense(String shardLicense) { + this.shardLicense = shardLicense; + } + + public void setShardHomepage(String shardHomepage) { + this.shardHomepage = shardHomepage; + } + + public void setShardAuthor(String shardAuthor) { + this.shardAuthor = shardAuthor; + } + + public void setShardAuthorEmail(String shardAuthorEmail) { + this.shardAuthorEmail = shardAuthorEmail; + } + + @Override + protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) { + final Schema additionalProperties = getAdditionalProperties(schema); + + if (additionalProperties != null) { + codegenModel.additionalPropertiesType = getSchemaType(additionalProperties); + } + } + + @Override + public Map postProcessOperationsWithModels(Map objs, List allModels) { + objs = super.postProcessOperationsWithModels(objs, allModels); + Map operations = (Map) objs.get("operations"); + HashMap modelMaps = new HashMap(); + HashMap processedModelMaps = new HashMap(); + + for (Object o : allModels) { + HashMap h = (HashMap) o; + CodegenModel m = (CodegenModel) h.get("model"); + modelMaps.put(m.classname, m); + } + + List operationList = (List) operations.get("operation"); + for (CodegenOperation op : operationList) { + for (CodegenParameter p : op.allParams) { + p.vendorExtensions.put("x-crystal-example", constructExampleCode(p, modelMaps, processedModelMaps)); + } + processedModelMaps.clear(); + for (CodegenParameter p : op.requiredParams) { + p.vendorExtensions.put("x-crystal-example", constructExampleCode(p, modelMaps, processedModelMaps)); + } + processedModelMaps.clear(); + for (CodegenParameter p : op.optionalParams) { + p.vendorExtensions.put("x-crystal-example", constructExampleCode(p, modelMaps, processedModelMaps)); + } + processedModelMaps.clear(); + for (CodegenParameter p : op.bodyParams) { + p.vendorExtensions.put("x-crystal-example", constructExampleCode(p, modelMaps, processedModelMaps)); + } + processedModelMaps.clear(); + for (CodegenParameter p : op.pathParams) { + p.vendorExtensions.put("x-crystal-example", constructExampleCode(p, modelMaps, processedModelMaps)); + } + processedModelMaps.clear(); + } + + return objs; + } + + private String constructExampleCode(CodegenParameter codegenParameter, HashMap modelMaps, HashMap processedModelMap) { + if (codegenParameter.isArray) { // array + return "[" + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap) + "]"; + } else if (codegenParameter.isMap) { + return "{ key: " + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap) + "}"; + } else if (codegenParameter.isPrimitiveType) { // primitive type + if (codegenParameter.isEnum) { + // When inline enum, set example to first allowable value + List values = (List) codegenParameter.allowableValues.get("values"); + codegenParameter.example = String.valueOf(values.get(0)); + } + if (codegenParameter.isString || "String".equalsIgnoreCase(codegenParameter.baseType)) { + if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) { + return "'" + codegenParameter.example + "'"; + } + return "'" + codegenParameter.paramName + "_example'"; + } else if (codegenParameter.isBoolean) { // boolean + if (Boolean.parseBoolean(codegenParameter.example)) { + return "true"; + } + return "false"; + } else if (codegenParameter.isUri) { + if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) { + return "'" + codegenParameter.example + "'"; + } + return "'https://example.com'"; + } else if (codegenParameter.isDateTime) { + if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) { + return "Time.parse('" + codegenParameter.example + "')"; + } + return "Time.now"; + } else if (codegenParameter.isDate) { + if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) { + return "Date.parse('" + codegenParameter.example + "')"; + } + return "Date.today"; + } else if (codegenParameter.isFile) { + return "File.new('/path/to/some/file')"; + } else if (codegenParameter.isInteger) { + if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) { + return codegenParameter.example; + } + return "37"; + } else { // number + if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) { + return codegenParameter.example; + } + return "3.56"; + } + } else { // model + // look up the model + if (modelMaps.containsKey(codegenParameter.dataType)) { + return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap); + } else { + //LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenParameter.dataType); + return "TODO"; + } + } + } + + private String constructExampleCode(CodegenProperty codegenProperty, HashMap modelMaps, HashMap processedModelMap) { + if (codegenProperty.isArray) { // array + return "[" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + "]"; + } else if (codegenProperty.isMap) { + return "{ key: " + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + "}"; + } else if (codegenProperty.isPrimitiveType) { // primitive type + if (codegenProperty.isEnum) { + // When inline enum, set example to first allowable value + List values = (List) codegenProperty.allowableValues.get("values"); + codegenProperty.example = String.valueOf(values.get(0)); + } + if (codegenProperty.isString || "String".equalsIgnoreCase(codegenProperty.baseType)) { + if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) { + return "'" + codegenProperty.example + "'"; + } else { + return "'" + codegenProperty.name + "_example'"; + } + } else if (codegenProperty.isBoolean) { // boolean + if (Boolean.parseBoolean(codegenProperty.example)) { + return "true"; + } else { + return "false"; + } + } else if (codegenProperty.isUri) { + if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) { + return "'" + codegenProperty.example + "'"; + } + return "'https://example.com'"; + } else if (codegenProperty.isDateTime) { + if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) { + return "Time.parse('" + codegenProperty.example + "')"; + } + return "Time.now"; + } else if (codegenProperty.isDate) { + if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) { + return "Date.parse('" + codegenProperty.example + "')"; + } + return "Date.today"; + } else if (codegenProperty.isFile) { + return "File.new('/path/to/some/file')"; + } else if (codegenProperty.isInteger) { + if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) { + return codegenProperty.example; + } + return "37"; + } else { // number + if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) { + return codegenProperty.example; + } + return "3.56"; + } + } else { // model + // look up the model + if (modelMaps.containsKey(codegenProperty.dataType)) { + return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap); + } else { + //LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenParameter.dataType); + return "TODO"; + } + } + } + + private String constructExampleCode(CodegenModel codegenModel, HashMap modelMaps, HashMap processedModelMap) { + // break infinite recursion. Return, in case a model is already processed in the current context. + String model = codegenModel.name; + if (processedModelMap.containsKey(model)) { + int count = processedModelMap.get(model); + if (count == 1) { + processedModelMap.put(model, 2); + } else if (count == 2) { + return ""; + } else { + throw new RuntimeException("Invalid count when constructing example: " + count); + } + } else if (codegenModel.isEnum) { + List> enumVars = (List>) codegenModel.allowableValues.get("enumVars"); + return moduleName + "::" + codegenModel.classname + "::" + enumVars.get(0).get("name"); + } else if (codegenModel.oneOf != null && !codegenModel.oneOf.isEmpty()) { + String subModel = (String) codegenModel.oneOf.toArray()[0]; + String oneOf = constructExampleCode(modelMaps.get(subModel), modelMaps, processedModelMap); + return oneOf; + } else { + processedModelMap.put(model, 1); + } + + List propertyExamples = new ArrayList<>(); + for (CodegenProperty codegenProperty : codegenModel.requiredVars) { + propertyExamples.add(codegenProperty.name + ": " + constructExampleCode(codegenProperty, modelMaps, processedModelMap)); + } + String example = moduleName + "::" + toModelName(model) + ".new"; + if (!propertyExamples.isEmpty()) { + example += "({" + StringUtils.join(propertyExamples, ", ") + "})"; + } + return example; + } + + @Override + public String escapeReservedWord(String name) { + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "_" + name; + } + + @Override + public String getTypeDeclaration(Schema schema) { + if (ModelUtils.isArraySchema(schema)) { + Schema inner = ((ArraySchema) schema).getItems(); + return getSchemaType(schema) + "(" + getTypeDeclaration(inner) + ")"; + } else if (ModelUtils.isMapSchema(schema)) { + Schema inner = getAdditionalProperties(schema); + return getSchemaType(schema) + "(String, " + getTypeDeclaration(inner) + ")"; + } + + return super.getTypeDeclaration(schema); + } + + @Override + public String toInstantiationType(Schema schema) { + if (ModelUtils.isMapSchema(schema)) { + return instantiationTypes.get("map"); + } else if (ModelUtils.isArraySchema(schema)) { + String parentType; + if (ModelUtils.isSet(schema)) { + parentType = "set"; + } else { + parentType = "array"; + } + return instantiationTypes.get(parentType); + } + return super.toInstantiationType(schema); + } + + @Override + public String toDefaultValue(Schema p) { + p = ModelUtils.getReferencedSchema(this.openAPI, p); + if (ModelUtils.isIntegerSchema(p) || ModelUtils.isNumberSchema(p) || ModelUtils.isBooleanSchema(p)) { + if (p.getDefault() != null) { + return p.getDefault().toString(); + } + } else if (ModelUtils.isStringSchema(p)) { + if (p.getDefault() != null) { + if (p.getDefault() instanceof Date) { + Date date = (Date) p.getDefault(); + LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + return "Date.parse(\"" + String.format(Locale.ROOT, localDate.toString(), "") + "\")"; + } else if (p.getDefault() instanceof java.time.OffsetDateTime) { + return "Time.parse(\"" + String.format(Locale.ROOT, ((java.time.OffsetDateTime) p.getDefault()).atZoneSameInstant(ZoneId.systemDefault()).toString(), "") + "\")"; + } else { + return "'" + escapeText((String) p.getDefault()) + "'"; + } + } + } + + return null; + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "::" + value; + } + + @Override + public String toVarName(final String name) { + String varName; + // sanitize name + varName = sanitizeName(name); + // if it's all uppper case, convert to lower case + if (name.matches("^[A-Z_]*$")) { + varName = varName.toLowerCase(Locale.ROOT); + } + + // camelize (lower first character) the variable name + // petId => pet_id + varName = underscore(varName); + + // for reserved word or word starting with number, append _ + if (isReservedWord(varName) || varName.matches("^\\d.*")) { + varName = escapeReservedWord(varName); + } + + return varName; + } + + public String toRegularExpression(String pattern) { + return addRegularExpressionDelimiter(pattern); + } + + @Override + public String toParamName(String name) { + // should be the same as variable name + return toVarName(name); + } + + @Override + public String escapeQuotationMark(String input) { + // remove ' to avoid code injection + return input.replace("'", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("=end", "=_end").replace("=begin", "=_begin").replace("#{", "\\#{"); + } + + @Override + public void postProcessFile(File file, String fileType) { + if (file == null) { + return; + } + String crystalPostProcessFile = System.getenv("CRYSTAL_POST_PROCESS_FILE"); + if (StringUtils.isEmpty(crystalPostProcessFile)) { + return; // skip if CRYSTAL_POST_PROCESS_FILE env variable is not defined + } + // only process files with cr extension + if ("cr".equals(FilenameUtils.getExtension(file.toString()))) { + String command = crystalPostProcessFile + " " + file.toString(); + try { + Process p = Runtime.getRuntime().exec(command); + int exitValue = p.waitFor(); + if (exitValue != 0) { + BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream(), StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + } + LOGGER.error("Error running the command ({}). Exit value: {}, Error output: {}", command, exitValue, sb.toString()); + } else { + LOGGER.info("Successfully executed: " + command); + } + } catch (Exception e) { + LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage()); + } + } + } +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/PrefixWithHashLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/PrefixWithHashLambda.java new file mode 100644 index 000000000000..c3418de09b78 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/PrefixWithHashLambda.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * Copyright 2018 SmartBear Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.templating.mustache; + +import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template.Fragment; + +import java.io.IOException; +import java.io.Writer; + +/** + * Replaces duplicate whitespace characters in a fragment with single space. + * + * Register: + *
+ * additionalProperties.put("lambdaPrefixWithHash", new PrefixWithHashLambda());
+ * 
+ * + * Use: + *
+ * {{#lambdaPrefixWithHash}}{{name}}{{/lambdaPrefixWithHash}}
+ * 
+ */ +public class PrefixWithHashLambda implements Mustache.Lambda { + private static final String WITH_HASH = "\n#"; + + private static final String NEWLINE_REGEX = "\\R"; + + @Override + public void execute(Fragment fragment, Writer writer) throws IOException { + writer.write(fragment.execute().replaceAll(NEWLINE_REGEX, WITH_HASH)); + } + +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 8fe400f1148c..f9c061816fb8 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -7,6 +7,7 @@ org.openapitools.codegen.languages.AsciidocDocumentationCodegen org.openapitools.codegen.languages.AspNetCoreServerCodegen org.openapitools.codegen.languages.AvroSchemaCodegen org.openapitools.codegen.languages.BashClientCodegen +org.openapitools.codegen.languages.CrystalClientCodegen org.openapitools.codegen.languages.CLibcurlClientCodegen org.openapitools.codegen.languages.ClojureClientCodegen org.openapitools.codegen.languages.ConfluenceWikiCodegen diff --git a/modules/openapi-generator/src/main/resources/crystal/Gemfile.mustache b/modules/openapi-generator/src/main/resources/crystal/Gemfile.mustache new file mode 100644 index 000000000000..c2e3127cdcfe --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/Gemfile.mustache @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gemspec + +group :development, :test do + gem 'rake', '~> 13.0.1' + gem 'pry-byebug' + gem 'rubocop', '~> 0.66.0' +end diff --git a/modules/openapi-generator/src/main/resources/crystal/README.mustache b/modules/openapi-generator/src/main/resources/crystal/README.mustache new file mode 100644 index 000000000000..20705236b9ce --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/README.mustache @@ -0,0 +1,46 @@ +# {{shardName}} + +The Crystsal module for the {{appName}} + +{{#appDescriptionWithNewLines}} +{{{appDescriptionWithNewLines}}} +{{/appDescriptionWithNewLines}} + +This SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: + +- API version: {{appVersion}} +- Package version: {{shardVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Installation + +### Install from Git + +Add the following to shard.yaml + +```yaml +dependencies: + {{{shardName}}}: + github: {{#gitUserId}}{{.}}{{/gitUserId}}{{^gitUserId}}YOUR_GIT_USERNAME{{/gitUserId}}/{{#gitRepoId}}{{.}}{{/gitRepoId}}{{^gitRepoId}}YOUR_GIT_REPO{{/gitRepoId}} + version: ~> {{shardVersion}} +``` + +## Development + +Install dependencies + +```shell +shards +``` + +Run the tests: + +```shell +crystal spec +``` diff --git a/modules/openapi-generator/src/main/resources/crystal/Rakefile.mustache b/modules/openapi-generator/src/main/resources/crystal/Rakefile.mustache new file mode 100644 index 000000000000..c72ca30d454e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/Rakefile.mustache @@ -0,0 +1,10 @@ +require "bundler/gem_tasks" + +begin + require 'rspec/core/rake_task' + + RSpec::Core::RakeTask.new(:spec) + task default: :spec +rescue LoadError + # no rspec available +end diff --git a/modules/openapi-generator/src/main/resources/crystal/api.mustache b/modules/openapi-generator/src/main/resources/crystal/api.mustache new file mode 100644 index 000000000000..6d702115a0f4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api.mustache @@ -0,0 +1,185 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +require "uri" + +module {{moduleName}} +{{#operations}} + class {{classname}} + property api_client : ApiClient + + def initialize(api_client = ApiClient.default) + @api_client = api_client + end +{{#operation}} + {{#summary}} + # {{{summary}}} + {{/summary}} + {{#notes}} + # {{{notes}}} + {{/notes}} + {{#allParams}} + {{#required}} + # @param {{paramName}} [{{{dataType}}}{{^required}}?{{/required}}] {{description}} + {{/required}} + {{/allParams}} + # @return [{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}nil{{/returnType}}] + def {{operationId}}({{#allParams}}{{paramName}} : {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) + {{#returnType}}data, _status_code, _headers = {{/returnType}}{{operationId}}_with_http_info({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) + {{#returnType}}data{{/returnType}}{{^returnType}}nil{{/returnType}} + end + + {{#summary}} + # {{summary}} + {{/summary}} + {{#notes}} + # {{notes}} + {{/notes}} + {{#allParams}} + {{#required}} + # @param {{paramName}} [{{{dataType}}}{{^required}}?{{/required}}] {{description}} + {{/required}} + {{/allParams}} + # @return [Array<({{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}nil{{/returnType}}, Integer, Hash)>] {{#returnType}}{{{returnType}}} data{{/returnType}}{{^returnType}}nil{{/returnType}}, response status code and response headers + def {{operationId}}_with_http_info({{#allParams}}{{paramName}} : {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) + if @api_client.config.debugging + Log.debug {"Calling API: {{classname}}.{{operationId}} ..."} + end + {{#allParams}} + {{^isNullable}} + {{#required}} + # verify the required parameter "{{paramName}}" is set + if @api_client.config.client_side_validation && {{{paramName}}}.nil? + raise ArgumentError.new("Missing the required parameter '{{paramName}}' when calling {{classname}}.{{operationId}}") + end + {{#isEnum}} + {{^isContainer}} + # verify enum value + allowable_values = [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}] + if @api_client.config.client_side_validation && !allowable_values.include?({{{paramName}}}) + raise ArgumentError.new("invalid value for \"{{{paramName}}}\", must be one of #{allowable_values}") + end + {{/isContainer}} + {{/isEnum}} + {{/required}} + {{/isNullable}} + {{^required}} + {{#isEnum}} + {{#collectionFormat}} + allowable_values = [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}] + if @api_client.config.client_side_validation && {{{paramName}}} && {{{paramName}}}.all? { |item| allowable_values.include?(item) } + raise ArgumentError.new("invalid value for \"{{{paramName}}}\", must include one of #{allowable_values}") + end + {{/collectionFormat}} + {{^collectionFormat}} + allowable_values = [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}] + if @api_client.config.client_side_validation && {{{paramName}}} && !allowable_values.include?({{{paramName}}}]) + raise ArgumentError.new("invalid value for \"{{{paramName}}}\", must be one of #{allowable_values}") + end + {{/collectionFormat}} + {{/isEnum}} + {{/required}} + {{#hasValidation}} + {{#maxLength}} + if @api_client.config.client_side_validation && {{^required}}!{{{paramName}}}.nil? && {{/required}}{{{paramName}}}.to_s.length > {{{maxLength}}} + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, the character length must be smaller than or equal to {{{maxLength}}}.") + end + + {{/maxLength}} + {{#minLength}} + if @api_client.config.client_side_validation && {{^required}}!{{{paramName}}}.nil? && {{/required}}{{{paramName}}}.to_s.length < {{{minLength}}} + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, the character length must be great than or equal to {{{minLength}}}.") + end + + {{/minLength}} + {{#maximum}} + if @api_client.config.client_side_validation && {{^required}}!{{{paramName}}}.nil? && {{/required}}{{{paramName}}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{{maximum}}} + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{{maximum}}}.") + end + + {{/maximum}} + {{#minimum}} + if @api_client.config.client_side_validation && {{^required}}!{{{paramName}}}.nil? && {{/required}}{{{paramName}}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{{minimum}}} + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, must be greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{{minimum}}}.") + end + + {{/minimum}} + {{#pattern}} + pattern = Regexp.new({{{pattern}}}) + if @api_client.config.client_side_validation && {{^required}}{{{paramName}}}.nil? && {{/required}}{{{paramName}}} !~ pattern + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, must conform to the pattern #{pattern}.") + end + + {{/pattern}} + {{#maxItems}} + if @api_client.config.client_side_validation && {{^required}}{{{paramName}}}.nil? && {{/required}}{{{paramName}}}.length > {{{maxItems}}} + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, number of items must be less than or equal to {{{maxItems}}}.") + end + + {{/maxItems}} + {{#minItems}} + if @api_client.config.client_side_validation && {{^required}}{{{paramName}}}.nil? && {{/required}}{{{paramName}}}.length < {{{minItems}}} + raise ArgumentError.new("invalid value for \"{{{paramName}}}\" when calling {{classname}}.{{operationId}}, number of items must be greater than or equal to {{{minItems}}}.") + end + + {{/minItems}} + {{/hasValidation}} + {{/allParams}} + # resource path + local_var_path = "{{{path}}}"{{#pathParams}}.sub("{" + "{{baseName}}" + "}", URI.encode({{paramName}}.to_s){{^strictSpecBehavior}}.gsub("%2F", "/"){{/strictSpecBehavior}}){{/pathParams}} + + # query parameters + query_params = Hash(Symbol, String).new + {{#queryParams}} + query_params[:"{{{baseName}}}"] = {{#collectionFormat}}@api_client.build_collection_param({{{paramName}}}, :{{{collectionFormat}}}){{/collectionFormat}}{{^collectionFormat}}{{{paramName}}}{{/collectionFormat}} + {{/queryParams}} + + # header parameters + header_params = Hash(String, String).new + {{#hasProduces}} + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept([{{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}]) + {{/hasProduces}} + {{#hasConsumes}} + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type([{{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}}]) + {{/hasConsumes}} + {{#headerParams}} + header_params["{{{baseName}}}"] = {{#collectionFormat}}@api_client.build_collection_param({{{paramName}}}, :{{{collectionFormat}}}){{/collectionFormat}}{{^collectionFormat}}{{{paramName}}}{{/collectionFormat}} + {{/headerParams}} + + # form parameters + form_params = Hash(Symbol, String).new + {{#formParams}} + form_params[:"{{baseName}}"] = {{#collectionFormat}}@api_client.build_collection_param({{{paramName}}}, :{{{collectionFormat}}}){{/collectionFormat}}{{^collectionFormat}}{{{paramName}}}{{/collectionFormat}} + {{/formParams}} + + # http body (model) + post_body = {{#bodyParam}}@api_client.object_to_http_body({{{paramName}}}){{/bodyParam}}{{^bodyParam}}nil{{/bodyParam}} + + # return_type + return_type = {{#returnType}}"{{{.}}}"{{/returnType}}{{^returnType}}nil{{/returnType}} + + # auth_names + auth_names = {{#authMethods}}{{#-first}}[{{/-first}}"{{name}}"{{^-last}}, {{/-last}}{{#-last}}]{{/-last}}{{/authMethods}}{{^authMethods}}[] of String{{/authMethods}} + + data, status_code, headers = @api_client.call_api(:{{httpMethod}}, + local_var_path, + :"{{classname}}.{{operationId}}", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: {{classname}}#{{operationId}}\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return {{#returnType}}{{{.}}}.from_json(data){{/returnType}}{{^returnType}}nil{{/returnType}}, status_code, headers + end +{{^-last}} + +{{/-last}} +{{/operation}} + end +{{/operations}} +end diff --git a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache new file mode 100644 index 000000000000..204c21c36027 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache @@ -0,0 +1,396 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +require "json" +require "time" +require "crest" + +module {{moduleName}} + class ApiClient + # The Configuration object holding settings to be used in the API client. + property config : Configuration + + # Defines the headers to be used in HTTP requests of all API calls by default. + # + # @return [Hash] + property default_headers : Hash(String, String) + + # Initializes the ApiClient + # @option config [Configuration] Configuration for initializing the object, default to Configuration.default + def initialize(config = Configuration.default) + @config = config + # TODO @user_agent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/#{VERSION}/crystal{{/httpUserAgent}}" + @default_headers = { + #"Content-Type" => "application/json" + #TODO: "User-Agent" => @user_agent + } + end + + def self.default + @@default ||= ApiClient.new + end + + # Check if the given MIME is a JSON MIME. + # JSON MIME examples: + # application/json + # application/json; charset=UTF8 + # APPLICATION/JSON + # */* + # @param [String] mime MIME + # @return [Boolean] True if the MIME is application/json + def json_mime?(mime) + (mime == "*/*") || !(mime =~ /Application\/.*json(?!p)(;.*)?/i).nil? + end + + # Deserialize the response to the given return type. + # + # @param [Response] response HTTP response + # @param [String] return_type some examples: "User", "Array", "Hash" + def deserialize(response, return_type) + body = response.body + + # handle file downloading - return the File instance processed in request callbacks + # note that response body is empty when the file is written in chunks in request on_body callback + if return_type == "File" + content_disposition = response.headers["Content-Disposition"].to_s + if content_disposition && content_disposition =~ /filename=/i + filename = content_disposition.match(/filename=[""]?([^""\s]+)[""]?/i).try &.[0] + prefix = sanitize_filename(filename) + else + prefix = "download-" + end + if !prefix.nil? && prefix.ends_with?("-") + prefix = prefix + "-" + end + encoding = response.headers["Content-Encoding"].to_s + + # TODO add file support + raise ApiError.new(code: 0, message: "File response not yet supported in the client.") if return_type + return nil + + #@tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) + #@tempfile.write(@stream.join.force_encoding(encoding)) + #@tempfile.close + #Log.info { "Temp file written to #{@tempfile.path}, please copy the file to a proper folder "\ + # "with e.g. `FileUtils.cp(tempfile.path, \"/new/file/path\")` otherwise the temp file "\ + # "will be deleted automatically with GC. It's also recommended to delete the temp file "\ + # "explicitly with `tempfile.delete`" } + #return @tempfile + end + + return nil if body.nil? || body.empty? + + # return response body directly for String return type + return body if return_type == "String" + + # ensuring a default content type + content_type = response.headers["Content-Type"] || "application/json" + + raise ApiError.new(code: 0, message: "Content-Type is not supported: #{content_type}") unless json_mime?(content_type) + + begin + data = JSON.parse("[#{body}]")[0] + rescue e : Exception + if %w(String Date Time).includes?(return_type) + data = body + else + raise e + end + end + + convert_to_type data, return_type + end + + # Convert data to the given return type. + # @param [Object] data Data to be converted + # @param [String] return_type Return type + # @return [Mixed] Data in a particular type + def convert_to_type(data, return_type) + return nil if data.nil? + case return_type + when "String" + data.to_s + when "Integer" + data.to_s.to_i + when "Float" + data.to_s.to_f + when "Boolean" + data == true + when "Time" + # parse date time (expecting ISO 8601 format) + Time.parse! data.to_s, "%Y-%m-%dT%H:%M:%S%Z" + when "Date" + # parse date (expecting ISO 8601 format) + Time.parse! data.to_s, "%Y-%m-%d" + when "Object" + # generic object (usually a Hash), return directly + data + when /\AArray<(.+)>\z/ + # e.g. Array + sub_type = $1 + data.map { |item| convert_to_type(item, sub_type) } + when /\AHash\\z/ + # e.g. Hash + sub_type = $1 + ({} of Symbol => String).tap do |hash| + data.each { |k, v| hash[k] = convert_to_type(v, sub_type) } + end + else + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(return_type) + klass.respond_to?(:openapi_one_of) ? klass.build(data) : klass.build_from_hash(data) + end + end + + # Sanitize filename by removing path. + # e.g. ../../sun.gif becomes sun.gif + # + # @param [String] filename the filename to be sanitized + # @return [String] the sanitized filename + def sanitize_filename(filename) + if filename.nil? + return nil + else + filename.gsub(/.*[\/\\]/, "") + end + end + + def build_request_url(path : String, operation : Symbol) + # Add leading and trailing slashes to path + path = "/#{path}".gsub(/\/+/, "/") + @config.base_url(operation) + path + end + + # Update hearder and query params based on authentication settings. + # + # @param [Hash] header_params Header parameters + # @param [Hash] query_params Query parameters + # @param [String] auth_names Authentication scheme name + def update_params_for_auth!(header_params, query_params, auth_names) + Array{auth_names}.each do |auth_name| + auth_setting = @config.auth_settings[auth_name] + next unless auth_setting + case auth_setting[:in] + when "header" then header_params[auth_setting[:key]] = auth_setting[:value] + when "query" then query_params[auth_setting[:key]] = auth_setting[:value] + else raise ArgumentError.new("Authentication token must be in `query` of `header`") + end + end + end + + # Sets user agent in HTTP header + # + # @param [String] user_agent User agent (e.g. openapi-generator/ruby/1.0.0) + def user_agent=(user_agent) + @user_agent = user_agent + @default_headers["User-Agent"] = @user_agent + end + + # Return Accept header based on an array of accepts provided. + # @param [Array] accepts array for Accept + # @return [String] the Accept header (e.g. application/json) + def select_header_accept(accepts) : String + #return nil if accepts.nil? || accepts.empty? + # use JSON when present, otherwise use all of the provided + json_accept = accepts.find { |s| json_mime?(s) } + if json_accept.nil? + accepts.join(",") + else + json_accept + end + end + + # Return Content-Type header based on an array of content types provided. + # @param [Array] content_types array for Content-Type + # @return [String] the Content-Type header (e.g. application/json) + def select_header_content_type(content_types) + # use application/json by default + return "application/json" if content_types.nil? || content_types.empty? + # use JSON when present, otherwise use the first one + json_content_type = content_types.find { |s| json_mime?(s) } + json_content_type || content_types.first + end + + # Convert object (array, hash, object, etc) to JSON string. + # @param [Object] model object to be converted into JSON string + # @return [String] JSON string representation of the object + def object_to_http_body(model) + return model if model.nil? || model.is_a?(String) + local_body = nil + if model.is_a?(Array) + local_body = model.map { |m| object_to_hash(m) } + else + local_body = object_to_hash(model) + end + local_body.to_json + end + + # Convert object(non-array) to hash. + # @param [Object] obj object to be converted into JSON string + # @return [String] JSON string representation of the object + def object_to_hash(obj) + if obj.respond_to?(:to_hash) + obj.to_hash + else + obj + end + end + + # Build parameter value according to the given collection format. + # @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi + def build_collection_param(param, collection_format) + case collection_format + when :csv + param.join(",") + when :ssv + param.join(" ") + when :tsv + param.join("\t") + when :pipes + param.join("|") + when :multi + # return the array directly as typhoeus will handle it as expected + param + else + fail "unknown collection format: #{collection_format.inspect}" + end + end + + # Call an API with given options. + # + # @return [Array<(Object, Integer, Hash)>] an array of 3 elements: + # the data deserialized from response body (could be nil), response status code and response headers. + def call_api(http_method : Symbol, path : String, operation : Symbol, return_type : String, post_body : String?, auth_names = [] of String, header_params = {} of String => String, query_params = {} of Symbol => String, form_params = {} of Symbol => String) + #ssl_options = { + # :ca_file => @config.ssl_ca_file, + # :verify => @config.ssl_verify, + # :verify_mode => @config.ssl_verify_mode, + # :client_cert => @config.ssl_client_cert, + # :client_key => @config.ssl_client_key + #} + + #connection = Faraday.new(:url => config.base_url, :ssl => ssl_options) do |conn| + # conn.basic_auth(config.username, config.password) + # if opts[:header_params]["Content-Type"] == "multipart/form-data" + # conn.request :multipart + # conn.request :url_encoded + # end + # conn.adapter(Faraday.default_adapter) + #end + + request = Crest::Request.new(http_method, + build_request_url(path, operation), + params: query_params, + headers: header_params, + #cookies: cookie_params, # TODO add cookies support + form: form_params, + logging: @config.debugging, + handle_errors: false + ) + + response = request.execute + + if @config.debugging + Log.debug {"HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"} + end + + if !response.success? + if response.status == 0 + # Errors from libcurl will be made visible here + raise ApiError.new(code: 0, + message: response.body) + else + raise ApiError.new(code: response.status_code, + response_headers: response.headers, + message: response.body) + end + end + + return response.body, response.status_code, response.headers + end + + # Builds the HTTP request + # + # @param [String] http_method HTTP method/verb (e.g. POST) + # @param [String] path URL path (e.g. /account/new) + # @option opts [Hash] :header_params Header parameters + # @option opts [Hash] :query_params Query parameters + # @option opts [Hash] :form_params Query parameters + # @option opts [Object] :body HTTP body (JSON/XML) + # @return [Typhoeus::Request] A Typhoeus Request + def build_request(http_method, path, request, opts = {} of Symbol => String) + url = build_request_url(path, opts) + http_method = http_method.to_sym.downcase + + header_params = @default_headers.merge(opts[:header_params] || {} of Symbole => String) + query_params = opts[:query_params] || {} of Symbol => String + form_params = opts[:form_params] || {} of Symbol => String + + update_params_for_auth! header_params, query_params, opts[:auth_names] + + req_opts = { + :method => http_method, + :headers => header_params, + :params => query_params, + :params_encoding => @config.params_encoding, + :timeout => @config.timeout, + :verbose => @config.debugging + } + + if [:post, :patch, :put, :delete].include?(http_method) + req_body = build_request_body(header_params, form_params, opts[:body]) + req_opts.update body: req_body + if @config.debugging + Log.debug {"HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"} + end + end + request.headers = header_params + request.body = req_body + request.url url + request.params = query_params + download_file(request) if opts[:return_type] == "File" + request + end + + # Builds the HTTP request body + # + # @param [Hash] header_params Header parameters + # @param [Hash] form_params Query parameters + # @param [Object] body HTTP body (JSON/XML) + # @return [String] HTTP body data in the form of string + def build_request_body(header_params, form_params, body) + # http form + if header_params["Content-Type"] == "application/x-www-form-urlencoded" + data = URI.encode_www_form(form_params) + elsif header_params["Content-Type"] == "multipart/form-data" + data = {} of Symbol => String + form_params.each do |key, value| + case value + when ::File, ::Tempfile + # TODO hardcode to application/octet-stream, need better way to detect content type + data[key] = Faraday::UploadIO.new(value.path, "application/octet-stream", value.path) + when ::Array, nil + # let Faraday handle Array and nil parameters + data[key] = value + else + data[key] = value.to_s + end + end + elsif body + data = body.is_a?(String) ? body : body.to_json + else + data = nil + end + data + end + + # TODO fix streaming response + #def download_file(request) + # @stream = [] + + # # handle streaming Responses + # request.options.on_data = Proc.new do |chunk, overall_received_bytes| + # @stream << chunk + # end + #end + end +end diff --git a/modules/openapi-generator/src/main/resources/crystal/api_client_faraday_partial.mustache b/modules/openapi-generator/src/main/resources/crystal/api_client_faraday_partial.mustache new file mode 100644 index 000000000000..dfef217e0374 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_client_faraday_partial.mustache @@ -0,0 +1,138 @@ + # Call an API with given options. + # + # @return [Array<(Object, Integer, Hash)>] an array of 3 elements: + # the data deserialized from response body (could be nil), response status code and response headers. + def call_api(http_method, path, opts = {} of Symbol => String) + ssl_options = { + :ca_file => @config.ssl_ca_file, + :verify => @config.ssl_verify, + :verify_mode => @config.ssl_verify_mode, + :client_cert => @config.ssl_client_cert, + :client_key => @config.ssl_client_key + } + + connection = Faraday.new(:url => config.base_url, :ssl => ssl_options) do |conn| + conn.basic_auth(config.username, config.password) + if opts[:header_params]["Content-Type"] == "multipart/form-data" + conn.request :multipart + conn.request :url_encoded + end + conn.adapter(Faraday.default_adapter) + end + + begin + response = connection.public_send(http_method.to_sym.downcase) do |req| + build_request(http_method, path, req, opts) + end + + if @config.debugging + Log.debug {"HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"} + end + + unless response.success? + if response.status == 0 + # Errors from libcurl will be made visible here + fail ApiError.new(code: 0, + message: response.return_message) + else + fail ApiError.new(code: response.status, + response_headers: response.headers, + response_body: response.body), + response.reason_phrase + end + end + rescue Faraday::TimeoutError + fail ApiError.new("Connection timed out") + end + + if opts[:return_type] + data = deserialize(response, opts[:return_type]) + else + data = nil + end + return data, response.status, response.headers + end + + # Builds the HTTP request + # + # @param [String] http_method HTTP method/verb (e.g. POST) + # @param [String] path URL path (e.g. /account/new) + # @option opts [Hash] :header_params Header parameters + # @option opts [Hash] :query_params Query parameters + # @option opts [Hash] :form_params Query parameters + # @option opts [Object] :body HTTP body (JSON/XML) + # @return [Typhoeus::Request] A Typhoeus Request + def build_request(http_method, path, request, opts = {} of Symbol => String) + url = build_request_url(path, opts) + http_method = http_method.to_sym.downcase + + header_params = @default_headers.merge(opts[:header_params] || {} of Symbole => String) + query_params = opts[:query_params] || {} of Symbol => String + form_params = opts[:form_params] || {} of Symbol => String + + update_params_for_auth! header_params, query_params, opts[:auth_names] + + req_opts = { + :method => http_method, + :headers => header_params, + :params => query_params, + :params_encoding => @config.params_encoding, + :timeout => @config.timeout, + :verbose => @config.debugging + } + + if [:post, :patch, :put, :delete].include?(http_method) + req_body = build_request_body(header_params, form_params, opts[:body]) + req_opts.update body: req_body + if @config.debugging + Log.debug {"HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"} + end + end + request.headers = header_params + request.body = req_body + request.url url + request.params = query_params + download_file(request) if opts[:return_type] == "File" + request + end + + # Builds the HTTP request body + # + # @param [Hash] header_params Header parameters + # @param [Hash] form_params Query parameters + # @param [Object] body HTTP body (JSON/XML) + # @return [String] HTTP body data in the form of string + def build_request_body(header_params, form_params, body) + # http form + if header_params["Content-Type"] == "application/x-www-form-urlencoded" + data = URI.encode_www_form(form_params) + elsif header_params["Content-Type"] == "multipart/form-data" + data = {} of Symbol => String + form_params.each do |key, value| + case value + when ::File, ::Tempfile + # TODO hardcode to application/octet-stream, need better way to detect content type + data[key] = Faraday::UploadIO.new(value.path, "application/octet-stream", value.path) + when ::Array, nil + # let Faraday handle Array and nil parameters + data[key] = value + else + data[key] = value.to_s + end + end + elsif body + data = body.is_a?(String) ? body : body.to_json + else + data = nil + end + data + end + + def download_file(request) + @stream = [] + + # handle streaming Responses + request.options.on_data = Proc.new do |chunk, overall_received_bytes| + @stream << chunk + end + end diff --git a/modules/openapi-generator/src/main/resources/crystal/api_client_typhoeus_partial.mustache b/modules/openapi-generator/src/main/resources/crystal/api_client_typhoeus_partial.mustache new file mode 100644 index 000000000000..d8e95e774618 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_client_typhoeus_partial.mustache @@ -0,0 +1,153 @@ + # Call an API with given options. + # + # @return [Array<(Object, Integer, Hash)>] an array of 3 elements: + # the data deserialized from response body (could be nil), response status code and response headers. + def call_api(http_method, path, opts = {} of Symbol => String) + request = build_request(http_method, path, opts) + response = request.run + + if @config.debugging + @config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n" + end + + unless response.success? + if response.timed_out? + fail ApiError.new("Connection timed out") + elsif response.code == 0 + # Errors from libcurl will be made visible here + fail ApiError.new(code: 0, + message: response.return_message) + else + fail ApiError.new(code: response.code, + response_headers: response.headers, + response_body: response.body), + response.status_message + end + end + + if opts[:return_type] + data = deserialize(response, opts[:return_type]) + else + data = nil + end + return data, response.code, response.headers + end + + # Builds the HTTP request + # + # @param [String] http_method HTTP method/verb (e.g. POST) + # @param [String] path URL path (e.g. /account/new) + # @option opts [Hash] :header_params Header parameters + # @option opts [Hash] :query_params Query parameters + # @option opts [Hash] :form_params Query parameters + # @option opts [Object] :body HTTP body (JSON/XML) + # @return [Typhoeus::Request] A Typhoeus Request + def build_request(http_method, path, opts = {} of Symbol => String) + url = build_request_url(path, opts) + http_method = http_method.to_sym.downcase + + header_params = @default_headers.merge(opts[:header_params] || {} of Symbol => String) + query_params = opts[:query_params] || {} of Symbol => String + form_params = opts[:form_params] || {} of Symbol => String + + {{#hasAuthMethods}} + update_params_for_auth! header_params, query_params, opts[:auth_names] + {{/hasAuthMethods}} + + # set ssl_verifyhosts option based on @config.verify_ssl_host (true/false) + _verify_ssl_host = @config.verify_ssl_host ? 2 : 0 + + req_opts = { + :method => http_method, + :headers => header_params, + :params => query_params, + :params_encoding => @config.params_encoding, + :timeout => @config.timeout, + :ssl_verifypeer => @config.verify_ssl, + :ssl_verifyhost => _verify_ssl_host, + :sslcert => @config.cert_file, + :sslkey => @config.key_file, + :verbose => @config.debugging + } + + # set custom cert, if provided + req_opts[:cainfo] = @config.ssl_ca_cert if @config.ssl_ca_cert + + if [:post, :patch, :put, :delete].include?(http_method) + req_body = build_request_body(header_params, form_params, opts[:body]) + req_opts.update body: req_body + if @config.debugging + @config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n" + end + end + + request = Typhoeus::Request.new(url, req_opts) + download_file(request) if opts[:return_type] == "File" + request + end + + # Builds the HTTP request body + # + # @param [Hash] header_params Header parameters + # @param [Hash] form_params Query parameters + # @param [Object] body HTTP body (JSON/XML) + # @return [String] HTTP body data in the form of string + def build_request_body(header_params, form_params, body) + # http form + if header_params["Content-Type"] == "application/x-www-form-urlencoded" || + header_params["Content-Type"] == "multipart/form-data" + data = {} of Symbol => String + form_params.each do |key, value| + case value + when ::File, ::Array, nil + # let typhoeus handle File, Array and nil parameters + data[key] = value + else + data[key] = value.to_s + end + end + elsif body + data = body.is_a?(String) ? body : body.to_json + else + data = nil + end + data + end + + # Save response body into a file in (the defined) temporary folder, using the filename + # from the "Content-Disposition" header if provided, otherwise a random filename. + # The response body is written to the file in chunks in order to handle files which + # size is larger than maximum Ruby String or even larger than the maximum memory a Ruby + # process can use. + # + # @see Configuration#temp_folder_path + def download_file(request) + tempfile = nil + encoding = nil + request.on_headers do |response| + content_disposition = response.headers["Content-Disposition"] + if content_disposition && content_disposition =~ /filename=/i + filename = content_disposition[/filename=[""]?([^""\s]+)[""]?/, 1] + prefix = sanitize_filename(filename) + else + prefix = "download-" + end + prefix = prefix + "-" unless prefix.end_with?("-") + encoding = response.body.encoding + tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) + @tempfile = tempfile + end + request.on_body do |chunk| + chunk.force_encoding(encoding) + tempfile.write(chunk) + end + request.on_complete do |response| + if tempfile + tempfile.close + @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\ + "with e.g. `FileUtils.cp(tempfile.path, \"/new/file/path\")` otherwise the temp file "\ + "will be deleted automatically with GC. It's also recommended to delete the temp file "\ + "explicitly with `tempfile.delete`" + end + end + end diff --git a/modules/openapi-generator/src/main/resources/crystal/api_doc.mustache b/modules/openapi-generator/src/main/resources/crystal/api_doc.mustache new file mode 100644 index 000000000000..bdeeb5689122 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_doc.mustache @@ -0,0 +1,118 @@ +# {{moduleName}}::{{classname}}{{#description}} + +{{description}}{{/description}} + +All URIs are relative to *{{basePath}}* + +| Method | HTTP request | Description | +| ------ | ------------ | ----------- | +{{#operations}} +{{#operation}} +| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} | +{{/operation}} +{{/operations}} + +{{#operations}} +{{#operation}} + +## {{operationId}} + +> {{#returnType}}{{#returnTypeIsPrimitive}}{{returnType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}<{{{returnType}}}>{{/returnTypeIsPrimitive}} {{/returnType}}{{operationId}}{{#hasParams}}({{#requiredParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/requiredParams}}{{#optionalParams}}{{#-last}}{{#hasRequiredParams}}, {{/hasRequiredParams}}opts{{/-last}}{{/optionalParams}}){{/hasParams}} + +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Examples + +```ruby +require 'time' +require '{{{gemName}}}' +{{#hasAuthMethods}} +# setup authorization +{{{moduleName}}}.configure do |config|{{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + # Configure HTTP basic authorization: {{{name}}} + config.username = 'YOUR USERNAME' + config.password = 'YOUR PASSWORD'{{/isBasicBasic}}{{#isBasicBearer}} + # Configure Bearer authorization{{#bearerFormat}} ({{{.}}}){{/bearerFormat}}: {{{name}}} + config.access_token = 'YOUR_BEARER_TOKEN'{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + # Configure API key authorization: {{{name}}} + config.api_key['{{{keyParamName}}}'] = 'YOUR API KEY' + # Uncomment the following line to set a prefix for the API key, e.g. 'Bearer' (defaults to nil) + # config.api_key_prefix['{{{keyParamName}}}'] = 'Bearer'{{/isApiKey}}{{#isOAuth}} + # Configure OAuth2 access token for authorization: {{{name}}} + config.access_token = 'YOUR ACCESS TOKEN'{{/isOAuth}} +{{/authMethods}}end +{{/hasAuthMethods}} + +api_instance = {{{moduleName}}}::{{{classname}}}.new +{{#requiredParams}} +{{{paramName}}} = {{{vendorExtensions.x-ruby-example}}} # {{{dataType}}} | {{{description}}} +{{/requiredParams}} +{{#optionalParams}} +{{#-first}} +opts = { +{{/-first}} + {{{paramName}}}: {{{vendorExtensions.x-ruby-example}}}{{^-last}},{{/-last}} # {{{dataType}}} | {{{description}}} +{{#-last}} +} +{{/-last}} +{{/optionalParams}} + +begin + {{#summary}}# {{{.}}}{{/summary}} + {{#returnType}}result = {{/returnType}}api_instance.{{{operationId}}}{{#hasParams}}({{#requiredParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/requiredParams}}{{#optionalParams}}{{#-last}}{{#hasRequiredParams}}, {{/hasRequiredParams}}opts{{/-last}}{{/optionalParams}}){{/hasParams}} + {{#returnType}} + p result + {{/returnType}} +rescue {{{moduleName}}}::ApiError => e + puts "Error when calling {{classname}}->{{{operationId}}}: #{e}" +end +``` + +#### Using the {{operationId}}_with_http_info variant + +This returns an Array which contains the response data{{^returnType}} (`nil` in this case){{/returnType}}, status code and headers. + +> {{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}nil{{/returnType}}, Integer, Hash)> {{operationId}}_with_http_info{{#hasParams}}({{#requiredParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/requiredParams}}{{#optionalParams}}{{#-last}}{{#hasRequiredParams}}, {{/hasRequiredParams}}opts{{/-last}}{{/optionalParams}}){{/hasParams}} + +```ruby +begin + {{#summary}}# {{{.}}}{{/summary}} + data, status_code, headers = api_instance.{{{operationId}}}_with_http_info{{#hasParams}}({{#requiredParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/requiredParams}}{{#optionalParams}}{{#-last}}{{#hasRequiredParams}}, {{/hasRequiredParams}}opts{{/-last}}{{/optionalParams}}){{/hasParams}} + p status_code # => 2xx + p headers # => { ... } + p data # => {{#returnType}}{{#returnTypeIsPrimitive}}{{returnType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}<{{{returnType}}}>{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}nil{{/returnType}} +rescue {{{moduleName}}}::ApiError => e + puts "Error when calling {{classname}}->{{{operationId}}}_with_http_info: #{e}" +end +``` + +### Parameters + +{{^allParams}} +This endpoint does not need any parameter. +{{/allParams}} +{{#allParams}} +{{#-first}} +| Name | Type | Description | Notes | +| ---- | ---- | ----------- | ----- | +{{/-first}} +| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}} | {{description}} | {{^required}}[optional]{{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}} | +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}nil (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + +- **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} +- **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/crystal/api_error.mustache b/modules/openapi-generator/src/main/resources/crystal/api_error.mustache new file mode 100644 index 000000000000..fd39b5bef277 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_error.mustache @@ -0,0 +1,33 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +module {{moduleName}} + class ApiError < Exception + getter code : Int32? + getter response_headers : Hash(String, Array(String) | String)? + + # Usage examples: + # ApiError.new + # ApiError.new(message: "message") + # ApiError.new(code: 500, response_headers: {}, message: "") + # ApiError.new(code: 404, message: "Not Found") + def initialize(@code , @message, @response_headers) + end + + def initialize(@code , @message) + end + + # Override to_s to display a friendly error message + def to_s + msg = "" + msg = msg + "\nHTTP status code: #{code}" if @code + msg = msg + "\nResponse headers: #{response_headers}" if @response_headers + if @message.nil? || @message.empty? + msg = msg + "\nError message: the server returns an error but the HTTP respone body is empty." + else + msg = msg + "\nResponse body: #{@message}" + end + + msg + end + end +end diff --git a/modules/openapi-generator/src/main/resources/crystal/api_info.mustache b/modules/openapi-generator/src/main/resources/crystal/api_info.mustache new file mode 100644 index 000000000000..1b3f9cb5ac45 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_info.mustache @@ -0,0 +1,12 @@ +{{#appName}} +#{{{appName}}} + +{{/appName}} +{{#appDescription}} +#{{{appDescription}}} + +{{/appDescription}} +{{#version}}The version of the OpenAPI document: {{version}}{{/version}} +{{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} +Generated by: https://openapi-generator.tech +OpenAPI Generator version: {{{generatorVersion}}} diff --git a/modules/openapi-generator/src/main/resources/crystal/api_test.mustache b/modules/openapi-generator/src/main/resources/crystal/api_test.mustache new file mode 100644 index 000000000000..a2c6ab5f0215 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/api_test.mustache @@ -0,0 +1,38 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for {{moduleName}}::{{classname}} +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +{{#operations}}describe "{{classname}}" do + describe "test an instance of {{classname}}" do + it "should create an instance of {{classname}}" do + api_instance = {{moduleName}}::{{classname}}.new + # TODO expect(api_instance).to be_instance_of({{moduleName}}::{{classname}}) + end + end + +{{#operation}} + # unit tests for {{operationId}} + {{#summary}} + # {{summary}} + {{/summary}} + {{#notes}} + # {{notes}} + {{/notes}} +{{#allParams}}{{#required}} # @param {{paramName}} {{description}} +{{/required}}{{/allParams}} # @param [Hash] opts the optional parameters +{{#allParams}}{{^required}} # @option opts [{{{dataType}}}] :{{paramName}} {{description}} +{{/required}}{{/allParams}} # @return [{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}nil{{/returnType}}] + describe "{{operationId}} test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +{{/operation}} +end +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/crystal/base_object.mustache b/modules/openapi-generator/src/main/resources/crystal/base_object.mustache new file mode 100644 index 000000000000..c7abd09b36bd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/base_object.mustache @@ -0,0 +1,120 @@ + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + {{#parent}} + super(attributes) + {{/parent}} + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = {{moduleName}}.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {{^parent}}{} of Symbol => String{{/parent}}{{#parent}}super{{/parent}} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end diff --git a/modules/openapi-generator/src/main/resources/crystal/configuration.mustache b/modules/openapi-generator/src/main/resources/crystal/configuration.mustache new file mode 100644 index 000000000000..a3a815fc0421 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/configuration.mustache @@ -0,0 +1,356 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +require "log" + +module {{moduleName}} + class Configuration + # Defines url scheme + property scheme : String + + # Defines url host + property host : String + + # Defines url base path + property base_path : String + + # Define server configuration index + property server_index : Int32 + + # Define server operation configuration index + property server_operation_index : Hash(Symbol, String) + + # Default server variables + property server_variables : Hash(Symbol, String) + + # Default server operation variables + property server_operation_variables : Hash(Symbol, String) + + # Defines API keys used with API Key authentications. + # + # @return [Hash] key: parameter name, value: parameter value (API key) + # + # @example parameter name is "api_key", API key is "xxx" (e.g. "api_key=xxx" in query string) + # config.api_key[:"api_key"] = "xxx" + property api_key : Hash(Symbol, String) + + # Defines API key prefixes used with API Key authentications. + # + # @return [Hash] key: parameter name, value: API key prefix + # + # @example parameter name is "Authorization", API key prefix is "Token" (e.g. "Authorization: Token xxx" in headers) + # config.api_key_prefix[:"api_key"] = "Token" + property api_key_prefix : Hash(Symbol, String) + + # Defines the username used with HTTP basic authentication. + # + # @return [String] + property username : String? + + # Defines the password used with HTTP basic authentication. + # + # @return [String] + property password : String? + + # Defines the access token (Bearer) used with OAuth2. + property access_token : String? + + # Set this to enable/disable debugging. When enabled (set to true), HTTP request/response + # details will be logged with `logger.debug` (see the `logger` attribute). + # Default to false. + # + # @return [true, false] + property debugging : Bool + + # Defines the temporary folder to store downloaded files + # (for API endpoints that have file response). + # Default to use `Tempfile`. + # + # @return [String] + property temp_folder_path : String? + + # The time limit for HTTP request in seconds. + # Default to 0 (never times out). + property timeout : Int32 + + # Set this to false to skip client side validation in the operation. + # Default to true. + # @return [true, false] + property client_side_validation : Bool + + ### TLS/SSL setting + # Set this to false to skip verifying SSL certificate when calling API from https server. + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + #TODO attr_accessor :verify_ssl + + ### TLS/SSL setting + # Set this to false to skip verifying SSL host name + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + # TODO attr_accessor :verify_ssl_host + + ### TLS/SSL setting + # Set this to customize the certificate file to verify the peer. + # + # @return [String] the path to the certificate file + # + # @see The `cainfo` option of Typhoeus, `--cert` option of libcurl. Related source code: + # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145 + # TODO attr_accessor :ssl_ca_cert + + ### TLS/SSL setting + # Client certificate file (for client certificate) + # TODO attr_accessor :cert_file + + ### TLS/SSL setting + # Client private key file (for client certificate) + # TODO attr_accessor :key_file + + # Set this to customize parameters encoding of array parameter with multi collectionFormat. + # Default to Nil. + # + # @see The params_encoding option of Ethon. Related source code: + # https://github.com/typhoeus/ethon/blob/master/lib/ethon/easy/queryable.rb#L96 + #property params_encoding : String? + + def initialize + @scheme = "{{scheme}}" + @host = "{{host}}{{#port}}:{{{.}}}{{/port}}" + @base_path = "{{contextPath}}" + @server_index = 0 + @server_operation_index = {} of Symbol => String + @server_variables = {} of Symbol => String + @server_operation_variables = {} of Symbol => String + @api_key = {} of Symbol => String + @api_key_prefix = {} of Symbol => String + @timeout = 0 + @client_side_validation = true + @verify_ssl = true + @verify_ssl_host = true + #@params_encoding = nil + #@cert_file = nil + #@key_file = nil + @debugging = false + @username = nil + @password = nil + @access_token = nil + @temp_folder_path = nil + + # TODO revise below to support block + #yield(self) if block_given? + end + + # The default Configuration object. + def self.default + @@default ||= Configuration.new + end + + def configure + yield(self) if block_given? + end + + def scheme=(scheme) + # remove :// from scheme + @scheme = scheme.sub(/:\/\//, "") + end + + def host=(host) + # remove http(s):// and anything after a slash + @host = host.sub(/https?:\/\//, "").split("/").first + end + + def base_path=(base_path) + # Add leading and trailing slashes to base_path + @base_path = "/#{base_path}".gsub(/\/+/, "/") + @base_path = "" if @base_path == "/" + end + + # Returns base URL for specified operation based on server settings + def base_url(operation = Nil) + # TODO revise below to support operation-level server setting + #index = server_operation_index.fetch(operation, server_index) + return "#{scheme}://#{[host, base_path].join("/").gsub(/\/+/, "/")}".sub(/\/+\z/, "") #if index == Nil + + #server_url(index, server_operation_variables.fetch(operation, server_variables), operation_server_settings[operation]) + end + + # Gets API key (with prefix if set). + # @param [String] param_name the parameter name of API key auth + def api_key_with_prefix(param_name) + if @api_key_prefix[param_name] + "#{@api_key_prefix[param_name]} #{@api_key[param_name]}" + else + @api_key[param_name] + end + end + + # Gets Basic Auth token string + def basic_auth_token + "Basic " + ["#{username}:#{password}"].pack("m").delete("\r\n") + end + + # Returns Auth Settings hash for api client. + def auth_settings + Hash{ {{#authMethods}}{{#isApiKey}}"{{name}}" => { + type: "api_key", + in: {{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}, + key: "{{keyParamName}}", + value: api_key_with_prefix("{{keyParamName}}") + }, +{{/isApiKey}} +{{#isBasic}} +{{#isBasicBasic}} + "{{name}}" => + { + type: "basic", + in: "header", + key: "Authorization", + value: basic_auth_token + }, +{{/isBasicBasic}} +{{#isBasicBearer}} + "{{name}}" => + { + type: "bearer", + in: "header", + {{#bearerFormat}} + format: "{{{.}}}", + {{/bearerFormat}} + key: "Authorization", + value: "Bearer #{access_token}" + }, +{{/isBasicBearer}} +{{/isBasic}} +{{#isOAuth}} + "{{name}}" => + { + type: "oauth2", + in: "header", + key: "Authorization", + value: "Bearer #{access_token}" + }, +{{/isOAuth}} +{{/authMethods}} + } + end + + # Returns an array of Server setting + def server_settings + [ + {{#servers}} + { + url: "{{{url}}}", + description: "{{{description}}}{{^description}}No description provided{{/description}}", + {{#variables}} + {{#-first}} + variables: { + {{/-first}} + {{{name}}}: { + description: "{{{description}}}{{^description}}No description provided{{/description}}", + default_value: "{{{defaultValue}}}", + {{#enumValues}} + {{#-first}} + enum_values: [ + {{/-first}} + "{{{.}}}"{{^-last}},{{/-last}} + {{#-last}} + ] + {{/-last}} + {{/enumValues}} + }{{^-last}},{{/-last}} + {{#-last}} + } + {{/-last}} + {{/variables}} + }{{^-last}},{{/-last}} + {{/servers}} + ] + end + + def operation_server_settings + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + {{#servers}} + {{#-first}} + { + "{{{classname}}}.{{{nickname}}}": [ + {{/-first}} + { + url: "{{{url}}}", + description: "{{{description}}}{{^description}}No description provided{{/description}}", + {{#variables}} + {{#-first}} + variables: { + {{/-first}} + {{{name}}}: { + description: "{{{description}}}{{^description}}No description provided{{/description}}", + default_value: "{{{defaultValue}}}", + {{#enumValues}} + {{#-first}} + enum_values: [ + {{/-first}} + "{{{.}}}"{{^-last}},{{/-last}} + {{#-last}} + ] + {{/-last}} + {{/enumValues}} + }{{^-last}},{{/-last}} + {{#-last}} + } + {{/-last}} + {{/variables}} + }{{^-last}},{{/-last}} + {{#-last}} + ], + } + {{/-last}} + {{/servers}} + {{/operation}} + {{/operations}} + {{/apis}} + {{/apiInfo}} + end + + # Returns URL based on server settings + # + # @param index array index of the server settings + # @param variables hash of variable and the corresponding value + def server_url(index, variables = {} of Symbol => String, servers = Nil) + servers = server_settings if servers == Nil + + # check array index out of bound + if (index < 0 || index >= servers.size) + raise ArgumentError.new("Invalid index #{index} when selecting the server. Must be less than #{servers.size}") + end + + server = servers[index] + url = server[:url] + + return url unless server.key? :variables + + # go through variable and assign a value + server[:variables].each do |name, variable| + if variables.key?(name) + if (!server[:variables][name].key?(:enum_values) || server[:variables][name][:enum_values].include?(variables[name])) + url.gsub! "{" + name.to_s + "}", variables[name] + else + raise ArgumentError.new("The variable `#{name}` in the server URL has invalid value #{variables[name]}. Must be #{server[:variables][name][:enum_values]}.") + end + else + # use default value + url.gsub! "{" + name.to_s + "}", server[:variables][name][:default_value] + end + end + + url + end + end +end diff --git a/modules/openapi-generator/src/main/resources/crystal/configuration_spec.mustache b/modules/openapi-generator/src/main/resources/crystal/configuration_spec.mustache new file mode 100644 index 000000000000..22a113e8e324 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/configuration_spec.mustache @@ -0,0 +1,34 @@ +=begin +{{> api_info}} +=end + +require 'spec_helper' + +describe {{moduleName}}::Configuration do + let(:config) { {{moduleName}}::Configuration.default } + + before(:each) do + # uncomment below to setup host and base_path + # require 'URI' + # uri = URI.parse("{{{basePath}}}") + # {{moduleName}}.configure do |c| + # c.host = uri.host + # c.base_path = uri.path + # end + end + + describe '#base_url' do + it 'should have the default value' do + # uncomment below to test default value of the base path + # expect(config.base_url).to eq("{{{basePath}}}") + end + + it 'should remove trailing slashes' do + [nil, '', '/', '//'].each do |base_path| + config.base_path = base_path + # uncomment below to test trailing slashes + # expect(config.base_url).to eq("{{{basePath}}}") + end + end + end +end diff --git a/modules/openapi-generator/src/main/resources/crystal/configuration_tls_faraday_partial.mustache b/modules/openapi-generator/src/main/resources/crystal/configuration_tls_faraday_partial.mustache new file mode 100644 index 000000000000..c35c988f9330 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/configuration_tls_faraday_partial.mustache @@ -0,0 +1,29 @@ + ### TLS/SSL setting + # Set this to false to skip verifying SSL certificate when calling API from https server. + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + #TODO attr_accessor :ssl_verify + + ### TLS/SSL setting + # Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html) + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + #TODO attr_accessor :ssl_verify_mode + + ### TLS/SSL setting + # Set this to customize the certificate file to verify the peer. + # + # @return [String] the path to the certificate file + #TODO attr_accessor :ssl_ca_file + + ### TLS/SSL setting + # Client certificate file (for client certificate) + #TODO attr_accessor :ssl_client_cert + + ### TLS/SSL setting + # Client private key file (for client certificate) + #TODO attr_accessor :ssl_client_key diff --git a/modules/openapi-generator/src/main/resources/crystal/configuration_tls_typhoeus_partial.mustache b/modules/openapi-generator/src/main/resources/crystal/configuration_tls_typhoeus_partial.mustache new file mode 100644 index 000000000000..1ab1b2d03079 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/configuration_tls_typhoeus_partial.mustache @@ -0,0 +1,34 @@ + ### TLS/SSL setting + # Set this to false to skip verifying SSL certificate when calling API from https server. + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + #TODO attr_accessor :verify_ssl + + ### TLS/SSL setting + # Set this to false to skip verifying SSL host name + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + # TODO attr_accessor :verify_ssl_host + + ### TLS/SSL setting + # Set this to customize the certificate file to verify the peer. + # + # @return [String] the path to the certificate file + # + # @see The `cainfo` option of Typhoeus, `--cert` option of libcurl. Related source code: + # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145 + # TODO attr_accessor :ssl_ca_cert + + ### TLS/SSL setting + # Client certificate file (for client certificate) + # TODO attr_accessor :cert_file + + ### TLS/SSL setting + # Client private key file (for client certificate) + # TODO attr_accessor :key_file diff --git a/modules/openapi-generator/src/main/resources/crystal/git_push.sh.mustache b/modules/openapi-generator/src/main/resources/crystal/git_push.sh.mustache new file mode 100755 index 000000000000..8b3f689c9121 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/git_push.sh.mustache @@ -0,0 +1,58 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="{{{gitHost}}}" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/modules/openapi-generator/src/main/resources/crystal/gitignore.mustache b/modules/openapi-generator/src/main/resources/crystal/gitignore.mustache new file mode 100644 index 000000000000..05a17cb8f0a0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/gitignore.mustache @@ -0,0 +1,39 @@ +# Generated by: https://openapi-generator.tech +# + +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +## Specific to RubyMotion: +.dat* +.repl_history +build/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc diff --git a/modules/openapi-generator/src/main/resources/crystal/model.mustache b/modules/openapi-generator/src/main/resources/crystal/model.mustache new file mode 100644 index 000000000000..a19cc5f3f6a6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/model.mustache @@ -0,0 +1,23 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +require "time" + +module {{moduleName}} +{{#models}} +{{#model}} +{{#isEnum}} +{{>partial_model_enum_class}} +{{/isEnum}} +{{^isEnum}} +{{#oneOf}} +{{#-first}} +{{>partial_oneof_module}} +{{/-first}} +{{/oneOf}} +{{^oneOf}} +{{>partial_model_generic}} +{{/oneOf}} +{{/isEnum}} +{{/model}} +{{/models}} +end diff --git a/modules/openapi-generator/src/main/resources/crystal/model_doc.mustache b/modules/openapi-generator/src/main/resources/crystal/model_doc.mustache new file mode 100644 index 000000000000..37809685d1c0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/model_doc.mustache @@ -0,0 +1,12 @@ +{{#models}} +{{#model}} +{{#oneOf}} +{{#-first}} +{{>partial_oneof_module_doc}} +{{/-first}} +{{/oneOf}} +{{^oneOf}} +{{>partial_model_generic_doc}} +{{/oneOf}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/crystal/model_test.mustache b/modules/openapi-generator/src/main/resources/crystal/model_test.mustache new file mode 100644 index 000000000000..08733f8000bf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/model_test.mustache @@ -0,0 +1,75 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for {{moduleName}}::{{classname}} +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +{{#models}} +{{#model}} +describe {{moduleName}}::{{classname}} do +{{^oneOf}} + + describe "test an instance of {{classname}}" do + it "should create an instance of {{classname}}" do + #instance = {{moduleName}}::{{classname}}.new + #expect(instance).to be_instance_of({{moduleName}}::{{classname}}) + end + end +{{#vars}} + describe "test attribute '{{{name}}}'" do + it "should work" do + {{#isEnum}} + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # validator = Petstore::EnumTest::EnumAttributeValidator.new("{{{dataType}}}", [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}]) + # validator.allowable_values.each do |value| + # expect { instance.{{name}} = value }.not_to raise_error + # end + {{/isEnum}} + {{^isEnum}} + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + {{/isEnum}} + end + end + +{{/vars}} +{{/oneOf}} +{{#oneOf}} +{{#-first}} + describe ".openapi_one_of" do + it "lists the items referenced in the oneOf array" do + expect(described_class.openapi_one_of).to_not be_empty + end + end + + {{#discriminator}} + {{#propertyName}} + describe ".openapi_discriminator_name" do + it "returns the value of the "discriminator" property" do + expect(described_class.openapi_discriminator_name).to_not be_empty + end + end + + {{/propertyName}} + {{#mappedModels}} + {{#-first}} + describe ".openapi_discriminator_mapping" do + it "returns the key/values of the "mapping" property" do + expect(described_class.openapi_discriminator_mapping.values.sort).to eq(described_class.openapi_one_of.sort) + end + end + + {{/-first}} + {{/mappedModels}} + {{/discriminator}} + describe ".build" do + it "returns the correct model" do + end + end +{{/-first}} +{{/oneOf}} +end +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/crystal/partial_model_enum_class.mustache b/modules/openapi-generator/src/main/resources/crystal/partial_model_enum_class.mustache new file mode 100644 index 000000000000..4b8b5a0ffdae --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/partial_model_enum_class.mustache @@ -0,0 +1,20 @@ + class {{classname}}{{#allowableValues}}{{#enumVars}} + {{{name}}} = {{{value}}}.freeze{{/enumVars}} + +{{/allowableValues}} + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def self.build_from_hash(value) + new.build_from_hash(value) + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def build_from_hash(value) + constantValues = {{classname}}.constants.select { |c| {{classname}}::const_get(c) == value } + raise "Invalid ENUM value #{value} for class #{{{classname}}}" if constantValues.empty? + value + end + end \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/crystal/partial_model_generic.mustache b/modules/openapi-generator/src/main/resources/crystal/partial_model_generic.mustache new file mode 100644 index 000000000000..18f00b1d5298 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/partial_model_generic.mustache @@ -0,0 +1,296 @@ + {{#description}} + # {{{description}}} + {{/description}} + class {{classname}}{{#parent}} < {{{.}}}{{/parent}} include JSON::Serializable + include JSON::Serializable {{#vars}} + {{#description}} + # {{{description}}} + {{/description}} + @[JSON::Field(key: {{{baseName}}}, type: {{{dataType}}}{{#default}}, default: {{{.}}}{{/default}}{{#isNullable}}, nilable: true, emit_null: true{{/isNullable}})] + property {{{name}}} : {{{dataType}}} + + {{/vars}} +{{#hasEnums}} + class EnumAttributeValidator + getter datatype : String + getter allowable_values : Array(String) + + def initialize(datatype, allowable_values) + @datatype = datatype + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + +{{/hasEnums}} + {{#anyOf}} + {{#-first}} + # List of class defined in anyOf (OpenAPI v3) + def self.openapi_any_of + [ + {{/-first}} + :"{{{.}}}"{{^-last}},{{/-last}} + {{#-last}} + ] + end + + {{/-last}} + {{/anyOf}} + {{#allOf}} + {{#-first}} + # List of class defined in allOf (OpenAPI v3) + def self.openapi_all_of + [ + {{/-first}} + :"{{{.}}}"{{^-last}},{{/-last}} + {{#-last}} + ] + end + + {{/-last}} + {{/allOf}} + {{#discriminator}} + {{#propertyName}} + # discriminator's property name in OpenAPI v3 + def self.openapi_discriminator_name + :"{{{.}}}" + end + + {{/propertyName}} + {{/discriminator}} + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize({{#vars}}@{{{name}}} : {{{dataType}}}{{^required}} | Nil{{/required}}{{^-last}}, {{/-last}}{{/vars}}) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = {{^parent}}Array.new{{/parent}}{{#parent}}super{{/parent}} + {{#vars}} + {{^isNullable}} + {{#required}} + if @{{{name}}}.nil? + invalid_properties.push("invalid value for \"{{{name}}}\", {{{name}}} cannot be nil.") + end + + {{/required}} + {{/isNullable}} + {{#hasValidation}} + {{#maxLength}} + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.to_s.length > {{{maxLength}}} + invalid_properties.push("invalid value for \"{{{name}}}\", the character length must be smaller than or equal to {{{maxLength}}}.") + end + + {{/maxLength}} + {{#minLength}} + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.to_s.length < {{{minLength}}} + invalid_properties.push("invalid value for \"{{{name}}}\", the character length must be great than or equal to {{{minLength}}}.") + end + + {{/minLength}} + {{#maximum}} + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{{maximum}}} + invalid_properties.push("invalid value for \"{{{name}}}\", must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{{maximum}}}.") + end + + {{/maximum}} + {{#minimum}} + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{{minimum}}} + invalid_properties.push("invalid value for \"{{{name}}}\", must be greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{{minimum}}}.") + end + + {{/minimum}} + {{#pattern}} + pattern = Regexp.new({{{pattern}}}) + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}} !~ pattern + invalid_properties.push("invalid value for \"{{{name}}}\", must conform to the pattern #{pattern}.") + end + + {{/pattern}} + {{#maxItems}} + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.length > {{{maxItems}}} + invalid_properties.push("invalid value for \"{{{name}}}\", number of items must be less than or equal to {{{maxItems}}}." + end + + {{/maxItems}} + {{#minItems}} + if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.length < {{{minItems}}} + invalid_properties.push("invalid value for \"{{{name}}}\", number of items must be greater than or equal to {{{minItems}}}." + end + + {{/minItems}} + {{/hasValidation}} + {{/vars}} + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + {{#vars}} + {{^isNullable}} + {{#required}} + return false if @{{{name}}}.nil? + {{/required}} + {{/isNullable}} + {{#isEnum}} + {{^isContainer}} + {{{name}}}_validator = EnumAttributeValidator.new("{{{dataType}}}", [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}]) + return false unless {{{name}}}_validator.valid?(@{{{name}}}) + {{/isContainer}} + {{/isEnum}} + {{#hasValidation}} + {{#maxLength}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.to_s.length > {{{maxLength}}} + {{/maxLength}} + {{#minLength}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.to_s.length < {{{minLength}}} + {{/minLength}} + {{#maximum}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{{maximum}}} + {{/maximum}} + {{#minimum}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{{minimum}}} + {{/minimum}} + {{#pattern}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}} !~ Regexp.new({{{pattern}}}) + {{/pattern}} + {{#maxItems}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.length > {{{maxItems}}} + {{/maxItems}} + {{#minItems}} + return false if {{^required}}!@{{{name}}}.nil? && {{/required}}@{{{name}}}.length < {{{minItems}}} + {{/minItems}} + {{/hasValidation}} + {{/vars}} + {{#anyOf}} + {{#-first}} + _any_of_found = false + self.class.openapi_any_of.each do |_class| + _any_of = {{moduleName}}.const_get(_class).build_from_hash(self.to_hash) + if _any_of.valid? + _any_of_found = true + end + end + + if !_any_of_found + return false + end + + {{/-first}} + {{/anyOf}} + true{{#parent}} && super{{/parent}} + end + + {{#vars}} + {{#isEnum}} + {{^isContainer}} + # Custom attribute writer method checking allowed values (enum). + # @param [Object] {{{name}}} Object to be assigned + def {{{name}}}=({{{name}}}) + validator = EnumAttributeValidator.new("{{{dataType}}}", [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}]) + unless validator.valid?({{{name}}}) + raise ArgumentError.new("invalid value for \"{{{name}}}\", must be one of #{validator.allowable_values}.") + end + @{{{name}}} = {{{name}}} + end + + {{/isContainer}} + {{/isEnum}} + {{^isEnum}} + {{#hasValidation}} + # Custom attribute writer method with validation + # @param [Object] {{{name}}} Value to be assigned + def {{{name}}}=({{{name}}}) + {{^isNullable}} + {{#required}} + if {{{name}}}.nil? + raise ArgumentError.new("{{{name}}} cannot be nil") + end + + {{/required}} + {{/isNullable}} + {{#maxLength}} + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}}.to_s.length > {{{maxLength}}} + raise ArgumentError.new("invalid value for "{{{name}}}", the character length must be smaller than or equal to {{{maxLength}}}.") + end + + {{/maxLength}} + {{#minLength}} + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}}.to_s.length < {{{minLength}}} + raise ArgumentError.new("invalid value for \"{{{name}}}\", the character length must be great than or equal to {{{minLength}}}.") + end + + {{/minLength}} + {{#maximum}} + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{{maximum}}} + raise ArgumentError.new("invalid value for \"{{{name}}}\", must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{{maximum}}}.") + end + + {{/maximum}} + {{#minimum}} + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{{minimum}}} + raise ArgumentError.new("invalid value for \"{{{name}}}\", must be greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{{minimum}}}.") + end + + {{/minimum}} + {{#pattern}} + pattern = Regexp.new({{{pattern}}}) + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}} !~ pattern + raise ArgumentError.new("invalid value for \"{{{name}}}\", must conform to the pattern #{pattern}.") + end + + {{/pattern}} + {{#maxItems}} + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}}.length > {{{maxItems}}} + raise ArgumentError.new("invalid value for \"{{{name}}}\", number of items must be less than or equal to {{{maxItems}}}.") + end + + {{/maxItems}} + {{#minItems}} + if {{^required}}!{{{name}}}.nil? && {{/required}}{{{name}}}.length < {{{minItems}}} + raise ArgumentError.new("invalid value for \"{{{name}}}\", number of items must be greater than or equal to {{{minItems}}}.") + end + + {{/minItems}} + @{{{name}}} = {{{name}}} + end + + {{/hasValidation}} + {{/isEnum}} + {{/vars}} + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class{{#vars}} && + {{name}} == o.{{name}}{{/vars}}{{#parent}} && super(o){{/parent}} + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [{{#vars}}{{name}}{{^-last}}, {{/-last}}{{/vars}}].hash + end + +{{> base_object}} + end diff --git a/modules/openapi-generator/src/main/resources/crystal/partial_model_generic_doc.mustache b/modules/openapi-generator/src/main/resources/crystal/partial_model_generic_doc.mustache new file mode 100644 index 000000000000..f188dd23e6ec --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/partial_model_generic_doc.mustache @@ -0,0 +1,28 @@ +# {{moduleName}}::{{classname}} + +## Properties + +| Name | Type | Description | Notes | +| ---- | ---- | ----------- | ----- | +{{#vars}} +| **{{name}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional]{{/required}}{{#isReadOnly}}[readonly]{{/isReadOnly}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}} | +{{/vars}} + +## Example + +```ruby +require '{{{gemName}}}' + +{{^vars}} +instance = {{moduleName}}::{{classname}}.new() +{{/vars}} +{{#vars}} +{{#-first}} +instance = {{moduleName}}::{{classname}}.new( +{{/-first}} + {{name}}: {{example}}{{^-last}},{{/-last}} +{{#-last}} +) +{{/-last}} +{{/vars}} +``` diff --git a/modules/openapi-generator/src/main/resources/crystal/partial_oneof_module.mustache b/modules/openapi-generator/src/main/resources/crystal/partial_oneof_module.mustache new file mode 100644 index 000000000000..14dc1635bb74 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/partial_oneof_module.mustache @@ -0,0 +1,137 @@ + {{#description}} + # {{{description}}} + {{/description}} + module {{classname}} + class << self + {{#oneOf}} + {{#-first}} + # List of class defined in oneOf (OpenAPI v3) + def openapi_one_of + [ + {{/-first}} + :'{{{.}}}'{{^-last}},{{/-last}} + {{#-last}} + ] + end + + {{/-last}} + {{/oneOf}} + {{#discriminator}} + {{#propertyName}} + # Discriminator's property name (OpenAPI v3) + def openapi_discriminator_name + :'{{{.}}}' + end + + {{/propertyName}} + {{#mappedModels}} + {{#-first}} + # Discriminator's mapping (OpenAPI v3) + def openapi_discriminator_mapping + { + {{/-first}} + :'{{{mappingName}}}' => :'{{{modelName}}}'{{^-last}},{{/-last}} + {{#-last}} + } + end + + {{/-last}} + {{/mappedModels}} + {{/discriminator}} + # Builds the object + # @param [Mixed] Data to be matched against the list of oneOf items + # @return [Object] Returns the model or the data itself + def build(data) + {{#discriminator}} + discriminator_value = data[openapi_discriminator_name] + return nil unless discriminator_value + {{#mappedModels}} + {{#-first}} + + klass = openapi_discriminator_mapping[discriminator_value.to_sym] + return nil unless klass + + {{moduleName}}.const_get(klass).build_from_hash(data) + {{/-first}} + {{/mappedModels}} + {{^mappedModels}} + {{moduleName}}.const_get(discriminator_value).build_from_hash(data) + {{/mappedModels}} + {{/discriminator}} + {{^discriminator}} + # Go through the list of oneOf items and attempt to identify the appropriate one. + # Note: + # - We do not attempt to check whether exactly one item matches. + # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 }) + # due to the way the deserialization is made in the base_object template (it just casts without verifying). + # - TODO: scalar values are defacto behaving as if they were nullable. + # - TODO: logging when debugging is set. + openapi_one_of.each do |klass| + begin + next if klass == :AnyType # "nullable: true" + typed_data = find_and_cast_into_type(klass, data) + return typed_data if typed_data + rescue # rescue all errors so we keep iterating even if the current item lookup raises + end + end + + openapi_one_of.include?(:AnyType) ? data : nil + {{/discriminator}} + end + {{^discriminator}} + + private + + SchemaMismatchError = Class.new(StandardError) + + # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse. + def find_and_cast_into_type(klass, data) + return if data.nil? + + case klass.to_s + when 'Boolean' + return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass) + when 'Float' + return data if data.instance_of?(Float) + when 'Integer' + return data if data.instance_of?(Integer) + when 'Time' + return Time.parse(data) + when 'Date' + return Date.parse(data) + when 'String' + return data if data.instance_of?(String) + when 'Object' # "type: object" + return data if data.instance_of?(Hash) + when /\AArray<(?.+)>\z/ # "type: array" + if data.instance_of?(Array) + sub_type = Regexp.last_match[:sub_type] + return data.map { |item| find_and_cast_into_type(sub_type, item) } + end + when /\AHash.+)>\z/ # "type: object" with "additionalProperties: { ... }" + if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) } + sub_type = Regexp.last_match[:sub_type] + return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) } + end + else # model + const = {{moduleName}}.const_get(klass) + if const + if const.respond_to?(:openapi_one_of) # nested oneOf model + model = const.build(data) + return model if model + else + # raise if data contains keys that are not known to the model + raise unless (data.keys - const.acceptable_attributes).empty? + model = const.build_from_hash(data) + return model if model && model.valid? + end + end + end + + raise # if no match by now, raise + rescue + raise SchemaMismatchError, "#{data} doesn't match the #{klass} type" + end + {{/discriminator}} + end + end diff --git a/modules/openapi-generator/src/main/resources/crystal/partial_oneof_module_doc.mustache b/modules/openapi-generator/src/main/resources/crystal/partial_oneof_module_doc.mustache new file mode 100644 index 000000000000..64a6c32dc854 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/partial_oneof_module_doc.mustache @@ -0,0 +1,92 @@ +# {{moduleName}}::{{classname}} + +## Class instance methods + +### `openapi_one_of` + +Returns the list of classes defined in oneOf. + +#### Example + +```ruby +require '{{{gemName}}}' + +{{moduleName}}::{{classname}}.openapi_one_of +# => +{{#oneOf}} +{{#-first}} +# [ +{{/-first}} +# :'{{{.}}}'{{^-last}},{{/-last}} +{{#-last}} +# ] +{{/-last}} +{{/oneOf}} +``` +{{#discriminator}} +{{#propertyName}} + +### `openapi_discriminator_name` + +Returns the discriminator's property name. + +#### Example + +```ruby +require '{{{gemName}}}' + +{{moduleName}}::{{classname}}.openapi_discriminator_name +# => :'{{{.}}}' +``` +{{/propertyName}} +{{#mappedModels}} +{{#-first}} + +### `openapi_discriminator_name` + +Returns the discriminator's mapping. + +#### Example + +```ruby +require '{{{gemName}}}' + +{{moduleName}}::{{classname}}.openapi_discriminator_mapping +# => +# { +{{/-first}} +# :'{{{mappingName}}}' => :'{{{modelName}}}'{{^-last}},{{/-last}} +{{#-last}} +# } +{{/-last}} +{{/mappedModels}} +{{/discriminator}} + +### build + +Find the appropriate object from the `openapi_one_of` list and casts the data into it. + +#### Example + +```ruby +require '{{{gemName}}}' + +{{moduleName}}::{{classname}}.build(data) +# => {{#oneOf}}{{#-first}}#<{{{.}}}:0x00007fdd4aab02a0>{{/-first}}{{/oneOf}} + +{{moduleName}}::{{classname}}.build(data_that_doesnt_match) +# => nil +``` + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| **data** | **Mixed** | data to be matched against the list of oneOf items | + +#### Return type + +{{#oneOf}} +- `{{{.}}}` +{{/oneOf}} +- `nil` (if no type matches) diff --git a/modules/openapi-generator/src/main/resources/crystal/rspec.mustache b/modules/openapi-generator/src/main/resources/crystal/rspec.mustache new file mode 100644 index 000000000000..83e16f804474 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/rspec.mustache @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/modules/openapi-generator/src/main/resources/crystal/rubocop.mustache b/modules/openapi-generator/src/main/resources/crystal/rubocop.mustache new file mode 100644 index 000000000000..d32b2b1cdab5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/rubocop.mustache @@ -0,0 +1,148 @@ +# This file is based on https://github.com/rails/rails/blob/master/.rubocop.yml (MIT license) +# Automatically generated by OpenAPI Generator (https://openapi-generator.tech) +AllCops: + TargetRubyVersion: 2.4 + # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop + # to ignore them, so only the ones explicitly set in this file are enabled. + DisabledByDefault: true + Exclude: + - '**/templates/**/*' + - '**/vendor/**/*' + - 'actionpack/lib/action_dispatch/journey/parser.rb' + +# Prefer &&/|| over and/or. +Style/AndOr: + Enabled: true + +# Align `when` with `case`. +Layout/CaseIndentation: + Enabled: true + +# Align comments with method definitions. +Layout/CommentIndentation: + Enabled: true + +Layout/ElseAlignment: + Enabled: true + +Layout/EmptyLineAfterMagicComment: + Enabled: true + +# In a regular class definition, no empty lines around the body. +Layout/EmptyLinesAroundClassBody: + Enabled: true + +# In a regular method definition, no empty lines around the body. +Layout/EmptyLinesAroundMethodBody: + Enabled: true + +# In a regular module definition, no empty lines around the body. +Layout/EmptyLinesAroundModuleBody: + Enabled: true + +Layout/FirstArgumentIndentation: + Enabled: true + +# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }. +Style/HashSyntax: + Enabled: false + +# Method definitions after `private` or `protected` isolated calls need one +# extra level of indentation. +Layout/IndentationConsistency: + Enabled: true + EnforcedStyle: indented_internal_methods + +# Two spaces, no tabs (for indentation). +Layout/IndentationWidth: + Enabled: true + +Layout/LeadingCommentSpace: + Enabled: true + +Layout/SpaceAfterColon: + Enabled: true + +Layout/SpaceAfterComma: + Enabled: true + +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: true + +Layout/SpaceAroundKeyword: + Enabled: true + +Layout/SpaceAroundOperators: + Enabled: true + +Layout/SpaceBeforeComma: + Enabled: true + +Layout/SpaceBeforeFirstArg: + Enabled: true + +Style/DefWithParentheses: + Enabled: true + +# Defining a method with parameters needs parentheses. +Style/MethodDefParentheses: + Enabled: true + +Style/FrozenStringLiteralComment: + Enabled: false + EnforcedStyle: always + +# Use `foo {}` not `foo{}`. +Layout/SpaceBeforeBlockBraces: + Enabled: true + +# Use `foo { bar }` not `foo {bar}`. +Layout/SpaceInsideBlockBraces: + Enabled: true + +# Use `{ a: 1 }` not `{a:1}`. +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + +Layout/SpaceInsideParens: + Enabled: true + +# Check quotes usage according to lint rule below. +#Style/StringLiterals: +# Enabled: true +# EnforcedStyle: single_quotes + +# Detect hard tabs, no hard tabs. +Layout/IndentationStyle: + Enabled: true + +# Blank lines should not have any spaces. +Layout/TrailingEmptyLines: + Enabled: true + +# No trailing whitespace. +Layout/TrailingWhitespace: + Enabled: false + +# Use quotes for string literals when they are enough. +Style/RedundantPercentQ: + Enabled: true + +# Align `end` with the matching keyword or starting expression except for +# assignments, where it should be aligned with the LHS. +Layout/EndAlignment: + Enabled: true + EnforcedStyleAlignWith: variable + AutoCorrect: true + +# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg. +Lint/RequireParentheses: + Enabled: true + +Style/RedundantReturn: + Enabled: true + AllowMultipleReturnValues: true + +Style/Semicolon: + Enabled: true + AllowAsExpressionSeparator: true diff --git a/modules/openapi-generator/src/main/resources/crystal/shard.mustache b/modules/openapi-generator/src/main/resources/crystal/shard.mustache new file mode 100644 index 000000000000..89376ca4593b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/shard.mustache @@ -0,0 +1,20 @@ +name: {{{moduleName}}} +version: {{{shardVersion}}} +authors: + - {{{shardAuthors}}} +description: | + - {{{ shardDescription}}} +crystal: ">= 0.35.1" +dependencies: + crest: + github: mamantoha/crest + version: ~> 0.26.0 + +development_dependencies: + kemal: + github: kemalcr/kemal + version: ~>0.27.0 + ameba: + github: crystal-ameba/ameba + +license: {{{shardLicense}}} diff --git a/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache b/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache new file mode 100644 index 000000000000..7e8afff45d6e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache @@ -0,0 +1,25 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +# Dependencies +require "crest" +require "log" + +module {{moduleName}} + Log = ::Log.for("{{moduleName}}") # => Log for {{moduleName}} source + + # Customize default settings for the SDK using block. + # {{moduleName}}.configure do |config| + # config.username = "xxx" + # config.password = "xxx" + # end + # If no block given, return the default Configuration object. + def configure + if block_given? + yield(Configuration.default) + else + Configuration.default + end + end +end + +require "./{{shardName}}/**" diff --git a/modules/openapi-generator/src/main/resources/crystal/spec_helper.mustache b/modules/openapi-generator/src/main/resources/crystal/spec_helper.mustache new file mode 100644 index 000000000000..9facafa4a931 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/spec_helper.mustache @@ -0,0 +1,6 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +# load modules +require "spec" +require "json" +require "../src/{{{shardName}}}" \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/crystal/travis.mustache b/modules/openapi-generator/src/main/resources/crystal/travis.mustache new file mode 100644 index 000000000000..21509cfe82ae --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/travis.mustache @@ -0,0 +1,8 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +language: crystal + +script: + - crystal spec +# uncomment below to check the code format +# - crystal tool format --check diff --git a/modules/openapi-generator/src/main/resources/crystal/version.mustache b/modules/openapi-generator/src/main/resources/crystal/version.mustache new file mode 100644 index 000000000000..9be1d633971f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/crystal/version.mustache @@ -0,0 +1,5 @@ +# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}} + +module {{moduleName}} + VERSION = '{{shardVersion}}' +end diff --git a/samples/client/petstore/crystal/.gitignore b/samples/client/petstore/crystal/.gitignore new file mode 100644 index 000000000000..05a17cb8f0a0 --- /dev/null +++ b/samples/client/petstore/crystal/.gitignore @@ -0,0 +1,39 @@ +# Generated by: https://openapi-generator.tech +# + +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +## Specific to RubyMotion: +.dat* +.repl_history +build/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc diff --git a/samples/client/petstore/crystal/.openapi-generator-ignore b/samples/client/petstore/crystal/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/crystal/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/petstore/crystal/.openapi-generator/FILES b/samples/client/petstore/crystal/.openapi-generator/FILES new file mode 100644 index 000000000000..aa17a1e4b987 --- /dev/null +++ b/samples/client/petstore/crystal/.openapi-generator/FILES @@ -0,0 +1,19 @@ +.gitignore +.travis.yml +README.md +git_push.sh +shard.yml +spec/spec_helper.cr +src/petstore.cr +src/petstore/api/pet_api.cr +src/petstore/api/store_api.cr +src/petstore/api/user_api.cr +src/petstore/api_client.cr +src/petstore/api_error.cr +src/petstore/configuration.cr +src/petstore/models/api_response.cr +src/petstore/models/category.cr +src/petstore/models/order.cr +src/petstore/models/pet.cr +src/petstore/models/tag.cr +src/petstore/models/user.cr diff --git a/samples/client/petstore/crystal/.openapi-generator/VERSION b/samples/client/petstore/crystal/.openapi-generator/VERSION new file mode 100644 index 000000000000..3fa3b389a57d --- /dev/null +++ b/samples/client/petstore/crystal/.openapi-generator/VERSION @@ -0,0 +1 @@ +5.0.1-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore/crystal/.rspec b/samples/client/petstore/crystal/.rspec new file mode 100644 index 000000000000..83e16f804474 --- /dev/null +++ b/samples/client/petstore/crystal/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/samples/client/petstore/crystal/.rubocop.yml b/samples/client/petstore/crystal/.rubocop.yml new file mode 100644 index 000000000000..d32b2b1cdab5 --- /dev/null +++ b/samples/client/petstore/crystal/.rubocop.yml @@ -0,0 +1,148 @@ +# This file is based on https://github.com/rails/rails/blob/master/.rubocop.yml (MIT license) +# Automatically generated by OpenAPI Generator (https://openapi-generator.tech) +AllCops: + TargetRubyVersion: 2.4 + # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop + # to ignore them, so only the ones explicitly set in this file are enabled. + DisabledByDefault: true + Exclude: + - '**/templates/**/*' + - '**/vendor/**/*' + - 'actionpack/lib/action_dispatch/journey/parser.rb' + +# Prefer &&/|| over and/or. +Style/AndOr: + Enabled: true + +# Align `when` with `case`. +Layout/CaseIndentation: + Enabled: true + +# Align comments with method definitions. +Layout/CommentIndentation: + Enabled: true + +Layout/ElseAlignment: + Enabled: true + +Layout/EmptyLineAfterMagicComment: + Enabled: true + +# In a regular class definition, no empty lines around the body. +Layout/EmptyLinesAroundClassBody: + Enabled: true + +# In a regular method definition, no empty lines around the body. +Layout/EmptyLinesAroundMethodBody: + Enabled: true + +# In a regular module definition, no empty lines around the body. +Layout/EmptyLinesAroundModuleBody: + Enabled: true + +Layout/FirstArgumentIndentation: + Enabled: true + +# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }. +Style/HashSyntax: + Enabled: false + +# Method definitions after `private` or `protected` isolated calls need one +# extra level of indentation. +Layout/IndentationConsistency: + Enabled: true + EnforcedStyle: indented_internal_methods + +# Two spaces, no tabs (for indentation). +Layout/IndentationWidth: + Enabled: true + +Layout/LeadingCommentSpace: + Enabled: true + +Layout/SpaceAfterColon: + Enabled: true + +Layout/SpaceAfterComma: + Enabled: true + +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: true + +Layout/SpaceAroundKeyword: + Enabled: true + +Layout/SpaceAroundOperators: + Enabled: true + +Layout/SpaceBeforeComma: + Enabled: true + +Layout/SpaceBeforeFirstArg: + Enabled: true + +Style/DefWithParentheses: + Enabled: true + +# Defining a method with parameters needs parentheses. +Style/MethodDefParentheses: + Enabled: true + +Style/FrozenStringLiteralComment: + Enabled: false + EnforcedStyle: always + +# Use `foo {}` not `foo{}`. +Layout/SpaceBeforeBlockBraces: + Enabled: true + +# Use `foo { bar }` not `foo {bar}`. +Layout/SpaceInsideBlockBraces: + Enabled: true + +# Use `{ a: 1 }` not `{a:1}`. +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + +Layout/SpaceInsideParens: + Enabled: true + +# Check quotes usage according to lint rule below. +#Style/StringLiterals: +# Enabled: true +# EnforcedStyle: single_quotes + +# Detect hard tabs, no hard tabs. +Layout/IndentationStyle: + Enabled: true + +# Blank lines should not have any spaces. +Layout/TrailingEmptyLines: + Enabled: true + +# No trailing whitespace. +Layout/TrailingWhitespace: + Enabled: false + +# Use quotes for string literals when they are enough. +Style/RedundantPercentQ: + Enabled: true + +# Align `end` with the matching keyword or starting expression except for +# assignments, where it should be aligned with the LHS. +Layout/EndAlignment: + Enabled: true + EnforcedStyleAlignWith: variable + AutoCorrect: true + +# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg. +Lint/RequireParentheses: + Enabled: true + +Style/RedundantReturn: + Enabled: true + AllowMultipleReturnValues: true + +Style/Semicolon: + Enabled: true + AllowAsExpressionSeparator: true diff --git a/samples/client/petstore/crystal/.travis.yml b/samples/client/petstore/crystal/.travis.yml new file mode 100644 index 000000000000..c81e9f2a6e3e --- /dev/null +++ b/samples/client/petstore/crystal/.travis.yml @@ -0,0 +1,16 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +language: crystal + +script: + - crystal spec +# uncomment below to check the code format +# - crystal tool format --check diff --git a/samples/client/petstore/crystal/README.md b/samples/client/petstore/crystal/README.md new file mode 100644 index 000000000000..fc7fa03548c5 --- /dev/null +++ b/samples/client/petstore/crystal/README.md @@ -0,0 +1,38 @@ +# petstore + +The Crystsal module for the OpenAPI Petstore + +This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + +This SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: + +- API version: 1.0.0 +- Package version: 1.0.0 +- Build package: org.openapitools.codegen.languages.CrystalClientCodegen + +## Installation + +### Install from Git + +Add the following to shard.yaml + +```yaml +dependencies: + petstore: + github: GIT_USER_ID/GIT_REPO_ID + version: ~> 1.0.0 +``` + +## Development + +Install dependencies + +```shell +shards +``` + +Run the tests: + +```shell +crystal spec +``` diff --git a/samples/client/petstore/crystal/bin/ameba b/samples/client/petstore/crystal/bin/ameba new file mode 100755 index 0000000000000000000000000000000000000000..332b8c750a451f5455b78b2c2e41f80e6e6a8ea3 GIT binary patch literal 7907132 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%0Aaw6c00 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
  • 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�nvQ&-olxZYP*w1~PcjWNhq^*#KGxi)2O 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

    5F9g6}sm%?{Tkhu$lhul^EU#iCT= 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¬dc;07;ws45b!^){F+>LnHgS-qdDiMkOT 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

    Ze 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 z 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%0Deq>xcW28=P}cba%?+Q-=KWH1WI$Rh*B81lnc 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

  • i`#SXn*%v8u9YWOKLXl+7`+G{mMc)bp2OfLyCa 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%0AoRW2zB8v*+S3$TZ3En-+K5PJr#}r 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�jPibx95~RI& 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
    !`P*C)zn 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Π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ℭ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%0D3l21Vfk+1$zNFviw=rLh zkHeSg1qT|wM5F@^-=#m1UX72>Grv!N54~Exfnn)DW4<@$kLVZP%=^!l?*pepzF*bi z^>-S4~wfs2y|3P?Terv7pAGjsvtL57^59{}aAJAV%ua+NY-#>f9_ns2-Pq{Vb ztL6QAVZJx~kbX?BmhWfl_l9q;6Y~$aE#|A`yP5ZfAJhMXUM*kUxbzrktlzuxPmTG< zjAQp8-7HumM_vs9BB9gkq$I`gMR(r#e6kBj_1G6n>N2q|8{z{yzdv@ z8}r*wi}f${dtQGv=htDMR^_lED&Z*oVhUoGFxyf=JpgP8vz zdbRv8^WN}7`rpv2<;%=_SMxWF`J4P9)?Z`Gw=?ez-{$$p(W~VrnfHcoJu~LtOs|$N zGw%)Gqks9Gv3|9@Ki`|_o!;>MXT|(0>DBVB?CY;L{N&l;_xNMXSIZBw|DTR`HGlK) zf1>ZR<&Vks?>+1Lmv`N@UNgPZ8-7TCKfPMMoz3@#FPf-%ek!<*P5;X%A?4@5;X*=5I9N{b$RcpY7j!*8ZldGBifFAV>}2e^N>e3^OgYCg|D zg}$BnZsxt=$2|XDdiA{Xi>+e)yZ)8yXUn&<`QDh{<@smOtLOD&`hU`^<^AuAHPbu2 zXFY!w{%y(U&l^)ud;Sz#$NuXNa{p}2_sGAyY|A`Mx zzkfZ>zbJitL3(_(=KFdv-y7>6(BDR{mj7@6d-$~El?xTJ!7K z_4mg5NA%18BfMJvzg_>f>GAyf@0fbp`p2CAQ_fdwzF+TY*T1%HJb(X2ua@`y!h2)? zb^6u+8T(hux3cT!4d15Uo?b2gSnKc7znNYw-^k{BWB!2tJ@jh%apt|@d)vkJJDXlD zUu539noobL=CkGNnfHbt^8CmDi_hQ8KQr^{jnnz~?>X|}H?d{`wuK4fRk6PaM-%RiHhVRgCPOp}4Wb?h@`}A+1SIf6D?+rhp z{|>!c{=fbIvyao``SpJw^|b3>-yyEw#~$J5BU|&^+4{Y!>&Nr2q3>k=NtySa_4CPc zk1o0X$ETjQ{vPL@nm%6re3H$tXY;+Q`^WRYO5e|Y_UqxgHT>X3as95KSL^yUviaWd zWBNPj)${uLi(~#O|Ka^(%a5KI3qUv158l=MSB2k}e$1BdX6yHcZ|@!c74&NPGV|W> zefq=c)${r>{V`fUTmJdkeD#;o`S^PFxYM_-eu;$hXFVUko6h&or>j#>dp;KX#Pv8h zeWk(kQLXi~v-No6dUWYep;yoA$Mip;SIal>Mmq4SnSSub{Q7>e{!NyO^{a9Ig7or& z^nyd)d)Dn$Dg)tc|0Kh^qUIn{cq>qoyQeU~j?+WGL_@I#(| z61`g9zka}b!;k1Mp;yZfvVSkZ8@_u$T>nS3eztr!TfaAa?ZEI`FB8{KE${1{w*ET( zJL%Q)`hxze^lJIzvh{n;8Q+J)Ymci&Ia#f203L+PT2XR?h!_N~Lt0ibBbXlWx>>A>^_|657VC zBxP{AQYH~f8OoWST5k#sYzNvfep7n&|A3KNkC5DOcNxJQVJ8578U;LvqAMa-$=2|+223tzB%gqN!q_8<%>}d>ibr@VW>XmY3FDARTk$jO^oxC=QqXa$}bR}hVqMwg3;eL{rAxq&+2#S-$P$4zhQYeKhDS>(0|6NQNMUre@y>_ z^u_Z1=het}p4R`Lr{nqg|5M_L{XZo7f81*9KUw+pZ2itye~a^fKwm7sk2`5#gZiD} z*AI>S`{;`?AJ?Dv8@@CAlKz&fNBv^?{^woro#A&6i~RlRi{*RXr2GN>uhJLGFVY$? z!0-z|8ers)=wCx$jQv=DKl}X+=c@iAqyEj;i1l~L^2>A#3^4LbKpJ59L(boez8L$f zd`wOQ48LE|g5NnR^3SI)mcOk2_38hLzIaxDM1PGnWBp?Jh1HAtow5GfyQBVH>5FIe z3;OS(FP7iT@|}_2rhgrMvHW)CJHsE*U$M^hlV|mtomhWKUo5|ynB_aeZyXbTeXUr(Sbj6}o#A)r??hiLf0WhlT-E=9$p0|? z7FoXM&7}vO;rBWJd-TQf`&qtom4961ue^4wzeSec$ky)+zsvbs(HG0FXTCH19{oM& zi{*RXTzb$M{*eBO^u_W!S-vy;=JB!q%jk>c_cGrZ{+Rx~^u_X9+2?cTD*wdD-)5cY zf0Hb~ndLjf?{R*czF2;-LL2}*m%cc|ADk5VefnbgWtQ&@zxJ{4e@0&{zn=Nd@ay#N zr!St>FX%t+;n6>_{6?1VjQl43-RO(ud%bh%L1*|a`p3~1%dg(}QVn4E&Xxah_D?=p zelx4z8GfJhANq(`zgYedchbNH`Ofe=CrAEs=!-FbmiO<*DdE41zF2+(;c0-8?~MEw z{j=$dGS)U`^h(xS6?^wKQQC>J4Kqmlv&t3HSwg+yIt0MYuaCZ zze85Nt7P>$Py4*R(Rz#XcSt-bzsJ1W(thNNmEXwz|8r;bd-%CHUt2vgeDS=W{k~Si z7=G=G;U7U?EWf~=G_b+>c7|V^5&oCxi!mS9+l$i;L%#F0{#}!f=jZR|i6`~1&HRHN z73&u(zo^m|@|{tCm;U?ci{;ld-x+?7{@L`!@;$HWpB)eWfc_8Yi)Zym^zWoEp4G2? zDbC+YkBj{yjbMr2ci8KT7*? z{fd=eWckjhzfXUIB7Cv@cIG?7FV2qjzk$A3zV~}BJ?IR-N&gi3;#vI;{hR2E<$J%8 z?>w#lPu^&8|6iJTQvZj{zbEZS|HaDpek0!*>mSqK_Hp5h<$J&3JHv0C6a7D&zF5BZ z8@@CAF8#027tiVs=>M6%Sibig`Oee&|Ki6l?*G!nllosjH`c%6lIWjU`49H@E1sE- z=dWMQ#FO$H%>N7X#sAm*$8EegfBVG9-#z^qe=YRE0B3~@uuXp&)@Ei1>L0>H2uUGia z)2`p+(((NIIX&^D{&kqY)230sSoz*Bih|*{>F>E2&p%nd_Z#`n@OzwpIeqc0{*eAdHjjL<{HE25d}rhr{aF9r^u_Xf znePmLK>rr{V);epJHsE*-(`!aUo8K_neRO9`nxP0&#%AVB%XBrwZ9tcKmLh4e`Mu9 z*w@c1)A2n27l|k3cbWH&Cq=&a|C)bxI-chb5>LwSGyip4M!s12e!g)1IOF^c>3@R0 zSpG0Of6nlGUyJ^)v{mGb5JtzvV3RwWBRAl7t3#EzBBytn~{G% zeX;yb<~vvQ(|94(Y z)GwCrd6V*o^jq}B@*7#cGxBTSj`~lhFP7iWd}sK5`hTP^mcOj}8y7_WHrvGd#qzyg ztlt^=E&2!37t234tKWIr>)%(>@%;IJVPZW0UzL4+s(&Zyf7!NCzgYQ2mhX)E8}yH& zFP7iVd}sJQ`d^_hmhb(V)W0G9U(y%L_s=Wvou~EhiQ6si-|mSg^{==v`nSfDqkm%M zd%uwHjP;lF_nFjqp4R^h z)A9WJy*=@y{FuL_vGO16`}ghXc%J{zr%ulAF#m$Ie@O}z|6lVTNXPU1`qL)o z_n7~cv>*9m<@@!9{yXFP8PNX?eX)GM-rzgKZ(J1Tf63D$Uo3xF*I$eNp7h1?{dz&Z z^R(;dtLb>&zv~lEx_=L_q1M*qk3 zKSp0H-_I9(XZWp)wS&Q?Zn0Qit?ULx<)oDNS#me{dh4nk5{{{W!pB=tfzMn7n&hWeRccw3v zzpV2&px>b{mha~a`OeeM-_7ZGe*OO`@udFM2hqRd%UHiy`Q9(&J7fI~`UCo6`Q9)1 z&hUHm|4Lshe_8z-(qHd6QNLJzy%qzEd}rj3=3E+1gT#~aJIvqXxsfkczF#j`zcc#ZqyGW= zV)^YjeKjzAXZZE+NB_^FFP6Wo^Hi?PQC(EyA`Ofeg zoc|~KV)@O?cZT1gzx*zI{rMmByPUrveetaMBl_FY7t4RJzkl)kbUc6lzC7`y>wnDr z-!osV{6^M4=j!}j5$A7}7e@aIviy4HJ6GqA^Ead4$b9~H0rT&F&eNX%FHFbt^}jmt zr1h7~`y2Dcs=v(Ycdo7<`s==k*H2czuXipT)ER!4^Pfjwto%Wi?+m|rW%U1z^u_X9 znePn0Pygfe#qt}O?+kxL|3dm=`E}e$1Dm<@#Towis;GZVUyS)n(ruJ(81hFIE&Tb{ z@aph4d2#e#EZ_TGjtqa=_4(N}->=Wl zC!TbD)_)x9+kDqppIG&kS$)pvZ5JvR2zS!J2J3U4R{zQAcwYaxi6_0M-x>LR`VY_-%lF?$MgN?q_3zm) zS=_(5#FP3rVE%8@{w3-7V&yMu{X_a|HTnD?%P+F^J7fK0&fkf?SbjV6o#D5JasJ*- zUo5|s`OfeM^e><>8zF5AmcP>5X48KSJ zRQh81o(JC<{*eBVzF2-UtKS)Zaeee}rI$zl#PaKz?+m{|{~7ef@*A1&48KMH_4LK^ zJ#Q{O=nTJ2|3vy?`49HzBbTM)`Ro7fi6^~&b(#P1S498B%I{?BcSim78>0Vr&=<>Z zXTCH14*eysjC`?t@7JXMb?LvIzF7V^%XhBwe;V~)t9-KjTK4yuoGbt4@XOs;KUw}b zeFnh*qkd=j?O%j{A$_s@MrAMf&hUHm|3zOczmxgS@QYtYertC=f5@}?Jle@JxBn6TQS`;~{q+L%J6HMlg#TydljXOwpN~4jAKw@LL9dJY#qvws zNdp_?JHv1MEBwpoi!pymy6ur}81kK`egC@3>lc6ids^a&pZ}OYPW$owYnQD2dc@D? zJEQ&{=fB|%ynZvknfcCD|2Y3z`s2(mGT#|~{ekG;&PyX-JZt?0{S)bn<+rnZXXJP3 zueuNWN0#5qd}sKLe@FeN(HF~aWxjKjPk+OGnNODA$b4t`1I~XFeX)H1_jBgbgU-{g zzd<^lfB$hu;z`$E@t;`#XZMTx#mcW|^*dMnqkjke23dYD^PS-jIscITBVR1P$b4t` zWBPZ|7t22^^PQ*lZ}$Th_wUfells^GZ}jg`2S)v3<*$f4X<&o%=R7U{Iq7(Q{&r40 zDZf)&tvdE~%oi(vT%|AMJEMQ?<-)&L^^@iM`9}TD@H?E}cw?+zEWe36XVZwh42reFP2}=-hZ6ocUB6& zqxF;Jd%sb?bCv(l@J~`cS^oM~FMRR0(|r7Vy~&@aUrAm% z6Hi+Ii20vO`u>@S^f92{#R$ezfoH`uCLP$ zj{3#Q?;t!4<##Gtcz$%~-$h?6zisnTzw?%f$MiRTbL5NVdmeme`0Z7q{zK`D<&SVD z4Q${$!*8w{{?F-)F(21oE8Q^Uw<=m#f1m!N+flz*eh+ujknddOuO9hb`c1O@M(Fdt zGyD$cUrk>uzs!7R_yhWz9TN46<+n258Gf-w)c;BPV)+f+Ndud?^u-x|bItJYqA$jL z^uJ6u4Ebe63;plXf6`l`ezE+P%}2g7^2hW~q%WS;Z`Py!pVAl0A7uH?$gi&z{v!^J z`o;3g%y+K(M}If^O|tygWWMvX*N;!9<1M9&&E<*l`j@|c^qF_mTY3Fw`SmQ{8TAi1 z|4#a1)$iB)Tzb$M{+Rwt-WK^{`9+rR48Oj1^#3CIV)@=L6s9&sn?-%l&QGbX2Qu<=~Www52_yhWv(-+HcWxg}~Vx8#UPVb2N#q$0A7WF$% z>)$ui@%;L`Eb*lNmCWDsoslnAeg}8bzy|rw)%qVE^?#K9fGpoXkHU9`Utc%;pVJr1 zA0a#qF#Hi94KVyM{jCq@^-IQny#BomchbNHzIgXEAD{2uG2`dC1JZo&XD9Kbel{N& z>v`M}QIA;l3{XrOs%KEq!g}hD3jaX*V)=gkVm;32M?wGF^u@FK4f^-e7tiXK^tXQ( zuP5@Xev|Xhqc5J-Z_(fA$jBGVZ(G07KWFrBNPj>2V)+mD_u;Nc$Mfsw&cu_hpAqv< zJ}T-LE8oxer1L+f{|EZwS^e6hdHudS^2PF-S^u2Tzb5@7=!@k)*#2Fgj_3Vbxih(c zE#_a6_Ag1HV&%88`khh#fc{SJ312LKS^XcKN#W&LFNW#&7> z?{WT5=!@kyGT(XH`^jEMFYf=l6HmH+hs@vVn5bW@{Cbw}jQU6P51=oe)gRNpguYmQ zGs|~Iez`&Pe}nf${bKoE?_7G&8Ge)gJLrq$^S_6ffBko!*8gkL@%;M#ed0;|Z!!Ob z_w)K8E8pv#RDYZEANqmt#k2Yy`tPSNp4IQtzl*+jR=-Dos}Dx~V)+MW=ihl+|KFaD z=l%b9;z|9lKPIl9XCE8+V&(6h+0>>3H7%cP5^+{&J(pf7FK~U#$FPt-neC)%3;k zAME@0Dd~7#|5b@6)!$mSkIyUXi`EWefIJ0rh* zT=akAkFft_`HjqXo_7BG>3H71s}oP^Uz>Sv{b=NimG7@Nc>Q;tmVZ$?p63q}Ps$%M z|KO7%U#$F2wti>yule}s-x>79^8I|HerNat`Zv-S%P+EgXZR!fYkZ97pDcfz{l0-S z{NW~1|337^@;wjrJHsE-znH#QeybJ(4Br`kbJNKGCw;N}G47;+4SZ+#<>ukHKF;fx zjQLB_&97JZ&hXoue-eGM{4yN_0}Q_eqydKCr~ga(V(dqLE8Q@_@LPa1!0_u^ME%eB zMAR?F{#pDs{S)Ym<@mOsdRXZYo|k^h8G zMgPR|%glGK^6BqQzeASa%6w<|BhLR6eX;yMX1?>Z_um`S@s`rX=Dx&u|J^hD^T^?L zvHo*E9qSh>znD2H?J98TqxRME#f27tiXq=x_Yl$QR3RWckj>@6z9wzF5Bhe$!lf z(0SVRcWyeKUw@Y;o^<_W=|9{ZR1 zo(JD~+WOy+j_2!tbK*(sZ!_;9pO1X8>hEOhcSikV`rFVK%O7OEGyMKjqkjj`7t7x< z^PQ)y|D<$0U;h^qPg;NNX_3F@{26r8TA+RkD)J?@8>H&fAAahzfNB)-+$f% z-+9{lf1i%$>wnnklh@y4{>5qkl5~8r@;ll3ovZafJ^J_XFN9x^<+n568Ge`ZUrt{v zznS^Y@JIB|qA#BJaVHII(7&cJ{Khk){*}KN^@}lWNxF3(lD?z?hVKl2@a*u9qc4`< z$^O2EGyHO=@E>_bUV};+a>%9 z>5Ju;X$l4yehEkeT;wc7Wce*jPD6gHq6NRl`Mc5=%Wv9zpohdetT{0{wt=!@kKZ9aVGDu1_F|F4x# zmhay$!*_!0(q&wnd?ZSm*7rzD>A`M20F z^8b|fV)@O?cZT1ge?v3&o2doDfbJnj1XMLM2;|MBa@ldiu$^WSuS)Gt=PuNV2wsK0(-tp8;CV)?#a z_|EW4`q$AH%P+G2Im2(!{}+9+{Ceg)!*A1n)VE{(V)?zycZOeoW32!A^u_YqnePn0 zLw`^DV)@<7cdqi^6!}LfpDf?==F)@CRX*o`T=|*b&+?t&Hx7#YEiQ=uiIv~Vd}sJw z`p=^;mS1MRGyEa_x6>EP_q@6EpmSCK!BPLEs-G;s`rw~x0K<2#{5OaH@b7T_Wcg)M zFyxmNEqwpb=KKbIvHXV3N4_)id-R`2Uo7AA;5)+~(cg!@SbjUpcZT0=$NCSaFP7iR zKL0qw?;jfen7&wkFUxnX^4}W%x)(5G-`^&;OH`OWu-|5Ey5 z`Hif9&hR_*52r7d-^zSv_#^r!&==3@mmi4wds;tPelyE=Mt+y`zd>Itzs!7R_(S?X zq%W2~$XGENpH{Jn8$X*2kkC?-|7T5UZXJkD>KA zqn{)CL;7O*gUok^-}^-5Z}Pp!7t8nS5B+e4Kc@dW`r=vr^5n=rlfGDfnbq%%{0{wl z>5Jw2=P%Un48MCyzMhihdmeme_(RVBD1EW~9`2-p4SZ+#?N3GiFX@XhAJ2z1 z(hWnt^R(~hA93m8>u=k{ldiuG^M950B{0{x6{ebHy%Wq`9GyFd1A5C98t3RUuWBOwG>t^}R z)6V~6f4KPkZz(Y-x>ax^Iu6{EPsGI zX<###zBpI?|7_I%5&C_y{AOh@_|EWapX2)Ji{*R0$ajX{rN7#bc>R;*ms!3u{2}LW zMPDqxk@?P5{k^FF74&Ol`R&YihTrD=56~CO@8V7x*vzFb&Q<MMb0dE{`eNl5xRVAp$ajX{J}>5DNR`6K>=>GGYc{BMWNv?fS3%c=7fBgv1lC zf9Btk_T&Adm(}0S@|{tC{eoEk_CE<3#qztE?+m}VIP!<|#qw9coiwnSOJAI)U4L6%yZHLsE%Bu5uW?D_uYO&u zU#xszFY=vHe@VYhUo5{_RliWbGyE?7pU@Y}_vah(ou{pTyXzOP|Iox(|7)_}$L=$K z^BZFQV&!+T`khgKV-WrO4t=qF|NTYO?>w#kb$`0J{(TZps=vql4Q`D3#me`;zl?n6 zY56}+$Mg5^>l07P?=yezo7g|H^1WZkcdq*Pz3AVu^c!UP{_|7#&hX1i*?;=tc^`Mu zz^3wz;WsY}|9bjjOvCF(EqncNuKXW{zx>Uy{w7&|nf-Z;GyLuq;qOLYEWeK$(g34= zXZYon;h#-kEZ<)*;X7CPS8@HyC(HM|iTPKDf4}m{@}HWm-+9{g_pF~SzW#PiJn8!D zTod`Xr~OM(s95>+s(TClbFS7;fBm0_-zCrL_c;IA^u@FKefn>uFP_yO&_9j7SpH7g z`kkls|Ci}_{`|iy@udFOhtdD{-V*&2E5DQFJ6HXu|4sT`viw2jJHu~Y8~J1UV)@O? zcZT1izrn3hzgWIsFLUWZXZU^kP5NT_WtQ&@e@y>9^u_W$Z&H5kx>)~N^u_X9S-vy! zTl8u+-YkiJ-cGkgDZhTpp}*1ztE&p)#KZkF#1zj0Ie`_dQ7?_|C+{676F=!@kK za3>9H=F%5u`0bxX{tmZA{bJ0oK0ojuG?lM^;1pc>zX|`B^2zecG!H|5Sy6CRKj$C$ z>&WkCeqr-bzcc(H=bu4ej6Bru&lmX4@XOny{yXT4<$KhID&n7&xPuNS^E{Mv70{a>app4D&DzmvXLewiXLz{oEFX@HU6r~mZZ zdH%`RkMrlB_uxBM_1_Woe~f;OEZ_SD-x+?L^KYUrmcL>4{1!n zuV?!up7eUwxHHzX;qRg!V%1Zoo?w8no)VA-80%@%Z_^iJf7Q=yJ^Aa|;E$32bNXWW zU4*3pM!qxhn|FmjcSqDOmfwM#hWt)N3-u4_|CGL1ejj(zknfEA&YvUyjlYk4v3!61 zLjBH_e{cB3o%G4_ON6HZhF=2G0K*?}{4pJ@UjWho!ynOK?GKSJ#(wzi zbi)9{cZT1*FY13SeX;xkavJiBiWc(Q^gmBuJgeWOe+zxF{JQ0#erMF*qrbr)WBp?J zoy>QJU)&$-e*=B7{1HEZfRXPEzqaz~Rc}_mEAqwihuQao&hUq;hJO-$v3y@I@}1$2 z=>ML+SpHhs^VfOW=cyO{Y4PjN0f{HQ{?yip`q%k0*H2b{FSQ5*jQV?kG{C68$N9U` z7h^xJ-$A-zfZ-1SX@KFk){Oi!>5H)+zV{2hGyFFF8|jPXKiGd?>T!Qr+`ktlp47h% z^OqY(|K^$XchVLE^`QO^APq3;uhnDyN6;5z7uN5uckrFz_ty&lCi-IeMLGrs7=8gr z0}Ovi|J8qu`o-9f{4(7z!0<~z8esVKwIlx<^u^c@zeqO>@TB|({Wbm;`C|Ev%3koD zk>8@fl)hMgEAyS<59r@QUo5|w{ryVkYWMLSHPupFMw^kw4ft{HNR(^^4_~C_W7^@=HJ(VEDBsgnuM` zF?OMUt#rcx!*2o70K;$5zlOdT`{57L4Fe2+07wH2zt|-5*S|mN7h^yCGTku1@Jm1% z;3}W~{`6a9`7KONLw>8G1%JT#7tj~WAKQH7J0ripY1IF)e?|Rb`DNxi!ynT>h`w09 zU+>6whF{w}@^7Lqp7*osr)~_t{-p3<`9Rb!mS1GQPvH!|u}%2DqA!+zNVb0GX`laI z`|rh{UrtUu>GMmU`E&na|H;a)XZg;kf57?I(ih8bXTEc_{%vFZd;K@^8)W&OHIn_0dy{4V`1YHL+L&lSrrGT#|~pZ-_ri{%e8-x+>;yI6l~xyTpGZ)LtS z{2~3j=!@kyGT#|~?a7h9Z)x z{Ab&IZ_5UIr&!2z)Njz!&P3E7sO4Kh_zCYj4e`nO+qQBz9!WYZ;^9|n_{*eAY z^u_XvZ2ivg$Mo-_FP_z}KP&qG{8giV@vMG>{x9f@XZ4%(-?|$6PoC9pbN;{Ri)Zz_ z^t-D^zIax@Pk*B|!WYl#59yylUp%WnroZi)kuRRruRlA^|2OE1XZ0KOcdAFecvinj z|7QB)S^YNsL)VIY@vMHA{sZ*Iv-*AdN36~BPoC8ua{eRN312*`Kc;^+eetY*z2xin z!y{ijtKXo14Sn&fev^Ld5s@#R)o;`P7k%-pewY4n>qfqKR=-bw#r47$&*~59e}TSu zR)0)?>qkbucvipuoH+mAr7xbUZg{yMFlM zS^Yl!3+anz^@sF#+aU7Av-)HDx6v2R>eqLS^S}3FB40eK-=Kd7eetY*lm5XQM!tAf zzfFIw$A&MS)$h_jmA-gZzfXU&BJ#zv`a}BPpf8@)AJc#CMv*U`)vrG{&i@bTi)Zy4 z^k4M2$QRG*H|gIU&do784 zvGN<)&%d1U`Mv*w=>I3^i{*E+&;QQw8@q%*q%W55-!EYO&hVS`SJ*h}7tiXq>2FV8 zEWiHHSO6IL&dBdHqy9J17t8NwzBByBtHQs4zF5A0zgFc(IrBgNE$OfPgjm0LR=-LA zmGs54`Yrljq%WS;Z`1!feX)E$-&nsh`q!m@)F!chvHWHAuSfsK^u@FKefsNf8u{W` z{Q>=h=!<9dhx9L@FP_yO(O+$|s9!v*Kc>GIeetY*ZI8JA&!;b*)vwcEZS$yKJgZ;O ze;s}CtbT+3x%9=e`X&A4w}|@1v-(Z?FQYG>)o;=NB7O0!ew+S%^u@FK9s0XHG1f1h z)$h_jgT8oHzeoS?^u@FKefrONQq(V=)gRFB(ihL_59$AkzF5A$UQT-b9MgZ^mQlZW zR=@u0c>aEkzIax@LH}`EMZS1eze)di`r=vrHvNCn7tiW<>F?i&`o**QefrnZ7tiVs z=|6qz$QRG*kLjO5Up%W{-!smCeVfP^&+0emA3 z*e>c9D}SeG-~6BdJ5S5+q~m%1ClgP~?=$~dPmX-C@|X4bYe4@L`eONwtbS+oe@y>2 z`eONy&wS@;>)+rhi`Tzd;z{c--WmPBIPG7OLdDA87{6<#4GyEa_edvqjmznPj zzkYP&e}leQekb#t;dkk;`Shq?JgZ+nCh~WoFP_!!(mzM}WcjVEerMD_=KMd>7t0@H zzBBy(`=b6OJH-0M^2^M3uJY;cO@B<5-^zSv_|5l6{;BlE@;z@ZJ?IR-OaEu|#q)ml z{!tpkA08k1Pk9EPzhwDEEe06*&hW>ae-eE$=HvCFmp%WStNK42`71n=*B@DaI~@Z9 zjC^PKwG+dC6@9UMf4;$YhTow7A^KwZMT)=x!!H17fZ;dk|B${I`>}q{o0Q+7|1bJt z`Q3C33^06WE>&Zxil@mT-<^u_YaZ2ivgNAypnFP88BpCs~~r+xnTc{<)wW@Gc;#FIXM3_lU| zfAcx9ezEfFJO&u~&Q<;N*WNMw5n2AS&R_lH$Ul(2SbmY^J0rhA|4RB|`Cjj&`b+u` zeQwk*mfz0uosr+Ce-M4~tbTDytp7Lk#qwKOzBBSW^mln4uV1qKW%a+$`6tj9%lF?m zME{(pU4K7K$Mfs&uZbsJfAvqs`aiu>)Gt>4vd&*Y|7Y~Y^8I|FerNQrOMl1bN4{9T z|NBAkou{q;)O0*w|CbVD{d?vAo)Gh&zH{XF$;w~W`5STmp7h1?{qr{JcgFgAr$+ye zr!SVjto$MUYv_yRckS~I@|}_2IxX@yenG5XEWgOEe`oj|`mdxfp4IQsKasvz{wT|L zuJTWh`bWwq%lCd`{m$^qGs53>msr18ekaR!hCiU+qc4{KNnWsEp^|aSl@{L-{_0w7inD>VE6?f z4KV!Lg^~Z*7e&4pyOyL|E8Q@_@LPa1!0=o2zeZn-{qXDQh5?4}48KkPPWs|m{SN(g zUL5s{z1O;Z5myOX*_s&cu_RKPB^5n~VCz%5P`+ z&N%;l`cI-Smfy*IXZVeaqJMkS7t8nS9p}#(eo6lr)lZi1uZQrR;nxO{e=~it{9abS zGyER?N4_NbCzfAkzBBw0{g=@f%dalnbPO=+cdq>JMg7OiC(AFgpHDl(ul*?eU(pxK z_w$W>XZUUUt2Sf(V);dizyQN90BL~Xcj>>Az8L%Q`Y}#73^06W_?;`F{vXg6%fIxI zl|Ju_|CHwA{psEr-;Zi9UHp7nl6cbdsrl=u=ccqD??+v6_f4 z-hcGN8GiYj$bSoc@vMHI{;l-I^1IpZt2kHre~kQnUmo?BWcfw5erNbC&cBVmSibiQ z>vx7fqQChoB3~@ONaq{_4Br`kZ5;Lgh`v~U19BSjo#8j=ulCBw7t8O4KJPojAJBg% zeX;z0<~zf0|268rpT1bW=gp-Do#EI17XG2TMg3y=<1F79{+Rx6>5JtzGT#|~@%PBz zZ1>0)%P%tD8GcFs5c*>Io;Q~sbcWxi|4sU0`33Hzfen0T_}za*{ad~&>K9`^UeEmJ zbMT$v_x>6Ff%L`ly8-K{=eyq$vlX06B<%kN~qGyFdNW9WmmOsvZUgiwHws!a<`eONB@1*)W z>xBQT*GIls{y3}O8Tsw?!v7Y1vHT+Qo#A&M9sV=l5cy*HZQMx%8`SR%zqUd6r_dK; z7yAG9biS{=PT-WuGUX~=Y6<-@~nQ3 z^G~8Lmfz+Yz*YZ@kzapI^l!a=BVR1PY4hPb!*9|55Ph-yBJ-W$cj&LWANxm^KgfJ% z_~nLC|9SMq@>`kj48KQzoBbnSEWeTY&hUrykD)J??|(irmmYMUcKzL&j_23kpAt{{ z{5JvJvV3RwZTdIR7t8m&N%=kcf1@v+)gRK|>W$GqvHTKu z(!d7!&ei%KAM1Y&{W^KxuWa*;;Ws(|boyfXO`DH=XZSt(*U}fu?`6I-{BlXu{{Vfl z{08o%ferGV;kP#qf1@`=|HPP&*Z(f=q=5~5XZW>E!hZ>UF?PZC|1Sc*GyM8y;U7s~ zEdPjf3=A-Q=V{OXv(oYW`TzaIlb-)w<}W=c)-P6my-HumcSij^`X8b%p4IQu|1o{B z{36SDM*fig-Soxs*Uo(BY5iOO;KlvhI`O3bjhR18`|id$MgO#cj*78|I9x*?Z@>uAS>V3J8AumEu;QR=!@kKa3>9H;5%de!B-X7~O$@1&##{s}|>5DV`=0~`G`eOMF{)1!kovZp! z4gV|jJ7oE7+(|>eGyL&q!oQWiSbmxL&hQ(b3xCUZ#QMeZhY>%Y?+icvgVoi(Bj}6e zzaDqez-BIe5f9S*>i+{j^%>PZ?4R~}d|jIFugA5+8t~0HYr*`d>aWd@=STe}Fq_U<2P7e({CK-|XGYCu2T* zfBwLChTrD=b30sr=3kTjJjZ!j|DXEa#r;1xF+Pvv{V&gq`Zqp0^2MsZOl!aZqy7?* z1{n2s=pRL2jQv=DJ>4+C@SWjz>0d)%EWZUg4f(B#7V^jRH#;Wk7t22|dp~iW*1wOW z<1J+tHm4+>)W6o*(Z40{i+r*2OCCe@J7fJl`a9DX%lGRA{d0ytrhgcHv3$>i?+m|j zPSpQV`eONhzTi8&3tG0o$o~b|D`Whek=2x;g9I={DC-s;#vLj!pMIIeX)H1{%tNj z=sfND-%rQ$=l>w_r00K!`LFt5)Gt)PF2}v3&10>UW0UrGE*1vHa@9 zrx-AN=gPk*^8X{BEWbz!hWw(Uh1cIc=WlattY0j@XY-NojQr-sk-tBEvHW`GJHv0$ z|15p6{6^+GSL>&L4gD@zek=2x;g320Z}i3Ti_CY1U%w>QUq3GTCzjvLeCKNY^mn1( zBFpE?*ZlbtACczc^IUJn&vPG2^Zohog~WJ1yej`Z$Nc9XAM0sn^>niJI9L7T{Nw45 zGk=iz&hYz#=;zJ!#p;LmZ!SIP48Qih@K-+}>KDr|vdHmtpSbmZD&hUryxBhU{ zFP_yO(|;#@v3&pczvj||&eQsTK{}rI|GLDJ`d_;&&i`9aV{u&!d38~I}8`}xNDozee-{;%na<@dAtovZUl|1BTk`ODVd%zWo6{|C{(+vyi% zagRZoig#me`3(LZO@Kc@d8`r=vr@`thh!|03Ue=1wQ z^R)F}l#b`u&y|TMt$)P)*M2P4FIIj#%Xh~5o0muZ$I=(eFEig6ewY4*^u_Y)nePn0 zNB#qwL3?+m~8qo{xVkH`AO^2^M3hF{X(i@sQXEAySJ`ssg+evdq>U%Mje|EcOH z%Wr1+&d6_a{zEle%KWWF=}A^mOXi{<i5r!bLm0nY4snO zj_38iH}RzUJIr76Q&GQI`3Gh7J5S5sDjm=BcT7Aff6V;fFkh_v2JWPR4c70B{uRSG z|7(3Z>K9}Fl5}g~P8!(2cZT1&HvATSG3LYXq#K6(PDKmf57({>|BLj+@>?r}Am17J ztsBF?mA+Vh9e2`@?+m|vQ}`R78tWI!FS4H}JHzk(Jp4oGi{-bf^o4w9_+$F#&=<=e zWWF=}{wO4)ldJg^v7iRW#&7>Z~Qs(H~n1HFP7iTd}sI_`mdocmOsdzf6nmBzeoQ4 z^u_X9+20p*hTpp{{B|$u7t3$rP8!(Er7zC#$M=VS5q&Y{UG9?2b}*}`eONw%y))A zrhhJdvHTA1q=C&``r-_~ojyTe-yQVDcnsHDC*3f>m0wYCl}~^DFU0!CWcmL71m78c zZTZN5Gkvjq{eT7x-?{SF4F5d&WckDF{mvQwaP9Ed_+r#Amfy?%{Kpx7d&BUbOJ6L% zlS;+_qy7#c4KVyM{ln;su^;^(r5gqq{s@o;82<3Fk^f!QPsV=ugLK0H!yf?B0K@MV z;jev0tY3`%@W<(f0fz4kzq3*JyVDoX``OP+$Hws6OTs^nzF7VMchbNH`OfegPYC}) z`eMvS{cX#G?+m}UY54!4FP7g)$G`x??*P&O!!I@qf158w|HRmj{9d|YfZ_K5X@KDm z=)a%782jNja3>9H;5)+~Zyx!V(-&ht{6V^5$RAX+kl)`T{QKyO)eX;y{<~zgh(7%_ySbi__o#7W-Mg1@S zGS5F*eiwJrz-BIeafaV-gnt5kG3Mj^`M+lY-+9{iTi;8^Tbg{0pC!io*B;rQ*9@8e z-m{{9vGO}S02ukssK2>&)PEU$vHW)CJHsE)zmL9H{xI{M;di%*{AZsX^^4_??DH+^ zcZT2IA^b1W7t1fQeCH~k{>tYtpDe$V`Ofg`&xrh;=!@l#SBL|EQNJ_%+H=CcguYmQ zBlP*}&l!G${yp@?@_U)@41Y-f#pm++Aze!&#zn|qh!*4z}{B_TZ ze6f6gy+pn<{Lb^jZ_^jg`&oX&7=EArne@f-3!9I8XZS<EP_dNK{@M}9o{TqBG z)-RUd!ksj*f$t2z_5ARUq%X#Ny#Ca2Ck<@iJHv199R9iV#h4G@>xJ(Ozej(qe$+3P z-%iKC0K<2NUw=X5&(RmlFCnKPzpN-2ev|$w^u_WEn~!{F_-*>XrZ1N7>xJ(Ozej)5 zuX6q5S^XjBA4*>=zmvUwI3vHgYxM6d`eONIwti>$UHZ4t7t3#DzBBwW{l|YT)-RSn z$b4t`{kf?Bwe-dE%glGK^68(de6svj<~zf0z9jO0L0>F?ocYf1%Vzi+em&MNmfz0K zpL4bTSA>5g{XSWKFZ+3g^R&+ozfZ?o%G$B1e`E6JhvuGjQU&j zH~(h%;#vJR{R8QX<+rnZXXKCQ-$-99zm@sU)%kl(tbe<2@%)kH7n$!|t)KHhMZcB# z$b4t`_5EV~chDEh_q@6Epfmh7{q-)4{)^>zvwUaxwf!Ui9rVTW>zVId)la`i zzfP84X1+80Cg)#CUo5}KeCKNY^jG;V*PrD#Gv68hfb%z_FIIj(^PQ{w1EPN~Q9fDz zDD$1G^&iOo({GaHmznPjzsvdGqA!+T&wOY2J^FXi7t1d)-x>aZ{>B$Y|HblKnePn0 zcw?-85Bg&HW#&7>Z_z)VzF5AmcP>5X48KkP`}D=~yu#k2Yy`p=;+ zp4IQs-;cg{R)0YM!}P_o`Xl=1(ihL_*WME6|0?=o`Hk%SIalYO{sZ(IWck$x&vXnh z&YyGT9~$|O{eGOk23dZQ6b$)AMZxf!oc~<S0LYc+Vf}sbUgq5_nnC+J%8KG zf8?c6zgYQgs~7pssDDKNh4jVp%glF%U%WN?cMN^8d_P~vcZT1je*t~5{5I~Sfen0T z_=C4a{(tFZ{^u_Y~HXpuowSM|{&>xWHmznPjzjk=!Z~cQi;`^G3Mj^kJ1f8{-~mb^WT40_?!PQ)-RUd!JRbZJ0rhwRQUVT z7t60_@1M@_%lCx;S^8r6`ukE~tY3UnnvcIfHJb7FN&b-LFRfM*U;dqV((6f6^;{nP z5UZX#kD+>;v7Q$Fo9K(>H#6TEexLrTKjQfy%P%tD8UB#-ccw3v-^zSv_>K3*`8bxo zSbjV6o#FTBUqxRm|Mbjvp7wsQ>J^L6-y;%FJby<={b!~9_&n7lE5DoNJEQ&)=ifwM zEWgNnXZW>aB7dzbBVR1v^XAfn&hWeRUqD|h-}B%*!|&7o1bwmmGJ8FDhChCP)c<|@ zV)@OqL<}(Uo#B@s2>&+vV)_33hwlu(d0hDGT*dQ8mfy(!JlPq3=cD0oLti}aSJf}n z-!O*Xr@tS4v3x&YsNWfWc~azml)hNL=fQV|-=_Z~`eONhzTi8Jju7``+7@{Gv8o4#1S_Y1x={5Jh3 z52Jpu{9=VFem>tBe)p`%|1^EEe9xPdKcIgjeX;y;#LwqDBfo!kq@-K<{f2Vx1{6_ZwyEwyd{xJLluIKenmS4-Bf6kTvqwsH$ zPnJK#oiwmP{mxbWSBAgS4eTFTzW@J!@SWlJuL=Jn^u_YW+3yoL!|&Y~{(bbt@&~I_ z<FnuxR zih|*H>3@U1Sbkyik?#z@Pk*_eNB#4hyqVTd|Aq8NI^K+a{mxkbDfGpvzq4X2 z0F3&btM&gm{Ojm<$@0t4|G)hka{g+!#QMeZ$C>Yp{J~!$ze!&#znl5a@SEfCKS5tC zzsRnCXZY>Eh5sA+V)>2i{lgi4@80lVaBHkzEWe(;e>lVMFITU6djWm1{1SK4z-BIe zafUx$KK$YrkuS!4Tz{Q(!;s&pXyN*AKP3DXeX;yD?xZ2#8Tq3X!oP;TSiXPWME%b2 zYpaC+s9#3?V)-M4rvZjP0;B=1^69^t{)jBUUd>+co#Br;|8w-k@>j{8KjPcceEdB1 zff;|E`j0e!DQm&zvA>%9{YY)i=*Nv|KYku3Ry|%n`r(ZA)agHa6uww~n>B#pJHzkL zKb^i#@Fe_!rX`%Wv3xUT!{WBQNzUHD@8-Y@vh)%qV1>)(lf zjV!+%%b#DrGyFE^A5C8@zm@sU@Q3ujPG2m)k@?P5{p&{kzf=8W`JK#nhTq}*_umoy z6U#3%-x>aZ{@3V>-{66Xz%Wr4-&hR_*UqfF! zt3RgyQTk%}W%lz-XXH25kNU5sFP1;b>UXZzzd`uR-O2TflZWSTJ@cL67o7hT`eNn( zG^<~{f0~c?<41mGwtw&Yh&10{4?mN5((6HbL#du?ulPf(N343h{<-v^bJY*}r_isH z<(Jv#Q)l?i$3^}+e~f&w{9SP;4Q!C_Jni-HUFmqf{*NS{wEo`XBY*q5B44b0zkZPK zjQU4U2>;#m#qt|f^$Ybo!>?}={x|50<##jR8GeucL;l42$@1%&?+m}s`8&}U%O7Uf zqci-@7E%BE=!@ld(lv_#p2K`&__ap(x6v2N?`7YgIK%HfCH!6f%=MGy`};5Qo#BtS z5C2Q_#qz!1@SWjzo*Mq`^u_W^+(`o)_|EVLPYeHre~J3Vn7<_5TIq%%zg1B%{Nm~1 zA4^{>e_->G?+kxL|NHdC^2^M3uJU(?{0Edzmfy;JXZRh?fBHDqFP7iQd}sKzXGH!9 z^u_YKxRVApbLopS{O&Wuzn;Dr^U?nz-7vuL3qTrR_SE(pZ*T?#h4F& zl>L0(8GiTqQU59Q#qyggAbla<8UE;n;ji-ds9!999Qu6y&hUpX3V$#9V)>2CcZOek zarhslFP2|qzBBxS{#EqF@;z@ZJ?IR-Nx$}wSie|)9e2{e2EH@=)~-?ip7h0-kMrmA zC;9EU@IOdjEZ^&e?~ME&{om0S%WtO|Fu?Gg;n!ah`NcnD{bKo^Hz~iQzdwDk{91PX zIU~P(dE}oepDe#b@o9jOUjotq!|!nZkiHoE(f>cD8wMD@^R&-DYuvr~^Ut=4Cw=~D zzar}YL)wq;2gS;7R_P1*&ZvJtf5&^m7t1d)-x>ah{%Q2Z^6Q!J41Y}jzx2iO+nMhS zzxB#k|33Fd{bKo^HK=|j;Z;<78v-+Lk z4>5DNR{qxs9_|EW~^v|X* zmS3b}V1VHlfHc7H+w`xcFUEf4chU_548H?N0}Ovm|33O+?1w+dexBeAzkgD!f79io z|6=)#bPNnI@}1$2=zVHizeWH1^u_W^+(`o))b9+x z_wmTTo4y$H(Lb*jzBBy#C&GWyLwNm?6E5 zS$>)M&hVR@e?EP&{08o%flXDvG5pRck-ymrv3@b8p?|G(!;s&qC>Z{T{!8hL<+p7< z@}1!~KNzcc(X{ln;s zu=U>3ClMnTaRW-~K}M zZ^u<4U#$ER;b|zptZ3o+(5JtYzF59rKd9fiT0i|y(jSrKcPtOSGyK*UqyFpZi{&?Q zCk<@iJHzjv5&pUli}i~!e@VI(>4pJ@UjWho!ynRrA$>9S!(TPsFu?Ggr=7pU)A9WL zosf9a`5Q5R^Hq8Mkde~|gk@SA7G`fsH#mfy^LXZSt(4_`g%7t8NuzBByt%aPxr zFP2}&oiwnSOJAI;`p*jg+w_}c`6c8u_1t47kARY2EH@=#(ClIMPH2hc>ea%4MTpfqJ{nq z>7PqqEWd5@k?)NB@+*=5SNdZ4W#&7>@6dl{J=QOle_ZA}Pka42DIL$Bzvm^M`26ih z{kyLf`E|1L>sh`t>MuC|H2Pxs-mgjh>(alQzF2;B;!+J@_|C}hel_ZU>Ds*h$?}V& zV8|~jS~&lG&i@>JvHX_JN4_)i>tBof-_jS)>bL1XZJnrJEWeTEJ0rhG|4{m3`Fmx) z^R(;d>*;uY{al-P()CmO`u`*CUf^b{=k||}ltYq92SQoZE}a`Wl|z;ePBBfCPO8CH zQEE~u2@`qbl%Z5q3`ugxAcSOiIyj|CNg+n*Kw(6pNBn=+`d-)G*Z#fcx$ouquh;9| z_x+h!@A>}LnwfRqd$+4N{sM=jC;R&W-dKOki>sMocT)!Ht;+N&# zyYk-+|5tgod|Bz`-Wz_v{m*z5&tK+?%zML+xc_DJYR&Iu-W$I7?^ypsA06}6@; zr{jlYmnfHb-xc`gk)$+dHdF!9ipF^*f_w~YiV}9?4vHmOR z)$+bxcyIVV{rc<0@vG%$+3|Z<^M4fc52f$1zL|M%_zwO3^lEuO zU+r|GH~fVDN$bb)tL1yyeD7-gKaTYuLtnDxeZ83P4d3DZ*V3!y%WS?k{D6L|$H)5B z@;-0g{2~2u^lJHjHs2fbi>u@KKTEHc_w{1FH++}=p+&4;UDprkm(r{2`YHXT^lJHT zBQ`YF?~V1BlQ{nMH;DDC>-r)63G{0DGR8O1^UmLt{ulJ>y1uw3*1!FRv3_-3-=#m7 zUR~D@>HkfymhWW8?~U_6qks8Ev3|AuV&|{*lQ{k_)2r+H9{n0iV!m45*Nf-R8|xp_ zA4{*U>zmic`fsLJ*Y#cc=WQJ8SJ(9;`U~mR@{67S8U0$D@cCoc^{wmT_>ZJl%lER^ zk2lW$kp60Vwfti1pVDvrgjm12t}lKX>pz`dUDtQ%@1j@N^+Wm>Z5r!W*Y#8SFVU;( z`r`UH{xvs?`Rcm9OMeu-x~?D6Ur(>D>!si{t+oy}GXN(%(z3mhWb- zfA8x0L%+usvHmezezDho3gGy}GU+)9GVcxF{AJ94IlWrGm3eRYlKxzJwS19z@9OyJucz;_<-3{pu8yDkZ@4Xw z|3CD_O>z8((W~pu@6&&cUM=6p7iq(%cKXE|>u>xz=Kr2vjk}hlk8b+FCf}_px;lUK zPkK@uf0r%awEZ#P8@|W=kEU14|6l+4>DSWZ`TJ**dfxk|&-uqcIo7Y%{9d+xZ>)bt ze<{6MzRbKgd}|u#f7PeNe6_rvuX*RMq<=QOx~}ige~4Z!-_6$VjrEV{|3I&n_v6L- zz2Rr{FL-Jkzgphs!F$7ZeiP^KBlPOJenfu{y;^>;^FO9%De{Z~g#`NdVtK}D)KcW8%y;^>;<8S>oj(^vu^Z8@TKP6kg_d-8EoRJ>SuYX@k zJ@5M0Mx&Qw3YWWQ^@4e9L_u}+;{`$QkHD15_W&eIlkMrO6Y`%W8_4l*+-dO*P`~QYs zt@ZaZ?+xF*JZ>)bxzxmFwezkm=d2jgQ z?wH@BSIc)Z?+xFl{|3EU-siQ`jo$EM`VDr8^{bcp?B5S6t>GJgkM*BNua+;;F4)kR zUm((khA-$J^W2!P#{KyH(=dDgc~|SdFXn%Ve$1Bl*DL0G!?*q?{QA#}`D*!AS_3vT zyf=K8{y=)Qd;y#``J$@j$KR*_B)wYR=gpfxqQ8}1EkCe%@ZMN|`Oi51{hlAkuaQvy;}2&2gU)QtMyxB{gVf* zQJtF`>DBUO@c;GvHCGD1?yj+Zwfrpe-k9HbVE9+jtK}D)U($b%UcJoQ^$N$IU%&bf ziTS&}i07Xz@7Ei6Z>+y~SooXi)$;Gkj^BHspWpU+@rpmcy)*T^pWjBDze79Luh#r# zHs2fTAJc!CUM=6syf^%eexu!DzFNM>yf=JvwK#ug(W~Wsy>0Aoj|X4S|CU}Y-?RPU zz2OJ+uk6J7)pdR2;j#XI(yQgW*?e!z@6sQ?d(2nMw=(Ztt)Kq;^aHlM|NHPbe(!}| zf9tbibJ ze=@zgt{>A+=+$-ognr#U;`r6_MRxq&IQ|*^i|Ezze!lShd&4$FF*dUajjq`#S7 zUDp@%TkRRgua<9QKmU1S{lyxw{;oV*-j8?Q@wd4Dh4kvWzNG&*y}GXN(C_%NIDU0q z-=%*iy}GXN(NE~rb$y?HjlE+1YWe@`KY!RGJ)Zyh#hX&kyZ#I~f7`udzFPAad;W*? zC(x_qd)fQX8?V3SBjfeA+CDK~E${DFy#Bo5Tl52ZwY<-R_l6(RueWc^SIhgndGp8g zXVI(WecrtJtu^ELH{UPjtL4|p&Y$-}-+w-r9?zfuZ>OI3{C7D26)%tZYRzwE^SyEY zy7V8TSIf6D?+rhozn)$#-^si;{FHvJ{bT)V`3*Dgz0mpFJ3XGCze7{cJAci!`1(B{ z=BqV-n9cXb`bYH7p;yZ{Gw%&Qrti_K<%`UFSLcs@NIzxEcQfw|U#=a;KcQF4`+D2y zMsN5I{h9~z^~bL3yWIZ(dUajjqyHSex~}ij-$k#k>j(5(cjNfg@}2DbdsojN{bBS| zw!FXIasIs*di{^mogy^^oL|8V_X?tcuuTJwk5`n|FK#-rl=eUV-*zu5Yl^mo#$>-vIz!&k-e ztLyp}{lWC=y1vx$)2rqC+5dmS8|QCa#QD3GUM;`a`J2#hyEKkpUDr?P-$t*l>u2^?$Mfs&A5zb~{&N2N502w+vNgZR z=6hGqKldNgw=zG>yf^%W``=5i*8IiJ-<19dhs65Tb^VOKORui$8ym&*cRIbgu5Z%c zK(DUr3;MNQ6UVQXZ)NA-8|S}6zYD!uezEi4rT+lEx~}ihPwCZleV=}V*T(Uy>-qux zQhK$#pRabh(HrM)On(8rT7Kv3{Ch9-{=FkTp1*(ZO+EMhyChzJryR=nFI)2)_#$oi zg!Ox4{mqTTUn|eXu1nI#@$02u(kAb{(E1;H*oy0KrJlF`!H%4t?q8CIs5O7F*WZx- z(XR`ymM^Q-FJHemj(Upo9iRSlWzFPAq+0TF8SpV=Par}GJtK}Da{f+3)p;yayvg7xz&i_ke{;%m9Z2AA| zpWm%>u2dvD<1#xsplR4;k(@bQ}k+if4$)Nz2QgnchIZlo7sGC_%VI)hB$t;ynnyIeDBI16vw}} zJX_w^3-1lzJUIME=+*MRUU+Z#g8nD;YWdkpaRSir-qrlq#{6}U;p>+z@Bck#c<+T? zf8F$We*JoL>UpofKId(6Y|K|{eluIYH`YHmEY^QIy;}ZP+4Jwc(E4vrkLT-u`f>Bu z-+Num|8u$@*AKPk`}2kK=Z*FE=?{D(kDo2S2fj!fKEZo0wEnBoDBT+58fMo zNPiZ+TE1+=hKBcspVD7Sua@uNi?rbryf=L7^>O@v)bX>i|C03K`_J?Jqr$I$0$)Gu zx_)wW_%^*-zLnO14UPHUSbyUU;g6+P%a>^vZ1QDQ(eNex`Sj|#zEA%v9Y0&XZ}YHz z?`r+W#QN8KbDX~hTfUolZ}=|ve=WUQzRbKg{E+@^dUahtrT-4Sx~?ydjpM(KUM*kX zi?rbr&Yw5dUmh2Jy|={qQ)7R;e#`WMO}?yZ`SahWe?Gmst{>AMMX#3cXWu`(tM$Jr z)_<ygwS3R!VZJwfkABy;#`#yvcQWq{ zKc+v8UM=6vyf=K&kM-X|ua+Oo{aC*m`{VVKep^s&^4{>B)54!f zua<8GUpC(xzE6K4y;{D=yf^%i{$_f$yr1uOy3recM&CR!j$bYB>xK7*FFq8<|1^4a zUEiTUh+Zx4$BX&inBSv6n_ey7ZN!F#_lBR)|A<~KKMcO?`SY&kpB~4*`rG*WXUmtF z_l6&F|L4-H<^6nP{oe3X`XlJo@;(pV8@@Or*8g#OwS2b`8yemlzEA&idbNBReA)T) zh9A>EtCua@_DSid)XhyG}KwR|_5?+rhq{}jDi-sjDmKcoL8 zy}GV%eK?N)5hum@Q_K5$G2a{OAJFeeua<8%Vnf4w!w>0u^lEv3zTmy#XY{AhtLysm zBXRuSrdP}Nv-Nvp{+NE{ckuPcmM=5!4c|B`=C|q9@~zB!!?)<)NUyHzyYv^*tL1&Y z?R29z<`3zAO|P!&r}S(6SDZh!ydN*-dt-j{>^Ofr)2rp1+4=K^FX)e_SIc)Y?+xFh zzk*&Z@8=8a_l6(Q-%GESZ)Nkn;ivQ)zLV#VUDr3yiSzeddUai2(jQN+uIqdBpQcyW z^+Wn!(W~qF3H|CP$N5v&^^K46^+&IkFS6&)8_!=s-=|m0`|Ay_A8+^`{jccN^380% z_dQqTMImp$-FmwV;J*aMz5A1trQP{#`?Xh`JV{?9?fUVm%*1Ezc>7h`~Nq+TE3fk zZ}`r+G5@dhYWYFtz2T?yTfB$Y54QZqnfG4k^|NPsyrs1G^orE;UO&xG#`^!p`D)EC z@I~72shxiD#`;_5h2QYKv3@o7$M3&d=>wa*H++|VM|!pV3}2Y{hVPso^ADj{%U>FN z*?jMX&fm%D@%;RqntJZ}`*h5I!u#U*n{3VZ^ELPUT@e0-^ewi$pD%dt>ilv4L+QK! zp&xMnchjru&L7i%j$U2Y&**QaSIhVCMcVKQ$M0Po|7YX;J@}M3|6R7cpRal6Z@~Sx zpjXQ`tLe+<$MdI_A7$PfzW74;ZRyqWt;~DFcj)(~SIZZf_pa7Y zf27vWmM=5!4L{=k@1|GF_cHHY9sekf|LZz_wtO@5-taB%{}Xz({5bR8@H6`R>DBVx z%zMLkz8LG@bP&(KT7H;$?`l5%KJ+uT{05o#Ug-M&mh^aj{eN5PdDs8qOR@fke<0Sc z*8B;+NE<%2(=XoD`oA20OZpjGz64I2d|6dAeCwj{&!tz(w`_mR_lED%A3(2`Z)87z zc~}0cG5;iaw)`-g?+xGhTKEg-)$;y&#rnPBoAlqLSJ(9g{dM$ec|Tv6?~VB#`rp&5 z<$b;L*59Rn#0UBPi!JZ-=FJ~)|4r!Cb^VzBS@df8e)jx%l-ou3G{0DX*S;*^PArczZbn)ewcZ0_!0dv^lJG@=Dp$jm&E*!(yQzG&bPu}POp}) zF1%?C=r(@A2R~tc=hE;GKaJnN*x0T5{ZDrNY*|<9ABTTAeV;Aw^XAR(e<%EV^mu;! zA5T5+_r>A={{9bQ{zE^)=Z~%V{(i;l*L$J)Pfd^K^Pio1 z-uyA=UB~(A|KIul%;v9p*8KU6AI9;Yo9DBW6%zMMP zu8sAdN3WJ2W!@WpN`F7ST7H^&Z}{jq|6LFEj5A-}q_xW9Zd&eMx@~y;{DL z&G*Lq0sXh>)$+dHcDm6UenLN`SJ(B;>*M(UO|O<;lC9r+q3iEsKDOfZcZ1aPuD>Pc zkJA0P{;4&e|N6(WpC7yzntxL^|JMIyeuwjq8OD6||L^=yq{s8`KVL~b@A$i%zsJX8 zzFPBp+4=Xbo`3oi=_hRYR_49o8$XNZ|1x^D{3P?<)qMKm6S4jYTfWS^H+=Jkn14LI zx~}igUrMi*A7%5stK+{h=C5~dtiQpQA7|bhe$4&fNUxS}X5PCxfArs=pJemP%zIbo z@8_}pl|LElZ?fyoFS!46>DBT+ubpo6#{4e*C+XF7{gD3O^y<2PO25x}ar|oeZX-4{ z*6)q=m%oVf_jP)8T|cCM)Td&;TE2|&%dTJEm_MaIhF)FQ7r%`4Uq`R5>$~(joFD5~ z*Y!jCkI<{-JK6Dj-+S7 zqF2lN@nXI==1=JNydc)EuIr0m#qp2n)pdQ3e&f%?e05zvrhhNJTHeq1yz}4qb*%qx zdUai2((m@!Sif4nmtG>+(3tOy^FN~hJiS`J1WucLSyeRrjDEGx#e8*L-wEM+pjX%RBl^uhAM01w^)vbby}GV%{U(n8&-CiLzDK|Ng?#<8>-rJ*zldI4*U#wJ z`2wH+|8Rcm<~aU0)2r*w@6rF1UM=6X*DGHC-gx~^=(iok`qlDf=Dp#Yx5WC-q*vGV z9s2v|)ph-Ve!nlq`qg#)g#IFWbzR^5Z5;oaUyAway1qkyIK8^AAJAV-udeGS^xJ-! z&p*4aZ{8Zme+s?2uJ6#_O|P!&2lOwyh|fQ}j$i2ak6%iU=f8jZY3g~uf17aLyT8Ko zm(A~I?;mfxe>%6t`Ma53t@Zo&E4+Wb;m7nFel_N+<^BB%?+xF$J?6iiUM=6k7iq&M zcyIXD9pUe!S7U$tSwZ)K>6f(0d&3Xz41dVi`24Zu%SHbCj<{}{bmet<91hEMJEi#L4pFEM}pug82f_Q&~a;)}H56TCNk z@z?N&)2p#R{2+Z`llO)n(ceR_mhV=2x%aNte^1Qc@f%z}TYj8*Z}wPoUudeGS^e54)<$Di`1)$rQXN~#g{jvUsT@v%v@ua+M-Vnf4w!xs+>e-*u2zLj}z_zr#Z+dO`@e35x?_#XG)i(W17 z^RRwz_#ypA=+*M$Y`!;qcjY+#HO4%DYP9-Wz_#{U3CB%vZ}7nfHcot`_Tm8@*cI*W1Sa z_IU6G{Z;g8`JU|$?+rho-|Y&XKeqfZ^WN~ShsXN=M6Z_bX5PD+Pk-cBPhua@`!Uq?IL=+QK&zclr{*I%)E9RI4{kM*lH-_JMZdsoL#e;ECM zEnlt_Cjbra4L@nd{JZGY^4-jP!?)H9fA|k#{c8CGGVkr@@1k`dzQXU9?oN-ll+PVL z{VVmn^EX&K=6~pieE!**e+nN1jpJ8;FYUj{%8ka-bDmlK{DJ!yTF=kZ{=S|+rp9{q z&;EOA#X7N`!H@WSvbCOmwjOVs&ms4}l3p$EuU{OGH~fTtjjLk5x~^|LD%QU@y;^>d zt=}8-2lQvqtL2N#d&7_DC-iFhZsxt=n~#q5Z}#Ijezp88^WN}{b;G}wUM=tQ+UZ7b z_%8j0^lJHDHs2e5LVpXrTHe=-`QGs3$He+KzdDXzEnjByz2O^=4gWrRwR|)4-tbNO zU(>7Q2buSVAJT6yiS?`HTlgYv_|#6nc*9Q~7wg}jUXA^iq>owpz$WhvKVC2Vx%6uJ zQSfEn8@{)G_&e#<^4-jPSL>%=at+_VYzT4?WZ}@Vf@PDLN%a_@FZ}>j_PS?fptLyqP{io^G^8S2d z{oa^Aq5mhnTE58E?+rhrf8$SM{c8Df=Dp!ZOXB=}gDNMHOs=BwrVnfHb-wu<=&(W~W$+3yFu;m6yA z|0lg#zLm}QhM&lb|evE|Fmd&9R{G5?eFYWaTVz2QgnkN;)NSJ(BeC&v5_(yQhD z_0~=|dSm{O{$KQJc|Tw9-tZIpXWta-SIZaK@q5G1=ue_o%lo`}^NVfc_%Ekd%MY^o z-k3k4-~LxT|7>~x^GD40h95sE=D(g^Ex*|MXY_Z_tL1yy`n@r~`{bCv^{-?7YWXtr z-ta^E6Y16RMdrP$_7CyQ{woKrB`cyk^On3H`ZTn7yfJX zYI#3jZR~I71HMmxFTGm6Yx~1{!%yjV{7oFcT7H~)Z}{Q%vHo|`tL4kgdsp-6uhM+B zd^hvn@a5BE{-bV=^{eG)nfHcoJtO=p=+*M$?E3EwKYLdApU|u2JL%7Cu%X-O7w>BQ zJBHuw7OtNyUjnC1zN{)5zR&%?O0Qn#7y14>eqPLf`fp>tT7H-vzc=Q0U&!O9SIZA< zy*Pev_!<54Z;knCc|TrwZ}`@(G5_=Q>ScbBUpofDd+zt-M=I~zFPB} z+3|a0{WJP4{t#X*-^;u=eD`H>{=QDHmM=2zU7bJr?f=NhC#=x3St zdGOxw&Anp%JO3%>tJ7W8pa1#1dGq`9*U_uxecrtJL;AgEF<&je*!Oy?0B8uPukPF>QUPOp}qRC>AhhHvf@ z$G_QM`24ZuUz&OEg^vI8>GAx}4}O+<-tqT1@07pBe6{BHv-#dw|BU`0^lJHW=Dp!Z z`^NDfc2CS#%Xc#G4d2)={6+L?`DW(5tMgC)5BersewcahYW>`Q&%bf~neS)b8@~1O zSpQA*YRxY*?+xFh-{)RFe{A_~=Dp!3-2VskYWe*#@4e9LZ=Z23;+z2O@N#QHx-ua++|?+stj zH~uH)tLyrb{vdj_d@GypjrsWhU}N74=+$-okp3_9YWZ$9-@7`02gdxJ{u#&LV9Wc@ z8|`$X_d>6~ucgQH>)$o0=e_>AoOiI{lmiN~i=6hp)@v1of=dBd$SIhhJ z4et%#qCbOPEkDZs{L34DdQi+?`++fEE#FV)0UH|g`-rrm;kyTiKZIV5`*Hpz_#$oi z1n&*sdQJHI=+)RC-p?1jcQv2>=_~W~%a$+GV_-wWmx#2X;fLJ+WAtj=kNLgyfej7s z4c~litbdbLV!pbr@6(@0ua@s(c-qjI-$kSijrmjhM?8qn9~<}AIe*PVWBt3)tL2OI zIM~pbUm((k#{3rjsq|{xUuS-o{#tsqd_O%7HZ`_ zf5!bE_wev)`CjI|;k$2)`DfCr<$WH`pErDueoC*FFS7aG@B{kx>OBAKx_-p{FQZq> zce453nBO=)j(@*L#C)}UH}l@`UHYHVtL0mn_pXkge&Z&OpDpjdUumZsy%&1@y)iwW zzy5})=e_=BoVV8+eEnqeo7wuktMm7!IDbR>23zazWZoOT!~HjZWXxB~ua z^mu;!pHDsS_`96{k~L$#TJu}@B5nAD^Y4xI4^D{V|0=y2`{VoPM(G2ayn2@Q$KR(u zy5`@fUTLirkLU5J=N-?O>$xJ`za$M+YdyVK&a(A*<9J$cj^k;q9bPT}xXgPm^nAQM zJ)S=w=cS&v{toBAY#qKH*qZPEJx@Fz-dO+et+D>k(W~XlZ2jKwQ~JNstK|oo_lEEF zWBwkGiuJ4I`B`ka=(T{)b}zd+F8k{mgsAH%<@#4|=t{ zUoYF~MsN7h8R6eh#QN3pe!YbEuKdTtueSj`TfR&L8ydbuqzw%}nK}z5fU2#rjWQ66;rMzQ5lw-y6q2JwNDBUOcK*EKi%*B&bmN$>miP0G z`QGp&`aZo{zMswahHqUE^Y5fr%lrEs^S$Axp9%l!O=A6Od4IpddsqIV@T)z6o-JQy z>-UB)z7qZm^lJGc^WN|!{V(a&^1aM^!;k1U*fiF!miKw>bfY)?gnmzYbzMKBKapN7 z-^|wUjronQ#`(L5UR~EW>DSsUj$bW5&gOep$4`Ge{e&&=>&5x=hM#i(o9NZ@J`dg- ze)zRG{^xEU>sQPBym|9S^q;0z%lo`}^LrP^{N@%hUoF4b{677W^lJIV=9gcO`8Uw3 z;4{!Jp{RZ2_ z`qlEo%zMLk#xeg)dbNBvJAUtK{&&N#-{O3>eDmS40CYS3;tfB(I{Xjm)$(QVWykLg z-_H+*qp_)F;3 z@}taq!%yfR_taRwT7H;$Z}{HNWB&W;)$-l!_`R$7zYM>|(>R|k@7IfVy3rec`a2#! zy;{D^*6$5p{674vwu|{{`6Bb)@Fo3e^lJHD=Dp!Z^f%F~<$Yc|-RKQJpz~{me$Qv} z`De?Q+4=W|Z~i6xN9on_gUoxw59u4viur2!e&)U5i@(PFH_)r){r9WwbfY)?V zpjXTL?^ofyEC28C=k37r$CfX%^?SoN)8AvmzAt`uc(r_yd2jd@{afkP^1aM^!w>1d zPp_8udF^zgH~g4>r83s9uIs1tPoh`LH?#G7WB!c(P1{qxARiuz&yDq~<@=fUhVRp# zLa&w|EA@JmLFt4&w0bo(m%(+zBkdU<$d0~`Hc<3 zZ~8*Me%bOHWb?fj`g!ie^mt2g;M~5DdfxlLwIt>rv@6d)Tl4+>j`e%v`1|S4;j!;^ z^lJH%^U?6$@MHQ-UljAz@`KEK!*|l3qhsG8y;{DXd2jd${T45d`D%Ip`3>v$hVO3{ z^Dn1Y%lrEs-n;VY&jE1r3GJBQXUmuAK5S_C5|K7E{FwW1vm4J}<_qkcHu<8e<$lWj z52IIO|2psgPWp2O?E4*hwY<;6`n|FK9{nnvSiiciAJ9LCUM;`a`)^2p61`g9UvF5y zH`d=ue~y8D@1a-Am-r%W_yq4=9e?_B2;A4%oyX6{Z8-mnt-sIx&!<<*>*ohF=6f%6 z{aEcKD_%dgOg-=VF-(6BfXDttx*xwERO2?R->(;#?~UW1rQgS5->qI6UM*jy`>>(m zOGMhx@U8UwP<(w9y&AV+zCT~^-tc4ktLfG9{qz{v(C~dk+R*T2`h6(A-eHeezZ$n; zzF#lkz2Rr+_kq~=5_+|~UoYUjE1!ODz|F7Tljn~uU#9!8q2WtJ+R*T=^nDv&ullm^ zYTSnP7wH2V8oofJ4GrI+-=1EL`{8@(0~;FN8-7gRr&r4tz-f~&s#-pOO8-@QbzR>` z-?y>vZ|T+YO`C`Hdt?1g`gQk;^QV^YX1^cthVP~CTiExF^lEvZH*bEQ{=4*Qd4Ipd zdoT3E;Aj{`_!%q$j|Caq?zFNMl^m6YF-|U88^78O%`6Bb) z@Gbg1=+*MQ%zMKR>CdKD%lo``y3recOn)uCx~`wnue5(0zgoVTt=}8-XY||AtK|oo z_lEDkBF_KO^lJHD=Dn-=^q<#!w)_BJqz#|i=@;*6{;OjCV-JYq@3ZA6m0s?>;X6yi zA4;#5Z)Dd$@5&z>{@e0w`4V5G4WF=nZ}`C>;nz;@16+U9*dOn|Zub83hM&DQ{0a1G z`PPHegQN|O`QGrw>%-qcua<9Cdb#(8Z_#g?UPoBJTK?#4{oV^*zu%o6Zz*Tv(|M`q zUB5?1$ND?zb%Xh8&G(KefF7yaw-G`IEwb zTAnRmCV~wOUn0_mhA+AQ^V9nf^VPT?>o3v=HZ**JNE;fyOaFR$HSUM+r4MXqcyIU# z{YCU@`2sj?@{AziBzhV8}Sbssk1HD?lTDYy>yYl}P z^FJ)lmiKw{=C`>2P4sGc|9K7Gd!hILHV3Wv{@*?Iy!U_SAH*8k6QEn{f_zG zIR4?g!|zS6mM`u3hWCb_(hup?@`KEK!}s13^Bd{=0M@UT?`Pf{enx*3y;|Pi?^wS# z{P2A-{~mg^yuaV!y(@o4_~C0}{X@2VnXTU&e#-rym%fkT_|@`7=Dpz?XU6>F=+*MQ z%zMN4=)Xg+miKw>bfY)?fc_u!>bibJzuBR2{A&4TwtjESAJcc~)$%32NE<$3{oe46 zkHq=AoL-H)aQ*ao@ZRuE`sQJ=ezm;6-{HL%djG#BJ>JqZU)!mv=e_@1XUF7pzHvBTzijzo=Dp$jAB*`tdbRu@^WN~yVfeq$ ztK~=8@q5FMKN0>NN5uNo^0VysZ{F~u3&U^M3$K&NuJqgTr}v-Nvp{)B$h^m90_pKAF6 zU!)D6s`XpL&qi_n`t)k-hWCFrd;NIBcfK6{PI|SxuNU*Z;k)!Z92M(V%lq#a;Jp{R zetaxFo?kzHoO<5%WBQd?|2xv}BdYH|Y|Z!U1?GEW{hf=$-$SpKFVi{1hK4T@X+y&g z=wJATn6JkDIRC@+fej5mM5GN3-}-vYzl>gu`{4)a0~;FN8-7f``!O+Jz07CN|G*l) zb4kqq1HD>)l3hQ%;fwEuKQH}02IR0(l80%Np^)31%=+*K~ zn}_v#WBn!lm+95=o$Sv)yx|8wiuG@lejkbFPc2{Ii?rbr=6l1Bt_t6$S7U#ie_t=W zH~g6X270x;UoYUj7rK7z@TL{79|xzNcl{_QvHtZ>h~rmlzF#je-y7>6{UrR`=+*LN zItSR$@FgN`X!sfZHS}uSkMlQ7AK1|FLqyup@Plh({_i?vTi)l*n?L0KpQcyK`}-Zco4x+Mas2%|!@uP1;nnhGcK*HL$MomYtL2B8 z_lED@74z3le~yLqtK|oo_lBR*e}rBw-^h;NyYhdG`CF$y$HM#>Ti*Y9Q9Iq}4d1yh z{LS=g`7&F-H~fJ9gZ~xt)$&E=z2QgnKcZL5_cHGdU;Iz3fBkpHe6_sKYh!+z_(X-{tZ2jKwQ|^B=y;{D=yf=KK8S@`|O3YWw_cHGd z-=jZ_UM=tQ+UZ7b_yPUr=+$-oi2k?qYWZfies9bl({J?tIDWN!C-dI$%{AiuT|}>z zA7t+zZ}{Pw;kO&``De@fdfS+1>wzC}|M$?V<^9h);k_5Se%zHF£|3F+n_R0SK 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 z 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�S(z3;W9(V9ZCH(N^tSlH>Y(T1qcu_^D9Wvw$ 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

    D1| z%$Z(7(LY;21<7&0xFMc%aSL}%;9zQjZ~f2UJ7m!EMV2p**Ki1PwPU~RHt_M^ub|rT z3w(y{oIPj~I=O%j>3=E3PiN5NZkaD2^`30tMC{puSK^#4NPW<4Qb3Q@u2?KlF6E3M zhknHuzz@2LF`=a(u)2l&?hfVRCM(VW=4P|l7w}>#G&__G)p`s6gfE-jZjUIsfsES* z0s{hmfvWIO3E!F70BHCE0CNbzas)U99QY^rHyj_Aeo1fEn~eSdOE@^9dbq)U2I_Fn z76nMP*e+H@o^RGAq6|cZ{&ln5eEBH_;r(=B`3+uqg?5NOut_i`d$}nYgB;GYmxtqW zxdZqqaCQyxf`CUFAx!44KpuL^q2dFf0qsyOvOAzt0)AcWzLDA+`PU-91{OgL4K0vc zUFTa!PHaFjA+3?1^RfgnTwlY;<*QPL#bIoARNv&Ux4W+vjWk+V`(CI2T&$2rW8Fhk z$o;TTP!m$Ir+-?bD=My)9z=zAU|J~3$6FB00rvTbGJ=FurE-2I6gfW=nw*~@4o5!3g?sG(Jn?_N^nZp$g%9@( ze`b{F#@PIw2*UiGNB{-_nI-xeqA@-~uzj80(85OeeSw#Ncs2^7;c2s2XP{Vc1&beG z$X9JBsXyE94(pYGE{n|4!VUu*>c$6xQ)faapR8cXCr!f$P2`iNUGgVQv*b@itH8p3 zOA5F}hK0{EjgF6mO2rQ8iBM~?<^DE= zJbxQPrN0dkE7fSk3ZrKUMGUH|OAa6n9#L@-;vYo>#eaw42Uv#jQGX+BaO-?~$09Go zH2d4Q;K9){$6e_DUA6M z#xVT?)&v4X6fVg9ScHJAO!`I9!SYz{QHV)7LmpP{39Etq;@hrJ<3ge?#?LrhVt`QU zONQk5I55^IQ}d^b{>W%{(Vv;WL5aA&JQ*1^a`JD8Y#hkdgz%(Dev zEut9yE2?@hG70F1_3iN%twn34<~g2ne{9TevLjJFM!7vs|QK&laEXB78nij|Ez>|F^^D>wiP01_yKCt9B_` zfiJd)8!$%4ErJB*_=r~fc>!kfHi0q*SoFi5A}r-#{RxQ#D9nRY9tsxGU!X7y>?WAO z)p3i~dKd_ND{pp3$OynXZv$a8q1Q~OI2r<~Eu>c9D@m5XDsqdKfQL1pzfM6y*t%lG zdss2bblJXSYp7e=6(Cq7HjpgH0^L_je;_FymXRTe9Bh#BiXZ(A@Irxji2B%9#k zU-AJZ$v{uQjgY~|z6A&BE5uuSso!b#qv!sp5|H&iwqXz zz$|=6eIUkM>~{rC6B!{Q{sHB&NXST(AM!1?FeEJY;y;#qNS}R8)*C!nEJtt)6BJJw zG)Dj{S|bzB{L^Y(fQnEz4e3M>nfw##2ZCn-%^E)AAE@}n-|4i)KO~!j{GI)iHsPra bMbSu1h^F6$tbWcGkV+%z4W^A}=zsqo%3Xv~ literal 0 HcmV?d00001 diff --git a/samples/client/petstore/crystal/bin/ameba.cr b/samples/client/petstore/crystal/bin/ameba.cr new file mode 100644 index 000000000000..d61d0ff11a5c --- /dev/null +++ b/samples/client/petstore/crystal/bin/ameba.cr @@ -0,0 +1,7 @@ +# Require ameba cli which starts the inspection. +require "ameba/cli" + +# Require ameba extensions here which are added as project dependencies. +# Example: +# +# require "ameba-performance" diff --git a/samples/client/petstore/crystal/git_push.sh b/samples/client/petstore/crystal/git_push.sh new file mode 100644 index 000000000000..ced3be2b0c7b --- /dev/null +++ b/samples/client/petstore/crystal/git_push.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/samples/client/petstore/crystal/lib/.shards.info b/samples/client/petstore/crystal/lib/.shards.info new file mode 100644 index 000000000000..d6e1324434fa --- /dev/null +++ b/samples/client/petstore/crystal/lib/.shards.info @@ -0,0 +1,27 @@ +--- +version: 1.0 +shards: + http-client-digest_auth: + git: https://github.com/mamantoha/http-client-digest_auth.git + version: 0.4.0 + http_proxy: + git: https://github.com/mamantoha/http_proxy.git + version: 0.7.2 + crest: + git: https://github.com/mamantoha/crest.git + version: 0.26.1 + radix: + git: https://github.com/luislavena/radix.git + version: 0.3.9 + kilt: + git: https://github.com/jeromegn/kilt.git + version: 0.4.0 + exception_page: + git: https://github.com/crystal-loot/exception_page.git + version: 0.1.4 + kemal: + git: https://github.com/kemalcr/kemal.git + version: 0.27.0 + ameba: + git: https://github.com/crystal-ameba/ameba.git + version: 0.13.3 diff --git a/samples/client/petstore/crystal/lib/ameba/.dockerignore b/samples/client/petstore/crystal/lib/ameba/.dockerignore new file mode 100644 index 000000000000..69e9d6b588e3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/.dockerignore @@ -0,0 +1,4 @@ +.* +!Makefile +!shard.yml +!src \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/ameba/.editorconfig b/samples/client/petstore/crystal/lib/ameba/.editorconfig new file mode 100644 index 000000000000..8f0c87a10c53 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/.editorconfig @@ -0,0 +1,7 @@ +[*.cr] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml b/samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml new file mode 100644 index 000000000000..a1a56190039e --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml @@ -0,0 +1,9 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: veelenga +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +custom: https://liberapay.com/crystal-ameba diff --git a/samples/client/petstore/crystal/lib/ameba/.gitignore b/samples/client/petstore/crystal/lib/ameba/.gitignore new file mode 100644 index 000000000000..dddc937e8824 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/.gitignore @@ -0,0 +1,11 @@ +/doc/ +/lib/ +/bin/ameba +/bin/ameba.dwarf +/.shards/ + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock + +*.html \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/ameba/.travis.yml b/samples/client/petstore/crystal/lib/ameba/.travis.yml new file mode 100644 index 000000000000..5de00985bee5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/.travis.yml @@ -0,0 +1,28 @@ +language: crystal + +crystal: + - latest + - nightly + +jobs: + allow_failures: + - crystal: nightly + +install: + - shards install + +script: + - make test + - crystal tool format --check + - sed -i -e 's:<.*>::g' README.md + - crystal docs + +deploy: + provider: pages + github_token: $GITHUB_TOKEN + project_name: ameba + skip_cleanup: true + on: + branch: master + local_dir: docs + verbose: true diff --git a/samples/client/petstore/crystal/lib/ameba/Dockerfile b/samples/client/petstore/crystal/lib/ameba/Dockerfile new file mode 100644 index 000000000000..cb4480788cef --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.12 as builder +RUN apk add --update crystal shards openssl-dev yaml-dev musl-dev make +RUN mkdir /ameba +WORKDIR /ameba +COPY . /ameba/ +RUN make clean && make + +FROM alpine:3.12 +RUN apk add --update openssl yaml pcre gc libevent libgcc +RUN mkdir /src +WORKDIR /src +COPY --from=builder /ameba/bin/ameba /usr/bin/ +ENTRYPOINT [ "/usr/bin/ameba" ] diff --git a/samples/client/petstore/crystal/lib/ameba/LICENSE b/samples/client/petstore/crystal/lib/ameba/LICENSE new file mode 100644 index 000000000000..3970ca6f4df2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-2020 Vitalii Elenhaupt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/ameba/Makefile b/samples/client/petstore/crystal/lib/ameba/Makefile new file mode 100644 index 000000000000..e0c861151071 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/Makefile @@ -0,0 +1,21 @@ +CRYSTAL_BIN ?= $(shell which crystal) +SHARDS_BIN ?= $(shell which shards) +PREFIX ?= /usr/local +SHARD_BIN ?= ../../bin + +build: bin/ameba +bin/ameba: + $(SHARDS_BIN) build $(CRFLAGS) +clean: + rm -f ./bin/ameba ./bin/ameba.dwarf +install: build + mkdir -p $(PREFIX)/bin + cp ./bin/ameba $(PREFIX)/bin +bin: build + mkdir -p $(SHARD_BIN) + cp ./bin/ameba $(SHARD_BIN) +run_file: + cp -n ./bin/ameba.cr $(SHARD_BIN) || true +test: build + $(CRYSTAL_BIN) spec + ./bin/ameba --all diff --git a/samples/client/petstore/crystal/lib/ameba/README.md b/samples/client/petstore/crystal/lib/ameba/README.md new file mode 100644 index 000000000000..422776df548d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/README.md @@ -0,0 +1,249 @@ +

    + +

    Ameba

    +

    Code style linter for Crystal

    +

    + + (a single-celled animal that catches food and moves about by extending fingerlike projections of protoplasm) + +

    +

    + + + + +

    +

    + +- [About](#about) +- [Usage](#usage) + * [Run in parallel](#run-in-parallel) +- [Installation](#installation) + * [As a project dependency:](#as-a-project-dependency) + * [OS X](#os-x) + * [Docker](#docker) + * [From sources](#from-sources) +- [Configuration](#configuration) + * [Sources](#sources) + * [Rules](#rules) + * [Explain issues](#explain-issues) + * [Inline disabling](#inline-disabling) +- [Editors & integrations](#editors--integrations) +- [Credits & inspirations](#credits--inspirations) +- [Contributors](#contributors) + +## About + +Ameba is a static code analysis tool for the Crystal language. +It enforces a consistent [Crystal code style](https://crystal-lang.org/docs/conventions/coding_style.html), +also catches code smells and wrong code constructions. + +See also [Roadmap](https://github.com/crystal-ameba/ameba/wiki). + +## Usage + +Run `ameba` binary within your project directory to catch code issues: + +```sh +$ ameba +Inspecting 107 files. + +...............F.....................F.................................................................... + +src/ameba/formatter/flycheck_formatter.cr:4:33 +[W] Lint/UnusedArgument: Unused argument `location` +> source.issues.each do |e, location| + ^ + +src/ameba/formatter/base_formatter.cr:12:7 +[W] Lint/UselessAssign: Useless assignment to variable `s` +> return s += issues.size + ^ + +Finished in 542.64 milliseconds + +129 inspected, 2 failures. + +``` + +### Run in parallel + +Starting from 0.31.0 Crystal [supports parallelism](https://crystal-lang.org/2019/09/06/parallelism-in-crystal.html). +It allows to run linting in parallel too. +In order to take advantage of this feature you need to build ameba with preview_mt support: + +```sh +$ crystal build src/cli.cr -Dpreview_mt -o bin/ameba +$ make install +``` + +Some quick benchmark results measured while running Ameba on Crystal repo: + +```sh +$ CRYSTAL_WORKERS=1 ameba #=> 29.11 seconds +$ CRYSTAL_WORKERS=2 ameba #=> 19.49 seconds +$ CRYSTAL_WORKERS=4 ameba #=> 13.48 seconds +$ CRYSTAL_WORKERS=8 ameba #=> 10.14 seconds +``` + +## Installation + +### As a project dependency: + +Add this to your application's `shard.yml`: + +```yaml +development_dependencies: + ameba: + github: crystal-ameba/ameba + version: ~> 0.13.0 +``` + +Build `bin/ameba` binary within your project directory while running `shards install`. + +You may also want to use it on [Travis](travis-ci.org): + +```yaml +# .travis.yml +language: crystal +install: + - shards install +script: + - crystal spec + - crystal bin/ameba.cr +``` + +Using this config Ameba will inspect files just after the specs run. Travis will also fail +the build if some problems detected. + +### OS X + +```sh +$ brew tap veelenga/tap +$ brew install ameba +``` + +### Docker + +Build the image: + +```sh +$ docker build -t crystal-ameba/ameba . +``` + +To use the resulting image on a local source folder, mount the current (or target) directory into `/src`: + +```sh +$ docker run -v $(pwd):/src crystal-ameba/ameba +``` + +Also available on DockerHub: https://hub.docker.com/r/veelenga/ameba + +### From sources + +```sh +$ git clone https://github.com/crystal-ameba/ameba && cd ameba +$ make install +``` + +## Configuration + +Default configuration file is `.ameba.yml`. +It allows to configure rule properties, disable specific rules and exclude sources from the rules. + +Generate new file by running `ameba --gen-config`. + +### Sources + +**List of sources to run Ameba on can be configured globally via:** + +- `Globs` section - an array of wildcards (or paths) to include to the + inspection. Defaults to `%w(**/*.cr !lib)`, meaning it includes all project + files with `*.cr` extension except those which exist in `lib` folder. +- `Excluded` section - an array of wildcards (or paths) to exclude from the + source list defined by `Globs`. Defaults to an empty array. + +In this example we define default globs and exclude `src/compiler` folder: + +``` yaml +Globs: + - **/*.cr + - !lib + +Excluded: + - src/compiler +``` + +**Specific sources can be excluded at rule level**: + +``` yaml +Style/RedundantBegin: + Excluded: + - src/server/processor.cr + - src/server/api.cr +``` + +### Rules + +One or more rules, or a one or more group of rules can be included or excluded +via command line arguments: + +```sh +$ ameba --only Lint/Syntax # runs only Lint/Syntax rule +$ ameba --only Style,Lint # runs only rules from Style and Lint groups +$ ameba --except Lint/Syntax # runs all rules except Lint/Syntax +$ ameba --except Style,Lint # runs all rules except rules in Style and Lint groups +``` + +Or through the configuration file: + +``` yaml +Style/RedundantBegin: + Enabled: false +``` + +### Explain issues + +Ameba allows you to dig deeper into an issue, by showing you details about the issue +and the reasoning by it being reported. + +To be convenient, you can just copy-paste the `PATH:line:column` string from the +report and paste behind the `ameba` command to check it out. + +```sh +$ ameba crystal/command/format.cr:26:83 # show explanation for the issue +$ ameba --explain crystal/command/format.cr:26:83 # same thing +``` + +### Inline disabling + +One or more rules or one or more group of rules can be disabled using inline directives: + +```crystal +# ameba:disable Style/LargeNumbers +time = Time.epoch(1483859302) + +time = Time.epoch(1483859302) # ameba:disable Style/LargeNumbers, Lint/UselessAssign + +time = Time.epoch(1483859302) # ameba:disable Style, Lint +``` + +## Editors & integrations + + * Vim: [vim-crystal](https://github.com/rhysd/vim-crystal), [Ale](https://github.com/w0rp/ale) + * Emacs: [ameba.el](https://github.com/crystal-ameba/ameba.el) + * Sublime Text: [Sublime Linter Ameba](https://github.com/epergo/SublimeLinter-contrib-ameba) + * VSCode: [vscode-crystal-ameba](https://github.com/crystal-ameba/vscode-crystal-ameba) + * Codacy: [codacy-ameba](https://github.com/codacy/codacy-ameba) + * GitHub Actions: [github-action](https://github.com/crystal-ameba/github-action) + +## Credits & inspirations + +- [Crystal Language](https://crystal-lang.org) +- [Rubocop](https://rubocop.readthedocs.io/en/latest/) +- [Credo](http://credo-ci.org/) +- [Dogma](https://github.com/lpil/dogma) + +## Contributors + +- [veelenga](https://github.com/veelenga) Vitalii Elenhaupt - creator, maintainer diff --git a/samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr b/samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr new file mode 100644 index 000000000000..d66f9603dff2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr @@ -0,0 +1,30 @@ +require "../src/ameba" +require "benchmark" + +private def get_files(n) + Dir["src/**/*.cr"].first(n) +end + +puts "== Compare:" +Benchmark.ips do |x| + [ + 1, + 3, + 5, + 10, + 20, + 30, + 40, + ].each do |n| + config = Ameba::Config.load + config.formatter = Ameba::Formatter::BaseFormatter.new + config.globs = get_files(n) + s = n == 1 ? "" : "s" + x.report("#{n} source#{s}") { Ameba.run config } + end +end + +puts "== Measure:" +config = Ameba::Config.load +config.formatter = Ameba::Formatter::BaseFormatter.new +puts Benchmark.measure { Ameba.run config } diff --git a/samples/client/petstore/crystal/lib/ameba/bin/.keep b/samples/client/petstore/crystal/lib/ameba/bin/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/samples/client/petstore/crystal/lib/ameba/bin/ameba.cr b/samples/client/petstore/crystal/lib/ameba/bin/ameba.cr new file mode 100644 index 000000000000..d61d0ff11a5c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/bin/ameba.cr @@ -0,0 +1,7 @@ +# Require ameba cli which starts the inspection. +require "ameba/cli" + +# Require ameba extensions here which are added as project dependencies. +# Example: +# +# require "ameba-performance" diff --git a/samples/client/petstore/crystal/lib/ameba/lib b/samples/client/petstore/crystal/lib/ameba/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/ameba/shard.yml b/samples/client/petstore/crystal/lib/ameba/shard.yml new file mode 100644 index 000000000000..68a3ae25716c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/shard.yml @@ -0,0 +1,20 @@ +name: ameba +version: 0.13.3 + +authors: + - Vitalii Elenhaupt + +targets: + ameba: + main: src/cli.cr + +scripts: + # TODO: remove pre-compiled executable in future releases + postinstall: make bin && make run_file + +executables: + - ameba + +crystal: ">= 0.35.0" + +license: MIT diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr new file mode 100644 index 000000000000..e15f44afdf1c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr @@ -0,0 +1,363 @@ +require "../../spec_helper" + +private def branch_of_assign_in_def(source) + nodes = as_nodes source + Ameba::AST::Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first) +end + +module Ameba::AST + describe Branch do + describe ".of" do + context "Crystal::If" do + it "constructs a branch in If.cond" do + branch = branch_of_assign_in_def %( + def method + if a = get_something # --> Crystal::Assign + puts a + end + end + ) + branch.to_s.should eq "a = get_something" + end + + it "constructs a branch in If.then" do + branch = branch_of_assign_in_def %( + def method + if true + a = 2 # --> Crystal::Assign + end + end + ) + branch.to_s.should eq "a = 2" + end + + it "constructs a branch in If.else" do + branch = branch_of_assign_in_def %( + def method + if true + nil + else + a = 2 # --> Crystal::Assign + end + end + ) + branch.to_s.should eq "a = 2" + end + + it "constructs a branch in inline If" do + branch = branch_of_assign_in_def %( + def method(a) + a = 0 if a == 2 # --> Crystal::Assign + end + ) + branch.to_s.should eq "a = 0" + end + end + + context "Crystal::Unless" do + it "constructs a branch in Unless.cond" do + branch = branch_of_assign_in_def %( + def method + unless a = get_something # --> Crystal::Assign + puts a + end + end + ) + branch.to_s.should eq "a = get_something" + end + + it "constructs a branch in Unless.then" do + branch = branch_of_assign_in_def %( + def method + unless true + a = 2 # --> Crystal::Assign + end + end + ) + branch.to_s.should eq "a = 2" + end + + it "constructs a new branch in Unless.else" do + branch = branch_of_assign_in_def %( + def method + unless true + nil + else + a = 2 # --> Crystal::Assign + end + end + ) + branch.to_s.should eq "a = 2" + end + + it "constructs a branch in inline Unless" do + branch = branch_of_assign_in_def %( + def method(a) + (a = 0; b = 3) unless a == 2 # --> Crystal::Expressions + end + ) + branch.to_s.should eq "(a = 0\nb = 3)" + end + end + + context "Crystal::BinaryOp" do + it "constructs a branch in left node" do + branch = branch_of_assign_in_def %( + def method(a) + (a = 2) && do_something + end + ) + branch.to_s.should eq "(a = 2)" + end + + it "constructs a branch in right node" do + branch = branch_of_assign_in_def %( + def method(a) + do_something || (a = 0) + end + ) + branch.to_s.should eq "(a = 0)" + end + end + + context "Crystal::Case" do + it "constructs a branch in cond" do + branch = branch_of_assign_in_def %( + def method(a) + case (a = 2) + when true then nil + end + end + ) + branch.to_s.should eq "(a = 2)" + end + + it "constructs a branch in when" do + branch = branch_of_assign_in_def %( + def method(a) + case a + when a = 3 then nil + end + end + ) + branch.to_s.should eq "when a = 3\n nil\n" + end + + it "constructs a branch in else" do + branch = branch_of_assign_in_def %( + def method(a) + case a + when true then nil + else a = 4 + end + end + ) + branch.to_s.should eq "a = 4" + end + end + + context "Crystal::While" do + it "constructs a branch in cond" do + branch = branch_of_assign_in_def %( + def method(a) + while a = 1 + nil + end + end + ) + branch.to_s.should eq "a = 1" + end + + it "constructs a branch in body" do + branch = branch_of_assign_in_def %( + def method(a) + while true + b = (a = 1) + end + end + ) + branch.to_s.should eq "b = (a = 1)" + end + end + + context "Crystal::Until" do + it "constructs a branch in cond" do + branch = branch_of_assign_in_def %( + def method(a) + until a = 1 + nil + end + end + ) + branch.to_s.should eq "a = 1" + end + + it "constructs a branch in body" do + branch = branch_of_assign_in_def %( + def method(a) + until false + b = (a = 1) + end + end + ) + branch.to_s.should eq "b = (a = 1)" + end + end + + context "Crystal::ExceptionHandler" do + it "constructs a branch in body" do + branch = branch_of_assign_in_def %( + def method(a) + a = 1 + rescue + nil + end + ) + branch.to_s.should eq "a = 1" + end + + it "constructs a branch in a rescue" do + branch = branch_of_assign_in_def %( + def method(a) + rescue + a = 1 + end + ) + branch.to_s.should eq "a = 1" + end + + it "constructs a branch in else" do + branch = branch_of_assign_in_def %( + def method(a) + rescue + else + a = 1 + end + ) + branch.to_s.should eq "a = 1" + end + + it "constructs a branch in ensure" do + branch = branch_of_assign_in_def %( + def method(a) + rescue + ensure + a = 1 + end + ) + branch.to_s.should eq "a = 1" + end + end + + context "Crystal::MacroIf" do + it "constructs a branch in cond" do + branch = branch_of_assign_in_def %( + def method(a) + {% if a = 2 %} + {% end %} + end + ) + branch.to_s.should eq "a = 2" + end + + it "constructs a branch in then" do + nodes = as_nodes %( + def method(a) + {% if true %} + a = 2 + {% end %} + end + ) + branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) + branch.to_s.strip.should eq "a = 2" + end + end + + context "Crystal::MacroFor" do + it "constructs a branch in body" do + nodes = as_nodes %( + def method(a) + {% for x in [1, 2, 3] %} + a = 2 + {% end %} + end + ) + branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) + branch.to_s.strip.should eq "a = 2" + end + end + + it "returns nil if branch does not exist" do + nodes = as_nodes %( + def method + a = 2 + end + ) + branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first) + branch.should be_nil + end + end + + describe "#initialize" do + it "creates new branch" do + nodes = as_nodes %( + if true + a = 2 + end + ) + branchable = Branchable.new nodes.if_nodes.first + branch = Branch.new nodes.assign_nodes.first, branchable + branch.node.should_not be_nil + end + end + + describe "delegation" do + it "delegates to_s to node" do + nodes = as_nodes %( + if true + a = 2 + end + ) + branchable = Branchable.new nodes.if_nodes.first + branch = Branch.new nodes.assign_nodes.first, branchable + branch.to_s.should eq branch.node.to_s + end + + it "delegates locations to node" do + nodes = as_nodes %( + if true + a = 2 + end + ) + branchable = Branchable.new nodes.if_nodes.first + branch = Branch.new nodes.assign_nodes.first, branchable + branch.location.should eq branch.node.location + branch.end_location.should eq branch.node.end_location + end + end + + describe "#in_loop?" do + it "returns true if branch is in a loop" do + nodes = as_nodes %( + while true + a = 1 + end + ) + branchable = Branchable.new nodes.while_nodes.first + branch = Branch.new nodes.assign_nodes.first, branchable + branch.in_loop?.should be_true + end + + it "returns false if branch is not in a loop" do + nodes = as_nodes %( + if a > 2 + a = 1 + end + ) + branchable = Branchable.new nodes.if_nodes.first + branch = Branch.new nodes.assign_nodes.first, branchable + branch.in_loop?.should be_false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr new file mode 100644 index 000000000000..d8cf7b24f00d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr @@ -0,0 +1,49 @@ +require "../../spec_helper" + +module Ameba::AST + describe Branchable do + describe "#initialize" do + it "creates a new branchable" do + branchable = Branchable.new as_node %(a = 2 if true) + branchable.node.should_not be_nil + end + end + + describe "delegation" do + it "delegates to_s to @node" do + node = as_node %(a = 2 if true) + branchable = Branchable.new node + branchable.to_s.should eq node.to_s + end + + it "delegates locations to @node" do + node = as_node %(a = 2 if true) + branchable = Branchable.new node + branchable.location.should eq node.location + branchable.end_location.should eq node.end_location + end + end + + describe "#loop?" do + it "returns true if it is a while loop" do + branchable = Branchable.new as_node %(while true; a = 2; end) + branchable.loop?.should be_true + end + + it "returns true if it is the until loop" do + branchable = Branchable.new as_node %(until false; a = 2; end) + branchable.loop?.should be_true + end + + it "returns true if it is loop" do + branchable = Branchable.new as_node %(loop {}) + branchable.loop?.should be_true + end + + it "returns false otherwise" do + branchable = Branchable.new as_node %(a = 2 if true) + branchable.loop?.should be_false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr new file mode 100644 index 000000000000..a71e251f09c4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr @@ -0,0 +1,56 @@ +require "../../spec_helper" + +module Ameba::AST + describe FlowExpression do + describe "#initialize" do + it "creates a new flow expression" do + node = as_node("return 22") + flow_expression = FlowExpression.new node, false + flow_expression.node.should_not be_nil + flow_expression.in_loop?.should eq false + end + + describe "#delegation" do + it "delegates to_s to @node" do + node = as_node("return 22") + flow_expression = FlowExpression.new node, false + flow_expression.to_s.should eq node.to_s + end + + it "delegates locations to @node" do + node = as_node %(break if true) + flow_expression = FlowExpression.new node, false + flow_expression.location.should eq node.location + flow_expression.end_location.should eq node.end_location + end + end + + describe "#unreachable_nodes" do + it "returns unreachable nodes" do + nodes = as_nodes %( + def foobar + return + a = 1 + a = 2 + end + ) + node = nodes.expressions_nodes.first + flow_expression = FlowExpression.new node, false + flow_expression.unreachable_nodes.should eq nodes.assign_nodes + end + + it "returns nil if there is no unreachable node" do + nodes = as_nodes %( + def foobar + a = 1 + return a + end + ) + node = nodes.expressions_nodes.first + flow_expression = FlowExpression.new node, false + flow_expression.unreachable_nodes.empty?.should eq true + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr new file mode 100644 index 000000000000..72951b14d5d8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr @@ -0,0 +1,143 @@ +require "../../spec_helper" + +module Ameba::AST + describe Scope do + describe "#initialize" do + source = "a = 2" + + it "assigns outer scope" do + root = Scope.new as_node(source) + child = Scope.new as_node(source), root + child.outer_scope.should_not be_nil + end + + it "assigns node" do + scope = Scope.new as_node(source) + scope.node.should_not be_nil + end + end + end + + describe "delegation" do + it "delegates to_s to node" do + node = as_node("def foo; end") + scope = Scope.new node + scope.to_s.should eq node.to_s + end + + it "delegates locations to node" do + node = as_node("def foo; end") + scope = Scope.new node + scope.location.should eq node.location + scope.end_location.should eq node.end_location + end + end + + describe "#references" do + it "can return an empty list of references" do + scope = Scope.new as_node("") + scope.references.should be_empty + end + + it "allows to add variable references" do + scope = Scope.new as_node("") + nodes = as_nodes "a = 2" + scope.references << Reference.new(nodes.var_nodes.first, scope) + scope.references.size.should eq 1 + end + end + + describe "#add_variable" do + it "adds a new variable to the scope" do + scope = Scope.new as_node("") + scope.add_variable(Crystal::Var.new "foo") + scope.variables.any?.should be_true + end + end + + describe "#find_variable" do + it "returns the variable in the scope by name" do + scope = Scope.new as_node("foo = 1") + scope.add_variable Crystal::Var.new "foo" + scope.find_variable("foo").should_not be_nil + end + + it "returns nil if variable not exist in this scope" do + scope = Scope.new as_node("foo = 1") + scope.find_variable("bar").should be_nil + end + end + + describe "#assign_variable" do + it "creates a new assignment" do + scope = Scope.new as_node("foo = 1") + scope.add_variable Crystal::Var.new "foo" + scope.assign_variable("foo", Crystal::Var.new "foo") + scope.find_variable("foo").not_nil!.assignments.size.should eq 1 + end + + it "does not create the assignment if variable is wrong" do + scope = Scope.new as_node("foo = 1") + scope.add_variable Crystal::Var.new "foo" + scope.assign_variable("bar", Crystal::Var.new "bar") + scope.find_variable("foo").not_nil!.assignments.size.should eq 0 + end + end + + describe "#block?" do + it "returns true if Crystal::Block" do + nodes = as_nodes %( + 3.times {} + ) + scope = Scope.new nodes.block_nodes.first + scope.block?.should be_true + end + + it "returns false otherwise" do + scope = Scope.new as_node "a = 1" + scope.block?.should be_false + end + end + + describe "#spawn_block?" do + it "returns true if a node is a spawn block" do + nodes = as_nodes %( + spawn {} + ) + scope = Scope.new nodes.block_nodes.first + scope.spawn_block?.should be_true + end + + it "returns false otherwise" do + scope = Scope.new as_node "a = 1" + scope.spawn_block?.should be_false + end + end + + describe "#in_macro?" do + it "returns true if Crystal::Macro" do + nodes = as_nodes %( + macro included + end + ) + scope = Scope.new nodes.macro_nodes.first + scope.in_macro?.should be_true + end + + it "returns true if node is nested to Crystal::Macro" do + nodes = as_nodes %( + macro included + {{@type.each do |type| a = type end}} + end + ) + outer_scope = Scope.new nodes.macro_nodes.first + scope = Scope.new nodes.block_nodes.first, outer_scope + scope.in_macro?.should be_true + end + + it "returns false otherwise" do + scope = Scope.new as_node "a = 1" + scope.in_macro?.should be_false + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr new file mode 100644 index 000000000000..d2f63de810d8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr @@ -0,0 +1,326 @@ +require "../../spec_helper" +require "semantic_version" + +module Ameba::AST + struct Test + include Util + end + + subject = Test.new + + describe Util do + describe "#literal?" do + [ + Crystal::ArrayLiteral.new, + Crystal::BoolLiteral.new(false), + Crystal::CharLiteral.new('a'), + Crystal::HashLiteral.new, + Crystal::NamedTupleLiteral.new, + Crystal::NilLiteral.new, + Crystal::NumberLiteral.new(42), + Crystal::RegexLiteral.new(Crystal::StringLiteral.new("")), + Crystal::StringLiteral.new(""), + Crystal::SymbolLiteral.new(""), + Crystal::TupleLiteral.new([] of Crystal::ASTNode), + Crystal::RangeLiteral.new( + Crystal::NilLiteral.new, + Crystal::NilLiteral.new, + true), + ].each do |literal| + it "returns true if node is #{literal}" do + subject.literal?(literal).should be_true + end + end + + it "returns false if node is not a literal" do + subject.literal?(Crystal::Nop).should be_false + end + end + + describe "#node_source" do + it "returns original source of the node" do + s = %( + a = 1 + ) + node = Crystal::Parser.new(s).parse + source = subject.node_source node, s.split("\n") + source.should eq ["a = 1"] + end + + it "returns original source of multiline node" do + s = %( + if () + :ok + end + ) + node = Crystal::Parser.new(s).parse + source = subject.node_source node, s.split("\n") + source.should eq([ + "if ()", + " :ok", + " end", + ]) + end + + it "does not report source of node which has incorrect location" do + s = %q( + module MyModule + macro conditional_error_for_inline_callbacks + \{% + raise "" + %} + end + + macro before_save(x = nil) + end + end + ) + node = as_nodes(s).nil_literal_nodes.first + source = subject.node_source node, s.split("\n") + + if SemanticVersion.parse(Crystal::VERSION) <= SemanticVersion.parse("0.35.1") + source.should be_nil + else + source.should eq %w(nil) + end + end + end + + describe "#flow_command?" do + it "returns true if this is return" do + node = as_node("return 22") + subject.flow_command?(node, false).should eq true + end + + it "returns true if this is a break in a loop" do + node = as_node("break") + subject.flow_command?(node, true).should eq true + end + + it "returns false if this is a break out of loop" do + node = as_node("break") + subject.flow_command?(node, false).should eq false + end + + it "returns true if this is a next in a loop" do + node = as_node("next") + subject.flow_command?(node, true).should eq true + end + + it "returns false if this is a next out of loop" do + node = as_node("next") + subject.flow_command?(node, false).should eq false + end + + it "returns true if this is raise" do + node = as_node("raise e") + subject.flow_command?(node, false).should eq true + end + + it "returns true if this is exit" do + node = as_node("exit") + subject.flow_command?(node, false).should eq true + end + + it "returns true if this is abort" do + node = as_node("abort") + subject.flow_command?(node, false).should eq true + end + + it "returns false otherwise" do + node = as_node("foobar") + subject.flow_command?(node, false).should eq false + end + end + + describe "#flow_expression?" do + it "returns true if this is a flow command" do + node = as_node("return") + subject.flow_expression?(node, true).should eq true + end + + it "returns true if this is if-else consumed by flow expressions" do + node = as_node %( + if foo + return :foo + else + return :bar + end + ) + subject.flow_expression?(node, false).should eq true + end + + it "returns true if this is unless-else consumed by flow expressions" do + node = as_node %( + unless foo + return :foo + else + return :bar + end + ) + subject.flow_expression?(node).should eq true + end + + it "returns true if this is case consumed by flow expressions" do + node = as_node %( + case + when 1 + return 1 + when 2 + return 2 + else + return 3 + end + ) + subject.flow_expression?(node).should eq true + end + + it "returns true if this is exception handler consumed by flow expressions" do + node = as_node %( + begin + raise "exp" + rescue e + return e + end + ) + subject.flow_expression?(node).should eq true + end + + it "returns true if this while consumed by flow expressions" do + node = as_node %( + while true + return + end + ) + subject.flow_expression?(node).should eq true + end + + it "returns false if this while with break" do + node = as_node %( + while true + break + end + ) + subject.flow_expression?(node).should eq false + end + + it "returns true if this until consumed by flow expressions" do + node = as_node %( + until false + return + end + ) + subject.flow_expression?(node).should eq true + end + + it "returns false if this until with break" do + node = as_node %( + until false + break + end + ) + subject.flow_expression?(node).should eq false + end + + it "returns true if this expressions consumed by flow expressions" do + node = as_node %( + exp1 + exp2 + return + ) + subject.flow_expression?(node).should eq true + end + + it "returns false otherwise" do + node = as_node %( + exp1 + exp2 + ) + subject.flow_expression?(node).should eq false + end + end + + describe "#raise?" do + it "returns true if this is a raise method call" do + node = as_node "raise e" + subject.raise?(node).should eq true + end + + it "returns false if it has a receiver" do + node = as_node "obj.raise e" + subject.raise?(node).should eq false + end + + it "returns false if size of the arguments doesn't match" do + node = as_node "raise" + subject.raise?(node).should eq false + end + end + + describe "#exit?" do + it "returns true if this is a exit method call" do + node = as_node "exit" + subject.exit?(node).should eq true + end + + it "returns true if this is a exit method call with one argument" do + node = as_node "exit 1" + subject.exit?(node).should eq true + end + + it "returns false if it has a receiver" do + node = as_node "obj.exit" + subject.exit?(node).should eq false + end + + it "returns false if size of the arguments doesn't match" do + node = as_node "exit 1, 1" + subject.exit?(node).should eq false + end + end + + describe "#abort?" do + it "returns true if this is an abort method call" do + node = as_node "abort" + subject.abort?(node).should eq true + end + + it "returns true if this is an abort method call with one argument" do + node = as_node "abort \"message\"" + subject.abort?(node).should eq true + end + + it "returns true if this is an abort method call with two arguments" do + node = as_node "abort \"message\", 1" + subject.abort?(node).should eq true + end + + it "returns false if it has a receiver" do + node = as_node "obj.abort" + subject.abort?(node).should eq false + end + + it "returns false if size of the arguments doesn't match" do + node = as_node "abort 1, 1, 1" + subject.abort?(node).should eq false + end + end + + describe "#loop?" do + it "returns true if this is a loop method call" do + node = as_node "loop" + subject.loop?(node).should eq true + end + + it "returns false if it has a receiver" do + node = as_node "obj.loop" + subject.loop?(node).should eq false + end + + it "returns false if size of the arguments doesn't match" do + node = as_node "loop 1" + subject.loop?(node).should eq false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr new file mode 100644 index 000000000000..6a3debe2dec8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr @@ -0,0 +1,41 @@ +require "../../../spec_helper" + +module Ameba::AST + describe Argument do + arg = Crystal::Arg.new "a" + scope = Scope.new as_node "foo = 1" + variable = Variable.new(Crystal::Var.new("foo"), scope) + + describe "#initialize" do + it "creates a new argument" do + argument = Argument.new(arg, variable) + argument.node.should_not be_nil + end + end + + describe "delegation" do + it "delegates locations to node" do + argument = Argument.new(arg, variable) + argument.location.should eq arg.location + argument.end_location.should eq arg.end_location + end + + it "delegates to_s to node" do + argument = Argument.new(arg, variable) + argument.to_s.should eq arg.to_s + end + end + + describe "#ignored?" do + it "is true if arg starts with _" do + argument = Argument.new(Crystal::Arg.new("_a"), variable) + argument.ignored?.should be_true + end + + it "is false otherwise" do + argument = Argument.new(arg, variable) + argument.ignored?.should be_false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr new file mode 100644 index 000000000000..7f50f6d1cf92 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr @@ -0,0 +1,118 @@ +require "../../../spec_helper" + +module Ameba::AST + describe Assignment do + node = Crystal::NilLiteral.new + scope = Scope.new as_node "foo = 1" + variable = Variable.new(Crystal::Var.new("foo"), scope) + + describe "#initialize" do + it "creates a new assignment with node and var" do + assignment = Assignment.new(node, variable, scope) + assignment.node.should_not be_nil + end + end + + describe "#reference=" do + it "creates a new reference" do + assignment = Assignment.new(node, variable, scope) + assignment.referenced = true + assignment.referenced?.should be_true + end + end + + describe "delegation" do + it "delegates locations" do + assignment = Assignment.new(node, variable, scope) + assignment.location.should eq node.location + assignment.end_location.should eq node.end_location + end + + it "delegates to_s" do + assignment = Assignment.new(node, variable, scope) + assignment.to_s.should eq node.to_s + end + + it "delegates scope" do + assignment = Assignment.new(node, variable, scope) + assignment.scope.should eq variable.scope + end + end + + describe "#branch" do + it "returns the branch of the assignment" do + nodes = as_nodes %( + def method(a) + if a + a = 3 # --> Crystal::Expressions + puts a + end + end + ) + + scope = Scope.new nodes.def_nodes.first + variable = Variable.new(nodes.var_nodes.first, scope) + assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) + assignment.branch.should_not be_nil + assignment.branch.not_nil!.node.class.should eq Crystal::Expressions + end + + it "returns inner branch" do + nodes = as_nodes %( + def method(a, b) + if a + if b + a = 3 # --> Crystal::Assign + end + end + end + ) + scope = Scope.new nodes.def_nodes.first + variable = Variable.new(nodes.var_nodes.first, scope) + assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) + assignment.branch.should_not be_nil + assignment.branch.not_nil!.node.class.should eq Crystal::Assign + end + + it "returns nil if assignment does not have a branch" do + nodes = as_nodes %( + def method(a) + a = 2 + end + ) + + scope = Scope.new nodes.def_nodes.first + variable = Variable.new(nodes.var_nodes.first, scope) + assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) + assignment.branch.should be_nil + end + end + + describe "#transformed?" do + it "returns false if the assignment is not transformed by the compiler" do + nodes = as_nodes %( + def method(a) + a = 2 + end + ) + + scope = Scope.new nodes.def_nodes.first + variable = Variable.new(nodes.var_nodes.first, scope) + assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) + assignment.transformed?.should be_false + end + + it "returns true if the assignment is transformed by the compiler" do + nodes = as_nodes %( + array.each do |(a, b)| + end + ) + + scope = Scope.new nodes.block_nodes.first + variable = Variable.new(nodes.var_nodes.first, scope) + assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) + assignment.transformed?.should be_true + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr new file mode 100644 index 000000000000..155270918e1c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr @@ -0,0 +1,10 @@ +require "../../../spec_helper" + +module Ameba::AST + describe Reference do + it "is derived from a Variable" do + node = Crystal::Var.new "foo" + Reference.new(node, Scope.new as_node "foo = 1").is_a?(Variable).should be_true + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr new file mode 100644 index 000000000000..fc39ef0d1cfc --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr @@ -0,0 +1,218 @@ +require "../../../spec_helper" + +module Ameba::AST + describe Variable do + var_node = Crystal::Var.new("foo") + scope = Scope.new as_node "foo = 1" + + describe "#initialize" do + it "creates a new variable" do + variable = Variable.new(var_node, scope) + variable.node.should_not be_nil + end + end + + describe "delegation" do + it "delegates locations" do + variable = Variable.new(var_node, scope) + variable.location.should eq var_node.location + variable.end_location.should eq var_node.end_location + end + + it "delegates name" do + variable = Variable.new(var_node, scope) + variable.name.should eq var_node.name + end + + it "delegates to_s" do + variable = Variable.new(var_node, scope) + variable.to_s.should eq var_node.to_s + end + end + + describe "#special?" do + it "returns truthy if it is a special `$?` var" do + variable = Variable.new Crystal::Var.new("$?"), scope + variable.special?.should be_truthy + end + + it "returns falsey otherwise" do + variable = Variable.new Crystal::Var.new("a"), scope + variable.special?.should be_falsey + end + end + + describe "#assign" do + assign_node = as_node("foo=1") + + it "assigns the variable (creates a new assignment)" do + variable = Variable.new(var_node, scope) + variable.assign(assign_node, scope) + variable.assignments.any?.should be_true + end + + it "can create multiple assignments" do + variable = Variable.new(var_node, scope) + variable.assign(assign_node, scope) + variable.assign(assign_node, scope) + variable.assignments.size.should eq 2 + end + end + + describe "#reference" do + it "references the existed assignment" do + variable = Variable.new(var_node, scope) + variable.assign(as_node("foo=1"), scope) + variable.reference(var_node, scope) + variable.references.any?.should be_true + end + + it "adds a reference to the scope" do + scope = Scope.new as_node "foo = 1" + variable = Variable.new(var_node, scope) + variable.assign(as_node("foo=1"), scope) + variable.reference(var_node, scope) + scope.references.size.should eq 1 + scope.references.first.node.to_s.should eq "foo" + end + end + + describe "#captured_by_block?" do + it "returns truthy if the variable is captured by block" do + nodes = as_nodes %( + def method + a = 2 + 3.times { |i| a = a + i } + end + ) + scope = Scope.new nodes.def_nodes.first + var_node = nodes.var_nodes.first + scope.add_variable var_node + scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope) + + variable = Variable.new(var_node, scope) + variable.reference nodes.var_nodes.last, scope.inner_scopes.last + variable.captured_by_block?.should be_truthy + end + + it "returns falsey if the variable is not captured by the block" do + scope = Scope.new as_node %( + def method + a = 1 + end + ) + scope.add_variable Crystal::Var.new "a" + variable = scope.variables.first + variable.captured_by_block?.should be_falsey + end + end + + describe "#target_of?" do + it "returns true if the variable is a target of Crystal::Assign node" do + assign_node = as_nodes("foo=1").assign_nodes.last + variable = Variable.new assign_node.target.as(Crystal::Var), scope + variable.target_of?(assign_node).should be_true + end + + it "returns true if the variable is a target of Crystal::OpAssign node" do + assign_node = as_nodes("foo=1;foo+=1").op_assign_nodes.last + variable = Variable.new assign_node.target.as(Crystal::Var), scope + variable.target_of?(assign_node).should be_true + end + + it "returns true if the variable is a target of Crystal::MultiAssign node" do + assign_node = as_nodes("a,b,c={1,2,3}").multi_assign_nodes.last + assign_node.targets.size.should_not eq 0 + assign_node.targets.each do |target| + variable = Variable.new target.as(Crystal::Var), scope + variable.target_of?(assign_node).should be_true + end + end + + it "returns false if the node is not assign" do + variable = Variable.new(Crystal::Var.new("v"), scope) + variable.target_of?(as_node "nil").should be_false + end + + it "returns false if the variable is not a target of the assign" do + variable = Variable.new(Crystal::Var.new("foo"), scope) + variable.target_of?(as_node("bar = 1")).should be_false + end + end + + describe "#ignored?" do + it "is true if variable is ignored" do + variable = Variable.new(Crystal::Var.new("_var"), scope) + variable.ignored?.should be_true + end + + it "is false if variable is not ignored" do + variable = Variable.new(Crystal::Var.new("v_ar"), scope) + variable.ignored?.should be_false + end + + it "is true if variable is a black hole" do + variable = Variable.new(Crystal::Var.new("_"), scope) + variable.ignored?.should be_true + end + end + + describe "#eql?" do + var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) + variable = Variable.new var, scope + + it "is false if node is not a Crystal::Var" do + variable.eql?(as_node("nil")).should be_false + end + + it "is false if node name is different" do + variable.eql?(Crystal::Var.new "bar").should be_false + end + + it "is false if node has a different location" do + variable.eql?(Crystal::Var.new "foo").should be_false + end + + it "is true otherwise" do + variable.eql?(variable.node).should be_true + end + end + + describe "#declared_before?" do + it "is falsey if variable doesn't have location" do + var1 = Crystal::Var.new("foo") + var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 2)) + Variable.new(var1, scope).declared_before?(var2).should be_falsey + end + + it "is falsey if node doesn't have location" do + var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) + var2 = Crystal::Var.new("bar") + Variable.new(var1, scope).declared_before?(var2).should be_falsey + end + + it "is true if var's line_number below the node" do + var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) + var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 2, 2)) + Variable.new(var1, scope).declared_before?(var2).should be_true + end + + it "is true if var's column_number is after the node" do + var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) + var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3)) + Variable.new(var1, scope).declared_before?(var2).should be_true + end + + it "is false if var's location is before the node" do + var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2)) + var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3)) + Variable.new(var1, scope).declared_before?(var2).should be_false + end + + it "is false is the node is the same var" do + var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2)) + Variable.new(var, scope).declared_before?(var).should be_false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr new file mode 100644 index 000000000000..5ee964ae8361 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr @@ -0,0 +1,65 @@ +require "../../../spec_helper" + +module Ameba::AST + describe CountingVisitor do + describe "#visit" do + it "allow to visit ASTNode" do + node = Crystal::Parser.new("").parse + visitor = CountingVisitor.new node + node.accept visitor + end + end + + describe "#count" do + it "is 1 for an empty method" do + node = Crystal::Parser.new("def hello; end").parse + visitor = CountingVisitor.new node + + visitor.count.should eq 1 + end + + it "is 1 if there is Macro::For" do + code = %( + def initialize() + {% for c in ALL_NODES %} + true || false + {% end %} + end + ) + node = Crystal::Parser.new(code).parse + visitor = CountingVisitor.new node + visitor.count.should eq 1 + end + + it "is 1 if there is Macro::If" do + code = %( + def initialize() + {% if foo.bar? %} + true || false + {% end %} + end + ) + node = Crystal::Parser.new(code).parse + visitor = CountingVisitor.new node + visitor.count.should eq 1 + end + + {% for pair in [ + {code: "if true; end", description: "conditional"}, + {code: "while true; end", description: "while loop"}, + {code: "until 1 < 2; end", description: "until loop"}, + {code: "begin; rescue; end", description: "rescue"}, + {code: "case 1 when 1; end", description: "when"}, + {code: "true || false", description: "or"}, + {code: "true && false", description: "and"}, + ] %} + it "increases count for every {{ pair[:description].id }}" do + node = Crystal::Parser.new("def hello; {{ pair[:code].id }} end").parse + visitor = CountingVisitor.new node + + visitor.count.should eq 2 + end + {% end %} + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr new file mode 100644 index 000000000000..2e41ce530697 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr @@ -0,0 +1,66 @@ +require "../../../spec_helper" + +module Ameba::AST + source = Source.new "" + + describe FlowExpressionVisitor do + it "creates an expression for return" do + rule = FlowExpressionRule.new + FlowExpressionVisitor.new rule, Source.new %( + def foo + return :bar + end + ) + rule.expressions.size.should eq 1 + end + + it "can create multiple expressions" do + rule = FlowExpressionRule.new + FlowExpressionVisitor.new rule, Source.new %( + def foo + if bar + return :baz + else + return :foobar + end + end + ) + rule.expressions.size.should eq 3 + end + + it "properly creates nested flow expressions" do + rule = FlowExpressionRule.new + FlowExpressionVisitor.new rule, Source.new %( + def foo + return( + loop do + break if a > 1 + return a + end + ) + end + ) + rule.expressions.size.should eq 4 + end + + it "creates an expression for break" do + rule = FlowExpressionRule.new + FlowExpressionVisitor.new rule, Source.new %( + while true + break + end + ) + rule.expressions.size.should eq 1 + end + + it "creates an expression for next" do + rule = FlowExpressionRule.new + FlowExpressionVisitor.new rule, Source.new %( + while true + next if something + end + ) + rule.expressions.size.should eq 1 + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr new file mode 100644 index 000000000000..38535a9c4ca0 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr @@ -0,0 +1,16 @@ +require "../../../spec_helper" + +module Ameba::AST + rule = DummyRule.new + source = Source.new "" + + describe NodeVisitor do + describe "visit" do + it "allow to visit ASTNode" do + visitor = NodeVisitor.new rule, source + nodes = Crystal::Parser.new("").parse + nodes.accept visitor + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr new file mode 100644 index 000000000000..a523ad8378aa --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr @@ -0,0 +1,26 @@ +require "../../../spec_helper" + +module Ameba::AST + source = Source.new "" + rule = RedundantControlExpressionRule.new + + describe RedundantControlExpressionVisitor do + node = as_node %( + a = 1 + b = 2 + return a + b + ) + subject = RedundantControlExpressionVisitor.new(rule, source, node) + + it "assigns valid attributes" do + subject.rule.should eq rule + subject.source.should eq source + subject.node.should eq node + end + + it "fires a callback with a valid node" do + rule.nodes.size.should eq 1 + rule.nodes.first.to_s.should eq "return a + b" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr new file mode 100644 index 000000000000..98818fa52ef2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr @@ -0,0 +1,58 @@ +require "../../../spec_helper" + +module Ameba::AST + describe ScopeVisitor do + it "creates a scope for the def" do + rule = ScopeRule.new + ScopeVisitor.new rule, Source.new %( + def method + end + ) + rule.scopes.size.should eq 1 + end + + it "creates a scope for the proc" do + rule = ScopeRule.new + ScopeVisitor.new rule, Source.new %( + -> {} + ) + rule.scopes.size.should eq 1 + end + + it "creates a scope for the block" do + rule = ScopeRule.new + ScopeVisitor.new rule, Source.new %( + 3.times {} + ) + rule.scopes.size.should eq 2 + end + + context "inner scopes" do + it "creates scope for block inside def" do + rule = ScopeRule.new + ScopeVisitor.new rule, Source.new %( + def method + 3.times {} + end + ) + rule.scopes.size.should eq 2 + rule.scopes.last.outer_scope.should_not be_nil + rule.scopes.first.outer_scope.should eq rule.scopes.last + end + + it "creates scope for block inside block" do + rule = ScopeRule.new + ScopeVisitor.new rule, Source.new %( + 3.times do + 2.times {} + end + ) + rule.scopes.size.should eq 3 + inner_block = rule.scopes.first + outer_block = rule.scopes.last + inner_block.outer_scope.should_not eq outer_block + outer_block.outer_scope.should be_nil + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr new file mode 100644 index 000000000000..219703207406 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr @@ -0,0 +1,75 @@ +require "../spec_helper" + +module Ameba::Rule + describe Base do + context ".rules" do + it "returns a list of all rules" do + rules = Rule.rules + rules.should_not be_nil + rules.should contain DummyRule + end + end + + context "properties" do + subject = DummyRule.new + + it "is enabled by default" do + subject.enabled.should be_true + end + + it "has a description property" do + subject.description.should_not be_nil + end + + it "has excluded property" do + subject.excluded.should be_nil + end + end + + describe "#excluded?" do + it "returns false if a rule does no have a list of excluded source" do + DummyRule.new.excluded?(Source.new "", "source.cr").should_not be_true + end + + it "returns false if source is not excluded from this rule" do + rule = DummyRule.new + rule.excluded = %w(some_source.cr) + rule.excluded?(Source.new "", "another_source.cr").should_not be_true + end + + it "returns true if source is excluded from this rule" do + rule = DummyRule.new + rule.excluded = %w(source.cr) + rule.excluded?(Source.new "", "source.cr").should be_true + end + + it "returns true if source matches the wildcard" do + rule = DummyRule.new + rule.excluded = %w(**/*.cr) + rule.excluded?(Source.new "", __FILE__).should be_true + end + + it "returns false if source does not match the wildcard" do + rule = DummyRule.new + rule.excluded = %w(*_spec.cr) + rule.excluded?(Source.new "", "source.cr").should be_false + end + end + + describe ".parsed_doc" do + it "returns the parsed rule doc" do + DummyRule.parsed_doc.should eq "Dummy Rule which does nothing." + end + end + + describe "#==" do + it "returns true if rule has the same name" do + DummyRule.new.should eq(DummyRule.new) + end + + it "returns false if rule has a different name" do + DummyRule.new.should_not eq(ScopeRule.new) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr new file mode 100644 index 000000000000..f44ff6c6b177 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr @@ -0,0 +1,165 @@ +require "../../spec_helper" +require "../../../src/ameba/cli/cmd" + +module Ameba::Cli + describe "Cmd" do + describe ".run" do + it "runs ameba" do + r = Cli.run %w(-f silent file.cr) + r.should be_nil + end + end + + describe ".parse_args" do + %w(-s --silent).each do |f| + it "accepts #{f} flag" do + c = Cli.parse_args [f] + c.formatter.should eq :silent + end + end + + %w(-c --config).each do |f| + it "accepts #{f} flag" do + c = Cli.parse_args [f, "config.yml"] + c.config.should eq "config.yml" + end + end + + %w(-f --format).each do |f| + it "accepts #{f} flag" do + c = Cli.parse_args [f, "my-formatter"] + c.formatter.should eq "my-formatter" + end + end + + it "accepts --only flag" do + c = Cli.parse_args ["--only", "RULE1,RULE2"] + c.only.should eq %w(RULE1 RULE2) + end + + it "accepts --except flag" do + c = Cli.parse_args ["--except", "RULE1,RULE2"] + c.except.should eq %w(RULE1 RULE2) + end + + it "defaults all? flag to false" do + c = Cli.parse_args %w(file.cr) + c.all?.should eq false + end + + it "accepts --all flag" do + c = Cli.parse_args %w(--all) + c.all?.should eq true + end + + it "accepts --gen-config flag" do + c = Cli.parse_args %w(--gen-config) + c.formatter.should eq :todo + end + + it "accepts --no-color flag" do + c = Cli.parse_args %w(--no-color) + c.colors?.should be_false + end + + it "accepts --without-affected-code flag" do + c = Cli.parse_args %w(--without-affected-code) + c.without_affected_code?.should be_true + end + + it "doesn't disable colors by default" do + c = Cli.parse_args %w(--all) + c.colors?.should be_true + end + + it "ignores --config if --gen-config flag passed" do + c = Cli.parse_args %w(--gen-config --config my_config.yml) + c.formatter.should eq :todo + c.config.should eq "" + end + + describe "-e/--explain" do + it "configures file/line/column" do + c = Cli.parse_args %w(--explain src/file.cr:3:5) + c.location_to_explain.should_not be_nil + + location_to_explain = c.location_to_explain.not_nil! + location_to_explain[:file].should eq "src/file.cr" + location_to_explain[:line].should eq 3 + location_to_explain[:column].should eq 5 + end + + it "raises an error if location is not valid" do + expect_raises(Exception, "location should have PATH:line:column") do + Cli.parse_args %w(--explain src/file.cr:3) + end + end + + it "raises an error if line number is not valid" do + expect_raises(Exception, "location should have PATH:line:column") do + Cli.parse_args %w(--explain src/file.cr:a:3) + end + end + + it "raises an error if column number is not valid" do + expect_raises(Exception, "location should have PATH:line:column") do + Cli.parse_args %w(--explain src/file.cr:3:&) + end + end + + it "raises an error if line/column are missing" do + expect_raises(Exception, "location should have PATH:line:column") do + Cli.parse_args %w(--explain src/file.cr) + end + end + end + + context "--fail-level" do + it "configures fail level Convention" do + c = Cli.parse_args %w(--fail-level convention) + c.fail_level.should eq Severity::Convention + end + + it "configures fail level Warning" do + c = Cli.parse_args %w(--fail-level Warning) + c.fail_level.should eq Severity::Warning + end + + it "configures fail level Error" do + c = Cli.parse_args %w(--fail-level error) + c.fail_level.should eq Severity::Error + end + + it "raises if fail level is incorrect" do + expect_raises(Exception, "Incorrect severity name JohnDoe") do + Cli.parse_args %w(--fail-level JohnDoe) + end + end + end + + it "accepts unknown args as globs" do + c = Cli.parse_args %w(source1.cr source2.cr) + c.globs.should eq %w(source1.cr source2.cr) + end + + it "accepts one unknown arg as explain location if it has correct format" do + c = Cli.parse_args %w(source.cr:3:22) + c.location_to_explain.should_not be_nil + + location_to_explain = c.location_to_explain.not_nil! + location_to_explain[:file].should eq "source.cr" + location_to_explain[:line].should eq 3 + location_to_explain[:column].should eq 22 + end + + it "allows args to be blank" do + c = Cli.parse_args [] of String + c.formatter.should be_nil + c.globs.should be_nil + c.only.should be_nil + c.except.should be_nil + c.config.should eq Config::PATH + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr new file mode 100644 index 000000000000..c92768737075 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr @@ -0,0 +1,215 @@ +require "../spec_helper" + +module Ameba + describe Config do + config_sample = "config/ameba.yml" + + it "should have a list of available formatters" do + Config::AVAILABLE_FORMATTERS.should_not be_nil + end + + describe ".new" do + it "loads default globs when config is empty" do + yml = YAML.parse "{}" + config = Config.new(yml) + config.globs.should eq Config::DEFAULT_GLOBS + end + + it "initializes globs as string" do + yml = YAML.parse <<-CONFIG + --- + Globs: src/*.cr + CONFIG + config = Config.new(yml) + config.globs.should eq %w(src/*.cr) + end + + it "initializes globs as array" do + yml = YAML.parse <<-CONFIG + --- + Globs: + - "src/*.cr" + - "!spec" + CONFIG + config = Config.new(yml) + config.globs.should eq %w(src/*.cr !spec) + end + + it "raises if Globs has a wrong type" do + yml = YAML.parse <<-CONFIG + --- + Globs: 100 + CONFIG + expect_raises(Exception, "incorrect 'Globs' section in a config file") { Config.new(yml) } + end + + it "initializes excluded as string" do + yml = YAML.parse <<-CONFIG + --- + Excluded: spec + CONFIG + config = Config.new(yml) + config.excluded.should eq %w(spec) + end + + it "initializes excluded as array" do + yml = YAML.parse <<-CONFIG + --- + Excluded: + - spec + - lib/*.cr + CONFIG + config = Config.new(yml) + config.excluded.should eq %w(spec lib/*.cr) + end + + it "raises if Excluded has a wrong type" do + yml = YAML.parse <<-CONFIG + --- + Excluded: true + CONFIG + expect_raises(Exception, "incorrect 'Excluded' section in a config file") { Config.new(yml) } + end + end + + describe ".load" do + it "loads custom config" do + config = Config.load config_sample + config.should_not be_nil + config.globs.should_not be_nil + config.formatter.should_not be_nil + end + + it "loads default config" do + config = Config.load + config.should_not be_nil + config.globs.should_not be_nil + config.formatter.should_not be_nil + end + end + + describe "#globs, #globs=" do + config = Config.load config_sample + + it "holds source globs" do + config.globs.should eq Config::DEFAULT_GLOBS + end + + it "allows to set globs" do + config.globs = ["file.cr"] + config.globs.should eq ["file.cr"] + end + end + + describe "#excluded, #excluded=" do + config = Config.load config_sample + + it "defaults to empty array" do + config.excluded.should be_empty + end + + it "allows to set excluded" do + config.excluded = ["spec"] + config.excluded.should eq ["spec"] + end + end + + describe "#sources" do + config = Config.load config_sample + + it "returns list of sources" do + config.sources.size.should be > 0 + config.sources.first.should be_a Source + config.sources.any? { |s| s.fullpath == __FILE__ }.should be_true + end + + it "returns a list of sources mathing globs" do + config.globs = %w(**/config_spec.cr) + config.sources.size.should eq(1) + end + + it "returns a lisf of sources excluding 'Excluded'" do + config.excluded = %w(**/config_spec.cr) + config.sources.any? { |s| s.fullpath == __FILE__ }.should be_false + end + end + + describe "#formatter, formatter=" do + config = Config.load config_sample + formatter = DummyFormatter.new + + it "contains default formatter" do + config.formatter.should_not be_nil + end + + it "allows to set formatter" do + config.formatter = formatter + config.formatter.should eq formatter + end + + it "allows to set formatter using a name" do + config.formatter = :progress + config.formatter.should_not be_nil + end + + it "raises an error if not available formatter is set" do + expect_raises(Exception) do + config.formatter = :no_such_formatter + end + end + end + + describe "#update_rule" do + config = Config.load config_sample + + it "updates enabled property" do + name = DummyRule.rule_name + config.update_rule name, enabled: false + rule = config.rules.find(&.name.== name).not_nil! + rule.enabled.should be_false + end + + it "updates excluded property" do + name = DummyRule.rule_name + excluded = %w(spec/source.cr) + config.update_rule name, excluded: excluded + rule = config.rules.find(&.name.== name).not_nil! + rule.excluded.should eq excluded + end + end + + describe "#update_rules" do + config = Config.load config_sample + + it "updates multiple rules by enabled property" do + name = DummyRule.rule_name + config.update_rules [name], enabled: false + rule = config.rules.find(&.name.== name).not_nil! + rule.enabled.should be_false + end + + it "updates multiple rules by excluded property" do + name = DummyRule.rule_name + excluded = %w(spec/source.cr) + config.update_rules [name], excluded: excluded + rule = config.rules.find(&.name.== name).not_nil! + rule.excluded.should eq excluded + end + + it "updates a group of rules by enabled property" do + group = DummyRule.group_name + config.update_rules [group], enabled: false + rule = config.rules.find(&.name.== DummyRule.rule_name).not_nil! + rule.enabled.should be_false + end + + it "updates a group by excluded property" do + name = DummyRule.group_name + excluded = %w(spec/source.cr) + config.update_rules [name], excluded: excluded + rule = config.rules.find(&.name.== DummyRule.rule_name).not_nil! + rule.excluded.should eq excluded + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr new file mode 100644 index 000000000000..5c338ae00cdb --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr @@ -0,0 +1,42 @@ +require "../../spec_helper" + +module Ameba::Formatter + describe DisabledFormatter do + output = IO::Memory.new + subject = DisabledFormatter.new output + + describe "#finished" do + it "writes a final message" do + subject.finished [Source.new ""] + output.to_s.should contain "Disabled rules using inline directives:" + end + + it "writes disabled rules if any" do + Colorize.enabled = false + + path = "source.cr" + s = Source.new("", path).tap do |source| + source.add_issue(ErrorRule.new, {1, 2}, message: "ErrorRule", status: :disabled) + source.add_issue(NamedRule.new, location: {2, 2}, message: "NamedRule", status: :disabled) + end + subject.finished [s] + log = output.to_s + log.should contain "#{path}:1 #{ErrorRule.rule_name}" + log.should contain "#{path}:2 #{NamedRule.rule_name}" + ensure + output.clear + Colorize.enabled = true + end + + it "does not write not-disabled rules" do + s = Source.new("", "source.cr").tap do |source| + source.add_issue(ErrorRule.new, {1, 2}, "ErrorRule") + source.add_issue(NamedRule.new, location: {2, 2}, + message: "NamedRule", status: :disabled) + end + subject.finished [s] + output.to_s.should_not contain ErrorRule.rule_name + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr new file mode 100644 index 000000000000..444426228592 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr @@ -0,0 +1,107 @@ +require "../../spec_helper" + +module Ameba::Formatter + describe DotFormatter do + output = IO::Memory.new + subject = DotFormatter.new output + + describe "#started" do + it "writes started message" do + subject.started [Source.new ""] + output.to_s.should eq "Inspecting 1 file.\n\n" + end + end + + describe "#source_finished" do + it "writes valid source" do + subject.source_finished Source.new "" + output.to_s.should contain "." + end + + it "writes invalid source" do + s = Source.new "" + s.add_issue DummyRule.new, Crystal::Nop.new, "message" + subject.source_finished s + output.to_s.should contain "F" + end + end + + describe "#finished" do + it "writes a final message" do + subject.finished [Source.new ""] + output.to_s.should contain "1 inspected, 0 failures." + end + + it "writes the elapsed time" do + subject.finished [Source.new ""] + output.to_s.should contain "Finished in" + end + + context "when issues found" do + it "writes each issue" do + s = Source.new("").tap do |source| + source.add_issue(DummyRule.new, {1, 1}, "DummyRuleError") + source.add_issue(NamedRule.new, {1, 2}, "NamedRuleError") + end + subject.finished [s] + log = output.to_s + log.should contain "1 inspected, 2 failures." + log.should contain "DummyRuleError" + log.should contain "NamedRuleError" + end + + it "writes affected code by default" do + output.clear + s = Source.new(%( + a = 22 + puts a + )).tap do |source| + source.add_issue(DummyRule.new, {1, 5}, "DummyRuleError") + end + subject.finished [s] + log = output.to_s + log.should contain "> a = 22" + log.should contain " \e[33m^\e[0m" + end + + it "writes severity" do + output.clear + s = Source.new(%( + a = 22 + puts a + )).tap do |source| + source.add_issue(DummyRule.new, {1, 5}, "DummyRuleError") + end + subject.finished [s] + log = output.to_s + log.should contain "[C]" + end + + it "doesn't write affected code if it is disabled" do + output.clear + s = Source.new(%( + a = 22 + puts a + )).tap do |source| + source.add_issue(DummyRule.new, {1, 5}, "DummyRuleError") + end + + formatter = DotFormatter.new output + formatter.config[:without_affected_code] = true + formatter.finished [s] + log = output.to_s + log.should_not contain "> a = 22" + log.should_not contain " \e[33m^\e[0m" + end + + it "does not write disabled issues" do + s = Source.new "" + s.add_issue(DummyRule.new, location: {1, 1}, + message: "DummyRuleError", status: :disabled) + subject.finished [s] + output.to_s.should contain "1 inspected, 0 failures." + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr new file mode 100644 index 000000000000..770d42774613 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr @@ -0,0 +1,71 @@ +require "../../spec_helper" + +module Ameba + def explanation(source) + output = IO::Memory.new + ErrorRule.new.catch(source) + location = {file: "source.cr", line: 1, column: 1} + Formatter::ExplainFormatter.new(output, location).finished([source]) + output.to_s + end + + describe Formatter::ExplainFormatter do + describe "#location" do + it "returns crystal location" do + location = Formatter::ExplainFormatter + .new(STDOUT, {file: "compiler.cr", line: 3, column: 8}).location + + location.is_a?(Crystal::Location).should be_true + location.filename.should eq "compiler.cr" + location.line_number.should eq 3 + location.column_number.should eq 8 + end + end + + describe "#output" do + it "returns io" do + output = Formatter::ExplainFormatter + .new(STDOUT, {file: "compiler.cr", line: 3, column: 8}).output + output.should eq STDOUT + end + end + + describe "#finished" do + it "writes issue info" do + source = Source.new "a = 42", "source.cr" + output = explanation(source) + output.should contain "ISSUE INFO" + output.should contain "This rule always adds an error" + output.should contain "source.cr:1:1" + end + + it "writes affected code" do + source = Source.new "a = 42", "source.cr" + output = explanation(source) + output.should contain "AFFECTED CODE" + output.should contain "a = 42" + end + + it "writes rule info" do + source = Source.new "a = 42", "source.cr" + output = explanation(source) + output.should contain "RULE INFO" + output.should contain "Convention" + output.should contain "Ameba/ErrorRule" + output.should contain "Always adds an error at 1:1" + end + + it "writes detailed description" do + source = Source.new "a = 42", "source.cr" + output = explanation(source) + output.should contain "DETAILED DESCRIPTION" + output.should contain "TO BE DONE..." + end + + it "writes nothing if location not found" do + source = Source.new "a = 42", "another_source.cr" + explanation(source).should be_empty + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr new file mode 100644 index 000000000000..d9fda5fdca48 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr @@ -0,0 +1,48 @@ +require "../../spec_helper" + +private def flycheck + output = IO::Memory.new + Ameba::Formatter::FlycheckFormatter.new output +end + +module Ameba::Formatter + describe FlycheckFormatter do + context "problems not found" do + it "reports nothing" do + subject = flycheck + subject.source_finished Source.new "" + subject.output.to_s.empty?.should be_true + end + end + + context "when problems found" do + it "reports an issue" do + s = Source.new "a = 1", "source.cr" + s.add_issue DummyRule.new, {1, 2}, "message" + subject = flycheck + subject.source_finished s + subject.output.to_s.should eq( + "source.cr:1:2: C: [#{DummyRule.rule_name}] message\n" + ) + end + + it "properly reports multi-line message" do + s = Source.new "a = 1", "source.cr" + s.add_issue DummyRule.new, {1, 2}, "multi\nline" + subject = flycheck + subject.source_finished s + subject.output.to_s.should eq( + "source.cr:1:2: C: [#{DummyRule.rule_name}] multi line\n" + ) + end + + it "reports nothing if location was not set" do + s = Source.new "a = 1", "source.cr" + s.add_issue DummyRule.new, Crystal::Nop.new, "message" + subject = flycheck + subject.source_finished s + subject.output.to_s.should eq "" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr new file mode 100644 index 000000000000..6011e4596fa5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr @@ -0,0 +1,99 @@ +require "../../spec_helper" + +module Ameba + def get_result(sources = [Source.new ""]) + file = IO::Memory.new + formatter = Formatter::JSONFormatter.new file + + formatter.started sources + sources.each { |source| formatter.source_finished source } + formatter.finished sources + + JSON.parse file.to_s + end + + describe Formatter::JSONFormatter do + context "metadata" do + it "shows ameba version" do + get_result["metadata"]["ameba_version"].should eq Ameba::VERSION + end + + it "shows crystal version" do + get_result["metadata"]["crystal_version"].should eq Crystal::VERSION + end + end + + context "sources" do + it "shows path to the source" do + result = get_result [Source.new "", "source.cr"] + result["sources"][0]["path"].should eq "source.cr" + end + + it "shows rule name" do + s = Source.new "" + s.add_issue DummyRule.new, {1, 2}, "message1" + + result = get_result [s] + result["sources"][0]["issues"][0]["rule_name"].should eq DummyRule.rule_name + end + + it "shows severity" do + s = Source.new "" + s.add_issue DummyRule.new, {1, 2}, "message" + + result = get_result [s] + result["sources"][0]["issues"][0]["severity"].should eq "Convention" + end + + it "shows a message" do + s = Source.new "" + s.add_issue DummyRule.new, {1, 2}, "message" + + result = get_result [s] + result["sources"][0]["issues"][0]["message"].should eq "message" + end + + it "shows issue location" do + s = Source.new "" + s.add_issue DummyRule.new, {1, 2}, "message" + + result = get_result [s] + location = result["sources"][0]["issues"][0]["location"] + location["line"].should eq 1 + location["column"].should eq 2 + end + + it "shows issue end_location" do + s = Source.new "" + s.add_issue DummyRule.new, + Crystal::Location.new("path", 3, 3), + Crystal::Location.new("path", 5, 4), + "message" + + result = get_result [s] + end_location = result["sources"][0]["issues"][0]["end_location"] + end_location["line"].should eq 5 + end_location["column"].should eq 4 + end + end + + context "summary" do + it "shows a target sources count" do + result = get_result [Source.new(""), Source.new("")] + result["summary"]["target_sources_count"].should eq 2 + end + + it "shows issues count" do + s1 = Source.new "" + s1.add_issue DummyRule.new, {1, 2}, "message1" + s1.add_issue DummyRule.new, {1, 2}, "message2" + + s2 = Source.new "" + s2.add_issue DummyRule.new, {1, 2}, "message3" + + result = get_result [s1, s2] + result["summary"]["issues_count"].should eq 3 + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr new file mode 100644 index 000000000000..050162afb4d4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr @@ -0,0 +1,127 @@ +require "../../spec_helper" +require "file_utils" + +module Ameba + private def create_todo + formatter = Formatter::TODOFormatter.new IO::Memory.new + s = Source.new "a = 1", "source.cr" + s.add_issue DummyRule.new, {1, 2}, "message" + file = formatter.finished([s]) + file ? File.read(file.path) : "" + end + + describe Formatter::TODOFormatter do + Spec.after_each do + FileUtils.rm(Ameba::Config::PATH) if File.exists?(Ameba::Config::PATH) + end + + context "problems not found" do + it "does not create file" do + formatter = Formatter::TODOFormatter.new IO::Memory.new + file = formatter.finished [Source.new ""] + file.should be_nil + end + + it "reports a message saying file is not created" do + io = IO::Memory.new + formatter = Formatter::TODOFormatter.new io + formatter.finished [Source.new ""] + io.to_s.should contain "No issues found. File is not generated" + end + end + + context "problems found" do + it "prints a message saying file is created" do + io = IO::Memory.new + formatter = Formatter::TODOFormatter.new io + s = Source.new "a = 1", "source.cr" + s.add_issue DummyRule.new, {1, 2}, "message" + formatter.finished([s]) + io.to_s.should contain "Created .ameba.yml" + end + + it "creates a valid YAML document" do + YAML.parse(create_todo).should_not be_nil + end + + it "creates a todo with header" do + create_todo.should contain "# This configuration file was generated by" + end + + it "creates a todo with UTC time" do + create_todo.should match /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC/ + end + + it "creates a todo with version" do + create_todo.should contain "Ameba version #{VERSION}" + end + + it "creates a todo with a rule name" do + create_todo.should contain "DummyRule" + end + + it "creates a todo with severity" do + create_todo.should contain "Convention" + end + + it "creates a todo with problems count" do + create_todo.should contain "Problems found: 1" + end + + it "creates a todo with run details" do + create_todo.should contain "Run `ameba --only #{DummyRule.rule_name}`" + end + + it "excludes source from this rule" do + create_todo.should contain "Excluded:\n - source.cr" + end + + context "with multiple issues" do + formatter = Formatter::TODOFormatter.new IO::Memory.new + + s1 = Source.new "a = 1", "source1.cr" + s2 = Source.new "a = 1", "source2.cr" + s1.add_issue DummyRule.new, {1, 2}, "message1" + s1.add_issue NamedRule.new, {1, 2}, "message1" + s1.add_issue DummyRule.new, {2, 2}, "message1" + s2.add_issue DummyRule.new, {2, 2}, "message2" + + file = formatter.finished([s1, s2]) + content = File.read(file.not_nil!.path) + content.should contain <<-CONTENT + # Problems found: 3 + # Run `ameba --only Ameba/DummyRule` for details + Ameba/DummyRule: + Description: Dummy rule that does nothing. + Excluded: + - source1.cr + - source2.cr + Enabled: true + Severity: Convention + CONTENT + end + + context "when invalid syntax" do + it "does generate todo file" do + formatter = Formatter::TODOFormatter.new IO::Memory.new + s = Source.new "def invalid_syntax" + s.add_issue Rule::Lint::Syntax.new, {1, 2}, "message" + + file = formatter.finished [s] + file.should be_nil + end + + it "prints an error message" do + io = IO::Memory.new + formatter = Formatter::TODOFormatter.new io + s = Source.new "def invalid_syntax" + s.add_issue Rule::Lint::Syntax.new, {1, 2}, "message" + + formatter.finished [s] + io.to_s.should contain "Unable to generate TODO file" + io.to_s.should contain "Please fix syntax issues" + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr new file mode 100644 index 000000000000..e2f95423dee7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr @@ -0,0 +1,29 @@ +require "../../spec_helper" + +module Ameba::Formatter + class Subject + include Util + end + + subject = Subject.new + + describe Util do + describe "#affected_code" do + it "returns nil if there is no such a line number" do + source = Source.new %( + a = 1 + ) + location = Crystal::Location.new("filename", 2, 1) + subject.affected_code(source, location).should be_nil + end + + it "returns correct line if it is found" do + source = Source.new %( + a = 1 + ) + location = Crystal::Location.new("filename", 1, 1) + subject.affected_code(source, location).should eq "> a = 1\n \e[33m^\e[0m" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr new file mode 100644 index 000000000000..413091952cdb --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr @@ -0,0 +1,50 @@ +require "../spec_helper" + +module Ameba + struct GlobUtilsClass + include GlobUtils + end + + subject = GlobUtilsClass.new + current_file_basename = File.basename(__FILE__) + current_file_path = "spec/ameba/#{current_file_basename}" + + describe GlobUtils do + describe "#find_files_by_globs" do + it "returns a file by globs" do + subject.find_files_by_globs(["**/#{current_file_basename}"]) + .should eq [current_file_path] + end + + it "returns files by globs" do + subject.find_files_by_globs(["**/*_spec.cr"]) + .should contain current_file_path + end + + it "doesn't return rejected globs" do + subject + .find_files_by_globs(["**/*_spec.cr", "!**/#{current_file_basename}"]) + .should_not contain current_file_path + end + + it "doesn't return duplicated globs" do + subject + .find_files_by_globs(["**/*_spec.cr", "**/*_spec.cr"]) + .count(current_file_path) + .should eq 1 + end + end + + describe "#expand" do + it "expands globs" do + subject.expand(["**/#{current_file_basename}"]) + .should eq [current_file_path] + end + + it "does not list duplicated files" do + subject.expand(["**/#{current_file_basename}", "**/#{current_file_basename}"]) + .should eq [current_file_path] + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr new file mode 100644 index 000000000000..3f3b2e182561 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr @@ -0,0 +1,161 @@ +require "../spec_helper" + +module Ameba + describe InlineComments do + describe InlineComments::COMMENT_DIRECTIVE_REGEX do + subject = InlineComments::COMMENT_DIRECTIVE_REGEX + + it "allows to parse action and rule name" do + result = subject.match("# ameba:enable Group/RuleName") + result = result.should_not be_nil + result["action"].should eq "enable" + result["rules"].should eq "Group/RuleName" + end + + it "parses multiple rules" do + result = subject.match("# ameba:enable Group/RuleName, OtherRule, Foo/Bar") + result = result.should_not be_nil + result["action"].should eq "enable" + result["rules"].should eq "Group/RuleName, OtherRule, Foo/Bar" + end + + it "fails to parse directives with spaces" do + result = subject.match("# ameba : enable Group/RuleName") + result.should be_nil + end + end + + it "disables a rule with a comment directive" do + s = Source.new %Q( + # ameba:disable #{NamedRule.name} + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") + s.should be_valid + end + + it "disables a rule with a line that ends with a comment directive" do + s = Source.new %Q( + Time.epoch(1483859302) # ameba:disable #{NamedRule.name} + ) + s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") + s.should be_valid + end + + it "does not disable a rule of a different name" do + s = Source.new %Q( + # ameba:disable WrongName + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {2, 12}, message: "Error!") + s.should_not be_valid + end + + it "disables a rule if multiple rule names provided" do + s = Source.new %Q( + # ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {2, 12}, message: "") + s.should be_valid + end + + it "disables a rule if multiple rule names are separated by comma" do + s = Source.new %Q( + # ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {2, 12}, message: "") + s.should be_valid + end + + it "does not disable if multiple rule names used without required one" do + s = Source.new %( + # ameba:disable SomeRule, SomeOtherRule LargeNumbers + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {2, 12}, message: "") + s.should_not be_valid + end + + it "does not disable if comment directive has wrong place" do + s = Source.new %Q( + # ameba:disable #{NamedRule.name} + # + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {3, 12}, message: "") + s.should_not be_valid + end + + it "does not disable if comment directive added to the wrong line" do + s = Source.new %Q( + if use_epoch? # ameba:disable #{NamedRule.name} + Time.epoch(1483859302) + end + ) + s.add_issue(NamedRule.new, location: {3, 12}, message: "") + s.should_not be_valid + end + + it "does not disable if that is not a comment directive" do + s = Source.new %Q( + "ameba:disable #{NamedRule.name}" + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {3, 12}, message: "") + s.should_not be_valid + end + + it "does not disable if that is a commented out directive" do + s = Source.new %Q( + # # ameba:disable #{NamedRule.name} + Time.epoch(1483859302) + ) + s.add_issue(NamedRule.new, location: {3, 12}, message: "") + s.should_not be_valid + end + + it "does not disable if that is an inline commented out directive" do + s = Source.new %Q( + a = 1 # Disable it: # ameba:disable #{NamedRule.name} + ) + s.add_issue(NamedRule.new, location: {2, 12}, message: "") + s.should_not be_valid + end + + context "with group name" do + it "disables one rule with a group" do + s = Source.new %Q( + a = 1 # ameba:disable #{DummyRule.rule_name} + ) + s.add_issue(DummyRule.new, location: {1, 12}, message: "") + s.should be_valid + end + + it "doesn't disable others rules" do + s = Source.new %Q( + a = 1 # ameba:disable #{DummyRule.rule_name} + ) + s.add_issue(NamedRule.new, location: {2, 12}, message: "") + s.should_not be_valid + end + + it "disables a hole group of rules" do + s = Source.new %Q( + a = 1 # ameba:disable #{DummyRule.group_name} + ) + s.add_issue(DummyRule.new, location: {1, 12}, message: "") + s.should be_valid + end + + it "does not disable rules which do not belong to the group" do + s = Source.new %Q( + a = 1 # ameba:disable Lint + ) + s.add_issue(DummyRule.new, location: {2, 12}, message: "") + s.should_not be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr new file mode 100644 index 000000000000..652f52473d02 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr @@ -0,0 +1,50 @@ +require "../spec_helper" + +module Ameba + describe Issue do + it "accepts rule and message" do + issue = Issue.new rule: DummyRule.new, + location: nil, + end_location: nil, + message: "Blah", + status: nil + + issue.rule.should_not be_nil + issue.message.should eq "Blah" + end + + it "accepts location" do + location = Crystal::Location.new("path", 3, 2) + issue = Issue.new rule: DummyRule.new, + location: location, + end_location: nil, + message: "Blah", + status: nil + + issue.location.to_s.should eq location.to_s + issue.end_location.should eq nil + end + + it "accepts end_location" do + location = Crystal::Location.new("path", 3, 2) + issue = Issue.new rule: DummyRule.new, + location: nil, + end_location: location, + message: "Blah", + status: nil + + issue.location.should eq nil + issue.end_location.to_s.should eq location.to_s + end + + it "accepts status" do + issue = Issue.new rule: DummyRule.new, + location: nil, + end_location: nil, + message: "", + status: :enabled + + issue.status.should eq :enabled + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr new file mode 100644 index 000000000000..8d85a55aafc9 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr @@ -0,0 +1,40 @@ +require "../spec_helper" + +module Ameba + describe Reportable do + describe "#add_issue" do + it "adds a new issue for node" do + s = Source.new "", "source.cr" + s.add_issue(DummyRule.new, Crystal::Nop.new, "Error!") + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "" + issue.message.should eq "Error!" + end + + it "adds a new issue by line and column number" do + s = Source.new "", "source.cr" + s.add_issue(DummyRule.new, {23, 2}, "Error!") + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:23:2" + issue.message.should eq "Error!" + end + end + + describe "#valid?" do + it "returns true if no issues added" do + s = Source.new "", "source.cr" + s.should be_valid + end + + it "returns false if there are issues added" do + s = Source.new "", "source.cr" + s.add_issue DummyRule.new, {22, 2}, "ERROR!" + s.should_not be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr new file mode 100644 index 000000000000..166e1e2624a7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr @@ -0,0 +1,32 @@ +require "../../spec_helper" + +module Ameba + describe Rule::Base do + describe "#catch" do + it "accepts and returns source" do + s = Source.new "", "" + DummyRule.new.catch(s).should eq s + end + end + + describe "#name" do + it "returns name of the rule" do + DummyRule.new.name.should eq "Ameba/DummyRule" + end + end + + describe "#group" do + it "returns a group rule belongs to" do + DummyRule.new.group.should eq "Ameba" + end + end + end + + describe Rule do + describe ".rules" do + it "returns a list of all defined rules" do + Rule.rules.includes?(DummyRule).should be_true + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr new file mode 100644 index 000000000000..10392789b39b --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr @@ -0,0 +1,43 @@ +require "../../../spec_helper" + +module Ameba::Rule::Layout + subject = LineLength.new + long_line = "*" * (subject.max_length + 1) + + describe LineLength do + it "passes if all lines are shorter than MaxLength symbols" do + source = Source.new "short line" + subject.catch(source).should be_valid + end + + it "passes if line consists of MaxLength symbols" do + source = Source.new "*" * subject.max_length + subject.catch(source).should be_valid + end + + it "fails if there is at least one line longer than MaxLength symbols" do + source = Source.new long_line + subject.catch(source).should_not be_valid + end + + it "reports rule, pos and message" do + source = Source.new long_line, "source.cr" + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should eq subject + issue.location.to_s.should eq "source.cr:1:#{subject.max_length + 1}" + issue.end_location.should be_nil + issue.message.should eq "Line too long" + end + + context "properties" do + it "allows to configure max length of the line" do + source = Source.new long_line + rule = LineLength.new + rule.max_length = long_line.size + rule.catch(source).should be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr new file mode 100644 index 000000000000..4f42f9a61229 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr @@ -0,0 +1,58 @@ +require "../../../spec_helper" + +module Ameba::Rule::Layout + subject = TrailingBlankLines.new + + describe TrailingBlankLines do + it "passes if there is a blank line at the end of a source" do + source = Source.new "a = 1\n", normalize: false + subject.catch(source).should be_valid + end + + it "passes if source is empty" do + source = Source.new "" + subject.catch(source).should be_valid + end + + it "fails if there is no blank lines at the end" do + source = Source.new "no-blankline" + subject.catch(source).should_not be_valid + end + + it "fails if there more then one blank line at the end of a source" do + source = Source.new "a = 1\n \n", normalize: false + subject.catch(source).should_not be_valid + end + + it "fails if last line is not blank" do + source = Source.new "\n\n\n puts 22", normalize: false + subject.catch(source).should_not be_valid + end + + context "when unnecessary blank line has been detected" do + it "reports rule, pos and message" do + source = Source.new "a = 1\n\n", "source.cr", normalize: false + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:1" + issue.end_location.should be_nil + issue.message.should eq "Excessive trailing newline detected" + end + end + + context "when final line has been missed" do + it "reports rule, pos and message" do + source = Source.new "a = 1", "source.cr", normalize: false + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:0:1" + issue.end_location.should be_nil + issue.message.should eq "Trailing newline missing" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr new file mode 100644 index 000000000000..59c95e25b3b3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr @@ -0,0 +1,28 @@ +require "../../../spec_helper" + +module Ameba::Rule::Layout + subject = TrailingWhitespace.new + + describe TrailingWhitespace do + it "passes if all lines do not have trailing whitespace" do + source = Source.new "no-whispace" + subject.catch(source).should be_valid + end + + it "fails if there is a line with trailing whitespace" do + source = Source.new "whitespace at the end " + subject.catch(source).should_not be_valid + end + + it "reports rule, pos and message" do + source = Source.new "a = 1\n b = 2 ", "source.cr" + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:7" + issue.end_location.should be_nil + issue.message.should eq "Trailing whitespace detected" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr new file mode 100644 index 000000000000..5667982d9637 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr @@ -0,0 +1,66 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe BadDirective do + subject = BadDirective.new + + it "does not report if rule is correct" do + s = Source.new %( + # ameba:disable Lint/BadDirective + ) + subject.catch(s).should be_valid + end + + it "reports if there is incorrect action" do + s = Source.new %( + # ameba:foo Lint/BadDirective + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.message.should eq( + "Bad action in comment directive: 'foo'. Possible values: disable, enable" + ) + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "" + end + + it "reports if there are incorrect rule names" do + s = Source.new %( + # ameba:enable BadRule1, BadRule2 + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.message.should eq( + "Such rules do not exist: BadRule1, BadRule2" + ) + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "" + end + + it "does not report if there no action and rules at all" do + s = Source.new %( + # ameba: + ) + subject.catch(s).should be_valid + end + + it "does not report if there are no rules" do + s = Source.new %( + # ameba:enable + # ameba:disable + ) + subject.catch(s).should be_valid + end + + it "does not report if there are group names in the directive" do + s = Source.new %( + # ameba:disable Style Performance + ) + subject.catch(s).should be_valid + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr new file mode 100644 index 000000000000..da737643c657 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr @@ -0,0 +1,114 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = ComparisonToBoolean.new + + describe ComparisonToBoolean do + it "passes if there is no comparison to boolean" do + source = Source.new %( + a = true + + if a + :ok + end + + if true + :ok + end + + unless s.empty? + :ok + end + + :ok if a + + :ok if a != 1 + + :ok if a == "true" + + case a + when true + :ok + when false + :not_ok + end + ) + subject.catch(source).should be_valid + end + + context "boolean on the right" do + it "fails if there is == comparison to boolean" do + source = Source.new %( + if s.empty? == true + :ok + end + ) + subject.catch(source).should_not be_valid + end + + it "fails if there is != comparison to boolean" do + source = Source.new %( + if a != false + :ok + end + ) + subject.catch(source).should_not be_valid + end + + it "fails if there is case comparison to boolean" do + source = Source.new %( + a === true + ) + subject.catch(source).should_not be_valid + end + + it "reports rule, pos and message" do + source = Source.new "a != true", "source.cr" + subject.catch(source) + + issue = source.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.message.should eq "Comparison to a boolean is pointless" + end + end + + context "boolean on the left" do + it "fails if there is == comparison to boolean" do + source = Source.new %( + if true == s.empty? + :ok + end + ) + subject.catch(source).should_not be_valid + end + + it "fails if there is != comparison to boolean" do + source = Source.new %( + if false != a + :ok + end + ) + subject.catch(source).should_not be_valid + end + + it "fails if there is case comparison to boolean" do + source = Source.new %( + true === a + ) + subject.catch(source).should_not be_valid + end + + it "reports rule, pos and message" do + source = Source.new "true != a", "source.cr" + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:9" + issue.message.should eq "Comparison to a boolean is pointless" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr new file mode 100644 index 000000000000..ff29e001744d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr @@ -0,0 +1,45 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = DebuggerStatement.new + + describe DebuggerStatement do + it "passes if there is no debugger statement" do + s = Source.new %( + "this is not a debugger statement" + s = "debugger" + + def debugger(program) + end + debugger "" + + class A + def debugger + end + end + A.new.debugger + ) + subject.catch(s).should be_valid + end + + it "fails if there is a debugger statement" do + s = Source.new %( + a = 2 + debugger + a = a + 1 + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, pos and message" do + s = Source.new "debugger", "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:8" + issue.message.should eq "Possible forgotten debugger statement detected" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr new file mode 100644 index 000000000000..bba7a6d88289 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr @@ -0,0 +1,69 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe EmptyEnsure do + subject = EmptyEnsure.new + + it "passes if there is no empty ensure blocks" do + s = Source.new %( + def some_method + do_some_stuff + ensure + do_something_else + end + + begin + do_some_stuff + ensure + do_something_else + end + + def method_with_rescue + rescue + ensure + nil + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is an empty ensure in method" do + s = Source.new %( + def method + do_some_stuff + ensure + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is an empty ensure in a block" do + s = Source.new %( + begin + do_some_stuff + ensure + # nothing here + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, pos and message" do + s = Source.new %( + begin + do_some_stuff + rescue + do_some_other_stuff + ensure + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:3" + issue.end_location.to_s.should eq "source.cr:6:3" + issue.message.should eq "Empty `ensure` block detected" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr new file mode 100644 index 000000000000..f757321acbe3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr @@ -0,0 +1,127 @@ +require "../../../spec_helper" + +module Ameba + subject = Rule::Lint::EmptyExpression.new + + def it_detects_empty_expression(code) + it "detects empty expression" do + s = Source.new code + rule = Rule::Lint::EmptyExpression.new + rule.catch(s).should_not be_valid + end + end + + describe Rule::Lint::EmptyExpression do + it "passes if there is no empty expression" do + s = Source.new %( + def method() + end + + method() + method(1, 2, 3) + method(nil) + + a = nil + a = "" + a = 0 + + nil + :any.nil? + + begin "" end + [nil] << nil + ) + subject.catch(s).should be_valid + end + + it_detects_empty_expression %(()) + it_detects_empty_expression %(((()))) + it_detects_empty_expression %(a = ()) + it_detects_empty_expression %((();())) + it_detects_empty_expression %(if (); end) + it_detects_empty_expression %( + if foo + 1 + elsif () + 2 + end + ) + it_detects_empty_expression %( + case foo + when :foo then () + end + ) + it_detects_empty_expression %( + case foo + when :foo then 1 + else + () + end + ) + it_detects_empty_expression %( + case foo + when () then 1 + end + ) + it_detects_empty_expression %( + def method + a = 1 + () + end + ) + it_detects_empty_expression %( + def method + rescue + () + end + ) + it_detects_empty_expression %( + def method + begin + end + end + ) + it_detects_empty_expression %( + begin; end + ) + it_detects_empty_expression %( + begin + nil + end + ) + it_detects_empty_expression %( + begin + () + end + ) + + it "does not report emtpy expression in macro" do + s = Source.new %q( + module MyModule + macro conditional_error_for_inline_callbacks + \{% + raise "" + %} + end + + macro before_save(x = nil) + end + end + ) + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + if () + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:4" + issue.end_location.to_s.should eq "source.cr:1:5" + issue.message.should eq "Avoid empty expressions" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr new file mode 100644 index 000000000000..26f63fbaf2cb --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr @@ -0,0 +1,88 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe EmptyLoop do + subject = EmptyLoop.new + + it "does not report if there are not empty loops" do + s = Source.new %( + a = 1 + + while a < 10 + a += 1 + end + + until a == 10 + a += 1 + end + + loop do + a += 1 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is an empty while loop" do + s = Source.new %( + a = 1 + while true + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if while loop has non-literals in cond block" do + s = Source.new %( + a = 1 + while a = gets.to_s + # nothing here + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is an empty until loop" do + s = Source.new %( + do_something + until false + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if until loop has non-literals in cond block" do + s = Source.new %( + until socket_open? + end + ) + subject.catch(s).should be_valid + end + + it "reports if there an empty loop" do + s = Source.new %( + a = 1 + loop do + + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, message and location" do + s = Source.new %( + a = 1 + loop do + # comment goes here + end + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:1" + issue.end_location.to_s.should eq "source.cr:4:3" + issue.message.should eq EmptyLoop::MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr new file mode 100644 index 000000000000..43b5a4475943 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr @@ -0,0 +1,51 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe HashDuplicatedKey do + subject = HashDuplicatedKey.new + + it "passes if there is no duplicated keys in a hash literals" do + s = Source.new %( + h = {"a" => 1, :a => 2, "b" => 3} + h = {"a" => 1, "b" => 2, "c" => {"a" => 3, "b" => 4}} + h = {} of String => String + ) + subject.catch(s).should be_valid + end + + it "fails if there is a duplicated key in a hash literal" do + s = Source.new %q( + h = {"a" => 1, "b" => 2, "a" => 3} + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is a duplicated key in the inner hash literal" do + s = Source.new %q( + h = {"a" => 1, "b" => {"a" => 3, "b" => 4, "a" => 5}} + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, location and message" do + s = Source.new %q( + h = {"a" => 1, "a" => 2} + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:5" + issue.end_location.to_s.should eq "source.cr:1:24" + issue.message.should eq %(Duplicated keys in hash literal: "a") + end + + it "reports multiple duplicated keys" do + s = Source.new %q( + h = {"key1" => 1, "key1" => 2, "key2" => 3, "key2" => 4} + ) + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.message.should eq %(Duplicated keys in hash literal: "key1", "key2") + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr new file mode 100644 index 000000000000..8150ad2bd8ce --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr @@ -0,0 +1,179 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = LiteralInCondition.new + + describe LiteralInCondition do + it "passes if there is not literals in conditional" do + s = Source.new %( + if a == 2 + :ok + end + + :ok unless b + + case string + when "a" + :ok + when "b" + :ok + end + + unless a.nil? + :ok + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is a predicate in if conditional" do + s = Source.new %( + if "string" + :ok + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is a predicate in unless conditional" do + s = Source.new %( + unless true + :ok + end + ) + subject.catch(s).should_not be_valid + end + + describe "range" do + it "reports range with literals" do + s = Source.new %( + case 1..2 + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report range with non-literals" do + s = Source.new %( + case (1..a) + end + ) + subject.catch(s).should be_valid + end + end + + describe "array" do + it "reports array with literals" do + s = Source.new %( + case [1, 2, 3] + when :array + :ok + when :not_array + :also_ok + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report array with non-literals" do + s = Source.new %( + a, b = 1, 2 + case [1, 2, a] + when :array + :ok + when :not_array + :also_ok + end + ) + subject.catch(s).should be_valid + end + end + + describe "hash" do + it "reports hash with literals" do + s = Source.new %( + case { "name" => 1, 33 => 'b' } + when :hash + :ok + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report hash with non-literals in keys" do + s = Source.new %( + case { a => 1, 33 => 'b' } + when :hash + :ok + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report hash with non-literals in values" do + s = Source.new %( + case { "name" => a, 33 => 'b' } + when :hash + :ok + end + ) + subject.catch(s).should be_valid + end + end + + describe "tuple" do + it "reports tuple with literals" do + s = Source.new %( + case {1, false} + when {1, _} + :ok + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report tuple with non-literals" do + s = Source.new %( + a, b = 1, 2 + case {1, b} + when {1, 2} + :ok + end + ) + subject.catch(s).should be_valid + end + end + + describe "named tuple" do + it "reports named tuple with literals" do + s = Source.new %( + case { name: 1, foo: :bar} + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report named tuple with non-literals" do + s = Source.new %( + case { name: a, foo: :bar} + end + ) + subject.catch(s).should be_valid + end + end + + it "reports rule, pos and message" do + s = Source.new %( + puts "hello" if true + ), "source.cr" + subject.catch(s).should_not be_valid + + s.issues.size.should eq 1 + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:20" + issue.message.should eq "Literal value found in conditional" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr new file mode 100644 index 000000000000..218d94d78375 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr @@ -0,0 +1,51 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = LiteralInInterpolation.new + + describe LiteralInInterpolation do + it "passes with good interpolation examples" do + s = Source.new %q( + name = "Ary" + "Hello, #{name}" + + "#{name}" + + "Name size: #{name.size}" + ) + subject.catch(s).should be_valid + end + + it "fails if there is useless interpolation" do + [ + %q("#{:Ary}"), + %q("#{[1, 2, 3]}"), + %q("#{true}"), + %q("#{false}"), + %q("here are #{4} cats"), + ].each do |str| + subject.catch(Source.new str).should_not be_valid + end + end + + it "reports rule, pos and message" do + s = Source.new %q( + "Hello, #{:world} from #{:ameba}" + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:16" + issue.message.should eq "Literal value found in interpolation" + + issue = s.issues.last + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:26" + issue.end_location.to_s.should eq "source.cr:1:31" + issue.message.should eq "Literal value found in interpolation" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr new file mode 100644 index 000000000000..42c46272ff18 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr @@ -0,0 +1,86 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe PercentArrays do + subject = PercentArrays.new + + it "passes if percent arrays are written correctly" do + s = Source.new %q( + %i(one two three) + %w(one two three) + + %i(1 2 3) + %w(1 2 3) + + %i() + %w() + ) + subject.catch(s).should be_valid + end + + it "fails if string percent array has commas" do + s = Source.new %( %w(one, two) ) + subject.catch(s).should_not be_valid + end + + it "fails if string percent array has quotes" do + s = Source.new %( %w("one" "two") ) + subject.catch(s).should_not be_valid + end + + it "fails if symbols percent array has commas" do + s = Source.new %( %i(one, two) ) + subject.catch(s).should_not be_valid + end + + it "fails if symbols percent array has a colon" do + s = Source.new %( %i(:one :two) ) + subject.catch(s).should_not be_valid + end + + it "reports rule, location and message for %i" do + s = Source.new %( + %i(:one) + ), "source.cr" + + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.message.should eq( + "Symbols `,:` may be unwanted in %i array literals" + ) + end + + it "reports rule, location and message for %w" do + s = Source.new %( + %w("one") + ), "source.cr" + + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.should be_nil + issue.message.should eq( + "Symbols `,\"` may be unwanted in %w array literals" + ) + end + + context "properties" do + it "allows to configure string_array_unwanted_symbols" do + rule = PercentArrays.new + rule.string_array_unwanted_symbols = "," + s = Source.new %( %w("one") ) + rule.catch(s).should be_valid + end + + it "allows to configure symbol_array_unwanted_symbols" do + rule = PercentArrays.new + rule.symbol_array_unwanted_symbols = "," + s = Source.new %( %i(:one) ) + rule.catch(s).should be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr new file mode 100644 index 000000000000..a86d30f2780b --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr @@ -0,0 +1,37 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe RandZero do + subject = RandZero.new + + it "passes if it is not rand(1) or rand(0)" do + s = Source.new %( + rand(1.0) + rand(0.11) + rand(2) + ) + subject.catch(s).should be_valid + end + + it "fails if it is rand(0)" do + s = Source.new "rand(0)" + subject.catch(s).should_not be_valid + end + + it "fails if it is rand(1)" do + s = Source.new "rand(1)" + subject.catch(s).should_not be_valid + end + + it "reports rule, location and a message" do + s = Source.new "rand(1)", "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:7" + issue.message.should eq "rand(1) always returns 0" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr new file mode 100644 index 000000000000..cacb74e04a14 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr @@ -0,0 +1,97 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe RedundantStringCoercion do + subject = RedundantStringCoercion.new + + it "does not report if there is no redundant string coersion" do + s = Source.new %( + "Hello, #{name}" + ) + subject.catch(s).should be_valid + end + + it "reports if there is a redundant string coersion" do + s = Source.new %q( + "Hello, #{name.to_s}" + ) + subject.catch(s).should_not be_valid + end + + it "does not report if coersion is used in binary op" do + s = Source.new %q( + "Hello, #{3.to_s + 's'}" + ) + subject.catch(s).should be_valid + end + + it "reports if coercion is used with symbol literals" do + s = Source.new %q("Hello, #{:symbol.to_s}") + subject.catch(s).should_not be_valid + end + + it "reports if coercion is used with number literals" do + s = Source.new %q("Hello, #{42.to_s}") + subject.catch(s).should_not be_valid + end + + it "reports if coercion is used with boolean literals" do + s = Source.new %q("Hello, #{false.to_s}") + subject.catch(s).should_not be_valid + end + + it "reports if coercion is used with char literals" do + s = Source.new %q("Hello, #{'t'.to_s}") + subject.catch(s).should_not be_valid + end + + it "reports redundant coercion in regex" do + s = Source.new %q( + /\w #{name.to_s}/ + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if Object#to_s is called with arguments" do + s = Source.new %q( + /\w #{name.to_s(io)}/ + ) + subject.catch(s).should be_valid + end + + it "doesn't report if Object#to_s is called without receiver" do + s = Source.new %q( + /\w #{to_s}/ + ) + subject.catch(s).should be_valid + end + + it "doesn't report if Object#to_s is called with named args" do + s = Source.new %q( + "0x#{250.to_s base: 16}" + ) + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + s = Source.new %q( + "Hello, #{name1.to_s}" + "Hello, #{name2.to_s}" + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + + issue = s.issues[0] + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:17" + issue.end_location.to_s.should eq "source.cr:1:20" + issue.message.should eq RedundantStringCoercion::MSG + + issue = s.issues[1] + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:17" + issue.end_location.to_s.should eq "source.cr:2:20" + issue.message.should eq RedundantStringCoercion::MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr new file mode 100644 index 000000000000..494ea4eff9e8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr @@ -0,0 +1,163 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe RedundantWithIndex do + subject = RedundantWithIndex.new + + context "with_index" do + it "does not report if there is index argument" do + s = Source.new %( + collection.each.with_index do |e, i| + e += i + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is not index argument" do + s = Source.new %( + collection.each.with_index do |e| + e += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is underscored index argument" do + s = Source.new %( + collection.each.with_index do |e, _| + e += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is no args" do + s = Source.new %( + collection.each.with_index do + puts :nothing + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if there is no block" do + s = Source.new %( + collection.each.with_index + ) + subject.catch(s).should be_valid + end + + it "does not report if first argument is underscored" do + s = Source.new %( + collection.each.with_index do |_, i| + puts i + end + ) + subject.catch(s).should be_valid + end + + it "does not report if there are more than 2 args" do + s = Source.new %( + tup.each.with_index do |key, value, index| + puts i + end + ) + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + def valid? + collection.each.with_index do |e| + end + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:19" + issue.end_location.to_s.should eq "source.cr:2:29" + issue.message.should eq "Remove redundant with_index" + end + end + + context "each_with_index" do + it "does not report if there is index argument" do + s = Source.new %( + collection.each_with_index do |e, i| + e += i + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is not index argument" do + s = Source.new %( + collection.each_with_index do |e| + e += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is underscored index argument" do + s = Source.new %( + collection.each_with_index do |e, _| + e += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is no args" do + s = Source.new %( + collection.each_with_index do + puts :nothing + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if there is no block" do + s = Source.new %( + collection.each_with_index(1) + ) + subject.catch(s).should be_valid + end + + it "does not report if first argument is underscored" do + s = Source.new %( + collection.each_with_index do |_, i| + puts i + end + ) + subject.catch(s).should be_valid + end + + it "does not report if there are more than 2 args" do + s = Source.new %( + tup.each_with_index do |key, value, index| + puts i + end + ) + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + def valid? + collection.each_with_index do |e| + end + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:14" + issue.end_location.to_s.should eq "source.cr:2:29" + issue.message.should eq "Use each instead of each_with_index" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr new file mode 100644 index 000000000000..f6adc130ec94 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr @@ -0,0 +1,83 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe RedundantWithObject do + subject = RedundantWithObject.new + + it "does not report if there is index argument" do + s = Source.new %( + collection.each_with_object(0) do |e, obj| + obj += i + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is not index argument" do + s = Source.new %( + collection.each_with_object(0) do |e| + e += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is underscored index argument" do + s = Source.new %( + collection.each_with_object(0) do |e, _| + e += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is no args" do + s = Source.new %( + collection.each_with_object(0) do + puts :nothing + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if there is no block" do + s = Source.new %( + collection.each_with_object(0) + ) + subject.catch(s).should be_valid + end + + it "does not report if first argument is underscored" do + s = Source.new %( + collection.each_with_object(0) do |_, obj| + puts i + end + ) + subject.catch(s).should be_valid + end + + it "does not report if there are more than 2 args" do + s = Source.new %( + tup.each_with_object(0) do |key, value, obj| + puts i + end + ) + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + def valid? + collection.each_with_object(0) do |e| + end + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:14" + issue.end_location.to_s.should eq "source.cr:2:30" + issue.message.should eq "Use each instead of each_with_object" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr new file mode 100644 index 000000000000..540f83d8cd11 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr @@ -0,0 +1,166 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe ShadowedArgument do + subject = ShadowedArgument.new + + it "doesn't report if there is not a shadowed argument" do + s = Source.new %( + def foo(bar) + baz = 1 + end + + 3.times do |i| + a = 1 + end + + proc = -> (a : Int32) { + b = 2 + } + ) + subject.catch(s).should be_valid + end + + it "reports if there is a shadowed method argument" do + s = Source.new %( + def foo(bar) + bar = 1 + bar + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is a shadowed block argument" do + s = Source.new %( + 3.times do |i| + i = 2 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is a shadowed proc argument" do + s = Source.new %( + ->(x : Int32) { + x = 20 + x + } + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if the argument is referenced before the assignment" do + s = Source.new %( + def foo(bar) + bar + bar = 1 + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if the argument is conditionally reassigned" do + s = Source.new %( + def foo(bar = nil) + bar ||= true + bar + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if the op assign is followed by another assignment" do + s = Source.new %( + def foo(bar) + bar ||= 3 + bar = 43 + bar + end + ) + subject.catch(s).should be_valid + end + + it "reports if the shadowing assignment is followed by op assign" do + s = Source.new %( + def foo(bar) + bar = 42 + bar ||= 43 + bar + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if the argument is unused" do + s = Source.new %( + def foo(bar) + end + ) + subject.catch(s).should be_valid + end + + it "reports if the argument is shadowed before super" do + s = Source.new %( + def foo(bar) + bar = 1 + super + end + ) + subject.catch(s).should_not be_valid + end + + context "branch" do + it "doesn't report if the argument is not shadowed in a condition" do + s = Source.new %( + def foo(bar, baz) + bar = 1 if baz + bar + end + ) + subject.catch(s).should be_valid + end + + it "reports if the argument is shadowed after the condition" do + s = Source.new %( + def foo(foo) + if something + foo = 42 + end + foo = 43 + foo + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if the argument is conditionally assigned in a branch" do + s = Source.new %( + def foo(bar) + if something + bar ||= 22 + end + bar + end + ) + subject.catch(s).should be_valid + end + end + + it "reports rule, location and message" do + s = Source.new %( + def foo(bar) + bar = 22 + bar + end + ), "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:3" + issue.end_location.to_s.should eq "source.cr:2:10" + issue.message.should eq "Argument `bar` is assigned before it is used" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr new file mode 100644 index 000000000000..5b7d49330b82 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr @@ -0,0 +1,176 @@ +require "../../../spec_helper" + +private def check_shadowed(source, exceptions) + s = Ameba::Source.new source + Ameba::Rule::Lint::ShadowedException.new.catch(s).should_not be_valid + s.issues.first.message.should contain exceptions.join(", ") +end + +module Ameba::Rule::Lint + describe ShadowedException do + subject = ShadowedException.new + + it "passes if there isn't shadowed exception" do + s = Source.new %( + def method + do_something + rescue ArgumentError + handle_argument_error_exception + rescue Exception + handle_exception + end + + def method + rescue Exception + handle_exception + end + + def method + rescue e : ArgumentError + handle_argument_error_exception + rescue e : Exception + handle_exception + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is a shadowed exception" do + check_shadowed %( + begin + do_something + rescue Exception + handle_exception + rescue ArgumentError + handle_argument_error_exception + end + ), %w(ArgumentError) + end + + it "fails if there is a custom shadowed exceptions" do + check_shadowed %( + begin + 1 + rescue Exception + 2 + rescue MySuperException + 3 + end + ), %w(MySuperException) + end + + it "fails if there is a shadowed exception in a type list" do + check_shadowed %( + begin + rescue Exception | IndexError + end + ), %w(IndexError) + end + + it "fails if there is a first shadowed exception in a type list" do + check_shadowed %( + begin + rescue IndexError | Exception + rescue Exception + rescue + end + ), %w(IndexError) + end + + it "fails if there is a shadowed duplicated exception" do + check_shadowed %( + begin + rescue IndexError + rescue ArgumentError + rescue IndexError + end + ), %w(IndexError) + end + + it "fails if there is a shadowed duplicated exception in a type list" do + check_shadowed %( + begin + rescue IndexError + rescue ArgumentError | IndexError + end + ), %w(IndexError) + end + + it "fails if there is only shadowed duplicated exceptions" do + check_shadowed %( + begin + rescue IndexError + rescue IndexError + end + ), %w(IndexError) + end + + it "fails if there is only shadowed duplicated exceptions in a type list" do + check_shadowed %( + begin + rescue IndexError | IndexError + end + ), %w(IndexError) + end + + it "fails if all rescues are shadowed and there is a catch-all rescue" do + check_shadowed %( + begin + rescue Exception + rescue ArgumentError + rescue IndexError + rescue KeyError | IO::Error + rescue + end + ), %w(IndexError KeyError IO::Error) + end + + it "fails if there are shadowed exception with args" do + check_shadowed %( + begin + rescue Exception + rescue ex : IndexError + rescue + end + ), %w(IndexError) + end + + it "fails if there are multiple shadowed exceptions" do + check_shadowed %( + begin + rescue Exception + rescue ArgumentError + rescue IndexError + end + ), %w(ArgumentError IndexError) + end + + it "fails if there are multiple shadowed exceptions in a type list" do + check_shadowed %( + begin + rescue Exception + rescue ArgumentError | IndexError + rescue IO::Error + end + ), %w(ArgumentError IndexError IO::Error) + end + + it "reports rule, location and a message" do + s = Source.new %q( + begin + do_something + rescue Exception | IndexError + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:3" + issue.end_location.to_s.should eq "source.cr:4:3" + issue.message.should eq( + "Exception handler has shadowed exceptions: IndexError" + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr new file mode 100644 index 000000000000..2ed9112ddef4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr @@ -0,0 +1,241 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe ShadowingOuterLocalVar do + subject = ShadowingOuterLocalVar.new + + it "doesn't report if there is no shadowing" do + source = Source.new %( + def some_method + foo = 1 + + 3.times do |bar| + bar + end + + -> (baz : Int32) {} + + -> (bar : String) {} + end + ) + + subject.catch(source).should be_valid + end + + it "reports if there is a shadowing in a block" do + source = Source.new %( + def some_method + foo = 1 + + 3.times do |foo| + end + end + ) + subject.catch(source).should_not be_valid + end + + it "does not report outer vars declared below shadowed block" do + source = Source.new %( + methods = klass.methods.select { |m| m.annotation(MyAnn) } + m = methods.last + ) + subject.catch(source).should be_valid + end + + it "reports if there is a shadowing in a proc" do + source = Source.new %( + def some_method + foo = 1 + + -> (foo : Int32) {} + end + ) + subject.catch(source).should_not be_valid + end + + it "reports if there is a shadowing in an inner scope" do + source = Source.new %( + def foo + foo = 1 + + 3.times do |i| + 3.times { |foo| foo } + end + end + ) + subject.catch(source).should_not be_valid + end + + it "reports if variable is shadowed twice" do + source = Source.new %( + foo = 1 + + 3.times do |foo| + -> (foo : Int32) { foo + 1 } + end + ) + subject.catch(source).should_not be_valid + + source.issues.size.should eq 2 + end + + it "reports if a splat block argument shadows local var" do + source = Source.new %( + foo = 1 + + 3.times do |*foo| + end + ) + subject.catch(source).should_not be_valid + end + + it "reports if a &block argument is shadowed" do + source = Source.new %( + def method_with_block(a, &block) + 3.times do |block| + end + end + ) + subject.catch(source).should_not be_valid + source.issues.first.message.should eq "Shadowing outer local variable `block`" + end + + it "reports if there are multiple args and one shadows local var" do + source = Source.new %( + foo = 1 + [1, 2, 3].each_with_index do |i, foo| + i + foo + end + ) + subject.catch(source).should_not be_valid + source.issues.first.message.should eq "Shadowing outer local variable `foo`" + end + + it "doesn't report if an outer var is reassigned in a block" do + source = Source.new %( + def foo + foo = 1 + 3.times do |i| + foo = 2 + end + end + ) + subject.catch(source).should be_valid + end + + it "doesn't report if an argument is a black hole '_'" do + source = Source.new %( + _ = 1 + 3.times do |_| + end + ) + subject.catch(source).should be_valid + end + + it "doesn't report if it shadows record type declaration" do + source = Source.new %( + class FooBar + record Foo, index : String + + def bar + 3.times do |index| + end + end + end + ) + subject.catch(source).should be_valid + end + + it "doesn't report if it shadows throwaway arguments" do + source = Source.new %( + data = [{1, "a"}, {2, "b"}, {3, "c"}] + + data.each do |_, string| + data.each do |number, _| + puts string, number + end + end + ) + subject.catch(source).should be_valid + end + + it "does not report if argument shadows an ivar assignment" do + s = Source.new %( + def bar(@foo) + @foo.try do |foo| + end + end + ) + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + source = Source.new %( + foo = 1 + 3.times { |foo| foo + 1 } + ), "source.cr" + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:12" + issue.end_location.should be_nil + issue.message.should eq "Shadowing outer local variable `foo`" + end + + context "macro" do + it "does not report shadowed vars in outer scope" do + source = Source.new %( + macro included + def foo + {% for ivar in instance_vars %} + {% ann = ivar.annotation(Name) %} + {% end %} + end + + def bar + {% instance_vars.reject { |ivar| ivar } %} + end + end + ) + subject.catch(source).should be_valid + end + + it "does not report shadowed vars in macro withing the same scope" do + source = Source.new %( + {% methods = klass.methods.select { |m| m.annotation(MyAnn) } %} + + {% for m, m_idx in methods %} + {% if d = m.annotation(MyAnn) %} + {% d %} + {% end %} + {% end %} + ) + subject.catch(source).should be_valid + end + + it "does not report shadowed vars withing nested macro" do + source = Source.new %( + module Foo + macro included + def foo + {% for ann in instance_vars %} + {% pos_args = ann.args.empty? ? "Tuple.new".id : ann.args %} + {% end %} + end + + def bar + {{@type.instance_vars.map do |ivar| + ivar.annotations(Name).each do |ann| + puts ann.args + end + end}} + end + end + end + ) + subject.catch(source).should be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr new file mode 100644 index 000000000000..c1179dc36514 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr @@ -0,0 +1,236 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe SharedVarInFiber do + subject = SharedVarInFiber.new + + it "doesn't report if there is only local shared var in fiber" do + s = Source.new %( + spawn do + i = 1 + puts i + end + + Fiber.yield + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is only block shared var in fiber" do + s = Source.new %( + 10.times do |i| + spawn do + puts i + end + end + + Fiber.yield + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there a spawn macro is used" do + s = Source.new %( + i = 0 + while i < 10 + spawn puts(i) + i += 1 + end + + Fiber.yield + ) + subject.catch(s).should be_valid + end + + it "reports if there is a shared var in spawn" do + s = Source.new %( + i = 0 + while i < 10 + spawn do + puts(i) + end + i += 1 + end + + Fiber.yield + ) + subject.catch(s).should_not be_valid + end + + it "reports reassigned reference to shared var in spawn" do + s = Source.new %( + channel = Channel(String).new + n = 0 + + while n < 10 + n = n + 1 + spawn do + m = n + channel.send m + end + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report reassigned reference to shared var in block" do + s = Source.new %( + channel = Channel(String).new + n = 0 + + while n < 3 + n = n + 1 + m = n + spawn do + channel.send m + end + end + ) + subject.catch(s).should be_valid + end + + it "does not report block is called in a spawn" do + s = Source.new %( + def method(block) + spawn do + block.call(10) + end + end + ) + subject.catch(s).should be_valid + end + + it "reports multiple shared variables in spawn" do + s = Source.new %( + foo, bar, baz = 0, 0, 0 + while foo < 10 + baz += 1 + spawn do + puts foo + puts foo + bar + baz + end + foo += 1 + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 3 + s.issues[0].location.to_s.should eq ":5:10" + s.issues[0].end_location.to_s.should eq ":5:12" + s.issues[0].message.should eq "Shared variable `foo` is used in fiber" + + s.issues[1].location.to_s.should eq ":6:10" + s.issues[1].end_location.to_s.should eq ":6:12" + s.issues[1].message.should eq "Shared variable `foo` is used in fiber" + + s.issues[2].location.to_s.should eq ":6:22" + s.issues[2].end_location.to_s.should eq ":6:24" + s.issues[2].message.should eq "Shared variable `baz` is used in fiber" + end + + it "doesn't report if variable is passed to the proc" do + s = Source.new %( + i = 0 + while i < 10 + proc = ->(x : Int32) do + spawn do + puts(x) + end + end + proc.call(i) + i += 1 + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if a channel is declared in outer scope" do + s = Source.new %( + channel = Channel(Nil).new + spawn { channel.send(nil) } + channel.receive + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is a loop in spawn" do + s = Source.new %( + channel = Channel(String).new + + spawn do + server = TCPServer.new("0.0.0.0", 8080) + socket = server.accept + while line = socket.gets + channel.send(line) + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if a var is mutated in spawn and referenced outside" do + s = Source.new %( + def method + foo = 1 + spawn { foo = 2 } + foo + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if variable is changed without iterations" do + s = Source.new %( + def foo + i = 0 + i += 1 + spawn { i } + end + ), "source.cr" + + subject.catch(s).should be_valid + end + + it "doesn't report if variable is in a loop inside spawn" do + s = Source.new %( + i = 0 + spawn do + while i < 10 + i += 1 + end + end + ) + + subject.catch(s).should be_valid + end + + it "doesn't report if variable declared inside loop" do + s = Source.new %( + while true + i = 0 + spawn { i += 1 } + end + ) + + subject.catch(s).should be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + i = 0 + while true + i += 1 + spawn { i } + end + ), "source.cr" + + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:4:11" + issue.end_location.to_s.should eq "source.cr:4:11" + issue.message.should eq "Shared variable `i` is used in fiber" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr new file mode 100644 index 000000000000..f7402e760698 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr @@ -0,0 +1,41 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe Syntax do + subject = Syntax.new + + it "passes if there is no invalid syntax" do + s = Source.new %( + def hello + puts "totally valid" + rescue e: Exception + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is an invalid syntax" do + s = Source.new %( + def hello + puts "invalid" + rescue Exception => e + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, location and message" do + s = Source.new "def hello end", "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.message.should eq "unexpected token: end (expected ';' or newline)" + end + + it "has highest severity" do + subject.severity.should eq Severity::Error + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr new file mode 100644 index 000000000000..cc21a00f35db --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr @@ -0,0 +1,102 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe UnneededDisableDirective do + subject = UnneededDisableDirective.new + + it "passes if there are no comments" do + s = Source.new %( + a = 1 + ) + subject.catch(s).should be_valid + end + + it "passes if there is disable directive" do + s = Source.new %( + a = 1 # my super var + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is disable directive and it is needed" do + s = Source.new %Q( + # ameba:disable #{NamedRule.name} + a = 1 + ) + s.add_issue NamedRule.new, location: {2, 1}, + message: "Useless assignment", status: :disabled + subject.catch(s).should be_valid + end + + it "passes if there is inline disable directive and it is needed" do + s = Source.new %Q( + a = 1 # ameba:disable #{NamedRule.name} + ) + s.add_issue NamedRule.new, location: {1, 1}, + message: "Alarm!", status: :disabled + subject.catch(s).should be_valid + end + + it "ignores commented out disable directive" do + s = Source.new %Q( + # # ameba:disable #{NamedRule.name} + a = 1 + ) + s.add_issue NamedRule.new, location: {3, 1}, + message: "Alarm!", status: :disabled + subject.catch(s).should be_valid + end + + it "fails if there is unneeded directive" do + s = Source.new %Q( + # ameba:disable #{NamedRule.name} + a = 1 + ) + subject.catch(s).should_not be_valid + s.issues.first.message.should eq( + "Unnecessary disabling of #{NamedRule.name}" + ) + end + + it "fails if there is inline unneeded directive" do + s = Source.new %Q(a = 1 # ameba:disable #{NamedRule.name}) + subject.catch(s).should_not be_valid + s.issues.first.message.should eq( + "Unnecessary disabling of #{NamedRule.name}" + ) + end + + it "detects mixed inline directives" do + s = Source.new %Q( + # ameba:disable Rule1, Rule2 + a = 1 # ameba:disable Rule3 + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.message.should contain "Rule1, Rule2" + s.issues.last.message.should contain "Rule3" + end + + it "fails if there is disabled UnneededDisableDirective" do + s = Source.new %Q( + # ameba:disable #{UnneededDisableDirective.rule_name} + a = 1 + ), "source.cr" + s.add_issue UnneededDisableDirective.new, location: {3, 1}, + message: "Alarm!", status: :disabled + subject.catch(s).should_not be_valid + end + + it "reports issue, location and message" do + s = Source.new %Q( + # ameba:disable Rule1, Rule2 + a = 1 + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.message.should eq "Unnecessary disabling of Rule1, Rule2" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr new file mode 100644 index 000000000000..5e50d05fca04 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr @@ -0,0 +1,701 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = UnreachableCode.new + + describe UnreachableCode do + context "return" do + it "reports if there is unreachable code after return" do + s = Source.new %( + def foo + a = 1 + return false + b = 2 + end + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":4:3" + end + + it "doesn't report if there is return in if" do + s = Source.new %( + def foo + a = 1 + return false if bar + b = 2 + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there are returns in if-then-else" do + s = Source.new %( + if a > 0 + return :positive + else + return :negative + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is no else in if" do + s = Source.new %( + if a > 0 + return :positive + end + :reachable + ) + subject.catch(s).should be_valid + end + + it "doesn't report return in on-line if" do + s = Source.new %( + return :positive if a > 0 + ) + subject.catch(s).should be_valid + end + + it "doesn't report if return is used in a block" do + s = Source.new %( + def foo + bar = obj.try do + if something + a = 1 + end + return nil + end + + bar + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is unreachable code after if-then-else" do + s = Source.new %( + def foo + if a > 0 + return :positive + else + return :negative + end + + :unreachable + end + ) + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.location.to_s.should eq ":8:3" + end + + it "reports if there is unreachable code after if-then-else-if" do + s = Source.new %( + def foo + if a > 0 + return :positive + elsif a != 0 + return :negative + else + return :zero + end + + :unreachable + end + ) + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.location.to_s.should eq ":10:3" + end + + it "doesn't report if there is no unreachable code after if-then-else" do + s = Source.new %( + def foo + if a > 0 + return :positive + else + return :negative + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is no unreachable in inner branch" do + s = Source.new %( + def foo + if a > 0 + return :positive if a != 1 + else + return :negative + end + + :not_unreachable + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is no unreachable in exception handler" do + s = Source.new %( + def foo + puts :bar + rescue Exception + raise "Error!" + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is multiple conditions with return" do + s = Source.new %( + if :foo + if :bar + return :foobar + else + return :foobaz + end + elsif :fox + return :foofox + end + + return :reachable + ) + subject.catch(s).should be_valid + end + + it "reports if there is unreachable code after unless" do + s = Source.new %( + unless :foo + return :bar + else + return :foo + end + + :unreachable + ) + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.location.to_s.should eq ":7:1" + end + + it "doesn't report if there is no unreachable code after unless" do + s = Source.new %( + unless :foo + return :bar + end + + :reachable + ) + subject.catch(s).should be_valid + end + end + + context "binary op" do + it "reports unreachable code in a binary operator" do + s = Source.new %( + (return 22) && puts "a" + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":1:16" + end + + it "reports unreachable code in inner binary operator" do + s = Source.new %( + do_something || (return 22) && puts "a" + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":1:32" + end + + it "reports unreachable code after the binary op" do + s = Source.new %( + (return 22) && break + :unreachable + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":2:1" + end + + it "doesn't report if return is not the right" do + s = Source.new %( + puts "a" && return + ) + subject.catch(s).should be_valid + end + + it "doesn't report unreachable code in multiple binary expressions" do + s = Source.new %( + foo || bar || baz + ) + subject.catch(s).should be_valid + end + end + + context "case" do + it "reports if there is unreachable code after case" do + s = Source.new %( + def foo + case cond + when 1 + something + return + when 2 + something2 + return + else + something3 + return + end + :unreachable + end + ) + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.location.to_s.should eq ":13:3" + end + + it "doesn't report if case does not have else" do + s = Source.new %( + def foo + case cond + when 1 + something + return + when 2 + something2 + return + end + :reachable + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if one when does not return" do + s = Source.new %( + def foo + case cond + when 1 + something + return + when 2 + something2 + else + something3 + return + end + :reachable + end + ) + subject.catch(s).should be_valid + end + end + + context "exception handler" do + it "reports unreachable code if it returns in body and rescues" do + s = Source.new %( + def foo + begin + return false + rescue Error + return false + rescue Exception + return false + end + :unreachable + end + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":9:3" + end + + it "reports unreachable code if it returns in rescues and else" do + s = Source.new %( + def foo + begin + do_something + rescue Error + return :error + else + return true + end + :unreachable + end + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":9:3" + end + + it "doesn't report if there is no else and ensure doesn't return" do + s = Source.new %( + def foo + begin + return false + rescue Error + puts "error" + rescue Exception + return false + end + :reachable + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is no else and body doesn't return" do + s = Source.new %( + def foo + begin + do_something + rescue Error + return true + rescue Exception + return false + end + :reachable + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is else and ensure doesn't return" do + s = Source.new %( + def foo + begin + do_something + rescue Error + puts "yo" + else + return true + end + :reachable + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there is else and it doesn't return" do + s = Source.new %( + def foo + begin + do_something + rescue Error + return false + else + puts "yo" + end + :reachable + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is unreachable code in rescue" do + s = Source.new %( + def method + rescue + return 22 + :unreachable + end + ) + + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":4:3" + end + end + + context "while/until" do + it "reports if there is unreachable code after while" do + s = Source.new %( + def method + while something + if :foo + return :foo + else + return :foobar + end + end + :unreachable + end + ) + + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":9:3" + end + + it "reports if there is unreachable code after until" do + s = Source.new %( + def method + until something + if :foo + return :foo + else + return :foobar + end + end + :unreachable + end + ) + + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":9:3" + end + + it "doesn't report if there is reachable code after while with break" do + s = Source.new %( + while something + break + end + :reachable + ) + + subject.catch(s).should be_valid + end + end + + context "rescue" do + it "reports unreachable code in rescue" do + s = Source.new %( + begin + + rescue e + raise e + :unreachable + end + ) + + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":5:3" + end + + it "doesn't report if there is no unreachable code in rescue" do + s = Source.new %( + begin + + rescue e + raise e + end + ) + + subject.catch(s).should be_valid + end + end + + context "when" do + it "reports unreachable code in when" do + s = Source.new %( + case + when valid? + return 22 + :unreachable + else + + end + ) + + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":4:3" + end + + it "doesn't report if there is no unreachable code in when" do + s = Source.new %( + case + when valid? + return 22 + else + end + ) + + subject.catch(s).should be_valid + end + end + + context "break" do + it "reports if there is unreachable code after break" do + s = Source.new %( + def foo + loop do + break + a = 1 + end + end + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":4:5" + end + + it "doesn't report if break is in a condition" do + s = Source.new %( + a = -100 + while true + break if a > 0 + a += 1 + end + ) + subject.catch(s).should be_valid + end + end + + context "next" do + it "reports if there is unreachable code after next" do + s = Source.new %( + a = 1 + while a < 5 + next + puts a + end + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":4:3" + end + + it "doesn't report if next is in a condition" do + s = Source.new %( + a = 1 + while a < 5 + if a == 3 + next + end + puts a + end + ) + subject.catch(s).should be_valid + end + end + + context "raise" do + it "reports if there is unreachable code after raise" do + s = Source.new %( + a = 1 + raise "exception" + b = 2 + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":3:1" + end + + it "doesn't report if raise is in a condition" do + s = Source.new %( + a = 1 + raise "exception" if a > 0 + b = 2 + ) + subject.catch(s).should be_valid + end + end + + context "exit" do + it "reports if there is unreachable code after exit without args" do + s = Source.new %( + a = 1 + exit + b = 2 + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":3:1" + end + + it "reports if there is unreachable code after exit with exit code" do + s = Source.new %( + a = 1 + exit 1 + b = 2 + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":3:1" + end + + it "doesn't report if exit is in a condition" do + s = Source.new %( + a = 1 + exit if a > 0 + b = 2 + ) + subject.catch(s).should be_valid + end + end + + context "abort" do + it "reports if there is unreachable code after abort with one argument" do + s = Source.new %( + a = 1 + abort "abort" + b = 2 + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":3:1" + end + + it "reports if there is unreachable code after abort with two args" do + s = Source.new %( + a = 1 + abort "abort", 1 + b = 2 + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":3:1" + end + + it "doesn't report if abort is in a condition" do + s = Source.new %( + a = 1 + abort "abort" if a > 0 + b = 2 + ) + subject.catch(s).should be_valid + end + end + + it "reports message, rule, location" do + s = Source.new %( + return + :unreachable + ), "source.cr" + + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:1" + issue.end_location.to_s.should eq "source.cr:2:12" + issue.message.should eq "Unreachable code detected" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr new file mode 100644 index 000000000000..69bac6ea6e04 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr @@ -0,0 +1,306 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = UnusedArgument.new + subject.ignore_defs = false + + describe UnusedArgument do + it "doesn't report if arguments are used" do + s = Source.new %( + def method(a, b, c) + a + b + c + end + + 3.times do |i| + i + 1 + end + + ->(i : Int32) { i + 1 } + ) + subject.catch(s).should be_valid + end + + it "reports if method argument is unused" do + s = Source.new %( + def method(a, b, c) + a + b + end + ) + subject.catch(s).should_not be_valid + s.issues.first.message.should eq "Unused argument `c`. If it's necessary, use `_c` " \ + "as an argument name to indicate that it won't be used." + end + + it "reports if block argument is unused" do + s = Source.new %( + [1,2].each_with_index do |a, i| + a + end + ) + subject.catch(s).should_not be_valid + s.issues.first.message.should eq "Unused argument `i`. If it's necessary, use `_` " \ + "as an argument name to indicate that it won't be used." + end + + it "reports if proc argument is unused" do + s = Source.new %( + -> (a : Int32, b : String) do + a = a + 1 + end + ) + subject.catch(s).should_not be_valid + s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \ + "as an argument name to indicate that it won't be used." + end + + it "reports multiple unused args" do + s = Source.new %( + def method(a, b, c) + nil + end + ) + subject.catch(s).should_not be_valid + s.issues[0].message.should eq "Unused argument `a`. If it's necessary, use `_a` " \ + "as an argument name to indicate that it won't be used." + s.issues[1].message.should eq "Unused argument `b`. If it's necessary, use `_b` " \ + "as an argument name to indicate that it won't be used." + s.issues[2].message.should eq "Unused argument `c`. If it's necessary, use `_c` " \ + "as an argument name to indicate that it won't be used." + end + + it "doesn't report if it is an instance var argument" do + s = Source.new %( + class A + def method(@name) + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if a typed argument is used" do + s = Source.new %( + def method(x : Int32) + 3.times do + puts x + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if an argument with default value is used" do + s = Source.new %( + def method(x = 1) + puts x + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if argument starts with a _" do + s = Source.new %( + def method(_x) + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if it is a block and used" do + s = Source.new %( + def method(&block) + block.call + end + ) + subject.catch(s).should be_valid + end + + it "reports if block arg is not used" do + s = Source.new %( + def method(&block) + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if unused and there is yield" do + s = Source.new %( + def method(&block) + yield 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if variable is referenced implicitly" do + s = Source.new %( + class Bar < Foo + def method(a, b) + super + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if arg if referenced in case" do + s = Source.new %( + def foo(a) + case a + when /foo/ + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if enum in a record" do + s = Source.new %( + class Class + record Record do + enum Enum + CONSTANT + end + end + end + ) + subject.catch(s).should be_valid + end + + context "super" do + it "reports if variable is not referenced implicitly by super" do + s = Source.new %( + class Bar < Foo + def method(a, b) + super a + end + end + ) + subject.catch(s).should_not be_valid + s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \ + "as an argument name to indicate that it won't be used." + end + + it "reports rule, location and message" do + s = Source.new %( + def method(a) + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.message.should eq "Unused argument `a`. If it's necessary, use `_a` " \ + "as an argument name to indicate that it won't be used." + issue.location.to_s.should eq "source.cr:1:12" + end + end + + context "macro" do + it "doesn't report if it is a used macro argument" do + s = Source.new %( + macro my_macro(arg) + {% arg %} + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if it is a used macro block argument" do + s = Source.new %( + macro my_macro(&block) + {% block %} + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report used macro args with equal names in record" do + s = Source.new %( + record X do + macro foo(a, b) + {{a}} + {{b}} + end + + macro bar(a, b, c) + {{a}} + {{b}} + {{c}} + end + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report used args in macro literals" do + s = Source.new %( + def print(f : Array(U)) forall U + f.size.times do |i| + {% if U == Float64 %} + puts f[i].round(3) + {% else %} + puts f[i] + {% end %} + end + end + ) + subject.catch(s).should be_valid + end + end + + context "properties" do + describe "#ignore_defs" do + it "lets the rule to ignore def scopes if true" do + subject.ignore_defs = true + s = Source.new %( + def method(a) + end + ) + subject.catch(s).should be_valid + end + + it "lets the rule not to ignore def scopes if false" do + subject.ignore_defs = false + s = Source.new %( + def method(a) + end + ) + subject.catch(s).should_not be_valid + end + end + + context "#ignore_blocks" do + it "lets the rule to ignore block scopes if true" do + subject.ignore_blocks = true + s = Source.new %( + 3.times { |i| puts "yo!" } + ) + subject.catch(s).should be_valid + end + + it "lets the rule not to ignore block scopes if false" do + subject.ignore_blocks = false + s = Source.new %( + 3.times { |i| puts "yo!" } + ) + subject.catch(s).should_not be_valid + end + end + + context "#ignore_procs" do + it "lets the rule to ignore proc scopes if true" do + subject.ignore_procs = true + s = Source.new %( + ->(a : Int32) {} + ) + subject.catch(s).should be_valid + end + + it "lets the rule not to ignore proc scopes if false" do + subject.ignore_procs = false + s = Source.new %( + ->(a : Int32) {} + ) + subject.catch(s).should_not be_valid + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr new file mode 100644 index 000000000000..bd5fdb2dddb3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr @@ -0,0 +1,977 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + describe UselessAssign do + subject = UselessAssign.new + + it "does not report used assigments" do + s = Source.new %( + def method + a = 2 + a + end + ) + subject.catch(s).should be_valid + end + + it "reports a useless assignment in a method" do + s = Source.new %( + def method + a = 2 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports a useless assignment in a proc" do + s = Source.new %( + ->() { + a = 2 + } + ) + subject.catch(s).should_not be_valid + end + + it "reports a useless assignment in a block" do + s = Source.new %( + def method + 3.times do + a = 1 + end + end + ) + subject.catch(s).should_not be_valid + end + + it "reports a useless assignment in a proc inside def" do + s = Source.new %( + def method + ->() { + a = 2 + } + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report ignored assigments" do + s = Source.new %( + payload, _header = decode + puts payload + ) + subject.catch(s).should be_valid + end + + it "reports a useless assignment in a proc inside a block" do + s = Source.new %( + def method + 3.times do + ->() { + a = 2 + } + end + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, position and a message" do + s = Source.new %( + def method + a = 2 + end + ), "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:3" + issue.message.should eq "Useless assignment to variable `a`" + end + + it "does not report useless assignment of instance var" do + s = Source.new %( + class Cls + def initialize(@name) + end + end + ) + subject.catch(s).should be_valid + end + + it "does not report if assignment used in the inner block scope" do + s = Source.new %( + def method + var = true + 3.times { var = false } + end + ) + subject.catch(s).should be_valid + end + + it "reports if assigned is not referenced in the inner block scope" do + s = Source.new %( + def method + var = true + 3.times {} + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if assignment in referenced in inner block" do + s = Source.new %( + def method + two = true + + 3.times do + mutex.synchronize do + two = 2 + end + end + + two.should be_true + end + ) + subject.catch(s).should be_valid + end + + it "reports if first assignment is useless" do + s = Source.new %( + def method + var = true + var = false + var + end + ) + subject.catch(s).should_not be_valid + s.issues.first.location.to_s.should eq ":2:3" + end + + it "reports if variable reassigned and not used" do + s = Source.new %( + def method + var = true + var = false + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if variable used in a condition" do + s = Source.new %( + def method + a = 1 + if a + nil + end + end + ) + subject.catch(s).should be_valid + end + + it "reports second assignment as useless" do + s = Source.new %( + def method + a = 1 + a = a + 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if variable is referenced in other assignment" do + s = Source.new %( + def method + if f = get_something + @f = f + end + end + ) + subject.catch(s).should be_valid + end + + it "does not report if variable is referenced in a setter" do + s = Source.new %( + def method + foo = 2 + table[foo] ||= "bar" + end + ) + subject.catch(s).should be_valid + end + + it "does not report if variable is reassigned but not referenced" do + s = Source.new %( + def method + foo = 1 + puts foo + foo = 2 + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if variable is referenced in a call" do + s = Source.new %( + def method + if f = FORMATTER + @formatter = f.new + end + end + ) + subject.catch(s).should be_valid + end + + it "does not report if a setter is invoked with operator assignment" do + s = Source.new %( + def method + obj = {} of Symbol => Int32 + obj[:name] = 3 + end + ) + subject.catch(s).should be_valid + end + + context "when transformed" do + it "does not report if the first arg is transformed and not used" do + s = Source.new %( + collection.each do |(a, b)| + puts b + end + ) + subject.catch(s).should be_valid + end + + it "does not report if the second arg is transformed and not used" do + s = Source.new %( + collection.each do |(a, b)| + puts a + end + ) + subject.catch(s).should be_valid + end + + it "does not report if all transformed args are not used in a block" do + s = Source.new %( + collection.each do |(foo, bar), (baz, _qux), index, object| + end + ) + subject.catch(s).should be_valid + end + end + + it "does not report if global var" do + s = Source.new %( + def method + $1 = 3 + end + ) + subject.catch(s).should be_valid + end + + it "does not report if assignment is referenced in a proc" do + s = Source.new %( + def method + called = false + ->() { called = true } + called + end + ) + subject.catch(s).should be_valid + end + + it "reports if variable is shadowed in inner scope" do + s = Source.new %( + def method + i = 1 + 3.times do |i| + i + 1 + end + end + ) + subject.catch(s).should_not be_valid + end + + it "does not report if parameter is referenced after the branch" do + s = Source.new %( + def method(param) + 3.times do + param = 3 + end + param + end + ) + subject.catch(s).should be_valid + end + + context "op assigns" do + it "does not report if variable is referenced below the op assign" do + s = Source.new %( + def method + a = 1 + a += 1 + a + end + ) + subject.catch(s).should be_valid + end + + it "does not report if variable is referenced in op assign few times" do + s = Source.new %( + def method + a = 1 + a += 1 + a += 1 + a = a + 1 + a + end + ) + subject.catch(s).should be_valid + end + + it "reports if variable is not referenced below the op assign" do + s = Source.new %( + def method + a = 1 + a += 1 + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, location and a message" do + s = Source.new %( + def method + b = 2 + a = 3 + a += 1 + end + ), "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.last + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:4:3" + issue.message.should eq "Useless assignment to variable `a`" + end + end + + context "multi assigns" do + it "does not report if all assigns are referenced" do + s = Source.new %( + def method + a, b = {1, 2} + a + b + end + ) + subject.catch(s).should be_valid + end + + it "reports if one assign is not referenced" do + s = Source.new %( + def method + a, b = {1, 2} + a + end + ) + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.location.to_s.should eq ":2:6" + issue.message.should eq "Useless assignment to variable `b`" + end + + it "reports if both assigns are reassigned and useless" do + s = Source.new %( + def method + a, b = {1, 2} + a, b = {3, 4} + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if both assigns are not referenced" do + s = Source.new %( + def method + a, b = {1, 2} + end + ) + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.location.to_s.should eq ":2:3" + issue.message.should eq "Useless assignment to variable `a`" + + issue = s.issues.last + issue.location.to_s.should eq ":2:6" + issue.message.should eq "Useless assignment to variable `b`" + end + end + + context "top level" do + it "reports if assignment is not referenced" do + s = Source.new %( + a = 1 + a = 2 + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":1:1" + s.issues.last.location.to_s.should eq ":2:1" + end + + it "doesn't report if assignments are referenced" do + s = Source.new %( + a = 1 + a += 1 + a + + b, c = {1, 2} + b + c + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is captured by block" do + s = Source.new %( + a = 1 + + 3.times do + a = 2 + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment initialized and captured by block" do + s = Source.new %( + a : String? = nil + + 1.times do + a = "Fotis" + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if this is a record declaration" do + s = Source.new %( + record Foo, foo = "foo" + ) + subject.catch(s).should be_valid + end + + it "does not report if assignment is referenced after the record declaration" do + s = Source.new %( + foo = 2 + record Bar, foo = 3 # foo = 3 is not parsed as assignment + puts foo + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is not referenced after the record declaration" do + s = Source.new %( + foo = 2 + record Bar, foo = 3 + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.location.to_s.should eq "source.cr:1:1" + issue.message.should eq "Useless assignment to variable `foo`" + end + end + + context "branching" do + context "if-then-else" do + it "doesn't report if assignment is consumed by branches" do + s = Source.new %( + def method + a = 0 + if something + a = 1 + else + a = 2 + end + a + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is in one branch" do + s = Source.new %( + def method + a = 0 + if something + a = 1 + else + nil + end + a + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is in one line branch" do + s = Source.new %( + def method + a = 0 + a = 1 if something + a + end + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is useless in the branch" do + s = Source.new %( + def method(a) + if a + a = 2 + end + end + ) + subject.catch(s).should_not be_valid + end + + it "reports if only last assignment is referenced in a branch" do + s = Source.new %( + def method(a) + a = 1 + if a + a = 2 + a = 3 + end + a + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":4:5" + end + + it "does not report of assignments are referenced in all branches" do + s = Source.new %( + def method + if matches + matches = owner.lookup_matches signature + else + matches = owner.lookup_matches signature + end + + matches + end + ) + subject.catch(s).should be_valid + end + + it "does not report referenced assignments in inner branches" do + s = Source.new %( + def method + has_newline = false + + if something + do_something unless false + has_newline = false + else + do_something if true + has_newline = true + end + + has_newline + end + ) + subject.catch(s).should be_valid + end + end + + context "unless-then-else" do + it "doesn't report if assignment is consumed by branches" do + s = Source.new %( + def method + a = 0 + unless something + a = 1 + else + a = 2 + end + a + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is a useless assignment in a branch" do + s = Source.new %( + def method + a = 0 + unless something + a = 1 + a = 2 + else + a = 2 + end + a + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":4:5" + end + end + + context "case" do + it "does not report if assignment is referenced" do + s = Source.new %( + def method(a) + case a + when /foo/ + a = 1 + when /bar/ + a = 2 + end + puts a + end + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is useless" do + s = Source.new %( + def method(a) + case a + when /foo/ + a = 1 + when /bar/ + a = 2 + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":4:5" + s.issues.last.location.to_s.should eq ":6:5" + end + + it "doesn't report if assignment is referenced in cond" do + s = Source.new %( + def method + a = 2 + case a + when /foo/ + end + end + ) + subject.catch(s).should be_valid + end + end + + context "binary operator" do + it "does not report if assignment is referenced" do + s = Source.new %( + def method(a) + (a = 1) && (b = 1) + a + b + end + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is useless" do + s = Source.new %( + def method(a) + (a = 1) || (b = 1) + a + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:15" + end + end + + context "while" do + it "does not report if assignment is referenced" do + s = Source.new %( + def method(a) + while a < 10 + a = a + 1 + end + a + end + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is useless" do + s = Source.new %( + def method(a) + while a < 10 + b = a + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":3:5" + end + + it "does not report if assignment is referenced in a loop" do + s = Source.new %( + def method + a = 3 + result = 0 + + while result < 10 + result += a + a = a + 1 + end + result + end + ) + subject.catch(s).should be_valid + end + + it "does not report if assignment is referenced as param in a loop" do + s = Source.new %( + def method(a) + result = 0 + + while result < 10 + result += a + a = a + 1 + end + result + end + ) + subject.catch(s).should be_valid + end + + it "does not report if assignment is referenced in loop and inner branch" do + s = Source.new %( + def method(a) + result = 0 + + while result < 10 + result += a + if result > 0 + a = a + 1 + else + a = 3 + end + end + result + end + ) + subject.catch(s).should be_valid + end + + it "works properly if there is branch with blank node" do + s = Source.new %( + def visit + count = 0 + while true + break if count == 1 + case something + when :any + else + :anything_else + end + count += 1 + end + end + ) + subject.catch(s).should be_valid + end + end + + context "until" do + it "does not report if assignment is referenced" do + s = Source.new %( + def method(a) + until a > 10 + a = a + 1 + end + a + end + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is useless" do + s = Source.new %( + def method(a) + until a > 10 + b = a + 1 + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":3:5" + end + end + + context "exception handler" do + it "does not report if assignment is referenced in body" do + s = Source.new %( + def method(a) + a = 2 + rescue + a + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is referenced in ensure" do + s = Source.new %( + def method(a) + a = 2 + ensure + a + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is referenced in else" do + s = Source.new %( + def method(a) + a = 2 + rescue + else + a + end + ) + subject.catch(s).should be_valid + end + + it "reports if assignment is useless" do + s = Source.new %( + def method(a) + rescue + a = 2 + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":3:3" + end + end + end + + context "typeof" do + it "reports useless assigments in typeof" do + s = Source.new %( + typeof(begin + foo = 1 + bar = 2 + end) + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":2:3" + s.issues.first.message.should eq "Useless assignment to variable `foo`" + + s.issues.last.location.to_s.should eq ":3:3" + s.issues.last.message.should eq "Useless assignment to variable `bar`" + end + end + + context "macro" do + it "doesn't report if assignment is referenced in macro" do + s = Source.new %( + def method + a = 2 + {% if flag?(:bits64) %} + a.to_s + {% else %} + a + {% end %} + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report referenced assignments in macro literal" do + s = Source.new %( + def method + a = 2 + {% if flag?(:bits64) %} + a = 3 + {% else %} + a = 4 + {% end %} + puts a + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is referenced in macro def" do + s = Source.new %( + macro macro_call + puts x + end + + def foo + x = 1 + macro_call + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if assignment is referenced in a macro below" do + s = Source.new %( + class Foo + def foo + a = 1 + macro_call + end + + macro macro_call + puts a + end + end + ) + subject.catch(s).should be_valid + end + end + + context "uninitialized" do + it "reports if uninitialized assignment is not referenced at a top level" do + s = Source.new %( + a = uninitialized U + ) + subject.catch(s).should_not be_valid + end + + it "reports if uninitialized assignment is not referenced in a method" do + s = Source.new %( + def foo + a = uninitialized U + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if uninitialized assignment is referenced" do + s = Source.new %( + def foo + a = uninitialized U + a + end + ) + subject.catch(s).should be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr new file mode 100644 index 000000000000..3dadc3bb0033 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr @@ -0,0 +1,46 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = UselessConditionInWhen.new + + describe UselessConditionInWhen do + it "passes if there is not useless condition" do + s = Source.new %( + case + when utc? + io << " UTC" + when local? + Format.new(" %:z").format(self, io) if utc? + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is useless if condition" do + s = Source.new %( + case + when utc? + io << " UTC" if utc? + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + case + when String + puts "hello" + when can_generate? + generate if can_generate? + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:5:15" + issue.end_location.to_s.should eq "source.cr:5:27" + issue.message.should eq "Useless condition in when detected" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr new file mode 100644 index 000000000000..bc81fc7d7f6c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr @@ -0,0 +1,47 @@ +require "../../../spec_helper" + +module Ameba::Rule::Metrics + subject = CyclomaticComplexity.new + complex_method = <<-CODE + def hello(a, b, c) + if a && b && c + begin + while true + return if false && b + end + "" + rescue + "" + end + end + end + CODE + + describe CyclomaticComplexity do + it "passes for empty methods" do + source = Source.new %( + def hello + end + ) + subject.catch(source).should be_valid + end + + it "reports one issue for a complex method" do + subject.max_complexity = 5 + source = Source.new(complex_method, "source.cr") + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.rule.should eq subject + issue.location.to_s.should eq "source.cr:1:5" + issue.end_location.to_s.should eq "source.cr:1:10" + issue.message.should eq "Cyclomatic complexity too high [8/5]" + end + + it "doesn't report an issue for an increased threshold" do + subject.max_complexity = 100 + source = Source.new(complex_method, "source.cr") + subject.catch(source).should be_valid + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr new file mode 100644 index 000000000000..d78722c2b959 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr @@ -0,0 +1,73 @@ +require "../../../spec_helper" + +module Ameba::Rule::Performance + subject = AnyAfterFilter.new + + describe AnyAfterFilter do + it "passes if there is no potential performance improvements" do + source = Source.new %( + [1, 2, 3].select { |e| e > 1 }.any?(&.zero?) + [1, 2, 3].reject { |e| e > 1 }.any?(&.zero?) + [1, 2, 3].select { |e| e > 1 } + [1, 2, 3].reject { |e| e > 1 } + [1, 2, 3].any? { |e| e > 1 } + ) + subject.catch(source).should be_valid + end + + it "reports if there is select followed by any? without a block" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.any? + ) + subject.catch(source).should_not be_valid + end + + it "reports if there is reject followed by any? without a block" do + source = Source.new %( + [1, 2, 3].reject { |e| e > 2 }.any? + ) + subject.catch(source).should_not be_valid + end + + it "does not report if any? calls contains a block" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.any?(&.zero?) + [1, 2, 3].reject { |e| e > 2 }.any?(&.zero?) + ) + subject.catch(source).should be_valid + end + + context "properties" do + it "allows to configure object_call_names" do + source = Source.new %( + [1, 2, 3].reject { |e| e > 2 }.any? + ) + rule = Rule::Performance::AnyAfterFilter.new + rule.filter_names = %w(select) + rule.catch(source).should be_valid + end + end + + context "macro" do + it "reports in macro scope" do + source = Source.new %( + {{ [1, 2, 3].reject { |e| e > 2 }.any? }} + ) + subject.catch(source).should_not be_valid + end + end + + it "reports rule, pos and message" do + s = Source.new %( + [1, 2, 3].reject { |e| e > 2 }.any? + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:36" + issue.message.should eq "Use `any? {...}` instead of `reject {...}.any?`" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr new file mode 100644 index 000000000000..d3320ccb2a33 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr @@ -0,0 +1,141 @@ +require "../../../spec_helper" + +module Ameba::Rule::Performance + subject = FirstLastAfterFilter.new + + describe FirstLastAfterFilter do + it "passes if there is no potential performance improvements" do + source = Source.new %( + [1, 2, 3].select { |e| e > 1 } + [1, 2, 3].reverse.select { |e| e > 1 } + [1, 2, 3].reverse.last + [1, 2, 3].reverse.first + [1, 2, 3].reverse.first + ) + subject.catch(source).should be_valid + end + + it "reports if there is select followed by last" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.last + ) + subject.catch(source).should_not be_valid + end + + it "reports if there is select followed by last?" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.last? + ) + subject.catch(source).should_not be_valid + end + + it "reports if there is select followed by first" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.first + ) + subject.catch(source).should_not be_valid + end + + it "does not report if there is selected followed by first with arguments" do + source = Source.new %( + [1, 2, 3].select { |n| n % 2 == 0 }.first(2) + ) + subject.catch(source).should be_valid + end + + it "reports if there is select followed by first?" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.first? + ) + subject.catch(source).should_not be_valid + end + + it "does not report if there is select followed by any other call" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.size + [1, 2, 3].select { |e| e > 2 }.any? + ) + subject.catch(source).should be_valid + end + + context "properties" do + it "allows to configure object_call_names" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.first + ) + rule = Rule::Performance::FirstLastAfterFilter.new + rule.filter_names = %w(reject) + rule.catch(source).should be_valid + end + end + + it "reports rule, pos and message" do + s = Source.new %( + [1, 2, 3].select { |e| e > 2 }.first + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:37" + + issue.message.should eq "Use `find {...}` instead of `select {...}.first`" + end + + it "reports a correct message for first?" do + s = Source.new %( + [1, 2, 3].select { |e| e > 2 }.first? + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:38" + + issue.message.should eq "Use `find {...}` instead of `select {...}.first?`" + end + + it "reports rule, pos and reverse message" do + s = Source.new %( + [1, 2, 3].select { |e| e > 2 }.last + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:36" + + issue.message.should eq "Use `reverse_each.find {...}` instead of `select {...}.last`" + end + + context "macro" do + it "doesn't report in macro scope" do + source = Source.new %( + {{[1, 2, 3].select { |e| e > 2 }.last }} + ) + subject.catch(source).should be_valid + end + end + + it "reports a correct message for last?" do + s = Source.new %( + [1, 2, 3].select { |e| e > 2 }.last? + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:37" + + issue.message.should eq "Use `reverse_each.find {...}` instead of `select {...}.last?`" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr new file mode 100644 index 000000000000..ccf958d8702b --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr @@ -0,0 +1,74 @@ +require "../../../spec_helper" + +module Ameba::Rule::Performance + subject = SizeAfterFilter.new + + describe SizeAfterFilter do + it "passes if there is no potential performance improvements" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 } + [1, 2, 3].reject { |e| e < 2 } + [1, 2, 3].count { |e| e > 2 && e.odd? } + [1, 2, 3].count { |e| e < 2 && e.even? } + + User.select("field AS name").count + Company.select(:value).count + ) + subject.catch(source).should be_valid + end + + it "reports if there is a select followed by size" do + source = Source.new %( + [1, 2, 3].select { |e| e > 2 }.size + ) + subject.catch(source).should_not be_valid + end + + it "reports if there is a reject followed by size" do + source = Source.new %( + [1, 2, 3].reject { |e| e < 2 }.size + ) + subject.catch(source).should_not be_valid + end + + it "reports if a block shorthand used" do + source = Source.new %( + [1, 2, 3].reject(&.empty?).size + ) + subject.catch(source).should_not be_valid + end + + context "properties" do + it "allows to configure object caller names" do + source = Source.new %( + [1, 2, 3].reject(&.empty?).size + ) + rule = Rule::Performance::SizeAfterFilter.new + rule.filter_names = %w(select) + rule.catch(source).should be_valid + end + end + + context "macro" do + it "doesn't report in macro scope" do + source = Source.new %( + {{[1, 2, 3].select { |v| v > 1 }.size}} + ) + subject.catch(source).should be_valid + end + end + + it "reports rule, pos and message" do + s = Source.new %( + lines.split("\n").reject(&.empty?).size + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:4" + issue.end_location.to_s.should eq "source.cr:2:25" + issue.message.should eq "Use `count {...}` instead of `reject {...}.size`." + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr new file mode 100644 index 000000000000..f31814969084 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr @@ -0,0 +1,54 @@ +require "../../../spec_helper" + +module Ameba + subject = Rule::Style::ConstantNames.new + + private def it_reports_constant(code, expected) + it "reports constant name #{expected}" do + s = Source.new code + Rule::Style::ConstantNames.new.catch(s).should_not be_valid + s.issues.first.message.should contain expected + end + end + + describe Rule::Style::ConstantNames do + it "passes if type names are screaming-cased" do + s = Source.new %( + LUCKY_NUMBERS = [3, 7, 11] + DOCUMENTATION_URL = "http://crystal-lang.org/docs" + + Int32 + + s : String = "str" + + def works(n : Int32) + end + + Log = ::Log.for("db") + + a = 1 + myVar = 2 + m_var = 3 + ) + subject.catch(s).should be_valid + end + + # it_reports_constant "MyBadConstant=1", "MYBADCONSTANT" + it_reports_constant "Wrong_NAME=2", "WRONG_NAME" + it_reports_constant "Wrong_Name=3", "WRONG_NAME" + + it "reports rule, pos and message" do + s = Source.new %( + Const_Name = 1 + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:10" + issue.message.should eq( + "Constant name should be screaming-cased: CONST_NAME, not Const_Name" + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr new file mode 100644 index 000000000000..a3bf0ee6c2a5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr @@ -0,0 +1,45 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + describe IsANil do + subject = IsANil.new + + it "doesn't report if there are no is_a?(Nil) calls" do + s = Source.new %( + a = 1 + a.nil? + a.is_a?(NilLiteral) + a.is_a?(Custom::Nil) + ) + subject.catch(s).should be_valid + end + + it "reports if there is a call to is_a?(Nil) without receiver" do + s = Source.new %( + is_a?(Nil) + ) + subject.catch(s).should_not be_valid + end + + it "reports if there is a call to is_a?(Nil) with receiver" do + s = Source.new %( + a.is_a?(Nil) + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, location and message" do + s = Source.new %( + nil.is_a? Nil + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:11" + issue.end_location.to_s.should eq "source.cr:1:13" + issue.message.should eq IsANil::MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr new file mode 100644 index 000000000000..cd1f14f64d6a --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr @@ -0,0 +1,134 @@ +require "../../../spec_helper" + +module Ameba + subject = Rule::Style::LargeNumbers.new + + private def it_transforms(number, expected) + it "transforms large number #{number}" do + s = Source.new number + Rule::Style::LargeNumbers.new.catch(s).should_not be_valid + s.issues.first.message.should contain expected + end + end + + describe Rule::Style::LargeNumbers do + it "passes if large number does not require underscore" do + s = Source.new %q( + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + 16 17 18 19 20 30 40 50 60 70 80 90 + 100 + 999 + 1000 + 1_000 + 9999 + 9_999 + 10_000 + 100_000 + 200_000 + 300_000 + 400_000 + 500_000 + 600_000 + 700_000 + 800_000 + 900_000 + 1_000_000 + + -9_223_372_036_854_775_808 + 9_223_372_036_854_775_807 + + 141_592_654 + 141_592_654.0 + 141_592_654.001 + 141_592_654.001_2 + 141_592_654.001_23 + 141_592_654.001_234 + 141_592_654.001_234_5 + + 0b1101 + 0o123 + 0xFE012D + 0xfe012d + 0xfe012dd11 + + 1_i8 + 12_i16 + 123_i32 + 1_234_i64 + + 12_u8 + 123_u16 + 1_234_u32 + 9_223_372_036_854_775_808_u64 + 9_223_372_036_854_775_808.000_123_456_789_f64 + + +100_u32 + -900_000_i32 + + 1_234.5e-7 + 11_234e10_f32 + +1.123 + -0.000_5 + + 1200.0 + 1200.01 + 1200.012 + ) + subject.catch(s).should be_valid + end + + it_transforms "10000", "10_000" + it_transforms "+10000", "+10_000" + it_transforms "-10000", "-10_000" + + it_transforms "9223372036854775808", "9_223_372_036_854_775_808" + it_transforms "-9223372036854775808", "-9_223_372_036_854_775_808" + it_transforms "+9223372036854775808", "+9_223_372_036_854_775_808" + + it_transforms "1_00000", "100_000" + + it_transforms "10000_i16", "10_000_i16" + it_transforms "10000_i32", "10_000_i32" + it_transforms "10000_i64", "10_000_i64" + + it_transforms "10000_u16", "10_000_u16" + it_transforms "10000_u32", "10_000_u32" + it_transforms "10000_u64", "10_000_u64" + + it_transforms "123456_f32", "123_456_f32" + it_transforms "123456_f64", "123_456_f64" + + it_transforms "123456.5e-7_f32", "123_456.5e-7_f32" + it_transforms "123456e10_f64", "123_456e10_f64" + + it_transforms "123456.5e-7", "123_456.5e-7" + it_transforms "123456e10", "123_456e10" + + it_transforms "3.00_1", "3.001" + it_transforms "3.0012", "3.001_2" + it_transforms "3.00123", "3.001_23" + it_transforms "3.001234", "3.001_234" + it_transforms "3.0012345", "3.001_234_5" + + it "reports rule, pos and message" do + s = Source.new %q( + 1200000 + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.should be_nil + issue.message.should match /1_200_000/ + end + + context "properties" do + it "allows to configure integer min digits" do + s = Source.new %q(1200000) + rule = Rule::Style::LargeNumbers.new + rule.int_min_digits = 10 + rule.catch(s).should be_valid + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr new file mode 100644 index 000000000000..4f828b5830a7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr @@ -0,0 +1,56 @@ +require "../../../spec_helper" + +module Ameba + subject = Rule::Style::MethodNames.new + + private def it_reports_method_name(code, expected) + it "reports method name #{expected}" do + s = Source.new code + Rule::Style::MethodNames.new.catch(s).should_not be_valid + s.issues.first.message.should contain expected + end + end + + describe Rule::Style::MethodNames do + it "passes if method names are underscore-cased" do + s = Source.new %( + class Person + def first_name + end + + def date_of_birth + end + + def homepage_url + end + + def valid? + end + + def name + end + end + ) + subject.catch(s).should be_valid + end + + it_reports_method_name %(def firstName; end), "first_name" + it_reports_method_name %(def date_of_Birth; end), "date_of_birth" + it_reports_method_name %(def homepageURL; end), "homepage_url" + + it "reports rule, pos and message" do + s = Source.new %( + def bad_Name(a) + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:5" + issue.end_location.to_s.should eq "source.cr:1:12" + issue.message.should eq( + "Method name should be underscore-cased: bad_name, not bad_Name" + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr new file mode 100644 index 000000000000..e8a042dcdd4a --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr @@ -0,0 +1,69 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + subject = NegatedConditionsInUnless.new + + describe NegatedConditionsInUnless do + it "passes with a unless without negated condition" do + s = Source.new %( + unless a + :ok + end + + :ok unless b + + unless s.empty? + :ok + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is a negated condition in unless" do + s = Source.new %( + unless !a + :nok + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if one of AND conditions is negated" do + s = Source.new %( + unless a && !b + :nok + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if one of OR conditions is negated" do + s = Source.new %( + unless a || !b + :nok + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if one of inner conditions is negated" do + s = Source.new %( + unless a && (b || !c) + :nok + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, pos and message" do + s = Source.new ":nok unless !s.empty?", "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:21" + issue.message.should eq "Avoid negated conditions in unless blocks" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr new file mode 100644 index 000000000000..eb797a221ea8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr @@ -0,0 +1,60 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + subject = PredicateName.new + + describe PredicateName do + it "passes if predicate name is correct" do + s = Source.new %q( + def valid?(x) + end + + class Image + def picture?(x) + end + end + + def allow_this_picture? + end + ) + subject.catch(s).should be_valid + end + + it "fails if predicate name is wrong" do + s = Source.new %q( + def is_valid?(x) + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, pos and message" do + s = Source.new %q( + class Image + def has_picture?(x) + true + end + end + ), "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:3" + issue.end_location.to_s.should eq "source.cr:4:5" + issue.message.should eq( + "Favour method name 'picture?' over 'has_picture?'") + end + + it "ignores if alternative name isn't valid syntax" do + s = Source.new %q( + class Image + def is_404?(x) + true + end + end + ) + subject.catch(s).should be_valid + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr new file mode 100644 index 000000000000..cbd18eb47d2d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr @@ -0,0 +1,227 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + describe RedundantBegin do + subject = RedundantBegin.new + + it "passes if there is no redundant begin blocks" do + s = Source.new %( + def method + do_something + rescue + do_something_else + end + + def method + do_something + do_something_else + ensure + handle_something + end + + def method + yield + rescue + end + + def method; end + def method; a = 1; rescue; end + def method; begin; rescue; end; end + ) + subject.catch(s).should be_valid + end + + it "passes if there is a correct begin block in a handler" do + s = Source.new %q( + def handler_and_expression + begin + open_file + rescue + close_file + end + do_some_stuff + end + + def multiple_handlers + begin + begin1 + rescue + end + + begin + begin2 + rescue + end + rescue + do_something_else + end + + def assign_and_begin + @result ||= + begin + do_something + do_something_else + returnit + end + rescue + end + + def inner_handler + s = begin + rescue + end + rescue + end + + def begin_and_expression + begin + a = 1 + b = 2 + end + expr + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is a redundant begin block" do + s = Source.new %q( + def method(a : String) : String + begin + open_file + do_some_stuff + ensure + close_file + end + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is a redundant begin block in a method without args" do + s = Source.new %q( + def method + begin + open_file + ensure + close_file + end + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is a redundant block in a method with return type" do + s = Source.new %q( + def method : String + begin + open_file + ensure + close_file + end + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is a redundant block in a method with multiple args" do + s = Source.new %q( + def method(a : String, + b : String) + begin + open_file + ensure + close_file + end + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is a redundant block in a method with multiple args" do + s = Source.new %q( + def method(a : String, + b : String + ) + begin + open_file + ensure + close_file + end + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if there is an inner redundant block" do + s = Source.new %q( + def method + begin + open_file + ensure + close_file + end + rescue + end + ) + subject.catch(s).should be_valid + end + + it "fails if there is a redundant block with yield" do + s = Source.new %q( + def method + begin + yield + ensure + close_file + end + end + ) + subject.catch(s).should_not be_valid + end + + it "fails if there is top level redundant block in a method" do + s = Source.new %q( + def method + begin + a = 1 + b = 2 + end + end + ) + subject.catch(s).should_not be_valid + end + + it "doesn't report if begin-end block in a proc literal" do + s = Source.new %q( + foo = ->{ + begin + raise "Foo!" + rescue ex + pp ex + end + } + ) + subject.catch(s).should be_valid + end + + it "reports rule, pos and message" do + s = Source.new %q( + def method + begin + open_connection + ensure + close_connection + end + end + ), "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:7:3" + issue.message.should eq "Redundant `begin` block detected" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr new file mode 100644 index 000000000000..460d2dcac8ea --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr @@ -0,0 +1,219 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + subject = RedundantNext.new + + describe RedundantNext do + it "does not report if there is no redundant next" do + s = Source.new %( + array.map { |x| x + 1 } + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant next with argument in the block" do + s = Source.new %( + block do |v| + next v + 1 + end + ) + subject.catch(s).should_not be_valid + end + + context "if" do + it "doesn't report if there is not redundant next in if branch" do + s = Source.new %( + block do |v| + next if v > 10 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant next in if/else branch" do + s = Source.new %( + block do |a| + if a > 0 + next a + 1 + else + next a + 2 + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":3:5" + s.issues.last.location.to_s.should eq ":5:5" + end + end + + context "unless" do + it "doesn't report if there is no redundant next in unless branch" do + s = Source.new %( + block do |v| + next unless v > 10 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant next in unless/else branch" do + s = Source.new %( + block do |a| + unless a > 0 + next a + 1 + else + next a + 2 + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":3:5" + s.issues.last.location.to_s.should eq ":5:5" + end + end + + context "expressions" do + it "doesn't report if there is no redundant next in expressions" do + s = Source.new %( + block do |v| + a = 1 + a + v + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant next in expressions" do + s = Source.new %( + block do |a| + a = 1 + next a + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":3:3" + end + end + + context "binary-op" do + it "doesn't report if there is no redundant next in binary op" do + s = Source.new %( + block do |v| + a && v + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant next in binary op" do + s = Source.new %( + block do |a| + a && next a + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:8" + end + end + + context "expception handler" do + it "doesn't report if there is no redundant next in exception handler" do + s = Source.new %( + block do |v| + v + 1 + rescue e + next v if v > 0 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant next in exception handler" do + s = Source.new %( + block do |a| + next a + 1 + rescue ArgumentError + next a + 2 + rescue Exception + a + 2 + next a + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 3 + s.issues[0].location.to_s.should eq ":2:3" + s.issues[1].location.to_s.should eq ":4:3" + s.issues[2].location.to_s.should eq ":7:3" + end + end + + it "reports correct rule, error message and position" do + s = Source.new %( + block do |v| + next v + 1 + end + ), "source.cr" + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:2:3" + issue.end_location.to_s.should eq "source.cr:2:12" + issue.message.should eq "Redundant `next` detected" + end + + context "properties" do + context "#allow_multi_next=" do + it "allows multi next statements by default" do + s = Source.new %( + block do |a, b| + next a, b + end + ) + subject.catch(s).should be_valid + end + + it "allows to configure multi next statements" do + s = Source.new %( + block do |a, b| + next a, b + end + ) + rule = Rule::Style::RedundantNext.new + rule.allow_multi_next = false + rule.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:3" + end + end + + context "#allow_empty_next" do + it "allows empty next statements by default" do + s = Source.new %( + block do + next + end + ) + subject.catch(s).should be_valid + end + + it "allows to configure empty next statements" do + s = Source.new %( + block do + next + end + ) + rule = Rule::Style::RedundantNext.new + rule.allow_empty_next = false + rule.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:3" + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr new file mode 100644 index 000000000000..4638dfc9e20d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr @@ -0,0 +1,266 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + subject = RedundantReturn.new + + describe RedundantReturn do + it "does not report if there is no return" do + s = Source.new %( + def inc(a) + a + 1 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is redundant return in method body" do + s = Source.new %( + def inc(a) + return a + 1 + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:3" + end + + it "doesn't report if it returns tuple literal" do + s = Source.new %( + def foo(a) + return a, a + 2 + end + ) + subject.catch(s).should be_valid + end + + it "doesn't report if there are other expressions after control flow" do + s = Source.new %( + def method(a) + case a + when true then return true + when .nil? then return :nil + end + false + rescue + nil + end + ) + subject.catch(s).should be_valid + end + + context "if" do + it "doesn't report if there is return in if branch" do + s = Source.new %( + def inc(a) + return a + 1 if a > 0 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there are returns in if/else branch" do + s = Source.new %( + def inc(a) + do_something(a) + if a > 0 + return :positive + else + return :negative + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":4:5" + s.issues.last.location.to_s.should eq ":6:5" + end + end + + context "unless" do + it "doesn't report if there is return in unless branch" do + s = Source.new %( + def inc(a) + return a + 1 unless a > 0 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there are returns in unless/else branch" do + s = Source.new %( + def inc(a) + do_something(a) + unless a < 0 + return :positive + else + return :negative + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":4:5" + s.issues.last.location.to_s.should eq ":6:5" + end + end + + context "binary op" do + it "doesn't report if there is no return in the right binary op node" do + s = Source.new %( + def can_create?(a) + valid? && a > 0 + end + ) + subject.catch(s).should be_valid + end + + it "reports if there is return in the right binary op node" do + s = Source.new %( + def can_create?(a) + valid? && return a > 0 + end + ) + subject.catch(s).should_not be_valid + end + end + + context "case" do + it "reports if there are returns in whens" do + s = Source.new %( + def foo(a) + case a + when .nil? + puts "blah" + return nil + when .blank? + return "" + when true + true + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":5:5" + s.issues.last.location.to_s.should eq ":7:5" + end + + it "reports if there is return in else" do + s = Source.new %( + def foo(a) + do_something_with(a) + + case a + when true + true + else + return false + end + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":8:5" + end + end + + context "exception handler" do + it "reports if there are returns in body" do + s = Source.new %( + def foo(a) + return true + rescue + false + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:3" + end + + it "reports if there are returns in rescues" do + s = Source.new %( + def foo(a) + true + rescue ArgumentError + return false + rescue RuntimeError + "" + rescue Exception + return nil + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 2 + s.issues.first.location.to_s.should eq ":4:3" + s.issues.last.location.to_s.should eq ":8:3" + end + + it "reports if there are returns in else" do + s = Source.new %( + def foo(a) + true + rescue Exception + nil + else + puts "else branch" + return :bar + end + ) + subject.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":7:3" + end + end + + context "properties" do + context "#allow_multi_return=" do + it "allows multi returns by default" do + s = Source.new %( + def method(a, b) + return a, b + end + ) + subject.catch(s).should be_valid + end + + it "allows to configure multi returns" do + s = Source.new %( + def method(a, b) + return a, b + end + ) + rule = Rule::Style::RedundantReturn.new + rule.allow_multi_return = false + rule.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:3" + end + end + + context "#allow_empty_return" do + it "allows empty returns by default" do + s = Source.new %( + def method + return + end + ) + subject.catch(s).should be_valid + end + + it "allows to configure empty returns" do + s = Source.new %( + def method + return + end + ) + rule = Rule::Style::RedundantReturn.new + rule.allow_empty_return = false + rule.catch(s).should_not be_valid + s.issues.size.should eq 1 + s.issues.first.location.to_s.should eq ":2:3" + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr new file mode 100644 index 000000000000..b5064411e31a --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr @@ -0,0 +1,61 @@ +require "../../../spec_helper" + +module Ameba + subject = Rule::Style::TypeNames.new + + private def it_reports_name(code, expected) + it "reports type name #{expected}" do + s = Source.new code + Rule::Style::TypeNames.new.catch(s).should_not be_valid + s.issues.first.message.should contain expected + end + end + + describe Rule::Style::TypeNames do + it "passes if type names are camelcased" do + s = Source.new %( + class ParseError < Exception + end + + module HTTP + class RequestHandler + end + end + + alias NumericValue = Float32 | Float64 | Int32 | Int64 + + lib LibYAML + end + + struct TagDirective + end + + enum Time::DayOfWeek + end + ) + subject.catch(s).should be_valid + end + + it_reports_name "class My_class; end", "MyClass" + it_reports_name "module HTT_p; end", "HTTP" + it_reports_name "alias Numeric_value = Int32", "NumericValue" + it_reports_name "lib Lib_YAML; end", "LibYAML" + it_reports_name "struct Tag_directive; end", "TagDirective" + it_reports_name "enum Time_enum::Day_of_week; end", "TimeEnum::DayOfWeek" + + it "reports rule, pos and message" do + s = Source.new %( + class My_class + end + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:2:3" + issue.message.should eq( + "Type name should be camelcased: MyClass, but it was My_class" + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr new file mode 100644 index 000000000000..bb8b6b25849f --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr @@ -0,0 +1,45 @@ +require "../../../spec_helper" + +module Ameba::Rule::Style + subject = UnlessElse.new + + describe UnlessElse do + it "passes if unless hasn't else" do + s = Source.new %( + unless something + :ok + end + ) + subject.catch(s).should be_valid + end + + it "fails if unless has else" do + s = Source.new %( + unless something + :one + else + :two + end + ) + subject.catch(s).should_not be_valid + end + + it "reports rule, pos and message" do + s = Source.new %( + unless something + :one + else + :two + end + ), "source.cr" + subject.catch(s).should_not be_valid + + issue = s.issues.first + issue.should_not be_nil + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:5:3" + issue.message.should eq "Favour if over unless with else" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr new file mode 100644 index 000000000000..6ec6ae9f19b6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr @@ -0,0 +1,62 @@ +require "../../../spec_helper" + +module Ameba + subject = Rule::Style::VariableNames.new + + private def it_reports_var_name(code, expected) + it "reports method name #{expected}" do + s = Source.new code + Rule::Style::VariableNames.new.catch(s).should_not be_valid + s.issues.first.message.should contain expected + end + end + + describe Rule::Style::VariableNames do + it "passes if var names are underscore-cased" do + s = Source.new %( + class Greeting + @@default_greeting = "Hello world" + + def initialize(@custom_greeting = nil) + end + + def print_greeting + greeting = @custom_greeting || @@default_greeting + puts greeting + end + end + ) + subject.catch(s).should be_valid + end + + it_reports_var_name %(myBadNamedVar = 1), "my_bad_named_var" + it_reports_var_name %(wrong_Name = 'y'), "wrong_name" + + it_reports_var_name %( + class Greeting + def initialize(@badNamed = nil) + end + end + ), "bad_named" + + it_reports_var_name %( + class Greeting + @@defaultGreeting = "Hello world" + end + ), "default_greeting" + + it "reports rule, pos and message" do + s = Source.new %( + badName = "Yeah" + ), "source.cr" + subject.catch(s).should_not be_valid + issue = s.issues.first + issue.rule.should_not be_nil + issue.location.to_s.should eq "source.cr:1:1" + issue.end_location.to_s.should eq "source.cr:1:7" + issue.message.should eq( + "Var name should be underscore-cased: bad_name, not badName" + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr new file mode 100644 index 000000000000..6d9d85da208e --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr @@ -0,0 +1,43 @@ +require "../../../spec_helper" + +valid_source = <<-EOF +a = 1 +loop do + a += 1 + break if a > 5 +end +EOF + +invalid_source = <<-EOF +a = 1 +while true + a += 1 + break if a > 5 +end +EOF + +module Ameba::Rule::Style + subject = WhileTrue.new + + describe WhileTrue do + it "passes if there is no `while true`" do + source = Source.new valid_source + subject.catch(source).should be_valid + end + + it "fails if there is `while true`" do + source = Source.new invalid_source + subject.catch(source).should_not be_valid + end + + it "reports rule, pos and message" do + source = Source.new invalid_source, "source.cr" + subject.catch(source).should_not be_valid + + issue = source.issues.first + issue.location.to_s.should eq "source.cr:2:1" + issue.end_location.to_s.should eq "source.cr:5:3" + issue.message.should eq "While statement using true literal as condition" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr new file mode 100644 index 000000000000..16957e687790 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr @@ -0,0 +1,183 @@ +require "../spec_helper" + +module Ameba + private def runner(files = [__FILE__], formatter = DummyFormatter.new) + config = Config.load + config.formatter = formatter + config.globs = files + + config.update_rule ErrorRule.rule_name, enabled: false + + Runner.new(config) + end + + describe Runner do + formatter = DummyFormatter.new + default_severity = Severity::Convention + + describe "#run" do + it "returns self" do + runner.run.should_not be_nil + end + + it "calls started callback" do + runner(formatter: formatter).run + formatter.started_sources.should_not be_nil + end + + it "calls finished callback" do + runner(formatter: formatter).run + formatter.finished_sources.should_not be_nil + end + + it "calls source_started callback" do + runner(formatter: formatter).run + formatter.started_source.should_not be_nil + end + + it "calls source_finished callback" do + runner(formatter: formatter).run + formatter.finished_source.should_not be_nil + end + + it "skips rule check if source is excluded" do + path = "source.cr" + source = Source.new "", path + + all_rules = ([] of Rule::Base).tap do |rules| + rule = ErrorRule.new + rule.excluded = [path] + rules << rule + end + + Runner.new(all_rules, [source], formatter, default_severity).run.success?.should be_true + end + + context "exception in rule" do + it "raises an exception raised in fiber while running a rule" do + rule = RaiseRule.new + rule.should_raise = true + rules = [rule] of Rule::Base + source = Source.new "", "source.cr" + + expect_raises(Exception, "something went wrong") do + Runner.new(rules, [source], formatter, default_severity).run + end + end + end + + context "invalid syntax" do + it "reports a syntax error" do + rules = [Rule::Lint::Syntax.new] of Rule::Base + source = Source.new "def bad_syntax" + + Runner.new(rules, [source], formatter, default_severity).run + source.should_not be_valid + source.issues.first.rule.name.should eq Rule::Lint::Syntax.rule_name + end + + it "does not run other rules" do + rules = [Rule::Lint::Syntax.new, Rule::Style::ConstantNames.new] of Rule::Base + source = Source.new %q( + MyBadConstant = 1 + + when my_bad_syntax + ) + + Runner.new(rules, [source], formatter, default_severity).run + source.should_not be_valid + source.issues.size.should eq 1 + end + end + + context "unneeded disables" do + it "reports an issue if such disable exists" do + rules = [Rule::Lint::UnneededDisableDirective.new] of Rule::Base + source = Source.new %( + a = 1 # ameba:disable LineLength + ) + + Runner.new(rules, [source], formatter, default_severity).run + source.should_not be_valid + source.issues.first.rule.name.should eq Rule::Lint::UnneededDisableDirective.rule_name + end + end + end + + describe "#explain" do + io = IO::Memory.new + + it "writes nothing if sources are valid" do + io.clear + runner = runner(formatter: formatter).run + runner.explain({file: "source.cr", line: 1, column: 2}, io) + io.to_s.should be_empty + end + + it "writes the explanation if sources are not valid and location found" do + io.clear + rules = [ErrorRule.new] of Rule::Base + source = Source.new %( + a = 1 + ), "source.cr" + + runner = Runner.new(rules, [source], formatter, default_severity).run + runner.explain({file: "source.cr", line: 1, column: 1}, io) + io.to_s.should_not be_empty + end + + it "writes nothing if sources are not valid and location is not found" do + io.clear + rules = [ErrorRule.new] of Rule::Base + source = Source.new %( + a = 1 + ), "source.cr" + + runner = Runner.new(rules, [source], formatter, default_severity).run + runner.explain({file: "source.cr", line: 1, column: 2}, io) + io.to_s.should be_empty + end + end + + describe "#success?" do + it "returns true if runner has not been run" do + runner.success?.should be_true + end + + it "returns true if all sources are valid" do + runner.run.success?.should be_true + end + + it "returns false if there are invalid sources" do + rules = Rule.rules.map &.new.as(Rule::Base) + s = Source.new %q( + WrongConstant = 5 + ) + Runner.new(rules, [s], formatter, default_severity).run.success?.should be_false + end + + it "depends on the level of severity" do + rules = Rule.rules.map &.new.as(Rule::Base) + s = Source.new %q(WrongConstant = 5) + Runner.new(rules, [s], formatter, Severity::Error).run.success?.should be_true + Runner.new(rules, [s], formatter, Severity::Warning).run.success?.should be_true + Runner.new(rules, [s], formatter, Severity::Convention).run.success?.should be_false + end + + it "returns false if issue is disabled" do + rules = [NamedRule.new] of Rule::Base + source = Source.new %( + def foo + bar = 1 # ameba:disable #{NamedRule.name} + end + ) + source.add_issue NamedRule.new, location: {2, 1}, + message: "Useless assignment" + + Runner + .new(rules, [source], formatter, default_severity) + .run.success?.should be_true + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr new file mode 100644 index 000000000000..94f3b7304cd2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr @@ -0,0 +1,108 @@ +require "../spec_helper" + +module Ameba + describe Severity do + describe "#symbol" do + it "returns the symbol for each severity in the enum" do + Severity.values.each do |severity| + severity.symbol.should_not be_nil + end + end + + it "returns symbol for Error" do + Severity::Error.symbol.should eq 'E' + end + + it "returns symbol for Warning" do + Severity::Warning.symbol.should eq 'W' + end + + it "returns symbol for Convention" do + Severity::Convention.symbol.should eq 'C' + end + end + + describe ".parse" do + it "creates error severity by name" do + Severity.parse("Error").should eq Severity::Error + end + + it "creates warning severity by name" do + Severity.parse("Warning").should eq Severity::Warning + end + + it "creates convention severity by name" do + Severity.parse("Convention").should eq Severity::Convention + end + + it "raises when name is incorrect" do + expect_raises(Exception, "Incorrect severity name BadName. Try one of [Error, Warning, Convention]") do + Severity.parse("BadName") + end + end + end + end + + struct SeverityConvertable + include YAML::Serializable + + @[YAML::Field(converter: Ameba::SeverityYamlConverter)] + property severity : Severity + end + + describe SeverityYamlConverter do + describe ".from_yaml" do + it "converts from yaml to Severity::Error" do + yaml = {severity: "error"}.to_yaml + converted = SeverityConvertable.from_yaml(yaml) + converted.severity.should eq Severity::Error + end + + it "converts from yaml to Severity::Warning" do + yaml = {severity: "warning"}.to_yaml + converted = SeverityConvertable.from_yaml(yaml) + converted.severity.should eq Severity::Warning + end + + it "converts from yaml to Severity::Convention" do + yaml = {severity: "convention"}.to_yaml + converted = SeverityConvertable.from_yaml(yaml) + converted.severity.should eq Severity::Convention + end + + it "raises if severity is not a scalar" do + yaml = {severity: {convention: true}}.to_yaml + expect_raises(Exception, "Severity must be a scalar") do + SeverityConvertable.from_yaml(yaml) + end + end + + it "raises if severity has a wrong type" do + yaml = {severity: [1, 2, 3]}.to_yaml + expect_raises(Exception, "Severity must be a scalar") do + SeverityConvertable.from_yaml(yaml) + end + end + end + + describe ".to_yaml" do + it "converts Severity::Error to yaml" do + yaml = {severity: "error"}.to_yaml + converted = SeverityConvertable.from_yaml(yaml).to_yaml + converted.should eq "---\nseverity: Error\n" + end + + it "converts Severity::Warning to yaml" do + yaml = {severity: "warning"}.to_yaml + converted = SeverityConvertable.from_yaml(yaml).to_yaml + converted.should eq "---\nseverity: Warning\n" + end + + it "converts Severity::Convention to yaml" do + yaml = {severity: "convention"}.to_yaml + converted = SeverityConvertable.from_yaml(yaml).to_yaml + converted.should eq "---\nseverity: Convention\n" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr new file mode 100644 index 000000000000..cd8af8759757 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr @@ -0,0 +1,38 @@ +require "../spec_helper" + +module Ameba + describe Source do + describe ".new" do + it "allows to create a source by code and path" do + s = Source.new("code", "path") + s.path.should eq "path" + s.code.should eq "code" + s.lines.should eq ["code"] + end + end + + describe "#fullpath" do + it "returns a relative path of the source" do + s = Source.new "", "./source_spec.cr" + s.fullpath.should contain "source_spec.cr" + end + + it "returns fullpath if path is blank" do + s = Source.new "", "" + s.fullpath.should_not be_nil + end + end + + describe "#matches_path?" do + it "returns true if source's path is matched" do + s = Source.new "", "source.cr" + s.matches_path?("source.cr").should be_true + end + + it "returns false if source's path is not matched" do + s = Source.new "", "source.cr" + s.matches_path?("new_source.cr").should be_false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr new file mode 100644 index 000000000000..2970f9d83194 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr @@ -0,0 +1,44 @@ +require "../spec_helper" + +module Ameba + private def it_tokenizes(str, expected) + it "tokenizes #{str}" do + ([] of Symbol).tap do |token_types| + Tokenizer.new(Source.new str, normalize: false) + .run { |token| token_types << token.type } + .should be_true + end.should eq expected + end + end + + describe Tokenizer do + describe "#run" do + it_tokenizes %("string"), %i(DELIMITER_START STRING DELIMITER_END EOF) + it_tokenizes %(100), %i(NUMBER EOF) + it_tokenizes %('a'), %i(CHAR EOF) + it_tokenizes %([]), %i([] EOF) + it_tokenizes %([] of String), %i([] SPACE IDENT SPACE CONST EOF) + it_tokenizes %q("str #{3}"), %i( + DELIMITER_START STRING INTERPOLATION_START NUMBER } DELIMITER_END EOF + ) + + it_tokenizes %(%w(1 2)), + %i(STRING_ARRAY_START STRING STRING STRING_ARRAY_END EOF) + + it_tokenizes %(%i(one two)), + %i(SYMBOL_ARRAY_START STRING STRING STRING_ARRAY_END EOF) + + it_tokenizes %( + class A + def method + puts "hello" + end + end + ), %i( + NEWLINE SPACE IDENT SPACE CONST NEWLINE SPACE IDENT SPACE IDENT + NEWLINE SPACE IDENT SPACE DELIMITER_START STRING DELIMITER_END + NEWLINE SPACE IDENT NEWLINE SPACE IDENT NEWLINE SPACE EOF + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr new file mode 100644 index 000000000000..48e203552980 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr @@ -0,0 +1,4 @@ +require "./spec_helper" + +describe Ameba do +end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr new file mode 100644 index 000000000000..a1739beccb47 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr @@ -0,0 +1,200 @@ +require "spec" +require "../src/ameba" + +module Ameba + # Dummy Rule which does nothing. + struct DummyRule < Rule::Base + properties do + description : String = "Dummy rule that does nothing." + end + + def test(source) + end + end + + class Source + def initialize(code : String, @path = "", normalize = true) + @code = normalize ? normalize_source(code) : code + end + + private def normalize_source(code, separator = "\n") + lines = code.split(separator) + + # remove unneeded first blank lines if any + lines.shift if lines[0].blank? && lines.size > 1 + + # find the minimum indentation + min_indent = lines.min_of do |line| + line.blank? ? code.size : line.size - line.lstrip.size + end + + # remove the width of minimum indentation in each line + lines + .map! { |line| line.blank? ? line : line[min_indent..-1] } + .join(separator) + end + end + + struct NamedRule < Rule::Base + properties do + description "A rule with a custom name." + end + + def self.name + "BreakingRule" + end + end + + struct ErrorRule < Rule::Base + properties do + description "Always adds an error at 1:1" + end + + def test(source) + issue_for({1, 1}, "This rule always adds an error") + end + end + + struct ScopeRule < Rule::Base + @[YAML::Field(ignore: true)] + getter scopes = [] of AST::Scope + + properties do + description "Internal rule to test scopes" + end + + def test(source, node : Crystal::ASTNode, scope : AST::Scope) + @scopes << scope + end + end + + struct FlowExpressionRule < Rule::Base + @[YAML::Field(ignore: true)] + getter expressions = [] of AST::FlowExpression + + properties do + description "Internal rule to test flow expressions" + end + + def test(source, node, flow_expression : AST::FlowExpression) + @expressions << flow_expression + end + end + + struct RedundantControlExpressionRule < Rule::Base + @[YAML::Field(ignore: true)] + getter nodes = [] of Crystal::ASTNode + + properties do + description "Internal rule to test redundant control expressions" + end + + def test(source, node, visitor : AST::RedundantControlExpressionVisitor) + nodes << node + end + end + + # A rule that always raises an error + struct RaiseRule < Rule::Base + property should_raise = false + + properties do + description "Internal rule that always raises" + end + + def test(source) + should_raise && raise "something went wrong" + end + end + + class DummyFormatter < Formatter::BaseFormatter + property started_sources : Array(Source)? + property finished_sources : Array(Source)? + property started_source : Source? + property finished_source : Source? + + def started(sources) + @started_sources = sources + end + + def source_finished(source : Source) + @started_source = source + end + + def source_started(source : Source) + @finished_source = source + end + + def finished(sources) + @finished_sources = sources + end + end + + struct BeValidExpectation + def match(source) + source.valid? + end + + def failure_message(source) + String.build do |str| + str << "Source expected to be valid, but there are issues: \n\n" + source.issues.reject(&.disabled?).each do |e| + str << " * #{e.rule.name}: #{e.message}\n" + end + end + end + + def negative_failure_message(source) + "Source expected to be invalid, but it is valid." + end + end + + class TestNodeVisitor < Crystal::Visitor + NODES = [ + Crystal::NilLiteral, + Crystal::Var, + Crystal::Assign, + Crystal::OpAssign, + Crystal::MultiAssign, + Crystal::Block, + Crystal::Macro, + Crystal::Def, + Crystal::If, + Crystal::While, + Crystal::MacroLiteral, + Crystal::Expressions, + Crystal::ControlExpression, + ] + + def initialize(node) + node.accept self + end + + def visit(node : Crystal::ASTNode) + true + end + + {% for node in NODES %} + {{getter_name = node.stringify.split("::").last.underscore + "_nodes"}} + + getter {{getter_name.id}} = [] of {{node}} + + def visit(node : {{node}}) + {{getter_name.id}} << node + true + end + {% end %} + end +end + +def be_valid + Ameba::BeValidExpectation.new +end + +def as_node(source) + Crystal::Parser.new(source).parse +end + +def as_nodes(source) + Ameba::TestNodeVisitor.new(as_node source) +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba.cr new file mode 100644 index 000000000000..4649b00c1585 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba.cr @@ -0,0 +1,42 @@ +require "./ameba/*" +require "./ameba/ast/**" +require "./ameba/rule/**" +require "./ameba/formatter/*" + +# Ameba's entry module. +# +# To run the linter with default parameters: +# +# ``` +# Ameba.run +# ``` +# +# To configure and run it: +# +# ``` +# config = Ameba::Config.load +# config.formatter = formatter +# config.files = file_paths +# +# Ameba.run config +# ``` +# +module Ameba + extend self + + VERSION = {{ `shards version "#{__DIR__}"`.chomp.stringify }} + + # Initializes `Ameba::Runner` and runs it. + # Can be configured via `config` parameter. + # + # Examples: + # + # ``` + # Ameba.run + # Ameba.run config + # ``` + # + def run(config = Config.load) + Runner.new(config).run + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr new file mode 100644 index 000000000000..dcca21aa8a9f --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr @@ -0,0 +1,194 @@ +module Ameba::AST + # Represents the branch in Crystal code. + # Branch is a part of a branchable statement. + # For example, the branchable if statement contains 3 branches: + # + # ``` + # if a = something # --> Branch A + # a = 1 # --> Branch B + # put a if out # --> Branch C + # else + # do_something a # --> Branch D + # end + # ``` + # + class Branch + # The actual branch node. + getter node : Crystal::ASTNode + + # The parent branchable. + getter parent : Branchable + + delegate to_s, to: @node + delegate location, to: @node + delegate end_location, to: @node + + def_equals_and_hash node, location + + # Creates a new branch. + # + # ``` + # Branch.new(if_node) + # ``` + def initialize(@node, @parent) + end + + # Returns true if current branch is in a loop, false - otherwise. + # For example, this branch is in a loop: + # + # ``` + # while true + # handle_input # this branch is in a loop + # if wrong_input + # show_message # this branch is also in a loop. + # end + # end + # ``` + # + def in_loop? + @parent.loop? + end + + # Constructs a new branch based on the node in scope. + # + # ``` + # Branch.of(assign_node, scope) + # ``` + def self.of(node : Crystal::ASTNode, scope : Scope) + of(node, scope.node) + end + + # Constructs a new branch based on the node some parent scope. + # + # ``` + # Branch.of(assign_node, def_node) + # ``` + def self.of(node : Crystal::ASTNode, parent_node : Crystal::ASTNode) + BranchVisitor.new(node).tap(&.accept parent_node).branch + end + + # :nodoc: + private class BranchVisitor < Crystal::Visitor + @current_branch : Crystal::ASTNode? + + property branchable : Branchable? + property branch : Branch? + + def initialize(@node : Crystal::ASTNode) + end + + private def on_branchable_start(node, *branches) + on_branchable_start(node, branches) + end + + private def on_branchable_start(node, branches : Array | Tuple) + @branchable = Branchable.new(node, @branchable) + + branches.each do |branch_node| + break if branch # branch found + @current_branch = branch_node if branch_node && !branch_node.nop? + branch_node.try &.accept(self) + end + + false + end + + private def on_branchable_end(node) + @branchable = @branchable.try &.parent + end + + def visit(node : Crystal::ASTNode) + return false if branch + + if node.class == @node.class && + node.location == @node.location && + (branchable = @branchable) && + (branch = @current_branch) + @branch = Branch.new(branch, branchable) + end + + true + end + + def visit(node : Crystal::If) + on_branchable_start node, node.cond, node.then, node.else + end + + def end_visit(node : Crystal::If) + on_branchable_end node + end + + def visit(node : Crystal::Unless) + on_branchable_start node, node.cond, node.then, node.else + end + + def end_visit(node : Crystal::Unless) + on_branchable_end node + end + + def visit(node : Crystal::BinaryOp) + on_branchable_start node, node.left, node.right + end + + def end_visit(node : Crystal::BinaryOp) + on_branchable_end node + end + + def visit(node : Crystal::Case) + on_branchable_start node, [node.cond, node.whens, node.else].flatten + end + + def end_visit(node : Crystal::Case) + on_branchable_end node + end + + def visit(node : Crystal::While) + on_branchable_start node, node.cond, node.body + end + + def end_visit(node : Crystal::While) + on_branchable_end node + end + + def visit(node : Crystal::Until) + on_branchable_start node, node.cond, node.body + end + + def end_visit(node : Crystal::Until) + on_branchable_end node + end + + def visit(node : Crystal::ExceptionHandler) + on_branchable_start node, [node.body, node.rescues, node.else, node.ensure].flatten + end + + def end_visit(node : Crystal::ExceptionHandler) + on_branchable_end node + end + + def visit(node : Crystal::Rescue) + on_branchable_start node, node.body + end + + def end_visit(node : Crystal::Rescue) + on_branchable_end node + end + + def visit(node : Crystal::MacroIf) + on_branchable_start node, node.cond, node.then, node.else + end + + def end_visit(node : Crystal::MacroIf) + on_branchable_end node + end + + def visit(node : Crystal::MacroFor) + on_branchable_start node, node.exp, node.body + end + + def end_visit(node : Crystal::MacroFor) + on_branchable_end node + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr new file mode 100644 index 000000000000..2495dbb2ab81 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr @@ -0,0 +1,44 @@ +require "./util" + +module Ameba::AST + # A generic entity to represent a branchable Crystal node. + # For example, `Crystal::If`, `Crystal::Unless`, `Crystal::While` + # are branchables. + # + # ``` + # white a > 100 # Branchable A + # if b > 2 # Branchable B + # a += 1 + # end + # end + # ``` + class Branchable + include Util + + getter branches = [] of Crystal::ASTNode + + # The actual Crystal node. + getter node : Crystal::ASTNode + + # Parent branchable (if any) + getter parent : Branchable? + + delegate to_s, to: @node + delegate location, to: @node + delegate end_location, to: @node + + # Creates a new branchable + # + # ``` + # Branchable.new(node, parent_branchable) + # ``` + def initialize(@node, @parent = nil) + end + + # Returns true if this node or one of the parent branchables is a loop, false otherwise. + def loop? + return true if loop?(node) + parent.try(&.loop?) || false + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr new file mode 100644 index 000000000000..6b21496224c8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr @@ -0,0 +1,69 @@ +require "./util" + +module Ameba::AST + # Represents a flow expression in Crystal code. + # For example, + # + # ``` + # def foobar + # a = 3 + # return 42 # => flow expression + # a + 1 + # end + # ``` + # + # Flow expression contains an actual node of a control expression and + # a parent node, which allows easily search through the related statement + # (i.e. find unreachable code) + class FlowExpression + include Util + + # Is true only if some of the nodes parents is a loop. + getter? in_loop : Bool + + # The actual node of the flow expression. + getter node : Crystal::ASTNode + + delegate to_s, to: @node + delegate location, to: @node + delegate end_location, to: @node + + # Creates a new flow expression. + # + # ``` + # FlowExpression.new(node, parent_node) + # ``` + def initialize(@node, @in_loop) + end + + # Returns nodes which can't be reached because of a flow command inside. + # For example: + # + # ``` + # def foobar + # a = 1 + # return 42 + # + # a + 2 # => unreachable assign node + # end + # ``` + def unreachable_nodes + unreachable_nodes = [] of Crystal::ASTNode + + case current_node = node + when Crystal::Expressions + control_flow_found = false + current_node.expressions.each do |exp| + unreachable_nodes << exp if control_flow_found + control_flow_found ||= flow_expression?(exp, in_loop?) + end + when Crystal::BinaryOp + unreachable_nodes << current_node.right if flow_expression?(current_node.left, in_loop?) + else + # nop + end + + unreachable_nodes + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr new file mode 100644 index 000000000000..3627ef73e067 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr @@ -0,0 +1,180 @@ +require "./variabling/*" + +module Ameba::AST + # Represents a context of the local variable visibility. + # This is where the local variables belong to. + class Scope + # Link to local variables + getter variables = [] of Variable + + # Link to all variable references in currency scope + getter references = [] of Reference + + # Link to the arguments in current scope + getter arguments = [] of Argument + + # Link to the instance variables used in current scope + getter ivariables = [] of InstanceVariable + + # Link to the outer scope + getter outer_scope : Scope? + + # List of inner scopes + getter inner_scopes = [] of Scope + + # The actual AST node that represents a current scope. + getter node : Crystal::ASTNode + + delegate to_s, to: node + delegate location, to: node + delegate end_location, to: node + + def_equals_and_hash node, location + + # Creates a new scope. Accepts the AST node and the outer scope. + # + # ``` + # scope = Scope.new(class_node, nil) + # ``` + def initialize(@node, @outer_scope = nil) + @outer_scope.try &.inner_scopes.<<(self) + end + + # Creates a new variable in the current scope. + # + # ``` + # scope = Scope.new(class_node, nil) + # scope.add_variable(var_node) + # ``` + def add_variable(node) + variables << Variable.new(node, self) + end + + # Creates a new argument in the current scope. + # + # ``` + # scope = Scope.new(class_node, nil) + # scope.add_argument(arg_node) + # ``` + def add_argument(node) + add_variable Crystal::Var.new(node.name).at(node) + arguments << Argument.new(node, variables.last) + end + + # Adds a new instance variable to the current scope. + # + # ``` + # scope = Scope.new(class_node, nil) + # scope.add_ivariable(ivar_node) + # ``` + def add_ivariable(node) + ivariables << InstanceVariable.new(node) + end + + # Returns variable by its name or nil if it does not exist. + # + # ``` + # scope = Scope.new(class_node, nil) + # scope.find_variable("foo") + # ``` + def find_variable(name : String) + variables.find { |v| v.name == name } || outer_scope.try &.find_variable(name) + end + + # Creates a new assignment for the variable. + # + # ``` + # scope = Scope.new(class_node, nil) + # scope.assign_variable(var_name, assign_node) + # ``` + def assign_variable(name, node) + find_variable(name).try &.assign(node, self) + end + + # Returns true if current scope represents a block (or proc), + # false if not. + def block? + node.is_a?(Crystal::Block) || node.is_a?(Crystal::ProcLiteral) + end + + # Returns true if current scope represents a spawn block, e. g. + # + # ``` + # spawn do + # # ... + # end + # ``` + def spawn_block? + return false unless node.is_a?(Crystal::Block) + call = node.as(Crystal::Block).call + call.try(&.name) == "spawn" + end + + # Returns true if current scope sits inside a macro. + def in_macro? + node.is_a?(Crystal::Macro) || !!outer_scope.try(&.in_macro?) + end + + # Returns true instance variable assinged in this scope. + def assigns_ivar?(name) + arguments.find { |arg| arg.name == name } && + ivariables.find { |var| var.name == "@#{name}" } + end + + # Returns true if and only if current scope represents some + # type definition, for example a class. + def type_definition? + node.is_a?(Crystal::ClassDef) || + node.is_a?(Crystal::ModuleDef) || + node.is_a?(Crystal::LibDef) || + node.is_a?(Crystal::FunDef) || + node.is_a?(Crystal::TypeDef) || + node.is_a?(Crystal::CStructOrUnionDef) + end + + # Returns true if current scope (or any of inner scopes) references variable, + # false if not. + def references?(variable : Variable) + variable.references.any? do |reference| + reference.scope == self || + inner_scopes.any?(&.references? variable) + end || variable.used_in_macro? + end + + # Returns true if current scope is a def, false if not. + def def? + node.is_a? Crystal::Def + end + + # Returns true if this scope is a top level scope, false if not. + def top_level? + outer_scope.nil? + end + + # Returns true if var is an argument in current scope, false if not. + def arg?(var) + case current_node = node + when Crystal::Def + var.is_a?(Crystal::Arg) && any_arg?(current_node.args, var) + when Crystal::Block + var.is_a?(Crystal::Var) && any_arg?(current_node.args, var) + when Crystal::ProcLiteral + var.is_a?(Crystal::Var) && any_arg?(current_node.def.args, var) + else + false + end + end + + private def any_arg?(args, var) + args.any? { |arg| arg.name == var.name && arg.location == var.location } + end + + # Returns true if the `node` represents exactly + # the same Crystal node as `@node`. + def eql?(node) + node == @node && + !node.location.nil? && + node.location == @node.location + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr new file mode 100644 index 000000000000..28f55dd0ac1e --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr @@ -0,0 +1,157 @@ +# Utility module for Ameba's rules. +module Ameba::AST::Util + # Returns true if current `node` is a literal, false otherwise. + def literal?(node) + case node + when Crystal::NilLiteral, + Crystal::BoolLiteral, + Crystal::NumberLiteral, + Crystal::CharLiteral, + Crystal::StringLiteral, + Crystal::SymbolLiteral, + Crystal::RegexLiteral, + Crystal::ProcLiteral, + Crystal::MacroLiteral + true + when Crystal::RangeLiteral + literal?(node.from) && literal?(node.to) + when Crystal::ArrayLiteral, + Crystal::TupleLiteral + node.elements.all? { |el| literal?(el) } + when Crystal::HashLiteral + node.entries.all? { |entry| literal?(entry.key) && literal?(entry.value) } + when Crystal::NamedTupleLiteral + node.entries.all? { |entry| literal?(entry.value) } + else + false + end + end + + # Returns a source code for the current node. + # This method uses `node.location` and `node.end_location` + # to determine and cut a piece of source of the node. + def node_source(node, code_lines) + loc, end_loc = node.location, node.end_location + + return unless loc && end_loc + + line, column = loc.line_number - 1, loc.column_number - 1 + end_line, end_column = end_loc.line_number - 1, end_loc.column_number - 1 + node_lines = code_lines[line..end_line] + first_line, last_line = node_lines[0]?, node_lines[-1]? + + return if first_line.nil? || last_line.nil? + return if first_line.size < column # compiler reports incorrection location + + node_lines[0] = first_line.sub(0...column, "") + + if line == end_line # one line + end_column = end_column - column + last_line = node_lines[0] + end + + return if last_line.size < end_column + 1 + node_lines[-1] = last_line.sub(end_column + 1...last_line.size, "") + + node_lines + end + + # Returns true if node is a flow command, false - otherwise. + # Node represents a flow command if it is a control expression, + # or special call node that interrupts execution (i.e. raise, exit, abort). + def flow_command?(node, in_loop) + case node + when Crystal::Return + true + when Crystal::Break, Crystal::Next + in_loop + when Crystal::Call + raise?(node) || exit?(node) || abort?(node) + else + false + end + end + + # Returns true if node is a flow expression, false if not. + # Node represents a flow expression if it is full-filled by a flow command. + # + # For example, this node is a flow expression, because each branch contains + # a flow command `return`: + # + # ``` + # if a > 0 + # return :positive + # elsif a < 0 + # return :negative + # else + # return :zero + # end + # ``` + # + # This node is a not a flow expression: + # + # ``` + # if a > 0 + # return :positive + # end + # ``` + # + # That's because not all branches return(i.e. `else` is missing). + # + def flow_expression?(node, in_loop = false) + return true if flow_command? node, in_loop + + case node + when Crystal::If, Crystal::Unless + flow_expressions? [node.then, node.else], in_loop + when Crystal::BinaryOp + flow_expression? node.left, in_loop + when Crystal::Case + flow_expressions? [node.whens, node.else].flatten, in_loop + when Crystal::ExceptionHandler + flow_expressions? [node.else || node.body, node.rescues].flatten, in_loop + when Crystal::While, Crystal::Until + flow_expression? node.body, in_loop + when Crystal::Rescue, Crystal::When + flow_expression? node.body, in_loop + when Crystal::Expressions + node.expressions.any? { |exp| flow_expression? exp, in_loop } + else + false + end + end + + private def flow_expressions?(nodes, in_loop) + nodes.all? { |exp| flow_expression? exp, in_loop } + end + + # Returns true if node represents `raise` method call. + def raise?(node) + node.is_a?(Crystal::Call) && + node.name == "raise" && node.args.size == 1 && node.obj.nil? + end + + # Returns true if node represents `exit` method call. + def exit?(node) + node.is_a?(Crystal::Call) && + node.name == "exit" && node.args.size <= 1 && node.obj.nil? + end + + # Returns true if node represents `abort` method call. + def abort?(node) + node.is_a?(Crystal::Call) && + node.name == "abort" && node.args.size <= 2 && node.obj.nil? + end + + # Returns true if node represents a loop. + def loop?(node) + case node + when Crystal::While, Crystal::Until + true + when Crystal::Call + node.name == "loop" && node.args.size == 0 && node.obj.nil? + else + false + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr new file mode 100644 index 000000000000..91204f0e2f77 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr @@ -0,0 +1,49 @@ +module Ameba::AST + # Represents the argument of some node. + # Holds the reference to the variable, thus to scope. + # + # For example, all these vars are arguments: + # + # ``` + # def method(a, b, c = 10, &block) + # 3.times do |i| + # end + # + # ->(x : Int32) {} + # end + # ``` + class Argument + # The actual node. + getter node : Crystal::Var | Crystal::Arg + + # Variable of this argument (may be the same node) + getter variable : Variable + + delegate location, to: @node + delegate end_location, to: @node + delegate to_s, to: @node + + # Creates a new argument. + # + # ``` + # Argument.new(node, variable) + # ``` + def initialize(@node, @variable) + end + + # Returns true if the name starts with '_', false if not. + def ignored? + name.starts_with? '_' + end + + # Name of the argument. + def name + case current_node = node + when Crystal::Var then current_node.name + when Crystal::Arg then current_node.name + else + raise ArgumentError.new "invalid node" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr new file mode 100644 index 000000000000..6908572a20e2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr @@ -0,0 +1,106 @@ +require "./reference" +require "./variable" + +module Ameba::AST + # Represents the assignment to the variable. + # Holds the assign node and the variable. + class Assignment + property? referenced = false + + # The actual assignment node. + getter node : Crystal::ASTNode + + # Variable of this assignment. + getter variable : Variable + + # Branch of this assignment. + getter branch : Branch? + + # A scope assignment belongs to + getter scope : Scope + + delegate to_s, to: @node + delegate location, to: @node + delegate end_location, to: @node + + # Creates a new assignment. + # + # ``` + # Assignment.new(node, variable, scope) + # ``` + # + def initialize(@node, @variable, @scope) + if scope = @variable.scope + @branch = Branch.of(@node, scope) + @referenced = true if @variable.special? || + @variable.scope.type_definition? || + referenced_in_loop? + end + end + + def referenced_in_loop? + @variable.referenced? && @branch.try &.in_loop? + end + + # Returns true if this assignment is an op assign, false if not. + # For example, this is an op assign: + # + # ``` + # a ||= 1 + # ``` + def op_assign? + node.is_a? Crystal::OpAssign + end + + # Returns true if this assignment is in a branch, false if not. + # For example, this assignment is in a branch: + # + # ``` + # a = 1 if a.nil? + # ``` + def in_branch? + !branch.nil? + end + + # Returns the target node of the variable in this assignment. + def target_node + case assign = node + when Crystal::Assign then assign.target + when Crystal::OpAssign then assign.target + when Crystal::UninitializedVar then assign.var + when Crystal::MultiAssign + assign.targets.find(node) do |target| + target.is_a?(Crystal::Var) && target.name == variable.name + end + else + node + end + end + + # Indicates whether the node is a transformed assignment by the compiler. + # i.e. + # + # ``` + # collection.each do |(a, b)| + # puts b + # end + # ``` + # + # is transformed to: + # + # ``` + # collection.each do |__arg0| + # a = __arg0[0] + # b = __arg0[1] + # puts(b) + # end + # ``` + # + def transformed? + return false unless (assign = node).is_a?(Crystal::Assign) + return false unless (value = assign.value).is_a?(Crystal::Call) + return false unless (obj = value.obj).is_a?(Crystal::Var) + obj.name.starts_with? "__arg" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr new file mode 100644 index 000000000000..836ad345fabb --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr @@ -0,0 +1,13 @@ +module Ameba::AST + class InstanceVariable + getter node : Crystal::InstanceVar + + delegate location, to: @node + delegate end_location, to: @node + delegate name, to: @node + delegate to_s, to: @node + + def initialize(@node) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr new file mode 100644 index 000000000000..885234d9b11e --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr @@ -0,0 +1,10 @@ +require "./variable" + +module Ameba::AST + # Represents a reference to the variable. + # It behaves like a variable is used to distinguish a + # the variable from its reference. + class Reference < Variable + property? explicit = true + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr new file mode 100644 index 000000000000..1a7eab42a3ed --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr @@ -0,0 +1,198 @@ +module Ameba::AST + # Represents the existence of the local variable. + # Holds the var node and variable assigments. + class Variable + # List of the assigments of this variable. + getter assignments = [] of Assignment + + # List of the references of this variable. + getter references = [] of Reference + + # The actual var node. + getter node : Crystal::Var + + # Scope of this variable. + getter scope : Scope + + # Node of the first assignment which can be available before any reference. + getter assign_before_reference : Crystal::ASTNode? + + delegate location, to: @node + delegate end_location, to: @node + delegate name, to: @node + delegate to_s, to: @node + + # Creates a new variable(in the scope). + # + # ``` + # Variable.new(node, scope) + # ``` + # + def initialize(@node, @scope) + end + + # Returns true if it is a special variable, i.e `$?`. + def special? + @node.special_var? + end + + # Assigns the variable (creates a new assignment). + # Variable may have multiple assignments. + # + # ``` + # variable = Variable.new(node, scope) + # variable.assign(node1) + # variable.assign(node2) + # variable.assignment.size # => 2 + # ``` + # + def assign(node, scope) + assignments << Assignment.new(node, self, scope) + + update_assign_reference! + end + + # Returns true if variable has any reference. + # + # ``` + # variable = Variable.new(node, scope) + # variable.reference(var_node) + # variable.referenced? # => true + # ``` + def referenced? + references.any? + end + + # Creates a reference to this variable in some scope. + # + # ``` + # variable = Variable.new(node, scope) + # variable.reference(var_node, some_scope) + # ``` + # + def reference(node : Crystal::Var, scope : Scope) + Reference.new(node, scope).tap do |reference| + references << reference + scope.references << reference + end + end + + # Reference variable's assignments. + # + # ``` + # variable = Variable.new(node, scope) + # variable.assign(assign_node) + # variable.reference_assignments! + # ``` + def reference_assignments! + consumed_branches = Set(Branch).new + + assignments.reverse_each do |assignment| + next if consumed_branches.includes?(assignment.branch) + assignment.referenced = true + + break unless assignment.branch + consumed_branches << assignment.branch.not_nil! + end + end + + # Returns true if the current var is referenced in + # in the block. For example this variable is captured + # by block: + # + # ``` + # a = 1 + # 3.times { |i| a = a + i } + # ``` + # + # And this variable is not captured by block. + # + # ``` + # i = 1 + # 3.times { |i| i + 1 } + # ``` + def captured_by_block?(scope = @scope) + scope.inner_scopes.each do |inner_scope| + return true if inner_scope.block? && inner_scope.references?(self) + return true if captured_by_block?(inner_scope) + end + + false + end + + # Returns true if current variable potentially referenced in a macro literal, + # false if not. + def used_in_macro?(scope = @scope) + scope.inner_scopes.each do |inner_scope| + return true if MacroLiteralFinder.new(inner_scope.node).references? node + end + return true if (outer_scope = scope.outer_scope) && used_in_macro?(outer_scope) + false + end + + # Returns true if the variable is a target (on the left) of the assignment, + # false otherwise. + def target_of?(assign) + case assign + when Crystal::Assign then eql?(assign.target) + when Crystal::OpAssign then eql?(assign.target) + when Crystal::MultiAssign then assign.targets.any? { |t| eql?(t) } + when Crystal::UninitializedVar then eql?(assign.var) + else + false + end + end + + # Returns true if the name starts with '_', false if not. + def ignored? + name.starts_with? '_' + end + + # Returns true if the `node` represents exactly + # the same Crystal node as `@node`. + def eql?(node) + node.is_a?(Crystal::Var) && + node.name == @node.name && + node.location == @node.location + end + + # Returns true if the variable is delcared before the `node`. + def declared_before?(node) + var_location, node_location = location, node.location + + return if var_location.nil? || node_location.nil? + + (var_location.line_number < node_location.line_number) || + (var_location.line_number == node_location.line_number && + var_location.column_number < node_location.column_number) + end + + private class MacroLiteralFinder < Crystal::Visitor + @macro_literals = [] of Crystal::MacroLiteral + + def initialize(node) + node.accept self + end + + def references?(node : Crystal::Var) + @macro_literals.any? { |literal| literal.value.includes? node.name } + end + + def visit(node : Crystal::ASTNode) + true + end + + def visit(node : Crystal::MacroLiteral) + @macro_literals << node + end + end + + private def update_assign_reference! + if @assign_before_reference.nil? && + references.size <= assignments.size && + assignments.none? { |ass| ass.op_assign? } + @assign_before_reference = assignments.find { |ass| !ass.in_branch? }.try &.node + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr new file mode 100644 index 000000000000..20a2ef5afc20 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr @@ -0,0 +1,29 @@ +require "compiler/crystal/syntax/*" + +# A module that helps to traverse Crystal AST using `Crystal::Visitor`. +module Ameba::AST + # An abstract base visitor that utilizes general logic for all visitors. + abstract class BaseVisitor < Crystal::Visitor + # A corresponding rule that uses this visitor. + @rule : Rule::Base + + # A source that needs to be traversed. + @source : Source + + # Creates instance of this visitor. + # + # ``` + # visitor = Ameba::AST::NodeVisitor.new(rule, source) + # ``` + # + def initialize(@rule, @source) + @source.ast.accept self + end + + # A main visit method that accepts `Crystal::ASTNode`. + # Returns true meaning all child nodes will be traversed. + def visit(node : Crystal::ASTNode) + true + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr new file mode 100644 index 000000000000..24e560e18be1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr @@ -0,0 +1,39 @@ +module Ameba::AST + # AST Visitor that counts occurrences of certain keywords + class CountingVisitor < Crystal::Visitor + DEFAULT_COMPLEXITY = 1 + getter macro_condition = false + + # Creates a new counting visitor + def initialize(@scope : Crystal::ASTNode) + @complexity = DEFAULT_COMPLEXITY + end + + # :nodoc: + def visit(node : Crystal::ASTNode) + true + end + + # Returns the number of keywords that were found in the node + def count + @scope.accept(self) + @complexity + end + + # Uses the same logic than rubocop. See + # https://github.com/rubocop-hq/rubocop/blob/master/lib/rubocop/cop/metrics/cyclomatic_complexity.rb#L21 + # Except "for", because crystal doesn't have a "for" loop. + {% for node in %i(if while until rescue when or and) %} + # :nodoc: + def visit(node : Crystal::{{ node.id.capitalize }}) + @complexity += 1 unless macro_condition + end + {% end %} + + def visit(node : Crystal::MacroIf | Crystal::MacroFor) + @macro_condition = true + @complexity = DEFAULT_COMPLEXITY + false + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr new file mode 100644 index 000000000000..f75e0d6473cc --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr @@ -0,0 +1,67 @@ +require "../util" +require "./base_visitor" + +module Ameba::AST + # AST Visitor that traverses all the flow expressions. + class FlowExpressionVisitor < BaseVisitor + include Util + + @loop_stack = [] of Crystal::ASTNode + + # Creates a new flow expression visitor. + def initialize(@rule, @source) + @source.ast.accept self + end + + # :nodoc: + def visit(node) + if flow_expression?(node, in_loop?) + @rule.test @source, node, FlowExpression.new(node, in_loop?) + end + + true + end + + # :nodoc: + def visit(node : Crystal::While) + on_loop_started(node) + end + + # :nodoc: + def visit(node : Crystal::Until) + on_loop_started(node) + end + + # :nodoc: + def visit(node : Crystal::Call) + on_loop_started(node) if loop?(node) + end + + # :nodoc: + def end_visit(node : Crystal::While) + on_loop_ended(node) + end + + # :nodoc: + def end_visit(node : Crystal::Until) + on_loop_ended(node) + end + + # :nodoc: + def end_visit(node : Crystal::Call) + on_loop_ended(node) if loop?(node) + end + + private def on_loop_started(node) + @loop_stack.push(node) + end + + private def on_loop_ended(node) + @loop_stack.pop? + end + + private def in_loop? + @loop_stack.any? + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr new file mode 100644 index 000000000000..47bb343da8e8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr @@ -0,0 +1,61 @@ +require "./base_visitor" + +module Ameba::AST + # List of nodes to be visited by Ameba's rules. + NODES = [ + Alias, + IsA, + Assign, + Call, + Block, + Case, + ClassDef, + ClassVar, + Def, + EnumDef, + ExceptionHandler, + Expressions, + HashLiteral, + If, + InstanceVar, + LibDef, + ModuleDef, + NilLiteral, + StringInterpolation, + Unless, + Var, + When, + While, + Until, + ] + + # An AST Visitor that traverses the source and allows all nodes + # to be inspected by rules. + # + # ``` + # visitor = Ameba::AST::NodeVisitor.new(rule, source) + # ``` + # + class NodeVisitor < BaseVisitor + @skip : Array(Crystal::ASTNode.class)? + + def initialize(@rule, @source, skip = nil) + @skip = skip.try &.map { |el| el.as(Crystal::ASTNode.class) } + super @rule, @source + end + + {% for name in NODES %} + # A visit callback for `Crystal::{{name}}` node. + # Returns true meaning that child nodes will be traversed as well. + def visit(node : Crystal::{{name}}) + @rule.test @source, node + true + end + {% end %} + + def visit(node) + return true unless (skip = @skip) + !skip.includes?(node.class) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr new file mode 100644 index 000000000000..a1ef1c3dfd6a --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr @@ -0,0 +1,62 @@ +module Ameba::AST + # A class that utilizes a logic to traverse AST nodes and + # fire a source test callback if a redundant `Crystal::ControlExpression` + # is reached. + class RedundantControlExpressionVisitor + # A corresponding rule that uses this visitor. + getter rule : Rule::Base + + # A source that needs to be traversed. + getter source : Source + + # A node to run traversal on. + getter node : Crystal::ASTNode + + def initialize(@rule, @source, @node) + traverse_node node + end + + private def traverse_control_expression(node) + @rule.test(@source, node, self) + end + + private def traverse_node(node) + case node + when Crystal::ControlExpression then traverse_control_expression node + when Crystal::Expressions then traverse_expressions node + when Crystal::If, Crystal::Unless then traverse_condition node + when Crystal::Case then traverse_case node + when Crystal::BinaryOp then traverse_binary_op node + when Crystal::ExceptionHandler then traverse_exception_handler node + else + # ok + end + end + + private def traverse_expressions(node) + traverse_node node.expressions.last? + end + + private def traverse_condition(node) + return if node.else.nil? || node.else.nop? + + traverse_node(node.then) + traverse_node(node.else) + end + + private def traverse_case(node) + node.whens.each { |n| traverse_node n.body } + traverse_node(node.else) + end + + private def traverse_binary_op(node) + traverse_node(node.right) + end + + private def traverse_exception_handler(node) + traverse_node node.body + traverse_node node.else + node.rescues.try &.each { |n| traverse_node n.body } + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr new file mode 100644 index 000000000000..084e83aea242 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr @@ -0,0 +1,185 @@ +require "./base_visitor" + +module Ameba::AST + # AST Visitor that traverses the source and constructs scopes. + class ScopeVisitor < BaseVisitor + SUPER_NODE_NAME = "super" + RECORD_NODE_NAME = "record" + + @scope_queue = [] of Scope + + @current_scope : Scope + + def initialize(@rule, @source) + @current_scope = Scope.new(@source.ast) # top level scope + @source.ast.accept self + @scope_queue.each { |scope| @rule.test @source, scope.node, scope } + end + + private def on_scope_enter(node) + @current_scope = Scope.new(node, @current_scope) + end + + private def on_scope_end(node) + @scope_queue << @current_scope + + # go up if this is not a top level scope + if outer_scope = @current_scope.outer_scope + @current_scope = outer_scope + end + end + + private def on_assign_end(target, node) + target.is_a?(Crystal::Var) && @current_scope.assign_variable(target.name, node) + end + + # :nodoc: + def end_visit(node : Crystal::ASTNode) + on_scope_end(node) if @current_scope.eql?(node) + end + + # :nodoc: + def visit(node : Crystal::ClassDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::ModuleDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::EnumDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::LibDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::FunDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::TypeDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::TypeOf) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::CStructOrUnionDef) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::Def) + node.name == "->" || on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::ProcLiteral) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::Block) + on_scope_enter(node) + end + + # :nodoc: + def visit(node : Crystal::Macro) + on_scope_enter(node) + end + + @current_assign : Crystal::ASTNode? + + # :nodoc: + def visit(node : Crystal::Assign | Crystal::OpAssign | Crystal::MultiAssign | Crystal::UninitializedVar) + @current_assign = node + end + + # :nodoc: + def end_visit(node : Crystal::Assign | Crystal::OpAssign) + on_assign_end(node.target, node) + @current_assign = nil + on_scope_end(node) if @current_scope.eql?(node) + end + + # :nodoc: + def end_visit(node : Crystal::MultiAssign) + node.targets.each { |target| on_assign_end(target, node) } + @current_assign = nil + on_scope_end(node) if @current_scope.eql?(node) + end + + # :nodoc: + def end_visit(node : Crystal::UninitializedVar) + on_assign_end(node.var, node) + @current_assign = nil + on_scope_end(node) if @current_scope.eql?(node) + end + + # :nodoc: + def visit(node : Crystal::TypeDeclaration) + if !@current_scope.type_definition? && (var = node.var).is_a?(Crystal::Var) + @current_scope.add_variable var + end + end + + # :nodoc: + def visit(node : Crystal::Arg) + @current_scope.add_argument node + end + + # :nodoc: + def visit(node : Crystal::InstanceVar) + @current_scope.add_ivariable(node) + end + + # :nodoc: + def visit(node : Crystal::Var) + variable = @current_scope.find_variable node.name + + if @current_scope.arg?(node) # node is an argument + @current_scope.add_argument(node) + elsif variable.nil? && @current_assign # node is a variable + @current_scope.add_variable(node) + elsif variable # node is a reference + reference = variable.reference node, @current_scope + if @current_assign.is_a?(Crystal::OpAssign) || !reference.target_of?(@current_assign) + variable.reference_assignments! + end + end + end + + # :nodoc: + def visit(node : Crystal::Call) + case + when @current_scope.def? + if node.name == SUPER_NODE_NAME && node.args.empty? + @current_scope.arguments.each do |arg| + variable = arg.variable + variable.reference(variable.node, @current_scope).explicit = false + end + end + + true + when @current_scope.top_level? && record_macro?(node) + false + else + true + end + end + + private def record_macro?(node) + node.name == RECORD_NODE_NAME && node.args.first?.is_a?(Crystal::Path) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr new file mode 100644 index 000000000000..42778da287b3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr @@ -0,0 +1,152 @@ +require "../../ameba" +require "option_parser" + +# :nodoc: +module Ameba::Cli + extend self + + def run(args = ARGV) + opts = parse_args args + config = Config.load opts.config, opts.colors? + config.globs = opts.globs.not_nil! if opts.globs + config.severity = opts.fail_level.not_nil! if opts.fail_level + + configure_formatter(config, opts) + configure_rules(config, opts) + + runner = Ameba.run(config) + + if location = opts.location_to_explain + runner.explain(location) + else + exit 1 unless runner.success? + end + rescue e + puts "Error: #{e.message}" + exit 255 + end + + private class Opts + property config = Config::PATH + property formatter : Symbol | String | Nil + property globs : Array(String)? + property only : Array(String)? + property except : Array(String)? + property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)? + property fail_level : Severity? + property? all = false + property? colors = true + property? without_affected_code = false + end + + def parse_args(args, opts = Opts.new) + OptionParser.parse(args) do |parser| + parser.banner = "Usage: ameba [options] [file1 file2 ...]" + + parser.on("-v", "--version", "Print version") { print_version } + parser.on("-h", "--help", "Show this help") { show_help parser } + parser.on("-s", "--silent", "Disable output") { opts.formatter = :silent } + parser.unknown_args do |f| + if f.size == 1 && f.first =~ /.+:\d+:\d+/ + configure_explain_opts(f.first, opts) + else + opts.globs = f if f.any? + end + end + + parser.on("-c", "--config PATH", + "Specify a configuration file") do |path| + opts.config = path unless opts.config.empty? + end + + parser.on("-f", "--format FORMATTER", + "Choose an output formatter: #{Config.formatter_names}") do |formatter| + opts.formatter = formatter + end + + parser.on("--only RULE1,RULE2,...", + "Run only given rules (or groups)") do |rules| + opts.only = rules.split "," + end + + parser.on("--except RULE1,RULE2,...", + "Disable the given rules (or groups)") do |rules| + opts.except = rules.split "," + end + + parser.on("--all", "Enables all available rules") do + opts.all = true + end + + parser.on("--gen-config", + "Generate a configuration file acting as a TODO list") do + opts.formatter = :todo + opts.config = "" + end + + parser.on("--fail-level SEVERITY", "Change the level of failure to exit. Defaults to Convention") do |level| + opts.fail_level = Severity.parse(level) + end + + parser.on("-e", "--explain PATH:line:column", + "Explain an issue at a specified location") do |loc| + configure_explain_opts(loc, opts) + end + + parser.on("--without-affected-code", + "Stop showing affected code while using a default formatter") do + opts.without_affected_code = true + end + + parser.on("--no-color", "Disable colors") do + opts.colors = false + end + end + + opts + end + + private def configure_rules(config, opts) + if only = opts.only + config.rules.map! { |r| r.enabled = false; r } + config.update_rules(only, enabled: true) + elsif opts.all? + config.rules.map! { |r| r.enabled = true; r } + end + + config.update_rules(opts.except, enabled: false) + end + + private def configure_formatter(config, opts) + if name = opts.formatter + config.formatter = name + end + config.formatter.config[:without_affected_code] = opts.without_affected_code? + end + + private def configure_explain_opts(loc, opts) + location_to_explain = parse_explain_location(loc) + opts.location_to_explain = location_to_explain + opts.globs = [location_to_explain[:file]] + opts.formatter = :silent + end + + private def parse_explain_location(arg) + location = arg.split(":", remove_empty: true).map &.strip + raise ArgumentError.new unless location.size === 3 + file, line, column = location + {file: file, line: line.to_i, column: column.to_i} + rescue + raise "location should have PATH:line:column format" + end + + private def print_version + puts VERSION + exit 0 + end + + private def show_help(parser) + puts parser + exit 0 + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr new file mode 100644 index 000000000000..1ecbd3a8cea8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr @@ -0,0 +1,280 @@ +require "yaml" +require "./glob_utils" + +# A configuration entry for `Ameba::Runner`. +# +# Config can be loaded from configuration YAML file and adjusted. +# +# ``` +# config = Config.load +# config.formatter = my_formatter +# ``` +# +# By default config loads `.ameba.yml` file in a current directory. +# +class Ameba::Config + include GlobUtils + + AVAILABLE_FORMATTERS = { + progress: Formatter::DotFormatter, + todo: Formatter::TODOFormatter, + flycheck: Formatter::FlycheckFormatter, + silent: Formatter::BaseFormatter, + disabled: Formatter::DisabledFormatter, + json: Formatter::JSONFormatter, + } + + PATH = ".ameba.yml" + + DEFAULT_GLOBS = %w( + **/*.cr + !lib + ) + + setter formatter : Formatter::BaseFormatter? + getter rules : Array(Rule::Base) + property severity = Severity::Convention + + # Returns a list of paths (with wildcards) to files. + # Represents a list of sources to be inspected. + # If globs are not set, it will return default list of files. + # + # ``` + # config = Ameba::Config.load + # config.globs = ["**/*.cr"] + # config.globs + # ``` + property globs : Array(String) + + # Represents a list of paths to exclude from globs. + # Can have wildcards. + # + # ``` + # config = Ameba::Config.load + # config.excluded = ["spec", "src/server/*.cr"] + # ``` + property excluded : Array(String) + + @rule_groups : Hash(String, Array(Rule::Base)) + + # Creates a new instance of `Ameba::Config` based on YAML parameters. + # + # `Config.load` uses this constructor to instantiate new config by YAML file. + protected def initialize(config : YAML::Any) + @rules = Rule.rules.map &.new(config).as(Rule::Base) + @rule_groups = @rules.group_by &.group + @excluded = load_array_section(config, "Excluded") + @globs = load_array_section(config, "Globs", DEFAULT_GLOBS) + + self.formatter = load_formatter_name(config) + end + + # Loads YAML configuration file by `path`. + # + # ``` + # config = Ameba::Config.load + # ``` + # + def self.load(path = PATH, colors = true) + Colorize.enabled = colors + content = File.exists?(path) ? File.read path : "{}" + Config.new YAML.parse(content) + rescue e + raise "Config file is invalid: #{e.message}" + end + + def self.formatter_names + AVAILABLE_FORMATTERS.keys.join("|") + end + + # Returns a list of sources matching globs and excluded sections. + # + # ``` + # config = Ameba::Config.load + # config.sources # => list of default sources + # config.globs = ["**/*.cr"] + # config.excluded = ["spec"] + # config.sources # => list of sources pointing to files found by the wildcards + # ``` + # + def sources + (find_files_by_globs(globs) - find_files_by_globs(excluded)) + .map { |path| Source.new File.read(path), path } + end + + # Returns a formatter to be used while inspecting files. + # If formatter is not set, it will return default formatter. + # + # ``` + # config = Ameba::Config.load + # config.formatter = custom_formatter + # config.formatter + # ``` + # + def formatter + @formatter ||= Formatter::DotFormatter.new + end + + # Sets formatter by name. + # + # ``` + # config = Ameba::Config.load + # config.formatter = :progress + # ``` + # + def formatter=(name : String | Symbol) + if f = AVAILABLE_FORMATTERS[name]? + @formatter = f.new + else + raise "Unknown formatter `#{name}`. Use one of #{Config.formatter_names}." + end + end + + # Updates rule properties. + # + # ``` + # config = Ameba::Config.load + # config.update_rule "MyRuleName", enabled: false + # ``` + # + def update_rule(name, enabled = true, excluded = nil) + index = @rules.index { |r| r.name == name } + raise ArgumentError.new("Rule `#{name}` does not exist") unless index + + rule = @rules[index] + rule.enabled = enabled + rule.excluded = excluded + @rules[index] = rule + end + + # Updates rules properties. + # + # ``` + # config = Ameba::Config.load + # config.update_rules %w(Rule1 Rule2), enabled: true + # ``` + # + # also it allows to update groups of rules: + # + # ``` + # config.update_rules %w(Group1 Group2), enabled: true + # ``` + # + def update_rules(names, **args) + names.try &.each do |name| + if group = @rule_groups[name]? + group.each { |rule| update_rule(rule.name, **args) } + else + update_rule name, **args + end + end + end + + private def load_formatter_name(config) + name = config["Formatter"]?.try &.["Name"]? + name ? name.to_s : nil + end + + private def load_array_section(config, section_name, default = [] of String) + case value = config[section_name]? + when .nil? then default + when .as_s? then [value.to_s] + when .as_a? then value.as_a.map(&.as_s) + else + raise "incorrect '#{section_name}' section in a config files" + end + end + + # :nodoc: + module RuleConfig + macro properties(&block) + {% definitions = [] of NamedTuple %} + {% if block.body.is_a? Assign %} + {% definitions << {var: block.body.target, value: block.body.value} %} + {% elsif block.body.is_a? Call %} + {% definitions << {var: block.body.name, value: block.body.args.first} %} + {% elsif block.body.is_a? TypeDeclaration %} + {% definitions << {var: block.body.var, value: block.body.value, type: block.body.type} %} + {% elsif block.body.is_a? Expressions %} + {% for prop in block.body.expressions %} + {% if prop.is_a? Assign %} + {% definitions << {var: prop.target, value: prop.value} %} + {% elsif prop.is_a? Call %} + {% definitions << {var: prop.name, value: prop.args.first} %} + {% elsif prop.is_a? TypeDeclaration %} + {% definitions << {var: prop.var, value: prop.value, type: prop.type} %} + {% end %} + {% end %} + {% end %} + + {% properties = {} of MacroId => NamedTuple %} + {% for df in definitions %} + {% name = df[:var].id %} + {% key = name.camelcase.stringify %} + {% value = df[:value] %} + {% type = df[:type] %} + {% converter = nil %} + + {% if key == "Severity" %} + {% type = Severity %} + {% converter = SeverityYamlConverter %} + {% end %} + + {% if type == nil %} + {% if value.is_a? BoolLiteral %} + {% type = Bool %} + {% elsif value.is_a? StringLiteral %} + {% type = String %} + {% elsif value.is_a? NumberLiteral %} + {% if value.kind == :i32 %} + {% type = Int32 %} + {% elsif value.kind == :i64 %} + {% type = Int64 %} + {% elsif value.kind == :f32 %} + {% type = Float32 %} + {% elsif value.kind == :f64 %} + {% type = Float64 %} + {% end %} + {% end %} + + {% type = Nil if type == nil %} + {% end %} + + {% properties[name] = {key: key, default: value, type: type, converter: converter} %} + + @[YAML::Field(key: {{key}}, converter: {{converter}}, type: {{type}})] + property {{name}} : {{type}} = {{value}} + {% end %} + + {% if properties["enabled".id] == nil %} + @[YAML::Field(key: "Enabled")] + property enabled = true + {% end %} + + {% if properties["severity".id] == nil %} + {% default = @type.name.starts_with?("Ameba::Rule::Lint") ? "Ameba::Severity::Warning".id : "Ameba::Severity::Convention".id %} + @[YAML::Field(key: "Severity", converter: Ameba::SeverityYamlConverter)] + property severity = {{default}} + {% end %} + + {% if properties["excluded".id] == nil %} + @[YAML::Field(key: "Excluded")] + property excluded : Array(String)? + {% end %} + end + + macro included + macro inherited + include YAML::Serializable + include YAML::Serializable::Strict + + def self.new(config = nil) + if (raw = config.try &.raw).is_a? Hash + yaml = raw[rule_name]?.try &.to_yaml + end + from_yaml yaml || "{}" + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr new file mode 100644 index 000000000000..4009c9921efb --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr @@ -0,0 +1,32 @@ +require "./util" + +# A module that utilizes Ameba's formatters. +module Ameba::Formatter + # A base formatter for all formatters. It uses `output` IO + # to report results and also implements stub methods for + # callbacks in `Ameba::Runner#run` method. + class BaseFormatter + # TODO: allow other IOs + getter output : IO::FileDescriptor | IO::Memory + getter config = {} of Symbol => String | Bool + + def initialize(@output = STDOUT) + end + + # Callback that indicates when inspecting is started. + # A list of sources to inspect is passed as an argument. + def started(sources); end + + # Callback that indicates when source inspection is finished. + # A corresponding source is passed as an argument. + def source_finished(source : Source); end + + # Callback that indicates when source inspection is finished. + # A corresponding source is passed as an argument. + def source_started(source : Source); end + + # Callback that indicates when inspection is finished. + # A list of inspected sources is passed as an argument. + def finished(sources); end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr new file mode 100644 index 000000000000..cbe7905b4a61 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr @@ -0,0 +1,17 @@ +module Ameba::Formatter + # A formatter that shows all disabled lines by inline directives. + class DisabledFormatter < BaseFormatter + def finished(sources) + output << "Disabled rules using inline directives: \n\n" + + sources.each do |source| + source.issues.select(&.disabled?).each do |e| + if loc = e.location + output << "#{source.path}:#{loc.line_number}".colorize(:cyan) + output << " #{e.rule.name}\n" + end + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr new file mode 100644 index 000000000000..396069e53bc6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr @@ -0,0 +1,96 @@ +require "./util" + +module Ameba::Formatter + # A formatter that shows a progress of inspection in a terminal using dots. + # It is similar to Crystal's dot formatter for specs. + class DotFormatter < BaseFormatter + include Util + + @started_at : Time? + @mutex = Thread::Mutex.new + + # Reports a message when inspection is started. + def started(sources) + @started_at = Time.utc # Time.monotonic + + output << started_message(sources.size) + end + + # Reports a result of the inspection of a corresponding source. + def source_finished(source : Source) + sym = source.valid? ? ".".colorize(:green) : "F".colorize(:red) + @mutex.synchronize { output << sym } + end + + # Reports a message when inspection is finished. + def finished(sources) + output.flush + output << "\n\n" + + show_affected_code = !config[:without_affected_code]? + failed_sources = sources.reject &.valid? + + failed_sources.each do |source| + source.issues.each do |issue| + next if issue.disabled? + next if (location = issue.location).nil? + + output << "#{location}\n".colorize(:cyan) + output << "[#{issue.rule.severity.symbol}] #{issue.rule.name}: #{issue.message}\n".colorize(:red) + + if show_affected_code && (code = affected_code(source, location)) + output << code.colorize(:default) + end + + output << "\n" + end + end + + output << finished_in_message(@started_at, Time.utc) # Time.monotonic + output << final_message(sources, failed_sources) + end + + private def started_message(size) + if size == 1 + "Inspecting 1 file.\n\n".colorize(:default) + else + "Inspecting #{size} files.\n\n".colorize(:default) + end + end + + private def finished_in_message(started, finished) + if started && finished + "Finished in #{to_human(finished - started)} \n\n".colorize(:default) + end + end + + private def to_human(span : Time::Span) + total_milliseconds = span.total_milliseconds + if total_milliseconds < 1 + return "#{(span.total_milliseconds * 1_000).round.to_i} microseconds" + end + + total_seconds = span.total_seconds + if total_seconds < 1 + return "#{span.total_milliseconds.round(2)} milliseconds" + end + + if total_seconds < 60 + return "#{total_seconds.round(2)} seconds" + end + + minutes = span.minutes + seconds = span.seconds + "#{minutes}:#{seconds < 10 ? "0" : ""}#{seconds} minutes" + end + + private def final_message(sources, failed_sources) + total = sources.size + failures = failed_sources.map { |f| f.issues.size }.sum + color = failures == 0 ? :green : :red + s = failures != 1 ? "s" : "" + + "#{total} inspected, #{failures} failure#{s}.\n".colorize color + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr new file mode 100644 index 000000000000..6670245ba576 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr @@ -0,0 +1,83 @@ +require "./util" + +module Ameba::Formatter + # A formatter that shows the detailed explanation of the issue at + # a specific location. + class ExplainFormatter + LINE_BREAK = "\n" + HEADING = "## " + PREFIX = " " + + include Util + + getter output : IO::FileDescriptor | IO::Memory + getter location : Crystal::Location + + # Creates a new instance of ExplainFormatter. + # Accepts *output* which indicates the io where the explanation will be wrtitten to. + # Second argument is *location* which indicates the location to explain. + # + # ``` + # ExplainFormatter.new output, + # {file: path, line: line_number, column: column_number} + # ``` + # + def initialize(@output, loc) + @location = Crystal::Location.new(loc[:file], loc[:line], loc[:column]) + end + + # Reports the explainations at the *@location*. + def finished(sources) + source = sources.find { |s| s.path == @location.filename } + + return unless source + + source.issues.each do |issue| + if (location = issue.location) && + location.line_number == @location.line_number && + location.column_number == @location.column_number + explain(source, issue) + end + end + end + + private def explain(source, issue) + rule = issue.rule + + output_title "ISSUE INFO" + output_paragraph [ + issue.message.colorize(:red).to_s, + @location.to_s.colorize(:cyan).to_s, + ] + + if affected_code = affected_code(source, @location) + output_title "AFFECTED CODE" + output_paragraph affected_code + end + + if rule.responds_to?(:description) + output_title "RULE INFO" + output_paragraph [rule.severity.to_s, rule.name, rule.description] + end + + output_title "DETAILED DESCRIPTION" + output_paragraph(rule.class.parsed_doc || "TO BE DONE...") + end + + private def output_title(title) + output << HEADING.colorize(:yellow) << title.colorize(:yellow) << LINE_BREAK + output << LINE_BREAK + end + + private def output_paragraph(paragraph : String) + output_paragraph(paragraph.split(LINE_BREAK)) + end + + private def output_paragraph(paragraph : Array(String)) + paragraph.each do |line| + output << PREFIX << line << LINE_BREAK + end + output << LINE_BREAK + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr new file mode 100644 index 000000000000..89423ff39843 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr @@ -0,0 +1,18 @@ +module Ameba::Formatter + class FlycheckFormatter < BaseFormatter + @mutex = Mutex.new + + def source_finished(source : Source) + source.issues.each do |e| + next if e.disabled? + if loc = e.location + @mutex.synchronize do + output.printf "%s:%d:%d: %s: [%s] %s\n", + source.path, loc.line_number, loc.column_number, e.rule.severity.symbol, + e.rule.name, e.message.gsub("\n", " ") + end + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr new file mode 100644 index 000000000000..211dabb98555 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr @@ -0,0 +1,152 @@ +require "json" + +module Ameba::Formatter + # A formatter that produces the result in a json format. + # + # Example: + # + # ``` + # { + # "metadata": { + # "ameba_version": "x.x.x", + # "crystal_version": "x.x.x", + # }, + # "sources": [ + # { + # "issues": [ + # { + # "location": { + # "column": 7, + # "line": 17, + # }, + # "end_location": { + # "column": 20, + # "line": 17, + # }, + # "message": "Useless assignment to variable `a`", + # "rule_name": "UselessAssign", + # "severity": "Convention", + # }, + # { + # "location": { + # "column": 7, + # "line": 18, + # }, + # "end_location": { + # "column": 8, + # "line": 18, + # }, + # "message": "Useless assignment to variable `a`", + # "rule_name": "UselessAssign", + # }, + # { + # "location": { + # "column": 7, + # "line": 19, + # }, + # "end_location": { + # "column": 9, + # "line": 19, + # }, + # "message": "Useless assignment to variable `a`", + # "rule_name": "UselessAssign", + # "severity": "Convention", + # }, + # ], + # "path": "src/ameba/formatter/json_formatter.cr", + # }, + # ], + # "summary": { + # "issues_count": 3, + # "target_sources_count": 1, + # }, + # } + # ``` + # + class JSONFormatter < BaseFormatter + def initialize(@output = STDOUT) + @result = AsJSON::Result.new + end + + def started(sources) + @result.summary.target_sources_count = sources.size + end + + def source_finished(source : Source) + json_source = AsJSON::Source.new source.path + + source.issues.each do |e| + next if e.disabled? + json_source.issues << AsJSON::Issue.new(e.rule.name, e.rule.severity.to_s, e.location, e.end_location, e.message) + @result.summary.issues_count += 1 + end + + @result.sources << json_source + end + + def finished(sources) + @result.to_json @output + end + end + + private module AsJSON + record Result, + sources = [] of Source, + metadata = Metadata.new, + summary = Summary.new do + def to_json(json) + {sources: sources, metadata: metadata, summary: summary}.to_json(json) + end + end + + record Source, + path : String, + issues = [] of Issue do + def to_json(json) + {path: path, issues: issues}.to_json(json) + end + end + + record Issue, + rule_name : String, + severity : String, + location : Crystal::Location?, + end_location : Crystal::Location?, + message : String do + def to_json(json) + json.object do + json.field :rule_name, rule_name + json.field :severity, severity + json.field :message, message + json.field :location, + {line: location.try &.line_number, column: location.try &.column_number} + json.field :end_location, + {line: end_location.try &.line_number, column: end_location.try &.column_number} + end + end + end + + record Metadata, + ameba_version : String = Ameba::VERSION, + crystal_version : String = Crystal::VERSION do + def to_json(json) + json.object do + json.field :ameba_version, ameba_version + json.field :crystal_version, crystal_version + end + end + end + + class Summary + property target_sources_count = 0 + property issues_count = 0 + + def to_json(json) + json.object do + json.field :target_sources_count, target_sources_count + json.field :issues_count, issues_count + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr new file mode 100644 index 000000000000..fd0b81000213 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr @@ -0,0 +1,68 @@ +module Ameba::Formatter + # A formatter that creates a todo config. + # Basically, it takes all issues reported and disables corresponding rules + # or excludes failed sources from these rules. + class TODOFormatter < DotFormatter + def initialize(@output = STDOUT) + end + + def finished(sources) + super + issues = sources.map(&.issues).flatten + unless issues.any? { |issue| !issue.disabled? } + @output << "No issues found. File is not generated.\n" + return + end + + if issues.any? { |issue| issue.syntax? } + @output << "Unable to generate TODO file. Please fix syntax issues.\n" + return + end + + file = generate_todo_config issues + @output << "Created #{file.path}\n" + file + end + + private def generate_todo_config(issues) + file = File.new(Config::PATH, mode: "w") + file << header + rule_issues_map(issues).each do |rule, rule_issues| + file << "\n# Problems found: #{rule_issues.size}" + file << "\n# Run `ameba --only #{rule.name}` for details" + file << rule_todo(rule, rule_issues).gsub("---", "") + end + file + ensure + file.close if file + end + + private def rule_issues_map(issues) + Hash(Rule::Base, Array(Issue)).new.tap do |h| + issues.each do |issue| + next if issue.disabled? || issue.rule.is_a? Rule::Lint::Syntax + (h[issue.rule] ||= Array(Issue).new) << issue + end + end + end + + private def header + <<-HEADER + # This configuration file was generated by `ameba --gen-config` + # on #{Time.utc} using Ameba version #{VERSION}. + # The point is for the user to remove these configuration records + # one by one as the reported problems are removed from the code base. + + HEADER + end + + private def rule_todo(rule, issues) + rule.excluded = + issues.map(&.location.try &.filename.try &.to_s) + .compact + .uniq! + + {rule.name => rule}.to_yaml + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr new file mode 100644 index 000000000000..7219a67bbb9e --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr @@ -0,0 +1,23 @@ +module Ameba::Formatter + module Util + def affected_code(source, location, max_length = 100, placeholder = " ...", prompt = "> ") + line, column = location.line_number, location.column_number + affected_line = source.lines[line - 1]? + + return if affected_line.nil? || affected_line.strip.empty? + + if affected_line.size > max_length && column < max_length + affected_line = affected_line[0, max_length - placeholder.size - 1] + placeholder + end + + stripped = affected_line.lstrip + position = column - (affected_line.size - stripped.size) + prompt.size + + String.build do |str| + str << prompt << stripped << "\n" + str << " " * (position - 1) + str << "^".colorize(:yellow) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr new file mode 100644 index 000000000000..a714f8851c8a --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr @@ -0,0 +1,37 @@ +module Ameba + # Helper module that is utilizes helpers for working with globs. + module GlobUtils + # Returns all files that match specified globs. + # Globs can have wildcards or be rejected: + # + # ``` + # find_files_by_globs(["**/*.cr", "!lib"]) + # ``` + # + def find_files_by_globs(globs) + rejected = rejected_globs(globs) + selected = globs - rejected + + expand(selected) - expand(rejected.map! { |p| p[1..-1] }) + end + + # Expands globs. Globs can point to files or even directories. + # + # ``` + # expand(["spec/*.cr", "src"]) # => all files in src folder + first level specs + # ``` + # + def expand(globs) + globs.map do |glob| + glob += "/**/*.cr" if File.directory?(glob) + Dir[glob] + end.flatten.uniq! + end + + private def rejected_globs(globs) + globs.select do |glob| + glob.starts_with?('!') && !File.exists?(glob) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr new file mode 100644 index 000000000000..cece57fec648 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr @@ -0,0 +1,104 @@ +module Ameba + # A module that utilizes inline comments parsing and processing logic. + module InlineComments + COMMENT_DIRECTIVE_REGEX = /# ameba:(?\w+) (?\w+(?:\/\w+)?(?:,? \w+(?:\/\w+)?)*)/ + + # Available actions in the inline comments + enum Action + Disable + Enable + end + + # Returns true if current location is disabled for a particular rule, + # false otherwise. + # + # Location is disabled in two cases: + # 1. The line of the location ends with a comment directive. + # 2. The line above the location is a comment directive. + # + # For example, here are two examples of disabled location: + # + # ``` + # # ameba:disable Style/LargeNumbers + # Time.epoch(1483859302) + # + # Time.epoch(1483859302) # ameba:disable Style/LargeNumbers + # ``` + # + # But here are examples which are not considered as disabled location: + # + # ``` + # # ameba:disable Style/LargeNumbers + # # + # Time.epoch(1483859302) + # + # if use_epoch? # ameba:disable Style/LargeNumbers + # Time.epoch(1483859302) + # end + # ``` + # + def location_disabled?(location, rule) + return false if Rule::SPECIAL.includes?(rule.name) + return false unless line_number = location.try &.line_number.try &.- 1 + return false unless line = lines[line_number]? + + line_disabled?(line, rule) || + (line_number > 0 && + (prev_line = lines[line_number - 1]) && + comment?(prev_line) && + line_disabled?(prev_line, rule)) + end + + # Parses inline comment directive. Returns a tuple that consists of + # an action and parsed rules if directive found, nil otherwise. + # + # ``` + # line = "# ameba:disable Rule1, Rule2" + # directive = parse_inline_directive(line) + # directive[:action] # => "disable" + # directive[:rules] # => ["Rule1", "Rule2"] + # ``` + # + # It ignores the directive if it is commented out. + # + # ``` + # line = "# # ameba:disable Rule1, Rule2" + # parse_inline_directive(line) # => nil + # ``` + # + def parse_inline_directive(line) + if directive = COMMENT_DIRECTIVE_REGEX.match(line) + return if commented_out?(line.gsub(directive[0], "")) + { + action: directive["action"], + rules: directive["rules"].split(/[\s,]/, remove_empty: true), + } + end + end + + # Returns true if the line at the given `line_number` is a comment. + def comment?(line_number : Int32) + if line = lines[line_number]? + comment?(line) + end + end + + private def comment?(line : String) + line.lstrip.starts_with? '#' + end + + private def line_disabled?(line, rule) + return false unless directive = parse_inline_directive(line) + Action.parse?(directive[:action]).try(&.disable?) && + (directive[:rules].includes?(rule.name) || directive[:rules].includes?(rule.group)) + end + + private def commented_out?(line) + commented = false + + lexer = Crystal::Lexer.new(line).tap(&.comments_enabled = true) + Tokenizer.new(lexer).run { |t| commented = true if t.type == :COMMENT } + commented + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr new file mode 100644 index 000000000000..03e95165bb91 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr @@ -0,0 +1,26 @@ +module Ameba + # Represents an issue reported by Ameba. + record Issue, + # A rule that triggers this issue. + rule : Rule::Base, + + # Location of the issue. + location : Crystal::Location?, + + # End location of the issue. + end_location : Crystal::Location?, + + # Issue message. + message : String, + + # Issue status. + status : Symbol? do + def disabled? + status == :disabled + end + + def syntax? + rule.is_a?(Rule::Lint::Syntax) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr new file mode 100644 index 000000000000..354539506d62 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr @@ -0,0 +1,41 @@ +module Ameba + # Represents a module used to report issues. + module Reportable + # List of reported issues. + getter issues = [] of Issue + + # Adds a new issue to the list of issues. + def add_issue(rule, location, end_location, message, status = nil) + status ||= :disabled if location_disabled?(location, rule) + issues << Issue.new rule, location, end_location, message, status + end + + # Adds a new issue for AST *node*. + def add_issue(rule, node : Crystal::ASTNode, message, **args) + add_issue rule, node.location, node.end_location, message, **args + end + + # Adds a new issue for Crystal *token*. + def add_issue(rule, token : Crystal::Token, message, **args) + add_issue rule, token.location, nil, message, **args + end + + # Adds a new issue for *location* defined by line and column numbers. + def add_issue(rule, location : Tuple(Int32, Int32), message, **args) + location = Crystal::Location.new path, *location + add_issue rule, location, nil, message, **args + end + + # Adds a new issue for *location* and *end_location* defined by line and column numbers. + def add_issue(rule, location : Tuple(Int32, Int32), end_location : Tuple(Int32, Int32), message, **args) + location = Crystal::Location.new path, *location + end_location = Crystal::Location.new path, *end_location + add_issue rule, location, end_location, message, **args + end + + # Returns true if the list of not disabled issues is empty, false otherwise. + def valid? + issues.reject(&.disabled?).empty? + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr new file mode 100644 index 000000000000..c895499a2ad5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr @@ -0,0 +1,197 @@ +module Ameba::Rule + # List of names of the special rules, which + # behave differently than usual rules. + SPECIAL = [ + Lint::Syntax.rule_name, + Lint::UnneededDisableDirective.rule_name, + ] + + # Represents a base of all rules. In other words, all rules + # inherits from this struct: + # + # ``` + # struct MyRule < Ameba::Rule::Base + # def test(source) + # if invalid?(source) + # issue_for line, column, "Something wrong." + # end + # end + # + # private def invalid?(source) + # # ... + # end + # end + # ``` + # + # Enforces rules to implement an abstract `#test` method which + # is designed to test the source passed in. If source has issues + # that are tested by this rule, it should add an issue. + # + abstract struct Base + include Config::RuleConfig + + # This method is designed to test the source passed in. If source has issues + # that are tested by this rule, it should add an issue. + # + # Be default it uses a node visitor to traverse all the nodes in the source. + # Must be overriten for other type of rules. + def test(source : Source) + AST::NodeVisitor.new self, source + end + + def test(source : Source, node : Crystal::ASTNode, *opts) + # can't be abstract + end + + # A convenient addition to `#test` method that does the same + # but returns a passed in `source` as an addition. + # + # ``` + # source = MyRule.new.catch(source) + # source.valid? + # ``` + # + def catch(source : Source) + source.tap { |s| test s } + end + + # Returns a name of this rule, which is basically a class name. + # + # ``` + # struct MyRule < Ameba::Rule::Base + # def test(source) + # end + # end + # + # MyRule.new.name # => "MyRule" + # ``` + # + def name + {{@type}}.rule_name + end + + # Returns a group this rule belong to. + # + # ``` + # struct MyGroup::MyRule < Ameba::Rule::Base + # # ... + # end + # + # MyGroup::MyRule.new.group # => "MyGroup" + # ``` + # + def group + {{@type}}.group_name + end + + # Checks whether the source is excluded from this rule. + # It searches for a path in `excluded` property which matches + # the one of the given source. + # + # ``` + # my_rule.excluded?(source) # => true or false + # ``` + # + def excluded?(source) + excluded.try &.any? do |path| + source.matches_path?(path) || + Dir.glob(path).any? { |glob| source.matches_path? glob } + end + end + + # Returns true if this rule is special and behaves differently than + # usual rules. + # + # ``` + # my_rule.special? # => true or false + # ``` + # + def special? + SPECIAL.includes? name + end + + def ==(other) + name == other.try &.name + end + + def hash + name.hash + end + + macro issue_for(*args) + source.add_issue self, {{*args}} + end + + protected def self.rule_name + name.gsub("Ameba::Rule::", "").gsub("::", "/") + end + + protected def self.group_name + rule_name.split("/")[0...-1].join("/") + end + + protected def self.subclasses + {{ @type.subclasses }} + end + + macro inherited + protected def self.path_to_source_file + __FILE__ + end + end + + # Returns documentation for this rule if any. + # + # ``` + # module Ameba + # # This is a test rule. + # # Does nothing. + # struct MyRule < Ameba::Rule::Base + # def test(source) + # end + # end + # end + # + # MyRule.parsed_doc # => "This is a test rule.\nDoes nothing." + # ``` + def self.parsed_doc + source = File.read(path_to_source_file) + nodes = Crystal::Parser.new(source).tap(&.wants_doc = true).parse + type_name = rule_name.split("/").last? + DocFinder.new(nodes, type_name).doc + end + + # :nodoc: + private class DocFinder < Crystal::Visitor + getter doc : String? + getter type_name : String? + + def initialize(nodes, @type_name) + self.accept(nodes) + end + + def visit(node : Crystal::ASTNode) + return false if @doc + + if node.responds_to?(:name) && + (name = node.name) && + name.is_a?(Crystal::Path) && + name.names.last? == @type_name + @doc = node.doc + end + + true + end + end + end + + # Returns a list of all available rules. + # + # ``` + # Ameba::Rule.rules # => [Rule1, Rule2, ....] + # ``` + # + def self.rules + Base.subclasses + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr new file mode 100644 index 000000000000..a4c24d37dffd --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr @@ -0,0 +1,29 @@ +module Ameba::Rule::Layout + # A rule that disallows lines longer than `max_length` number of symbols. + # + # YAML configuration example: + # + # ``` + # Layout/LineLength: + # Enabled: true + # MaxLength: 100 + # ``` + # + struct LineLength < Base + properties do + enabled false + description "Disallows lines longer than `MaxLength` number of symbols" + max_length 140 + end + + MSG = "Line too long" + + def test(source) + source.lines.each_with_index do |line, index| + next unless line.size > max_length + + issue_for({index + 1, max_length + 1}, MSG) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr new file mode 100644 index 000000000000..24499e9c0099 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr @@ -0,0 +1,33 @@ +module Ameba::Rule::Layout + # A rule that disallows trailing blank lines at the end of the source file. + # + # YAML configuration example: + # + # ``` + # Layout/TrailingBlankLines: + # Enabled: true + # ``` + # + struct TrailingBlankLines < Base + properties do + description "Disallows trailing blank lines" + end + + MSG = "Excessive trailing newline detected" + MSG_FINAL_NEWLINE = "Trailing newline missing" + + def test(source) + source_lines = source.lines + return if source_lines.empty? + + last_source_line = source_lines.last + source_lines_size = source_lines.size + return if source_lines_size == 1 && last_source_line.empty? + + last_line_not_empty = !last_source_line.empty? + if source_lines_size >= 1 && (source_lines.last(2).join.strip.empty? || last_line_not_empty) + issue_for({source_lines_size - 1, 1}, last_line_not_empty ? MSG_FINAL_NEWLINE : MSG) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr new file mode 100644 index 000000000000..a4df797ee138 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr @@ -0,0 +1,25 @@ +module Ameba::Rule::Layout + # A rule that disallows trailing whitespaces. + # + # YAML configuration example: + # + # ``` + # Layout/TrailingWhitespace: + # Enabled: true + # ``` + # + struct TrailingWhitespace < Base + properties do + description "Disallows trailing whitespaces" + end + + MSG = "Trailing whitespace detected" + + def test(source) + source.lines.each_with_index do |line, index| + next unless line =~ /\s$/ + issue_for({index + 1, line.size}, MSG) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr new file mode 100644 index 000000000000..76b6d3589ed7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr @@ -0,0 +1,54 @@ +module Ameba::Rule::Lint + # A rule that reports incorrect comment directives for Ameba. + # + # For example, the user can mistakenly add a directive + # to disable a rule that even doesn't exist: + # + # ``` + # # ameba:disable BadRuleName + # def foo + # :bar + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/BadDirective: + # Enabled: true + # ``` + # + struct BadDirective < Base + properties do + description "Reports bad comment directives" + end + + AVAILABLE_ACTIONS = InlineComments::Action.names.map(&.downcase) + ALL_RULE_NAMES = Rule.rules.map(&.rule_name) + ALL_GROUP_NAMES = Rule.rules.map(&.group_name).uniq! + + def test(source) + Tokenizer.new(source).run do |token| + next unless token.type == :COMMENT + next unless directive = source.parse_inline_directive(token.value.to_s) + + check_action source, token, directive[:action] + check_rules source, token, directive[:rules] + end + end + + private def check_action(source, token, action) + return if InlineComments::Action.parse?(action) + + issue_for token, + "Bad action in comment directive: '%s'. Possible values: %s" % { + action, AVAILABLE_ACTIONS.join(", "), + } + end + + private def check_rules(source, token, rules) + bad_names = rules - ALL_RULE_NAMES - ALL_GROUP_NAMES + issue_for token, "Such rules do not exist: %s" % bad_names.join(", ") unless bad_names.empty? + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr new file mode 100644 index 000000000000..313db5a22dd4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr @@ -0,0 +1,41 @@ +module Ameba::Rule::Lint + # A rule that disallows comparison to booleans. + # + # For example, these are considered invalid: + # + # ``` + # foo == true + # bar != false + # false === baz + # ``` + # + # This is because these expressions evaluate to `true` or `false`, so you + # could get the same result by using either the variable directly, + # or negating the variable. + # + # YAML configuration example: + # + # ``` + # Lint/ComparisonToBoolean: + # Enabled: true + # ``` + # + struct ComparisonToBoolean < Base + properties do + enabled false + description "Disallows comparison to booleans" + end + + MSG = "Comparison to a boolean is pointless" + + def test(source, node : Crystal::Call) + comparison = %w(== != ===).includes?(node.name) + to_boolean = node.args.first?.try &.is_a?(Crystal::BoolLiteral) || + node.obj.is_a?(Crystal::BoolLiteral) + + return unless comparison && to_boolean + + issue_for node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr new file mode 100644 index 000000000000..976744901fa9 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr @@ -0,0 +1,29 @@ +module Ameba::Rule::Lint + # A rule that disallows calls to debugger. + # + # This is because we don't want debugger breakpoints accidentally being + # committed into our codebase. + # + # YAML configuration example: + # + # ``` + # Lint/DebuggerStatement: + # Enabled: true + # ``` + # + struct DebuggerStatement < Base + properties do + description "Disallows calls to debugger" + end + + MSG = "Possible forgotten debugger statement detected" + + def test(source, node : Crystal::Call) + return unless node.name == "debugger" && + node.args.empty? && + node.obj.nil? + + issue_for node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr new file mode 100644 index 000000000000..f8fdeb48e033 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr @@ -0,0 +1,56 @@ +module Ameba::Rule::Lint + # A rule that disallows empty ensure statement. + # + # For example, this is considered invalid: + # + # ``` + # def some_method + # do_some_stuff + # ensure + # end + # + # begin + # do_some_stuff + # ensure + # end + # ``` + # + # And it should be written as this: + # + # + # ``` + # def some_method + # do_some_stuff + # ensure + # do_something_else + # end + # + # begin + # do_some_stuff + # ensure + # do_something_else + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/EmptyEnsure + # Enabled: true + # ``` + # + struct EmptyEnsure < Base + properties do + description "Disallows empty ensure statement" + end + + MSG = "Empty `ensure` block detected" + + def test(source, node : Crystal::ExceptionHandler) + node_ensure = node.ensure + return if node_ensure.nil? || !node_ensure.nop? + + issue_for node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr new file mode 100644 index 000000000000..682ed8a359ba --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr @@ -0,0 +1,56 @@ +module Ameba::Rule::Lint + # A rule that disallows empty expressions. + # + # This is considered invalid: + # + # ``` + # foo = () + # + # if () + # bar + # end + # ``` + # + # And this is valid: + # + # ``` + # foo = (some_expression) + # + # if (some_expression) + # bar + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/EmptyExpression: + # Enabled: true + # ``` + # + struct EmptyExpression < Base + include AST::Util + + properties do + description "Disallows empty expressions" + enabled false + end + + MSG = "Avoid empty expression %s" + MSG_EXRS = "Avoid empty expressions" + + def test(source, node : Crystal::NilLiteral) + exp = node_source(node, source.lines).try &.join + + return if exp.nil? || exp == "nil" + + issue_for node, MSG % exp + end + + def test(source, node : Crystal::Expressions) + if node.expressions.size == 1 && node.expressions.first.nop? + issue_for node, MSG_EXRS + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr new file mode 100644 index 000000000000..19eafd2feebc --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr @@ -0,0 +1,68 @@ +module Ameba::Rule::Lint + # A rule that disallows empty loops. + # + # This is considered invalid: + # + # ``` + # while false + # end + # + # until 10 + # end + # + # loop do + # # nothing here + # end + # ``` + # + # And this is valid: + # + # ``` + # a = 1 + # while a < 10 + # a += 1 + # end + # + # until socket_opened? + # end + # + # loop do + # do_something_here + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/EmptyLoop: + # Enabled: true + # ``` + struct EmptyLoop < Base + include AST::Util + + properties do + description "Disallows empty loops" + end + + MSG = "Empty loop detected" + + def test(source, node : Crystal::Call) + return unless loop?(node) + + check_node(source, node, node.block) + end + + def test(source, node : Crystal::While) + check_node(source, node, node.body) if literal?(node.cond) + end + + def test(source, node : Crystal::Until) + check_node(source, node, node.body) if literal?(node.cond) + end + + private def check_node(source, node, loop_body) + body = loop_body.is_a?(Crystal::Block) ? loop_body.body : loop_body + issue_for node, MSG if body.nil? || body.nop? + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr new file mode 100644 index 000000000000..c9de6a788a45 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr @@ -0,0 +1,43 @@ +module Ameba::Rule::Lint + # A rule that disallows duplicated keys in hash literals. + # + # This is considered invalid: + # + # ``` + # h = {"foo" => 1, "bar" => 2, "foo" => 3} + # ``` + # + # And it has to written as this instead: + # + # ``` + # h = {"foo" => 1, "bar" => 2} + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/HashDuplicatedKey: + # Enabled: true + # ``` + # + struct HashDuplicatedKey < Base + properties do + description "Disallows duplicated keys in hash literals" + end + + MSG = "Duplicated keys in hash literal: %s" + + def test(source, node : Crystal::HashLiteral) + return unless (keys = duplicated_keys(node.entries)).any? + + issue_for node, MSG % keys.join(", ") + end + + private def duplicated_keys(entries) + entries.map(&.key) + .group_by(&.itself) + .select { |_, v| v.size > 1 } + .map { |k, _| k } + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr new file mode 100644 index 000000000000..2dc2e6004361 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr @@ -0,0 +1,50 @@ +module Ameba::Rule::Lint + # A rule that disallows useless conditional statements that contain a literal + # in place of a variable or predicate function. + # + # This is because a conditional construct with a literal predicate will + # always result in the same behaviour at run time, meaning it can be + # replaced with either the body of the construct, or deleted entirely. + # + # This is considered invalid: + # ``` + # if "something" + # :ok + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/LiteralInCondition: + # Enabled: true + # ``` + # + struct LiteralInCondition < Base + include AST::Util + + properties do + description "Disallows useless conditional statements that contain \ + a literal in place of a variable or predicate function" + end + + MSG = "Literal value found in conditional" + + def check_node(source, node) + return unless literal?(node.cond) + issue_for node, MSG + end + + def test(source, node : Crystal::If) + check_node source, node + end + + def test(source, node : Crystal::Unless) + check_node source, node + end + + def test(source, node : Crystal::Case) + check_node source, node + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr new file mode 100644 index 000000000000..df674f74f997 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr @@ -0,0 +1,34 @@ +module Ameba::Rule::Lint + # A rule that disallows useless string interpolations + # that contain a literal value instead of a variable or function. + # + # For example: + # + # ``` + # "Hello, #{:Ary}" + # "There are #{4} cats" + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/LiteralInInterpolation + # Enabled: true + # ``` + # + struct LiteralInInterpolation < Base + include AST::Util + + properties do + description "Disallows useless string interpolations" + end + + MSG = "Literal value found in interpolation" + + def test(source, node : Crystal::StringInterpolation) + node.expressions + .select { |e| !e.is_a?(Crystal::StringLiteral) && literal?(e) } + .each { |n| issue_for n, MSG } + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr new file mode 100644 index 000000000000..0f1d3de82b48 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr @@ -0,0 +1,74 @@ +module Ameba::Rule::Lint + # A rule that disallows some unwanted symbols in percent array literals. + # + # For example, this is usually written by mistake: + # + # ``` + # %i(:one, :two) + # %w("one", "two") + # ``` + # + # And the expected example is: + # + # ``` + # %i(one two) + # %w(one two) + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/PercentArrays: + # Enabled: true + # StringArrayUnwantedSymbols: ',"' + # SymbolArrayUnwantedSymbols: ',:' + # ``` + # + struct PercentArrays < Base + properties do + description "Disallows some unwanted symbols in percent array literals" + string_array_unwanted_symbols ",\"" + symbol_array_unwanted_symbols ",:" + end + + MSG = "Symbols `%s` may be unwanted in %s array literals" + + def test(source) + issue = start_token = nil + + Tokenizer.new(source).run do |token| + case token.type + when :STRING_ARRAY_START, :SYMBOL_ARRAY_START + start_token = token.dup + when :STRING + if start_token && issue.nil? + issue = array_entry_invalid?(token.value, start_token.not_nil!.raw) + end + when :STRING_ARRAY_END, :SYMBOL_ARRAY_END + if issue + issue_for start_token.not_nil!, issue.not_nil! + end + issue = start_token = nil + else + # nop + end + end + end + + private def array_entry_invalid?(entry, array_type) + case array_type + when .starts_with? "%w" + check_array_entry entry, string_array_unwanted_symbols, "%w" + when .starts_with? "%i" + check_array_entry entry, symbol_array_unwanted_symbols, "%i" + else + # nop + end + end + + private def check_array_entry(entry, symbols, literal) + return unless entry =~ /[#{symbols}]/ + MSG % {symbols, literal} + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr new file mode 100644 index 000000000000..6c2b70f286d1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr @@ -0,0 +1,44 @@ +module Ameba::Rule::Lint + # A rule that disallows `rand(0)` and `rand(1)` calls. + # Such calls always return `0`. + # + # For example: + # + # ``` + # rand(1) + # ``` + # + # Should be written as: + # + # ``` + # rand + # # or + # rand(2) + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/RandZero: + # Enabled: true + # ``` + # + struct RandZero < Base + properties do + description "Disallows rand zero calls" + end + + MSG = "%s always returns 0" + + def test(source, node : Crystal::Call) + return unless node.name == "rand" && + node.args.size == 1 && + (arg = node.args.first) && + (arg.is_a? Crystal::NumberLiteral) && + (value = arg.value) && + (value == "0" || value == "1") + + issue_for node, MSG % node + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr new file mode 100644 index 000000000000..f21484a22146 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr @@ -0,0 +1,47 @@ +module Ameba::Rule::Lint + # A rule that disallows string conversion in string interpolation, + # which is redundant. + # + # For example, this is considered invalid: + # + # ``` + # "Hello, #{name.to_s}" + # ``` + # + # And this is valid: + # + # ``` + # "Hello, #{name}" + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/RedundantStringCoersion + # Enabled: true + # ``` + # + struct RedundantStringCoercion < Base + include AST::Util + + properties do + description "Disallows redundant string conversions in interpolation" + end + + MSG = "Redundant use of `Object#to_s` in interpolation" + + def test(source, node : Crystal::StringInterpolation) + string_coercion_nodes(node).each { |n| issue_for n.name_location, n.end_location, MSG } + end + + private def string_coercion_nodes(node) + node.expressions.select do |e| + e.is_a?(Crystal::Call) && + e.name == "to_s" && + e.args.size.zero? && + e.named_args.nil? && + e.obj + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr new file mode 100644 index 000000000000..f67766d6d81b --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr @@ -0,0 +1,58 @@ +module Ameba::Rule::Lint + # A rule that disallows redundant `with_index` calls. + # + # For example, this is considered invalid: + # ``` + # collection.each.with_index do |e| + # # ... + # end + # + # collection.each_with_index do |e, _| + # # ... + # end + # ``` + # + # and it should be written as follows: + # + # ``` + # collection.each do |e| + # # ... + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/RedundantWithIndex: + # Enabled: true + # ``` + # + struct RedundantWithIndex < Base + properties do + description "Disallows redundant `with_index` calls" + end + + def test(source, node : Crystal::Call) + args, block = node.args, node.block + + return if args.size > 1 || block.nil? || with_index_arg?(block.not_nil!) + + case node.name + when "with_index" + report source, node, "Remove redundant with_index" + when "each_with_index" + report source, node, "Use each instead of each_with_index" + else + # nop + end + end + + private def with_index_arg?(block : Crystal::Block) + block.args.size >= 2 && block.args.last.name != "_" + end + + private def report(source, node, msg) + issue_for node.name_location, node.name_end_location, msg + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr new file mode 100644 index 000000000000..357e45d2467d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr @@ -0,0 +1,51 @@ +module Ameba::Rule::Lint + # A rule that disallows redundant `each_with_object` calls. + # + # For example, this is considered invalid: + # + # ``` + # collection.each_with_object(0) do |e| + # # ... + # end + # + # collection.each_with_object(0) do |e, _| + # # ... + # end + # ``` + # + # and it should be written as follows: + # + # ``` + # collection.each do |e| + # # ... + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/RedundantWithObject: + # Enabled: true + # ``` + # + struct RedundantWithObject < Base + properties do + description "Disallows redundant `with_object` calls" + end + + def test(source, node : Crystal::Call) + return if node.name != "each_with_object" || + node.args.size != 1 || + node.block.nil? || + with_index_arg?(node.block.not_nil!) + + issue_for node.name_location, + node.name_end_location, + "Use each instead of each_with_object" + end + + private def with_index_arg?(block : Crystal::Block) + block.args.size >= 2 && block.args.last.name != "_" + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr new file mode 100644 index 000000000000..0c27c25df87f --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr @@ -0,0 +1,58 @@ +module Ameba::Rule::Lint + # A rule that disallows shadowed arguments. + # + # For example, this is considered invalid: + # + # ``` + # do_something do |foo| + # foo = 1 # shadows block argument + # foo + # end + # + # def do_something(foo) + # foo = 1 # shadows method argument + # foo + # end + # ``` + # + # and it should be written as follows: + # + # ``` + # do_something do |foo| + # foo = foo + 42 + # foo + # end + # + # def do_something(foo) + # foo = foo + 42 + # foo + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/ShadowedArgument: + # Enabled: true + # ``` + # + struct ShadowedArgument < Base + properties do + description "Disallows shadowed arguments" + end + + MSG = "Argument `%s` is assigned before it is used" + + def test(source) + AST::ScopeVisitor.new self, source + end + + def test(source, node, scope : AST::Scope) + scope.arguments.each do |arg| + next unless assign = arg.variable.assign_before_reference + + issue_for assign, MSG % arg.name + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr new file mode 100644 index 000000000000..5357034070ea --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr @@ -0,0 +1,77 @@ +module Ameba::Rule::Lint + # A rule that disallows a rescued exception that get shadowed by a + # less specific exception being rescued before a more specific + # exception is rescued. + # + # For example, this is invalid: + # + # ``` + # begin + # do_something + # rescue Exception + # handle_exception + # rescue ArgumentError + # handle_argument_error_exception + # end + # ``` + # + # And it has to be written as follows: + # + # ``` + # begin + # do_something + # rescue ArgumentError + # handle_argument_error_exception + # rescue Exception + # handle_exception + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/ShadowedException: + # Enabled: true + # ``` + # + struct ShadowedException < Base + properties do + description "Disallows rescued exception that get shadowed" + end + + MSG = "Exception handler has shadowed exceptions: %s" + + def test(source, node : Crystal::ExceptionHandler) + return unless excs = node.rescues + + if (excs = shadowed excs.map(&.types)).any? + issue_for node, MSG % excs.join(", ") + end + end + + private def shadowed(exceptions, exception_found = false) + previous_exceptions = [] of String + + exceptions.reduce([] of String) do |shadowed, excs| + excs = excs ? excs.map(&.to_s) : ["Exception"] + + if exception_found + shadowed.concat excs + previous_exceptions.concat excs + else + exception_found ||= excs.any? &.== "Exception" + excs.each do |exc| + if exception_found && exc != "Exception" + shadowed << exc + else + shadowed << exc if previous_exceptions.any? &.== exc + end + previous_exceptions << exc + end + end + + shadowed + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr new file mode 100644 index 000000000000..846cc4840666 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr @@ -0,0 +1,69 @@ +module Ameba::Rule::Lint + # A rule that disallows the usage of the same name as outer local variables + # for block or proc arguments. + # + # For example, this is considered incorrect: + # + # ``` + # def some_method + # foo = 1 + # + # 3.times do |foo| # shadowing outer `foo` + # end + # end + # ``` + # + # and should be written as: + # + # ``` + # def some_method + # foo = 1 + # + # 3.times do |bar| + # end + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/ShadowingOuterLocalVar: + # Enabled: true + # ``` + # + struct ShadowingOuterLocalVar < Base + properties do + description "Disallows the usage of the same name as outer local variables" \ + " for block or proc arguments." + end + + MSG = "Shadowing outer local variable `%s`" + + def test(source) + AST::ScopeVisitor.new self, source + end + + def test(source, node : Crystal::ProcLiteral, scope : AST::Scope) + find_shadowing source, scope + end + + def test(source, node : Crystal::Block, scope : AST::Scope) + find_shadowing source, scope + end + + private def find_shadowing(source, scope) + outer_scope = scope.outer_scope + + return if outer_scope.nil? || outer_scope.in_macro? + + scope.arguments.reject(&.ignored?).each do |arg| + variable = outer_scope.find_variable(arg.name) + + next if variable.nil? || !variable.declared_before?(arg) + next if outer_scope.assigns_ivar?(arg.name) + + issue_for arg.node, MSG % arg.name + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr new file mode 100644 index 000000000000..8011fa036446 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr @@ -0,0 +1,86 @@ +module Ameba::Rule::Lint + # A rule that disallows using shared variables in fibers, + # which are mutated during iterations. + # + # In most cases it leads to unexpected behaviour and is undesired. + # + # For example, having this example: + # + # ``` + # n = 0 + # channel = Channel(Int32).new + # + # while n < 3 + # n = n + 1 + # spawn { channel.send n } + # end + # + # 3.times { puts channel.receive } # => # 3, 3, 3 + # ``` + # + # The problem is there is only one shared between fibers variable `n` + # and when `channel.receive` is executed its value is `3`. + # + # To solve this, the code above needs to be rewritten to the following: + # + # ``` + # n = 0 + # channel = Channel(Int32).new + # + # while n < 3 + # n = n + 1 + # m = n + # spawn do { channel.send m } + # end + # + # 3.times { puts channel.receive } # => # 1, 2, 3 + # ``` + # + # This rule is able to find the shared variables between fibers, which are mutated + # during iterations. So it reports the issue on the first sample and passes on + # the second one. + # + # There are also other technics to solve the problem above which are + # [officially documented](https://crystal-lang.org/reference/guides/concurrency.html) + # + # YAML configuration example: + # + # ``` + # Lint/SharedVarInFiber: + # Enabled: true + # ``` + # + struct SharedVarInFiber < Base + properties do + description "Disallows shared variables in fibers." + end + + MSG = "Shared variable `%s` is used in fiber" + + def test(source) + AST::ScopeVisitor.new self, source + end + + def test(source, node, scope : AST::Scope) + return unless scope.spawn_block? + + scope.references.each do |ref| + next if (variable = scope.find_variable(ref.name)).nil? + next if variable.scope == scope || !mutated_in_loop?(variable) + + issue_for ref.node, MSG % variable.name + end + end + + # Variable is mutated in loop if it was declared above the loop and assigned inside. + private def mutated_in_loop?(variable) + declared_in = variable.assignments.first?.try &.branch + + variable.assignments + .reject { |assign| assign.scope.spawn_block? } + .any? do |assign| + assign.branch.try(&.in_loop?) && assign.branch != declared_in + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr new file mode 100644 index 000000000000..8d135401ed76 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr @@ -0,0 +1,34 @@ +module Ameba::Rule::Lint + # A rule that reports invalid Crystal syntax. + # + # For example, this syntax is invalid: + # + # ``` + # def hello + # do_something + # rescue Exception => e + # end + # ``` + # + # And should be properly written: + # + # ``` + # def hello + # do_something + # rescue e : Exception + # end + # ``` + # + struct Syntax < Base + properties do + description "Reports invalid Crystal syntax" + severity Ameba::Severity::Error + end + + def test(source) + source.ast + rescue e : Crystal::SyntaxException + issue_for({e.line_number, e.column_number}, e.message.to_s) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr new file mode 100644 index 000000000000..6128fbeba8f4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr @@ -0,0 +1,67 @@ +module Ameba::Rule::Lint + # A rule that reports unneeded disable directives. + # For example, this is considered invalid: + # + # ``` + # # ameba:disable Style/PredicateName + # def comment? + # do_something + # end + # ``` + # + # as the predicate name is correct and the comment directive does not + # have any effect, the snippet should be written as the following: + # + # ``` + # def comment? + # do_something + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/UnneededDisableDirective + # Enabled: true + # ``` + # + struct UnneededDisableDirective < Base + properties do + description "Reports unneeded disable directives in comments" + end + + MSG = "Unnecessary disabling of %s" + + def test(source) + Tokenizer.new(source).run do |token| + next unless token.type == :COMMENT + next unless directive = source.parse_inline_directive(token.value.to_s) + next unless names = unneeded_disables(source, directive, token.location) + next unless names.any? + + issue_for token, MSG % names.join(", ") + end + end + + private def unneeded_disables(source, directive, location) + return unless directive[:action] == "disable" + + directive[:rules].reject do |rule_name| + source.issues.any? do |issue| + issue.rule.name == rule_name && + issue.disabled? && + issue_at_location?(source, issue, location) + end && rule_name != self.name + end + end + + private def issue_at_location?(source, issue, location) + return false unless issue_line_number = issue.location.try(&.line_number) + + issue_line_number == location.line_number || + ((prev_line_number = issue_line_number - 1) && + prev_line_number == location.line_number && + source.comment?(prev_line_number - 1)) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr new file mode 100644 index 000000000000..ded09bf57ba4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr @@ -0,0 +1,64 @@ +module Ameba::Rule::Lint + # A rule that reports unreachable code. + # + # For example, this is considered invalid: + # + # ``` + # def method(a) + # return 42 + # a + 1 + # end + # ``` + # + # ``` + # a = 1 + # loop do + # break + # a += 1 + # end + # ``` + # + # And has to be written as the following: + # + # ``` + # def method(a) + # return 42 if a == 0 + # a + 1 + # end + # ``` + # + # ``` + # a = 1 + # loop do + # break a > 3 + # a += 1 + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/UnreachableCode: + # Enabled: true + # ``` + # + struct UnreachableCode < Base + include AST::Util + + properties do + description "Reports unreachable code" + end + + MSG = "Unreachable code detected" + + def test(source) + AST::FlowExpressionVisitor.new self, source + end + + def test(source, node, flow_expression : AST::FlowExpression) + if unreachable_node = flow_expression.unreachable_nodes.first? + issue_for unreachable_node, MSG + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr new file mode 100644 index 000000000000..6abadca80b86 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr @@ -0,0 +1,65 @@ +module Ameba::Rule::Lint + # A rule that reports unused arguments. + # For example, this is considered invalid: + # + # ``` + # def method(a, b, c) + # a + b + # end + # ``` + # and should be written as: + # + # ``` + # def method(a, b) + # a + b + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/UnusedArgument: + # Enabled: true + # IgnoreDefs: true + # IgnoreBlocks: false + # IgnoreProcs: false + # ``` + # + struct UnusedArgument < Base + properties do + description "Disallows unused arguments" + + ignore_defs true + ignore_blocks false + ignore_procs false + end + + MSG = "Unused argument `%s`. If it's necessary, use `%s` " \ + "as an argument name to indicate that it won't be used." + + def test(source) + AST::ScopeVisitor.new self, source + end + + def test(source, node : Crystal::ProcLiteral, scope : AST::Scope) + ignore_procs || find_unused_arguments source, scope + end + + def test(source, node : Crystal::Block, scope : AST::Scope) + ignore_blocks || find_unused_arguments source, scope + end + + def test(source, node : Crystal::Def, scope : AST::Scope) + ignore_defs || find_unused_arguments source, scope + end + + private def find_unused_arguments(source, scope) + scope.arguments.each do |argument| + next if argument.ignored? || scope.references?(argument.variable) + + name_suggestion = scope.node.is_a?(Crystal::Block) ? '_' : "_#{argument.name}" + issue_for argument.node, MSG % {argument.name, name_suggestion} + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr new file mode 100644 index 000000000000..bff181889f3c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr @@ -0,0 +1,51 @@ +module Ameba::Rule::Lint + # A rule that disallows useless assignments. + # + # For example, this is considered invalid: + # + # ``` + # def method + # var = 1 + # do_something + # end + # ``` + # + # And has to be written as the following: + # + # ``` + # def method + # var = 1 + # do_something(var) + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/UselessAssign: + # Enabled: true + # ``` + # + struct UselessAssign < Base + properties do + description "Disallows useless variable assignments" + end + + MSG = "Useless assignment to variable `%s`" + + def test(source) + AST::ScopeVisitor.new self, source + end + + def test(source, node, scope : AST::Scope) + scope.variables.each do |var| + next if var.captured_by_block? || var.used_in_macro? || var.ignored? + + var.assignments.each do |assign| + next if assign.referenced? || assign.transformed? + issue_for assign.target_node, MSG % var.name + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr new file mode 100644 index 000000000000..4b2fb36cff72 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr @@ -0,0 +1,83 @@ +module Ameba::Rule::Lint + # A rule that disallows useless conditions in when clause + # where it is guaranteed to always return the same result. + # + # For example, this is considered invalid: + # + # ``` + # case + # when utc? + # io << " UTC" + # when local? + # Format.new(" %:z").format(self, io) if local? + # end + # ``` + # + # And has to be written as the following: + # + # ``` + # case + # when utc? + # io << " UTC" + # when local? + # Format.new(" %:z").format(self, io) + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Lint/UselessConditionInWhen: + # Enabled: true + # ``` + # + struct UselessConditionInWhen < Base + properties do + description "Disallows useless conditions in when" + end + + MSG = "Useless condition in when detected" + + # TODO: condition.cond may be a complex ASTNode with + # useless inner conditions. We might need to improve this + # simple implementation in future. + protected def check_node(source, when_node, cond) + cond_s = cond.to_s + return if when_node + .conds + .map(&.to_s) + .none? { |c| c == cond_s } + + issue_for cond, MSG + end + + def test(source, node : Crystal::When) + ConditionInWhenVisitor.new self, source, node + end + + # :nodoc: + private class ConditionInWhenVisitor < Crystal::Visitor + @source : Source + @rule : UselessConditionInWhen + @parent : Crystal::When + + def initialize(@rule, @source, @parent) + @parent.accept self + end + + def visit(node : Crystal::If) + @rule.check_node(@source, @parent, node.cond) + true + end + + def visit(node : Crystal::Unless) + @rule.check_node(@source, @parent, node.cond) + true + end + + def visit(node : Crystal::ASTNode) + true + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr new file mode 100644 index 000000000000..fb2e2fe22594 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr @@ -0,0 +1,38 @@ +module Ameba::Rule::Metrics + # A rule that disallows methods with a cyclomatic complexity higher than `MaxComplexity` + # + # YAML configuration example: + # + # ``` + # Metrics/CyclomaticComplexity: + # Enabled: true + # MaxComplexity: 10 + # ``` + # + struct CyclomaticComplexity < Base + properties do + description "Disallows methods with a cyclomatic complexity higher than `MaxComplexity`" + max_complexity 10 + end + + MSG = "Cyclomatic complexity too high [%d/%d]" + + def test(source, node : Crystal::Def) + complexity = AST::CountingVisitor.new(node).count + + if complexity > max_complexity && (location = node.name_location) + issue_for( + location, + def_name_end_location(node), + MSG % {complexity, max_complexity} + ) + end + end + + private def def_name_end_location(node) + return unless location = node.name_location + line_number, column_number = location.line_number, location.column_number + Crystal::Location.new(location.filename, line_number, column_number + node.name.size) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr new file mode 100644 index 000000000000..5e4ae0772f44 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr @@ -0,0 +1,46 @@ +module Ameba::Rule::Performance + # This rule is used to identify usage of `any?` calls that follow filters. + # + # For example, this is considered invalid: + # + # ``` + # [1, 2, 3].select { |e| e > 2 }.any? + # [1, 2, 3].reject { |e| e >= 2 }.any? + # ``` + # + # And it should be written as this: + # + # ``` + # [1, 2, 3].any? { |e| e > 2 } + # [1, 2, 3].any? { |e| e < 2 } + # ``` + # + # YAML configuration example: + # + # ``` + # Performance/AnyAfterFilter: + # Enabled: true + # FilterNames: + # - select + # - reject + # ``` + # + struct AnyAfterFilter < Base + ANY_NAME = "any?" + MSG = "Use `#{ANY_NAME} {...}` instead of `%s {...}.#{ANY_NAME}`" + + properties do + filter_names : Array(String) = %w(select reject) + description "Identifies usage of `any?` calls that follow filters." + end + + def test(source, node : Crystal::Call) + return unless node.name == ANY_NAME && (obj = node.obj) + + if node.block.nil? && obj.is_a?(Crystal::Call) && + filter_names.includes?(obj.name) && !obj.block.nil? + issue_for obj.name_location, node.name_end_location, MSG % obj.name + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr new file mode 100644 index 000000000000..a359942302ad --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr @@ -0,0 +1,57 @@ +module Ameba::Rule::Performance + # This rule is used to identify usage of `first/last/first?/last?` calls that follow filters. + # + # For example, this is considered inefficient: + # + # ``` + # [-1, 0, 1, 2].select { |e| e > 0 }.first? + # [-1, 0, 1, 2].select { |e| e > 0 }.last? + # ``` + # + # And can be written as this: + # + # ``` + # [-1, 0, 1, 2].find { |e| e > 0 } + # [-1, 0, 1, 2].reverse_each.find { |e| e > 0 } + # ``` + # + # YAML configuration example: + # + # ``` + # Performance/FirstLastAfterFilter + # Enabled: true + # FilterNames: + # - select + # ``` + # + struct FirstLastAfterFilter < Base + CALL_NAMES = %w(first last first? last?) + MSG = "Use `find {...}` instead of `%s {...}.%s`" + MSG_REVERSE = "Use `reverse_each.find {...}` instead of `%s {...}.%s`" + + properties do + filter_names : Array(String) = %w(select) + description "Identifies usage of `first/last/first?/last?` calls that follow filters." + end + + def test(source) + AST::NodeVisitor.new self, source, skip: [ + Crystal::Macro, + Crystal::MacroExpression, + Crystal::MacroIf, + Crystal::MacroFor, + ] + end + + def test(source, node : Crystal::Call) + return unless CALL_NAMES.includes?(node.name) && (obj = node.obj) + return if node.args.any? + + if node.block.nil? && obj.is_a?(Crystal::Call) && + filter_names.includes?(obj.name) && !obj.block.nil? + message = node.name.includes?(CALL_NAMES.first) ? MSG : MSG_REVERSE + issue_for obj.name_location, node.name_end_location, message % {obj.name, node.name} + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr new file mode 100644 index 000000000000..1fe26359b8b2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr @@ -0,0 +1,61 @@ +module Ameba::Rule::Performance + # This rule is used to identify usage of `size` calls that follow filter. + # + # For example, this is considered invalid: + # + # ``` + # [1, 2, 3].select { |e| e > 2 }.size + # [1, 2, 3].reject { |e| e < 2 }.size + # [1, 2, 3].select(&.< 2).size + # [0, 1, 2].select(&.zero?).size + # [0, 1, 2].reject(&.zero?).size + # ``` + # + # And it should be written as this: + # + # ``` + # [1, 2, 3].count { |e| e > 2 } + # [1, 2, 3].count { |e| e >= 2 } + # [1, 2, 3].count(&.< 2) + # [0, 1, 2].count(&.zero?) + # [0, 1, 2].count(&.!= 0) + # ``` + # + # YAML configuration example: + # + # ``` + # Performance/SizeAfterFilter: + # Enabled: true + # FilterNames: + # - select + # - reject + # ``` + # + struct SizeAfterFilter < Base + SIZE_NAME = "size" + MSG = "Use `count {...}` instead of `%s {...}.#{SIZE_NAME}`." + + properties do + filter_names : Array(String) = %w(select reject) + description "Identifies usage of `size` calls that follow filter" + end + + def test(source) + AST::NodeVisitor.new self, source, skip: [ + Crystal::Macro, + Crystal::MacroExpression, + Crystal::MacroIf, + Crystal::MacroFor, + ] + end + + def test(source, node : Crystal::Call) + return unless node.name == SIZE_NAME && (obj = node.obj) + + if obj.is_a?(Crystal::Call) && + filter_names.includes?(obj.name) && !obj.block.nil? + issue_for obj.name_location, node.name_end_location, MSG % obj.name + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr new file mode 100644 index 000000000000..3628006e7c5f --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr @@ -0,0 +1,43 @@ +module Ameba::Rule::Style + # A rule that enforces constant names to be in screaming case. + # + # For example, these constant names are considered valid: + # + # ``` + # LUCKY_NUMBERS = [3, 7, 11] + # DOCUMENTATION_URL = "http://crystal-lang.org/docs" + # ``` + # + # And these are invalid names: + # + # ``` + # myBadConstant = 1 + # Wrong_NAME = 2 + # ``` + # + # YAML configuration example: + # + # ``` + # Style/ConstantNames: + # Enabled: true + # ``` + # + struct ConstantNames < Base + properties do + description "Enforces constant names to be in screaming case" + end + + MSG = "Constant name should be screaming-cased: %s, not %s" + + def test(source, node : Crystal::Assign) + if (target = node.target).is_a? Crystal::Path + name = target.names.first + expected = name.upcase + + return if expected == name || name.camelcase == name + + issue_for target, MSG % {expected, name} + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr new file mode 100644 index 000000000000..18653bbc22be --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr @@ -0,0 +1,38 @@ +module Ameba::Rule::Style + # A rule that disallows calls to `is_a?(Nil)` in favor of `nil?`. + # + # This is considered bad: + # + # ``` + # var.is_a? Nil + # ``` + # + # And needs to be written as: + # + # ``` + # var.nil? + # ``` + # + # YAML configuration example: + # + # ``` + # Style/IsANil: + # Enabled: true + # ``` + # + struct IsANil < Base + properties do + description "Disallows calls to `is_a?(Nil)` in favor of `nil?`" + end + + MSG = "Use `nil?` instead of `is_a?(Nil)`" + PATH_NIL_NAMES = %w(Nil) + + def test(source, node : Crystal::IsA) + return if node.nil_check? + + const = node.const + issue_for const, MSG if const.is_a?(Crystal::Path) && const.names == PATH_NIL_NAMES + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr new file mode 100644 index 000000000000..a9a2b33dbc25 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr @@ -0,0 +1,109 @@ +module Ameba::Rule::Style + # A rule that disallows usage of large numbers without underscore. + # These do not affect the value of the number, but can help read + # large numbers more easily. + # + # For example, these are considered invalid: + # + # ``` + # 10000 + # 141592654 + # 5.12345 + # ``` + # + # And has to be rewritten as the following: + # + # ``` + # 10_000 + # 141_592_654 + # 5.123_45 + # ``` + # + # YAML configuration example: + # + # ``` + # Style/LargeNumbers: + # Enabled: true + # IntMinDigits: 5 # i.e. integers higher than 9999 + # ``` + # + struct LargeNumbers < Base + properties do + description "Disallows usage of large numbers without underscore" + int_min_digits 5 + enabled false + end + + MSG = "Large numbers should be written with underscores: %s" + + def test(source) + Tokenizer.new(source).run do |token| + next unless token.type == :NUMBER && decimal?(token.raw) + + parsed = parse_number token.raw + + if allowed?(*parsed) && (expected = underscored *parsed) != token.raw + issue_for token, MSG % expected + end + end + end + + private def decimal?(value) + value !~ /^0(x|b|o)/ + end + + private def allowed?(_sign, value, fraction, _suffix) + return true if !fraction.nil? && fraction.size > 3 + + digits = value.chars.select &.to_s.=~ /[0-9]/ + digits.size >= int_min_digits + end + + private def underscored(sign, value, fraction, suffix) + value = slice_digits(value.reverse) { |slice| slice }.reverse + fraction = "." + slice_digits(fraction) { |slice| slice } if fraction + + "#{sign}#{value}#{fraction}#{suffix}" + end + + private def slice_digits(value, by = 3) + ([] of String).tap do |slices| + value.chars.reject(&.== '_').each_slice(by) do |slice| + slices << (yield slice).join + end + end.join("_") + end + + private def parse_number(value) + value, sign = parse_sign(value) + value, suffix = parse_suffix(value) + value, fraction = parse_fraction(value) + + {sign, value, fraction, suffix} + end + + private def parse_sign(value) + if "+-".includes?(value[0]) + sign = value[0] + value = value[1..-1] + end + {value, sign} + end + + private def parse_suffix(value) + if pos = (value =~ /e/ || value =~ /_?(i|u|f)/) + suffix = value[pos..-1] + value = value[0..pos - 1] + end + {value, suffix} + end + + private def parse_fraction(value) + if comma = value.index('.') + fraction = value[comma + 1..-1] + value = value[0..comma - 1] + end + {value, fraction} + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr new file mode 100644 index 000000000000..905de3735d92 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr @@ -0,0 +1,63 @@ +module Ameba::Rule::Style + # A rule that enforces method names to be in underscored case. + # + # For example, these are considered valid: + # + # ``` + # class Person + # def first_name + # end + # + # def date_of_birth + # end + # + # def homepage_url + # end + # end + # ``` + # + # And these are invalid method names: + # + # ``` + # class Person + # def firstName + # end + # + # def date_of_Birth + # end + # + # def homepageURL + # end + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Style/MethodNames: + # Enabled: true + # ``` + # + struct MethodNames < Base + properties do + description "Enforces method names to be in underscored case" + end + + MSG = "Method name should be underscore-cased: %s, not %s" + + def test(source, node : Crystal::Def) + return if (expected = node.name.underscore) == node.name + + line_number = node.location.try &.line_number + column_number = node.name_location.try &.column_number + + return if line_number.nil? || column_number.nil? + + issue_for( + {line_number, column_number}, + {line_number, column_number + node.name.size - 1}, + MSG % {expected, node.name} + ) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr new file mode 100644 index 000000000000..8781b36a9771 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr @@ -0,0 +1,55 @@ +module Ameba::Rule::Style + # A rule that disallows negated conditions in unless. + # + # For example, this is considered invalid: + # + # ``` + # unless !s.empty? + # :ok + # end + # ``` + # + # And should be rewritten to the following: + # + # ``` + # if s.emtpy? + # :ok + # end + # ``` + # + # It is pretty difficult to wrap your head around a block of code + # that is executed if a negated condition is NOT met. + # + # YAML configuration example: + # + # ``` + # Style/NegatedConditionsInUnless: + # Enabled: true + # ``` + # + struct NegatedConditionsInUnless < Base + properties do + description "Disallows negated conditions in unless" + end + + MSG = "Avoid negated conditions in unless blocks" + + def test(source, node : Crystal::Unless) + return unless negated_condition? node.cond + issue_for node, MSG + end + + private def negated_condition?(node) + case node + when Crystal::BinaryOp + negated_condition?(node.left) || negated_condition?(node.right) + when Crystal::Expressions + node.expressions.any? { |e| negated_condition? e } + when Crystal::Not + true + else + false + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr new file mode 100644 index 000000000000..b23d938c38e1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr @@ -0,0 +1,49 @@ +module Ameba::Rule::Style + # A rule that disallows tautological predicate names, meaning those that + # start with the prefix `has_` or the prefix `is_`. Ignores if the alternative isn't valid Crystal code (e.g. `is_404?`). + # + # Favour these: + # + # ``` + # def valid?(x) + # end + # + # def picture?(x) + # end + # ``` + # + # Over these: + # + # ``` + # def is_valid?(x) + # end + # + # def has_picture?(x) + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Style/PredicateName: + # Enabled: true + # ``` + # + struct PredicateName < Base + properties do + description "Disallows tautological predicate names" + enabled false + end + + MSG = "Favour method name '%s?' over '%s'" + + def test(source, node : Crystal::Def) + if node.name =~ /^(is|has)_(\w+)\?/ + alternative = $2 + return unless alternative =~ /^[a-z][a-zA-Z_0-9]*$/ + + issue_for node, MSG % {alternative, node.name} + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr new file mode 100644 index 000000000000..bfb028a6354d --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr @@ -0,0 +1,131 @@ +module Ameba::Rule::Style + # A rule that disallows redundant begin blocks. + # + # Currently it is able to detect: + # + # 1. Exception handler block that can be used as a part of the method. + # + # For example, this: + # + # ``` + # def method + # begin + # read_content + # rescue + # close_file + # end + # end + # ``` + # + # should be rewritten as: + # + # ``` + # def method + # read_content + # rescue + # close_file + # end + # ``` + # + # 2. begin..end block as a top level block in a method. + # + # For example this is considered invalid: + # + # ``` + # def method + # begin + # a = 1 + # b = 2 + # end + # end + # ``` + # + # and has to be written as the following: + # + # ``` + # def method + # a = 1 + # b = 2 + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Style/RedundantBegin: + # Enabled: true + # ``` + # + struct RedundantBegin < Base + include AST::Util + properties do + description "Disallows redundant begin blocks" + end + + MSG = "Redundant `begin` block detected" + + def test(source, node : Crystal::Def) + return unless redundant_begin?(source, node) + + issue_for node, MSG + end + + private def redundant_begin?(source, node) + case body = node.body + when Crystal::ExceptionHandler + redundant_begin_in_handler?(source, body, node) + when Crystal::Expressions + redundant_begin_in_expressions?(body) + else + # nop + end + end + + private def redundant_begin_in_expressions?(node) + node.keyword == :begin + end + + private def redundant_begin_in_handler?(source, handler, node) + return false if begin_exprs_in_handler?(handler) || inner_handler?(handler) + + code = node_source(node, source.lines).try &.join("\n") + def_redundant_begin? code if code + rescue + false + end + + private def inner_handler?(handler) + handler.body.is_a?(Crystal::ExceptionHandler) + end + + private def begin_exprs_in_handler?(handler) + if (body = handler.body).is_a?(Crystal::Expressions) + body.expressions.first.is_a?(Crystal::ExceptionHandler) + end + end + + private def def_redundant_begin?(code) + lexer = Crystal::Lexer.new code + in_body = in_argument_list = false + loop do + token = lexer.next_token + + case token.type + when :EOF, :"->" + break + when :IDENT + return token.value == :begin if in_body + when :"(" + in_argument_list = true + when :")" + in_argument_list = false + when :NEWLINE + in_body = true unless in_argument_list + when :SPACE + else + return false if in_body + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr new file mode 100644 index 000000000000..fdf62d435242 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr @@ -0,0 +1,119 @@ +module Ameba::Rule::Style + # A rule that disallows redundant next expressions. A `next` keyword allows + # a block to skip to the next iteration early, however, it is considered + # redundant in cases where it is the last expression in a block or combines + # into the node which is the last in a block. + # + # For example, this is considered invalid: + # + # ``` + # block do |v| + # next v + 1 + # end + # ``` + # + # ``` + # block do |v| + # case v + # when .nil? + # next "nil" + # when .blank? + # next "blank" + # else + # next "empty" + # end + # end + # ``` + # + # And has to be written as the following: + # + # ``` + # block do |v| + # v + 1 + # end + # ``` + # + # ``` + # block do |v| + # case arg + # when .nil? + # "nil" + # when .blank? + # "blank" + # else + # "empty" + # end + # end + # ``` + # + # ### Configuration params + # + # 1. *allow_multi_next*, default: true + # + # Allows end-user to configure whether to report or not the next statements + # which yield tuple literals i.e. + # + # ``` + # block do + # next a, b + # end + # ``` + # + # If this param equals to `false`, the block above will be forced to be written as: + # + # ``` + # block do + # {a, b} + # end + # ``` + # + # 2. *allow_empty_next*, default: true + # + # Allows end-user to configure whether to report or not the next statements + # without arguments. Sometimes such statements are used to yild the `nil` value explicitly. + # + # ``` + # block do + # @foo = :empty + # next + # end + # ``` + # + # If this param equals to `false`, the block above will be forced to be written as: + # + # ``` + # block do + # @foo = :empty + # nil + # end + # ``` + # + # ### YAML config example + # + # ``` + # Style/RedundantNext: + # Enabled: true + # AllowMultiNext: true + # AllowEmptyNext: true + # ``` + struct RedundantNext < Base + properties do + description "Reports redundant next expressions" + allow_multi_next true + allow_empty_next true + end + + MSG = "Redundant `next` detected" + + def test(source, node : Crystal::Block) + AST::RedundantControlExpressionVisitor.new(self, source, node.body) + end + + def test(source, node : Crystal::Next, visitor : AST::RedundantControlExpressionVisitor) + return if allow_multi_next && node.exp.is_a?(Crystal::TupleLiteral) + return if allow_empty_next && (node.exp.nil? || node.exp.not_nil!.nop?) + + source.try &.add_issue self, node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr new file mode 100644 index 000000000000..be226bbbd507 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr @@ -0,0 +1,116 @@ +module Ameba::Rule::Style + # A rule that disallows redundant return expressions. + # + # For example, this is considered invalid: + # + # ``` + # def foo + # return :bar + # end + # ``` + # + # ``` + # def bar(arg) + # case arg + # when .nil? + # return "nil" + # when .blank? + # return "blank" + # else + # return "empty" + # end + # end + # ``` + # + # And has to be written as the following: + # + # ``` + # def foo + # :bar + # end + # ``` + # + # ``` + # def bar(arg) + # case arg + # when .nil? + # "nil" + # when .blank? + # "blank" + # else + # "empty" + # end + # end + # ``` + # + # ### Configuration params + # + # 1. *allow_multi_return*, default: true + # + # Allows end-user to configure whether to report or not the return statements + # which return tuple literals i.e. + # + # ``` + # def method(a, b) + # return a, b + # end + # ``` + # + # If this param equals to `false`, the method above has to be written as: + # + # ``` + # def method(a, b) + # {a, b} + # end + # ``` + # + # 2. *allow_empty_return*, default: true + # + # Allows end-user to configure whether to report or not the return statements + # without arguments. Sometimes such returns are used to return the `nil` value explicitly. + # + # ``` + # def method + # @foo = :empty + # return + # end + # ``` + # + # If this param equals to `false`, the method above has to be written as: + # + # ``` + # def method + # @foo = :empty + # nil + # end + # ``` + # + # ### YAML config example + # + # ``` + # Style/RedundantReturn: + # Enabled: true + # AllowMutliReturn: true + # AllowEmptyReturn: true + # ``` + struct RedundantReturn < Base + properties do + description "Reports redundant return expressions" + allow_multi_return true + allow_empty_return true + end + + MSG = "Redundant `return` detected" + + def test(source, node : Crystal::Def) + AST::RedundantControlExpressionVisitor.new(self, source, node.body) + end + + def test(source, node : Crystal::Return, visitor : AST::RedundantControlExpressionVisitor) + return if allow_multi_return && node.exp.is_a?(Crystal::TupleLiteral) + return if allow_empty_return && (node.exp.nil? || node.exp.not_nil!.nop?) + + source.try &.add_issue self, node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr new file mode 100644 index 000000000000..2daa28f84e6c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr @@ -0,0 +1,90 @@ +module Ameba::Rule::Style + # A rule that enforces type names in camelcase manner. + # + # For example, these are considered valid: + # + # ``` + # class ParseError < Exception + # end + # + # module HTTP + # class RequestHandler + # end + # end + # + # alias NumericValue = Float32 | Float64 | Int32 | Int64 + # + # lib LibYAML + # end + # + # struct TagDirective + # end + # + # enum Time::DayOfWeek + # end + # ``` + # + # And these are invalid type names + # + # ``` + # class My_class + # end + # + # module HTT_p + # end + # + # alias Numeric_value = Int32 + # + # lib Lib_YAML + # end + # + # struct Tag_directive + # end + # + # enum Time_enum::Day_of_week + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Style/TypeNames: + # Enabled: true + # ``` + # + struct TypeNames < Base + properties do + description "Enforces type names in camelcase manner" + end + + MSG = "Type name should be camelcased: %s, but it was %s" + + private def check_node(source, node) + name = node.name.to_s + expected = name.camelcase + return if expected == name + + issue_for node, MSG % {expected, name} + end + + def test(source, node : Crystal::ClassDef) + check_node(source, node) + end + + def test(source, node : Crystal::Alias) + check_node(source, node) + end + + def test(source, node : Crystal::LibDef) + check_node(source, node) + end + + def test(source, node : Crystal::EnumDef) + check_node(source, node) + end + + def test(source, node : Crystal::ModuleDef) + check_node(source, node) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr new file mode 100644 index 000000000000..55909cdc747e --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr @@ -0,0 +1,58 @@ +module Ameba::Rule::Style + # A rule that disallows the use of an `else` block with the `unless`. + # + # For example, the rule considers these valid: + # + # ``` + # unless something + # :ok + # end + # + # if something + # :one + # else + # :two + # end + # ``` + # + # But it considers this one invalid as it is an `unless` with an `else`: + # + # ``` + # unless something + # :one + # else + # :two + # end + # ``` + # + # The solution is to swap the order of the blocks, and change the `unless` to + # an `if`, so the previous invalid example would become this: + # + # ``` + # if something + # :two + # else + # :one + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Style/UnlessElse: + # Enabled: true + # ``` + # + struct UnlessElse < Base + properties do + description "Disallows the use of an `else` block with the `unless`" + end + + MSG = "Favour if over unless with else" + + def test(source, node : Crystal::Unless) + return if node.else.nop? + issue_for node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr new file mode 100644 index 000000000000..108c54d0c71a --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr @@ -0,0 +1,51 @@ +module Ameba::Rule::Style + # A rule that enforces variable names to be in underscored case. + # + # For example, these variable names are considered valid: + # + # ``` + # var_name = 1 + # name = 2 + # _another_good_name = 3 + # ``` + # + # And these are invalid variable names: + # + # ``` + # myBadNamedVar = 1 + # wrong_Name = 2 + # ``` + # + # YAML configuration example: + # + # ``` + # Style/VariableNames: + # Enabled: true + # ``` + # + struct VariableNames < Base + properties do + description "Enforces variable names to be in underscored case" + end + + MSG = "Var name should be underscore-cased: %s, not %s" + + private def check_node(source, node) + return if (expected = node.name.underscore) == node.name + + issue_for node, MSG % {expected, node.name} + end + + def test(source, node : Crystal::Var) + check_node source, node + end + + def test(source, node : Crystal::InstanceVar) + check_node source, node + end + + def test(source, node : Crystal::ClassVar) + check_node source, node + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr new file mode 100644 index 000000000000..f57289e0ad6c --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr @@ -0,0 +1,41 @@ +module Ameba::Rule::Style + # A rule that disallows the use of `while true` instead of using the idiomatic `loop` + # + # For example, this is considered invalid: + # + # ``` + # while true + # do_something + # break if some_condition + # end + # ``` + # + # And should be replaced by the following: + # + # ``` + # loop do + # do_something + # break if some_condition + # end + # ``` + # + # YAML configuration example: + # + # ``` + # Style/WhileTrue: + # Enabled: true + # ``` + # + struct WhileTrue < Base + properties do + description "Disallows while statements with a true literal as condition" + end + + MSG = "While statement using true literal as condition" + + def test(source, node : Crystal::While) + return unless node.cond.true_literal? + issue_for node, MSG + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr new file mode 100644 index 000000000000..ece391877e18 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr @@ -0,0 +1,145 @@ +module Ameba + # Represents a runner for inspecting sources files. + # Holds a list of rules to do inspection based on, + # list of sources to run inspection on and a formatter + # to prepare a report. + # + # ``` + # config = Ameba::Config.load + # runner = Ameba::Runner.new config + # runner.run.success? # => true or false + # ``` + # + class Runner + # A list of rules to do inspection based on. + @rules : Array(Rule::Base) + + # A list of sources to run inspection on. + getter sources : Array(Source) + + # A level of severity to be reported. + @severity : Severity + + # A formatter to prepare report. + @formatter : Formatter::BaseFormatter + + # A syntax rule which always inspects a source first + @syntax_rule = Rule::Lint::Syntax.new + + # Checks for unneeded disable directives. Always inspects a source last + @unneeded_disable_directive_rule : Rule::Base? + + # Instantiates a runner using a `config`. + # + # ``` + # config = Ameba::Config.load + # config.files = files + # config.formatter = formatter + # + # Ameba::Runner.new config + # ``` + # + def initialize(config : Config) + @sources = config.sources + @formatter = config.formatter + @severity = config.severity + @rules = config.rules.select(&.enabled).reject!(&.special?) + + @unneeded_disable_directive_rule = + config.rules + .find &.name.==(Rule::Lint::UnneededDisableDirective.rule_name) + end + + # :nodoc: + protected def initialize(@rules, @sources, @formatter, @severity) + end + + # Performs the inspection. Iterates through all sources and test it using + # list of rules. If a specific rule fails on a specific source, it adds + # an issue to that source. + # + # This action also notifies formatter when inspection is started/finished, + # and when a specific source started/finished to be inspected. + # + # ``` + # runner = Ameba::Runner.new config + # runner.run # => returns runner again + # ``` + # + def run + @formatter.started @sources + channels = @sources.map { Channel(Exception?).new } + @sources.each_with_index do |source, idx| + channel = channels[idx] + spawn do + run_source(source) + rescue e + channel.send(e) + else + channel.send(nil) + end + end + + channels.each do |c| + e = c.receive + raise e unless e.nil? + end + + self + ensure + @formatter.finished @sources + end + + private def run_source(source) + @formatter.source_started source + + if @syntax_rule.catch(source).valid? + @rules.each do |rule| + next if rule.excluded?(source) + rule.test(source) + end + check_unneeded_directives(source) + end + + @formatter.source_finished source + end + + # Explains an issue at a specified *location*. + # + # Runner should perform inspection before doing the explain. + # This is necessary to be able to find the issue at a specified location. + # + # ``` + # runner = Ameba::Runner.new config + # runner.run + # runner.explain({file: file, line: l, column: c}) + # ``` + # + def explain(location, output = STDOUT) + Formatter::ExplainFormatter.new(output, location).finished @sources + end + + # Indicates whether the last inspection successful or not. + # It returns true if no issues matching severity in sources found, false otherwise. + # + # ``` + # runner = Ameba::Runner.new config + # runner.run + # runner.success? # => true or false + # ``` + # + def success? + @sources.all? do |source| + source.issues + .reject(&.disabled?) + .none? { |issue| issue.rule.severity <= @severity } + end + end + + private def check_unneeded_directives(source) + if (rule = @unneeded_disable_directive_rule) && rule.enabled + rule.test(source) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr new file mode 100644 index 000000000000..917cf4e98492 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr @@ -0,0 +1,49 @@ +module Ameba + enum Severity + Error + Warning + Convention + + # Returns a symbol uniquely indicating severity. + # + # ``` + # Severity::Warning.symbol # => 'W' + # ``` + def symbol + to_s[0] + end + + # Creates Severity by the name. + # + # ``` + # Severity.parse("convention") # => Severity::Convention + # Severity.parse("foo-bar") # => Exception: Incorrect severity name + # ``` + # + def self.parse(name : String) + super name + rescue ArgumentError + raise "Incorrect severity name #{name}. Try one of #{values}" + end + end + + # Converter for `YAML.mapping` which converts severity enum to and from YAML. + class SeverityYamlConverter + def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) + unless node.is_a?(YAML::Nodes::Scalar) + raise "Severity must be a scalar, not #{node.class}" + end + + case value = node.value + when String then Severity.parse(value) + when Nil then nil + else + raise "Incorrect severity: #{value}" + end + end + + def self.to_yaml(value : Severity, yaml : YAML::Nodes::Builder) + yaml.scalar value + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr new file mode 100644 index 000000000000..3fd8d751d921 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr @@ -0,0 +1,69 @@ +module Ameba + # An entity that represents a Crystal source file. + # Has path, lines of code and issues reported by rules. + class Source + include InlineComments + include Reportable + + # Path to the source file. + getter path : String + + # Crystal code (content of a source file). + getter code : String + + @lines : Array(String)? + @ast : Crystal::ASTNode? + @fullpath : String? + + # Creates a new source by `code` and `path`. + # + # For example: + # + # ``` + # path = "./src/source.cr" + # Ameba::Source.new File.read(path), path + # ``` + # + def initialize(@code : String, @path = "") + end + + # Returns lines of code splitted by new line character. + # Since `code` is immutable and can't be changed, this + # method caches lines in an instance variable, so calling + # it second time will not perform a split, but will return + # lines instantly. + # + # ``` + # source = Ameba::Source.new "a = 1\nb = 2", path + # source.lines # => ["a = 1", "b = 2"] + # ``` + # + def lines + @lines ||= @code.split("\n") + end + + # Returns AST nodes constructed by `Crystal::Parser`. + # + # ``` + # source = Ameba::Source.new code, path + # source.ast + # ``` + # + def ast + @ast ||= + Crystal::Parser.new(code) + .tap { |parser| parser.wants_doc = true } + .tap { |parser| parser.filename = @path } + .parse + end + + def fullpath + @fullpath ||= File.expand_path @path + end + + # Returns true if *filepath* matches the source's path, false if it does not. + def matches_path?(filepath) + path == filepath || path == File.expand_path(filepath) + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr new file mode 100644 index 000000000000..070e5b552c90 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr @@ -0,0 +1,111 @@ +require "compiler/crystal/syntax/*" + +module Ameba + # Represents Crystal syntax tokenizer based on `Crystal::Lexer`. + # + # ``` + # source = Ameba::Source.new code, path + # tokenizer = Ameba::Tokenizer.new(source) + # tokenizer.run do |token| + # puts token + # end + # ``` + # + class Tokenizer + # Instantiates Tokenizer using a `source`. + # + # ``` + # source = Ameba::Source.new code, path + # Ameba::Tokenizer.new(source) + # ``` + # + def initialize(source) + @lexer = Crystal::Lexer.new source.code + @lexer.count_whitespace = true + @lexer.comments_enabled = true + @lexer.wants_raw = true + @lexer.filename = source.path + end + + # Instantiates Tokenizer using a `lexer`. + # + # ``` + # lexer = Crystal::Lexer.new(code) + # Ameba::Tokenizer.new(lexer) + # ``` + # + def initialize(@lexer : Crystal::Lexer) + end + + # Runs the tokenizer and yields each token as a block argument. + # + # ``` + # Ameba::Tokenizer.new(source).run do |token| + # puts token + # end + # ``` + # + def run(&block : Crystal::Token -> _) + run_normal_state @lexer, &block + true + rescue e : Crystal::SyntaxException + # puts e + false + end + + private def run_normal_state(lexer, break_on_rcurly = false, + &block : Crystal::Token -> _) + loop do + token = @lexer.next_token + block.call token + + case token.type + when :DELIMITER_START + run_delimiter_state lexer, token, &block + when :STRING_ARRAY_START, :SYMBOL_ARRAY_START + run_array_state lexer, token, &block + when :EOF + break + when :"}" + break if break_on_rcurly + else + # go on + end + end + end + + private def run_delimiter_state(lexer, token, &block : Crystal::Token -> _) + loop do + token = @lexer.next_string_token(token.delimiter_state) + block.call token + + case token.type + when :DELIMITER_END + break + when :INTERPOLATION_START + run_normal_state lexer, break_on_rcurly: true, &block + when :EOF + break + else + # go on + end + end + end + + private def run_array_state(lexer, token, &block : Crystal::Token -> _) + loop do + lexer.next_string_array_token + block.call token + + case token.type + when :STRING_ARRAY_END + break + when :EOF + break + else + # go on + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/ameba/src/cli.cr b/samples/client/petstore/crystal/lib/ameba/src/cli.cr new file mode 100644 index 000000000000..4bb38cf9be98 --- /dev/null +++ b/samples/client/petstore/crystal/lib/ameba/src/cli.cr @@ -0,0 +1,3 @@ +require "./ameba/cli/cmd" + +Ameba::Cli.run diff --git a/samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml b/samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml new file mode 100644 index 000000000000..02d3e3da9875 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml @@ -0,0 +1,49 @@ +name: Crystal CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + check_format: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Check format + run: crystal tool format --check + check_ameba: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Check ameba + run: ./bin/ameba + test_latest: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Run tests + run: crystal spec + test_nightly: + runs-on: ubuntu-latest + container: + image: crystallang/crystal:nightly + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Run tests + run: crystal spec diff --git a/samples/client/petstore/crystal/lib/crest/.gitignore b/samples/client/petstore/crystal/lib/crest/.gitignore new file mode 100644 index 000000000000..fb1aac0ac59c --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/.gitignore @@ -0,0 +1,13 @@ +/doc/ +/lib/ +/bin/ +/.shards/ +/.idea/ +/tmp/ +/samples/ +/docs/ +/.vscode + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock diff --git a/samples/client/petstore/crystal/lib/crest/.travis.yml b/samples/client/petstore/crystal/lib/crest/.travis.yml new file mode 100644 index 000000000000..702cd6ea17ba --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/.travis.yml @@ -0,0 +1,24 @@ +dist: xenial +language: crystal +crystal: + - latest + - nightly + +script: + - crystal spec + - crystal tool format --check + - bin/ameba + - crystal docs + +jobs: + allow_failures: + - crystal: nightly + +deploy: + provider: pages + skip_cleanup: true + github_token: $GITHUB_TOKEN + project_name: crest + local_dir: docs + on: + branch: master diff --git a/samples/client/petstore/crystal/lib/crest/CHANGELOG.md b/samples/client/petstore/crystal/lib/crest/CHANGELOG.md new file mode 100644 index 000000000000..bcf20d99a4fc --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/CHANGELOG.md @@ -0,0 +1,237 @@ +# Changelog + +## [...] + +## [0.26.1][] (2020-07-07) + +* Fixed compatibility with Crystal nightly + +## [0.26.0][] (2020-06-18) + +* Crystal 0.35.0 required +* Use [http_proxy](https://github.com/mamantoha/http_proxy) shard instead of build-in implementation + +## [0.25.1][] (2020-06-02) + +* Bug fixes and other improvements + +## [0.25.0][] (2020-04-07) + +* Crystal 0.34.0 required +* Rewrite `Crest::Logger` class +* Fix redirects when "Location" header is downcased + +## [0.24.1][] (2020-03-29) + +* Fix `handle_errors` is ignored for redirect errors ([#132](https://github.com/mamantoha/crest/issues/132)) + +## [0.24.0][] (2020-03-13) + +* Add `Crest#ParamsEncoder` module to encode/decode URI querystring +* Replace `Crest::Utils#encode_query_string` `with Crest::ParamsEncoder#encode` +* Allow `Boolean` in params + +## [0.23.2][] (2020-01-03) + +* Fix an issue with wrong "Content-Type" header + +## [0.23.1][] (2019-12-14) + +* Add a more descriptive crest user agent + +## [0.23.0][] (2019-12-12) + +* Add methods `to_s` and `inspect` to `Crest::Response` +* Support Crystal 0.32.0 + +## [0.22.0][] (2019-09-17) + +* Support Crystal 0.31.0 +* Digest access authentication support ([#127](https://github.com/mamantoha/crest/pull/127)) +* Add proxy to `to_curl` method + +## [0.21.1][] (2019-08-13) + +* **(breaking-change)** Require Crystal 0.30.1 + +## [0.21.0][] (2019-08-02) + +* **(breaking-change)** Require Crystal 0.30.0 +* **(breaking-change)** Rename `Crest::Response#successful?` to `Crest::Response#success?` +* Add method `Crest::Response#status` as `HTTP::Status` + +## [0.20.0][] (2019-06-14) + +* Tested with Crystal 0.29.0 +* Improve testing process ([#120](https://github.com/mamantoha/crest/pull/120)) + +## [0.19.1][] (2019-05-09) + +* Delegate method `to_curl` to `Crest::Response` instance +* Fix an issue in `Resource` when base url ends with `/` + +## [0.19.0][] (2019-04-18) + +* Add method `head` ([#116](https://github.com/mamantoha/crest/pull/116)) +* Tested with Crystal 0.28.0 + +## [0.18.3][] (2019-02-06) + +* Tested with Crystal 0.27.2 + +## [0.18.2][] (2019-02-03) + +* Tested with Crystal 0.27.1 + +## [0.18.1][] (2019-01-16) + +* Fix extracting filename from Content-Disposition header + +## [0.18.0][] (2019-01-06) + +* **(breaking-change)** Streaming support. `Crest`, `Crest::Request` and `Crest::Resource` verb methods(`get`, `post`, etc.) yields the `Crest::Response` as stream to the block ([#110](https://github.com/mamantoha/crest/pull/110)) +* **(breaking-change)** Needs to specify `form`, `headers` and `params` arguments for `Crest::Resource` methods ([#112](https://github.com/mamantoha/crest/pull/112)) +* Add `Crest::Response#filename` method ([#111](https://github.com/mamantoha/crest/pull/111)) +* Add response helper methods (`successful?`, `redirection?`, etc) ([#107](https://github.com/mamantoha/crest/pull/107)) +* Extract redirection logic into `Crest::Redirector` class ([#109](https://github.com/mamantoha/crest/pull/109)) + +## [0.17.0][] (2018-11-17) + +* **(breaking-change)** `Crest` and `Crest::Request` verb methods(`get`, `post`, etc.) yields the `Crest::Response` to the block +* Refactor proxy client + +## [0.16.1][] (2018-11-05) + +* Update to Kemal 0.25.1 + +## [0.16.0][] (2018-11-03) + +* Tested with Crystal 0.27.0 + +## [0.15.0][] (2018-10-12) + +* SSL/TLS support ([#100](https://github.com/mamantoha/crest/pull/100)) +* Tested with Crystal 0.26.1 + +## [0.14.0][] (2018-08-14) + +* Tested with Crystal 0.26.0 + +## [0.13.0][] (2018-08-13) + +* Add `Crest::Request#to_curl` to convert request to cURL command ([#95](https://github.com/mamantoha/crest/pull/95)) +* Bug fixes and other improvements + +## [0.12.0][] (2018-07-17) + +* **(breaking-change)** Rename `Request#payload` to `Request#form` +* Use `application/x-www-form-urlencoded` for forms by default. And `multipart/form-data` when a form includes any `` elements. +* Fix serialize query to string representation as http url-encoded + +## [0.11.0][] (2018-07-14) + +* Add `Logger#filter` method to filter sensitive information from logs with a regex matcher +* Allow to do request with `suburl` through `Request#http_verb(suburl)` method +* Bug fixes and other improvements + +## [0.10.2][] (2018-06-15) + +* Tested with Crystal 0.25.0 + +## [0.10.1][] (2018-05-14) + +* Fix `Crest::Utils.flatten_params` method ([#85](https://github.com/mamantoha/crest/pull/85)) +* Reduce the false positiveness in code as much as possible ([#83](https://github.com/mamantoha/crest/pull/83), thanks @veelenga) + +## [0.10.0][] (2018-04-24) + +* Add HTTP verb methods (`get`, `post`, etc) to `Crest::Request` +* `Crest` and `Crest::Request` verb methods(`get`, `post`, etc.) can yields the `Crest::Request` to the block +* `Crest::Request` and `Crest::Resource` initializer can accept block +* Access instance of `HTTP::Client` via `Crest::Request#http_client` +* Access instance of `HTTP::Client` via `Crest::Resource#http_client` +* `Crest::Request` and `Crest::Resource` initializer can accept `HTTP::Client` as `http_client` +* Add method `options` to `HTTP::Resource` + +## [0.9.10][] (2018-04-08) + +* Add option `:handle_errors` to don't raise exceptions but return the `Response` +* Add custom exceptions for each status code + +## [0.9.9][] (2018-04-03) + +* Add method `OPTIONS` +* Fix `Crest::Response#headers` method to return response headers + +## [0.9.8][] (2018-03-18) + +* Tested with Crystal 0.24.2 +* Fix Basic Authentication + +## [0.9.7][] (2018-03-05) + +* Allow `Crest::Resource` to accept default `params` and `headers` +* Allow `Crest::Resource` to accept more parameters(proxy authentication credentials, logging setup) +* Refactor exceptions class +* Setup GitHub Pages branch to host docs + +## [0.9.6][] (2018-01-05) + +* Proxy on redirects +* Logger in redirects + +## [0.9.5][] (2017-12-30) + +* Bug fixes and performance improvements + +## [0.9.4][] (2017-12-25) + +* Tested with Crystal 0.24.1 + +## [0.9.3][] (2017-12-19) + +* Add logging + +## 0.9.2 (2017-11-01) + +* First release :tada: + +[...]: https://github.com/mamantoha/crest/compare/v0.26.1...HEAD +[0.26.1]: https://github.com/mamantoha/crest/compare/v0.26.0...v0.26.1 +[0.26.0]: https://github.com/mamantoha/crest/compare/v0.25.1...v0.26.0 +[0.25.1]: https://github.com/mamantoha/crest/compare/v0.25.0...v0.25.1 +[0.25.0]: https://github.com/mamantoha/crest/compare/v0.24.1...v0.25.0 +[0.24.1]: https://github.com/mamantoha/crest/compare/v0.24.0...v0.24.1 +[0.24.0]: https://github.com/mamantoha/crest/compare/v0.23.2...v0.24.0 +[0.23.2]: https://github.com/mamantoha/crest/compare/v0.23.1...v0.23.2 +[0.23.1]: https://github.com/mamantoha/crest/compare/v0.23.0...v0.23.1 +[0.23.0]: https://github.com/mamantoha/crest/compare/v0.22.0...v0.23.0 +[0.22.0]: https://github.com/mamantoha/crest/compare/v0.21.1...v0.22.0 +[0.21.1]: https://github.com/mamantoha/crest/compare/v0.21.0...v0.21.1 +[0.21.0]: https://github.com/mamantoha/crest/compare/v0.20.0...v0.21.0 +[0.20.0]: https://github.com/mamantoha/crest/compare/v0.19.1...v0.20.0 +[0.19.1]: https://github.com/mamantoha/crest/compare/v0.19.0...v0.19.1 +[0.19.0]: https://github.com/mamantoha/crest/compare/v0.18.3...v0.19.0 +[0.18.3]: https://github.com/mamantoha/crest/compare/v0.18.2...v0.18.3 +[0.18.2]: https://github.com/mamantoha/crest/compare/v0.18.1...v0.18.2 +[0.18.1]: https://github.com/mamantoha/crest/compare/v0.18.0...v0.18.1 +[0.18.0]: https://github.com/mamantoha/crest/compare/v0.17.0...v0.18.0 +[0.17.0]: https://github.com/mamantoha/crest/compare/v0.16.1...v0.17.0 +[0.16.1]: https://github.com/mamantoha/crest/compare/v0.16.0...v0.16.1 +[0.16.0]: https://github.com/mamantoha/crest/compare/v0.15.0...v0.16.0 +[0.15.0]: https://github.com/mamantoha/crest/compare/v0.14.0...v0.15.0 +[0.14.0]: https://github.com/mamantoha/crest/compare/v0.13.0...v0.14.0 +[0.13.0]: https://github.com/mamantoha/crest/compare/v0.12.0...v0.13.0 +[0.12.0]: https://github.com/mamantoha/crest/compare/v0.11.0...v0.12.0 +[0.11.0]: https://github.com/mamantoha/crest/compare/v0.10.2...v0.11.0 +[0.10.2]: https://github.com/mamantoha/crest/compare/v0.10.1...v0.10.2 +[0.10.1]: https://github.com/mamantoha/crest/compare/v0.10.0...v0.10.1 +[0.10.0]: https://github.com/mamantoha/crest/compare/v0.9.10...v0.10.0 +[0.9.10]: https://github.com/mamantoha/crest/compare/v0.9.9...v0.9.10 +[0.9.9]: https://github.com/mamantoha/crest/compare/v0.9.8...v0.9.9 +[0.9.8]: https://github.com/mamantoha/crest/compare/v0.9.7...v0.9.8 +[0.9.7]: https://github.com/mamantoha/crest/compare/v0.9.6...v0.9.7 +[0.9.6]: https://github.com/mamantoha/crest/compare/v0.9.5...v0.9.6 +[0.9.5]: https://github.com/mamantoha/crest/compare/v0.9.4...v0.9.5 +[0.9.4]: https://github.com/mamantoha/crest/compare/v0.9.3...v0.9.4 +[0.9.3]: https://github.com/mamantoha/crest/compare/v0.9.2...v0.9.3 diff --git a/samples/client/petstore/crystal/lib/crest/LICENSE b/samples/client/petstore/crystal/lib/crest/LICENSE new file mode 100644 index 000000000000..e4c15b8ceaa7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Anton Maminov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/crest/README.md b/samples/client/petstore/crystal/lib/crest/README.md new file mode 100644 index 000000000000..5319e8783019 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/README.md @@ -0,0 +1,614 @@ +# + +

    crest

    + +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/16e439ef2706472988306ef13da91a51)](https://app.codacy.com/app/mamantoha/crest?utm_source=github.com&utm_medium=referral&utm_content=mamantoha/crest&utm_campaign=Badge_Grade_Dashboard) +[![Build Status](https://travis-ci.org/mamantoha/crest.svg?branch=master)](https://travis-ci.org/mamantoha/crest) +[![GitHub release](https://img.shields.io/github/release/mamantoha/crest.svg)](https://github.com/mamantoha/crest/releases) +[![Commits Since Last Release](https://img.shields.io/github/commits-since/mamantoha/crest/latest.svg)](https://github.com/mamantoha/crest/pulse) +[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://mamantoha.github.io/crest/) +[![License](https://img.shields.io/github/license/mamantoha/crest.svg)](https://github.com/mamantoha/crest/blob/master/LICENSE) + +HTTP and REST client for Crystal, inspired by the Ruby's RestClient gem. + +Beloved features: + +* Redirects support. +* HTTP(S) proxy support. +* Elegant Key/Value headers, cookies, params, and payload. +* Multipart file uploads. +* Digest access authentication. +* Logging. + +Hopefully, someday I can remove this shard though. Ideally, Crystal's standard library would do all this already. + +## Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + crest: + github: mamantoha/crest +``` + +## Usage + +```crystal +require "crest" +``` + +Basic usage: + +```crystal +Crest.get( + "http://httpbin.org/get", + params: {:lang => "en"} +) +# curl -L "http://httpbin.org/get?lang=en" + +Crest.post( + "http://httpbin.org/post", + form: {:age => 27, :name => {:first => "Kurt", :last => "Cobain"}} +) +# curl -L --data "age=27&name[first]=Kurt&name[last]=Cobain" -X POST "http://httpbin.org/post" + +Crest.post( + "http://httpbin.org/post", + form: {"file" => File.open("avatar.png"), "name" => "John"} +) +# curl -X POST http://httpbin.org/post -F 'file=@avatar.png' -F 'name=John' -H 'Content-Type: multipart/form-data' +``` + +### Request + +`Crest::Request` accept next parameters: + +Mandatory parameters: + +* `:method` - HTTP method (`:get`. `:post`, `:put`, `:patch`, `:delete`, `:options`, `head`) +* `:url` - URL (e.g.: `http://httpbin.org/ip`) + +Optional parameters: + +* `:headers` - a hash containing the request headers +* `:cookies` - a hash containing the request cookies +* `:form` - a hash containing form params +* `:params` - a hash that represent query-string separated from the preceding part by a question mark (`?`) a sequence of attribute–value pairs separated by a delimiter (`&`) +* `auth` - access authentication method `basic` or `digest` (default to `basic`) +* `:user` and `:password` - for authentication +* `:tls` - client certificates, you can pass in a custom `OpenSSL::SSL::Context::Client` (default to `nil`) +* `:p_addr`, `:p_port`, `:p_user`, and `:p_pass` - specify a per-request proxy by passing these parameters +* `:max_redirects` - maximum number of redirections (default to 10) +* `:logging` - enable logging (default to `false`) +* `:logger` - set logger (default to `Crest::CommonLogger`) +* `:handle_errors` - error handling (default to `true`) +* `:http_client` - instance of `HTTP::Client` + +More detailed examples: + +```crystal +request = Crest::Request.new(:post, + "http://httpbin.org/post", + headers: {"Content-Type" => "application/json"}, + form: {:width => 640, "height" => "480"} +) +request.execute +# curl -L --data "width=640&height=480" --header "Content-Type: application/json" -X POST "http://httpbin.org/post" +``` + +```crystal +Crest::Request.execute(:get, + "http://httpbin.org/get", + params: {:width => 640, "height" => "480"}, + headers: {"Content-Type" => "application/json"}) +) +# curl -L --header "Content-Type: application/json" "http://httpbin.org/get?width=640&height=480" +``` + +```crystal +Crest::Request.get( + "http://httpbin.org/get", + p_addr: "127.0.0.1", + p_port: 3128, + p_user: "admin", + p_pass: "1234" +) +# curl -L --proxy http://127.0.0.1:3128 --proxy-user admin:1234 "http://httpbin.org/get" +``` + +A block can be passed to the `Crest::Request` initializer. + +This block will then be called with the `Crest::Request`. + +```crystal +request = Crest::Request.new(:get, "http://httpbin.org/headers") do |request| + request.headers.add("foo", "bar") +end + +request.execute +# curl -L --header "foo: bar" http://httpbin.org/headers +``` + +### Resource + +A `Crest::Resource` class can be instantiated for access to a RESTful resource, +including authentication, proxy and logging. + +Additionally, you can set default `params` and `headers` separately. +So can use `Crest::Resource` to share common `headers` and `params`. + +The final `headers` and `params` consist of: + +* default headers from initializer +* headers provided in call method (`get`, `post`, etc) + +This is especially useful if you wish to define your site in one place and +call it in multiple locations. + +```crystal +resource = Crest::Resource.new( + "http://httpbin.org", + params: {"key" => "value"}, + headers: {"Content-Type" => "application/json"} +) + +resource["/get"].get( + headers: {"Auth-Token" => "secret"} +) + +resource["/post"].post( + form: {:height => 100, "width" => "100"}, + params: {:secret => "secret"} +) +``` + +Use the `[]` syntax to allocate subresources: + +```crystal +site = Crest::Resource.new("http://httpbin.org") + +site["/post"].post(form: {:param1 => "value1", :param2 => "value2"}) +# curl -L --data "param1=value1¶m2=value2" -X POST http://httpbin.org/post +``` + +You can pass `suburl` through `Request#http_verb` methods: + +```crystal +site = Crest::Resource.new("http://httpbin.org") + +site.post("/post", form: {:param1 => "value1", :param2 => "value2"}) +# curl -L --data "param1=value1¶m2=value2" -X POST http://httpbin.org/post + +site.get("/get", params: {:status => "active"}) +# curl -L http://httpbin.org/get?status=active +``` + +A block can be passed to the `Crest::Resource` instance. + +This block will then be called with the `Crest::Resource`. + +```crystal +resource = Crest::Resource.new("http://httpbin.org") do |resource| + resource.headers.merge!({"foo" => "bar"}) +end + +resource["/headers"].get +``` + +With HTTP basic authentication: + +```crystal +resource = Crest::Resource.new( + "http://httpbin.org/basic-auth/user/passwd", + user: "user", + password: "passwd" +) +``` + +With Proxy authentication: + +```crystal +resource = Crest::Resource.new( + "http://httpbin.org/get", + p_host: "localhost", + p_port: 3128 +) +``` + +### Result handling + +The result of a `Crest::Request` and `Crest::Resource` is a `Crest::Response` object. + +Response objects have several useful methods: + +* `Response#body`: The response body as a `String` +* `Response#body_io`: The response body as a `IO` +* `Response#status`: The response status as a `HTTP::Status` +* `Response#status_code`: The HTTP response code +* `Response#headers`: A hash of HTTP response headers +* `Response#cookies`: A hash of HTTP cookies set by the server +* `Response#request`: The `Crest::Request` object used to make the request +* `Response#http_client_res`: The `HTTP::Client::Response` object +* `Response#history`: A list of each response received in a redirection chain + +### Exceptions + +* for result codes between `200` and `207`, a `Crest::Response` will be returned +* for result codes `301`, `302`, `303` or `307`, the redirection will be followed and the request transformed into a `GET` +* for other cases, a `Crest::RequestFailed` holding the Response will be raised +* call `.response` on the exception to get the server's response + +```crystal +Crest.get("http://httpbin.org/status/404") +# => HTTP status code 404: Not Found (Crest::NotFound) + +begin + Crest.get("http://httpbin.org/status/404") +rescue ex : Crest::NotFound + puts ex.response +end +``` + +To not raise exceptions but return the `Crest::Response` you can set `:handle_errors => false`. + +```crystal +response = Crest.get("http://httpbin.org/status/404", handle_errors: false) do |resp| + case resp + when .success? + puts resp.body_io.gets_to_end + when .client_error? + puts "Client error" + when .server_error? + puts "Server error" + end +end +# => Client error + +response.status_code # => 404 +``` + +But note that it may be more straightforward to use exceptions to handle different HTTP error response cases: + +```crystal +response = begin + Crest.get("http://httpbin.org/status/404") +rescue ex : Crest::NotFound + puts "Not found" + ex.response +rescue ex : Crest::InternalServerError + puts "Internal server error" + ex.response +end +# => Not found + +response.status_code # => 404 +``` + +### Streaming responses + +Normally, when you use `Crest`, `Crest::Request` or `Crest::Resource` methods to retrieve data, the entire response is buffered in memory and returned as the response to the call. + +However, if you are retrieving a large amount of data, for example an iso, or any other large file, you may want to stream the response directly to disk rather than loading it in memory. If you have a very large file, it may become impossible to load it into memory. + +If you want to stream the data from the response to a file as it comes, rather than entirely in memory, you can pass a block to which you pass a additional logic, which you can use to stream directly to a file as each chunk is received. + +With a block, an `Crest::Response` body is returned and the response's body is available as an `IO` by invoking `Crest::Response#body_io`. + +The following is an example: + +```crystal +Crest.get("https://github.com/crystal-lang/crystal/archive/0.27.0.zip") do |resp| + filename = resp.filename || "crystal.zip" + + File.open(filename, "w") do |file| + IO.copy(resp.body_io, file) + end +end +``` + +### Advanced Usage + +This section covers some of `crest` more advanced features. + +#### Parameters serializer + +Under the hood `crest` uses `Crest::ParamsEncoder` module to encode param. + +The encoder affect both how `crest` processes query strings and how it serializes POST bodies. + +`Crest::ParamsEncoder` provides 2 methods: + +* `#encode` - converts the given param into a URI querystring + + ```crystal + Crest::ParamsEncoder.encode({"a" => ["one", "two", "three"], "b" => true, "c" => "C", "d" => 1}) + # => 'a[]=one&a[]=two&a[]=three&b=true&c=C&d=1' + ``` + +* `#decode` - converts the given URI querystring into a hash + + ```crystal + Crest::ParamsEncoder.decode("a[]=one&a[]=two&a[]=three&b=true&c=C&d=1") + # => {"a" => ["one", "two", "three"], "b" => "true", "c" => "C", "d" => "1"} + ``` + +#### Multipart + +Yeah, that's right! This does multipart sends for you! + +```crystal +file = File.open("#{__DIR__}/example.png") +Crest.post("http://httpbin.org/post", form: {:image => file}) +``` + +```crystal +file = File.open("#{__DIR__}/example.png") +resource = Crest::Resource.new("https://httpbin.org") +response = resource["/post"].post(form: {:image => file}) +``` + +#### JSON payload + +`crest` does not speak JSON natively, so serialize your *form* to a string before passing it to `crest`. + +```crystal +Crest.post( + "http://httpbin.org/post", + headers: {"Content-Type" => "application/json"}, + form: {:foo => "bar"}.to_json +) +``` + +#### Headers + +Request headers can be set by passing a hash containing keys and values representing header names and values: + +```crystal +response = Crest.get( + "http://httpbin.org/bearer", + headers: {"Authorization" => "Bearer cT0febFoD5lxAlNAXHo6g"} +) +response.headers +# => {"Authorization" => ["Bearer cT0febFoD5lxAlNAXHo6g"]} +``` + +#### Cookies + +`Request` and `Response` objects know about HTTP cookies, and will automatically extract and set headers for them as needed: + +```crystal +response = Crest.get( + "http://httpbin.org/cookies/set", + params: {"k1" => "v1", "k2" => "v2"} +) +response.cookies +# => {"k1" => "v1", "k2" => "v2"} +``` + +```crystal +response = Crest.get( + "http://httpbin.org/cookies", + cookies: {"k1" => "v1"} +) +response.cookies +# => {"k1" => "v1"} +``` + +#### Basic access authentication + +For basic access authentication for an HTTP user agent you should to provide a `user` name and `password` when making a request. + +```crystal +Crest.get( + "http://httpbin.org/basic-auth/user/passwd", + user: "user", + password: "passwd" +) +# curl -L --user user:passwd http://httpbin.org/basic-auth/user/passwd +``` + +#### Digest access authentication + +For digest access authentication for an HTTP user agent you should to provide a `user` name and `password` when making a request. + +```crystal +Crest.get( + "https://httpbin.org/digest-auth/auth/user/passwd/MD5", + auth: "digest", + user: "user", + password: "passwd" +) +# curl -L --digest --user user:passwd https://httpbin.org/digest-auth/auth/user/passwd/MD5 +``` + +#### SSL/TLS support + +If `tls` is given it will be used: + +```crystal +Crest.get("https://expired.badssl.com", tls: OpenSSL::SSL::Context::Client.insecure) +``` + +#### Proxy + +If you need to use a proxy, you can configure individual requests with the proxy host and port arguments to any request method: + +```crystal +Crest.get( + "http://httpbin.org/ip", + p_addr: "localhost", + p_port: 3128 +) +``` + +To use authentication with your proxy, use next syntax: + +```crystal +Crest.get( + "http://httpbin.org/ip", + p_addr: "localhost", + p_port: 3128, + p_user: "user", + p_pass: "qwerty" +) +``` + +#### Logging + +> `Logger` class is completely taken from [halite](https://github.com/icyleaf/halite) shard. +> Thanks [icyleaf](https://github.com/icyleaf)! + +By default, the `Crest` does not enable logging. You can enable it per request by setting `logging: true`: + +```crystal +Crest.get("http://httpbin.org/get", logging: true) +``` + +##### Filter sensitive information from logs with a regex matcher + +```crystal +resource = Crest::Request.get("http://httpbin.org/get", params: {api_key => "secret"}, logging: true) do |request| + request.logger.filter(/(api_key=)(\w+)/, "\\1[REMOVED]") +end + +# => crest | 2018-07-04 14:49:49 | GET | http://httpbin.org/get?api_key=[REMOVED] +``` + +##### Customize logger + +You can create the custom logger by integration `Crest::Logger` abstract class. +Here has two methods must be implement: `Crest::Logger.request` and `Crest::Logger.response`. + +```crystal +class MyLogger < Crest::Logger + def request(request) + @logger.info { ">> | %s | %s" % [request.method, request.url] } + end + + def response(response) + @logger.info { "<< | %s | %s" % [response.status_code, response.url] } + end +end + +Crest.get("http://httpbin.org/get", logging: true, logger: MyLogger.new) +``` + +#### Redirection + +By default, `crest` will follow HTTP 30x redirection requests. + +To disable automatic redirection, set `:max_redirects => 0`. + +```crystal +Crest::Request.execute(method: :get, url: "http://httpbin.org/redirect/1", max_redirects: 0) +# => Crest::Found: 302 Found +``` + +#### Access HTTP::Client + +You can access `HTTP::Client` via the `http_client` instance method. + +This is usually used to set additional options (e.g. read timeout, authorization header etc.) + +```crystal +client = HTTP::Client.new("httpbin.org") +client.read_timeout = 1.second + +begin + Crest::Request.new(:get, + "http://httpbin.org/delay/10", + http_client: client + ) +rescue IO::TimeoutError + puts "Timeout!" +end +``` + +```crystal +client = HTTP::Client.new("httpbin.org") +client.read_timeout = 1.second + +begin + resource = Crest::Resource.new("http://httpbin.org", http_client: client) + resource.get("/delay/10") +rescue IO::TimeoutError + puts "Timeout!" +end +``` + +#### Convert Request object to cURL command + +Use `to_curl` method on instance of `Crest::Request` to convert request to cURL command. + +```crystal +request = Crest::Request.new( + :post, + "http://httpbin.org/post", + form: {"title" => "New Title", "author" => "admin"} +) +request.to_curl +# => curl -X POST http://httpbin.org/post -d 'title=New+Title&author=admin' -H 'Content-Type: application/x-www-form-urlencoded' +``` + +```crystal +request = Crest::Request.new( + :get, + "http://httpbin.org/basic-auth/user/passwd", + user: "user", + password: "passwd" +) +request.to_curl +# => curl -X GET http://httpbin.org/basic-auth/user/passwd --user user:passwd +``` + +Also you can directly use `Crest::Curlify` which accept instance of `Crest::Request` + +```crystal +request = Crest::Request.new(:get, "http://httpbin.org") +Crest::Curlify.new(request).to_curl +# => curl -X GET http://httpbin.org +``` + +## Development + +Install dependencies: + +```console +shards +``` + +To run test: + +```console +crystal spec +``` + +### Workbook + +```console +crystal play +open http://localhost:8080 +``` + +Then select the Workbook -> Requests from the menu. + +## Contributing + +1. Fork it () +2. Create your feature branch (git checkout -b my-new-feature) +3. Commit your changes (git commit -am 'Add some feature') +4. Push to the branch (git push origin my-new-feature) +5. Create a new Pull Request + +## Contributors + +* [mamantoha](https://github.com/mamantoha) Anton Maminov - creator, maintainer +* [icyleaf](https://github.com/icyleaf) Icyleaf Wang - logging support +* [psikoz](https://github.com/psikoz) Logo design + +## License + +Copyright: 2017-2020 Anton Maminov (anton.maminov@gmail.com) + +This library is distributed under the MIT license. Please see the LICENSE file. diff --git a/samples/client/petstore/crystal/lib/crest/lib b/samples/client/petstore/crystal/lib/crest/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/crest/logo/icon.png b/samples/client/petstore/crystal/lib/crest/logo/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0de8645e839eac562f19de1ac0b2fe3f49b1aa1f GIT binary patch literal 2985 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 Jte*?HvYr+5k literal 0 HcmV?d00001 diff --git a/samples/client/petstore/crystal/lib/crest/logo/icon.svg b/samples/client/petstore/crystal/lib/crest/logo/icon.svg new file mode 100644 index 000000000000..faf0bc7eed49 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/logo/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.png b/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..d16513385d23875cf65a9eba09a2bb2391cb7404 GIT binary patch literal 7246 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 literal 0 HcmV?d00001 diff --git a/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg b/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg new file mode 100644 index 000000000000..1227fb165643 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/crest/playground/requests.md b/samples/client/petstore/crystal/lib/crest/playground/requests.md new file mode 100644 index 000000000000..fb4a2e4f81c0 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/playground/requests.md @@ -0,0 +1,38 @@ +# Making HTTP requests using Crest + +This workbook demonstrates how to make requests using `Crest` + +## Make request + +```playground +require "./src/crest" + +form = {:fizz => "buz"} + +Crest.post( + "http://httpbin.org/post", + headers: {"Access-Token" => ["secret1", "secret2"]}, + form: form, + logging: true, +) +``` + +## Set cookies + +```playground +require "./src/crest" + +url = "http://httpbin.org/cookies" +res = Crest.get(url, cookies: {"v1" => "k1"}, logging: true) + +params = {"k1" => "v1", "k2" => "v2"} +url = "http://httpbin.org/cookies/set" +res = Crest.get(url, params: params, logging: true) + +url = "http://httpbin.org/headers" +res = Crest.get( + url, + headers: {"Authorization" => "Bearer cT0febFoD5lxAlNAXHo6g"}, + logging: true +) +``` diff --git a/samples/client/petstore/crystal/lib/crest/shard.yml b/samples/client/petstore/crystal/lib/crest/shard.yml new file mode 100644 index 000000000000..cb607c172dc0 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/shard.yml @@ -0,0 +1,27 @@ +name: crest +version: 0.26.1 + +authors: + - Anton Maminov + +description: | + HTTP and REST client for Crystal + +crystal: ">= 0.35.1" + +dependencies: + http-client-digest_auth: + github: mamantoha/http-client-digest_auth + version: ~> 0.4.0 + http_proxy: + github: mamantoha/http_proxy + version: ~> 0.7.1 + +development_dependencies: + kemal: + github: kemalcr/kemal + branch: master + ameba: + github: crystal-ameba/ameba + +license: MIT diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr new file mode 100644 index 000000000000..e04cd5bf6ac1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr @@ -0,0 +1,75 @@ +require "../spec_helper" + +describe Crest do + describe "Basic Auth" do + context Crest::Request do + it "should be unsuccessful without credentials" do + expect_raises Crest::RequestFailed, "HTTP status code 401" do + Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret", user: "root", password: "qwerty") + end + end + + it "should be successful with valid credentials" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret", user: "username", password: "password") + (response.status_code).should eq(200) + (response.body).should eq("Secret World!") + end + + it "should be successful in the initializer block" do + request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/secret") do |req| + req.user = "username" + req.password = "password" + end + + response = request.execute + + (response.status_code).should eq(200) + (response.body).should eq("Secret World!") + end + + it "should set basic auth in requets initiliazer block" do + request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/secret") do |req| + req.user = "username" + req.password = "password" + end + + response = request.execute + + (response.status_code).should eq(200) + (response.body).should eq("Secret World!") + end + + it "should be successful with valid credentials on redirect" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret_redirect", user: "username", password: "password") + (response.status_code).should eq(200) + (response.body).should eq("Secret World!") + (response.history.size).should eq(1) + (response.history.first.url).should eq("#{TEST_SERVER_URL}/secret_redirect") + (response.history.first.status_code).should eq(302) + end + + it "should be unsuccessful with invalid credentials" do + expect_raises Crest::RequestFailed, "HTTP status code 401" do + Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret", user: "root", password: "qwerty") + end + end + end + + context Crest do + it "should be successful with valid credentials" do + response = Crest.get("#{TEST_SERVER_URL}/secret", user: "username", password: "password") + (response.status_code).should eq(200) + (response.body).should eq("Secret World!") + end + end + + context Crest::Resource do + it "should be successful with valid credentials" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/secret", user: "username", password: "password") + response = resource.get + (response.status_code).should eq(200) + (response.body).should eq("Secret World!") + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr new file mode 100644 index 000000000000..e7a9310469b6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr @@ -0,0 +1,41 @@ +require "../spec_helper" + +describe Crest do + describe "Cookies" do + context Crest::Request do + it "should set cookies" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}", cookies: {"k1" => "v1", "k2" => "v2"}) + (response.status_code).should eq(200) + (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) + end + + it "should set cookies in the block" do + request = Crest::Request.new(:get, TEST_SERVER_URL, cookies: {"k1" => "v1"}) do |req| + req.cookies["k2"] = "v2" + end + + response = request.execute + + (response.status_code).should eq(200) + (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) + end + + it "should access cookies from the server" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/cookies/set", params: {"k1" => "v1", "k2" => "v2"}) + + (response.status_code).should eq(200) + (JSON.parse(response.body)).should eq({"cookies" => {"k1" => "v1", "k2" => "v2"}}) + (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) + end + + it "should set cookies from server with redirect" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/cookies/set_redirect", params: {"k1" => "v1", "k2" => "v2"}) + + (response.status_code).should eq(200) + (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) + (response.headers.[]("Cookie")).should eq("k1=v1; k2=v2") + (JSON.parse(response.body)).should eq({"cookies" => {"k1" => "v1", "k2" => "v2"}}) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr new file mode 100644 index 000000000000..941e74adaea6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr @@ -0,0 +1,73 @@ +require "../spec_helper" + +describe Crest do + it "do GET request" do + response = Crest.get("#{TEST_SERVER_URL}") + (response.body).should eq("Hello World!") + end + + it "do GET request with params" do + response = Crest.get("#{TEST_SERVER_URL}/resize", params: {:width => 100, :height => 100}) + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with different params" do + response = Crest.get("#{TEST_SERVER_URL}/resize", params: {"width" => 100, :height => "100"}) + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with params with nil" do + response = Crest.get("#{TEST_SERVER_URL}/add_key", params: {:json => nil, :key => 123}) + (response.body).should eq("JSON: key[123]") + end + + it "do POST request" do + response = Crest.post("#{TEST_SERVER_URL}/post/1/comments", form: {:title => "Title"}) + (response.body).should eq("Post with title `Title` created") + end + + it "upload file" do + file = File.open("#{__DIR__}/../support/fff.png") + response = Crest.post("#{TEST_SERVER_URL}/upload", form: {:file => file}) + (response.body).should match(/Upload OK/) + end + + it "do POST nested params" do + response = Crest.post("#{TEST_SERVER_URL}/post_nested", form: {:params1 => "one", :nested => {:params2 => "two"}}) + (response.body).should eq("params1=one&nested%5Bparams2%5D=two") + end + + it "do PUT request" do + response = Crest.put("#{TEST_SERVER_URL}/post/1/comments/1", form: {:title => "Put Update"}) + (response.body).should eq("Update Comment `1` for Post `1` with title `Put Update`") + end + + it "do PATCH request" do + response = Crest.patch("#{TEST_SERVER_URL}/post/1/comments/1", form: {:title => "Patch Update"}) + (response.body).should eq("Update Comment `1` for Post `1` with title `Patch Update`") + end + + it "do DELETE request" do + response = Crest.delete("#{TEST_SERVER_URL}/post/1/comments/1") + (response.body).should eq("Delete Comment `1` for Post `1`") + end + + it "do GET request with block without handle errors" do + body = "" + + Crest.get("#{TEST_SERVER_URL}/404", handle_errors: false) do |resp| + case resp + when .success? + body = resp.body_io.gets_to_end + when .client_error? + body = "Client error" + when .server_error? + body = "Server error" + else + raise "Unknown response with code #{resp.status_code}" + end + end + + body.should eq("Client error") + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr new file mode 100644 index 000000000000..f384e1256ac2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr @@ -0,0 +1,82 @@ +require "../spec_helper" + +describe Crest do + describe "Raise exception" do + it "404" do + expect_raises Crest::RequestFailed, "HTTP status code 404: Not Found" do + Crest.get("#{TEST_SERVER_URL}/404") + end + end + + it "request with rescue" do + response = begin + Crest.get("#{TEST_SERVER_URL}/500") + rescue ex : Crest::NotFound + ex.response + rescue ex : Crest::InternalServerError + ex.response + end + + response.body.should eq("500 error") + end + + it "404 with Resource" do + expect_raises Crest::RequestFailed, "HTTP status code 404: Not Found" do + resource = Crest::Resource.new(TEST_SERVER_URL) + resource["/404"].get + end + end + + it "404 raises NotFound" do + expect_raises Crest::NotFound, "HTTP status code 404: Not Found" do + resource = Crest::Resource.new(TEST_SERVER_URL) + resource["/404"].get + end + end + + it "500" do + expect_raises Crest::RequestFailed, "HTTP status code 500" do + Crest.get("#{TEST_SERVER_URL}/500") + end + end + + it "500 raises InternalServerError" do + expect_raises Crest::InternalServerError, "HTTP status code 500" do + Crest.get("#{TEST_SERVER_URL}/500") + end + end + + it "call .response on the exception to get the server's response" do + response = + begin + Crest.get("#{TEST_SERVER_URL}/404") + rescue ex : Crest::RequestFailed + ex.response + end + + (response.status_code).should eq(404) + end + + context "with handle_errors: false" do + it "do not raise error for Crest" do + response = Crest.get("#{TEST_SERVER_URL}/404", handle_errors: false) + + (response.status_code).should eq(404) + end + + it "do not raise error for Request" do + request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/404", handle_errors: false) + response = request.execute + + (response.status_code).should eq(404) + end + + it "do not raise error for Resource" do + resource = Crest::Resource.new(TEST_SERVER_URL, handle_errors: false) + response = resource["/404"].get + + (response.status_code).should eq(404) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr new file mode 100644 index 000000000000..6d50bdb25af9 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr @@ -0,0 +1,20 @@ +require "../spec_helper" + +describe Crest do + describe "Headers" do + context Crest::Request do + it "should set headers" do + response = Crest.get("#{TEST_SERVER_URL}/headers", headers: {"Access-Token" => ["secret1", "secret2"]}) + (response.status_code).should eq(200) + (response.headers.[]("Access-Token")).should eq(["secret1", "secret2"]) + (JSON.parse(response.body)["headers"]["Access-Token"]).should eq("secret1;secret2") + end + + it "should get request headers" do + response = Crest.get("#{TEST_SERVER_URL}/headers/set", params: {"foo" => "bar"}) + (response.status_code).should eq(200) + (response.headers.[]("foo")).should eq("bar") + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr new file mode 100644 index 000000000000..34b46b30179f --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr @@ -0,0 +1,18 @@ +require "../spec_helper" + +describe Crest::Logger do + describe "filters" do + it "filter logs by regex" do + IO.pipe do |r, w| + params = {:width => "100", :height => 100, :api_key => "secret"} + logger = Crest::CommonLogger.new(w) + logger.filter(/(api_key=)(\w+)/, "\\1[REMOVED]") + + Crest::Request.get("#{TEST_SERVER_URL}/resize", params: params, logger: logger, logging: true) + + r.gets.should match(/[REMOVED]/) + r.gets.should match(/[REMOVED]/) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr new file mode 100644 index 000000000000..3bed6fbaefb5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr @@ -0,0 +1,61 @@ +require "../spec_helper" + +describe Crest do + describe "With proxy server" do + it "should make request" do + with_proxy_server do |host, port, wants_close| + response = Crest.get("#{TEST_SERVER_URL}/", p_addr: host, p_port: port) + (response.status_code).should eq(200) + (response.body).should contain("Hello World!") + (response.request.p_addr).should eq("127.0.0.1") + (response.request.p_port).should eq(8080) + ensure + wants_close.send(nil) + end + end + + it "should redirect with proxy" do + with_proxy_server do |_, _, wants_close| + response = Crest.get("#{TEST_SERVER_URL}/redirect/1", p_addr: "127.0.0.1", p_port: 8080) + (response.status_code).should eq(200) + (response.body).should contain("Hello World!") + (response.url).should eq("#{TEST_SERVER_URL}/") + (response.history.size).should eq(1) + (response.history.first.url).should eq("#{TEST_SERVER_URL}/redirect/1") + (response.history.first.status_code).should eq(302) + (response.request.p_addr).should eq("127.0.0.1") + (response.request.p_port).should eq(8080) + ensure + wants_close.send(nil) + end + end + end + + describe Crest::Resource do + it "should make request" do + with_proxy_server do |host, port, wants_close| + resource = Crest::Resource.new("#{TEST_SERVER_URL}", p_addr: host, p_port: port) + response = resource.get + + (response.status_code).should eq(200) + (response.body).should contain("Hello World!") + (response.request.p_addr).should eq("127.0.0.1") + (response.request.p_port).should eq(8080) + ensure + wants_close.send(nil) + end + end + + it "should make suburl request" do + with_proxy_server do |host, port, wants_close| + resource = Crest::Resource.new("#{TEST_SERVER_URL}", p_addr: host, p_port: port) + response = resource["/post/1/comments"].get + + (response.status_code).should eq(200) + (response.body).should contain("Post 1: comments") + ensure + wants_close.send(nil) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr new file mode 100644 index 000000000000..eb24f97fa590 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr @@ -0,0 +1,77 @@ +require "../spec_helper" + +describe Crest do + describe "Redirects handling" do + it "should redirect" do + response = Crest.get("#{TEST_SERVER_URL}/redirect/1") + (response.status_code).should eq(200) + (response.url).should eq("#{TEST_SERVER_URL}/") + (response.body).should eq("Hello World!") + (response.history.size).should eq(1) + (response.history.first.url).should eq("#{TEST_SERVER_URL}/redirect/1") + (response.history.first.status_code).should eq(302) + end + + it "should redirect and save history" do + response = Crest.get("#{TEST_SERVER_URL}/redirect/2") + (response.status_code).should eq(200) + (response.history.size).should eq(2) + (response.history.first.status_code).should eq(302) + end + + it "should redirect with logger" do + response = Crest.get("#{TEST_SERVER_URL}/redirect/1", logging: true) + (response.request.logging).should eq(true) + (response.request.logger).should be_a(Crest::Logger) + end + + it "should raise error when too many redirects" do + expect_raises Crest::RequestFailed, "HTTP status code 302" do + Crest.get("#{TEST_SERVER_URL}/redirect/circle1") + end + end + + it "should raise last error" do + expect_raises Crest::RequestFailed, "HTTP status code 404" do + Crest.get("#{TEST_SERVER_URL}/redirect/not_found") + end + end + + it "should not raise last error if handle_errors is false" do + response = Crest.get("#{TEST_SERVER_URL}/redirect/not_found", handle_errors: false) + + (response.status_code).should eq(404) + (response.history.first.status_code).should eq(302) + end + + it "should not follow redirection when max_redirects is 0" do + expect_raises Crest::RequestFailed, "HTTP status code 302" do + Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0) + end + end + + it "should not follow redirection when max_redirects is 0 and raise Crest::Found" do + expect_raises Crest::Found, "HTTP status code 302" do + Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0) + end + end + + it "should not raise exception when handle_errors is false" do + response = Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0, handle_errors: false) + (response.status_code).should eq(302) + (response.body).should eq("Redirecting to /") + end + + it "should not raise exception in the block when handle_errors is false" do + body = status_code = nil + + Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0, handle_errors: false) do |response| + status_code = response.status_code + body = response.body_io.gets_to_end + end + + body.should eq("Redirecting to /") + status_code.should eq(302) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr new file mode 100644 index 000000000000..a43299e2970f --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr @@ -0,0 +1,175 @@ +require "../spec_helper" + +describe Crest::Request do + it "initialize and do request" do + request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/post/1/comments") + response = request.execute + + (response.body).should eq("Post 1: comments") + end + + it "do GET request" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/post/1/comments") + + (response.body).should eq("Post 1: comments") + end + + it "call get method" do + response = Crest::Request.get("#{TEST_SERVER_URL}/post/1/comments") + + (response.body).should eq("Post 1: comments") + end + + it "do GET request with params" do + response = Crest::Request.execute(:get, + "#{TEST_SERVER_URL}/resize", + params: {:width => 100, :height => 100} + ) + + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with different params" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/resize", params: {"width" => 100, :height => "100"}) + + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with params with nil" do + response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/add_key", params: {:json => nil, :key => 123}) + (response.body).should eq("JSON: key[123]") + end + + it "should accept block on initialize as request" do + url = "#{TEST_SERVER_URL}/headers" + + request = Crest::Request.new(:get, url) do |req| + req.headers.add("foo", "bar") + end + + response = request.execute + + (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") + end + + it "initializer can accept HTTP::Client as http_client" do + url = "#{TEST_SERVER_URL}/headers" + uri = URI.parse(TEST_SERVER_URL) + + client = HTTP::Client.new(uri) + client.before_request do |request| + request.headers.add("foo", "bar") + end + + response = Crest::Request.execute(:get, url, http_client: client) + (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") + end + + it "access http_client in instance of Crest::Request" do + url = "#{TEST_SERVER_URL}/headers" + + request = Crest::Request.new(:get, url) + + request.http_client.before_request do |req| + req.headers.add("foo", "bar") + end + + response = request.execute + + (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") + end + + it "change HTTP::Client in Crest::Request" do + url = "#{TEST_SERVER_URL}/delay/2" + uri = URI.parse(TEST_SERVER_URL) + + client = HTTP::Client.new(uri) + client.read_timeout = 5.minutes + + request = Crest::Request.new(:get, url, http_client: client) + + request.http_client.read_timeout = 1.second + + expect_raises IO::TimeoutError do + request.execute + end + end + + it "do POST request" do + response = Crest::Request.execute(:post, "#{TEST_SERVER_URL}/post/1/comments", form: {:title => "Title"}) + + (response.body).should eq("Post with title `Title` created") + end + + it "do POST request and encode form" do + response = Crest::Request.execute(:post, "#{TEST_SERVER_URL}/post/1/comments", form: {:title => "New @Title"}) + + (response.body).should eq("Post with title `New @Title` created") + end + + it "call post method" do + response = Crest::Request.post("#{TEST_SERVER_URL}/post/1/comments", form: {:title => "Title"}) + + (response.body).should eq("Post with title `Title` created") + end + + it "call post method with json content type" do + response = Crest::Request.post( + "#{TEST_SERVER_URL}/post/1/json", + headers: {"Content-Type" => "application/json"}, + form: {:title => "Title"}.to_json + ) + + (response.body).should eq("Post with title `Title` created") + end + + it "upload file" do + file = File.open("#{__DIR__}/../support/fff.png") + response = Crest::Request.post("#{TEST_SERVER_URL}/upload", form: {:file => file}) + (response.body).should match(/Upload OK/) + end + + it "do OPTIONS request" do + response = Crest::Request.execute(:options, "#{TEST_SERVER_URL}") + + (response.headers["Allow"]).should eq("OPTIONS, GET") + end + + it "call options method" do + response = Crest::Request.options("#{TEST_SERVER_URL}") + + (response.headers["Allow"]).should eq("OPTIONS, GET") + end + + it "should skip 'Content-Type' in headers for requests with form" do + response = Crest::Request.post( + "#{TEST_SERVER_URL}/post/1/comments", + headers: {"Content-Type" => "application/json"}, + form: {:title => "Title"} + ) + + (response.body).should eq("Post with title `Title` created") + end + + describe "User-Agent" do + it "should have default user agent" do + url = "#{TEST_SERVER_URL}/headers" + + request = Crest::Request.new(:get, url) + + response = request.execute + + (JSON.parse(response.body)["headers"]["User-Agent"]).should eq("Crest/#{Crest::VERSION} (Crystal/#{Crystal::VERSION})") + end + + it "change user agent" do + url = "#{TEST_SERVER_URL}/headers" + + request = Crest::Request.new(:get, url, headers: {"User-Agent" => "Crest"}) + + response = request.execute + + (JSON.parse(response.body)["headers"]["User-Agent"]).should eq("Crest") + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr new file mode 100644 index 000000000000..9ea92f4b8577 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr @@ -0,0 +1,210 @@ +require "../spec_helper" + +describe Crest::Response do + it "do GET request" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments") + response = resource.get + (response.body).should eq("Post 1: comments") + end + + it "do GET request when base url ends with /" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/") + response = resource.get("/post/1/comments") + (response.body).should eq("Post 1: comments") + end + + it "do GET request when path does not start with /" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}") + response = resource.get("post/1/comments") + (response.body).should eq("Post 1: comments") + end + + it "do GET request with []" do + site = Crest::Resource.new("#{TEST_SERVER_URL}") + response = site["/post/1/comments"].get + (response.body).should eq("Post 1: comments") + end + + it "do GET request with [] when base url ends with /" do + site = Crest::Resource.new("#{TEST_SERVER_URL}/") + response = site["/post/1/comments"].get + (response.body).should eq("Post 1: comments") + end + + it "do multiple GET requests with []" do + site = Crest::Resource.new("#{TEST_SERVER_URL}") + + response1 = site["/post/1/comments"].get + response2 = site["/post/2/comments"].get + + (response1.body).should eq("Post 1: comments") + (response2.body).should eq("Post 2: comments") + end + + it "do GET request with params" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/resize") + response = resource.get(params: {:width => "100", :height => 100}) + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with [] and params" do + resource = Crest::Resource.new(TEST_SERVER_URL) + response = resource["/resize"].get(params: {:width => 100, :height => 100}) + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with suburl and params" do + resource = Crest::Resource.new(TEST_SERVER_URL) + response = resource.get("resize", params: {:width => 100, :height => 100}) + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with [] and default params" do + resource = Crest::Resource.new( + TEST_SERVER_URL, + params: {:width => 100, :height => 100} + ) + response = resource["/resize"].get + (response.body).should eq("Width: 100, height: 100") + end + + it "do GET request with suburl and default params" do + resource = Crest::Resource.new( + TEST_SERVER_URL, + params: {:width => 100} + ) + response = resource.get("/resize", params: {:height => 100}) + (response.body).should eq("Width: 100, height: 100") + end + + it "should accept block" do + resource = Crest::Resource.new(TEST_SERVER_URL) do |res| + res.headers.merge!({"foo" => "bar"}) + end + + response = resource["/headers"].get + + (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") + end + + it "initializer can accept HTTP::Client as http_client" do + uri = URI.parse(TEST_SERVER_URL) + + client = HTTP::Client.new(uri) + client.before_request do |request| + request.headers.add("foo", "bar") + end + + resource = Crest::Resource.new(TEST_SERVER_URL, http_client: client) + response = resource["/headers"].get + + (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") + end + + it "access http_client in instance of Crest::Resource" do + resource = Crest::Resource.new(TEST_SERVER_URL) + resource.http_client.before_request do |req| + req.headers.add("foo", "bar") + end + + response = resource["/headers"].get + + (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") + end + + it "change HTTP::Client in Crest::Resource" do + uri = URI.parse(TEST_SERVER_URL) + + client = HTTP::Client.new(uri) + client.read_timeout = 5.minutes + + resource = Crest::Resource.new(TEST_SERVER_URL, http_client: client) + + resource.http_client.read_timeout = 1.second + + expect_raises IO::TimeoutError do + resource["/delay/2"].get + end + end + + it "do POST request" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments") + response = resource.post(form: {:title => "Title"}) + (response.body).should eq("Post with title `Title` created") + end + + it "do POST request with []" do + site = Crest::Resource.new(TEST_SERVER_URL) + response = site["/post/1/comments"].post(form: {:title => "Title"}) + (response.body).should eq("Post with title `Title` created") + end + + it "do POST request with suburl" do + site = Crest::Resource.new(TEST_SERVER_URL) + response = site.post("/post/1/comments", form: {:title => "Title"}) + (response.body).should eq("Post with title `Title` created") + end + + it "do POST request with [] and default params" do + site = Crest::Resource.new(TEST_SERVER_URL, params: {"key" => "key"}) + response = site["/resize"].post( + form: {:height => 100, "width" => "100"}, + params: {:secret => "secret"} + ) + (response.body).should eq("Width: 100, height: 100. Key: key, secret: secret") + end + + it "upload file" do + file = File.open("#{__DIR__}/../support/fff.png") + resource = Crest::Resource.new("#{TEST_SERVER_URL}/upload") + response = resource.post(form: {:file => file}) + + (response.body).should match(/Upload OK/) + end + + it "upload file with []" do + file = File.open("#{__DIR__}/../support/fff.png") + resource = Crest::Resource.new("#{TEST_SERVER_URL}") + response = resource["/upload"].post(form: {:file => file}) + + (response.body).should match(/Upload OK/) + end + + it "do PUT request" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments/1") + response = resource.put(form: {:title => "Put Update"}) + (response.body).should eq("Update Comment `1` for Post `1` with title `Put Update`") + end + + it "do PATCH request" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments/1") + response = resource.patch(form: {:title => "Patch Update"}) + (response.body).should eq("Update Comment `1` for Post `1` with title `Patch Update`") + end + + it "do DELETE request" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments/1") + response = resource.delete + (response.body).should eq("Delete Comment `1` for Post `1`") + end + + it "do GET request with logging" do + resource = Crest::Resource.new(TEST_SERVER_URL, logging: true) + response = resource["/post/1/comments"].get + (response.body).should eq("Post 1: comments") + end + + it "do OPTIONS request" do + resource = Crest::Resource.new(TEST_SERVER_URL) + response = resource.options + + (response.headers["Allow"]).should eq("OPTIONS, GET") + end + + it "#to_curl" do + resource = Crest::Resource.new("#{TEST_SERVER_URL}") + response = resource["/post/1/comments"].get + + (response.to_curl).should eq("curl -X GET #{TEST_SERVER_URL}/post/1/comments") + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr new file mode 100644 index 000000000000..19b9727905f2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr @@ -0,0 +1,57 @@ +require "../spec_helper" + +describe Crest::Response do + it "response instance should respond to helper methods" do + response = Crest.get("#{TEST_SERVER_URL}") + (response.body).should eq("Hello World!") + (response.invalid?).should be_false + (response.informational?).should be_false + (response.success?).should be_true + (response.redirection?).should be_false + (response.redirect?).should be_false + (response.client_error?).should be_false + (response.server_error?).should be_false + end + + it "response instance should have status and status_code" do + response = Crest.get("#{TEST_SERVER_URL}") + + (response.status).should be_a(HTTP::Status) + (response.status_code).should be_a(Int32) + end + + it "response instance should have filename if available" do + filename = "filename.jpg" + response = Crest.get("#{TEST_SERVER_URL}/download?filename=#{filename}") + + (response.filename).should eq(filename) + end + + it "response instance should have filename nil unless available" do + response = Crest.get("#{TEST_SERVER_URL}") + + (response.filename).should be_nil + end + + it "#to_curl" do + response = Crest.get("#{TEST_SERVER_URL}") + + (response.to_curl).should eq("curl -X GET #{TEST_SERVER_URL}") + end + + describe "to_s" do + it "does to_s" do + response = Crest.get("#{TEST_SERVER_URL}") + + response.to_s.should eq("Hello World!") + end + end + + describe "inspect" do + it "does inspect" do + response = Crest.get("#{TEST_SERVER_URL}") + + (response.inspect).should eq("") + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr new file mode 100644 index 000000000000..f6ed035a1c37 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr @@ -0,0 +1,120 @@ +require "../spec_helper" + +describe Crest do + describe "Streaming" do + it "should stream Response#execute" do + count = 5 + body = String::Builder.new + request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/stream/#{count}") + + request.execute do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + + it "should stream Request.execute" do + count = 5 + body = String::Builder.new + Crest::Request.get("#{TEST_SERVER_URL}/stream/#{count}") do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + + it "should stream Request.get" do + count = 5 + body = String::Builder.new + Crest::Request.execute(:get, "#{TEST_SERVER_URL}/stream/#{count}") do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + + it "should stream Crest#get" do + count = 5 + body = String::Builder.new + Crest.get("#{TEST_SERVER_URL}/stream/#{count}") do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + + it "should stream Resource#get with []" do + count = 5 + body = String::Builder.new + resource = Crest::Resource.new(TEST_SERVER_URL) + resource["/stream/#{count}"].get do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + + it "should stream Resource#get" do + count = 5 + body = String::Builder.new + resource = Crest::Resource.new(TEST_SERVER_URL) + resource.get("/stream/#{count}") do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + + it "should stream Crest#get with redirects" do + body = String::Builder.new + + Crest.get("#{TEST_SERVER_URL}/redirect/2") do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!") + end + + it "should stream Response#execute with redirects" do + count = 5 + body = String::Builder.new + request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/redirect_stream/#{count}") + + request.execute do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * 5) + end + + it "should stream Resource#get with redirects" do + count = 5 + body = String::Builder.new + resource = Crest::Resource.new(TEST_SERVER_URL) + resource["/redirect_stream/#{count}"].get do |resp| + while line = resp.body_io.gets + body << line + end + end + + body.to_s.should eq("Hello World!" * count) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr new file mode 100644 index 000000000000..5aed9dbd45a4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr @@ -0,0 +1,40 @@ +require "spec" +require "json" +require "http_proxy" +require "../src/crest" +require "./support/server" + +TEST_SERVER_HOST = "0.0.0.0" +TEST_SERVER_PORT = 4567 +TEST_SERVER_URL = "http://#{TEST_SERVER_HOST}:#{TEST_SERVER_PORT}" + +kemal_config = Kemal.config +kemal_config.env = "development" +kemal_config.logging = false + +spawn do + Kemal.run(port: TEST_SERVER_PORT) +end + +until Kemal.config.running + sleep 1.millisecond +end + +def with_proxy_server(host = "127.0.0.1", port = 8080) + wants_close = Channel(Nil).new + server = HTTP::Proxy::Server.new + + spawn do + server.bind_tcp(host, port) + server.listen + end + + spawn do + wants_close.receive + server.close + end + + Fiber.yield + + yield host, port, wants_close +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr b/samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr new file mode 100644 index 000000000000..68308b60e622 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr @@ -0,0 +1,10 @@ +class HTTP::Server + class Context + # TODO https://github.com/kemalcr/kemal/pull/561 + def redirect(url : String, status_code : Int32 = 302, *, body : String = "") + @response.headers.add "Location", url + @response.write(body.to_slice) + @response.status_code = status_code + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/fff.png b/samples/client/petstore/crystal/lib/crest/spec/support/fff.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e71629295905dafa0d7d4c769ad57d32d4ff23 GIT binary patch literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)gaYymzYu0Z<#|Nl#G&c6#}aTa() v7BevL9RXp+soH$fKtV1~7sn8enaK%2HWLHmm%@}~Ko*0itDnm{r-UW|njsiP literal 0 HcmV?d00001 diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr b/samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr new file mode 100644 index 000000000000..17e81ad3f820 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr @@ -0,0 +1,35 @@ +require "kemal" +require "base64" + +module KemalBasicAuth + class Handler < Kemal::Handler + BASIC = "Basic" + AUTH = "Authorization" + AUTH_MESSAGE = "Could not verify your access level for that URL.\nYou have to login with proper credentials" + HEADER_LOGIN_REQUIRED = "Basic realm=\"Login Required\"" + + def initialize(@username : String, @password : String) + end + + def call(env) + if env.request.headers[AUTH]? + if value = env.request.headers[AUTH] + if value.size > 0 && value.starts_with?(BASIC) + if authorize?(value) + return call_next(env) + end + end + end + end + + env.response.status_code = 401 + env.response.headers["WWW-Authenticate"] = HEADER_LOGIN_REQUIRED + env.response.print(AUTH_MESSAGE) + end + + private def authorize?(value) : String? + username, password = Base64.decode_string(value[BASIC.size + 1..-1]).split(":") + [@username, @password] == [username, password] ? "" : nil + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/server.cr b/samples/client/petstore/crystal/lib/crest/spec/support/server.cr new file mode 100644 index 000000000000..aa4f8c4af0a1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/support/server.cr @@ -0,0 +1,229 @@ +require "compress/deflate" +require "compress/gzip" +require "compress/zlib" +require "kemal" +require "./kemal_basic_auth" +require "./ext/kemal/ext/context" + +class BasicAuthHandler < KemalBasicAuth::Handler + only ["/secret", "/secret_redirect"] + + def call(env) + return call_next(env) unless only_match?(env) + + super + end +end + +add_handler BasicAuthHandler.new("username", "password") + +error 404 do + "404 error" +end + +error 500 do + "500 error" +end + +get "/" do + "Hello World!" +end + +options "/" do |env| + env.response.headers["Allow"] = "OPTIONS, GET" +end + +get "/secret" do + "Secret World!" +end + +get "/secret_redirect" do |env| + env.redirect("/secret") +end + +post "/upload" do |env| + file = env.params.files["file"].tempfile + + File.open(file.path, "w") do |f| + IO.copy(file, f) + end + + "Upload OK - #{file.path}" +end + +post "/post_nested" do |env| + params = env.params + params.body.to_s +end + +# Comments +# +# index +get "/post/:id/comments" do |env| + "Post #{env.params.url["id"]}: comments" +end + +# create +post "/post/:id/comments" do |env| + "Post with title `#{env.params.body["title"]}` created" +end + +# update +put "/post/:post_id/comments/:id" do |env| + "Update Comment `#{env.params.url["id"]}` for Post `#{env.params.url["post_id"]}` with title `#{env.params.body["title"]}`" +end + +# update +patch "/post/:post_id/comments/:id" do |env| + "Update Comment `#{env.params.url["id"]}` for Post `#{env.params.url["post_id"]}` with title `#{env.params.body["title"]}`" +end + +# delete +delete "/post/:post_id/comments/:id" do |env| + "Delete Comment `#{env.params.url["id"]}` for Post `#{env.params.url["post_id"]}`" +end +### + +# Matches /resize?width=200&height=200 +get "/resize" do |env| + width = env.params.query["width"] + height = env.params.query["height"] + + "Width: #{width}, height: #{height}" +end + +# Matches /resize?key=secter +post "/resize" do |env| + height = env.params.body.[]("height") + width = env.params.body.[]("width") + key = env.params.query["key"] + secret = env.params.query["secret"] + + "Width: #{width}, height: #{height}. Key: #{key}, secret: #{secret}" +end + +# Matches /add_key?json&key=123 +get "/add_key" do |env| + key = env.params.query["key"] + + "JSON: key[#{key}]" +end + +post "/post/:id/json" do |env| + title = env.params.json["title"].as(String) + "Post with title `#{title}` created" +end + +get "/404" do |env| + env.response.status_code = 404 +end + +get "/500" do |env| + env.response.status_code = 500 +end + +get "/stream/:count" do |env| + count = env.params.url["count"].to_i + + count.times do + env.response.puts("Hello World!") + end + + env +end + +# Redirects +# +get "/redirect/1" do |env| + env.redirect("/", body: "Redirecting to /") +end + +get "/redirect/2" do |env| + env.redirect("/redirect/1") +end + +get "/redirect/circle1" do |env| + env.redirect("/redirect/circle2") +end + +get "/redirect/circle2" do |env| + env.redirect("/redirect/circle1") +end + +get "/redirect/not_found" do |env| + env.redirect("/404") +end + +get "/redirect_stream/:count" do |env| + count = env.params.url["count"].to_i + + env.redirect("/stream/#{count}") +end + +# Return request headers +get "/headers" do |env| + result = {} of String => String + env.request.headers.each do |key, value| + result[key] = value.join(";") + end + + {"headers" => result}.to_json +end + +# Set response headers +get "/headers/set" do |env| + env.params.query.each do |param| + env.response.headers[param[0]] = param[1] + end + + "" +end + +# Returns cookies data +get "/cookies" do |env| + result = {} of String => String + env.request.cookies.to_h.each do |_, cookie| + result[cookie.name] = cookie.value + end + + {"cookies" => result}.to_json +end + +# /cookies/set?name=value Sets one or more simple cookies. +get "/cookies/set" do |env| + env.params.query.each do |param| + env.response.cookies << HTTP::Cookie.new(name: param[0], value: param[1]) + end + + result = {} of String => String + env.response.cookies.to_h.each do |_, cookie| + result[cookie.name] = cookie.value + end + + {"cookies" => result}.to_json +end + +# /cookies/set_redirect?name=value Sets one or more simple cookies and redirect. +get "/cookies/set_redirect" do |env| + env.params.query.each do |param| + env.response.cookies << HTTP::Cookie.new(name: param[0], value: param[1]) + end + + env.redirect("/cookies") +end + +# Delays responding for `:seconds` seconds. +get "/delay/:seconds" do |env| + seconds = env.params.url["seconds"].to_i + sleep seconds + + "Delay #{seconds} seconds" +end + +# Matches /download?filename=foo.bar +get "/download" do |env| + filename = env.params.query["filename"]? || "foo.bar" + file = File.open("#{__DIR__}/fff.png") + + send_file env, file.path, mime_type: "Application/octet-stream", filename: filename +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr b/samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr new file mode 100644 index 000000000000..696e520f4827 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr @@ -0,0 +1,3 @@ +require "./server" + +Kemal.run diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr new file mode 100644 index 000000000000..b895485fc3fe --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr @@ -0,0 +1,7 @@ +require "../spec_helper" + +describe Crest::VERSION do + it "should have version" do + (Crest::VERSION).should be_a(String) + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr new file mode 100644 index 000000000000..80c51b649383 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr @@ -0,0 +1,95 @@ +require "../spec_helper" + +def curlify(request : Crest::Request) + Crest::Curlify.new(request).to_curl +end + +describe Crest::Curlify do + it "converts simple GET request" do + request = Crest::Request.new(:get, "http://httpbin.org/get") + + result = "curl -X GET http://httpbin.org/get" + (request.to_curl).should eq(result) + end + + it "converts GET request with params" do + request = Crest::Request.new(:get, "http://httpbin.org/get", params: {"foo" => "bar"}) + + result = "curl -X GET http://httpbin.org/get?foo=bar" + curlify(request).should eq(result) + end + + it "converts a request with basic auth as parameters" do + request = Crest::Request.new(:get, "http://httpbin.org/basic-auth/user/passwd", user: "user", password: "passwd") + + result = "curl -X GET http://httpbin.org/basic-auth/user/passwd --user user:passwd" + (request.to_curl).should eq(result) + end + + it "converts a request with digest auth as parameters" do + request = Crest::Request.new(:get, "http://httpbin.org/digest-auth/auth/user/passwd/MD5", auth: "digest", user: "user", password: "passwd") + + result = "curl -X GET http://httpbin.org/digest-auth/auth/user/passwd/MD5 --digest --user user:passwd" + (request.to_curl).should eq(result) + end + + it "converts a request to invalid domain with digest auth" do + request = Crest::Request.new(:get, "https://domain.invalid", auth: "digest", user: "user", password: "passwd") + + result = "curl -X GET https://domain.invalid --digest --user user:passwd" + (request.to_curl).should eq(result) + end + + it "converts a request with basic auth in headers" do + request = Crest::Request.new(:get, "http://httpbin.org/basic-auth/user/passwd", headers: {"Authorization" => "Basic dXNlcjpwYXNzd2Q="}) + + result = "curl -X GET http://httpbin.org/basic-auth/user/passwd -H 'Authorization: Basic dXNlcjpwYXNzd2Q='" + (request.to_curl).should eq(result) + end + + it "converts a request with proxy" do + request = Crest::Request.new(:get, "http://httpbin.org", p_addr: "127.0.0.1", p_port: 8080) + + result = "curl -X GET http://httpbin.org --proxy 127.0.0.1:8080" + (request.to_curl).should eq(result) + end + + it "converts a request with proxy with authentication" do + request = Crest::Request.new(:get, "http://httpbin.org", p_addr: "127.0.0.1", p_port: 8080, p_user: "user", p_pass: "pass") + + result = "curl -X GET http://httpbin.org --proxy 127.0.0.1:8080 --proxy-user user:pass" + (request.to_curl).should eq(result) + end + + it "converts POST request" do + request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"title" => "New Title", "author" => "admin"}) + + result = "curl -X POST http://httpbin.org/post -d 'title=New+Title&author=admin' -H 'Content-Type: application/x-www-form-urlencoded'" + curlify(request).should eq(result) + end + + it "converts POST request with multipart" do + current_dir = __DIR__ + file = File.open("#{current_dir}/../support/fff.png") + + request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"title" => "New Title", "file" => file}) + + result = "curl -X POST http://httpbin.org/post -F 'title=New Title' -F 'file=@#{"#{current_dir}/../support/fff.png"}' -H 'Content-Type: multipart/form-data'" + curlify(request).should eq(result) + end + + it "converts POST request with headers" do + request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"param1" => "value1"}, headers: {"user-agent" => "crest"}) + + result = "curl -X POST http://httpbin.org/post -d 'param1=value1' -H 'user-agent: crest' -H 'Content-Type: application/x-www-form-urlencoded'" + curlify(request).should eq(result) + end + + it "converts POST request with json" do + request = Crest::Request.new(:post, "http://httpbin.org/post", form: {:foo => "bar"}.to_json, headers: {"Content-Type" => "application/json"}) + + result = "curl -X POST http://httpbin.org/post -d '{\"foo\":\"bar\"}' -H 'Content-Type: application/json'" + + curlify(request).should eq(result) + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr new file mode 100644 index 000000000000..3129e0092a30 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr @@ -0,0 +1,31 @@ +require "../spec_helper" + +describe Crest::DataForm do + describe "#generate" do + it "generate form" do + input = {:file => {"one" => "one", "two" => "two"}} + parsed_input = [{"file[one]", "one"}, {"file[two]", "two"}] + content_type = "multipart/form-data" + + form = Crest::DataForm.generate(input) + + form.content_type.should contain(content_type) + form.params.should eq(input) + form.parsed_params.should eq(parsed_input) + end + + it "generate form with multipart" do + file = File.open("#{__DIR__}/../support/fff.png") + input = {:file => file, "param1" => "value1"} + + parsed_input = [{"file", file}, {"param1", "value1"}] + content_type = "multipart/form-data" + + form = Crest::DataForm.generate(input) + + form.content_type.should contain(content_type) + form.params.should eq(input) + form.parsed_params.should eq(parsed_input) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr new file mode 100644 index 000000000000..55d2a7cc0346 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr @@ -0,0 +1,11 @@ +require "../spec_helper" + +describe Crest::RequestFailed do + it "should return descendant class for existing status code" do + (Crest::RequestFailed.subclass_by_status_code(404)).should eq(Crest::NotFound) + end + + it "should return self for nonexistent status code" do + (Crest::RequestFailed.subclass_by_status_code(777)).should eq(Crest::RequestFailed) + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr new file mode 100644 index 000000000000..0b5435125b85 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr @@ -0,0 +1,138 @@ +require "../spec_helper" + +describe Crest::ParamsEncoder do + describe "#encode" do + it "encodes hash" do + input = {:foo => "123", :bar => "456"} + output = "foo=123&bar=456" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + + it "encodes hash as http url-encoded" do + input = {:email => "user@example.com", :title => "Hello world!"} + output = "email=user%40example.com&title=Hello+world%21" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + + it "encodes hash with nil" do + input = {:foo => nil, :bar => "2"} + output = "foo=&bar=2" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + + it "encodes hash with boolean" do + input = {:foo => true, :bar => "2"} + output = "foo=true&bar=2" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + + it "encodes hash with symbol" do + input = {:foo => :bar} + output = "foo=bar" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + + it "encodes hash with numeric values" do + input = {:foo => 1, :bar => 2} + output = "foo=1&bar=2" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + + it "encodes complex objects" do + input = {"a" => ["one", "two", "three"], "b" => true, "c" => "C", "d" => 1} + output = "a%5B%5D=one&a%5B%5D=two&a%5B%5D=three&b=true&c=C&d=1" + + Crest::ParamsEncoder.encode(input).should eq(output) + end + end + + describe "#decode" do + it "decodes simple params" do + query = "foo=1&bar=2" + params = {"foo" => "1", "bar" => "2"} + + Crest::ParamsEncoder.decode(query).should eq(params) + end + + it "decodes params with nil" do + query = "foo=&bar=2" + params = {"foo" => nil, "bar" => "2"} + + Crest::ParamsEncoder.decode(query).should eq(params) + end + + it "decodes array " do + query = "a[]=one&a[]=two&a[]=three" + params = {"a" => ["one", "two", "three"]} + + Crest::ParamsEncoder.decode(query).should eq(params) + end + + it "decodes hashes" do + query = "user[login]=admin" + params = {"user" => {"login" => "admin"}} + + Crest::ParamsEncoder.decode(query).should eq(params) + end + + it "decodes escaped string" do + query = "user%5Blogin%5D=admin" + params = {"user" => {"login" => "admin"}} + + Crest::ParamsEncoder.decode(query).should eq(params) + end + end + + describe "#flatten_params" do + it "transform nested param" do + input = {:key1 => {:key2 => "123"}} + output = [{"key1[key2]", "123"}] + + Crest::ParamsEncoder.flatten_params(input).should eq(output) + end + + it "transform deeply nested param" do + input = {:key1 => {:key2 => {:key3 => "123"}}} + output = [{"key1[key2][key3]", "123"}] + + Crest::ParamsEncoder.flatten_params(input).should eq(output) + end + + it "transform deeply nested param with file" do + file = File.open("#{__DIR__}/../support/fff.png") + input = {:key1 => {:key2 => {:key3 => file}}} + output = [{"key1[key2][key3]", file}] + + Crest::ParamsEncoder.flatten_params(input).should eq(output) + end + + it "transform nested param with array" do + input = {:key1 => {:arr => ["1", "2", "3"]}, :key2 => "123"} + output = [{"key1[arr][]", "1"}, {"key1[arr][]", "2"}, {"key1[arr][]", "3"}, {"key2", "123"}] + + Crest::ParamsEncoder.flatten_params(input).should eq(output) + end + + it "parse nested params with files" do + file = File.open("#{__DIR__}/../support/fff.png") + + input = {:key1 => {:key2 => file}} + output = [{"key1[key2]", file}] + + Crest::ParamsEncoder.flatten_params(input).should eq(output) + end + + it "parse simple params with nil value" do + input = {:key1 => nil} + output = [{"key1", nil}] + + Crest::ParamsEncoder.flatten_params(input).should eq(output) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr new file mode 100644 index 000000000000..71d4cfae368c --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr @@ -0,0 +1,141 @@ +require "../spec_helper" + +describe Crest::Request do + describe "#initialize" do + it "new HTTP request" do + request = Crest::Request.new(:get, "http://localhost/get") + (request.url).should eq("http://localhost/get") + (request.max_redirects).should eq(10) + (request.host).should eq("localhost") + (request.port).should eq(80) + (request.tls?).should eq(nil) + (request.user).should be_nil + (request.password).should be_nil + (request.proxy).should be_nil + (request.logging).should be_false + end + + it "new HTTPS request" do + request = Crest::Request.new(:get, "https://localhost/get") + (request.url).should eq("https://localhost/get") + (request.host).should eq("localhost") + (request.port).should eq(443) + (request.tls?).should be_a(OpenSSL::SSL::Context::Client) + end + + it "initialize the GET request" do + request = Crest::Request.new(:get, "http://localhost", headers: {"Content-Type" => "application/json"}) + (request.method).should eq("GET") + (request.url).should eq("http://localhost") + (request.headers).should eq(HTTP::Headers{"Content-Type" => "application/json"}) + (request.form_data).should eq(nil) + end + + it "initialize the GET request with params" do + request = Crest::Request.new(:get, "http://localhost", params: {:foo => "hello world", :bar => 456}) + (request.method).should eq("GET") + (request.url).should eq("http://localhost?foo=hello+world&bar=456") + (request.form_data).should eq(nil) + end + + it "initialize the GET request with params in url" do + request = Crest::Request.new(:get, "http://localhost?json", params: {:key => 123}) + (request.method).should eq("GET") + (request.url).should eq("http://localhost?json&key=123") + (request.form_data).should eq(nil) + end + + it "initialize the GET request with nil value in params" do + request = Crest::Request.new(:get, "http://localhost", params: {:json => nil, :key => 123}) + (request.method).should eq("GET") + (request.url).should eq("http://localhost?json=&key=123") + (request.form_data).should eq(nil) + end + + it "initialize the GET request with cookies" do + request = Crest::Request.new(:get, "http://localhost", cookies: {:foo => "123", :bar => 456}) + (request.headers).should eq(HTTP::Headers{"Cookie" => "foo=123; bar=456"}) + end + + it "initialize the POST request with form" do + request = Crest::Request.new(:post, "http://localhost", form: {:foo => "bar"}) + (request.method).should eq("POST") + (request.url).should eq("http://localhost") + (request.headers["Content-Type"]).should eq("application/x-www-form-urlencoded") + (request.form_data.to_s).should eq("foo=bar") + end + + it "initialize the POST request with form as a string" do + request = Crest::Request.new(:post, "http://localhost", headers: {"Content-Type" => "application/json"}, form: {:foo => "bar"}.to_json) + (request.method).should eq("POST") + (request.url).should eq("http://localhost") + (request.headers["Content-Type"]).should eq("application/json") + (request.form_data.to_s).should eq("{\"foo\":\"bar\"}") + end + + it "initialize the POST and encode string" do + request = Crest::Request.new(:post, "http://localhost", form: {:title => "New @Title"}) + (request.method).should eq("POST") + (request.url).should eq("http://localhost") + (request.form_data).should eq("title=New+%40Title") + end + + it "initialize the POST request with multipart" do + file = File.open("#{__DIR__}/../support/fff.png") + request = Crest::Request.new(:post, "http://localhost", form: {:file => file}) + (request.method).should eq("POST") + (request.url).should eq("http://localhost") + (request.headers["Content-Type"]).should contain("multipart/form-data; boundary=") + (request.form_data.to_s).should contain("form-data; name=\"file\"; filename=") + end + + it "POST request with nested hashes" do + request = Crest::Request.new(:post, "http://localhost", form: {:params1 => "one", :nested => {:params2 => "two"}}) + (request.headers["Content-Type"]).should eq("application/x-www-form-urlencoded") + (request.form_data.to_s).should eq("params1=one&nested%5Bparams2%5D=two") + end + + it "initialize the PUT request with form" do + request = Crest::Request.new(:put, "http://localhost", form: {:foo => "bar"}) + (request.method).should eq("PUT") + (request.url).should eq("http://localhost") + (request.headers["Content-Type"]).should eq("application/x-www-form-urlencoded") + (request.form_data.to_s).should eq("foo=bar") + end + + it "initialize the OPTIONS request" do + request = Crest::Request.new(:options, "http://localhost") + (request.method).should eq("OPTIONS") + (request.url).should eq("http://localhost") + end + + it "initialize Request with :max_redirects" do + request = Crest::Request.new(:get, "http://localhost", max_redirects: 3) + (request.max_redirects).should eq(3) + end + + it "initialize Request with basic auth params" do + request = Crest::Request.new(:get, "http://localhost", user: "user", password: "password") + (request.user).should eq("user") + (request.password).should eq("password") + end + + it "initialize Request with proxy params" do + request = Crest::Request.new(:get, "http://localhost", p_addr: "localhost", p_port: 3128) + (request.proxy).should be_a(HTTP::Proxy::Client) + end + + it "initialize Request with :logging and logger" do + request = Crest::Request.new(:get, "http://localhost", logging: true) + (request.logging).should eq(true) + (request.logger).should be_a(Crest::Logger) + end + end + + describe "#to_curl" do + it "converts request to cURL command" do + request = Crest::Request.new(:get, "http://localhost") + (request.to_curl).should eq("curl -X GET http://localhost") + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr new file mode 100644 index 000000000000..3adff233f23e --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr @@ -0,0 +1,48 @@ +require "../spec_helper" + +describe Crest::Resource do + describe "#initialize" do + it "initialize new resource" do + resource = Crest::Resource.new("http://localhost", headers: {"X-Something" => "1"}) + + resource.url.should eq("http://localhost") + resource.headers.should eq({"X-Something" => "1"}) + end + + it "initialize new resource with proxy params" do + resource = Crest::Resource.new("http://localhost", p_addr: "localhost", p_port: 3128) + + resource.p_addr.should eq("localhost") + resource.p_port.should eq(3128) + end + + it "initialize new resource with logger" do + resource = Crest::Resource.new("http://localhost", logging: true) + + (resource.logging).should eq(true) + (resource.logger).should be_a(Crest::Logger) + end + + it "initialize new resource without headers" do + resource = Crest::Resource.new("http://localhost") + + resource.url.should eq("http://localhost") + resource.headers.should eq({} of String => String) + end + + it "initialize new resource with []" do + site = Crest::Resource.new("http://localhost", headers: {"X-Something" => "1"}) + resource = site["/resource"] + + resource.url.should eq("http://localhost/resource") + resource.headers.should eq({"X-Something" => "1"}) + end + + it "initialize new resource with params" do + resource = Crest::Resource.new("http://localhost", params: {"foo" => "123", "bar" => "456"}) + + resource.url.should eq("http://localhost") + resource.params.should eq({"foo" => "123", "bar" => "456"}) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr new file mode 100644 index 000000000000..a399824ca1e6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr @@ -0,0 +1,29 @@ +require "../spec_helper" + +describe Crest::UrlencodedForm do + describe "#generate" do + it "generate nested form" do + input = {"year" => "2017 - today", "param2" => 2} + parsed_input = "year=2017+-+today¶m2=2" + content_type = "application/x-www-form-urlencoded" + + form = Crest::UrlencodedForm.generate(input) + + form.content_type.should contain(content_type) + form.params.should eq(input) + form.form_data.should eq(parsed_input) + end + + it "generate nested form" do + input = {:file => {"one" => "one", "two" => "two"}} + parsed_input = "file%5Bone%5D=one&file%5Btwo%5D=two" + content_type = "application/x-www-form-urlencoded" + + form = Crest::UrlencodedForm.generate(input) + + form.content_type.should contain(content_type) + form.params.should eq(input) + form.form_data.should eq(parsed_input) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest.cr b/samples/client/petstore/crystal/lib/crest/src/crest.cr new file mode 100644 index 000000000000..833e0a469478 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest.cr @@ -0,0 +1,75 @@ +require "http" +require "uri" +require "base64" +require "http-client-digest_auth" +require "http_proxy" + +# This module's static methods are the entry point for using the Crest client. +# +# Suported HTTP methods: `get`, `put`, `post`, `patch` `delete`, `options`, `head` +# +# Examples: +# +# ```crystal +# Crest.get( +# "http://httpbin.org/get", +# headers: {"Content-Type" => "image/jpg"}, +# params: {"lang" => "en"} +# ) +# +# Crest.post( +# "http://httpbin.org/post", +# headers: {"Access-Token" => ["secret1", "secret2"]}, +# form: {:fizz => "buz"}, +# logging: true, +# ) +# +# Crest.get("http://httpbin.org/stream/5") do |response| +# while line = response.body_io.gets +# puts line +# end +# end +# ``` +module Crest + VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} + + alias TextValue = String | Symbol | Int32 | Bool | Nil + + alias Params = Hash(Symbol | String, Int32 | String) | + Hash(String, String | Int32) | + Hash(Symbol, String | Int32) | + Hash(String, String) | + Hash(String, Int32) | + Hash(Symbol, String) | + Hash(Symbol, Int32) + + HTTP_METHODS = %w{get delete post put patch options head} + + {% for method in Crest::HTTP_METHODS %} + # Execute a {{method.id.upcase}} request and and yields the `Crest::Response` to the block. + # + # ```crystal + # Crest.{{method.id}}("http://httpbin.org/{{method.id}}") do |response| + # while line = response.body_io.gets + # puts line + # end + # end + # ``` + def self.{{method.id}}(url : String, **args, &block : Crest::Response ->) : Nil + request = Request.new(:{{method.id}}, url, **args) + request.execute(&block) + end + + # Execute a {{method.id.upcase}} request and returns a `Crest::Response`. + # + # ```crystal + # Crest.{{method.id}}("http://httpbin.org/{{method.id}}") + # ``` + def self.{{method.id}}(url : String, **args) : Crest::Response + request = Request.new(:{{method.id}}, url, **args) + request.execute + end + {% end %} +end + +require "./crest/**" diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr b/samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr new file mode 100644 index 000000000000..d5fe21b4d4f1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr @@ -0,0 +1,93 @@ +module Crest + # Class to convert `Crest::Request` object to cURL command + # + # ```crystal + # request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"title" => "New Title"}) + # Crest::Curlify.to_curl(request) + # => "curl -X POST http://httpbin.org/post -d 'title=New+Title' -H 'Content-Type: application/x-www-form-urlencoded'" + # ``` + class Curlify + # Returns string with cURL command by provided `Crest::Request` object + def self.to_curl(request : Crest::Request) + new(request).to_curl + end + + def initialize(@request : Crest::Request) + end + + def to_curl + ["curl", method, url, proxy, basic_auth, form_data, headers].reject(&.empty?).join(" ") + end + + private def method + "-X #{@request.method}" + end + + private def url + "#{@request.url}" + end + + private def headers : String + headers = [] of String + @request.headers.each do |k, v| + next if k == "Authorization" && basic_auth? && includes_authorization_header? + + value = v.is_a?(Array) ? v.first.split(";").first : v + headers << "-H '#{k}: #{value}'" + end + + headers.join(" ") + end + + private def form_data : String + form_data = [] of String + + HTTP::FormData.parse(@request.http_request) do |part| + value = part.filename ? "@#{part.filename}" : part.body.gets_to_end + + form_data << "-F '#{part.name}=#{value}'" + end + + form_data.join(" ") + rescue HTTP::FormData::Error + body = @request.http_request.body.to_s + + body.empty? ? "" : "-d '#{body}'" + end + + private def basic_auth : String + return "" unless basic_auth? + + params = [] of String + + params << "--digest" if @request.auth == "digest" + params << "--user #{@request.user}:#{@request.password}" + + params.join(" ") + end + + private def proxy : String + return "" unless @request.proxy + + params = [] of String + + params << "--proxy #{@request.p_addr}:#{@request.p_port}" + params << "--proxy-user #{@request.p_user}:#{@request.p_pass}" if proxy_auth? + + params.join(" ") + end + + private def basic_auth? : Bool + @request.user && @request.password ? true : false + end + + private def proxy_auth? : Bool + @request.p_user && @request.p_pass ? true : false + end + + private def includes_authorization_header? + @request.headers.includes_word?("Authorization", "Basic") || + @request.headers.includes_word?("Authorization", "Digest") + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr b/samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr new file mode 100644 index 000000000000..5f1aba4a6c84 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr @@ -0,0 +1,125 @@ +require "./response" + +module Crest + # Hash of HTTP status code => message. + STATUSES = {100 => "Continue", + 101 => "Switching Protocols", + 102 => "Processing", # WebDAV + + 200 => "OK", + 201 => "Created", + 202 => "Accepted", + 203 => "Non-Authoritative Information", # http/1.1 + 204 => "No Content", + 205 => "Reset Content", + 206 => "Partial Content", + 207 => "Multi-Status", # WebDAV + 208 => "Already Reported", # RFC5842 + 226 => "IM Used", # RFC3229 + + 300 => "Multiple Choices", + 301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", # http/1.1 + 304 => "Not Modified", + 305 => "Use Proxy", # http/1.1 + 306 => "Switch Proxy", # no longer used + 307 => "Temporary Redirect", # http/1.1 + 308 => "Permanent Redirect", # RFC7538 + + 400 => "Bad Request", + 401 => "Unauthorized", + 402 => "Payment Required", + 403 => "Forbidden", + 404 => "Not Found", + 405 => "Method Not Allowed", + 406 => "Not Acceptable", + 407 => "Proxy Authentication Required", + 408 => "Request Timeout", + 409 => "Conflict", + 410 => "Gone", + 411 => "Length Required", + 412 => "Precondition Failed", + 413 => "Payload Too Large", # RFC7231 (renamed, see below) + 414 => "URI Too Long", # RFC7231 (renamed, see below) + 415 => "Unsupported Media Type", + 416 => "Range Not Satisfiable", # RFC7233 (renamed, see below) + 417 => "Expectation Failed", + 418 => "I\"m A Teapot", # RFC2324 + 421 => "Too Many Connections From This IP", + 422 => "Unprocessable Entity", # WebDAV + 423 => "Locked", # WebDAV + 424 => "Failed Dependency", # WebDAV + 425 => "Unordered Collection", # WebDAV + 426 => "Upgrade Required", + 428 => "Precondition Required", # RFC6585 + 429 => "Too Many Requests", # RFC6585 + 431 => "Request Header Fields Too Large", # RFC6585 + 449 => "Retry With", # Microsoft + 450 => "Blocked By Windows Parental Controls", # Microsoft + + 500 => "Internal Server Error", + 501 => "Not Implemented", + 502 => "Bad Gateway", + 503 => "Service Unavailable", + 504 => "Gateway Timeout", + 505 => "HTTP Version Not Supported", + 506 => "Variant Also Negotiates", + 507 => "Insufficient Storage", # WebDAV + 508 => "Loop Detected", # RFC5842 + 509 => "Bandwidth Limit Exceeded", # Apache + 510 => "Not Extended", + 511 => "Network Authentication Required", # RFC6585 + } + + # This is the base `Crest` exception class. Rescue it if you want to + # catch any exception that your request might raise + # You can see anything about the response via `e.response`. + # For example, the entire result body (which is + # probably an HTML error page) is `e.response.body`. + # + # Hash of HTTP status `code => message`. + # + # * **1xx**: Informational - Request received, continuing process + # * **2xx**: Success - The action was successfully received, understood, and + # accepted + # * **3xx**: Redirection - Further action must be taken in order to complete the + # request + # * **4xx**: Client Error - The request contains bad syntax or cannot be fulfilled + # * **5xx**: Server Error - The server failed to fulfill an apparently valid + # request + # + # See [HTTP Status Code Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) + # for more Information. + class RequestFailed < Exception + getter response + + @response : Crest::Response + + def self.subclass_by_status_code(status_code) + EXCEPTIONS_MAP.fetch(status_code, self) + end + + def initialize(response) + @response = response + end + + def http_code + @response.status_code.to_i + end + + def message + "HTTP status code #{http_code}: #{STATUSES[http_code]}" + end + end + + EXCEPTIONS_MAP = {} of Int32 => Crest::RequestFailed.class + + {% for code, status in STATUSES %} + # :nodoc: + class {{status.gsub(/\W/, "").id}} < RequestFailed + end + + EXCEPTIONS_MAP[{{code.id}}] = Crest::{{status.gsub(/\W/, "").id}} + {% end %} +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/form.cr b/samples/client/petstore/crystal/lib/crest/src/crest/form.cr new file mode 100644 index 000000000000..e6898e4f3cbe --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/form.cr @@ -0,0 +1,17 @@ +module Crest + abstract class Form(T) + @form_data : String = "" + @content_type : String = "" + + getter params, form_data, content_type + + def self.generate(params : Hash) + new(params).generate + end + + def initialize(@params : T) + end + + abstract def generate + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr b/samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr new file mode 100644 index 000000000000..201968affb7a --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr @@ -0,0 +1,43 @@ +require "../form" + +module Crest + # This class lets `crest` emulate a filled-in form + # in which a user has pressed the submit button. + # This causes `crest` to POST data using the + # "Content-Type" `multipart/form-data according` to RFC 2388. + # This enables uploading of binary files etc. + class DataForm(T) < Crest::Form(T) + def generate + content_type_ch = Channel(String).new(1) + io = IO::Memory.new + + HTTP::FormData.build(io) do |formdata| + content_type_ch.send(formdata.content_type) + + # Creates an `HTTP::FormData` instance from the key-value + # pairs of the given `params`. + parsed_params.each do |name, value| + add_field(formdata, name.to_s, value) + end + end + + @form_data = io.to_s + @content_type = content_type_ch.receive + + self + end + + def parsed_params + Crest::ParamsEncoder.flatten_params(@params) + end + + private def add_field(formdata : HTTP::FormData::Builder, name : String | Symbol, value : TextValue) + formdata.field(name.to_s, value.to_s) + end + + private def add_field(formdata : HTTP::FormData::Builder, name : String | Symbol, value : File) + metadata = HTTP::FormData::FileMetadata.new(filename: value.path) + formdata.file(name.to_s, value, metadata) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr b/samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr new file mode 100644 index 000000000000..335b0597d9d3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr @@ -0,0 +1,21 @@ +require "../form" + +module Crest + # This class lets `crest` emulate a filled-in form + # in which a user has pressed the submit button. + # This causes `crest` to POST data using the + # "Content-Type" `application/x-www-form-urlencoded`. + class UrlencodedForm(T) < Crest::Form(T) + @content_type : String = "application/x-www-form-urlencoded" + + def generate + @form_data = parsed_params + + self + end + + def parsed_params + Crest::ParamsEncoder.encode(@params) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/logger.cr b/samples/client/petstore/crystal/lib/crest/src/crest/logger.cr new file mode 100644 index 000000000000..c69617f06e4a --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/logger.cr @@ -0,0 +1,53 @@ +require "log" + +module Crest + abstract class Logger + def self.new(filename : String) + new(File.open(filename, "w")) + end + + forward_missing_to @logger + + def initialize(@io : IO = STDOUT) + backend = Log::IOBackend.new + backend.io = @io + + @logger = Log.new("crest", backend, Log::Severity::Info) + @logger.backend.as(Log::IOBackend).formatter = default_formatter + + @filters = [] of Array(String | Regex) + end + + abstract def request(request : Crest::Request) : Nil + abstract def response(response : Crest::Response) : Nil + + def default_formatter : Log::Formatter + Log::Formatter.new do |entry, io| + io << entry.source + io << " | " << entry.timestamp.to_s("%F %T") + io << " " << entry.message + end + end + + def info(message : String) + @logger.info { apply_filters(message) } + end + + def filter(patern : String | Regex, replacement : String) + @filters.push([patern, replacement]) + end + + private def apply_filters(output : String) : String + @filters.each do |f| + patern = f[0] + replacement = f[1] + + output = output.gsub(patern, replacement) + end + + output + end + end +end + +require "./loggers/*" diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr b/samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr new file mode 100644 index 000000000000..8e0ae14c1b74 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr @@ -0,0 +1,77 @@ +# Copyright (c) 2017 icyleaf +# Licensed under The MIT License (MIT) +# http://opensource.org/licenses/MIT + +require "colorize" +require "../logger" + +module Crest + class CommonLogger < Logger + def request(request : Crest::Request) : Nil + message = String.build do |io| + io << "| " << colorful_method(request.method) + io << " | " << request.url + io << " | " << request.form_data.to_s.inspect unless request.form_data.nil? + end.to_s + + info(message) + + message + end + + def response(response : Crest::Response) : Nil + message = String.build do |io| + io << "| " << colorful_status_code(response.status_code) + io << " | " << response.url + io << " | " << response.body.inspect + end.to_s + + info(message) + + message + end + + private def colorful_method(method) + fore, back = + case method + when "GET" + {:white, :blue} + when "POST" + {:white, :cyan} + when "PUT" + {:white, :yellow} + when "DELETE" + {:white, :red} + when "PATCH" + {:white, :green} + when "HEAD" + {:white, :magenta} + else + {:dark_gray, :white} + end + + colorful((" %-7s" % method), fore, back) + end + + private def colorful_status_code(status_code) + fore, back = + case status_code + when 300..399 + {:dark_gray, :white} + when 400..499 + {:white, :yellow} + when 500..599 + {:white, :red} + else + {:white, :green} + end + + colorful((" %-7s" % status_code), fore, back) + end + + private def colorful(message, fore, back) + Colorize.enabled = !@io.is_a?(File) + message.colorize.fore(fore).back(back) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr b/samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr new file mode 100644 index 000000000000..b4eaa4042989 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr @@ -0,0 +1,130 @@ +module Crest + module ParamsEncoder + extend self + + alias Type = Nil | String | Array(Type) | Hash(String, Type) + + SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/ + ARRAY_REGEX = /[\[\]]+\Z/ + + # Converts the given params into a URI querystring. Keys and values + # will converted to strings and appropriately escaped for the URI. + # + # ``` + # Crest::ParamsEncoder.encode({"a" => ["one", "two", "three"], "b" => true, "c" => "C", "d" => 1}) + # # => 'a[]=one&a[]=two&a[]=three&b=true&c=C&d=1' + # ``` + def encode(params : Hash) : String + HTTP::Params.build do |form| + flatten_params(params).each do |name, value| + form.add(name.to_s, value.to_s) + end + end + end + + # Converts the given URI querystring into a hash. + # + # ``` + # Crest::ParamsEncoder.decode("a[]=one&a[]=two&a[]=three&b=true&c=C&d=1") + # # => {"a" => ["one", "two", "three"], "b" => "true", "c" => "C", "d" => "1"} + # ``` + def decode(query : String) : Hash + params = {} of String => Type + + query.split("&").each do |pair| + key, value = pair.split("=", 2) + key = URI.decode(key) + value = URI.decode(value) + + decode_pair(params, key, value) + end + + params + end + + private def decode_pair(context, key : String, value : String) + subkeys = key.scan(SUBKEYS_REGEX) + + subkeys.each_with_index do |subkey, i| + is_array = false + is_last_subkey = (i == subkeys.size - 1) + subkey = subkey[0] + + if match = subkey.match(ARRAY_REGEX) + is_array = true + subkey = match.pre_match + end + + context = prepare_context(context, subkey, is_array, is_last_subkey) + add_to_context(context, value, subkey) if is_last_subkey + end + end + + private def prepare_context(context, subkey : String, is_array : Bool, is_last_subkey : Bool) + if !is_last_subkey || is_array + context = new_context(context, subkey, is_array) + end + + context + end + + private def new_context(context, subkey : String, is_array : Bool) + value_type = is_array ? Array(Type) : Hash(String, Type) + + if context.is_a?(Hash) + context[subkey] ||= value_type.new + end + end + + private def add_to_context(context, value : String, subkey : String) + value = value.empty? ? nil : value + + if context.is_a?(Hash) + context[subkey] = value + elsif context.is_a?(Array) + context << value + end + end + + # Transform deeply nested params containers into a flat hash of `key => value`. + # + # ``` + # Crest::ParamsEncoder.flatten_params({:key1 => {:key2 => "123"}}) + # # => [{"key1[key2]", "123"}] + # ``` + def flatten_params(object : Hash, parent_key = nil) + object.reduce([] of Tuple(String, TextValue | File)) do |memo, item| + k, v = item + + processed_key = parent_key ? "#{parent_key}[#{k}]" : k.to_s + + case v + when Hash, Array + memo += flatten_params(v, processed_key) + else + memo << {processed_key, v} + end + + memo + end + end + + # Transform deeply nested params containers into a flat hash of `key => value`. + # + # ``` + # Crest::ParamsEncoder.flatten_params({:key1 => {:arr => ["1", "2", "3"]}}) + # # => [{"key1[arr][]", "1"}, {"key1[arr][]", "2"}, {"key1[arr][]", "3"}] + # ``` + def flatten_params(object : Array, parent_key = nil) + object.reduce([] of Tuple(String, TextValue | File)) do |memo, item| + k = :"" + v = item + + processed_key = parent_key ? "#{parent_key}[#{k}]" : k.to_s + memo << {processed_key, v} + + memo + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr b/samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr new file mode 100644 index 000000000000..9452b818c774 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr @@ -0,0 +1,89 @@ +module Crest + class Redirector + def initialize(@response : Crest::Response, @request : Crest::Request) + end + + def follow : Crest::Response + case @response + when .success? + @response + when .redirect? + check_max_redirects + + @request.max_redirects > 0 ? follow_redirection : @response + else + raise_exception! if @request.handle_errors + @response + end + end + + def follow(&block : Crest::Response ->) + case @response + when .success? + @response + when .redirect? + check_max_redirects + + @request.max_redirects > 0 ? follow_redirection(&block) : @response + else + raise_exception! if @request.handle_errors + @response + end + end + + private def check_max_redirects + raise_exception! if @request.max_redirects <= 0 && @request.handle_errors + end + + # Follow a redirection response by making a new HTTP request to the + # redirection target. + private def follow_redirection : Crest::Response + url = extract_url_from_headers + + new_request = prepare_new_request(url) + new_request.redirection_history = @response.history + [@response] + new_request.execute + end + + private def follow_redirection(&block : Crest::Response ->) + url = extract_url_from_headers + + new_request = prepare_new_request(url) + new_request.redirection_history = @response.history + [@response] + new_request.execute(&block) + end + + private def extract_url_from_headers + location_url = @response.http_client_res.headers["location"] + location_uri = URI.parse(location_url) + + return location_url if location_uri.absolute? + + uri = URI.parse(@request.url) + port = uri.port ? ":#{uri.port}" : "" + + "#{uri.scheme}://#{uri.host}#{port}#{location_url}" + end + + private def prepare_new_request(url) + Request.new( + method: :get, + url: url, + headers: @request.headers.to_h, + max_redirects: @request.max_redirects - 1, + cookies: @response.cookies, + logging: @request.logging, + logger: @request.logger, + handle_errors: @request.handle_errors, + p_addr: @request.p_addr, + p_port: @request.p_port, + p_user: @request.p_user, + p_pass: @request.p_pass + ) + end + + private def raise_exception! + raise RequestFailed.subclass_by_status_code(@response.status_code).new(@response) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/request.cr b/samples/client/petstore/crystal/lib/crest/src/crest/request.cr new file mode 100644 index 000000000000..fc9df3c26a17 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/request.cr @@ -0,0 +1,327 @@ +require "../crest" + +module Crest + # A class that used to make the requests + # The result of a `Crest::Request` is a `Crest::Response` object. + # + # Simple example: + # + # ```crystal + # request = Crest::Request.new(method: :post, url: "http://httpbin.org/post", form: {:age => 27}, params: {:name => "Kurt"}) + # request.execute + # + # Crest::Request.execute(method: :post, url: "http://httpbin.org/post", form: {:age => 27}.to_json) + # + # Crest::Request.post(url: http://httpbin.org/post", form: {:age => 27}.to_json) + # ``` + # + # Block style: + # + # ```crystal + # request = Crest::Request.new(:get, http://httpbin.org/get") do |request| + # request.headers.add("foo", "bar") + # request.user = "username" + # request.password = "password" + # end + # + # response = request.execute + # ``` + # + # Mandatory parameters: + # * `method` + # * `url` + # + # Optional parameters: + # * `headers` a hash containing the request headers + # * `cookies` a hash containing the request cookies + # * `form` a hash containing form params (or a raw string) + # * `params` a hash that represent query-string separated from the preceding part by a question mark (?) + # a sequence of attribute–value pairs separated by a delimiter (&). + # * `auth` access authentication method `basic` or `digest` (default to `basic`) + # * `user` and `password` for authentication + # * `tls` configuring TLS settings + # * `p_addr`, `p_port`, `p_user`, `p_pass` for proxy + # * `max_redirects` maximum number of redirections (default to `10`) + # * `logging` enable logging (default to `false`) + # * `logger` set logger (default to `Crest::CommonLogger`) + # * `handle_errors` error handling (default to `true`) + # * `http_client` instance of `HTTP::Client` + class Request + @method : String + @url : String + @tls : OpenSSL::SSL::Context::Client? + @http_client : HTTP::Client + @http_request : HTTP::Request + @headers : HTTP::Headers + @cookies : HTTP::Cookies + @form_data : String? + @max_redirects : Int32 + @auth : String + @user : String? + @password : String? + @proxy : HTTP::Proxy::Client? + @p_addr : String? + @p_port : Int32? + @p_user : String? + @p_pass : String? + @logger : Crest::Logger + @logging : Bool + @handle_errors : Bool + + getter http_client, http_request, method, url, form_data, headers, cookies, + max_redirects, logging, logger, handle_errors, auth, + proxy, p_addr, p_port, p_user, p_pass + + property redirection_history, user, password + + delegate host, port, tls?, to: @http_client + + def self.execute(method, url, **args) : Crest::Response + request = new(method, url, **args) + request.execute + end + + def self.execute(method, url, **args, &block : Crest::Response ->) : Nil + request = new(method, url, **args) + request.execute(&block) + end + + def initialize( + method : Symbol, + url : String, + *, + headers = {} of String => String, + cookies = {} of String => String, + form = {} of String => String, + params = {} of String => String, + max_redirects = 10, + **options + ) + @method = parse_verb(method) + @url = url + @headers = HTTP::Headers.new + @cookies = HTTP::Cookies.new + @redirection_history = [] of Crest::Response + + set_headers!(headers) + set_cookies!(cookies) unless cookies.empty? + generate_form_data!(form) if form + + unless params.empty? + @url = url + process_url_params(params) + end + + @max_redirects = max_redirects + + @tls = options.fetch(:tls, nil).as(OpenSSL::SSL::Context::Client | Nil) + @http_client = options.fetch(:http_client, new_http_client).as(HTTP::Client) + @auth = options.fetch(:auth, "basic").as(String) + @user = options.fetch(:user, nil).as(String | Nil) + @password = options.fetch(:password, nil).as(String | Nil) + @p_addr = options.fetch(:p_addr, nil).as(String | Nil) + @p_port = options.fetch(:p_port, nil).as(Int32 | Nil) + @p_user = options.fetch(:p_user, nil).as(String | Nil) + @p_pass = options.fetch(:p_pass, nil).as(String | Nil) + @logger = options.fetch(:logger, Crest::CommonLogger.new).as(Crest::Logger) + @logging = options.fetch(:logging, false).as(Bool) + @handle_errors = options.fetch(:handle_errors, true).as(Bool) + + @http_request = HTTP::Request.new(@method, @url, body: @form_data, headers: @headers) + + set_proxy!(@p_addr, @p_port, @p_user, @p_pass) + + yield self + end + + # When block is not given. + def initialize(method : Symbol, url : String, **args) + initialize(method, url, **args) { } + end + + {% for method in Crest::HTTP_METHODS %} + # Execute a {{method.id.upcase}} request and and yields the `Crest::Response` to the block. + # + # ```crystal + # Crest::Request.{{method.id}}("http://httpbin.org/{{method.id}}") do |resp| + # while line = resp.body_io.gets + # puts line + # end + # end + # ``` + def self.{{method.id}}(url : String, **args, &block : Crest::Response ->) : Nil + request = Request.new(:{{method.id}}, url, **args) + + response = request.execute(&block) + end + + # Execute a {{method.id.upcase}} request and returns a `Crest::Response`. + # + # ```crystal + # Crest::Request.{{method.id}}("http://httpbin.org/{{method.id}}") + # ``` + def self.{{method.id}}(url : String, **args) : Crest::Response + request = Request.new(:{{method.id}}, url, **args) + + request.execute + end + {% end %} + + # Execute HTTP request + def execute : Crest::Response + @http_client.set_proxy(@proxy) + authenticate! + @logger.request(self) if @logging + + @http_request = new_http_request(@method, @url, @headers, @form_data) + + http_response = @http_client.exec(@http_request) + + process_result(http_response) + end + + # Execute streaming HTTP request + def execute(&block : Crest::Response ->) : Nil + @http_client.set_proxy(@proxy) + authenticate! + @logger.request(self) if @logging + + @http_request = new_http_request(@method, @url, @headers, @form_data) + + @http_client.exec(@http_request) do |http_response| + response = process_result(http_response, &block) + + if response + yield response + end + end + end + + private def process_result(http_client_res) : Crest::Response + response = Response.new(http_client_res, self) + logger.response(response) if logging + response.return! + end + + private def process_result(http_client_res, &block : Crest::Response ->) + response = Response.new(http_client_res, self) + logger.response(response) if logging + response.return!(&block) + end + + # Convert `Request` object to cURL command + def to_curl + Crest::Curlify.to_curl(self) + end + + private def new_http_client : HTTP::Client + uri = URI.parse(@url) + HTTP::Client.new(uri, tls: @tls) + end + + private def new_http_request(method, path, headers, body) : HTTP::Request + HTTP::Request.new(method, path, headers, body).tap do |request| + request.headers["Host"] ||= host_header + request.headers["User-Agent"] ||= "Crest/#{Crest::VERSION} (Crystal/#{Crystal::VERSION})" + end + end + + private def host_header + if (tls? && port != 443) || (!tls? && port != 80) + "#{host}:#{port}" + else + host + end + end + + private def parse_verb(method : String | Symbol) : String + method.to_s.upcase + end + + private def multipart?(form : Hash) : Bool + form.any? { |_, v| v.is_a?(File) } + end + + private def generate_form_data!(form : Hash) : String? + return if form.empty? + + form_class = multipart?(form) ? Crest::DataForm : Crest::UrlencodedForm + form = form_class.generate(form) + + @form_data = form.form_data + content_type = form.content_type + + @headers["Content-Type"] = content_type + + @form_data + end + + private def generate_form_data!(form : String) : String? + @form_data = form + end + + private def set_headers!(params) : HTTP::Headers + params.each do |key, value| + @headers.add(key, value) + end + + @headers + end + + # Adds "Cookie" headers for the cookies in this collection to the @header instance and returns it + private def set_cookies!(cookies) : HTTP::Headers + cookies.each do |k, v| + @cookies << HTTP::Cookie.new(k.to_s, v.to_s) + end + + @cookies.add_request_headers(@headers) + end + + protected def authenticate! + return unless @user && @password + + if @auth == "basic" + basic_auth! + else + digest_auth! + end + end + + private def basic_auth! + auth = "Basic #{Base64.strict_encode("#{@user}:#{@password}")}" + + @headers.add("Authorization", auth) + end + + private def digest_auth! + uri = URI.parse(@url) + uri.user = @user + uri.password = @password + + response = @http_client.exec(@method, uri.full_path) + + www_authenticate = response.headers["WWW-Authenticate"] + + digest_auth = HTTP::Client::DigestAuth.new + auth = digest_auth.auth_header(uri, www_authenticate, @method) + + @headers.add("Authorization", auth) + end + + private def set_proxy!(p_addr, p_port, p_user, p_pass) + return unless p_addr && p_port + + @proxy = HTTP::Proxy::Client.new(p_addr, p_port, username: p_user, password: p_pass) + end + + # Extract the query parameters and append them to the url + private def process_url_params(url_params) : String + query_string = Crest::ParamsEncoder.encode(url_params) + + if url.includes?("?") + "&" + query_string + else + "?" + query_string + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/resource.cr b/samples/client/petstore/crystal/lib/crest/src/crest/resource.cr new file mode 100644 index 000000000000..d061244b8d17 --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/resource.cr @@ -0,0 +1,176 @@ +require "../crest" + +module Crest + # A class that can be instantiated for access to a RESTful resource, + # including authentication, proxy and logging. + # + # Simple example: + # + # ```crystal + # resource = Crest::Resource.new("https://httpbin.org/get") + # response = resource.get + # ``` + # + # Block style: + # + # ```crystal + # resource = Crest::Resource.new("http://httpbin.org") do |res| + # res.headers.merge!({"foo" => "bar"}) + # end + # + # response = resource["/headers"].get + # ``` + # + # With HTTP basic authentication: + # + # ```crystal + # resource = Crest::Resource.new("https://httpbin.org/get", user: "user", password: "password") + # ``` + # + # Use the `[]` syntax to allocate subresources: + # + # ```crystal + # resource = Crest::Resource.new("https://httpbin.org") + # resource["/get"].get + # ``` + # + # You can pass advanced parameters like default `params` or `headers`: + # + # ```crystal + # resource = Crest::Resource.new( + # "https://httpbin.org", + # params: {"key" => "key"}, + # headers: {"Content-Type" => "application/json"} + # ) + # response = response["/post"].post( + # form: {:height => 100, "width" => "100"}, + # params: {:secret => "secret"} + # ) + # ``` + # If you want to stream the data from the response you can pass a block: + # + # ```crystal + # resource = Crest::Resource.new("http://httpbin.org") + # resource["/stream/5"].get do |response| + # while line = response.body_io.gets + # puts line + # end + # end + # ``` + class Resource + getter http_client, url, user, password, headers, params, + logging, logger, handle_errors, p_addr, p_port, p_user, p_pass + + def initialize( + @url : String, + *, + @headers = {} of String => String, + @params : Params = {} of String => String, + **options + ) + @base_url = @url + @tls = options.fetch(:tls, nil).as(OpenSSL::SSL::Context::Client | Nil) + @http_client = options.fetch(:http_client, new_http_client).as(HTTP::Client) + @user = options.fetch(:user, nil).as(String | Nil) + @password = options.fetch(:password, nil).as(String | Nil) + @p_addr = options.fetch(:p_addr, nil).as(String | Nil) + @p_port = options.fetch(:p_port, nil).as(Int32 | Nil) + @p_user = options.fetch(:p_user, nil).as(String | Nil) + @p_pass = options.fetch(:p_pass, nil).as(String | Nil) + @logger = options.fetch(:logger, Crest::CommonLogger.new).as(Crest::Logger) + @logging = options.fetch(:logging, false).as(Bool) + @handle_errors = options.fetch(:handle_errors, true).as(Bool) + + yield self + end + + # When block is not given. + def initialize(@url : String, **args) + initialize(@url, **args) { } + end + + {% for method in Crest::HTTP_METHODS %} + # Execute a {{method.id.upcase}} request and returns a `Crest::Response`. + def {{method.id}}( + suburl : String? = nil, + *, + form = {} of String => String, + headers = {} of String => String, + params = {} of String => String + ) : Crest::Response + @url = concat_urls(@base_url, suburl) if suburl + @headers = @headers.merge(headers) + @params = merge_params(params) + + execute_request(:{{method.id}}, form) + end + + # Execute a {{method.id.upcase}} request and and yields the `Crest::Response` to the block. + def {{method.id}}( + suburl : String? = nil, + *, + form = {} of String => String, + headers = {} of String => String, + params = {} of String => String, + &block : Crest::Response -> + ) : Nil + @url = concat_urls(@base_url, suburl) if suburl + @headers = @headers.merge(headers) + @params = merge_params(params) + + execute_request(:{{method.id}}, form, &block) + end + {% end %} + + def [](suburl) + @url = concat_urls(@base_url, suburl) + + self + end + + private def new_http_client : HTTP::Client + uri = URI.parse(@url) + HTTP::Client.new(uri, tls: @tls) + end + + private def execute_request(method : Symbol, form = {} of String => String) + Request.execute(**request_params(method, form)) + end + + private def execute_request(method : Symbol, form = {} of String => String, &block : Crest::Response ->) + Request.execute(**request_params(method, form), &block) + end + + private def request_params(method : Symbol, form = {} of String => String) + { + method: method, + form: form, + url: @url, + params: @params, + headers: @headers, + tls: @tls, + user: @user, + password: @password, + p_addr: @p_addr, + p_port: @p_port, + p_user: @p_user, + p_pass: @p_pass, + logging: @logging, + logger: @logger, + handle_errors: @handle_errors, + http_client: @http_client, + } + end + + private def merge_params(other = {} of String => String) + @params.try do |params| + other = params.merge(other) + end + other + end + + private def concat_urls(url : String, suburl : String) : String + File.join(url, suburl) + end + end +end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/response.cr b/samples/client/petstore/crystal/lib/crest/src/crest/response.cr new file mode 100644 index 000000000000..342e1ea0a90c --- /dev/null +++ b/samples/client/petstore/crystal/lib/crest/src/crest/response.cr @@ -0,0 +1,118 @@ +require "http" +require "../crest" +require "../crest/redirector" + +module Crest + # Response objects have several useful methods: + # + # * `body`: The response body as a `String` + # * `body_io`: The response body as a `IO` + # * `status`: The response status as a `HTTP::Status` + # * `status_code`: The HTTP response code + # * `headers`: A hash of HTTP response headers + # * `cookies`: A hash of HTTP cookies set by the server + # * `request`: The `Crest::Request` object used to make the request + # * `http_client_res`: The `HTTP::Client::Response` object + # * `history`: A list of each response received in a redirection chain + class Response + getter http_client_res, request + + delegate body, to: http_client_res + delegate body_io, to: http_client_res + delegate status, to: http_client_res + delegate status_code, to: http_client_res + delegate informational?, success?, redirection?, client_error?, server_error?, to: status + delegate to_curl, to: request + + def initialize(@http_client_res : HTTP::Client::Response, @request : Crest::Request) + end + + def return! : Crest::Response + redirector = Redirector.new(self, request) + redirector.follow + end + + def return!(&block : Crest::Response ->) + redirector = Redirector.new(self, request) + redirector.follow(&block) + end + + def url : String + @request.url + end + + def headers + headers = @request.headers.dup.merge!(http_client_res.headers) + + normalize_headers(headers) + end + + def cookies + request_cookies.merge(response_cookies) + end + + def history : Array + @request.redirection_history + end + + # Extracts filename from Content-Disposition header + def filename : String? + filename_regex = /filename\*?=['"]?(?:UTF-\d['"]*)?([^;\r\n"']*)['"]?;?/xi + + if match_data = headers.fetch("Content-Disposition", "").as(String).match(filename_regex) + return match_data[1] + end + end + + def invalid? + status_code < 100 || status_code >= 600 + end + + def redirect? + [301, 302, 303, 307, 308].includes?(status_code) + end + + def to_s(io : IO) : Nil + io.write_utf8(body.to_slice) + end + + def inspect + "" + end + + private def raise_exception! + raise RequestFailed.subclass_by_status_code(status_code).new(self) + end + + private def request_cookies + cookies_to_h(@request.cookies) + end + + private def response_cookies + cookies_to_h(@http_client_res.cookies) + end + + private def normalize_headers(headers : HTTP::Headers) + headers.map do |header| + key, value = header + + if value.is_a?(Array) && value.size == 1 + value = value.first + end + {key, value} + end.to_h + end + + private def cookies_to_h(cookies : HTTP::Cookies) + cookies.to_h.map { |e| [e[1].name.to_s, URI.encode(e[1].value)] }.to_h + end + + private def body_truncated(size) + if body.size > size + body[0..size] + "..." + else + body + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/exception_page/.editorconfig b/samples/client/petstore/crystal/lib/exception_page/.editorconfig new file mode 100644 index 000000000000..163eb75c8525 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.cr] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/samples/client/petstore/crystal/lib/exception_page/.gitignore b/samples/client/petstore/crystal/lib/exception_page/.gitignore new file mode 100644 index 000000000000..e29dae78f635 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/.gitignore @@ -0,0 +1,9 @@ +/docs/ +/lib/ +/bin/ +/.shards/ +*.dwarf + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock diff --git a/samples/client/petstore/crystal/lib/exception_page/.travis.yml b/samples/client/petstore/crystal/lib/exception_page/.travis.yml new file mode 100644 index 000000000000..26b28c99eae6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/.travis.yml @@ -0,0 +1,28 @@ +language: crystal + +crystal: + - latest + - nightly + +matrix: + allow_failures: + - crystal: nightly + +addons: + chrome: stable + +services: + - xvfb + +before_install: + # Setup chromedriver for LuckyFlow + - npm -g install chromedriver --detect_chromedriver_version + - export DISPLAY=:99.0 + +install: + - shards install + +script: + - crystal spec + - crystal tool format --check + - bin/ameba diff --git a/samples/client/petstore/crystal/lib/exception_page/LICENSE b/samples/client/petstore/crystal/lib/exception_page/LICENSE new file mode 100644 index 000000000000..41e891668f45 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Paul Smith + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/exception_page/README.md b/samples/client/petstore/crystal/lib/exception_page/README.md new file mode 100644 index 000000000000..07466fd65ddf --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/README.md @@ -0,0 +1,111 @@ +# Exception Page + +A library for displaying exceptional exception pages for easier debugging. + +![screen shot 2018-06-29 at 2 39 18 pm](https://user-images.githubusercontent.com/22394/42109073-6e767d06-7baa-11e8-9ec9-0a2afce605be.png) + +## Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + exception_page: + github: crystal-loot/exception_page +``` + +## Usage + +Require the shard: + +```crystal +require "exception_page" +``` + +Create an exception page: + +```crystal +class MyApp::ExceptionPage < ExceptionPage + def styles : Styles + ExceptionPage::Styles.new( + accent: "purple", # Choose the HTML color value. Can be hex + ) + end +end +``` + +Render the HTML when an exception occurs: + +```crystal +class MyErrorHandler + include HTTP::Handler + + def call_next(context) + begin + # Normally you'd call some code to handle the request + # We're hard-coding an error here to show you how to use the lib. + raise SomeError.new("Something went wrong") + rescue e + context.response.status_code = 500 + context.response.print MyApp::ExceptionPage.for_runtime_exception(context, e).to_s + end + end +``` + +## Customizing the page + +```crystal +class MyApp::ExceptionPage < ExceptionPage + def styles : Styles + ExceptionPage::Styles.new( + accent: "purple", # Required + highlight: "gray", # Optional + flash_highlight: "red", # Optional + logo_uri: "base64_encoded_data_uri" # Optional. Defaults to Crystal logo. Generate a logo here: https://dopiaza.org/tools/datauri/index.php + ) + end + + # Optional. If provided, clicking the logo will open this page + def project_url + "https://myproject.com" + end + + # Optional + def stack_trace_heading_html + <<-HTML + Say hi + HTML + end + + # Optional + def extra_javascript + <<-JAVASCRIPT + window.sayHi = function() { + alert("Say Hi!"); + } + JAVASCRIPT + end +end +``` + +## Development + +TODO: Write development instructions here + +## Contributing + +1. Fork it () +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create a new Pull Request + +## Contributors + +- [@paulcsmith](https://github.com/paulcsmith) Paul Smith +- [@faustinoaq](https://github.com/faustinoaq) Faustino Aigular - Wrote the initial [Amber PR adding exception pages](https://github.com/amberframework/amber/pull/864) + +## Special Thanks + +This exception page is heavily based on the [Phoenix error page](https://github.com/phoenixframework/phoenix/issues/1776) +by [@rstacruz](https://github.com/rstacruz). Thanks to the Phoenix team and @rstacruz! diff --git a/samples/client/petstore/crystal/lib/exception_page/lib b/samples/client/petstore/crystal/lib/exception_page/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/exception_page/shard.yml b/samples/client/petstore/crystal/lib/exception_page/shard.yml new file mode 100644 index 000000000000..ca27bb1df867 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/shard.yml @@ -0,0 +1,18 @@ +name: exception_page +version: 0.1.4 + +authors: + - Paul Smith + - Faustino Aguilar + +development_dependencies: + lucky_flow: + github: luckyframework/lucky_flow + version: ~> 0.6.1 + ameba: + github: crystal-ameba/ameba + version: ~> 0.11.0 + +crystal: 0.33.0 + +license: MIT diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr b/samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr new file mode 100644 index 000000000000..f2867217019a --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr @@ -0,0 +1,34 @@ +require "./spec_helper" + +describe ExceptionPage do + it "allows debugging the exception page" do + flow = ErrorDebuggingFlow.new + + flow.view_error_page + flow.should_have_information_for_debugging + flow.show_all_frames + flow.should_be_able_to_view_other_frames + end +end + +class ErrorDebuggingFlow < LuckyFlow + def view_error_page + visit "/" + end + + def should_have_information_for_debugging + el("@exception-title", text: "Something went very wrong").should be_on_page + el("@code-frames", text: "test_server.cr").should be_on_page + click("@see-raw-error-message") + el("@raw-error-message").should be_on_page + end + + def show_all_frames + el("@show-all-frames").click + end + + def should_be_able_to_view_other_frames + el("@code-frame-file", "request_processor.cr").click + el("@code-frame-summary", text: "request_processor.cr").should be_on_page + end +end diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr b/samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr new file mode 100644 index 000000000000..5a47be002142 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr @@ -0,0 +1,27 @@ +require "./spec_helper" + +describe "Frame parsing" do + it "returns the correct label" do + frame = frame_for("from usr/crystal-lang/frame_spec.cr:6:7 in '->'") + frame.label.should eq("crystal") + + frame = frame_for("from usr/crystal/frame_spec.cr:6:7 in '->'") + frame.label.should eq("crystal") + + frame = frame_for("from lib/exception_page/spec/frame_spec.cr:6:7 in '->'") + frame.label.should eq("exception_page") + + frame = frame_for("from lib/exception_page/frame_spec.cr:6:7 in '->'") + frame.label.should eq("exception_page") + + frame = frame_for("from lib/frame_spec.cr:6:7 in '->'") + frame.label.should eq("app") + + frame = frame_for("from src/frame_spec.cr:6:7 in '->'") + frame.label.should eq("app") + end +end + +private def frame_for(backtrace_line) + ExceptionPage::FrameGenerator.generate_frames(backtrace_line).first +end diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr new file mode 100644 index 000000000000..b4dd080e00af --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr @@ -0,0 +1,26 @@ +require "http" +require "lucky_flow" +require "../src/exception_page" +require "./support/*" + +include LuckyFlow::Expectations + +server = TestServer.new + +LuckyFlow.configure do |settings| + settings.base_uri = "http://#{server.addr}" + settings.stop_retrying_after = 40.milliseconds +end + +spawn do + server.listen +end + +at_exit do + LuckyFlow.shutdown + server.close +end + +Habitat.raise_if_missing_settings! + +require "spec" diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr b/samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr new file mode 100644 index 000000000000..4b3b33c153b4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr @@ -0,0 +1,19 @@ +class MyApp::ExceptionPage < ExceptionPage + def styles : Styles + Styles.new(accent: "purple") + end + + def stack_trace_heading_html + <<-HTML + Say hi + HTML + end + + def extra_javascript + <<-JAVASCRIPT + window.sayHi = function() { + alert("Say Hi!"); + } + JAVASCRIPT + end +end diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr b/samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr new file mode 100644 index 000000000000..7eda8b3f1372 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr @@ -0,0 +1,29 @@ +class TestServer + getter addr : Socket::IPAddress + + delegate :listen, :close, + to: @server + + def initialize(port : Int32? = nil) + @server = HTTP::Server.new do |context| + if context.request.resource == "/favicon.ico" + context.response.print "" + else + begin + raise CustomException.new("Something went very wrong") + rescue e : CustomException + context.response.content_type = "text/html" + context.response.print MyApp::ExceptionPage.for_runtime_exception(context, e).to_s + end + end + end + if port + @addr = @server.bind_tcp(port: port) + else + @addr = @server.bind_unused_port + end + end +end + +class CustomException < Exception +end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr new file mode 100644 index 000000000000..6bbf5039e19a --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr @@ -0,0 +1,54 @@ +abstract class ExceptionPage +end + +require "ecr" +require "./exception_page/*" + +# :nodoc: +abstract class ExceptionPage + @params : Hash(String, String) + @headers : Hash(String, Array(String)) + @session : Hash(String, HTTP::Cookie) + @method : String + @path : String + @message : String + @query : String + @frames = [] of Frame + @title : String + + abstract def styles : Styles + + # Add an optional link to your project + def project_url : String? + nil + end + + # Override this method to add extra HTML to the top of the stack trace heading + def stack_trace_heading_html + "" + end + + # Override this method to add extra javascript to the page + def extra_javascript + "" + end + + # :nodoc: + def initialize(context : HTTP::Server::Context, @message, @title, @frames) + @params = context.request.query_params.to_h + @headers = context.response.headers.to_h + @method = context.request.method + @path = context.request.path + @url = "#{context.request.host_with_port}#{context.request.path}" + @query = context.request.query_params.to_s + @session = context.response.cookies.to_h + end + + def self.for_runtime_exception(context : HTTP::Server::Context, ex : Exception) + title = "Error #{context.response.status_code}" + frames = FrameGenerator.generate_frames(ex.inspect_with_backtrace) + new(context, ex.message.to_s, title: title, frames: frames) + end + + ECR.def_to_s "#{__DIR__}/exception_page/exception_page.ecr" +end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr new file mode 100644 index 000000000000..5b84e8e1358e --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr @@ -0,0 +1,857 @@ + +<%- +monospace_font = "menlo, consolas, monospace" +-%> + + + + <% + details = @message.lines + headline = details.first? || "Error" + %> + + <%= @title %> at <%= @method %> <%= @path %> - <%= headline %> + + + + + +
    + <%- if project_url -%> + + <%- else %> + + <%- end %> +
    +
    + <%= @title %> + at <%= @method %> <%= @path %> +
    +

    <%= HTML.escape(headline).gsub("'", '\'').gsub(""", '"') %>

    +
    + + See raw message + +
    +                    <%- details.each do |detail| -%>
    +                        <%= HTML.escape(detail).gsub("'", '\'').gsub(""", '"') %>
    +                    <%- end -%>
    +                
    +
    +
    + <% if !@frames.empty? %> +
    +
    + <% @frames.each do |frame| %> +
    + + + <%- if !frame.snippets.empty? -%> +
    +                        <%- frame.snippets.each do |snippet| -%>
    +                          <%= snippet.line %><%= HTML.escape(snippet.code.rstrip).gsub("'", '\'').gsub(""", '"') %>
    +                        <%- end -%>
    +                      
    + <%- else -%> +
    No code available.
    + <%- end -%> + + <% if !frame.args.blank? %> +
    + + <%= frame.label %> + <%= frame.filename %> + +
    <%= HTML.escape(frame.args).gsub("'", '\'').gsub(""", '"') %>
    +
    + <% else %> +
    +
    + <%= frame.label %> + <%= frame.filename %> +
    +
    + <% end %> +
    + <% end %> +
    + +
    +
    + <%= stack_trace_heading_html %> + +
    + +
      + <% @frames.each do |frame| %> +
    • + +
    • + <% end %> +
    +
    +
    + <% end %> +
    + +
    + <% if @params && !@params.empty? %> +
    + Params + <% @params.each do |key, value| %> +
    +
    <%= HTML.escape(key) %>
    +
    <%= HTML.escape(value.inspect) %>
    +
    + <% end %> +
    + <% end %> + +
    + Request info + +
    +
    URI:
    +
    <%= HTML.escape(@url) %>
    +
    + +
    +
    Query string:
    +
    <%= HTML.escape(@query) %>
    +
    +
    + +
    + Headers + <% @headers.each do |key, value| %> +
    +
    <%= HTML.escape(key) %>
    +
    <%= HTML.escape(value.inspect) %>
    +
    + <% end %> +
    + + <% if (session = @session) && !session.empty? %> +
    + Session + <% session.each do |key, value| %> +
    +
    <%= HTML.escape(key) %>
    +
    <%= HTML.escape(value.inspect) %>
    +
    + <% end %> +
    + <% end %> +
    + + + + diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr new file mode 100644 index 000000000000..693fbcdad9e2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr @@ -0,0 +1,77 @@ +# :nodoc: +struct ExceptionPage::Frame + property index : Int32, raw_frame : Regex::MatchData + + def initialize(@raw_frame, @index) + end + + def snippets : Array(Snippet) + snippets = [] of Snippet + if File.exists?(file) + lines = File.read_lines(file) + lines.each_with_index do |code, code_index| + if line_is_nearby?(code_index) + highlight = (code_index + 1 == line) ? true : false + snippets << Snippet.new( + line: code_index + 1, + code: code, + highlight: highlight + ) + end + end + end + snippets + end + + private def line_is_nearby?(code_index : Int32) + (code_index + 1) <= (line + 5) && (code_index + 1) >= (line - 5) + end + + def file : String + raw_frame[1] + end + + def filename : String + file.split('/').last + end + + def line : Int32 + raw_frame[2].to_i + end + + def args + "#{file}:#{line}#{column_with_surrounding_method_name}" + end + + private def column_with_surrounding_method_name + raw_frame[3] + end + + def label : String + case file + when .includes?("/crystal/"), .includes?("/crystal-lang/") + "crystal" + when /lib\/(?[^\/]+)\/.+/ + $~["name"] + else + "app" + end + end + + def context : String + if label == "app" + "app" + else + "all" + end + end + + struct Snippet + property line : Int32, + code : String, + highlight : Bool + + def initialize(@line, @code, @highlight) + end + end +end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr new file mode 100644 index 000000000000..c731b59da624 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr @@ -0,0 +1,13 @@ +# :nodoc: +class ExceptionPage::FrameGenerator + def self.generate_frames(message) + generated_frames = [] of Frame + if raw_frames = message.scan(/\s([^\s\:]+):(\d+)([^\n]+)/) + raw_frames.each_with_index do |frame, index| + generated_frames << Frame.new(raw_frame: frame, index: index) + end + end + + generated_frames + end +end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr new file mode 100644 index 000000000000..e2554f877acf --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr @@ -0,0 +1,18 @@ +class ExceptionPage::Styles + getter accent : String, + highlight : String, + flash_highlight : String, + logo_uri : String? + + def initialize( + @accent, + @highlight = "#e5e5e5", + @flash_highlight = "#ffdc93", + @logo_uri = crystal_logo + ) + end + + private def crystal_logo + "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAxOTMuMiAyMDYuNyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTkzLjIgMjA2Ljc7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOm5vbmU7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwYXRoIGQ9Ik0xNjUuNCwxMjJsLTUwLDQ5LjljLTAuMiwwLjItMC41LDAuMy0wLjcsMC4ybC02OC4zLTE4LjNjLTAuMy0wLjEtMC41LTAuMy0wLjUtMC41TDI3LjUsODUuMWMtMC4xLTAuMywwLTAuNSwwLjItMC43DQoJCWw1MC00OS45YzAuMi0wLjIsMC41LTAuMywwLjctMC4ybDY4LjMsMTguM2MwLjMsMC4xLDAuNSwwLjMsMC41LDAuNWwxOC4zLDY4LjJDMTY1LjcsMTIxLjYsMTY1LjYsMTIxLjgsMTY1LjQsMTIyeiBNOTguNCw2Ny43DQoJCUwzMS4zLDg1LjZjLTAuMSwwLTAuMiwwLjItMC4xLDAuM2w0OS4xLDQ5YzAuMSwwLjEsMC4zLDAuMSwwLjMtMC4xbDE4LTY3Qzk4LjcsNjcuOCw5OC41LDY3LjYsOTguNCw2Ny43eiIvPg0KCTxnPg0KCQk8cmVjdCBjbGFzcz0ic3QwIiB3aWR0aD0iMTkzLjIiIGhlaWdodD0iMjA2LjciLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==" + end +end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr new file mode 100644 index 000000000000..3f0cb7b97458 --- /dev/null +++ b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr @@ -0,0 +1,3 @@ +class ExceptionPage + VERSION = "0.1.4" +end diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig b/samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig new file mode 100644 index 000000000000..163eb75c8525 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.cr] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore b/samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore new file mode 100644 index 000000000000..0bb75ea03f73 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore @@ -0,0 +1,5 @@ +/docs/ +/lib/ +/bin/ +/.shards/ +*.dwarf diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml b/samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml new file mode 100644 index 000000000000..227f158d989c --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml @@ -0,0 +1,5 @@ +language: crystal + +script: + - crystal spec + - crystal tool format --check diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE b/samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE new file mode 100644 index 000000000000..914fdd2fa0fe --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-2020 Anton Maminov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/README.md b/samples/client/petstore/crystal/lib/http-client-digest_auth/README.md new file mode 100644 index 000000000000..3b2f1ae1a42b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/README.md @@ -0,0 +1,69 @@ +# HTTP::Client::DigestAuth + +[![Build Status](https://travis-ci.org/mamantoha/http-client-digest_auth.svg?branch=master)](https://travis-ci.org/mamantoha/http-client-digest_auth) +[![GitHub release](https://img.shields.io/github/release/mamantoha/http-client-digest_auth.svg)](https://github.com/mamantoha/http-client-digest_auth/releases) +[![License](https://img.shields.io/github/license/mamantoha/http-client-digest_auth.svg)](https://github.com/mamantoha/http-client-digest_auth/blob/master/LICENSE) + +An implementation of RFC 2617 - Digest Access Authentication. At this time +this library does not drop in to `HTTP::Client` and can be used for with other HTTP +clients. + +[crest](https://github.com/mamantoha/crest) uses this shard to provide _Digest Access Authentication_ support out of the box. + +In order to use `http-client-digest_auth` you'll need to perform some request +wrangling on your own. See the class documentation at `HTTP::Client::DigestAuth` +for an example. + +Ported from Ruby's [net-http-digest_auth](https://github.com/drbrain/net-http-digest_auth) gem. + +## Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + http-client-digest_auth: + github: mamantoha/http-client-digest_auth +``` + +## Usage + +```crystal +require "http/client" +require "uri" +require "http-client-digest_auth" + +url = "https://httpbin.org/digest-auth/auth/admin/passwd/MD5" + +uri = URI.parse(url) +uri.user = "admin" +uri.password = "passwd" + +client = HTTP::Client.new(uri) + +response = client.get(uri.full_path) +# response is a 401 response with a WWW-Authenticate header + +www_authenticate = response.headers["WWW-Authenticate"] + +digest_auth = HTTP::Client::DigestAuth.new +auth = digest_auth.auth_header(uri, www_authenticate, "GET") + +http_headers = HTTP::Headers.new +http_headers["Authorization"] = auth + +# re-issue request with Authorization +response = client.get(uri.full_path, http_headers) +``` + +## Contributing + +1. Fork it () +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create a new Pull Request + +## Contributors + +- [Anton Maminov](https://github.com/mamantoha) - creator and maintainer diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/lib b/samples/client/petstore/crystal/lib/http-client-digest_auth/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr new file mode 100644 index 000000000000..2b653b064b3a --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr @@ -0,0 +1,32 @@ +require "http/client" +require "uri" +require "../src/http-client-digest_auth" + +path = "auth" # auth or auth-int +algorithm = "MD5" # MD5, SHA-256, SHA-512 +user = "user" +password = "pass" +url = "https://httpbin.org/digest-auth/#{path}/#{user}/#{password}/#{algorithm}" + +uri = URI.parse(url) +uri.user = user +uri.password = password + +client = HTTP::Client.new(uri) + +response = client.get(uri.full_path) +# response is a 401 response with a WWW-Authenticate header + +www_authenticate = response.headers["WWW-Authenticate"] + +digest_auth = HTTP::Client::DigestAuth.new +auth = digest_auth.auth_header(uri, www_authenticate, "GET") + +http_headers = HTTP::Headers.new +http_headers["Authorization"] = auth + +# re-issue request with Authorization +response = client.get(uri.full_path, http_headers) + +puts response.status_code +puts response.body diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml b/samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml new file mode 100644 index 000000000000..96ef7cab932e --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml @@ -0,0 +1,13 @@ +name: http-client-digest_auth +version: 0.4.0 + +authors: + - Anton Maminov + +targets: + http-client-digest_auth: + main: src/http-client-digest_auth.cr + +crystal: 0.34.0 + +license: MIT diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr new file mode 100644 index 000000000000..06830abd38a3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr @@ -0,0 +1,116 @@ +require "../../spec_helper" + +class HTTP::Client::DigestAuth + # Hardcode nonce value for specs + private def make_cnonce + "9ea5ff3bd34554a4165bbdc1df91dcff" + end +end + +uri = URI.parse("http://www.example.com/") +cnonce = "" +header = "" +expected = [] of String + +da = HTTP::Client::DigestAuth.new + +describe "HTTP::Client::DigestAuth" do + Spec.before_each do + uri = URI.parse("http://www.example.com/") + uri.user = "user" + uri.password = "password" + + cnonce = "9ea5ff3bd34554a4165bbdc1df91dcff" + + header = [ + "Digest qop=\"auth\"", + "realm=\"www.example.com\"", + "nonce=\"4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9\"", + ].join ", " + + expected = [ + "Digest username=\"user\"", + "realm=\"www.example.com\"", + "algorithm=MD5", + "uri=\"/\"", + "nonce=\"4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9\"", + "response=\"67be92a5e7b38d08679957db04f5da04\"", + "qop=auth", + "nc=00000000", + "cnonce=\"9ea5ff3bd34554a4165bbdc1df91dcff\"", + ] + + da = HTTP::Client::DigestAuth.new + end + + it "test auth_header" do + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + + expected[7] = "nc=00000001" + expected[5] = "response=\"1f5f0cd1588690c1303737f081c0b9bb\"" + + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + end + + it "test auth_header iis" do + expected[6] = "qop=\"auth\"" + + (da.auth_header(uri, header, "GET", true)).should eq(expected.join(", ")) + end + + it "test auth_header no qop" do + header = header.sub(" qop=\"auth\",", "") + + expected[5] = "response=\"32f6ca1631ccf7c42a8075deff44e470\"" + expected.delete("qop=auth") + expected.delete("cnonce=\"9ea5ff3bd34554a4165bbdc1df91dcff\"") + expected.delete("nc=00000000") + + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + end + + it "test auth_header opaque" do + expected << "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"" + header = header + "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"" + + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + end + + it "test auth_header post" do + expected[5] = "response=\"d82219e1e5430b136bbae1670fa51d48\"" + + (da.auth_header(uri, header, "POST")).should eq(expected.join(", ")) + end + + it "test auth_header sess" do + header = header + ", algorithm=MD5-sess" + + expected[2] = "algorithm=MD5-sess" + expected[5] = "response=\"c22c5bd9112a86ca78ddc1ae772daeeb\"" + + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + end + + it "test auth_header sha1" do + expected[2] = "algorithm=SHA1" + expected[5] = "response=\"2cb62fc18f7b0ebdc34543f896bb77686b4115e4\"" + + header = header + "algorithm=SHA1" + + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + end + + it "test auth_header unknown algorithm" do + header = header + "algorithm=bogus" + + expect_raises HTTP::Client::DigestAuth::Error, "unknown algorithm \"bogus\"" do + da.auth_header(uri, header, "GET") + end + end + + it "test auth_header quoted algorithm" do + header = header + "algorithm=\"MD5\"" + + (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) + end +end diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr new file mode 100644 index 000000000000..d152e869daf6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr @@ -0,0 +1,2 @@ +require "spec" +require "../src/http-client-digest_auth" diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr new file mode 100644 index 000000000000..6e42367d80bf --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr @@ -0,0 +1,5 @@ +require "./http/client/digest_auth" + +class Http::Client::DigestAuth + VERSION = "0.4.0" +end diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr new file mode 100644 index 000000000000..2dd77da85c9b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr @@ -0,0 +1,168 @@ +require "http/client" +require "uri" +require "digest" + +# An implementation of RFC 2617 Digest Access Authentication. +# http://www.rfc-editor.org/rfc/rfc2617.txt +# +# Here is a sample usage of `DigestAuth` on `HTTP::Client`: +# +# ``` +# url = "https://httpbin.org/digest-auth/auth/user/password/MD5" +# +# uri = URI.parse(url) +# uri.user = "user" +# uri.password = "password" +# +# client = HTTP::Client.new(uri) +# +# response = client.get(uri.full_path) +# # response is a 401 response with a WWW-Authenticate header +# +# www_authenticate = response.headers["WWW-Authenticate"] +# +# digest_auth = HTTP::Client::DigestAuth.new +# auth = digest_auth.auth_header(uri, www_authenticate, "GET") +# +# http_headers = HTTP::Headers.new +# http_headers["Authorization"] = auth +# +# # re-issue request with Authorization +# response = client.get(uri.full_path, http_headers) +# ``` +class HTTP::Client::DigestAuth + class Error < Exception; end + + def initialize + @nonce_count = -1 + end + + # Creates a digest auth header for `uri` from the `www_authenticate` header + # for HTTP method `method`. + # + # The result of this method should be sent along with the HTTP request as + # the "Authorization" header. + # + # IIS servers handle the "qop" parameter of digest authentication + # differently so you may need to set `iis` to true for such servers. + def auth_header(uri : URI, www_authenticate : String, method : String, iis = false) : String + nonce_count = next_nonce + + user = uri.user + password = uri.password + + header = "" + params = {} of String => String + + if m = www_authenticate.match(/^(\w+) (.*)/) + challenge = m[2] + + challenge.scan(/(\w+)="(.*?)"/) do |m| + params[m[1]] = m[2] + end + + if m = challenge.match(/algorithm="?(.*?)"?([, ]|$)/) + params["algorithm"] = m[1] + else + params["algorithm"] = "MD5" + end + + if m = params["algorithm"].match(/(.*?)(-sess)?$/) + algorithm = choose_digest_algorithm(m[1]) + sess = m[2]? + end + + algorithm = algorithm.not_nil! + + qop = params["qop"]? + cnonce = make_cnonce if qop || sess + + a1 = + if sess + [ + hexdigest(algorithm, "#{user}:#{params["realm"]}:#{password}"), + params["nonce"], + cnonce, + ].join(":") + else + "#{user}:#{params["realm"]}:#{password}" + end + + ha1 = hexdigest(algorithm, a1) + ha2 = hexdigest(algorithm, "#{method}:#{uri.full_path}") + + request_digest = [] of String | Nil + request_digest.push(ha1, params["nonce"]) + request_digest.push(("%08x" % nonce_count), cnonce, qop) if qop + request_digest << ha2 + request_digest = request_digest.join(":") + + response_digest = hexdigest(algorithm, request_digest) + + header = [ + "Digest username=\"#{user}\"", + "realm=\"#{params["realm"]}\"", + "algorithm=#{params["algorithm"]}", + "uri=\"#{uri.full_path}\"", + "nonce=\"#{params["nonce"]}\"", + "response=\"#{response_digest}\"", + ] + + if qop + if iis + header << "qop=\"#{qop}\"" + else + header << "qop=#{qop}" + end + header << "nc=#{"%08x" % nonce_count}" + header << "cnonce=\"#{cnonce}\"" + else + end + + if params.has_key?("opaque") + header << "opaque=\"#{params["opaque"]}\"" + end + + header = header.compact.join(", ") + else + raise Error.new("Digest auth method not found or syntax error in auth header: #{www_authenticate}") + end + + header + end + + private def next_nonce + @nonce_count += 1 + end + + private def choose_digest_algorithm(algorithm : String) : String + case algorithm + when "MD5" then "MD5" + when "SHA1" then "SHA1" + when "SHA-256" then "SHA256" + when "SHA-512" then "SHA512" + else + raise Error.new("unknown algorithm \"#{algorithm}\"") + end + end + + private def hexdigest(algorithm, data) + digest = OpenSSL::Digest.new(algorithm) + digest << data + {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} + digest.final.hexstring + {% else %} + digest.hexdigest + {% end %} + end + + # Creates a client nonce value that is used across all requests based on the + # current time, process id and a random number + private def make_cnonce + Digest::MD5.hexdigest([ + Time.utc.to_unix, + Process.pid, + Random::Secure.rand(2_i64 ** 32), + ].join(":")) + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml b/samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml new file mode 100644 index 000000000000..02d3e3da9875 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml @@ -0,0 +1,49 @@ +name: Crystal CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + check_format: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Check format + run: crystal tool format --check + check_ameba: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Check ameba + run: ./bin/ameba + test_latest: + runs-on: ubuntu-latest + container: + image: crystallang/crystal + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Run tests + run: crystal spec + test_nightly: + runs-on: ubuntu-latest + container: + image: crystallang/crystal:nightly + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: shards install --ignore-crystal-version + - name: Run tests + run: crystal spec diff --git a/samples/client/petstore/crystal/lib/http_proxy/.gitignore b/samples/client/petstore/crystal/lib/http_proxy/.gitignore new file mode 100644 index 000000000000..40005bad64b5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/.gitignore @@ -0,0 +1,10 @@ +/doc/ +/lib/ +/bin/ +/.shards/ + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock + +/http_proxy diff --git a/samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md b/samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md new file mode 100644 index 000000000000..60566d72439b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md @@ -0,0 +1,66 @@ +# Changelog + +## 0.7.2 + +* Allow to set HTTP headers for proxy client ([#23](https://github.com/mamantoha/http_proxy/pull/23)) + +## 0.7.1 + +* Fixed compatibility with Crystal nightly + +## 0.7.0 + +* **(breaking-change)** Sending full URL instead of path when using HTTP proxy is not required + +## 0.6.0 + +* **(breaking-change)** Change `HTTP::Proxy::Server` implementation. + `HTTP::Proxy::Server.new` not require `host` and `port` parameters + +## 0.5.0 + +* Compatibility with Crystal 0.35.0 + +## 0.4.0 + +* Compatibility with Crystal 0.30 + +## 0.3.6 + +* Updates to Crystal 0.27 +* Refactor Proxy Client + +## 0.3.5 + +* Updates to Crystal 0.25 + +## 0.3.3 + +* Fixed an issue with HTTP resource when proxy server which initialized without handlers can't properly read a response + +## 0.3.2 + +* Fix compatibility with Kemal + +## 0.3.1 + +* minor bug fixes + +## 0.3.0 + +* Detach the fork and turn it into a standalone repository on GitHub +* Change shard name to `http_proxy` +* Add proxy client +* Add project to Travis CI + +## 0.2.1 + +* Bug fixes + +## 0.2.0 + +* First release after fork +* Change shard name to `http_proxy_server` +* Update to Crystal 0.23.0 +* New project structure +* Bug fixes and performance improvement diff --git a/samples/client/petstore/crystal/lib/http_proxy/LICENSE b/samples/client/petstore/crystal/lib/http_proxy/LICENSE new file mode 100644 index 000000000000..5d8ccaf773ae --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2018 Anton Maminov +Copyright (c) 2017 Theo Li + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/http_proxy/README.md b/samples/client/petstore/crystal/lib/http_proxy/README.md new file mode 100644 index 000000000000..4b726404018b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/README.md @@ -0,0 +1,135 @@ +# HTTP::Proxy + +![Crystal CI](https://github.com/mamantoha/http_proxy/workflows/Crystal%20CI/badge.svg) +[![GitHub release](https://img.shields.io/github/release/mamantoha/http_proxy.svg)](https://github.com/mamantoha/http_proxy/releases) +[![License](https://img.shields.io/github/license/mamantoha/http_proxy.svg)](https://github.com/mamantoha/http_proxy/blob/master/LICENSE) + +A HTTP Proxy server and client written in Crystal + +## Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + http_proxy: + github: mamantoha/http_proxy +``` + +## Usage + +### Server + +```crystal +require "http_proxy" + +host = "127.0.0.1" +port = 8080 + +server = HTTP::Proxy::Server.new + +address = server.bind_tcp(host, port) +puts "Listening on http://#{address}" +server.listen +``` + +```crystal +require "http_proxy" +require "option_parser" + +host = "192.168.0.1" +port = 3128 + +OptionParser.parse! do |opts| + opts.on("-h HOST", "--host HOST", "define host to run server") do |opt| + host = opt + end + + opts.on("-p PORT", "--port PORT", "define port to run server") do |opt| + port = opt.to_i + end +end + +server = HTTP::Proxy::Server.new(handlers: [ + HTTP::LogHandler.new, +]) do |context| + context.perform +end + +address = server.bind_tcp(host, port) +puts "Listening on http://#{address}" +server.listen +``` + +#### Basic Authentication + +```crystal +server = HTTP::Proxy::Server.new(handlers: [ + HTTP::LogHandler.new, + HTTP::Proxy::Server::BasicAuth.new("user", "passwd"), +]) do |context| + context.request.headers.add("X-Forwarded-For", "127.0.0.1") + context.perform +end +``` + +### Client + +#### Make request with proxy + +```crystal +require "http_proxy" + +proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080) + +uri = URI.parse("http://httpbin.org") +client = HTTP::Client.new(uri) +client.set_proxy(proxy_client) +response = client.get("/get") +``` + +#### Client Authentication + +```crystal +uri = URI.parse("https://httpbin.org") +proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "passwd") + +response = HTTP::Client.new(uri) do |client| + client.set_proxy(proxy_client) + client.get("/get") +end + +puts response.status_code +puts response.body +``` + +## Development + +### Proxy server + +* [x] Basic HTTP Proxy: GET, POST, PUT, DELETE support +* [x] Basic HTTP Proxy: OPTIONS support +* [x] HTTPS Proxy: CONNECT support +* [x] Make context.request & context.response writable +* [x] Basic Authentication +* [ ] MITM HTTPS Proxy + +### Proxy client + +* [x] Basic HTTP Proxy: GET, POST, PUT, DELETE support +* [x] Basic HTTP Proxy: OPTIONS support +* [x] HTTPS Proxy: CONNECT support +* [x] Basic Authentication + +## Contributing + +1. Fork it () +2. Create your feature branch (git checkout -b my-new-feature) +3. Commit your changes (git commit -am 'Add some feature') +4. Push to the branch (git push origin my-new-feature) +5. Create a new Pull Request + +## Contributors + +* [bbtfr](https://github.com/bbtfr) Theo Li - creator, maintainer +* [mamantoha](https://github.com/mamantoha) Anton Maminov - maintainer diff --git a/samples/client/petstore/crystal/lib/http_proxy/lib b/samples/client/petstore/crystal/lib/http_proxy/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/client.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/client.cr new file mode 100644 index 000000000000..0721d0373459 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/samples/client.cr @@ -0,0 +1,40 @@ +require "../src/http_proxy" +require "option_parser" + +host = "127.0.0.1" +port = 8080 + +OptionParser.parse! do |opts| + opts.on("-h HOST", "--host HOST", "define host") do |opt| + host = opt + end + + opts.on("-p PORT", "--port PORT", "define port") do |opt| + port = opt.to_i + end +end + +proxy_client = HTTP::Proxy::Client.new(host, port) + +puts "Make HTTPs request w/o proxy" +uri = URI.parse("https://httpbin.org") +client = HTTP::Client.new(uri) +response = client.get("/get") +puts response.status_code +puts response.body + +puts "Make HTTPS request" +uri = URI.parse("https://httpbin.org") +client = HTTP::Client.new(uri) +client.set_proxy(proxy_client) +response = client.get("/get") +puts response.status_code +puts response.body + +puts "Make HTTP request" +uri = URI.parse("http://httpbin.org") +client = HTTP::Client.new(uri) +client.set_proxy(proxy_client) +response = client.get("http://httpbin.org/get") +puts response.status_code +puts response.body diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/server.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/server.cr new file mode 100644 index 000000000000..de5de05bffdd --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/samples/server.cr @@ -0,0 +1,23 @@ +require "../src/http_proxy" +require "option_parser" + +host = "127.0.0.1" +port = 8080 + +OptionParser.parse do |opts| + opts.on("-h HOST", "--host HOST", "define host to run server") do |opt| + host = opt + end + + opts.on("-p PORT", "--port PORT", "define port to run server") do |opt| + port = opt.to_i + end +end + +server = HTTP::Proxy::Server.new(handlers: [ + HTTP::LogHandler.new, +]) + +address = server.bind_tcp(host, port) +puts "Listening on http://#{address}" +server.listen diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr new file mode 100644 index 000000000000..227c65868c74 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr @@ -0,0 +1,38 @@ +require "../src/http_proxy" +require "option_parser" + +host = "127.0.0.1" +port = 8080 +username = "user" +password = "1234" + +OptionParser.parse do |opts| + opts.on("-h HOST", "--host HOST", "define host to run server") do |opt| + host = opt + end + + opts.on("-p PORT", "--port PORT", "define port to run server") do |opt| + port = opt.to_i + end + + opts.on("-u USER", "--user USER", "define user for authentication") do |opt| + username = opt + end + + opts.on("--pass PASSWORD", "define password for authentication") do |opt| + password = opt + end +end + +server = HTTP::Proxy::Server.new(handlers: [ + HTTP::LogHandler.new, + HTTP::Proxy::Server::BasicAuth.new(username, password), +]) do |context| + context.request.headers.add("X-Forwarded-For", host) + context.perform +end + +address = server.bind_tcp(host, port) +puts "Listening on http://#{address}" +puts "Use #{username}:#{password} for authentication" +server.listen diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr new file mode 100644 index 000000000000..6e99f05c132b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr @@ -0,0 +1,60 @@ +require "../src/http_proxy" + +def with_proxy_server(host = "127.0.0.1", port = 8080) + wants_close = Channel(Nil).new + + server = HTTP::Proxy::Server.new(handlers: [ + HTTP::LogHandler.new, + HTTP::Proxy::Server::BasicAuth.new("user", "passwd"), + ]) do |context| + context.request.headers.add("X-Forwarded-For", host) + end + + spawn do + server.bind_tcp(host, port) + puts "start proxy server" + server.listen + end + + spawn do + wants_close.receive + puts "exit proxy server" + server.close + end + + Fiber.yield + + yield host, port, wants_close +end + +with_proxy_server do |_host, _port, wants_close| + puts "start proxy client" + + puts "HTTP Request:" + uri = URI.parse("http://httpbin.org") + proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "passwd") + + response = HTTP::Client.new(uri) do |client| + client.set_proxy(proxy_client) + client.get("http://httpbin.org/get") + end + + puts response.status_code + puts response.body + + puts "HTTPS Request:" + uri = URI.parse("https://httpbin.org") + proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "passwd") + + response = HTTP::Client.new(uri) do |client| + client.set_proxy(proxy_client) + client.get("/get") + end + + puts response.status_code + puts response.body + + puts "exit proxy client" +ensure + wants_close.send(nil) +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/shard.yml b/samples/client/petstore/crystal/lib/http_proxy/shard.yml new file mode 100644 index 000000000000..f41e1b723b6f --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/shard.yml @@ -0,0 +1,14 @@ +name: http_proxy +version: 0.7.2 + +authors: + - Theo Li + - Anton Maminov + +crystal: ">= 0.35.0" + +development_dependencies: + ameba: + github: crystal-ameba/ameba + +license: MIT diff --git a/samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr b/samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr new file mode 100644 index 000000000000..ae541dd4dd3b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr @@ -0,0 +1,53 @@ +require "./spec_helper" + +describe HTTP::Proxy::Client do + describe "#initialize" do + it "with host and port" do + client = HTTP::Proxy::Client.new("127.0.0.1", 8080) + (client.host).should eq("127.0.0.1") + (client.port).should eq(8080) + (client.username).should eq(nil) + (client.password).should eq(nil) + end + + it "with username and password" do + client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "password") + (client.username).should eq("user") + (client.password).should eq("password") + end + + context "HTTP::Client#set_proxy" do + it "should make HTTP request with proxy" do + with_proxy_server do |host, port, wants_close| + proxy_client = HTTP::Proxy::Client.new(host, port) + + uri = URI.parse("http://httpbin.org") + client = HTTP::Client.new(uri) + client.set_proxy(proxy_client) + response = client.get("/get") + + (client.proxy?).should eq(true) + (response.status_code).should eq(200) + ensure + wants_close.send(nil) + end + end + + it "should make HTTPS request with proxy" do + with_proxy_server do |host, port, wants_close| + proxy_client = HTTP::Proxy::Client.new(host, port) + + uri = URI.parse("https://httpbin.org") + client = HTTP::Client.new(uri) + client.set_proxy(proxy_client) + response = client.get("/get") + + (client.proxy?).should eq(true) + (response.status_code).should eq(200) + ensure + wants_close.send(nil) + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr b/samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr new file mode 100644 index 000000000000..9b00efdb67d4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr @@ -0,0 +1,17 @@ +require "./spec_helper" + +describe HTTP::Proxy::Server do + describe "#initialize" do + it "with params" do + server = HTTP::Proxy::Server.new + server.should be_a(HTTP::Proxy::Server) + end + + it "with BasicAuth handler" do + server = HTTP::Proxy::Server.new([ + HTTP::Proxy::Server::BasicAuth.new("user", "passwd"), + ]) + server.should be_a(HTTP::Proxy::Server) + end + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr new file mode 100644 index 000000000000..98a174ae6820 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr @@ -0,0 +1,27 @@ +require "spec" +require "../src/http_proxy" + +describe HTTP::Proxy do + it "should have version" do + (HTTP::Proxy::VERSION).should_not be_nil + end +end + +def with_proxy_server(host = "127.0.0.1", port = 8080) + wants_close = Channel(Nil).new + server = HTTP::Proxy::Server.new + + spawn do + server.bind_tcp(host, port) + server.listen + end + + spawn do + wants_close.receive + server.close + end + + Fiber.yield + + yield host, port, wants_close +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr new file mode 100644 index 000000000000..f28de7bc0e1f --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr @@ -0,0 +1,176 @@ +require "http" +require "socket" +require "base64" + +{% if !flag?(:without_openssl) %} + require "openssl" +{% end %} + +module HTTP + # :nodoc: + module Proxy + # Represents a proxy client with all its attributes. + # Provides convenient access and modification of them. + class Client + getter host : String + getter port : Int32 + property username : String? + property password : String? + property headers : HTTP::Headers + + getter tls : OpenSSL::SSL::Context::Client? + + @dns_timeout : Float64? + @connect_timeout : Float64? + @read_timeout : Float64? + + record Response, + version : String, + code : Int32, + reason : String, + headers = {} of String => String + + # Creates a new socket factory that tunnels via the given host and port. + # The following optional arguments are supported: + # + # * :headers - additional headers, which will be used for tls connections, which is useful to supply a User-Agent header + # * :username - the user name to use when authenticating to the proxy + # * :password - the password to use when authenticating + def initialize(@host, @port, *, headers : HTTP::Headers? = nil, @username = nil, @password = nil) + @headers = headers || HTTP::Headers.new + @headers["User-Agent"] ||= "Crystal, HTTP::Proxy/#{HTTP::Proxy::VERSION}" + end + + # Returns a new socket connected to the given host and port via the + # proxy that was requested when the socket factory was instantiated. + def open(host, port, tls = nil, *, @dns_timeout, @connect_timeout, @read_timeout) + socket = TCPSocket.new(@host, @port, @dns_timeout, @connect_timeout) + socket.read_timeout = @read_timeout if @read_timeout + socket.sync = true + + if tls + socket << "CONNECT #{host}:#{port} HTTP/1.1\r\n" + @headers.each do |name, values| + values.each do |value| + socket << "#{name}: #{value}\r\n" + end + end + socket << "Host: #{host}:#{port}\r\n" + + if @username + credentials = Base64.strict_encode("#{@username}:#{@password}") + credentials = "#{credentials}\n".gsub(/\s/, "") + socket << "Proxy-Authorization: Basic #{credentials}\r\n" + end + + socket << "\r\n" + + resp = parse_response(socket) + + if resp.code == 200 + {% if !flag?(:without_openssl) %} + if tls + tls_socket = OpenSSL::SSL::Socket::Client.new(socket, context: tls, sync_close: true, hostname: host) + socket = tls_socket + end + {% end %} + + return socket + else + socket.close + raise IO::Error.new(resp.inspect) + end + end + + socket + end + + private def parse_response(socket) : Response? + version, code, reason = socket.gets.as(String).chomp.split(/ /, 3) + + headers = {} of String => String + + while (line = socket.gets.as(String)) && (line.chomp != "") + name, value = line.split(/:/, 2) + headers[name.strip] = value.strip + end + + Response.new(version, code.to_i, reason, headers) + end + end + end + + class Client + getter proxy : Bool = false + + def set_proxy(proxy : HTTP::Proxy::Client?) + return unless proxy + + begin + {% if compare_versions(Crystal::VERSION, "1.0.0-0") >= 0 %} + @io = proxy.open( + host: @host, + port: @port, + tls: @tls, + dns_timeout: @dns_timeout, + connect_timeout: @connect_timeout, + read_timeout: @read_timeout + ) + {% else %} + @socket = proxy.open( + host: @host, + port: @port, + tls: @tls, + dns_timeout: @dns_timeout, + connect_timeout: @connect_timeout, + read_timeout: @read_timeout + ) + {% end %} + rescue ex : IO::Error + raise IO::Error.new("Failed to open TCP connection to #{@host}:#{@port} (#{ex.message})") + end + + @proxy = true + + if proxy.username && proxy.password + proxy_basic_auth(proxy.username, proxy.password) + end + + {% if compare_versions(Crystal::VERSION, "1.0.0-0") >= 0 %} + @io + {% else %} + @socket + {% end %} + end + + # True if requests for this connection will be proxied + def proxy? + @proxy + end + + private def new_request(method, path, headers, body : BodyType) + # Use full URL instead of path when using HTTP proxy + if proxy? && !@tls + path = "http://#{host_with_port}#{path}" + end + + HTTP::Request.new(method, path, headers, body) + end + + private def host_with_port + host = @host + host = "[#{host}]" if host.includes?(":") + default_port = @tls ? URI.default_port("https") : URI.default_port("http") + default_port == @port ? host : "#{host}:#{@port}" + end + + # Configures this client to perform proxy basic authentication in every + # request. + private def proxy_basic_auth(username : String?, password : String?) + header = "Basic #{Base64.strict_encode("#{username}:#{password}")}" + before_request do |request| + request.headers["Proxy-Authorization"] = header + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr new file mode 100644 index 000000000000..b05f7d9c8b25 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr @@ -0,0 +1,53 @@ +require "./server/handler" +require "./server/basic_auth" + +# A concurrent Proxy server implementation. +# +# ``` +# require "http_proxy" +# +# server = HTTP::Proxy::Server.new +# address = server.bind_tcp("127.0.0.1", 8080) +# puts "Listening on http://#{address}" +# server.listen +# ``` +# +class HTTP::Proxy::Server + def initialize + handler = build_middleware + @processor = RequestProcessor.new(handler) + end + + def initialize(&handler : Context ->) + @processor = RequestProcessor.new(handler) + end + + def initialize(handlers : Array(HTTP::Handler), &handler : Context ->) + handler = build_middleware(handlers, handler) + @processor = RequestProcessor.new(handler) + end + + def initialize(handlers : Array(HTTP::Handler)) + handler = build_middleware(handlers) + @processor = RequestProcessor.new(handler) + end + + def initialize(handler : HTTP::Handler | HTTP::Handler::HandlerProc) + @processor = RequestProcessor.new(handler) + end + + private def build_middleware(handler : (Context ->)? = nil) + proxy_handler = Handler.new + proxy_handler.next = handler if handler + proxy_handler + end + + private def build_middleware(handlers, last_handler : (Context ->)? = nil) + proxy_handler = build_middleware(last_handler) + return proxy_handler if handlers.empty? + + 0.upto(handlers.size - 2) { |i| handlers[i].next = handlers[i + 1] } + handlers.last.next = proxy_handler if proxy_handler + handlers.first + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr new file mode 100644 index 000000000000..e9a3fcb8b8fb --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr @@ -0,0 +1,27 @@ +class HTTP::Proxy::Server + class BasicAuth + include HTTP::Handler + + def initialize(@username : String, @password : String) + end + + def call(context) + if context.request.headers.has_key?("Proxy-Authorization") + _, enc = context.request.headers["Proxy-Authorization"].split + username, password = Base64.decode_string(enc).split(":") + if authorized?(username, password) + context.request.headers.delete("Proxy-Authorization") + return call_next(context) + end + end + + context.response.headers.add("Proxy-Authenticate", "Basic realm=\"hello\"") + context.response.status_code = 407 + context.response.puts("") + end + + private def authorized?(username, password) + username == @username && password == @password + end + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr new file mode 100644 index 000000000000..d9cf8367aac7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr @@ -0,0 +1,59 @@ +class HTTP::Proxy::Server < HTTP::Server + class Context < HTTP::Server::Context + def perform + case @request.method + when "OPTIONS" + @response.headers["Allow"] = "OPTIONS,GET,HEAD,POST,PUT,DELETE,CONNECT" + when "CONNECT" + handle_tunneling + else + handle_http + end + end + + private def handle_tunneling + host, port = @request.resource.split(":", 2) + upstream = TCPSocket.new(host, port) + + @response.upgrade do |downstream| + channel = Channel(Nil).new(2) + + downstream = downstream.as(TCPSocket) + downstream.sync = true + + spawn do + transfer(upstream, downstream, channel) + transfer(downstream, upstream, channel) + end + + 2.times { channel.receive } + end + end + + private def transfer(destination, source, channel) + spawn do + IO.copy(destination, source) + rescue ex + Log.error(exception: ex) { "Unhandled exception on HTTP::Proxy::Server::Context" } + ensure + channel.send(nil) + end + end + + private def handle_http + uri = URI.parse(@request.resource) + client = HTTP::Client.new(uri) + + @request.headers.delete("Accept-Encoding") + + response = client.exec(@request) + + response.headers.delete("Transfer-Encoding") + response.headers.delete("Content-Encoding") + + @response.headers.merge!(response.headers) + @response.status_code = response.status_code + @response.puts(response.body) + end + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr new file mode 100644 index 000000000000..de078cb1575b --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr @@ -0,0 +1,17 @@ +require "./context" + +class HTTP::Proxy::Server::Handler + include HTTP::Handler + + property next : HTTP::Handler | Proc | Nil + + alias Proc = Context -> + + def call(context) + request = context.request + response = context.response + context = Context.new(request, response) + + context.perform + end +end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr new file mode 100644 index 000000000000..edc48854791a --- /dev/null +++ b/samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr @@ -0,0 +1,12 @@ +require "http" +require "socket" +require "base64" + +require "./http/proxy/server" +require "./http/proxy/client" + +module HTTP + module Proxy + VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/.ameba.yml b/samples/client/petstore/crystal/lib/kemal/.ameba.yml new file mode 100644 index 000000000000..9cc083d6f85e --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/.ameba.yml @@ -0,0 +1,13 @@ +# This configuration file was generated by `ameba --gen-config` +# on 2019-08-25 09:29:24 UTC using Ameba version 0.10.0. +# 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: 7 +# Run `ameba --only Lint/UselessAssign` for details +Lint/UselessAssign: + Description: Disallows useless variable assignments + Enabled: true + Severity: Warning + Excluded: + - spec/view_spec.cr diff --git a/samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml b/samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml new file mode 100644 index 000000000000..3452e4af2f91 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml @@ -0,0 +1,8 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: sdogruyol +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +custom: # Replace with a single custom sponsorship URL diff --git a/samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md b/samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000000..42a81d927ad7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,23 @@ +### Description + +[Description of the issue] + +### Steps to Reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** [What you expect to happen] + +**Actual behavior:** [What actually happens] + +**Reproduces how often:** [What percentage of the time does it reproduce?] + +### Versions + +You can get this information from copy and pasting the output of `crystal --version`.Also, please include the OS and what version of the OS you're running. + +### Additional Information + +Any additional information, configuration or data that might be necessary to reproduce the issue. diff --git a/samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md b/samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..c97ebc981cb5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +### Description of the Change + + + +### Alternate Designs + + + +### Benefits + + + +### Possible Drawbacks + + diff --git a/samples/client/petstore/crystal/lib/kemal/.gitignore b/samples/client/petstore/crystal/lib/kemal/.gitignore new file mode 100644 index 000000000000..6c364e446777 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/.gitignore @@ -0,0 +1,8 @@ +/lib/ +/.crystal/ +/.shards/ +*.log +/bin/ +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/.travis.yml b/samples/client/petstore/crystal/lib/kemal/.travis.yml new file mode 100644 index 000000000000..9883f5e9ef49 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/.travis.yml @@ -0,0 +1,14 @@ +language: crystal +crystal: + - latest + - nightly + +script: + - crystal spec + - crystal spec --release --no-debug + - crystal tool format --check + - bin/ameba src + +matrix: + allow_failures: + - crystal: nightly diff --git a/samples/client/petstore/crystal/lib/kemal/CHANGELOG.md b/samples/client/petstore/crystal/lib/kemal/CHANGELOG.md new file mode 100644 index 000000000000..48d21fa8a3df --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/CHANGELOG.md @@ -0,0 +1,385 @@ +# 0.27.0 (28-11-2020) + +- Crystal 0.35.x support :tada: Thanks @bcardiff :pray: +- Fix issues with responding with long strings [#576](https://github.com/kemalcr/kemal/pull/576). Thanks @mamantoha :pray: +- Fix broken WebSocket support in 0.35.0 [#577](https://github.com/kemalcr/kemal/pull/577). Thanks @mamantoha :pray: +- Allow to set optional response body on redirects [#561](https://github.com/kemalcr/kemal/pull/561). Thanks @mamantoha :pray: + +# 0.26.1 (01-12-2019) + +- Fix process request when a response already closed [#550](https://github.com/kemalcr/kemal/pull/550). Thanks @mamantoha :pray: +- Switch to new Ameba repository [#549](https://github.com/kemalcr/kemal/pull/549). Thanks @mamantoha :pray: +- Check for `KEMAL_ENV` variable already in `Config#initialize`[#552](https://github.com/kemalcr/kemal/pull/552). Thanks @Sija :pray: +- Cleanup Ameba warnings [#551](https://github.com/kemalcr/kemal/pull/551). Thanks @Sija :pray: +- Flush io buffer after each write to log [#554](https://github.com/kemalcr/kemal/pull/554). Thanks @mang :pray: + +# 0.26.0 (05-08-2019) + +- Crystal 0.30.0 support :tada: [#548](https://github.com/kemalcr/kemal/pull/548) and [#544](https://github.com/kemalcr/kemal/pull/544). Thanks @bcardiff and @straight-shoota :pray: +- Add support for serving files greater than 2^31 bytes [#546](https://github.com/kemalcr/kemal/pull/546). Thanks @omarroth :pray: +- Properly measure request time using `Time.monotonic` [#527](https://github.com/kemalcr/kemal/pull/527). Thanks @spinscale :pray: + +# 0.25.2 (08-02-2019) + +- Add option to config to parse or not command line parameters [#483](https://github.com/kemalcr/kemal/pull/483). Thanks @diegogub :pray: + +- Allow to set filename for `send_file` [#512](https://github.com/kemalcr/kemal/pull/512). Thanks @mamantoha :pray: + + +```ruby +send_file env, "./asset/image.jpeg", filename: "image.jpg" +``` + +- Set `status_code` before response [#513](https://github.com/kemalcr/kemal/pull/513). Thanks @mamantohoa :pray: + +- Use Crystal MIME registry. [#516](https://github.com/kemalcr/kemal/pull/516) Thanks @Sija :pray: + +# 0.25.1 (06-10-2018) + +- Fix `params.files` memoization https://github.com/kemalcr/kemal/pull/503. Thanks @mamantoha :pray: + +# 0.25.0 (05-10-2018) + +- Crystal 0.27.0 support. +- *[breaking change]* Added back `env.params.files`. + +Here's a fully working sample for reading a image file upload `image1` and saving it under `public/uploads`. + +```crystal +post "/upload" do |env| + file = env.params.files["image1"].tempfile + file_path = ::File.join [Kemal.config.public_folder, "uploads/", File.basename(file.path)] + File.open(file_path, "w") do |f| + IO.copy(file, f) + end + "Upload ok" +end +``` + +To test + +`curl -F "image1=@/Users/serdar/Downloads/kemal.png" http://localhost:3000/upload` + +- Cache HTTP routes to increase performance :rocket: https://github.com/kemalcr/kemal/pull/493 + +# 0.24.0 (14-08-2018) + +- *[breaking change]* Removed `env.params.files`. You can use Crystal's built-in `HTTP::FormData.parse` instead + +```ruby +post "/upload" do |env| + HTTP::FormData.parse(env.request) do |upload| + filename = file.filename + + if !filename.is_a?(String) + "No filename included in upload" + else + file_path = ::File.join [Kemal.config.public_folder, "uploads/", filename] + File.open(file_path, "w") do |f| + IO.copy(file.tmpfile, f) + end + "Upload OK" + end +end +``` + +- *[breaking change]* From now on to access dynamic url params in a WebSocket route you have to use: + +```ruby +ws "/:id" do |socket, context| + id = context.ws_route_lookup.params["id"] +end +``` + +- *[breaking change]* Removed `_method` magic param. + +- Added new exception page [#466](https://github.com/kemalcr/kemal/pull/466). Thanks @mamantoha 🙏 + +- Support custom port binding. Thanks @straight-shoota 🙏 + +```ruby +Kemal.run do |config| + server = config.server.not_nil! + server.bind_tcp "127.0.0.1", 3000, reuse_port: true + server.bind_tcp "0.0.0.0", 3001, reuse_port: true +end +``` + +# 0.23.0 (17-06-2018) + +- Crystal 0.25.0 support 🎉 +- Add `Kemal::Context.get?` to safely access context storage :sunglasses: +- [Security] Don't serve 404 image dynamically :thumbsup: +- Disable `X-Powered-By` header [#449](https://github.com/kemalcr/kemal/pull/449). Thanks @Blacksmoke16 🙏 + +# 0.22.0 (29-12-2017) + +- Crystal 0.24.1 support 🎉 +- Only return string from route.[#408](https://github.com/kemalcr/kemal/pull/408) thanks @crisward 🙏 +- Don't crash on empty path when compiled in --release. [#407](https://github.com/kemalcr/kemal/pull/407) thanks @crisward 🙏 +- Rename `Kemal::CommonLogHandler` to `Kemal::LogHandler` and `Kemal::CommonExceptionHandler` to `Kemal::ExceptionHandler`. +- Allow videos to be opened with correct mime type. [#406](https://github.com/kemalcr/kemal/pull/406) thanks @crisward 🙏 +- Add webm mime type.[#413](https://github.com/kemalcr/kemal/pull/413) thanks @reindeer-cafe 🙏 + + +# 0.21.0 (05-09-2017) + +- Dynamically insert handlers :muscle: Fixes [#376](https://github.com/kemalcr/kemal/pull/376). +- Add context to WebSocket. This allows one to use `HTTP::Server::Context` in `ws` declarations :heart_eyes: Fixes [#349](https://github.com/kemalcr/kemal/pull/349). + +```ruby +ws "/:room_name" do |socket, env| + env.params.url["room_name"] +end +``` + +- Add support for customizing the headers of built-in `Kemal::StaticFileHandler` :hammer: Useful for supporting `CORS` for single page applications :clap: + +```ruby +static_headers do |response, filepath, filestat| + if filepath =~ /\.html$/ + response.headers.add("Access-Control-Allow-Origin", "*") + end + response.headers.add("Content-Size", filestat.size.to_s) + end +end +``` + +- Allow %w in Handler macros [#385](https://github.com/kemalcr/kemal/pull/385). Thanks @will :pray: + +- Security: X-Content-Type-Options: nosniff for static files. Fixes [#379](https://github.com/kemalcr/kemal/issues/379). Thanks @crisward :pray: + +- Performance: [Remove tempfile management to OS](https://github.com/kemalcr/kemal/commit/a1520de7ed3865fa73258343a80fad4f20666a99). This brings %10 - 15 performance boost to Kemal :rocket: + +# 0.20.0 (01-07-2017) + +- Crystal 0.23.0 support! As always, Kemal is compatible with the latest major release of Crystal 💎 +- Great news everyone 🎉 All handlers are now completely ***customizable***!. Use the default `Kemal` handlers or go wild, it's all up to you ⛏ + +```ruby +# Don't forget to add `Kemal::RouteHandler::INSTANCE` or your routes won't work! +Kemal.config.handlers = [Kemal::InitHandler.new, YourHandler.new, Kemal::RouteHandler::INSTANCE] +``` + +You can also insert a handler into a specific position. + +```ruby +# This adds MyCustomHandler instance to 1 position. Be aware that the index starts from 0. +add_handler MyCustomHandler.new, 1 +``` +- Updated [Kilt](https://github.com/jeromegn/kilt) to v0.4.0. +- Make `Route` a `Struct`. This improves the performance of route declarations. + +# 0.19.0 (09-05-2017) + +- Return no body for head route fixes #323. (thanks @crisward) +- Update `radix` to `0.3.8`. (thanks @waghanza) +- User defined context store types. (thanks @neovitange) + +```ruby +class User + property name +end + +add_context_storage_type(User) +``` + +- Prevent `send_file returning filesize. (thanks @crisward) +- Dont call setup in `config#add_filter_handler` fixes #338. + +# 0.18.3 (07-03-2017) + +- Remove `Gzip::Header` monkey patch since it's fixed in `Crystal 0.21.1`. + +# 0.18.2 (24-02-2017) + +- Fix [Gzip in Kemal Seems broken for static files](https://github.com/kemalcr/kemal/issues/316). This was caused by `Gzip::Writer` in `Crystal 0.21.0` and currently mitigated by monkey patching `Gzip::Header`. + +# 0.18.1 (21-02-2017) + +- Crystal 0.21.0 support +- Drop `multipart.cr` dependency. `multipart` support is now built-into Crystal <3 +- Since Crystal 0.21.0 comes built-in with `multipart` there are some improvements and deprecations. + +`meta` has been removed from `FileUpload` and it has the following properties + + + `tmpfile`: This is temporary file for file upload. Useful for saving the upload file. + + `filename`: File name of the file upload. (logo.png, images.zip e.g) + + `headers`: Headers for the file upload. + + `creation_time`: Creation time of the file upload. + + `modification_time`: Last Modification time of the file upload. + + `read_time`: Read time of the file upload. + + `size`: Size of the file upload. + + +# 0.18.0 (11-02-2017) + +- Simpler file upload. File uploads can now be access from `HTTP::Server::Context` like `env.params.files["filename"]`. + +`env.params.files["filename"]` has 5 methods + +- `tmpfile`: This is temporary file for file upload. Useful for saving the upload file. +- `tmpfile_path`: File path of `tmpfile`. +- `filename`: File name of the file upload. (logo.png, images.zip e.g) +- `meta`: Meta information for the file upload. +- `headers`: Headers for the file upload. + +Here's a fully working sample for reading a image file upload `image1` and saving it under `public/uploads`. + + ```crystal +post "/upload" do |env| + file = env.params.files["image1"].tmpfile + file_path = ::File.join [Kemal.config.public_folder, "uploads/", file.filename] + File.open(file_path, "w") do |f| + IO.copy(file, f) + end + "Upload ok" +end + ``` + +To test + +`curl -F "image1=@/Users/serdar/Downloads/kemal.png" http://localhost:3000/upload` + +- RF7233 support a.k.a file streaming. (https://github.com/kemalcr/kemal/pull/299) (thanks @denysvitali) + +- Update Radix to 0.3.7. Fixes https://github.com/kemalcr/kemal/issues/293 +- Configurable startup / shutdown logging. https://github.com/kemalcr/kemal/issues/291 and https://github.com/kemalcr/kemal/issues/292 (thanks @twisterghost). + +# 0.17.5 (09-01-2017) + +- Update multipart.cr to 0.1.2. Fixes #285 related to multipart.cr + +# 0.17.4 (24-12-2016) + +- Support for Crystal 0.20.3 +- Add `Kemal.stop`. Fixes #269. +- `HTTP::Handler` is not a class anymore, it's a module. See https://github.com/crystal-lang/crystal/releases/tag/0.20.3 + +# 0.17.3 (03-12-2016) + +- Handle missing 404 image. Fixes #263 +- Remove basic auth middleware from core and move to [kemalcr/kemal-basic-auth](https://github.com/kemalcr/kemal-basic-auth). + +# 0.17.2 (25-11-2016) + +- Use body.gets_to_end for parse_json. Fixes #260. +- Update Radix to 0.3.5 and lock pessimistically. (thanks @luislavena) + +# 0.17.1 (24-11-2016) + +- Treat `HTTP::Request` body as an `IO`. Fixes [#257](https://github.com/sdogruyol/kemal/issues/257) + +# 0.17.0 (23-11-2016) + +- Reimplemented Request middleware / filter routing. + +Now all requests will first go through the Middleware stack then Filters (before_*) and will finally reach the matching route. + +Which is illustrated as, + +``` +Request -> Middleware -> Filter -> Route +``` + +- Rename `return_with` as `halt`. +- Route declaration must start with `/`. Fixes [#242](https://github.com/sdogruyol/kemal/issues/242) +- Set default exception Content-Type to text/html. Fixes [#202](https://github.com/sdogruyol/kemal/issues/242) +- Add `only` and `exclude` paths for `Kemal::Handler`. This change requires that all handlers must inherit from `Kemal::Handler`. + +For example this handler will only work on `/` path. By default the HTTP method is `GET`. + + +```crystal +class OnlyHandler < Kemal::Handler + only ["/"] + + def call(env) + return call_next(env) unless only_match?(env) + puts "If the path is / i will be doing some processing here." + end +end +``` + +The handlers using `exclude` will work on the paths that isn't specified. For example this handler will work on any routes other than `/`. + +```crystal +class ExcludeHandler < Kemal::Handler + exclude ["/"] + + def call(env) + return call_next(env) unless only_match?(env) + puts "If the path is NOT / i will be doing some processing here." + end +end +``` + +- Close response on `halt`. (thanks @samueleaton). +- Update `Radix` to `v0.3.4`. +- `error` handler now also yields error. For example you can get the error mesasage like + +```crystal + error 500 do |env, err| + err.message + end +``` + +- Update `multipart.cr` to `v0.1.1` + +# 0.16.1 (12-10-2016) + +- Improved Multipart support with more info on parsed files. `parse_multipart(env)` now yields +an `UploadFile` object which has the following properties `field`,`data`,`meta`,`headers. + +```crystal +post "/upload" do |env| + parse_multipart(env) do |f| + image1 = f.data if f.field == "image1" + image2 = f.data if f.field == "image2" + puts f.meta + puts f.headers + "Upload complete" + end +end +``` + +# 0.16.0 + +- Multipart support <3 (thanks @RX14). Now you can handle file uploads. + +```crystal +post "/upload" do |env| + parse_multipart(env) do |field, data| + image1 = data if field == "image1" + image2 = data if field == "image2" + "Upload complete" + end +end +``` + +- Make session configurable. Now you can specify session name and expire time wit + +```crystal +Kemal.config.session["name"] = "your_app" +Kemal.config.session["expire_time"] = 48.hours +``` + +- Session now supports more types. (String, Int32, Float64, Bool) +- Add `gzip` helper to enable / disable gzip compression on responses. +- Static file caching with etag and gzip (thanks @crisward) +- `Kemal.run` now accepts port to listen. + +# 0.15.1 (05-09-2016) + +- Don't forget to call_next on NullLogHandler + +# 0.15.0 (03-09-2016) + +- Add context store +- `KEMAL_ENV` respects to `Kemal.config.env` and needs to be explicitly set. +- `Kemal::InitHandler` is introduced. Adds initial configuration, headers like `X-Powered-By`. +- Add `send_file` to helpers. +- Add mime types. +- Fix parsing JSON params when "charset" is present in "Content-Type" header. +- Use http-only cookie for session +- Inject STDOUT by default in CommonLogHandler diff --git a/samples/client/petstore/crystal/lib/kemal/LICENSE b/samples/client/petstore/crystal/lib/kemal/LICENSE new file mode 100644 index 000000000000..4b1bea44c498 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Serdar Doğruyol + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE diff --git a/samples/client/petstore/crystal/lib/kemal/README.md b/samples/client/petstore/crystal/lib/kemal/README.md new file mode 100644 index 000000000000..068cc25a3850 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/README.md @@ -0,0 +1,67 @@ + +[![Kemal](https://avatars3.githubusercontent.com/u/15321198?v=3&s=200)](http://kemalcr.com) + +# Kemal + +Lightning Fast, Super Simple web framework. + +[![Build Status](https://travis-ci.org/kemalcr/kemal.svg?branch=master)](https://travis-ci.org/kemalcr/kemal) +[![Join the chat at https://gitter.im/sdogruyol/kemal](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sdogruyol/kemal?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +# Super Simple ⚡️ + +```ruby +require "kemal" + +# Matches GET "http://host:port/" +get "/" do + "Hello World!" +end + +# Creates a WebSocket handler. +# Matches "ws://host:port/socket" +ws "/socket" do |socket| + socket.send "Hello from Kemal!" +end + +Kemal.run +``` + +Start your application! + +``` +crystal src/kemal_sample.cr +``` +Go to *http://localhost:3000* + +Check [documentation](http://kemalcr.com) or [samples](https://github.com/kemalcr/kemal/tree/master/samples) for more. + +# Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + kemal: + github: kemalcr/kemal +``` + +See also [Getting Started](http://kemalcr.com/guide/). + +# Features + +- Support all REST verbs +- Websocket support +- Request/Response context, easy parameter handling +- Middleware support +- Built-in JSON support +- Built-in static file serving +- Built-in view templating via [Kilt](https://github.com/jeromegn/kilt) + +# Documentation + +You can read the documentation at the official site [kemalcr.com](http://kemalcr.com) + +## Thanks + +Thanks to Manas for their awesome work on [Frank](https://github.com/manastech/frank). diff --git a/samples/client/petstore/crystal/lib/kemal/lib b/samples/client/petstore/crystal/lib/kemal/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr b/samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr new file mode 100644 index 000000000000..c04f1d588d08 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr @@ -0,0 +1,8 @@ +require "kemal" + +# Set root. If not specified the default content_type is 'text' +get "/" do + "Hello Kemal!" +end + +Kemal.run diff --git a/samples/client/petstore/crystal/lib/kemal/samples/json_api.cr b/samples/client/petstore/crystal/lib/kemal/samples/json_api.cr new file mode 100644 index 000000000000..0132c149a7d9 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/samples/json_api.cr @@ -0,0 +1,11 @@ +require "kemal" +require "json" + +# You can easily access the context and set content_type like 'application/json'. +# Look how easy to build a JSON serving API. +get "/" do |env| + env.response.content_type = "application/json" + {name: "Serdar", age: 27}.to_json +end + +Kemal.run diff --git a/samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr b/samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr new file mode 100644 index 000000000000..61a0802984d5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr @@ -0,0 +1,11 @@ +require "kemal" + +ws "/" do |socket| + socket.send "Hello from Kemal!" + + socket.on_message do |message| + socket.send "Echo back from server #{message}" + end +end + +Kemal.run diff --git a/samples/client/petstore/crystal/lib/kemal/shard.yml b/samples/client/petstore/crystal/lib/kemal/shard.yml new file mode 100644 index 000000000000..57ce8bbc6504 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/shard.yml @@ -0,0 +1,25 @@ +name: kemal +version: 0.27.0 + +authors: + - Serdar Dogruyol + +dependencies: + radix: + github: luislavena/radix + version: ~> 0.3.8 + kilt: + github: jeromegn/kilt + version: ~> 0.4.0 + exception_page: + github: crystal-loot/exception_page + version: ~> 0.1.1 + +development_dependencies: + ameba: + github: crystal-ameba/ameba + version: ~> 0.12.0 + +crystal: 0.35.0 + +license: MIT diff --git a/samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr new file mode 100644 index 000000000000..938dadf6753a --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr @@ -0,0 +1 @@ +require "./*" diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr new file mode 100644 index 000000000000..1cc8d41475fe --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr @@ -0,0 +1 @@ +Hello <%= name %> diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr new file mode 100644 index 000000000000..149b29448a57 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr @@ -0,0 +1,5 @@ +Hello <%= name %> + +<% content_for "custom" do %> +

    Hello from otherside

    +<% end %> \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr new file mode 100644 index 000000000000..d493b59d919d --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr @@ -0,0 +1 @@ +<%= content %> diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr new file mode 100644 index 000000000000..f6cd6736e65d --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr @@ -0,0 +1,6 @@ + + + <%= content %> + <%= yield_content "custom" %> + + \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr new file mode 100644 index 000000000000..3a82a7a65a98 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr @@ -0,0 +1,8 @@ + + + <%= content %> + <%= yield_content "custom" %> + <%= var1 %> + <%= var2 %> + + \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr new file mode 100644 index 000000000000..31a84380b04c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr @@ -0,0 +1,61 @@ +require "./spec_helper" + +describe "Config" do + it "sets default port to 3000" do + Kemal::Config.new.port.should eq 3000 + end + + it "sets default environment to development" do + Kemal::Config.new.env.should eq "development" + end + + it "sets environment to production" do + config = Kemal.config + config.env = "production" + config.env.should eq "production" + end + + it "sets default powered_by_header to true" do + Kemal::Config.new.powered_by_header.should be_true + end + + it "sets host binding" do + config = Kemal.config + config.host_binding = "127.0.0.1" + config.host_binding.should eq "127.0.0.1" + end + + it "adds a custom handler" do + config = Kemal.config + config.add_handler CustomTestHandler.new + Kemal.config.setup + config.handlers.size.should eq(7) + end + + it "toggles the shutdown message" do + config = Kemal.config + config.shutdown_message = false + config.shutdown_message.should eq false + config.shutdown_message = true + config.shutdown_message.should eq true + end + + it "adds custom options" do + config = Kemal.config + ARGV.push("--test") + ARGV.push("FOOBAR") + test_option = nil + + config.extra_options do |parser| + parser.on("--test TEST_OPTION", "Test an option") do |opt| + test_option = opt + end + end + Kemal::CLI.new ARGV + test_option.should eq("FOOBAR") + end + + it "gets the version from shards.yml" do + Kemal::VERSION.should_not be("") + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr new file mode 100644 index 000000000000..c972926652b7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr @@ -0,0 +1,107 @@ +require "./spec_helper" + +describe "Context" do + context "headers" do + it "sets content type" do + get "/" do |env| + env.response.content_type = "application/json" + "Hello" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.headers["Content-Type"].should eq("application/json") + end + + it "parses headers" do + get "/" do |env| + name = env.request.headers["name"] + "Hello #{name}" + end + headers = HTTP::Headers.new + headers["name"] = "kemal" + request = HTTP::Request.new("GET", "/", headers) + client_response = call_request_on_app(request) + client_response.body.should eq "Hello kemal" + end + + it "sets response headers" do + get "/" do |env| + env.response.headers.add "Accept-Language", "tr" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.headers["Accept-Language"].should eq "tr" + end + end + + context "storage" do + it "can store primitive types" do + before_get "/" do |env| + env.set "before_get", "Kemal" + env.set "before_get_int", 123 + env.set "before_get_float", 3.5 + end + + get "/" do |env| + { + before_get: env.get("before_get"), + before_get_int: env.get("before_get_int"), + before_get_float: env.get("before_get_float"), + } + end + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::FilterHandler::INSTANCE.call(context) + Kemal::RouteHandler::INSTANCE.call(context) + + context.get("before_get").should eq "Kemal" + context.get("before_get_int").should eq 123 + context.get("before_get_float").should eq 3.5 + end + + it "can store custom types" do + before_get "/" do |env| + t = TestContextStorageType.new + t.id = 32 + a = AnotherContextStorageType.new + + env.set "before_get_context_test", t + env.set "another_context_test", a + end + + get "/" do |env| + { + before_get_context_test: env.get("before_get_context_test"), + another_context_test: env.get("another_context_test"), + } + end + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::FilterHandler::INSTANCE.call(context) + Kemal::RouteHandler::INSTANCE.call(context) + + context.get("before_get_context_test").as(TestContextStorageType).id.should eq 32 + context.get("another_context_test").as(AnotherContextStorageType).name.should eq "kemal-context" + end + + it "fetches non-existent keys from store with get?" do + get "/" { } + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::FilterHandler::INSTANCE.call(context) + Kemal::RouteHandler::INSTANCE.call(context) + + context.get?("non_existent_key").should be_nil + context.get?("another_non_existent_key").should be_nil + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr new file mode 100644 index 000000000000..b9519e9dd4a7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr @@ -0,0 +1,115 @@ +require "./spec_helper" + +describe "Kemal::ExceptionHandler" do + it "renders 404 on route not found" do + get "/" do + "Hello" + end + + request = HTTP::Request.new("GET", "/asd") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 404 + end + + it "renders custom error" do + error 403 do + "403 error" + end + get "/" do |env| + env.response.status_code = 403 + end + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 403 + response.headers["Content-Type"].should eq "text/html" + response.body.should eq "403 error" + end + + it "renders custom 500 error" do + error 500 do + "Something happened" + end + get "/" do |env| + env.response.status_code = 500 + end + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 500 + response.headers["Content-Type"].should eq "text/html" + response.body.should eq "Something happened" + end + + it "keeps the specified error Content-Type" do + error 500 do + "Something happened" + end + get "/" do |env| + env.response.content_type = "application/json" + env.response.status_code = 500 + end + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 500 + response.headers["Content-Type"].should eq "application/json" + response.body.should eq "Something happened" + end + + it "renders custom error with env and error" do + error 500 do |_, err| + err.message + end + get "/" do |env| + env.response.content_type = "application/json" + env.response.status_code = 500 + end + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 500 + response.headers["Content-Type"].should eq "application/json" + response.body.should eq "Rendered error with 500" + end + + it "does not do anything on a closed io" do + get "/" do |env| + halt env, status_code: 404 + end + + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq 404 + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr new file mode 100644 index 000000000000..9b1019fcc368 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr @@ -0,0 +1,161 @@ +require "./spec_helper" + +class CustomTestHandler < Kemal::Handler + def call(env) + env.response << "Kemal" + call_next env + end +end + +class OnlyHandler < Kemal::Handler + only ["/only"] + + def call(env) + return call_next(env) unless only_match?(env) + env.response.print "Only" + call_next env + end +end + +class ExcludeHandler < Kemal::Handler + exclude ["/exclude"] + + def call(env) + return call_next(env) if exclude_match?(env) + env.response.print "Exclude" + call_next env + end +end + +class PostOnlyHandler < Kemal::Handler + only ["/only", "/route1", "/route2"], "POST" + + def call(env) + return call_next(env) unless only_match?(env) + env.response.print "Only" + call_next env + end +end + +class PostExcludeHandler < Kemal::Handler + exclude ["/exclude"], "POST" + + def call(env) + return call_next(env) if exclude_match?(env) + env.response.print "Exclude" + call_next env + end +end + +class ExcludeHandlerPercentW < Kemal::Handler + exclude %w[/exclude] + + def call(env) + return call_next(env) if exclude_match?(env) + env.response.print "Exclude" + call_next env + end +end + +class PostOnlyHandlerPercentW < Kemal::Handler + only %w[/only /route1 /route2], "POST" + + def call(env) + return call_next(env) unless only_match?(env) + env.response.print "Only" + call_next env + end +end + +describe "Handler" do + it "adds custom handler before before_*" do + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("GET", "/", :before) do |env| + env.response << " is" + end + + filter_middleware._add_route_filter("GET", "/", :before) do |env| + env.response << " so" + end + add_handler CustomTestHandler.new + + get "/" do + " Great" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq(200) + client_response.body.should eq("Kemal is so Great") + end + + it "runs specified only_routes in middleware" do + get "/only" do + "Get" + end + add_handler OnlyHandler.new + request = HTTP::Request.new("GET", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyGet" + end + + it "doesn't run specified exclude_routes in middleware" do + get "/" do + "Get" + end + get "/exclude" do + "Exclude" + end + add_handler ExcludeHandler.new + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.body.should eq "ExcludeGet" + end + + it "runs specified only_routes with method in middleware" do + post "/only" do + "Post" + end + get "/only" do + "Get" + end + add_handler PostOnlyHandler.new + request = HTTP::Request.new("POST", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyPost" + end + + it "doesn't run specified exclude_routes with method in middleware" do + post "/exclude" do + "Post" + end + post "/only" do + "Post" + end + add_handler PostOnlyHandler.new + add_handler PostExcludeHandler.new + request = HTTP::Request.new("POST", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyExcludePost" + end + + it "adds a handler at given position" do + post_handler = PostOnlyHandler.new + add_handler post_handler, 1 + Kemal.config.setup + Kemal.config.handlers[1].should eq post_handler + end + + it "assigns custom handlers" do + post_only_handler = PostOnlyHandler.new + post_exclude_handler = PostExcludeHandler.new + Kemal.config.handlers = [post_only_handler, post_exclude_handler] + Kemal.config.handlers.should eq [post_only_handler, post_exclude_handler] + end + + it "is able to use %w in macros" do + post_only_handler = PostOnlyHandlerPercentW.new + exclude_handler = ExcludeHandlerPercentW.new + Kemal.config.handlers = [post_only_handler, exclude_handler] + Kemal.config.handlers.should eq [post_only_handler, exclude_handler] + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr new file mode 100644 index 000000000000..4d45a666d4df --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr @@ -0,0 +1,173 @@ +require "./spec_helper" +require "./handler_spec" + +describe "Macros" do + describe "#public_folder" do + it "sets public folder" do + public_folder "/some/path/to/folder" + Kemal.config.public_folder.should eq("/some/path/to/folder") + end + end + + describe "#add_handler" do + it "adds a custom handler" do + add_handler CustomTestHandler.new + Kemal.config.setup + Kemal.config.handlers.size.should eq 7 + end + end + + describe "#logging" do + it "sets logging status" do + logging false + Kemal.config.logging.should eq false + end + + it "sets a custom logger" do + config = Kemal::Config::INSTANCE + logger CustomLogHandler.new + config.logger.should be_a(CustomLogHandler) + end + end + + describe "#halt" do + it "can break block with halt macro" do + get "/non-breaking" do + "hello" + "world" + end + request = HTTP::Request.new("GET", "/non-breaking") + client_response = call_request_on_app(request) + client_response.status_code.should eq(200) + client_response.body.should eq("world") + + get "/breaking" do |env| + halt env, 404, "hello" + "world" + end + request = HTTP::Request.new("GET", "/breaking") + client_response = call_request_on_app(request) + client_response.status_code.should eq(404) + client_response.body.should eq("hello") + end + + it "can break block with halt macro using default values" do + get "/" do |env| + halt env + "world" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq(200) + client_response.body.should eq("") + end + end + + describe "#callbacks" do + it "can break block with halt macro from before_* callback" do + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("GET", "/", :before) do |env| + halt env, status_code: 400, response: "Missing origin." + end + + get "/" do |_env| + "Hello world" + end + + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq(400) + client_response.body.should eq("Missing origin.") + end + end + + describe "#headers" do + it "can add headers" do + get "/headers" do |env| + env.response.headers.add "Content-Type", "image/png" + headers env, { + "Access-Control-Allow-Origin" => "*", + "Content-Type" => "text/plain", + } + end + request = HTTP::Request.new("GET", "/headers") + response = call_request_on_app(request) + response.headers["Access-Control-Allow-Origin"].should eq("*") + response.headers["Content-Type"].should eq("text/plain") + end + end + + describe "#send_file" do + it "sends file with given path and default mime-type" do + get "/" do |env| + send_file env, "./spec/asset/hello.ecr" + end + + request = HTTP::Request.new("GET", "/") + response = call_request_on_app(request) + response.status_code.should eq(200) + response.headers["Content-Type"].should eq("application/octet-stream") + response.headers["Content-Length"].should eq("18") + end + + it "sends file with given path and given mime-type" do + get "/" do |env| + send_file env, "./spec/asset/hello.ecr", "image/jpeg" + end + + request = HTTP::Request.new("GET", "/") + response = call_request_on_app(request) + response.status_code.should eq(200) + response.headers["Content-Type"].should eq("image/jpeg") + response.headers["Content-Length"].should eq("18") + end + + it "sends file with binary stream" do + get "/" do |env| + send_file env, "Serdar".to_slice + end + + request = HTTP::Request.new("GET", "/") + response = call_request_on_app(request) + response.status_code.should eq(200) + response.headers["Content-Type"].should eq("application/octet-stream") + response.headers["Content-Length"].should eq("6") + end + + it "sends file with given path and given filename" do + get "/" do |env| + send_file env, "./spec/asset/hello.ecr", filename: "image.jpg" + end + + request = HTTP::Request.new("GET", "/") + response = call_request_on_app(request) + response.status_code.should eq(200) + response.headers["Content-Disposition"].should eq("attachment; filename=\"image.jpg\"") + end + end + + describe "#gzip" do + it "adds HTTP::CompressHandler to handlers" do + gzip true + Kemal.config.setup + Kemal.config.handlers[4].should be_a(HTTP::CompressHandler) + end + end + + describe "#serve_static" do + it "should disable static file hosting" do + serve_static false + Kemal.config.serve_static.should eq false + end + + it "should disble enable gzip and dir_listing" do + serve_static({"gzip" => true, "dir_listing" => true}) + conf = Kemal.config.serve_static + conf.is_a?(Hash).should eq true + if conf.is_a?(Hash) + conf["gzip"].should eq true + conf["dir_listing"].should eq true + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr new file mode 100644 index 000000000000..601bbc1d1df6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr @@ -0,0 +1,32 @@ +require "./spec_helper" + +describe "Kemal::InitHandler" do + it "initializes context with Content-Type: text/html" do + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::InitHandler::INSTANCE.next = ->(_context : HTTP::Server::Context) {} + Kemal::InitHandler::INSTANCE.call(context) + context.response.headers["Content-Type"].should eq "text/html" + end + + it "initializes context with X-Powered-By: Kemal" do + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::InitHandler::INSTANCE.call(context) + context.response.headers["X-Powered-By"].should eq "Kemal" + end + + it "does not initialize context with X-Powered-By: Kemal if disabled" do + Kemal.config.powered_by_header = false + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::InitHandler::INSTANCE.call(context) + context.response.headers["X-Powered-By"]?.should be_nil + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr new file mode 100644 index 000000000000..5ee9c86323c7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr @@ -0,0 +1,21 @@ +require "./spec_helper" + +describe "Kemal::LogHandler" do + it "logs to the given IO" do + io = IO::Memory.new + logger = Kemal::LogHandler.new io + logger.write "Something" + io.to_s.should eq "Something" + end + + it "creates log message for each request" do + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + context_io = IO::Memory.new + response = HTTP::Server::Response.new(context_io) + context = HTTP::Server::Context.new(request, response) + logger = Kemal::LogHandler.new io + logger.call(context) + io.to_s.should_not be nil + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr new file mode 100644 index 000000000000..9bc2564c100c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr @@ -0,0 +1,190 @@ +require "../spec_helper" + +describe "Kemal::FilterHandler" do + it "executes code before home request" do + test_filter = FilterTest.new + test_filter.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("GET", "/greetings", :before) { test_filter.modified = "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + + test_filter.modified.should eq("false") + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + end + + it "executes code before GET home request but not POST home request" do + test_filter = FilterTest.new + test_filter.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("GET", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + kemal.add_route "POST", "/greetings" { test_filter.modified } + + test_filter.modified.should eq("false") + + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + + request = HTTP::Request.new("POST", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + end + + it "executes code before all GET/POST home request" do + test_filter = FilterTest.new + test_filter.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("GET", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("POST", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + kemal.add_route "POST", "/greetings" { test_filter.modified } + + test_filter.modified.should eq("false") + + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("false") + + request = HTTP::Request.new("POST", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("false") + end + + it "executes code after home request" do + test_filter = FilterTest.new + test_filter.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("GET", "/greetings", :after) { test_filter.modified = "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + + test_filter.modified.should eq("false") + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + end + + it "executes code after GET home request but not POST home request" do + test_filter = FilterTest.new + test_filter.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("GET", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + kemal.add_route "POST", "/greetings" { test_filter.modified } + + test_filter.modified.should eq("false") + + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + + request = HTTP::Request.new("POST", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + end + + it "executes code after all GET/POST home request" do + test_filter = FilterTest.new + test_filter.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("ALL", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("GET", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("POST", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + kemal.add_route "POST", "/greetings" { test_filter.modified } + + test_filter.modified.should eq("false") + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("false") + + request = HTTP::Request.new("POST", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("false") + end + + it "executes 3 differents blocks after all request" do + test_filter = FilterTest.new + test_filter.modified = "false" + test_filter_second = FilterTest.new + test_filter_second.modified = "false" + test_filter_third = FilterTest.new + test_filter_third.modified = "false" + + filter_middleware = Kemal::FilterHandler.new + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter_second.modified = test_filter_second.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter_third.modified = test_filter_third.modified == "true" ? "false" : "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + kemal.add_route "POST", "/greetings" { test_filter_second.modified } + kemal.add_route "PUT", "/greetings" { test_filter_third.modified } + + test_filter.modified.should eq("false") + test_filter_second.modified.should eq("false") + test_filter_third.modified.should eq("false") + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + + request = HTTP::Request.new("POST", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("false") + + request = HTTP::Request.new("PUT", "/greetings") + create_request_and_return_io_and_context(filter_middleware, request) + io_with_context = create_request_and_return_io_and_context(kemal, request)[0] + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + end +end + +class FilterTest + property modified : String? +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr new file mode 100644 index 000000000000..d63a2298c986 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr @@ -0,0 +1,204 @@ +require "./spec_helper" + +describe "ParamParser" do + it "parses query params" do + Route.new "POST", "/" do |env| + hasan = env.params.query["hasan"] + "Hello #{hasan}" + end + request = HTTP::Request.new("POST", "/?hasan=cemal") + query_params = Kemal::ParamParser.new(request).query + query_params["hasan"].should eq "cemal" + end + + it "parses multiple values for query params" do + Route.new "POST", "/" do |env| + hasan = env.params.query["hasan"] + "Hello #{hasan}" + end + request = HTTP::Request.new("POST", "/?hasan=cemal&hasan=lamec") + query_params = Kemal::ParamParser.new(request).query + query_params.fetch_all("hasan").should eq ["cemal", "lamec"] + end + + it "parses url params" do + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "POST", "/hello/:hasan" do |env| + "hello #{env.params.url["hasan"]}" + end + request = HTTP::Request.new("POST", "/hello/cemal") + # Radix tree MUST be run to parse url params. + context = create_request_and_return_io_and_context(kemal, request)[1] + url_params = Kemal::ParamParser.new(request, context.route_lookup.params).url + url_params["hasan"].should eq "cemal" + end + + it "decodes url params" do + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "POST", "/hello/:email/:money/:spanish" do |env| + email = env.params.url["email"] + money = env.params.url["money"] + spanish = env.params.url["spanish"] + "Hello, #{email}. You have #{money}. The spanish word of the day is #{spanish}." + end + request = HTTP::Request.new("POST", "/hello/sam%2Bspec%40gmail.com/%2419.99/a%C3%B1o") + # Radix tree MUST be run to parse url params. + context = create_request_and_return_io_and_context(kemal, request)[1] + url_params = Kemal::ParamParser.new(request, context.route_lookup.params).url + url_params["email"].should eq "sam+spec@gmail.com" + url_params["money"].should eq "$19.99" + url_params["spanish"].should eq "año" + end + + it "parses request body" do + Route.new "POST", "/" do |env| + name = env.params.query["name"] + age = env.params.query["age"] + hasan = env.params.body["hasan"] + "Hello #{name} #{hasan} #{age}" + end + + request = HTTP::Request.new( + "POST", + "/?hasan=cemal", + body: "name=serdar&age=99", + headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded"}, + ) + + query_params = Kemal::ParamParser.new(request).query + {"hasan" => "cemal"}.each do |k, v| + query_params[k].should eq(v) + end + + body_params = Kemal::ParamParser.new(request).body + {"name" => "serdar", "age" => "99"}.each do |k, v| + body_params[k].should eq(v) + end + end + + it "parses multiple values in request body" do + Route.new "POST", "/" do |env| + hasan = env.params.body["hasan"] + "Hello #{hasan}" + end + + request = HTTP::Request.new( + "POST", + "/", + body: "hasan=cemal&hasan=lamec", + headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded"}, + ) + + body_params = Kemal::ParamParser.new(request).body + body_params.fetch_all("hasan").should eq(["cemal", "lamec"]) + end + + context "when content type is application/json" do + it "parses request body" do + Route.new "POST", "/" { } + + request = HTTP::Request.new( + "POST", + "/", + body: "{\"name\": \"Serdar\"}", + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + + json_params = Kemal::ParamParser.new(request).json + json_params.should eq({"name" => "Serdar"}) + end + + it "parses request body when passed charset" do + Route.new "POST", "/" { } + + request = HTTP::Request.new( + "POST", + "/", + body: "{\"name\": \"Serdar\"}", + headers: HTTP::Headers{"Content-Type" => "application/json; charset=utf-8"}, + ) + + json_params = Kemal::ParamParser.new(request).json + json_params.should eq({"name" => "Serdar"}) + end + + it "parses request body for array" do + Route.new "POST", "/" { } + + request = HTTP::Request.new( + "POST", + "/", + body: "[1]", + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + + json_params = Kemal::ParamParser.new(request).json + json_params.should eq({"_json" => [1]}) + end + + it "parses request body and query params" do + Route.new "POST", "/" { } + + request = HTTP::Request.new( + "POST", + "/?foo=bar", + body: "[1]", + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + + query_params = Kemal::ParamParser.new(request).query + {"foo" => "bar"}.each do |k, v| + query_params[k].should eq(v) + end + + json_params = Kemal::ParamParser.new(request).json + json_params.should eq({"_json" => [1]}) + end + + it "handles no request body" do + Route.new "GET", "/" { } + + request = HTTP::Request.new( + "GET", + "/", + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + + url_params = Kemal::ParamParser.new(request).url + url_params.should eq({} of String => String) + + query_params = Kemal::ParamParser.new(request).query + query_params.to_s.should eq("") + + body_params = Kemal::ParamParser.new(request).body + body_params.to_s.should eq("") + + json_params = Kemal::ParamParser.new(request).json + json_params.should eq({} of String => Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Any) | Array(JSON::Any)) + end + end + + context "when content type is incorrect" do + it "does not parse request body" do + Route.new "POST", "/" do |env| + name = env.params.body["name"] + age = env.params.body["age"] + hasan = env.params.query["hasan"] + "Hello #{name} #{hasan} #{age}" + end + + request = HTTP::Request.new( + "POST", + "/?hasan=cemal", + body: "name=serdar&age=99", + headers: HTTP::Headers{"Content-Type" => "text/plain"}, + ) + + query_params = Kemal::ParamParser.new(request).query + query_params["hasan"].should eq("cemal") + + body_params = Kemal::ParamParser.new(request).body + body_params.to_s.should eq("") + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr new file mode 100644 index 000000000000..93cf0f06cddc --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr @@ -0,0 +1,146 @@ +require "./spec_helper" + +describe "Kemal::RouteHandler" do + it "routes" do + get "/" do + "hello" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.body.should eq("hello") + end + + it "routes with long response body" do + long_response_body = "string" * 10_000 + + get "/" do + long_response_body + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.body.should eq(long_response_body) + end + + it "routes should only return strings" do + get "/" do + 100 + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.body.should eq("") + end + + it "routes request with query string" do + get "/" do |env| + "hello #{env.params.query["message"]}" + end + request = HTTP::Request.new("GET", "/?message=world") + client_response = call_request_on_app(request) + client_response.body.should eq("hello world") + end + + it "routes request with multiple query strings" do + get "/" do |env| + "hello #{env.params.query["message"]} time #{env.params.query["time"]}" + end + request = HTTP::Request.new("GET", "/?message=world&time=now") + client_response = call_request_on_app(request) + client_response.body.should eq("hello world time now") + end + + it "route parameter has more precedence than query string arguments" do + get "/:message" do |env| + "hello #{env.params.url["message"]}" + end + request = HTTP::Request.new("GET", "/world?message=coco") + client_response = call_request_on_app(request) + client_response.body.should eq("hello world") + end + + it "parses simple JSON body" do + post "/" do |env| + name = env.params.json["name"] + age = env.params.json["age"] + "Hello #{name} Age #{age}" + end + + json_payload = {"name": "Serdar", "age": 26} + request = HTTP::Request.new( + "POST", + "/", + body: json_payload.to_json, + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + client_response = call_request_on_app(request) + client_response.body.should eq("Hello Serdar Age 26") + end + + it "parses JSON with string array" do + post "/" do |env| + skills = env.params.json["skills"].as(Array) + "Skills #{skills.each.join(',')}" + end + + json_payload = {"skills": ["ruby", "crystal"]} + request = HTTP::Request.new( + "POST", + "/", + body: json_payload.to_json, + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + client_response = call_request_on_app(request) + client_response.body.should eq("Skills ruby,crystal") + end + + it "parses JSON with json object array" do + post "/" do |env| + skills = env.params.json["skills"].as(Array) + skills_from_languages = skills.map do |skill| + skill["language"] + end + "Skills #{skills_from_languages.each.join(',')}" + end + + json_payload = {"skills": [{"language": "ruby"}, {"language": "crystal"}]} + request = HTTP::Request.new( + "POST", + "/", + body: json_payload.to_json, + headers: HTTP::Headers{"Content-Type" => "application/json"}, + ) + + client_response = call_request_on_app(request) + client_response.body.should eq("Skills ruby,crystal") + end + + it "can process HTTP HEAD requests for defined GET routes" do + get "/" do + "Hello World from GET" + end + request = HTTP::Request.new("HEAD", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq(200) + end + + it "redirects user to provided url" do + get "/" do |env| + env.redirect "/login" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq(302) + client_response.body.should eq("") + client_response.headers.has_key?("Location").should eq(true) + end + + it "redirects with body" do + get "/" do |env| + env.redirect "/login", body: "Redirecting to /login" + end + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.status_code.should eq(302) + client_response.body.should eq("Redirecting to /login") + client_response.headers.has_key?("Location").should eq(true) + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr new file mode 100644 index 000000000000..7634d51dc763 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr @@ -0,0 +1,25 @@ +require "./spec_helper" + +describe "Route" do + describe "match?" do + it "matches the correct route" do + get "/route1" do + "Route 1" + end + get "/route2" do + "Route 2" + end + request = HTTP::Request.new("GET", "/route2") + client_response = call_request_on_app(request) + client_response.body.should eq("Route 2") + end + + it "doesn't allow a route declaration start without /" do + expect_raises Kemal::Exceptions::InvalidPathStartException, "Route declaration get \"route\" needs to start with '/', should be get \"/route\"" do + get "route" do + "Route 1" + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr new file mode 100644 index 000000000000..ec89d233c8f6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr @@ -0,0 +1,46 @@ +require "./spec_helper" + +private def run(code) + code = <<-CR + require "./src/kemal" + #{code} + CR + + stdout = IO::Memory.new + stderr = IO::Memory.new + status = Process.new("crystal", ["eval"], input: IO::Memory.new(code), output: stdout, error: stderr).wait + fail(stderr.to_s) unless status.success? + stdout.to_s +end + +describe "Run" do + it "runs a code block after starting" do + run(<<-CR).should eq "started\nstopped\n" + Kemal.config.env = "test" + Kemal.run do + puts "started" + Kemal.stop + puts "stopped" + end + CR + end + + it "runs without a block being specified" do + run(<<-CR).should eq "[test] Kemal is ready to lead at http://0.0.0.0:3000\ntrue\n" + Kemal.config.env = "test" + Kemal.run + puts Kemal.config.running + CR + end + + it "allows custom HTTP::Server bind" do + run(<<-CR).should eq "[test] Kemal is ready to lead at http://127.0.0.1:3000, http://0.0.0.0:3001\n" + Kemal.config.env = "test" + Kemal.run do |config| + server = config.server.not_nil! + server.bind_tcp "127.0.0.1", 3000, reuse_port: true + server.bind_tcp "0.0.0.0", 3001, reuse_port: true + end + CR + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr new file mode 100644 index 000000000000..803aacf5a495 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr @@ -0,0 +1,91 @@ +require "spec" +require "../src/*" + +include Kemal + +class CustomLogHandler < Kemal::BaseLogHandler + def call(env) + call_next env + end + + def write(message) + end +end + +class TestContextStorageType + property id + @id = 1 + + def to_s + @id + end +end + +class AnotherContextStorageType + property name + @name = "kemal-context" +end + +add_context_storage_type(TestContextStorageType) +add_context_storage_type(AnotherContextStorageType) + +def create_request_and_return_io_and_context(handler, request) + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + handler.call(context) + response.close + io.rewind + {io, context} +end + +def create_ws_request_and_return_io_and_context(handler, request) + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + begin + handler.call context + rescue IO::Error + # Raises because the IO::Memory is empty + end + {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} + response.upgrade_handler.try &.call(io) + {% end %} + io.rewind + {io, context} +end + +def call_request_on_app(request) + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + main_handler = build_main_handler + main_handler.call context + response.close + io.rewind + HTTP::Client::Response.from_io(io, decompress: false) +end + +def build_main_handler + Kemal.config.setup + main_handler = Kemal.config.handlers.first + current_handler = main_handler + Kemal.config.handlers.each do |handler| + current_handler.next = handler + current_handler = handler + end + main_handler +end + +Spec.before_each do + config = Kemal.config + config.env = "development" + config.logging = false +end + +Spec.after_each do + Kemal.config.clear + Kemal::RouteHandler::INSTANCE.routes = Radix::Tree(Route).new + Kemal::RouteHandler::INSTANCE.cached_routes = Hash(String, Radix::Result(Route)).new + Kemal::WebSocketHandler::INSTANCE.routes = Radix::Tree(WebSocket).new +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt new file mode 100644 index 000000000000..36281ae8f9f3 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse posuere cursus consectetur. Donec mauris lorem, sodales a eros a, ultricies convallis ante. Quisque elementum lacus purus, sagittis mollis justo dignissim ac. Suspendisse potenti. Cras non mauris accumsan mi porttitor congue. Quisque posuere aliquam tellus sit amet ultrices. Sed at tortor sed libero fringilla luctus vitae quis magna. In maximus congue felis, et porta tortor egestas sed. Phasellus orci eros, finibus sed ipsum eget, euismod bibendum nisl. Etiam ultrices facilisis diam in gravida. Praesent lobortis leo vitae aliquet volutpat. Praesent vel blandit risus. In suscipit eget nunc at ultrices. Proin dapibus feugiat diam ut tincidunt. Donec lectus diam, ornare ut consequat nec, gravida sit amet metus. + +Nunc a viverra urna, quis ullamcorper augue. Morbi posuere auctor nibh, tempor luctus massa mollis laoreet. Pellentesque sagittis leo eu felis interdum finibus. Pellentesque porttitor lobortis arcu, eu mollis dui iaculis nec. Vestibulum sit amet sodales erat. Nullam quis mi massa. Suspendisse sit amet elit auctor, feugiat ipsum a, placerat metus. Vestibulum quis felis a lectus blandit aliquam. Nam consectetur iaculis nulla. Mauris sit amet condimentum erat, in vestibulum dui. Nullam nec mattis tortor, non viverra nunc. Proin eget congue augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ut hendrerit nulla. Etiam cursus sagittis metus, et feugiat ligula molestie sit amet. Aliquam laoreet auctor sagittis. + +Aliquam tempor urna non consectetur tincidunt. Maecenas porttitor augue diam, ac lobortis nulla suscipit eget. Ut quis lacus facilisis, euismod lacus non, ullamcorper urna. Cras pretium fringilla pharetra. Praesent sed nunc at elit vulputate elementum. Suspendisse ac molestie nunc, sit amet consectetur nunc. Cras placerat ligula tortor, non bibendum massa tempus ut. Etiam eros erat, gravida id felis eget, congue suscipit ipsum. Sed condimentum erat at facilisis dictum. Cras venenatis vitae turpis vitae sagittis. Proin id posuere est, non ornare sem. Donec vitae sollicitudin dolor, a pulvinar ex. Integer porta velit lectus, et imperdiet enim commodo a. + +Donec sit amet ipsum tempus, tincidunt neque eget, luctus massa. Praesent vel nulla pretium, bibendum enim a, pulvinar enim. Vestibulum non libero eu est dignissim cursus. Nullam commodo tellus imperdiet feugiat placerat. Sed sed dolor ut nibh blandit maximus ac eget neque. Ut sit amet augue maximus, lacinia eros non, faucibus eros. Suspendisse ac bibendum libero, eu lobortis nulla. Mauris arcu nulla, tempus eu varius eu, bibendum at nibh. Donec id libero consequat, volutpat ex vitae, molestie velit. Aliquam aliquam sem ac arcu pellentesque, placerat bibendum enim dapibus. Duis consectetur ligula non placerat euismod. + +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin commodo ullamcorper venenatis. Cras ac lorem sit amet augue varius convallis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris dolor nisi, efficitur id aliquet ut, ultricies sed elit. Proin ultricies turpis dolor, in auctor velit aliquet nec. Praesent vehicula aliquam viverra. Suspendisse potenti. Donec aliquet iaculis ultricies. Proin dignissim vitae nisl at rutrum. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html new file mode 100644 index 000000000000..32d977f9cc9f --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html @@ -0,0 +1,12 @@ + + + + + title + + + + + + + \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt new file mode 100644 index 000000000000..9db7df02b602 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt @@ -0,0 +1,2 @@ +hello +world \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr new file mode 100644 index 000000000000..1aac161bb7c9 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr @@ -0,0 +1,153 @@ +require "./spec_helper" + +private def handle(request, fallthrough = true) + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + handler = Kemal::StaticFileHandler.new "#{__DIR__}/static", fallthrough + handler.call context + response.close + io.rewind + HTTP::Client::Response.from_io(io) +end + +describe Kemal::StaticFileHandler do + file = File.open "#{__DIR__}/static/dir/test.txt" + file_size = file.size + + it "should serve a file with content type and etag" do + response = handle HTTP::Request.new("GET", "/dir/test.txt") + response.status_code.should eq(200) + response.headers["Content-Type"].should eq "text/plain" + response.headers["Etag"].should contain "W/\"" + response.body.should eq(File.read("#{__DIR__}/static/dir/test.txt")) + end + + it "should respond with 304 if file has not changed" do + response = handle HTTP::Request.new("GET", "/dir/test.txt") + response.status_code.should eq(200) + etag = response.headers["Etag"] + + headers = HTTP::Headers{"If-None-Match" => etag} + response = handle HTTP::Request.new("GET", "/dir/test.txt", headers) + response.headers["Content-Type"]?.should be_nil + response.status_code.should eq(304) + response.body.should eq "" + end + + it "should not list directory's entries" do + serve_static({"gzip" => true, "dir_listing" => false}) + response = handle HTTP::Request.new("GET", "/dir/") + response.status_code.should eq(404) + end + + it "should list directory's entries when config is set" do + serve_static({"gzip" => true, "dir_listing" => true}) + response = handle HTTP::Request.new("GET", "/dir/") + response.status_code.should eq(200) + response.body.should match(/test.txt/) + end + + it "should gzip a file if config is true, headers accept gzip and file is > 880 bytes" do + serve_static({"gzip" => true, "dir_listing" => true}) + headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} + response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers) + response.status_code.should eq(200) + response.headers["Content-Encoding"].should eq "gzip" + end + + it "should not gzip a file if config is true, headers accept gzip and file is < 880 bytes" do + serve_static({"gzip" => true, "dir_listing" => true}) + headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} + response = handle HTTP::Request.new("GET", "/dir/test.txt", headers) + response.status_code.should eq(200) + response.headers["Content-Encoding"]?.should be_nil + end + + it "should not gzip a file if config is false, headers accept gzip and file is > 880 bytes" do + serve_static({"gzip" => false, "dir_listing" => true}) + headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} + response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers) + response.status_code.should eq(200) + response.headers["Content-Encoding"]?.should be_nil + end + + it "should not serve a not found file" do + response = handle HTTP::Request.new("GET", "/not_found_file.txt") + response.status_code.should eq(404) + end + + it "should not serve a not found directory" do + response = handle HTTP::Request.new("GET", "/not_found_dir/") + response.status_code.should eq(404) + end + + it "should not serve a file as directory" do + response = handle HTTP::Request.new("GET", "/dir/test.txt/") + response.status_code.should eq(404) + end + + it "should handle only GET and HEAD method" do + %w(GET HEAD).each do |method| + response = handle HTTP::Request.new(method, "/dir/test.txt") + response.status_code.should eq(200) + end + + %w(POST PUT DELETE).each do |method| + response = handle HTTP::Request.new(method, "/dir/test.txt") + response.status_code.should eq(404) + response = handle HTTP::Request.new(method, "/dir/test.txt"), false + response.status_code.should eq(405) + response.headers["Allow"].should eq("GET, HEAD") + end + end + + it "should send part of files when requested (RFC7233)" do + %w(POST PUT DELETE HEAD).each do |method| + headers = HTTP::Headers{"Range" => "0-100"} + response = handle HTTP::Request.new(method, "/dir/test.txt", headers) + response.status_code.should_not eq(206) + response.headers.has_key?("Content-Range").should eq(false) + end + + %w(GET).each do |method| + headers = HTTP::Headers{"Range" => "0-100"} + response = handle HTTP::Request.new(method, "/dir/test.txt", headers) + response.status_code.should eq(206 || 200) + if response.status_code == 206 + response.headers.has_key?("Content-Range").should eq true + match = response.headers["Content-Range"].match(/bytes (\d+)-(\d+)\/(\d+)/) + match.should_not be_nil + if match + start_range = match[1].to_i { 0 } + end_range = match[2].to_i { 0 } + range_size = match[3].to_i { 0 } + + range_size.should eq file_size + (end_range < file_size).should eq true + (start_range < end_range).should eq true + end + end + end + end + + it "should handle setting custom headers" do + headers = Proc(HTTP::Server::Response, String, File::Info, Void).new do |response, path, stat| + if path =~ /\.html$/ + response.headers.add("Access-Control-Allow-Origin", "*") + end + response.headers.add("Content-Size", stat.size.to_s) + end + + static_headers(&headers) + + response = handle HTTP::Request.new("GET", "/dir/test.txt") + response.headers.has_key?("Access-Control-Allow-Origin").should be_false + response.headers["Content-Size"].should eq( + File.info("#{__DIR__}/static/dir/test.txt").size.to_s + ) + + response = handle HTTP::Request.new("GET", "/dir/index.html") + response.headers["Access-Control-Allow-Origin"].should eq("*") + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr new file mode 100644 index 000000000000..d09f4de4281f --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr @@ -0,0 +1,62 @@ +require "./spec_helper" + +macro render_with_base_and_layout(filename) + render "spec/asset/#{{{filename}}}", "spec/asset/layout.ecr" +end + +describe "Views" do + it "renders file" do + get "/view/:name" do |env| + name = env.params.url["name"] + render "spec/asset/hello.ecr" + end + request = HTTP::Request.new("GET", "/view/world") + client_response = call_request_on_app(request) + client_response.body.should contain("Hello world") + end + + it "renders file with dynamic variables" do + get "/view/:name" do |env| + name = env.params.url["name"] + render_with_base_and_layout "hello.ecr" + end + request = HTTP::Request.new("GET", "/view/world") + client_response = call_request_on_app(request) + client_response.body.should contain("Hello world") + end + + it "renders layout" do + get "/view/:name" do |env| + name = env.params.url["name"] + render "spec/asset/hello.ecr", "spec/asset/layout.ecr" + end + request = HTTP::Request.new("GET", "/view/world") + client_response = call_request_on_app(request) + client_response.body.should contain("Hello world") + end + + it "renders layout with variables" do + get "/view/:name" do |env| + name = env.params.url["name"] + var1 = "serdar" + var2 = "kemal" + render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield_and_vars.ecr" + end + request = HTTP::Request.new("GET", "/view/world") + client_response = call_request_on_app(request) + client_response.body.should contain("Hello world") + client_response.body.should contain("serdar") + client_response.body.should contain("kemal") + end + + it "renders layout with content_for" do + get "/view/:name" do |env| + name = env.params.url["name"] + render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield.ecr" + end + request = HTTP::Request.new("GET", "/view/world") + client_response = call_request_on_app(request) + client_response.body.should contain("Hello world") + client_response.body.should contain("

    Hello from otherside

    ") + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr new file mode 100644 index 000000000000..bc02d3c26634 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr @@ -0,0 +1,68 @@ +require "./spec_helper" + +describe "Kemal::WebSocketHandler" do + it "doesn't match on wrong route" do + handler = Kemal::WebSocketHandler::INSTANCE + handler.next = Kemal::RouteHandler::INSTANCE + ws "/" { } + headers = HTTP::Headers{ + "Upgrade" => "websocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==", + } + request = HTTP::Request.new("GET", "/asd", headers) + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + + expect_raises(Kemal::Exceptions::RouteNotFound) do + handler.call context + end + end + + it "matches on given route" do + handler = Kemal::WebSocketHandler::INSTANCE + ws "/" { |socket| socket.send("Match") } + ws "/no_match" { |socket| socket.send "No Match" } + headers = HTTP::Headers{ + "Upgrade" => "websocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==", + "Sec-WebSocket-Version" => "13", + } + request = HTTP::Request.new("GET", "/", headers) + + io_with_context = create_ws_request_and_return_io_and_context(handler, request)[0] + io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n\x81\u0005Match") + end + + it "fetches named url parameters" do + handler = Kemal::WebSocketHandler::INSTANCE + ws "/:id" { |_, c| c.ws_route_lookup.params["id"] } + headers = HTTP::Headers{ + "Upgrade" => "websocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==", + "Sec-WebSocket-Version" => "13", + } + request = HTTP::Request.new("GET", "/1234", headers) + io_with_context = create_ws_request_and_return_io_and_context(handler, request)[0] + io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n") + end + + it "matches correct verb" do + handler = Kemal::WebSocketHandler::INSTANCE + handler.next = Kemal::RouteHandler::INSTANCE + ws "/" { } + get "/" { "get" } + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + handler.call(context) + response.close + io.rewind + client_response = HTTP::Client::Response.from_io(io, decompress: false) + client_response.body.should eq("get") + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal.cr new file mode 100644 index 000000000000..009337e254fe --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal.cr @@ -0,0 +1,98 @@ +require "http" +require "json" +require "uri" +require "./kemal/*" +require "./kemal/ext/*" +require "./kemal/helpers/*" + +module Kemal + # Overload of `self.run` with the default startup logging. + def self.run(port : Int32?, args = ARGV) + self.run(port, args) { } + end + + # Overload of `self.run` without port. + def self.run(args = ARGV) + self.run(nil, args: args) + end + + # Overload of `self.run` to allow just a block. + def self.run(args = ARGV, &block) + self.run(nil, args: args, &block) + end + + # The command to run a `Kemal` application. + # + # If *port* is not given Kemal will use `Kemal::Config#port` + # + # To use custom command line arguments, set args to nil + # + def self.run(port : Int32? = nil, args = ARGV, &block) + Kemal::CLI.new args + config = Kemal.config + config.setup + config.port = port if port + + # Test environment doesn't need to have signal trap and logging. + if config.env != "test" + setup_404 + setup_trap_signal + end + + server = config.server ||= HTTP::Server.new(config.handlers) + + config.running = true + + yield config + + # Abort if block called `Kemal.stop` + return unless config.running + + unless server.each_address { |_| break true } + {% if flag?(:without_openssl) %} + server.bind_tcp(config.host_binding, config.port) + {% else %} + if ssl = config.ssl + server.bind_tls(config.host_binding, config.port, ssl) + else + server.bind_tcp(config.host_binding, config.port) + end + {% end %} + end + + display_startup_message(config, server) + + server.listen unless config.env == "test" + end + + def self.display_startup_message(config, server) + addresses = server.addresses.map { |address| "#{config.scheme}://#{address}" }.join ", " + log "[#{config.env}] Kemal is ready to lead at #{addresses}" + end + + def self.stop + raise "Kemal is already stopped." if !config.running + if server = config.server + server.close unless server.closed? + config.running = false + else + raise "Kemal.config.server is not set. Please use Kemal.run to set the server." + end + end + + private def self.setup_404 + unless Kemal.config.error_handlers.has_key?(404) + error 404 do + render_404 + end + end + end + + private def self.setup_trap_signal + Signal::INT.trap do + log "Kemal is going to take a rest!" if Kemal.config.shutdown_message + Kemal.stop + exit + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr new file mode 100644 index 000000000000..37ee980b9caf --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr @@ -0,0 +1,9 @@ +module Kemal + # All loggers must inherit from `Kemal::BaseLogHandler`. + abstract class BaseLogHandler + include HTTP::Handler + + abstract def call(context : HTTP::Server::Context) + abstract def write(message : String) + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr new file mode 100644 index 000000000000..b1667089e2b8 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr @@ -0,0 +1,55 @@ +require "option_parser" + +module Kemal + # Handles all the initialization from the command line. + class CLI + def initialize(args) + @ssl_enabled = false + @key_file = "" + @cert_file = "" + @config = Kemal.config + if args + parse args + end + configure_ssl + end + + private def parse(args : Array(String)) + OptionParser.parse args do |opts| + opts.on("-b HOST", "--bind HOST", "Host to bind (defaults to 0.0.0.0)") do |host_binding| + @config.host_binding = host_binding + end + opts.on("-p PORT", "--port PORT", "Port to listen for connections (defaults to 3000)") do |opt_port| + @config.port = opt_port.to_i + end + opts.on("-s", "--ssl", "Enables SSL") do + @ssl_enabled = true + end + opts.on("--ssl-key-file FILE", "SSL key file") do |key_file| + @key_file = key_file + end + opts.on("--ssl-cert-file FILE", "SSL certificate file") do |cert_file| + @cert_file = cert_file + end + opts.on("-h", "--help", "Shows this help") do + puts opts + exit 0 + end + @config.extra_options.try &.call(opts) + end + end + + private def configure_ssl + {% if !flag?(:without_openssl) %} + if @ssl_enabled + abort "SSL Key Not Found" if !@key_file + abort "SSL Certificate Not Found" if !@cert_file + ssl = Kemal::SSL.new + ssl.key_file = @key_file.not_nil! + ssl.cert_file = @cert_file.not_nil! + Kemal.config.ssl = ssl.context + end + {% end %} + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr new file mode 100644 index 000000000000..2efcd66ab485 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr @@ -0,0 +1,168 @@ +module Kemal + VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} + + # Stores all the configuration options for a Kemal application. + # It's a singleton and you can access it like. + # + # ``` + # Kemal.config + # ``` + class Config + INSTANCE = Config.new + HANDLERS = [] of HTTP::Handler + CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) + FILTER_HANDLERS = [] of HTTP::Handler + ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context, Exception -> String + + {% if flag?(:without_openssl) %} + @ssl : Bool? + {% else %} + @ssl : OpenSSL::SSL::Context::Server? + {% end %} + + property host_binding, ssl, port, env, public_folder, logging, running + property always_rescue, server : HTTP::Server?, extra_options, shutdown_message + property serve_static : (Bool | Hash(String, Bool)) + property static_headers : (HTTP::Server::Response, String, File::Info -> Void)? + property powered_by_header : Bool = true + + def initialize + @host_binding = "0.0.0.0" + @port = 3000 + @env = ENV["KEMAL_ENV"]? || "development" + @serve_static = {"dir_listing" => false, "gzip" => true} + @public_folder = "./public" + @logging = true + @logger = nil + @error_handler = nil + @always_rescue = true + @router_included = false + @default_handlers_setup = false + @running = false + @shutdown_message = true + @handler_position = 0 + end + + def logger + @logger.not_nil! + end + + def logger=(logger : Kemal::BaseLogHandler) + @logger = logger + end + + def scheme + ssl ? "https" : "http" + end + + def clear + @powered_by_header = true + @router_included = false + @handler_position = 0 + @default_handlers_setup = false + HANDLERS.clear + CUSTOM_HANDLERS.clear + FILTER_HANDLERS.clear + ERROR_HANDLERS.clear + end + + def handlers + HANDLERS + end + + def handlers=(handlers : Array(HTTP::Handler)) + clear + HANDLERS.replace(handlers) + end + + def add_handler(handler : HTTP::Handler) + CUSTOM_HANDLERS << {nil, handler} + end + + def add_handler(handler : HTTP::Handler, position : Int32) + CUSTOM_HANDLERS << {position, handler} + end + + def add_filter_handler(handler : HTTP::Handler) + FILTER_HANDLERS << handler + end + + def error_handlers + ERROR_HANDLERS + end + + def add_error_handler(status_code : Int32, &handler : HTTP::Server::Context, Exception -> _) + ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } + end + + def extra_options(&@extra_options : OptionParser ->) + end + + def setup + unless @default_handlers_setup && @router_included + setup_init_handler + setup_log_handler + setup_error_handler + setup_static_file_handler + setup_custom_handlers + setup_filter_handlers + @default_handlers_setup = true + @router_included = true + HANDLERS.insert(HANDLERS.size, Kemal::WebSocketHandler::INSTANCE) + HANDLERS.insert(HANDLERS.size, Kemal::RouteHandler::INSTANCE) + end + end + + private def setup_init_handler + HANDLERS.insert(@handler_position, Kemal::InitHandler::INSTANCE) + @handler_position += 1 + end + + private def setup_log_handler + @logger ||= if @logging + Kemal::LogHandler.new + else + Kemal::NullLogHandler.new + end + HANDLERS.insert(@handler_position, @logger.not_nil!) + @handler_position += 1 + end + + private def setup_error_handler + if @always_rescue + @error_handler ||= Kemal::ExceptionHandler.new + HANDLERS.insert(@handler_position, @error_handler.not_nil!) + @handler_position += 1 + end + end + + private def setup_static_file_handler + if @serve_static.is_a?(Hash) + HANDLERS.insert(@handler_position, Kemal::StaticFileHandler.new(@public_folder)) + @handler_position += 1 + end + end + + private def setup_custom_handlers + CUSTOM_HANDLERS.each do |ch0, ch1| + position = ch0 + HANDLERS.insert (position || @handler_position), ch1 + @handler_position += 1 + end + end + + private def setup_filter_handlers + FILTER_HANDLERS.each do |h| + HANDLERS.insert(@handler_position, h) + end + end + end + + def self.config + yield Config::INSTANCE + end + + def self.config + Config::INSTANCE + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr new file mode 100644 index 000000000000..15b3742455ca --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr @@ -0,0 +1,37 @@ +# Kemal DSL is defined here and it's baked into global scope. +# +# The DSL currently consists of: +# +# - get post put patch delete options +# - WebSocket(ws) +# - before_* +# - error +HTTP_METHODS = %w(get post put patch delete options) +FILTER_METHODS = %w(get post put patch delete options all) + +{% for method in HTTP_METHODS %} + def {{method.id}}(path : String, &block : HTTP::Server::Context -> _) + raise Kemal::Exceptions::InvalidPathStartException.new({{method}}, path) unless Kemal::Utils.path_starts_with_slash?(path) + Kemal::RouteHandler::INSTANCE.add_route({{method}}.upcase, path, &block) + end +{% end %} + +def ws(path : String, &block : HTTP::WebSocket, HTTP::Server::Context -> Void) + raise Kemal::Exceptions::InvalidPathStartException.new("ws", path) unless Kemal::Utils.path_starts_with_slash?(path) + Kemal::WebSocketHandler::INSTANCE.add_route path, &block +end + +def error(status_code : Int32, &block : HTTP::Server::Context, Exception -> _) + Kemal.config.add_error_handler status_code, &block +end + +# All the helper methods available are: +# - before_all, before_get, before_post, before_put, before_patch, before_delete, before_options +# - after_all, after_get, after_post, after_put, after_patch, after_delete, after_options +{% for type in ["before", "after"] %} + {% for method in FILTER_METHODS %} + def {{type.id}}_{{method.id}}(path : String = "*", &block : HTTP::Server::Context -> _) + Kemal::FilterHandler::INSTANCE.{{type.id}}({{method}}.upcase, path, &block) + end + {% end %} +{% end %} diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr new file mode 100644 index 000000000000..eee6eecd6a3a --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr @@ -0,0 +1,30 @@ +module Kemal + # Handles all the exceptions, including 404, custom errors and 500. + class ExceptionHandler + include HTTP::Handler + INSTANCE = new + + def call(context : HTTP::Server::Context) + call_next(context) + rescue ex : Kemal::Exceptions::RouteNotFound + call_exception_with_status_code(context, ex, 404) + rescue ex : Kemal::Exceptions::CustomException + call_exception_with_status_code(context, ex, context.response.status_code) + rescue ex : Exception + log("Exception: #{ex.inspect_with_backtrace}") + return call_exception_with_status_code(context, ex, 500) if Kemal.config.error_handlers.has_key?(500) + verbosity = Kemal.config.env == "production" ? false : true + render_500(context, ex, verbosity) + end + + private def call_exception_with_status_code(context : HTTP::Server::Context, exception : Exception, status_code : Int32) + return if context.response.closed? + if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(status_code) + context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") + context.response.status_code = status_code + context.response.print Kemal.config.error_handlers[status_code].call(context, exception) + context + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr new file mode 100644 index 000000000000..c2e51c6b3178 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr @@ -0,0 +1,62 @@ +# `HTTP::Server::Context` is the class which holds `HTTP::Request` and +# `HTTP::Server::Response` alongside with information such as request params, +# request/response content_type, session data and alike. +# +# Instances of this class are passed to an `HTTP::Server` handler. +class HTTP::Server + class Context + # :nodoc: + STORE_MAPPINGS = [Nil, String, Int32, Int64, Float64, Bool] + + macro finished + alias StoreTypes = Union({{ *STORE_MAPPINGS }}) + @store = {} of String => StoreTypes + end + + def params + @params ||= Kemal::ParamParser.new(@request, route_lookup.params) + end + + def redirect(url : String, status_code : Int32 = 302, *, body : String? = nil) + @response.headers.add "Location", url + @response.status_code = status_code + @response.print(body) if body + end + + def route + route_lookup.payload + end + + def websocket + ws_route_lookup.payload + end + + def route_lookup + Kemal::RouteHandler::INSTANCE.lookup_route(@request.method.as(String), @request.path) + end + + def route_found? + route_lookup.found? + end + + def ws_route_lookup + Kemal::WebSocketHandler::INSTANCE.lookup_ws_route(@request.path) + end + + def ws_route_found? + ws_route_lookup.found? + end + + def get(name : String) + @store[name] + end + + def set(name : String, value : StoreTypes) + @store[name] = value + end + + def get?(name : String) + @store[name]? + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr new file mode 100644 index 000000000000..6fd4c01946bf --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr @@ -0,0 +1,14 @@ +class HTTP::Server::Response + class Output + def close + # ameba:disable Style/NegatedConditionsInUnless + unless response.wrote_headers? && !response.headers.has_key?("Content-Range") + response.content_length = @out_count + end + + ensure_headers_written + + previous_def + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr new file mode 100644 index 000000000000..30eb26aa609e --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr @@ -0,0 +1,24 @@ +module Kemal + struct FileUpload + getter tempfile : File + getter filename : String? + getter headers : HTTP::Headers + getter creation_time : Time? + getter modification_time : Time? + getter read_time : Time? + getter size : UInt64? + + def initialize(upload) + @tempfile = File.tempfile + ::File.open(@tempfile.path, "w") do |file| + IO.copy(upload.body, file) + end + @filename = upload.filename + @headers = upload.headers + @creation_time = upload.creation_time + @modification_time = upload.modification_time + @read_time = upload.read_time + @size = upload.size + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr new file mode 100644 index 000000000000..6d28680a7420 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr @@ -0,0 +1,90 @@ +module Kemal + # :nodoc: + class FilterHandler + include HTTP::Handler + INSTANCE = new + + # This middleware is lazily instantiated and added to the handlers as soon as a call to `after_X` or `before_X` is made. + def initialize + @tree = Radix::Tree(Array(FilterBlock)).new + Kemal.config.add_filter_handler(self) + end + + # The call order of the filters is `before_all -> before_x -> X -> after_x -> after_all`. + def call(context : HTTP::Server::Context) + return call_next(context) unless context.route_found? + call_block_for_path_type("ALL", context.request.path, :before, context) + call_block_for_path_type(context.request.method, context.request.path, :before, context) + if Kemal.config.error_handlers.has_key?(context.response.status_code) + raise Kemal::Exceptions::CustomException.new(context) + end + call_next(context) + call_block_for_path_type(context.request.method, context.request.path, :after, context) + call_block_for_path_type("ALL", context.request.path, :after, context) + context + end + + # :nodoc: This shouldn't be called directly, it's not private because + # I need to call it for testing purpose since I can't call the macros in the spec. + # It adds the block for the corresponding verb/path/type combination to the tree. + def _add_route_filter(verb : String, path, type, &block : HTTP::Server::Context -> _) + lookup = lookup_filters_for_path_type(verb, path, type) + if lookup.found? && lookup.payload.is_a?(Array(FilterBlock)) + lookup.payload << FilterBlock.new(&block) + else + @tree.add radix_path(verb, path, type), [FilterBlock.new(&block)] + end + end + + # This can be called directly but it's simpler to just use the macros, + # it will check if another filter is not already defined for this + # verb/path/type and proceed to call `add_route_filter` + def before(verb : String, path : String = "*", &block : HTTP::Server::Context -> _) + _add_route_filter verb, path, :before, &block + end + + # This can be called directly but it's simpler to just use the macros, + # it will check if another filter is not already defined for this + # verb/path/type and proceed to call `add_route_filter` + def after(verb : String, path : String = "*", &block : HTTP::Server::Context -> _) + _add_route_filter verb, path, :after, &block + end + + # This will fetch the block for the verb/path/type from the tree and call it. + private def call_block_for_path_type(verb : String?, path : String, type, context : HTTP::Server::Context) + lookup = lookup_filters_for_path_type(verb, path, type) + if lookup.found? && lookup.payload.is_a? Array(FilterBlock) + blocks = lookup.payload + blocks.each &.call(context) + end + end + + # This checks is filter is already defined for the verb/path/type combination + private def filter_for_path_type_defined?(verb : String, path : String, type) + lookup = @tree.find radix_path(verb, path, type) + lookup.found? && lookup.payload.is_a? FilterBlock + end + + # This returns a lookup for verb/path/type + private def lookup_filters_for_path_type(verb : String?, path : String, type) + @tree.find radix_path(verb, path, type) + end + + private def radix_path(verb : String?, path : String, type : Symbol) + "#{type}/#{verb}/#{path}" + end + + # :nodoc: + class FilterBlock + property block : HTTP::Server::Context -> String + + def initialize(&block : HTTP::Server::Context -> _) + @block = ->(context : HTTP::Server::Context) { block.call(context).to_s } + end + + def call(context : HTTP::Server::Context) + @block.call(context) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr new file mode 100644 index 000000000000..bb5923810acc --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr @@ -0,0 +1,80 @@ +module Kemal + # `Kemal::Handler` is a subclass of `HTTP::Handler`. + # + # It adds `only`, `only_match?`, `exclude`, `exclude_match?`. + # These methods are useful for the conditional execution of custom handlers . + class Handler + include HTTP::Handler + + @@only_routes_tree = Radix::Tree(String).new + @@exclude_routes_tree = Radix::Tree(String).new + + macro only(paths, method = "GET") + class_name = {{@type.name}} + method_downcase = {{method.downcase}} + class_name_method = "#{class_name}/#{method_downcase}" + ({{paths}}).each do |path| + @@only_routes_tree.add class_name_method + path, '/' + method_downcase + path + end + end + + macro exclude(paths, method = "GET") + class_name = {{@type.name}} + method_downcase = {{method.downcase}} + class_name_method = "#{class_name}/#{method_downcase}" + ({{paths}}).each do |path| + @@exclude_routes_tree.add class_name_method + path, '/' + method_downcase + path + end + end + + def call(env : HTTP::Server::Context) + call_next(env) + end + + # Processes the path based on `only` paths which is a `Array(String)`. + # If the path is not found on `only` conditions the handler will continue processing. + # If the path is found in `only` conditions it'll stop processing and will pass the request + # to next handler. + # + # However this is not done automatically. All handlers must inherit from `Kemal::Handler`. + # + # ``` + # class OnlyHandler < Kemal::Handler + # only ["/"] + # + # def call(env) + # return call_next(env) unless only_match?(env) + # puts "If the path is / i will be doing some processing here." + # end + # end + # ``` + def only_match?(env : HTTP::Server::Context) + @@only_routes_tree.find(radix_path(env.request.method, env.request.path)).found? + end + + # Processes the path based on `exclude` paths which is a `Array(String)`. + # If the path is not found on `exclude` conditions the handler will continue processing. + # If the path is found in `exclude` conditions it'll stop processing and will pass the request + # to next handler. + # + # However this is not done automatically. All handlers must inherit from `Kemal::Handler`. + # + # ``` + # class ExcludeHandler < Kemal::Handler + # exclude ["/"] + # + # def call(env) + # return call_next(env) if exclude_match?(env) + # puts "If the path is not / i will be doing some processing here." + # end + # end + # ``` + def exclude_match?(env : HTTP::Server::Context) + @@exclude_routes_tree.find(radix_path(env.request.method, env.request.path)).found? + end + + private def radix_path(method : String, path : String) + "#{self.class}/#{method.downcase}#{path}" + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr new file mode 100644 index 000000000000..4ec180c9bf73 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr @@ -0,0 +1,38 @@ +require "exception_page" + +module Kemal + class ExceptionPage < ExceptionPage + def styles : ExceptionPage::Styles + ExceptionPage::Styles.new( + accent: "red", + logo_uri: "data:image/png;base64,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" + ) + end + + def project_url + "https://kemalcr.com/" + end + + def self.for_production_exception + <<-HTML + + + + + + +

    Kemal has encountered an error. (500)

    +

    Something wrong with the server :(

    + + + HTML + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr new file mode 100644 index 000000000000..53520b3acaae --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr @@ -0,0 +1,20 @@ +# Exceptions for 404 and custom errors are defined here. +module Kemal::Exceptions + class InvalidPathStartException < Exception + def initialize(method : String, path : String) + super "Route declaration #{method} \"#{path}\" needs to start with '/', should be #{method} \"/#{path}\"" + end + end + + class RouteNotFound < Exception + def initialize(context : HTTP::Server::Context) + super "Requested path: '#{context.request.method}:#{context.request.path}' was not found." + end + end + + class CustomException < Exception + def initialize(context : HTTP::Server::Context) + super "Rendered error with #{context.response.status_code}" + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr new file mode 100644 index 000000000000..bf212a21b84a --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr @@ -0,0 +1,270 @@ +{% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} + require "compress/deflate" + require "compress/gzip" +{% end %} +require "mime" + +# Adds given `Kemal::Handler` to handlers chain. +# There are 5 handlers by default and all the custom handlers +# goes between the first 4 and the last `Kemal::RouteHandler`. +# +# - `Kemal::InitHandler` +# - `Kemal::LogHandler` +# - `Kemal::ExceptionHandler` +# - `Kemal::StaticFileHandler` +# - Here goes custom handlers +# - `Kemal::RouteHandler` +def add_handler(handler : HTTP::Handler) + Kemal.config.add_handler handler +end + +def add_handler(handler : HTTP::Handler, position : Int32) + Kemal.config.add_handler handler, position +end + +# Sets public folder from which the static assets will be served. +# +# By default this is `/public` not `src/public`. +def public_folder(path : String) + Kemal.config.public_folder = path +end + +# Logs the output via `logger`. +# This is the built-in `Kemal::LogHandler` by default which uses STDOUT. +def log(message : String) + Kemal.config.logger.write "#{message}\n" +end + +# Enables / Disables logging. +# This is enabled by default. +# +# ``` +# logging false +# ``` +def logging(status : Bool) + Kemal.config.logging = status +end + +# This is used to replace the built-in `Kemal::LogHandler` with a custom logger. +# +# A custom logger must inherit from `Kemal::BaseLogHandler` and must implement +# `call(env)`, `write(message)` methods. +# +# ``` +# class MyCustomLogger < Kemal::BaseLogHandler +# def call(env) +# puts "I'm logging some custom stuff here." +# call_next(env) # => This calls the next handler +# end +# +# # This is used from `log` method. +# def write(message) +# STDERR.puts message # => Logs the output to STDERR +# end +# end +# ``` +# +# Now that we have a custom logger here's how we use it +# +# ``` +# logger MyCustomLogger.new +# ``` +def logger(logger : Kemal::BaseLogHandler) + Kemal.config.logger = logger + Kemal.config.add_handler logger +end + +# Enables / Disables static file serving. +# This is enabled by default. +# +# ``` +# serve_static false +# ``` +# +# Static server also have some advanced customization options like `dir_listing` and +# `gzip`. +# +# ``` +# serve_static {"gzip" => true, "dir_listing" => false} +# ``` +def serve_static(status : (Bool | Hash)) + Kemal.config.serve_static = status +end + +# Helper for easily modifying response headers. +# This can be used to modify a response header with the given hash. +# +# ``` +# def call(env) +# headers(env, {"X-Custom-Header" => "This is a custom value"}) +# end +# ``` +def headers(env : HTTP::Server::Context, additional_headers : Hash(String, String)) + env.response.headers.merge!(additional_headers) +end + +# Send a file with given path and base the mime-type on the file extension +# or default `application/octet-stream` mime_type. +# +# ``` +# send_file env, "./path/to/file" +# ``` +# +# Optionally you can override the mime_type +# +# ``` +# send_file env, "./path/to/file", "image/jpeg" +# ``` +# +# Also you can set the filename and the disposition +# +# ``` +# send_file env, "./path/to/file", filename: "image.jpg", disposition: "attachment" +# ``` +def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil, *, filename : String? = nil, disposition : String? = nil) + config = Kemal.config.serve_static + file_path = File.expand_path(path, Dir.current) + mime_type ||= MIME.from_filename(file_path, "application/octet-stream") + env.response.content_type = mime_type + env.response.headers["Accept-Ranges"] = "bytes" + env.response.headers["X-Content-Type-Options"] = "nosniff" + minsize = 860 # http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits ?? + request_headers = env.request.headers + filesize = File.size(file_path) + filestat = File.info(file_path) + attachment(env, filename, disposition) + + Kemal.config.static_headers.try(&.call(env.response, file_path, filestat)) + + File.open(file_path) do |file| + if env.request.method == "GET" && env.request.headers.has_key?("Range") + next multipart(file, env) + end + + condition = config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Kemal::Utils.zip_types(file_path) + if condition && request_headers.includes_word?("Accept-Encoding", "gzip") + env.response.headers["Content-Encoding"] = "gzip" + {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} + Compress::Gzip::Writer.open(env.response) do |deflate| + IO.copy(file, deflate) + end + {% else %} + Gzip::Writer.open(env.response) do |deflate| + IO.copy(file, deflate) + end + {% end %} + elsif condition && request_headers.includes_word?("Accept-Encoding", "deflate") + env.response.headers["Content-Encoding"] = "deflate" + {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} + Compress::Deflate::Writer.open(env.response) do |deflate| + IO.copy(file, deflate) + end + {% else %} + Flate::Writer.open(env.response) do |deflate| + IO.copy(file, deflate) + end + {% end %} + else + env.response.content_length = filesize + IO.copy(file, env.response) + end + end + return +end + +# Send a file with given data and default `application/octet-stream` mime_type. +# +# ``` +# send_file env, data_slice +# ``` +# +# Optionally you can override the mime_type +# +# ``` +# send_file env, data_slice, "image/jpeg" +# ``` +# +# Also you can set the filename and the disposition +# +# ``` +# send_file env, data_slice, filename: "image.jpg", disposition: "attachment" +# ``` +def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil, *, filename : String? = nil, disposition : String? = nil) + mime_type ||= "application/octet-stream" + env.response.content_type = mime_type + env.response.content_length = data.bytesize + attachment(env, filename, disposition) + env.response.write data +end + +private def multipart(file, env : HTTP::Server::Context) + # See http://httpwg.org/specs/rfc7233.html + fileb = file.size + startb = endb = 0_i64 + + if match = env.request.headers["Range"].match /bytes=(\d{1,})-(\d{0,})/ + startb = match[1].to_i64 { 0_i64 } if match.size >= 2 + endb = match[2].to_i64 { 0_i64 } if match.size >= 3 + end + + endb = fileb - 1 if endb == 0 + + if startb < endb < fileb + content_length = 1_i64 + endb - startb + env.response.status_code = 206 + env.response.content_length = content_length + env.response.headers["Accept-Ranges"] = "bytes" + env.response.headers["Content-Range"] = "bytes #{startb}-#{endb}/#{fileb}" # MUST + + if startb > 1024 + skipped = 0_i64 + # file.skip only accepts values less or equal to 1024 (buffer size, undocumented) + until (increase_skipped = skipped + 1024_i64) > startb + file.skip(1024) + skipped = increase_skipped + end + if (skipped_minus_startb = skipped - startb) > 0 + file.skip skipped_minus_startb + end + else + file.skip(startb) + end + + IO.copy(file, env.response, content_length) + else + env.response.content_length = fileb + env.response.status_code = 200 # Range not satisfable, see 4.4 Note + IO.copy(file, env.response) + end +end + +# Set the Content-Disposition to "attachment" with the specified filename, +# instructing the user agents to prompt to save. +private def attachment(env : HTTP::Server::Context, filename : String? = nil, disposition : String? = nil) + disposition = "attachment" if disposition.nil? && filename + if disposition && filename + env.response.headers["Content-Disposition"] = "#{disposition}; filename=\"#{File.basename(filename)}\"" + end +end + +# Configures an `HTTP::Server::Response` to compress the response +# output, either using gzip or deflate, depending on the `Accept-Encoding` request header. +# +# Disabled by default. +def gzip(status : Bool = false) + add_handler HTTP::CompressHandler.new if status +end + +# Adds headers to `Kemal::StaticFileHandler`. This is especially useful for `CORS`. +# +# ``` +# static_headers do |response, filepath, filestat| +# if filepath =~ /\.html$/ +# response.headers.add("Access-Control-Allow-Origin", "*") +# end +# response.headers.add("Content-Size", filestat.size.to_s) +# end +# ``` +def static_headers(&headers : HTTP::Server::Response, String, File::Info -> Void) + Kemal.config.static_headers = headers +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr new file mode 100644 index 000000000000..4b5e3090fffe --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr @@ -0,0 +1,98 @@ +require "kilt" + +CONTENT_FOR_BLOCKS = Hash(String, Tuple(String, Proc(String))).new + +# `content_for` is a set of helpers that allows you to capture +# blocks inside views to be rendered later during the request. The most +# common use is to populate different parts of your layout from your view. +# +# The currently supported engines are: ecr and slang. +# +# ## Usage +# +# You call `content_for`, generally from a view, to capture a block of markup +# giving it an identifier: +# +# ``` +# # index.ecr +# <% content_for "some_key" do %> +# ... +# <% end %> +# ``` +# +# Then, you call `yield_content` with that identifier, generally from a +# layout, to render the captured block: +# +# ``` +# # layout.ecr +# <%= yield_content "some_key" %> +# ``` +# +# ## And How Is This Useful? +# +# For example, some of your views might need a few javascript tags and +# stylesheets, but you don't want to force this files in all your pages. +# Then you can put `<%= yield_content :scripts_and_styles %>` on your +# layout, inside the tag, and each view can call `content_for` +# setting the appropriate set of tags that should be added to the layout. +macro content_for(key, file = __FILE__) + %proc = ->() { + __kilt_io__ = IO::Memory.new + {{ yield }} + __kilt_io__.to_s + } + + CONTENT_FOR_BLOCKS[{{key}}] = Tuple.new {{file}}, %proc + nil +end + +# Yields content for the given key if a `content_for` block exists for that key. +macro yield_content(key) + if CONTENT_FOR_BLOCKS.has_key?({{key}}) + __caller_filename__ = CONTENT_FOR_BLOCKS[{{key}}][0] + %proc = CONTENT_FOR_BLOCKS[{{key}}][1] + %proc.call if __content_filename__ == __caller_filename__ + end +end + +# Render view with a layout as the superview. +# +# ``` +# render "src/views/index.ecr", "src/views/layout.ecr" +# ``` +macro render(filename, layout) + __content_filename__ = {{filename}} + content = render {{filename}} + render {{layout}} +end + +# Render view with the given filename. +macro render(filename) + Kilt.render({{filename}}) +end + +# Halt execution with the current context. +# Returns 200 and an empty response by default. +# +# ``` +# halt env, status_code: 403, response: "Forbidden" +# ``` +macro halt(env, status_code = 200, response = "") + {{env}}.response.status_code = {{status_code}} + {{env}}.response.print {{response}} + {{env}}.response.close + next +end + +# Extends context storage with user defined types. +# +# ``` +# class User +# property name +# end +# +# add_context_storage_type(User) +# ``` +macro add_context_storage_type(type) + {{ HTTP::Server::Context::STORE_MAPPINGS.push(type) }} +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr new file mode 100644 index 000000000000..b343fc8a3cd2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr @@ -0,0 +1,35 @@ +# This file contains the built-in view templates that Kemal uses. +# Currently it contains templates for 404 and 500 error codes. + +def render_404 + <<-HTML + + + + + + +

    Kemal doesn't know this way.

    + + + + HTML +end + +def render_500(context, exception, verbosity) + context.response.status_code = 500 + + template = if verbosity + Kemal::ExceptionPage.for_runtime_exception(context, exception).to_s + else + Kemal::ExceptionPage.for_production_exception + end + + context.response.print template + context +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr new file mode 100644 index 000000000000..3ece5d448015 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr @@ -0,0 +1,13 @@ +module Kemal + module Utils + ZIP_TYPES = {".htm", ".html", ".txt", ".css", ".js", ".svg", ".json", ".xml", ".otf", ".ttf", ".woff", ".woff2"} + + def self.path_starts_with_slash?(path : String) + path.starts_with? '/' + end + + def self.zip_types(path : String) # https://github.com/h5bp/server-configs-nginx/blob/master/nginx.conf + ZIP_TYPES.includes? File.extname(path) + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr new file mode 100644 index 000000000000..881325b6cc5c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr @@ -0,0 +1,15 @@ +module Kemal + # Initializes the context with default values, such as + # *Content-Type* or *X-Powered-By* headers. + class InitHandler + include HTTP::Handler + + INSTANCE = new + + def call(context : HTTP::Server::Context) + context.response.headers.add "X-Powered-By", "Kemal" if Kemal.config.powered_by_header + context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") + call_next context + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr new file mode 100644 index 000000000000..ce08e571a4ad --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr @@ -0,0 +1,28 @@ +module Kemal + # Uses `STDOUT` by default and handles the logging of request/response process time. + class LogHandler < Kemal::BaseLogHandler + def initialize(@io : IO = STDOUT) + end + + def call(context : HTTP::Server::Context) + elapsed_time = Time.measure { call_next(context) } + elapsed_text = elapsed_text(elapsed_time) + @io << Time.utc << ' ' << context.response.status_code << ' ' << context.request.method << ' ' << context.request.resource << ' ' << elapsed_text << '\n' + @io.flush + context + end + + def write(message : String) + @io << message + @io.flush + @io + end + + private def elapsed_text(elapsed) + millis = elapsed.total_milliseconds + return "#{millis.round(2)}ms" if millis >= 1 + + "#{(millis * 1000).round(2)}µs" + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr new file mode 100644 index 000000000000..9f3e03abc30b --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr @@ -0,0 +1,11 @@ +module Kemal + # This is here to represent the logger corresponding to Null Object Pattern. + class NullLogHandler < Kemal::BaseLogHandler + def call(context : HTTP::Server::Context) + call_next(context) + end + + def write(message : String) + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr new file mode 100644 index 000000000000..5d87ba0e9bf2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr @@ -0,0 +1,113 @@ +module Kemal + # Parses the request contents including query_params and body + # and converts them into a params hash which you can use within + # the environment context. + class ParamParser + URL_ENCODED_FORM = "application/x-www-form-urlencoded" + APPLICATION_JSON = "application/json" + MULTIPART_FORM = "multipart/form-data" + PARTS = %w(url query body json files) + # :nodoc: + alias AllParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Any) | Array(JSON::Any) + getter files + + def initialize(@request : HTTP::Request, @url : Hash(String, String) = {} of String => String) + @query = HTTP::Params.new({} of String => Array(String)) + @body = HTTP::Params.new({} of String => Array(String)) + @json = {} of String => AllParamTypes + @files = {} of String => FileUpload + @url_parsed = false + @query_parsed = false + @body_parsed = false + @json_parsed = false + @files_parsed = false + end + + private def unescape_url_param(value : String) + value.empty? ? value : URI.decode(value) + rescue + value + end + + {% for method in PARTS %} + def {{method.id}} + # check memoization + return @{{method.id}} if @{{method.id}}_parsed + + parse_{{method.id}} + # memoize + @{{method.id}}_parsed = true + @{{method.id}} + end + {% end %} + + private def parse_body + content_type = @request.headers["Content-Type"]? + + return unless content_type + + if content_type.try(&.starts_with?(URL_ENCODED_FORM)) + @body = parse_part(@request.body) + return + end + + if content_type.try(&.starts_with?(MULTIPART_FORM)) + parse_files + end + end + + private def parse_query + @query = parse_part(@request.query) + end + + private def parse_url + @url.each { |key, value| @url[key] = unescape_url_param(value) } + end + + private def parse_files + return if @files_parsed + + HTTP::FormData.parse(@request) do |upload| + next unless upload + + filename = upload.filename + + if !filename.nil? + @files[upload.name] = FileUpload.new(upload) + else + @body.add(upload.name, upload.body.gets_to_end) + end + end + + @files_parsed = true + end + + # Parses JSON request body if Content-Type is `application/json`. + # + # - If request body is a JSON `Hash` then all the params are parsed and added into `params`. + # - If request body is a JSON `Array` it's added into `params` as `_json` and can be accessed like `params["_json"]`. + private def parse_json + return unless @request.body && @request.headers["Content-Type"]?.try(&.starts_with?(APPLICATION_JSON)) + + body = @request.body.not_nil!.gets_to_end + case json = JSON.parse(body).raw + when Hash + json.each do |key, value| + @json[key] = value.raw + end + when Array + @json["_json"] = json + else + # Ignore non Array or Hash json values + end + end + + private def parse_part(part : IO?) + HTTP::Params.parse(part ? part.gets_to_end : "") + end + + private def parse_part(part : String?) + HTTP::Params.parse part.to_s + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr new file mode 100644 index 000000000000..cfcf29ec30c4 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr @@ -0,0 +1,17 @@ +module Kemal + # Route is the main building block of Kemal. + # + # It takes 3 parameters: http *method*, *path* and a *handler* to specify + # what action to be done if the route is matched. + struct Route + getter method, path, handler + @handler : HTTP::Server::Context -> String + + def initialize(@method : String, @path : String, &handler : HTTP::Server::Context -> _) + @handler = ->(context : HTTP::Server::Context) do + output = handler.call(context) + output.is_a?(String) ? output : "" + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr new file mode 100644 index 000000000000..216616a97cb2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr @@ -0,0 +1,68 @@ +require "radix" + +module Kemal + class RouteHandler + include HTTP::Handler + + INSTANCE = new + CACHED_ROUTES_LIMIT = 1024 + property routes, cached_routes + + def initialize + @routes = Radix::Tree(Route).new + @cached_routes = Hash(String, Radix::Result(Route)).new + end + + def call(context : HTTP::Server::Context) + process_request(context) + end + + # Adds a given route to routing tree. As an exception each `GET` route additionaly defines + # a corresponding `HEAD` route. + def add_route(method : String, path : String, &handler : HTTP::Server::Context -> _) + add_to_radix_tree method, path, Route.new(method, path, &handler) + add_to_radix_tree("HEAD", path, Route.new("HEAD", path) { }) if method == "GET" + end + + # Looks up the route from the Radix::Tree for the first time and caches to improve performance. + def lookup_route(verb : String, path : String) + lookup_path = radix_path(verb, path) + + if cached_route = @cached_routes[lookup_path]? + return cached_route + end + + route = @routes.find(lookup_path) + + if route.found? + @cached_routes.clear if @cached_routes.size == CACHED_ROUTES_LIMIT + @cached_routes[lookup_path] = route + end + + route + end + + # Processes the route if it's a match. Otherwise renders 404. + private def process_request(context) + raise Kemal::Exceptions::RouteNotFound.new(context) unless context.route_found? + return if context.response.closed? + content = context.route.handler.call(context) + + if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(context.response.status_code) + raise Kemal::Exceptions::CustomException.new(context) + end + + context.response.print(content) + context + end + + private def radix_path(method, path) + '/' + method.downcase + path + end + + private def add_to_radix_tree(method, path, route) + node = radix_path method, path + @routes.add node, route + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr new file mode 100644 index 000000000000..e205b18b2584 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr @@ -0,0 +1,17 @@ +module Kemal + class SSL + getter context + + def initialize + @context = OpenSSL::SSL::Context::Server.new + end + + def key_file=(key_file : String) + @context.private_key = key_file + end + + def cert_file=(cert_file : String) + @context.certificate_chain = cert_file + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr new file mode 100644 index 000000000000..109e971bbeb2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr @@ -0,0 +1,68 @@ +module Kemal + class StaticFileHandler < HTTP::StaticFileHandler + # ameba:disable Metrics/CyclomaticComplexity + def call(context : HTTP::Server::Context) + return call_next(context) if context.request.path.not_nil! == "/" + + case context.request.method + when "GET", "HEAD" + else + if @fallthrough + call_next(context) + else + context.response.status_code = 405 + context.response.headers.add("Allow", "GET, HEAD") + end + return + end + + config = Kemal.config.serve_static + original_path = context.request.path.not_nil! + request_path = URI.decode(original_path) + + # File path cannot contains '\0' (NUL) because all filesystem I know + # don't accept '\0' character as file name. + if request_path.includes? '\0' + context.response.status_code = 400 + return + end + + expanded_path = File.expand_path(request_path, "/") + is_dir_path = if original_path.ends_with?('/') && !expanded_path.ends_with? '/' + expanded_path = expanded_path + '/' + true + else + expanded_path.ends_with? '/' + end + + file_path = File.join(@public_dir, expanded_path) + is_dir = Dir.exists? file_path + + if request_path != expanded_path + redirect_to context, expanded_path + elsif is_dir && !is_dir_path + redirect_to context, expanded_path + '/' + end + + if Dir.exists?(file_path) + if config.is_a?(Hash) && config["dir_listing"] == true + context.response.content_type = "text/html" + directory_listing(context.response, request_path, file_path) + else + call_next(context) + end + elsif File.exists?(file_path) + last_modified = modification_time(file_path) + add_cache_headers(context.response.headers, last_modified) + + if cache_request?(context, last_modified) + context.response.status_code = 304 + return + end + send_file(context, file_path) + else + call_next(context) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr new file mode 100644 index 000000000000..2b65f8ec3230 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr @@ -0,0 +1,14 @@ +module Kemal + # Takes 2 parameters: *path* and a *handler* to specify + # what action to be done if the route is matched. + class WebSocket < HTTP::WebSocketHandler + getter proc + + def initialize(@path : String, &@proc : HTTP::WebSocket, HTTP::Server::Context -> Void) + end + + def call(context : HTTP::Server::Context) + super + end + end +end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr new file mode 100644 index 000000000000..b1432a01d935 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr @@ -0,0 +1,41 @@ +module Kemal + class WebSocketHandler + include HTTP::Handler + + INSTANCE = new + property routes + + def initialize + @routes = Radix::Tree(WebSocket).new + end + + def call(context : HTTP::Server::Context) + return call_next(context) unless context.ws_route_found? && websocket_upgrade_request?(context) + context.websocket.call(context) + end + + def lookup_ws_route(path : String) + @routes.find "/ws" + path + end + + def add_route(path : String, &handler : HTTP::WebSocket, HTTP::Server::Context -> Void) + add_to_radix_tree path, WebSocket.new(path, &handler) + end + + private def add_to_radix_tree(path, websocket) + node = radix_path "ws", path + @routes.add node, websocket + end + + private def radix_path(method, path) + '/' + method.downcase + path + end + + private def websocket_upgrade_request?(context) + return unless upgrade = context.request.headers["Upgrade"]? + return unless upgrade.compare("websocket", case_insensitive: true) == 0 + + context.request.headers.includes_word?("Connection", "Upgrade") + end + end +end diff --git a/samples/client/petstore/crystal/lib/kilt/.gitignore b/samples/client/petstore/crystal/lib/kilt/.gitignore new file mode 100644 index 000000000000..2e5d65e9a41b --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/.gitignore @@ -0,0 +1,10 @@ +/doc/ +/lib/ +/.crystal/ +/.shards/ + + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock + diff --git a/samples/client/petstore/crystal/lib/kilt/.travis.yml b/samples/client/petstore/crystal/lib/kilt/.travis.yml new file mode 100644 index 000000000000..ffc7b6ac56d1 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/.travis.yml @@ -0,0 +1 @@ +language: crystal diff --git a/samples/client/petstore/crystal/lib/kilt/LICENSE b/samples/client/petstore/crystal/lib/kilt/LICENSE new file mode 100644 index 000000000000..f7175effc64c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jerome Gravel-Niquet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/kilt/README.md b/samples/client/petstore/crystal/lib/kilt/README.md new file mode 100644 index 000000000000..43272988e597 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/README.md @@ -0,0 +1,105 @@ +# Kilt [![Build Status](https://travis-ci.org/jeromegn/kilt.svg?branch=master)](https://travis-ci.org/jeromegn/kilt) [![Dependency Status](https://shards.rocks/badge/github/jeromegn/kilt/status.svg)](https://shards.rocks/github/jeromegn/kilt) [![devDependency Status](https://shards.rocks/badge/github/jeromegn/kilt/dev_status.svg)](https://shards.rocks/github/jeromegn/kilt) + +Generic templating interface for Crystal. + +## Goal + +Simplify developers' lives by abstracting template rendering for multiple template languages. + +## Supported out of the box + +| Language | File extensions | Required libraries | Maintainer | +| -------- | --------------- | ------------------ | ---------- | +| ECR | .ecr | none (part of the stdlib) | | +| Mustache | .mustache | [crustache](https://github.com/MakeNowJust/crustache) | [@MakeNowJust](https://github.com/MakeNowJust) | +| Slang | .slang | [slang](https://github.com/jeromegn/slang) | [@jeromegn](https://github.com/jeromegn) | +| Temel | .temel | [temel](https://github.com/f/temel) | [@f](https://github.com/f) | +| Crikey | .crikey | [crikey](https://github.com/domgetter/crikey) | [@domgetter](https://github.com/domgetter) | + +See also: +[Registering your own template engine](#registering-your-own-template-engine). + +## Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + kilt: + github: jeromegn/kilt + + # Any other template languages Crystal shard +``` + +## Usage + +- Kilt essentially adds two macros `Kilt.embed` and `Kilt.file`, the code is really simple. +- Add template language dependencies, as listed in the support table above. + +Both macros take a `filename` and a `io_name` (the latter defaults to `"__kilt_io__"`) + +### Example + +```crystal +require "kilt" + +# For slang, add: +require "kilt/slang" + +# With a Class + +class YourView + Kilt.file("path/to/template.ecr") # Adds a to_s method +end +puts YourView.new.to_s # => + + +# Embedded + +str = Kilt.render "path/to/template.slang" + +# or + +str = String.build do |__kilt_io__| + Kilt.embed "path/to/template.slang" +end + +puts str # => +``` + +## Registering your own template engine + +Use `Kilt.register_engine(extension, embed_command)` macro: + +```crystal +require "kilt" + +module MyEngine + macro embed(filename, io_name) + # .... + end +end + +Kilt.register_engine("myeng", MyEngine.embed) +``` + +This can be part of your own `my-engine` library: in this case it should depend +on `kilt` directly, or this could be a part of adapter library, like: +`kilt-my-engine`, which will depend on both `kilt` and `my-engine`. + +## Contributing + +Please contribute your own "adapter" if you create a template language for Crystal that's not yet supported here! + +1. Fork it ( https://github.com/jeromegn/kilt/fork ) +2. Create your feature branch (git checkout -b my-awesome-template-language) +3. Commit your changes (git commit -am 'Add my-awesome-template-language') +4. Push to the branch (git push origin my-awesome-template-language) +5. Create a new Pull Request + +## Contributors + +- [jeromegn](https://github.com/jeromegn) Jerome Gravel-Niquet - creator, maintainer +- [waterlink](https://github.com/waterlink) Oleksii Fedorov +- [MakeNowJust](https://github.com/MakeNowJust) TSUYUSATO Kitsune +- [f](https://github.com/f) Fatih Kadir Akın diff --git a/samples/client/petstore/crystal/lib/kilt/lib b/samples/client/petstore/crystal/lib/kilt/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/shard.yml b/samples/client/petstore/crystal/lib/kilt/shard.yml new file mode 100644 index 000000000000..562cb39c63f9 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/shard.yml @@ -0,0 +1,15 @@ +name: kilt +version: 0.4.0 + +authors: + - Jerome Gravel-Niquet + +license: MIT + +development_dependencies: + slang: + github: jeromegn/slang + crustache: + github: MakeNowJust/crustache + temel: + github: f/temel diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr new file mode 100644 index 000000000000..7ae325564d73 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr @@ -0,0 +1 @@ +<%= Process.pid %> \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache new file mode 100644 index 000000000000..f31f09dc559c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache @@ -0,0 +1 @@ +{{pid}} \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw new file mode 100644 index 000000000000..c57eff55ebc0 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang new file mode 100644 index 000000000000..e8325c711106 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang @@ -0,0 +1 @@ +span = Process.pid \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel new file mode 100644 index 000000000000..b214e1f4633a --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel @@ -0,0 +1 @@ +span Process.pid diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr new file mode 100644 index 000000000000..f62b7d26e9a6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr @@ -0,0 +1,26 @@ +require "../spec_helper" +require "../../src/crustache" + +class MustacheView + def has_key?(name) + name == "pid" + end + + def [](name) + name == "pid" ? Process.pid : nil + end + + Kilt.file "spec/fixtures/test.mustache", "__kilt_io__", self +end + +describe "kilt/crustache" do + + it "renders crustache" do + Kilt.render("spec/fixtures/test.mustache", { "pid" => Process.pid }).should eq("#{Process.pid}") + end + + it "works with classes" do + MustacheView.new.to_s.should eq("#{Process.pid}") + end + +end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr new file mode 100644 index 000000000000..4dbf66128183 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr @@ -0,0 +1,18 @@ +require "../spec_helper" +require "../../src/slang" + +class SlangView + Kilt.file "spec/fixtures/test.slang" +end + +describe "kilt/slang" do + + it "renders slang" do + Kilt.render("spec/fixtures/test.slang").should eq("#{Process.pid}") + end + + it "works with classes" do + SlangView.new.to_s.should eq("#{Process.pid}") + end + +end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr new file mode 100644 index 000000000000..aed4b48b3468 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr @@ -0,0 +1,18 @@ +# require "../spec_helper" +# require "../../src/temel" + +# class TemelView +# Kilt.file "spec/fixtures/test.temel" +# end + +# describe "kilt/temel" do + +# it "renders temel" do +# Kilt.render("spec/fixtures/test.temel").should eq("#{Process.pid}") +# end + +# it "works with classes" do +# TemelView.new.to_s.should eq("#{Process.pid}") +# end + +# end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr new file mode 100644 index 000000000000..dc3a3e503ac6 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr @@ -0,0 +1,28 @@ +require "./spec_helper" + +class View + Kilt.file "spec/fixtures/test.ecr" +end + +describe Kilt do + + it "renders ecr" do + Kilt.render("spec/fixtures/test.ecr").should eq("#{Process.pid}") + end + + it "works with classes" do + View.new.to_s.should eq("#{Process.pid}") + end + + it "raises with unsupported filetype" do + expect_raises(Kilt::Exception, "Unsupported template engine for extension: \"abc\"") { + Kilt.render("test.abc") + } + end + + it "renders registered engine" do + Kilt.register_engine "raw", Raw.embed + Kilt.render("spec/fixtures/test.raw").should eq("Hello World!") + end + +end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr new file mode 100644 index 000000000000..d69d98e36c4c --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr @@ -0,0 +1,3 @@ +require "spec" +require "../src/kilt" +require "./support/raw_engine" \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr b/samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr new file mode 100644 index 000000000000..2e0f9dc66e95 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr @@ -0,0 +1,5 @@ +module Raw + macro embed(filename, io) + {{ io.id }} << {{`cat #{filename}`.stringify}} + end +end \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/src/crikey.cr b/samples/client/petstore/crystal/lib/kilt/src/crikey.cr new file mode 100644 index 000000000000..f20b9e2129ba --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/crikey.cr @@ -0,0 +1,4 @@ +require "./kilt" +require "crikey" + +Kilt.register_engine "crikey", Crikey.embed diff --git a/samples/client/petstore/crystal/lib/kilt/src/crustache.cr b/samples/client/petstore/crystal/lib/kilt/src/crustache.cr new file mode 100644 index 000000000000..e766e6b40736 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/crustache.cr @@ -0,0 +1,4 @@ +require "./kilt" +require "crustache" + +Kilt.register_engine "mustache", Mustache.embed \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/src/ecr.cr b/samples/client/petstore/crystal/lib/kilt/src/ecr.cr new file mode 100644 index 000000000000..2360c0d42e19 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/ecr.cr @@ -0,0 +1,3 @@ +require "ecr/macros" + +Kilt.register_engine("ecr", ECR.embed) diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt.cr new file mode 100644 index 000000000000..6dfbb0e820b2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/kilt.cr @@ -0,0 +1,35 @@ +require "./kilt/version" +require "./kilt/exception" + +module Kilt + # macro only constant + ENGINES = {} of String => Int32 + + macro register_engine(ext, embed_macro) + {% Kilt::ENGINES[ext] = embed_macro.id %} + end + + macro embed(filename, io_name = "__kilt_io__", *args) + {% ext = filename.split(".").last %} + + {% if Kilt::ENGINES[ext] %} + {{Kilt::ENGINES[ext]}}({{filename}}, {{io_name}}, {{*args}}) + {% else %} + raise Kilt::Exception.new("Unsupported template engine for extension: \"" + {{ext}} + "\"") + {% end %} + end + + macro render(filename, *args) + String.build do |__kilt_io__| + Kilt.embed({{filename}}, "__kilt_io__", {{*args}}) + end + end + + macro file(filename, io_name = "__kilt_io__", *args) + def to_s({{io_name.id}}) + Kilt.embed({{filename}}, {{io_name}}, {{*args}}) + end + end +end + +require "./ecr" diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr new file mode 100644 index 000000000000..2e43dfd3f47a --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr @@ -0,0 +1,5 @@ +module Kilt + class Exception < ::Exception + # Nothing special + end +end \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr new file mode 100644 index 000000000000..38967c2556ec --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr @@ -0,0 +1,3 @@ +require "temel" + +puts File.read(ARGV[0]).to_s STDOUT diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr new file mode 100644 index 000000000000..2153f5ad1e8d --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr @@ -0,0 +1,3 @@ +module Kilt + VERSION = "0.4.0" +end diff --git a/samples/client/petstore/crystal/lib/kilt/src/slang.cr b/samples/client/petstore/crystal/lib/kilt/src/slang.cr new file mode 100644 index 000000000000..249af60310fc --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/slang.cr @@ -0,0 +1,4 @@ +require "./kilt" +require "slang" + +Kilt.register_engine "slang", Slang.embed diff --git a/samples/client/petstore/crystal/lib/kilt/src/temel.cr b/samples/client/petstore/crystal/lib/kilt/src/temel.cr new file mode 100644 index 000000000000..13a6423eb918 --- /dev/null +++ b/samples/client/petstore/crystal/lib/kilt/src/temel.cr @@ -0,0 +1,9 @@ +require "./kilt" +require "temel" + +macro embed_temel(filename, __kilt_io__) + __kilt_io__ << {{ run("./kilt/helpers/temel_embedder.cr", filename) }} + __kilt_io__ +end + +Kilt.register_engine "temel", embed_temel diff --git a/samples/client/petstore/crystal/lib/radix/.gitignore b/samples/client/petstore/crystal/lib/radix/.gitignore new file mode 100644 index 000000000000..591e49cd520c --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/.gitignore @@ -0,0 +1,8 @@ +/doc/ +/lib/ +/.crystal/ +/.shards/ + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock diff --git a/samples/client/petstore/crystal/lib/radix/.travis.yml b/samples/client/petstore/crystal/lib/radix/.travis.yml new file mode 100644 index 000000000000..33cd2975b2eb --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/.travis.yml @@ -0,0 +1,11 @@ +language: crystal +crystal: + - latest + - nightly +matrix: + allow_failures: + - crystal: nightly + +notifications: + email: + on_success: never diff --git a/samples/client/petstore/crystal/lib/radix/CHANGELOG.md b/samples/client/petstore/crystal/lib/radix/CHANGELOG.md new file mode 100644 index 000000000000..81ddc75bbdc2 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/CHANGELOG.md @@ -0,0 +1,102 @@ +# Change Log + +All notable changes to Radix project will be documented in this file. +This project aims to comply with [Semantic Versioning](http://semver.org/), +so please check *Changed* and *Removed* notes before upgrading. + +## [Unreleased] + +## [0.3.9] - 2019-01-02 +### Fixed +- Correct catch-all issue caused when paths differ [#26](https://github.com/luislavena/radix/pull/26) (@silasb) + +## [0.3.8] - 2017-03-12 +### Fixed +- Correct lookup issue caused by incorrect comparison of shared key [#21](https://github.com/luislavena/radix/issues/21) +- Improve support for non-ascii keys in a tree. + +## [0.3.7] - 2017-02-04 +### Fixed +- Correct prioritization of node's children using combination of kind and + priority, allowing partial shared keys to coexist and resolve lookup. + +## [0.3.6] - 2017-01-18 +### Fixed +- Correct lookup issue caused by similar priority between named paramter and + shared partial key [kemalcr/kemal#293](https://github.com/kemalcr/kemal/issues/293) + +## [0.3.5] - 2016-11-24 +### Fixed +- Correct lookup issue when dealing with catch all and shared partial key (@crisward) + +## [0.3.4] - 2016-11-12 +### Fixed +- Ensure catch all parameter can be used as optional globbing (@jwoertink) + +## [0.3.3] - 2016-11-12 [YANKED] +### Fixed +- Ensure catch all parameter can be used as optional globbing (@jwoertink) + +## [0.3.2] - 2016-11-05 +### Fixed +- Do not force adding paths with shared named parameter in an specific order (@jwoertink) +- Give proper name to `Radix::VERSION` spec when running in verbose mode. +- Ensure code samples in docs can be executed. + +## [0.3.1] - 2016-07-29 +### Added +- Introduce `Radix::VERSION` so library version can be used at runtime. + +## [0.3.0] - 2016-04-16 +### Fixed +- Improve forward compatibility with newer versions of the compiler by adding + missing types to solve type inference errors. + +### Changed +- `Radix::Tree` now requires the usage of a type which will be used as node's + payload. See [README](README.md) for details. + +## [0.2.1] - 2016-03-15 +### Fixed +- Correct `Result#key` incorrect inferred type. + +### Removed +- Attempt to use two named parameters at the same level will raise + `Radix::Tree::SharedKeyError` + +## [0.2.0] - 2016-03-15 [YANKED] +### Removed +- Attempt to use two named parameters at the same level will raise + `Radix::Tree::SharedKeyError` + +## [0.1.2] - 2016-03-10 +### Fixed +- No longer split named parameters that share same level (@alsm) + +### Changed +- Attempt to use two named parameters at same level will display a + deprecation warning. Future versions will raise `Radix::Tree::SharedKeyError` + +## [0.1.1] - 2016-02-29 +### Fixed +- Fix named parameter key names extraction. + +## [0.1.0] - 2016-01-24 +### Added +- Initial release based on code extracted from Beryl. + +[Unreleased]: https://github.com/luislavena/radix/compare/v0.3.9...HEAD +[0.3.9]: https://github.com/luislavena/radix/compare/v0.3.8...v0.3.9 +[0.3.8]: https://github.com/luislavena/radix/compare/v0.3.7...v0.3.8 +[0.3.7]: https://github.com/luislavena/radix/compare/v0.3.6...v0.3.7 +[0.3.6]: https://github.com/luislavena/radix/compare/v0.3.5...v0.3.6 +[0.3.5]: https://github.com/luislavena/radix/compare/v0.3.4...v0.3.5 +[0.3.4]: https://github.com/luislavena/radix/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/luislavena/radix/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/luislavena/radix/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/luislavena/radix/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/luislavena/radix/compare/v0.2.1...v0.3.0 +[0.2.1]: https://github.com/luislavena/radix/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/luislavena/radix/compare/v0.1.2...v0.2.0 +[0.1.2]: https://github.com/luislavena/radix/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/luislavena/radix/compare/v0.1.0...v0.1.1 diff --git a/samples/client/petstore/crystal/lib/radix/LICENSE b/samples/client/petstore/crystal/lib/radix/LICENSE new file mode 100644 index 000000000000..15bbbc167af5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Luis Lavena + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/radix/Makefile b/samples/client/petstore/crystal/lib/radix/Makefile new file mode 100644 index 000000000000..d5c063221459 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/Makefile @@ -0,0 +1,18 @@ +CRYSTAL ?= crystal + +profile ?= ## Display profiling information after specs execution +verbose ?= ## Run specs in verbose mode + +SPEC_FLAGS := $(if $(profile),--profile )$(if $(verbose),--verbose ) + +.PHONY: default autospec spec + +default: spec + +# `autospec` task uses `watchexec` external dependency: +# https://github.com/mattgreen/watchexec +autospec: + watchexec --exts cr --watch spec --watch src --clear $(CRYSTAL) spec $(SPEC_FLAGS) + +spec: + $(CRYSTAL) spec $(SPEC_FLAGS) \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/radix/README.md b/samples/client/petstore/crystal/lib/radix/README.md new file mode 100644 index 000000000000..0fe266dec643 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/README.md @@ -0,0 +1,129 @@ +# Radix Tree + +[Radix tree](https://en.wikipedia.org/wiki/Radix_tree) implementation for +Crystal language + +[![Build Status](https://img.shields.io/travis/luislavena/radix/master.svg)](https://travis-ci.org/luislavena/radix) +[![Latest Release](https://img.shields.io/github/release/luislavena/radix.svg)](https://github.com/luislavena/radix/releases) + +## Installation + +Add this to your application's `shard.yml`: + +```yaml +dependencies: + radix: + github: luislavena/radix +``` + +## Usage + +### Building Trees + +You can associate a *payload* with each path added to the tree: + +```crystal +require "radix" + +tree = Radix::Tree(Symbol).new +tree.add "/products", :products +tree.add "/products/featured", :featured + +result = tree.find "/products/featured" + +if result.found? + puts result.payload # => :featured +end +``` + +The types allowed for payload are defined on Tree definition: + +```crystal +tree = Radix::Tree(Symbol).new + +# Good, since Symbol is allowed as payload +tree.add "/", :root + +# Compilation error, Int32 is not allowed +tree.add "/meaning-of-life", 42 +``` + +Can combine multiple types if needed: + +```crystal +tree = Radix::Tree(Int32 | String | Symbol).new + +tree.add "/", :root +tree.add "/meaning-of-life", 42 +tree.add "/hello", "world" +``` + +### Lookup and placeholders + +You can also extract values from placeholders (as named segments or globbing): + +```crystal +tree.add "/products/:id", :product + +result = tree.find "/products/1234" + +if result.found? + puts result.params["id"]? # => "1234" +end +``` + +Please see `Radix::Tree#add` documentation for more usage examples. + +## Caveats + +Pretty much all Radix implementations have their limitations and this project +is no exception. + +When designing and adding *paths* to a Tree, please consider that two different +named parameters cannot share the same level: + +```crystal +tree.add "/", :root +tree.add "/:post", :post +tree.add "/:category/:post", :category_post # => Radix::Tree::SharedKeyError +``` + +This is because different named parameters at the same level will result in +incorrect `params` when lookup is performed, and sometimes the value for +`post` or `category` parameters will not be stored as expected. + +To avoid this issue, usage of explicit keys that differentiate each path is +recommended. + +For example, following a good SEO practice will be consider `/:post` as +absolute permalink for the post and have a list of categories which links to +the permalinks of the posts under that category: + +```crystal +tree.add "/", :root +tree.add "/:post", :post # this is post permalink +tree.add "/categories", :categories # list of categories +tree.add "/categories/:category", :category # listing of posts under each category +``` + +## Implementation + +This project has been inspired and adapted from +[julienschmidt/httprouter](https://github.com/julienschmidt/httprouter) and +[spriet2000/vertx-http-router](https://github.com/spriet2000/vertx-http-router) +Go and Java implementations, respectively. + +Changes to logic and optimizations have been made to take advantage of +Crystal's features. + +## Contributing + +1. Fork it ( https://github.com/luislavena/radix/fork ) +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create a new Pull Request + +## Contributors + +- [Luis Lavena](https://github.com/luislavena) - creator, maintainer diff --git a/samples/client/petstore/crystal/lib/radix/lib b/samples/client/petstore/crystal/lib/radix/lib new file mode 120000 index 000000000000..a96aa0ea9d8c --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/lib @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/radix/shard.yml b/samples/client/petstore/crystal/lib/radix/shard.yml new file mode 100644 index 000000000000..70565cd6549f --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/shard.yml @@ -0,0 +1,7 @@ +name: radix +version: 0.3.9 + +authors: + - Luis Lavena + +license: MIT diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr new file mode 100644 index 000000000000..e43ffaa96969 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr @@ -0,0 +1,150 @@ +require "../spec_helper" + +module Radix + describe Node do + describe "#glob?" do + it "returns true when key contains a glob parameter (catch all)" do + node = Node(Nil).new("a") + node.glob?.should be_false + + node = Node(Nil).new("*filepath") + node.glob?.should be_true + end + end + + describe "#key=" do + it "accepts change of key after initialization" do + node = Node(Nil).new("abc") + node.key.should eq("abc") + + node.key = "xyz" + node.key.should eq("xyz") + end + + it "also changes kind when modified" do + node = Node(Nil).new("abc") + node.normal?.should be_true + + node.key = ":query" + node.normal?.should be_false + node.named?.should be_true + end + end + + describe "#named?" do + it "returns true when key contains a named parameter" do + node = Node(Nil).new("a") + node.named?.should be_false + + node = Node(Nil).new(":query") + node.named?.should be_true + end + end + + describe "#normal?" do + it "returns true when key does not contain named or glob parameters" do + node = Node(Nil).new("a") + node.normal?.should be_true + + node = Node(Nil).new(":query") + node.normal?.should be_false + + node = Node(Nil).new("*filepath") + node.normal?.should be_false + end + end + + describe "#payload" do + it "accepts any form of payload" do + node = Node.new("abc", :payload) + node.payload?.should be_truthy + node.payload.should eq(:payload) + + node = Node.new("abc", 1_000) + node.payload?.should be_truthy + node.payload.should eq(1_000) + end + + # This example focuses on the internal representation of `payload` + # as inferred from supplied types and default values. + # + # We cannot compare `typeof` against `property!` since it excludes `Nil` + # from the possible types. + it "makes optional to provide a payload" do + node = Node(Int32).new("abc") + node.payload?.should be_falsey + typeof(node.@payload).should eq(Int32 | Nil) + end + end + + describe "#priority" do + it "calculates it based on key length" do + node = Node(Nil).new("a") + node.priority.should eq(1) + + node = Node(Nil).new("abc") + node.priority.should eq(3) + end + + it "considers key length up until named parameter presence" do + node = Node(Nil).new("/posts/:id") + node.priority.should eq(7) + + node = Node(Nil).new("/u/:username") + node.priority.should eq(3) + end + + it "considers key length up until glob parameter presence" do + node = Node(Nil).new("/search/*query") + node.priority.should eq(8) + + node = Node(Nil).new("/*anything") + node.priority.should eq(1) + end + + it "changes when key changes" do + node = Node(Nil).new("a") + node.priority.should eq(1) + + node.key = "abc" + node.priority.should eq(3) + + node.key = "/src/*filepath" + node.priority.should eq(5) + + node.key = "/search/:query" + node.priority.should eq(8) + end + end + + describe "#sort!" do + it "orders children" do + root = Node(Int32).new("/") + node1 = Node(Int32).new("a", 1) + node2 = Node(Int32).new("bc", 2) + node3 = Node(Int32).new("def", 3) + + root.children.push(node1, node2, node3) + root.sort! + + root.children[0].should eq(node3) + root.children[1].should eq(node2) + root.children[2].should eq(node1) + end + + it "orders catch all and named parameters lower than normal nodes" do + root = Node(Int32).new("/") + node1 = Node(Int32).new("*filepath", 1) + node2 = Node(Int32).new("abc", 2) + node3 = Node(Int32).new(":query", 3) + + root.children.push(node1, node2, node3) + root.sort! + + root.children[0].should eq(node2) + root.children[1].should eq(node3) + root.children[2].should eq(node1) + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr new file mode 100644 index 000000000000..89ad2267651c --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr @@ -0,0 +1,76 @@ +require "../spec_helper" + +module Radix + describe Result do + describe "#found?" do + context "a new instance" do + it "returns false when no payload is associated" do + result = Result(Nil).new + result.found?.should be_false + end + end + + context "with a payload" do + it "returns true" do + node = Node(Symbol).new("/", :root) + result = Result(Symbol).new + result.use node + + result.found?.should be_true + end + end + end + + describe "#key" do + context "a new instance" do + it "returns an empty key" do + result = Result(Nil).new + result.key.should eq("") + end + end + + context "given one used node" do + it "returns the node key" do + node = Node(Symbol).new("/", :root) + result = Result(Symbol).new + result.use node + + result.key.should eq("/") + end + end + + context "using multiple nodes" do + it "combines the node keys" do + node1 = Node(Symbol).new("/", :root) + node2 = Node(Symbol).new("about", :about) + result = Result(Symbol).new + result.use node1 + result.use node2 + + result.key.should eq("/about") + end + end + end + + describe "#use" do + it "uses the node payload" do + node = Node(Symbol).new("/", :root) + result = Result(Symbol).new + result.payload?.should be_falsey + + result.use node + result.payload?.should be_truthy + result.payload.should eq(node.payload) + end + + it "allow not to assign payload" do + node = Node(Symbol).new("/", :root) + result = Result(Symbol).new + result.payload?.should be_falsey + + result.use node, payload: false + result.payload?.should be_falsey + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr new file mode 100644 index 000000000000..760de45f82c7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr @@ -0,0 +1,626 @@ +require "../spec_helper" + +# Silence deprecation warnings when running specs and allow +# capture them for inspection. +module Radix + class Tree(T) + @show_deprecations = false + @stderr : IO::Memory? + + def show_deprecations! + @show_deprecations = true + end + + private def deprecation(message) + if @show_deprecations + @stderr ||= IO::Memory.new + @stderr.not_nil!.puts message + end + end + end +end + +# Simple Payload class +record Payload + +module Radix + describe Tree do + context "a new instance" do + it "contains a root placeholder node" do + tree = Tree(Symbol).new + tree.root.should be_a(Node(Symbol)) + tree.root.payload?.should be_falsey + tree.root.placeholder?.should be_true + end + end + + describe "#add" do + context "on a new instance" do + it "replaces placeholder with new node" do + tree = Tree(Symbol).new + tree.add "/abc", :abc + tree.root.should be_a(Node(Symbol)) + tree.root.placeholder?.should be_false + tree.root.payload?.should be_truthy + tree.root.payload.should eq(:abc) + end + end + + context "shared root" do + it "inserts properly adjacent nodes" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/a", :a + tree.add "/bc", :bc + + # / (:root) + # +-bc (:bc) + # \-a (:a) + tree.root.children.size.should eq(2) + tree.root.children[0].key.should eq("bc") + tree.root.children[0].payload.should eq(:bc) + tree.root.children[1].key.should eq("a") + tree.root.children[1].payload.should eq(:a) + end + + it "inserts nodes with shared parent" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/abc", :abc + tree.add "/axyz", :axyz + + # / (:root) + # +-a + # +-xyz (:axyz) + # \-bc (:abc) + tree.root.children.size.should eq(1) + tree.root.children[0].key.should eq("a") + tree.root.children[0].children.size.should eq(2) + tree.root.children[0].children[0].key.should eq("xyz") + tree.root.children[0].children[1].key.should eq("bc") + end + + it "inserts multiple parent nodes" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/admin/users", :users + tree.add "/admin/products", :products + tree.add "/blog/tags", :tags + tree.add "/blog/articles", :articles + + # / (:root) + # +-admin/ + # | +-products (:products) + # | \-users (:users) + # | + # +-blog/ + # +-articles (:articles) + # \-tags (:tags) + tree.root.children.size.should eq(2) + tree.root.children[0].key.should eq("admin/") + tree.root.children[0].payload?.should be_falsey + tree.root.children[0].children[0].key.should eq("products") + tree.root.children[0].children[1].key.should eq("users") + tree.root.children[1].key.should eq("blog/") + tree.root.children[1].payload?.should be_falsey + tree.root.children[1].children[0].key.should eq("articles") + tree.root.children[1].children[0].payload?.should be_truthy + tree.root.children[1].children[1].key.should eq("tags") + tree.root.children[1].children[1].payload?.should be_truthy + end + + it "inserts multiple nodes with mixed parents" do + tree = Tree(Symbol).new + tree.add "/authorizations", :authorizations + tree.add "/authorizations/:id", :authorization + tree.add "/applications", :applications + tree.add "/events", :events + + # / + # +-events (:events) + # +-a + # +-uthorizations (:authorizations) + # | \-/:id (:authorization) + # \-pplications (:applications) + tree.root.children.size.should eq(2) + tree.root.children[1].key.should eq("a") + tree.root.children[1].children.size.should eq(2) + tree.root.children[1].children[0].payload.should eq(:authorizations) + tree.root.children[1].children[1].payload.should eq(:applications) + end + + it "supports insertion of mixed routes out of order" do + tree = Tree(Symbol).new + tree.add "/user/repos", :my_repos + tree.add "/users/:user/repos", :user_repos + tree.add "/users/:user", :user + tree.add "/user", :me + + # /user (:me) + # +-/repos (:my_repos) + # \-s/:user (:user) + # \-/repos (:user_repos) + tree.root.key.should eq("/user") + tree.root.payload?.should be_truthy + tree.root.payload.should eq(:me) + tree.root.children.size.should eq(2) + tree.root.children[0].key.should eq("/repos") + tree.root.children[1].key.should eq("s/:user") + tree.root.children[1].payload.should eq(:user) + tree.root.children[1].children[0].key.should eq("/repos") + end + end + + context "mixed payloads" do + it "allows node with different payloads" do + payload1 = Payload.new + payload2 = Payload.new + + tree = Tree(Payload | Symbol).new + tree.add "/", :root + tree.add "/a", payload1 + tree.add "/bc", payload2 + + # / (:root) + # +-bc (payload2) + # \-a (payload1) + tree.root.children.size.should eq(2) + tree.root.children[0].key.should eq("bc") + tree.root.children[0].payload.should eq(payload2) + tree.root.children[1].key.should eq("a") + tree.root.children[1].payload.should eq(payload1) + end + end + + context "dealing with unicode" do + it "inserts properly adjacent parent nodes" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/日本語", :japanese + tree.add "/素晴らしい", :amazing + + # / (:root) + # +-素晴らしい (:amazing) + # \-日本語 (:japanese) + tree.root.children.size.should eq(2) + tree.root.children[0].key.should eq("素晴らしい") + tree.root.children[1].key.should eq("日本語") + end + + it "inserts nodes with shared parent" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/日本語", :japanese + tree.add "/日本は難しい", :japanese_is_difficult + + # / (:root) + # \-日本語 (:japanese) + # \-日本は難しい (:japanese_is_difficult) + tree.root.children.size.should eq(1) + tree.root.children[0].key.should eq("日本") + tree.root.children[0].children.size.should eq(2) + tree.root.children[0].children[0].key.should eq("は難しい") + tree.root.children[0].children[1].key.should eq("語") + end + end + + context "dealing with duplicates" do + it "does not allow same path be defined twice" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/abc", :abc + + expect_raises Tree::DuplicateError do + tree.add "/", :other + end + + tree.root.children.size.should eq(1) + end + end + + context "dealing with catch all and named parameters" do + it "prioritizes nodes correctly" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/*filepath", :all + tree.add "/products", :products + tree.add "/products/:id", :product + tree.add "/products/:id/edit", :edit + tree.add "/products/featured", :featured + + # / (:all) + # +-products (:products) + # | \-/ + # | +-featured (:featured) + # | \-:id (:product) + # | \-/edit (:edit) + # \-*filepath (:all) + tree.root.children.size.should eq(2) + tree.root.children[0].key.should eq("products") + tree.root.children[0].children[0].key.should eq("/") + + nodes = tree.root.children[0].children[0].children + nodes.size.should eq(2) + nodes[0].key.should eq("featured") + nodes[1].key.should eq(":id") + nodes[1].children[0].key.should eq("/edit") + + tree.root.children[1].key.should eq("*filepath") + end + + it "does not split named parameters across shared key" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/:category", :category + tree.add "/:category/:subcategory", :subcategory + + # / (:root) + # +-:category (:category) + # \-/:subcategory (:subcategory) + tree.root.children.size.should eq(1) + tree.root.children[0].key.should eq(":category") + + # inner children + tree.root.children[0].children.size.should eq(1) + tree.root.children[0].children[0].key.should eq("/:subcategory") + end + + it "does allow same named parameter in different order of insertion" do + tree = Tree(Symbol).new + tree.add "/members/:id/edit", :member_edit + tree.add "/members/export", :members_export + tree.add "/members/:id/videos", :member_videos + + # /members/ + # +-export (:members_export) + # \-:id/ + # +-videos (:members_videos) + # \-edit (:members_edit) + tree.root.key.should eq("/members/") + tree.root.children.size.should eq(2) + + # first level children nodes + tree.root.children[0].key.should eq("export") + tree.root.children[1].key.should eq(":id/") + + # inner children + nodes = tree.root.children[1].children + nodes[0].key.should eq("videos") + nodes[1].key.should eq("edit") + end + + it "does not allow different named parameters sharing same level" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/:post", :post + + expect_raises Tree::SharedKeyError do + tree.add "/:category/:post", :category_post + end + end + end + end + + describe "#find" do + context "a single node" do + it "does not find when using different path" do + tree = Tree(Symbol).new + tree.add "/about", :about + + result = tree.find "/products" + result.found?.should be_false + end + + it "finds when key and path matches" do + tree = Tree(Symbol).new + tree.add "/about", :about + + result = tree.find "/about" + result.found?.should be_true + result.key.should eq("/about") + result.payload?.should be_truthy + result.payload.should eq(:about) + end + + it "finds when path contains trailing slash" do + tree = Tree(Symbol).new + tree.add "/about", :about + + result = tree.find "/about/" + result.found?.should be_true + result.key.should eq("/about") + end + + it "finds when key contains trailing slash" do + tree = Tree(Symbol).new + tree.add "/about/", :about + + result = tree.find "/about" + result.found?.should be_true + result.key.should eq("/about/") + result.payload.should eq(:about) + end + end + + context "nodes with shared parent" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/abc", :abc + tree.add "/axyz", :axyz + + result = tree.find("/abc") + result.found?.should be_true + result.key.should eq("/abc") + result.payload.should eq(:abc) + end + + it "finds matching path across separator" do + tree = Tree(Symbol).new + tree.add "/products", :products + tree.add "/product/new", :product_new + + result = tree.find("/products") + result.found?.should be_true + result.key.should eq("/products") + result.payload.should eq(:products) + end + + it "finds matching path across parents" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/admin/users", :users + tree.add "/admin/products", :products + tree.add "/blog/tags", :tags + tree.add "/blog/articles", :articles + + result = tree.find("/blog/tags/") + result.found?.should be_true + result.key.should eq("/blog/tags") + result.payload.should eq(:tags) + end + end + + context "unicode nodes with shared parent" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/日本語", :japanese + tree.add "/日本日本語は難しい", :japanese_is_difficult + + result = tree.find("/日本日本語は難しい/") + result.found?.should be_true + result.key.should eq("/日本日本語は難しい") + end + end + + context "dealing with catch all" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/*filepath", :all + tree.add "/about", :about + + result = tree.find("/src/file.png") + result.found?.should be_true + result.key.should eq("/*filepath") + result.payload.should eq(:all) + end + + it "returns catch all in parameters" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/*filepath", :all + tree.add "/about", :about + + result = tree.find("/src/file.png") + result.found?.should be_true + result.params.has_key?("filepath").should be_true + result.params["filepath"].should eq("src/file.png") + end + + it "returns optional catch all after slash" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/search/*extra", :extra + + result = tree.find("/search") + result.found?.should be_true + result.key.should eq("/search/*extra") + result.params.has_key?("extra").should be_true + result.params["extra"].empty?.should be_true + end + + it "returns optional catch all by globbing" do + tree = Tree(Symbol).new + tree.add "/members*trailing", :members_catch_all + + result = tree.find("/members") + result.found?.should be_true + result.key.should eq("/members*trailing") + result.params.has_key?("trailing").should be_true + result.params["trailing"].empty?.should be_true + end + + it "does not find when catch all is not full match" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/search/public/*query", :search + + result = tree.find("/search") + result.found?.should be_false + end + + it "does not find when path search has been exhausted" do + tree = Tree(Symbol).new + tree.add "/members/*trailing", :members_catch_all + + result = tree.find("/members2") + result.found?.should be_false + end + + it "does prefer specific path over catch all if both are present" do + tree = Tree(Symbol).new + tree.add "/members", :members + tree.add "/members*trailing", :members_catch_all + + result = tree.find("/members") + result.found?.should be_true + result.key.should eq("/members") + end + + it "does prefer catch all over specific key with partially shared key" do + tree = Tree(Symbol).new + tree.add "/orders/*anything", :orders_catch_all + tree.add "/orders/closed", :closed_orders + + result = tree.find("/orders/cancelled") + result.found?.should be_true + result.key.should eq("/orders/*anything") + result.params.has_key?("anything").should be_true + result.params["anything"].should eq("cancelled") + end + end + + context "dealing with named parameters" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/products", :products + tree.add "/products/:id", :product + tree.add "/products/:id/edit", :edit + + result = tree.find("/products/10") + result.found?.should be_true + result.key.should eq("/products/:id") + result.payload.should eq(:product) + end + + it "does not find partial matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/products", :products + tree.add "/products/:id/edit", :edit + + result = tree.find("/products/10") + result.found?.should be_false + end + + it "returns named parameters in result" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/products", :products + tree.add "/products/:id", :product + tree.add "/products/:id/edit", :edit + + result = tree.find("/products/10/edit") + result.found?.should be_true + result.params.has_key?("id").should be_true + result.params["id"].should eq("10") + end + + it "returns unicode values in parameters" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/language/:name", :language + tree.add "/language/:name/about", :about + + result = tree.find("/language/日本語") + result.found?.should be_true + result.params.has_key?("name").should be_true + result.params["name"].should eq("日本語") + end + + it "does prefer specific path over named parameters one if both are present" do + tree = Tree(Symbol).new + tree.add "/tag-edit/:tag", :edit_tag + tree.add "/tag-edit2", :alternate_tag_edit + + result = tree.find("/tag-edit2") + result.found?.should be_true + result.key.should eq("/tag-edit2") + end + + it "does prefer named parameter over specific key with partially shared key" do + tree = Tree(Symbol).new + tree.add "/orders/:id", :specific_order + tree.add "/orders/closed", :closed_orders + + result = tree.find("/orders/10") + result.found?.should be_true + result.key.should eq("/orders/:id") + result.params.has_key?("id").should be_true + result.params["id"].should eq("10") + end + end + + context "dealing with multiple named parameters" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/:section/:page", :static_page + + result = tree.find("/about/shipping") + result.found?.should be_true + result.key.should eq("/:section/:page") + result.payload.should eq(:static_page) + end + + it "returns named parameters in result" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/:section/:page", :static_page + + result = tree.find("/about/shipping") + result.found?.should be_true + + result.params.has_key?("section").should be_true + result.params["section"].should eq("about") + + result.params.has_key?("page").should be_true + result.params["page"].should eq("shipping") + end + end + + context "dealing with both catch all and named parameters" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/", :root + tree.add "/*filepath", :all + tree.add "/products", :products + tree.add "/products/:id", :product + tree.add "/products/:id/edit", :edit + tree.add "/products/featured", :featured + + result = tree.find("/products/1000") + result.found?.should be_true + result.key.should eq("/products/:id") + result.payload.should eq(:product) + + result = tree.find("/admin/articles") + result.found?.should be_true + result.key.should eq("/*filepath") + result.params["filepath"].should eq("admin/articles") + + result = tree.find("/products/featured") + result.found?.should be_true + result.key.should eq("/products/featured") + result.payload.should eq(:featured) + end + end + + context "dealing with named parameters and shared key" do + it "finds matching path" do + tree = Tree(Symbol).new + tree.add "/one/:id", :one + tree.add "/one-longer/:id", :two + + result = tree.find "/one-longer/10" + result.found?.should be_true + result.key.should eq("/one-longer/:id") + result.params["id"].should eq("10") + end + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr new file mode 100644 index 000000000000..30ddc0847bd5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr @@ -0,0 +1,12 @@ +require "../spec_helper" +require "yaml" + +describe "Radix::VERSION" do + it "matches version defined in shard.yml" do + contents = File.read(File.expand_path("../../../shard.yml", __FILE__)) + meta = YAML.parse(contents) + + meta["version"]?.should_not be_falsey + Radix::VERSION.should eq(meta["version"].as_s) + end +end diff --git a/samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr new file mode 100644 index 000000000000..6fa0c75d7598 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr @@ -0,0 +1,2 @@ +require "spec" +require "../src/radix" diff --git a/samples/client/petstore/crystal/lib/radix/src/radix.cr b/samples/client/petstore/crystal/lib/radix/src/radix.cr new file mode 100644 index 000000000000..97ff540e5ce7 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/src/radix.cr @@ -0,0 +1,2 @@ +require "./radix/tree" +require "./radix/version" diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/node.cr b/samples/client/petstore/crystal/lib/radix/src/radix/node.cr new file mode 100644 index 000000000000..a87d39ebd656 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/src/radix/node.cr @@ -0,0 +1,207 @@ +module Radix + # A Node represents one element in the structure of a [Radix tree](https://en.wikipedia.org/wiki/Radix_tree) + # + # Carries a *payload* and might also contain references to other nodes + # down in the organization inside *children*. + # + # Each node also carries identification in relation to the kind of key it + # contains, which helps with characteristics of the node like named + # parameters or catch all kind (globbing). + # + # Is not expected direct usage of a node but instead manipulation via + # methods within `Tree`. + class Node(T) + include Comparable(self) + + # :nodoc: + enum Kind : UInt8 + Normal + Named + Glob + end + + getter key + getter? placeholder + property children = [] of Node(T) + property! payload : T | Nil + + # :nodoc: + protected getter kind = Kind::Normal + + # Returns the priority of the Node based on it's *key* + # + # This value will be directly associated to the key size up until a + # special elements is found. + # + # ``` + # Radix::Node(Nil).new("a").priority + # # => 1 + # + # Radix::Node(Nil).new("abc").priority + # # => 3 + # + # Radix::Node(Nil).new("/src/*filepath").priority + # # => 5 + # + # Radix::Node(Nil).new("/search/:query").priority + # # => 8 + # ``` + getter priority : Int32 + + # Instantiate a Node + # + # - *key* - A `String` that represents this node. + # - *payload* - An optional payload for this node. + # + # When *payload* is not supplied, ensure the type of the node is provided + # instead: + # + # ``` + # # Good, node type is inferred from payload (Symbol) + # node = Radix::Node.new("/", :root) + # + # # Good, node type is now Int32 but payload is optional + # node = Radix::Node(Int32).new("/") + # + # # Error, node type cannot be inferred (compiler error) + # node = Radix::Node.new("/") + # ``` + def initialize(@key : String, @payload : T? = nil, @placeholder = false) + @priority = compute_priority + end + + # Compares this node against *other*, returning `-1`, `0` or `1` depending + # on whether this node differentiates from *other*. + # + # Comparison is done combining node's `kind` and `priority`. Nodes of + # same kind are compared by priority. Nodes of different kind are + # ranked. + # + # ### Normal nodes + # + # ``` + # node1 = Radix::Node(Nil).new("a") # normal + # node2 = Radix::Node(Nil).new("bc") # normal + # node1 <=> node2 # => 1 + # ``` + # + # ### Normal vs named or glob nodes + # + # ``` + # node1 = Radix::Node(Nil).new("a") # normal + # node2 = Radix::Node(Nil).new(":query") # named + # node3 = Radix::Node(Nil).new("*filepath") # glob + # node1 <=> node2 # => -1 + # node1 <=> node3 # => -1 + # ``` + # + # ### Named vs glob nodes + # + # ``` + # node1 = Radix::Node(Nil).new(":query") # named + # node2 = Radix::Node(Nil).new("*filepath") # glob + # node1 <=> node2 # => -1 + # ``` + def <=>(other : self) + result = kind <=> other.kind + return result if result != 0 + + other.priority <=> priority + end + + # Returns `true` if the node key contains a glob parameter in it + # (catch all) + # + # ``` + # node = Radix::Node(Nil).new("*filepath") + # node.glob? # => true + # + # node = Radix::Node(Nil).new("abc") + # node.glob? # => false + # ``` + def glob? + kind.glob? + end + + # Changes current *key* + # + # ``` + # node = Radix::Node(Nil).new("a") + # node.key + # # => "a" + # + # node.key = "b" + # node.key + # # => "b" + # ``` + # + # This will also result in change of node's `priority` + # + # ``` + # node = Radix::Node(Nil).new("a") + # node.priority + # # => 1 + # + # node.key = "abcdef" + # node.priority + # # => 6 + # ``` + def key=(@key) + # reset kind on change of key + @kind = Kind::Normal + @priority = compute_priority + end + + # Returns `true` if the node key contains a named parameter in it + # + # ``` + # node = Radix::Node(Nil).new(":query") + # node.named? # => true + # + # node = Radix::Node(Nil).new("abc") + # node.named? # => false + # ``` + def named? + kind.named? + end + + # Returns `true` if the node key does not contain an special parameter + # (named or glob) + # + # ``` + # node = Radix::Node(Nil).new("a") + # node.normal? # => true + # + # node = Radix::Node(Nil).new(":query") + # node.normal? # => false + # ``` + def normal? + kind.normal? + end + + # :nodoc: + private def compute_priority + reader = Char::Reader.new(@key) + + while reader.has_next? + case reader.current_char + when '*' + @kind = Kind::Glob + break + when ':' + @kind = Kind::Named + break + else + reader.next_char + end + end + + reader.pos + end + + # :nodoc: + protected def sort! + @children.sort! + end + end +end diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/result.cr b/samples/client/petstore/crystal/lib/radix/src/radix/result.cr new file mode 100644 index 000000000000..ad0a0bb5049a --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/src/radix/result.cr @@ -0,0 +1,88 @@ +require "./node" + +module Radix + # A Result is the comulative output of walking our [Radix tree](https://en.wikipedia.org/wiki/Radix_tree) + # `Radix::Tree` implementation. + # + # It provides helpers to retrieve the information obtained from walking + # our tree using `Radix::Tree#find` + # + # This information can be used to perform actions in case of the *path* + # that was looked on the Tree was found. + # + # A Result is also used recursively by `Radix::Tree#find` when collecting + # extra information like *params*. + class Result(T) + @key : String? + + getter params + getter! payload : T? + + # :nodoc: + def initialize + @nodes = [] of Node(T) + @params = {} of String => String + end + + # Returns whatever a *payload* was found by `Tree#find` and is part of + # the result. + # + # ``` + # result = Radix::Result(Symbol).new + # result.found? + # # => false + # + # root = Radix::Node(Symbol).new("/", :root) + # result.use(root) + # result.found? + # # => true + # ``` + def found? + payload? ? true : false + end + + # Returns a String built based on the nodes used in the result + # + # ``` + # node1 = Radix::Node(Symbol).new("/", :root) + # node2 = Radix::Node(Symbol).new("about", :about) + # + # result = Radix::Result(Symbol).new + # result.use node1 + # result.use node2 + # + # result.key + # # => "/about" + # ``` + # + # When no node has been used, returns an empty String. + # + # ``` + # result = Radix::Result(Nil).new + # result.key + # # => "" + # ``` + def key + @key ||= begin + String.build { |io| + @nodes.each do |node| + io << node.key + end + } + end + end + + # Adjust result information by using the details of the given `Node`. + # + # * Collect `Node` for future references. + # * Use *payload* if present. + def use(node : Node(T), payload = true) + # collect nodes + @nodes << node + + if payload && node.payload? + @payload = node.payload + end + end + end +end diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/tree.cr b/samples/client/petstore/crystal/lib/radix/src/radix/tree.cr new file mode 100644 index 000000000000..d6f4a9697f2a --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/src/radix/tree.cr @@ -0,0 +1,472 @@ +require "./node" +require "./result" + +module Radix + # A [Radix tree](https://en.wikipedia.org/wiki/Radix_tree) implementation. + # + # It allows insertion of *path* elements that will be organized inside + # the tree aiming to provide fast retrieval options. + # + # Each inserted *path* will be represented by a `Node` or segmented and + # distributed within the `Tree`. + # + # You can associate a *payload* at insertion which will be return back + # at retrieval time. + class Tree(T) + # :nodoc: + class DuplicateError < Exception + def initialize(path) + super("Duplicate trail found '#{path}'") + end + end + + # :nodoc: + class SharedKeyError < Exception + def initialize(new_key, existing_key) + super("Tried to place key '#{new_key}' at same level as '#{existing_key}'") + end + end + + # Returns the root `Node` element of the Tree. + # + # On a new tree instance, this will be a placeholder. + getter root : Node(T) + + def initialize + @root = Node(T).new("", placeholder: true) + end + + # Inserts given *path* into the Tree + # + # * *path* - An `String` representing the pattern to be inserted. + # * *payload* - Required associated element for this path. + # + # If no previous elements existed in the Tree, this will replace the + # defined placeholder. + # + # ``` + # tree = Radix::Tree(Symbol).new + # + # # / (:root) + # tree.add "/", :root + # + # # / (:root) + # # \-abc (:abc) + # tree.add "/abc", :abc + # + # # / (:root) + # # \-abc (:abc) + # # \-xyz (:xyz) + # tree.add "/abcxyz", :xyz + # ``` + # + # Nodes inside the tree will be adjusted to accommodate the different + # segments of the given *path*. + # + # ``` + # tree = Radix::Tree(Symbol).new + # + # # / (:root) + # tree.add "/", :root + # + # # / (:root) + # # \-products/:id (:product) + # tree.add "/products/:id", :product + # + # # / (:root) + # # \-products/ + # # +-featured (:featured) + # # \-:id (:product) + # tree.add "/products/featured", :featured + # ``` + # + # Catch all (globbing) and named parameters *path* will be located with + # lower priority against other nodes. + # + # ``` + # tree = Radix::Tree(Symbol).new + # + # # / (:root) + # tree.add "/", :root + # + # # / (:root) + # # \-*filepath (:all) + # tree.add "/*filepath", :all + # + # # / (:root) + # # +-about (:about) + # # \-*filepath (:all) + # tree.add "/about", :about + # ``` + def add(path : String, payload : T) + root = @root + + # replace placeholder with new node + if root.placeholder? + @root = Node(T).new(path, payload) + else + add path, payload, root + end + end + + # :nodoc: + private def add(path : String, payload : T, node : Node(T)) + key_reader = Char::Reader.new(node.key) + path_reader = Char::Reader.new(path) + + # move cursor position to last shared character between key and path + while path_reader.has_next? && key_reader.has_next? + break if path_reader.current_char != key_reader.current_char + + path_reader.next_char + key_reader.next_char + end + + # determine split point difference between path and key + # compare if path is larger than key + if path_reader.pos == 0 || + (path_reader.pos < path.bytesize && path_reader.pos >= node.key.bytesize) + # determine if a child of this node contains the remaining part + # of the path + added = false + + new_key = path_reader.string.byte_slice(path_reader.pos) + node.children.each do |child| + # if child's key starts with named parameter, compare key until + # separator (if present). + # Otherwise, compare just first character + if child.key[0]? == ':' && new_key[0]? == ':' + unless _same_key?(new_key, child.key) + raise SharedKeyError.new(new_key, child.key) + end + else + next unless child.key[0]? == new_key[0]? + end + + # when found, add to this child + added = true + add new_key, payload, child + break + end + + # if no existing child shared part of the key, add a new one + unless added + node.children << Node(T).new(new_key, payload) + end + + # adjust priorities + node.sort! + elsif path_reader.pos == path.bytesize && path_reader.pos == node.key.bytesize + # determine if path matches key and potentially be a duplicate + # and raise if is the case + + if node.payload? + raise DuplicateError.new(path) + else + # assign payload since this is an empty node + node.payload = payload + end + elsif path_reader.pos > 0 && path_reader.pos < node.key.bytesize + # determine if current node key needs to be split to accomodate new + # children nodes + + # build new node with partial key and adjust existing one + new_key = node.key.byte_slice(path_reader.pos) + swap_payload = node.payload? ? node.payload : nil + + new_node = Node(T).new(new_key, swap_payload) + new_node.children.replace(node.children) + + # clear payload and children (this is no longer and endpoint) + node.payload = nil + node.children.clear + + # adjust existing node key to new partial one + node.key = path_reader.string.byte_slice(0, path_reader.pos) + node.children << new_node + node.sort! + + # determine if path still continues + if path_reader.pos < path.bytesize + new_key = path.byte_slice(path_reader.pos) + node.children << Node(T).new(new_key, payload) + node.sort! + + # clear payload (no endpoint) + node.payload = nil + else + # this is an endpoint, set payload + node.payload = payload + end + end + end + + # Returns a `Result` instance after walking the tree looking up for + # *path* + # + # It will start walking the tree from the root node until a matching + # endpoint is found (or not). + # + # ``` + # tree = Radix::Tree(Symbol).new + # tree.add "/about", :about + # + # result = tree.find "/products" + # result.found? + # # => false + # + # result = tree.find "/about" + # result.found? + # # => true + # + # result.payload + # # => :about + # ``` + def find(path : String) + result = Result(T).new + root = @root + + # walk the tree from root (first time) + find path, result, root, first: true + + result + end + + # :nodoc: + private def find(path : String, result : Result, node : Node, first = false) + # special consideration when comparing the first node vs. others + # in case of node key and path being the same, return the node + # instead of walking character by character + if first && (path.bytesize == node.key.bytesize && path == node.key) && node.payload? + result.use node + return + end + + key_reader = Char::Reader.new(node.key) + path_reader = Char::Reader.new(path) + + # walk both path and key while both have characters and they continue + # to match. Consider as special cases named parameters and catch all + # rules. + while key_reader.has_next? && path_reader.has_next? && + (key_reader.current_char == '*' || + key_reader.current_char == ':' || + path_reader.current_char == key_reader.current_char) + case key_reader.current_char + when '*' + # deal with catch all (globbing) parameter + # extract parameter name from key (exclude *) and value from path + name = key_reader.string.byte_slice(key_reader.pos + 1) + value = path_reader.string.byte_slice(path_reader.pos) + + # add this to result + result.params[name] = value + + result.use node + return + when ':' + # deal with named parameter + # extract parameter name from key (from : until / or EOL) and + # value from path (same rules as key) + key_size = _detect_param_size(key_reader) + path_size = _detect_param_size(path_reader) + + # obtain key and value using calculated sizes + # for name: skip ':' by moving one character forward and compensate + # key size. + name = key_reader.string.byte_slice(key_reader.pos + 1, key_size - 1) + value = path_reader.string.byte_slice(path_reader.pos, path_size) + + # add this information to result + result.params[name] = value + + # advance readers positions + key_reader.pos += key_size + path_reader.pos += path_size + else + # move to the next character + key_reader.next_char + path_reader.next_char + end + end + + # check if we reached the end of the path & key + if !path_reader.has_next? && !key_reader.has_next? + # check endpoint + if node.payload? + result.use node + return + end + end + + # still path to walk, check for possible trailing slash or children + # nodes + if path_reader.has_next? + # using trailing slash? + if node.key.bytesize > 0 && + path_reader.pos + 1 == path.bytesize && + path_reader.current_char == '/' + result.use node + return + end + + # not found in current node, check inside children nodes + new_path = path_reader.string.byte_slice(path_reader.pos) + node.children.each do |child| + # check if child key is a named parameter, catch all or shares parts + # with new path + if (child.key[0]? == '*' || child.key[0]? == ':') || + _shared_key?(new_path, child.key) + # consider this node for key but don't use payload + result.use node, payload: false + + find new_path, result, child + return + end + end + + # path differs from key, no use searching anymore + return + end + + # key still contains characters to walk + if key_reader.has_next? + # determine if there is just a trailing slash? + if key_reader.pos + 1 == node.key.bytesize && + key_reader.current_char == '/' + result.use node + return + end + + # check if remaining part is catch all + if key_reader.pos < node.key.bytesize && + ((key_reader.current_char == '/' && key_reader.peek_next_char == '*') || + key_reader.current_char == '*') + # skip to '*' only if necessary + unless key_reader.current_char == '*' + key_reader.next_char + end + + # deal with catch all, but since there is nothing in the path + # return parameter as empty + name = key_reader.string.byte_slice(key_reader.pos + 1) + + result.params[name] = "" + + result.use node + return + end + end + end + + # :nodoc: + private def _detect_param_size(reader) + # save old position + old_pos = reader.pos + + # move forward until '/' or EOL is detected + while reader.has_next? + break if reader.current_char == '/' + + reader.next_char + end + + # calculate the size + count = reader.pos - old_pos + + # restore old position + reader.pos = old_pos + + count + end + + # Internal: allow inline comparison of *char* against 3 defined markers: + # + # - Path separator (`/`) + # - Named parameter (`:`) + # - Catch all (`*`) + @[AlwaysInline] + private def _check_markers(char) + (char == '/' || char == ':' || char == '*') + end + + # Internal: Compares *path* against *key* for differences until the + # following criteria is met: + # + # - End of *path* or *key* is reached. + # - A separator (`/`) is found. + # - A character between *path* or *key* differs + # + # ``` + # _same_key?("foo", "bar") # => false (mismatch at 1st character) + # _same_key?("foo/bar", "foo/baz") # => true (only `foo` is compared) + # _same_key?("zipcode", "zip") # => false (`zip` is shorter) + # ``` + private def _same_key?(path, key) + path_reader = Char::Reader.new(path) + key_reader = Char::Reader.new(key) + + different = false + + while (path_reader.has_next? && path_reader.current_char != '/') && + (key_reader.has_next? && key_reader.current_char != '/') + if path_reader.current_char != key_reader.current_char + different = true + break + end + + path_reader.next_char + key_reader.next_char + end + + (!different) && + (path_reader.current_char == '/' || !path_reader.has_next?) + end + + # Internal: Compares *path* against *key* for equality until one of the + # following criterias is met: + # + # - End of *path* or *key* is reached. + # - A separator (`/`) is found. + # - A named parameter (`:`) or catch all (`*`) is found. + # - A character in *path* differs from *key* + # + # ``` + # _shared_key?("foo", "bar") # => false (mismatch at 1st character) + # _shared_key?("foo/bar", "foo/baz") # => true (only `foo` is compared) + # _shared_key?("zipcode", "zip") # => true (only `zip` is compared) + # _shared_key?("s", "/new") # => false (1st character is a separator) + # ``` + private def _shared_key?(path, key) + path_reader = Char::Reader.new(path) + key_reader = Char::Reader.new(key) + + if (path_reader.current_char != key_reader.current_char) && + _check_markers(key_reader.current_char) + return false + end + + different = false + + while (path_reader.has_next? && !_check_markers(path_reader.current_char)) && + (key_reader.has_next? && !_check_markers(key_reader.current_char)) + if path_reader.current_char != key_reader.current_char + different = true + break + end + + path_reader.next_char + key_reader.next_char + end + + (!different) && + (!key_reader.has_next? || _check_markers(key_reader.current_char)) + end + + # :nodoc: + private def deprecation(message : String) + STDERR.puts message + STDERR.flush + end + end +end diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/version.cr b/samples/client/petstore/crystal/lib/radix/src/radix/version.cr new file mode 100644 index 000000000000..f29f1a7c50a5 --- /dev/null +++ b/samples/client/petstore/crystal/lib/radix/src/radix/version.cr @@ -0,0 +1,3 @@ +module Radix + VERSION = "0.3.9" +end diff --git a/samples/client/petstore/crystal/shard.lock b/samples/client/petstore/crystal/shard.lock new file mode 100644 index 000000000000..97567adcb9e5 --- /dev/null +++ b/samples/client/petstore/crystal/shard.lock @@ -0,0 +1,34 @@ +version: 2.0 +shards: + ameba: + git: https://github.com/crystal-ameba/ameba.git + version: 0.13.3 + + crest: + git: https://github.com/mamantoha/crest.git + version: 0.26.1 + + exception_page: + git: https://github.com/crystal-loot/exception_page.git + version: 0.1.4 + + http-client-digest_auth: + git: https://github.com/mamantoha/http-client-digest_auth.git + version: 0.4.0 + + http_proxy: + git: https://github.com/mamantoha/http_proxy.git + version: 0.7.2 + + kemal: + git: https://github.com/kemalcr/kemal.git + version: 0.27.0 + + kilt: + git: https://github.com/jeromegn/kilt.git + version: 0.4.0 + + radix: + git: https://github.com/luislavena/radix.git + version: 0.3.9 + diff --git a/samples/client/petstore/crystal/shard.yml b/samples/client/petstore/crystal/shard.yml new file mode 100644 index 000000000000..06fc3300e053 --- /dev/null +++ b/samples/client/petstore/crystal/shard.yml @@ -0,0 +1,20 @@ +name: Petstore +version: 1.0.0 +authors: + - +description: | + - +crystal: ">= 0.35.1" +dependencies: + crest: + github: mamantoha/crest + version: ~> 0.26.0 + +development_dependencies: + kemal: + github: kemalcr/kemal + version: ~>0.27.0 + ameba: + github: crystal-ameba/ameba + +license: diff --git a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr new file mode 100644 index 000000000000..43f4748f2337 --- /dev/null +++ b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr @@ -0,0 +1,129 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" +require "spec" + +# Unit tests for Petstore::PetApi +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe "PetApi" do + describe "test an instance of PetApi" do + it "should create an instance of PetApi" do + api_instance = Petstore::PetApi.new + result = api_instance.get_pet_by_id(pet_id: 29) + result.id.should eq 29 + result.category.id.should eq 13 + result.category.name.should eq "Cainine" + result.name.should eq "Wolf" + result.photo_urls.should eq ["http://pathtoimage1"] + result.status.should eq "available" + end + end + + # unit tests for add_pet + # Add a new pet to the store + # @param pet Pet object that needs to be added to the store + # @param [Hash] opts the optional parameters + # @return [Pet] + describe "add_pet test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for delete_pet + # Deletes a pet + # @param pet_id Pet id to delete + # @param [Hash] opts the optional parameters + # @option opts [String] :api_key + # @return [nil] + describe "delete_pet test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for find_pets_by_status + # Finds Pets by status + # Multiple status values can be provided with comma separated strings + # @param status Status values that need to be considered for filter + # @param [Hash] opts the optional parameters + # @return [Array(Pet)] + describe "find_pets_by_status test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for find_pets_by_tags + # Finds Pets by tags + # Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + # @param tags Tags to filter by + # @param [Hash] opts the optional parameters + # @return [Array(Pet)] + describe "find_pets_by_tags test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for get_pet_by_id + # Find pet by ID + # Returns a single pet + # @param pet_id ID of pet to return + # @param [Hash] opts the optional parameters + # @return [Pet] + describe "get_pet_by_id test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for update_pet + # Update an existing pet + # @param pet Pet object that needs to be added to the store + # @param [Hash] opts the optional parameters + # @return [Pet] + describe "update_pet test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for update_pet_with_form + # Updates a pet in the store with form data + # @param pet_id ID of pet that needs to be updated + # @param [Hash] opts the optional parameters + # @option opts [String] :name Updated name of the pet + # @option opts [String] :status Updated status of the pet + # @return [nil] + describe "update_pet_with_form test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for upload_file + # uploads an image + # @param pet_id ID of pet to update + # @param [Hash] opts the optional parameters + # @option opts [String] :additional_metadata Additional data to pass to server + # @option opts [File] :file file to upload + # @return [ApiResponse] + describe "upload_file test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/api/store_api_spec.cr b/samples/client/petstore/crystal/spec/api/store_api_spec.cr new file mode 100644 index 000000000000..ca48fda865ec --- /dev/null +++ b/samples/client/petstore/crystal/spec/api/store_api_spec.cr @@ -0,0 +1,72 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::StoreApi +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe "StoreApi" do + describe "test an instance of StoreApi" do + it "should create an instance of StoreApi" do + api_instance = Petstore::StoreApi.new + # TODO expect(api_instance).to be_instance_of(Petstore::StoreApi) + end + end + + # unit tests for delete_order + # Delete purchase order by ID + # For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + # @param order_id ID of the order that needs to be deleted + # @param [Hash] opts the optional parameters + # @return [nil] + describe "delete_order test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for get_inventory + # Returns pet inventories by status + # Returns a map of status codes to quantities + # @param [Hash] opts the optional parameters + # @return [Hash(String, Int32)] + describe "get_inventory test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for get_order_by_id + # Find purchase order by ID + # For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + # @param order_id ID of pet that needs to be fetched + # @param [Hash] opts the optional parameters + # @return [Order] + describe "get_order_by_id test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for place_order + # Place an order for a pet + # @param order order placed for purchasing the pet + # @param [Hash] opts the optional parameters + # @return [Order] + describe "place_order test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/api/user_api_spec.cr b/samples/client/petstore/crystal/spec/api/user_api_spec.cr new file mode 100644 index 000000000000..e3e6e941261f --- /dev/null +++ b/samples/client/petstore/crystal/spec/api/user_api_spec.cr @@ -0,0 +1,118 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::UserApi +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe "UserApi" do + describe "test an instance of UserApi" do + it "should create an instance of UserApi" do + api_instance = Petstore::UserApi.new + # TODO expect(api_instance).to be_instance_of(Petstore::UserApi) + end + end + + # unit tests for create_user + # Create user + # This can only be done by the logged in user. + # @param user Created user object + # @param [Hash] opts the optional parameters + # @return [nil] + describe "create_user test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for create_users_with_array_input + # Creates list of users with given input array + # @param user List of user object + # @param [Hash] opts the optional parameters + # @return [nil] + describe "create_users_with_array_input test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for create_users_with_list_input + # Creates list of users with given input array + # @param user List of user object + # @param [Hash] opts the optional parameters + # @return [nil] + describe "create_users_with_list_input test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for delete_user + # Delete user + # This can only be done by the logged in user. + # @param username The name that needs to be deleted + # @param [Hash] opts the optional parameters + # @return [nil] + describe "delete_user test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for get_user_by_name + # Get user by user name + # @param username The name that needs to be fetched. Use user1 for testing. + # @param [Hash] opts the optional parameters + # @return [User] + describe "get_user_by_name test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for login_user + # Logs user into the system + # @param username The user name for login + # @param password The password for login in clear text + # @param [Hash] opts the optional parameters + # @return [String] + describe "login_user test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for logout_user + # Logs out current logged in user session + # @param [Hash] opts the optional parameters + # @return [nil] + describe "logout_user test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + # unit tests for update_user + # Updated user + # This can only be done by the logged in user. + # @param username name that need to be deleted + # @param user Updated user object + # @param [Hash] opts the optional parameters + # @return [nil] + describe "update_user test" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/models/api_response_spec.cr b/samples/client/petstore/crystal/spec/models/api_response_spec.cr new file mode 100644 index 000000000000..b8614edf1dd2 --- /dev/null +++ b/samples/client/petstore/crystal/spec/models/api_response_spec.cr @@ -0,0 +1,44 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::ApiResponse +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::ApiResponse do + + describe "test an instance of ApiResponse" do + it "should create an instance of ApiResponse" do + #instance = Petstore::ApiResponse.new + #expect(instance).to be_instance_of(Petstore::ApiResponse) + end + end + describe "test attribute 'code'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute '_type'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'message'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/models/category_spec.cr b/samples/client/petstore/crystal/spec/models/category_spec.cr new file mode 100644 index 000000000000..73a1b2a19f6a --- /dev/null +++ b/samples/client/petstore/crystal/spec/models/category_spec.cr @@ -0,0 +1,38 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::Category +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::Category do + + describe "test an instance of Category" do + it "should create an instance of Category" do + #instance = Petstore::Category.new + #expect(instance).to be_instance_of(Petstore::Category) + end + end + describe "test attribute 'id'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'name'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/models/order_spec.cr b/samples/client/petstore/crystal/spec/models/order_spec.cr new file mode 100644 index 000000000000..f758c0b5e0cd --- /dev/null +++ b/samples/client/petstore/crystal/spec/models/order_spec.cr @@ -0,0 +1,66 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::Order +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::Order do + + describe "test an instance of Order" do + it "should create an instance of Order" do + #instance = Petstore::Order.new + #expect(instance).to be_instance_of(Petstore::Order) + end + end + describe "test attribute 'id'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'pet_id'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'quantity'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'ship_date'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'status'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # validator = Petstore::EnumTest::EnumAttributeValidator.new("String", ["placed", "approved", "delivered"]) + # validator.allowable_values.each do |value| + # expect { instance.status = value }.not_to raise_error + # end + end + end + + describe "test attribute 'complete'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/models/pet_spec.cr b/samples/client/petstore/crystal/spec/models/pet_spec.cr new file mode 100644 index 000000000000..220c4297a983 --- /dev/null +++ b/samples/client/petstore/crystal/spec/models/pet_spec.cr @@ -0,0 +1,66 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::Pet +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::Pet do + + describe "test an instance of Pet" do + it "should create an instance of Pet" do + #instance = Petstore::Pet.new + #expect(instance).to be_instance_of(Petstore::Pet) + end + end + describe "test attribute 'id'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'category'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'name'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'photo_urls'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'tags'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'status'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # validator = Petstore::EnumTest::EnumAttributeValidator.new("String", ["available", "pending", "sold"]) + # validator.allowable_values.each do |value| + # expect { instance.status = value }.not_to raise_error + # end + end + end + +end diff --git a/samples/client/petstore/crystal/spec/models/tag_spec.cr b/samples/client/petstore/crystal/spec/models/tag_spec.cr new file mode 100644 index 000000000000..a6ab4415a555 --- /dev/null +++ b/samples/client/petstore/crystal/spec/models/tag_spec.cr @@ -0,0 +1,38 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::Tag +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::Tag do + + describe "test an instance of Tag" do + it "should create an instance of Tag" do + instance = Petstore::Tag.new(id: 123, name: "tag 1") + #expect(instance).to be_instance_of(Petstore::Tag) + end + end + describe "test attribute 'id'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'name'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/models/user_spec.cr b/samples/client/petstore/crystal/spec/models/user_spec.cr new file mode 100644 index 000000000000..f6303a4263ca --- /dev/null +++ b/samples/client/petstore/crystal/spec/models/user_spec.cr @@ -0,0 +1,74 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "../spec_helper" +require "json" +require "time" + +# Unit tests for Petstore::User +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::User do + + describe "test an instance of User" do + it "should create an instance of User" do + #instance = Petstore::User.new + #expect(instance).to be_instance_of(Petstore::User) + end + end + describe "test attribute 'id'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'username'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'first_name'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'last_name'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'email'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'password'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'phone'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + + describe "test attribute 'user_status'" do + it "should work" do + # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + end + end + +end diff --git a/samples/client/petstore/crystal/spec/spec_helper.cr b/samples/client/petstore/crystal/spec/spec_helper.cr new file mode 100644 index 000000000000..1cafe029c9da --- /dev/null +++ b/samples/client/petstore/crystal/spec/spec_helper.cr @@ -0,0 +1,14 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +# load modules +require "spec" +require "json" +require "../src/petstore" \ No newline at end of file diff --git a/samples/client/petstore/crystal/src/petstore.cr b/samples/client/petstore/crystal/src/petstore.cr new file mode 100644 index 000000000000..a3d79328d012 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore.cr @@ -0,0 +1,33 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +# Dependencies +require "crest" +require "log" + +module Petstore + Log = ::Log.for("Petstore") # => Log for Petstore source + + # Customize default settings for the SDK using block. + # Petstore.configure do |config| + # config.username = "xxx" + # config.password = "xxx" + # end + # If no block given, return the default Configuration object. + def configure + if block_given? + yield(Configuration.default) + else + Configuration.default + end + end +end + +require "./petstore/**" diff --git a/samples/client/petstore/crystal/src/petstore/api/pet_api.cr b/samples/client/petstore/crystal/src/petstore/api/pet_api.cr new file mode 100644 index 000000000000..5ca69c24bcc6 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/api/pet_api.cr @@ -0,0 +1,493 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "uri" + +module Petstore + class PetApi + property api_client : ApiClient + + def initialize(api_client = ApiClient.default) + @api_client = api_client + end + # Add a new pet to the store + # @param pet [Pet] Pet object that needs to be added to the store + # @return [Pet] + def add_pet(pet : Pet) + data, _status_code, _headers = add_pet_with_http_info(pet) + data + end + + # Add a new pet to the store + # @param pet [Pet] Pet object that needs to be added to the store + # @return [Array<(Pet, Integer, Hash)>] Pet data, response status code and response headers + def add_pet_with_http_info(pet : Pet) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.add_pet ..."} + end + # verify the required parameter "pet" is set + if @api_client.config.client_side_validation && pet.nil? + raise ArgumentError.new("Missing the required parameter 'pet' when calling PetApi.add_pet") + end + # resource path + local_var_path = "/pet" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json", "application/xml"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(pet) + + # return_type + return_type = "Pet" + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"PetApi.add_pet", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#add_pet\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Pet.from_json(data), status_code, headers + end + + # Deletes a pet + # @param pet_id [Int64] Pet id to delete + # @return [nil] + def delete_pet(pet_id : Int64, api_key : String?) + delete_pet_with_http_info(pet_id, api_key) + nil + end + + # Deletes a pet + # @param pet_id [Int64] Pet id to delete + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def delete_pet_with_http_info(pet_id : Int64, api_key : String?) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.delete_pet ..."} + end + # verify the required parameter "pet_id" is set + if @api_client.config.client_side_validation && pet_id.nil? + raise ArgumentError.new("Missing the required parameter 'pet_id' when calling PetApi.delete_pet") + end + # resource path + local_var_path = "/pet/{petId}".sub("{" + "petId" + "}", URI.encode(pet_id.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + header_params["api_key"] = api_key + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = nil + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:DELETE, + local_var_path, + :"PetApi.delete_pet", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#delete_pet\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Finds Pets by status + # Multiple status values can be provided with comma separated strings + # @param status [Array(String)] Status values that need to be considered for filter + # @return [Array(Pet)] + def find_pets_by_status(status : Array(String)) + data, _status_code, _headers = find_pets_by_status_with_http_info(status) + data + end + + # Finds Pets by status + # Multiple status values can be provided with comma separated strings + # @param status [Array(String)] Status values that need to be considered for filter + # @return [Array<(Array(Pet), Integer, Hash)>] Array(Pet) data, response status code and response headers + def find_pets_by_status_with_http_info(status : Array(String)) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.find_pets_by_status ..."} + end + # verify the required parameter "status" is set + if @api_client.config.client_side_validation && status.nil? + raise ArgumentError.new("Missing the required parameter 'status' when calling PetApi.find_pets_by_status") + end + # resource path + local_var_path = "/pet/findByStatus" + + # query parameters + query_params = Hash(Symbol, String).new + query_params[:"status"] = @api_client.build_collection_param(status, :csv) + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "Array(Pet)" + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"PetApi.find_pets_by_status", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#find_pets_by_status\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Array(Pet).from_json(data), status_code, headers + end + + # Finds Pets by tags + # Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + # @param tags [Array(String)] Tags to filter by + # @return [Array(Pet)] + def find_pets_by_tags(tags : Array(String)) + data, _status_code, _headers = find_pets_by_tags_with_http_info(tags) + data + end + + # Finds Pets by tags + # Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + # @param tags [Array(String)] Tags to filter by + # @return [Array<(Array(Pet), Integer, Hash)>] Array(Pet) data, response status code and response headers + def find_pets_by_tags_with_http_info(tags : Array(String)) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.find_pets_by_tags ..."} + end + # verify the required parameter "tags" is set + if @api_client.config.client_side_validation && tags.nil? + raise ArgumentError.new("Missing the required parameter 'tags' when calling PetApi.find_pets_by_tags") + end + # resource path + local_var_path = "/pet/findByTags" + + # query parameters + query_params = Hash(Symbol, String).new + query_params[:"tags"] = @api_client.build_collection_param(tags, :csv) + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "Array(Pet)" + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"PetApi.find_pets_by_tags", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#find_pets_by_tags\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Array(Pet).from_json(data), status_code, headers + end + + # Find pet by ID + # Returns a single pet + # @param pet_id [Int64] ID of pet to return + # @return [Pet] + def get_pet_by_id(pet_id : Int64) + data, _status_code, _headers = get_pet_by_id_with_http_info(pet_id) + data + end + + # Find pet by ID + # Returns a single pet + # @param pet_id [Int64] ID of pet to return + # @return [Array<(Pet, Integer, Hash)>] Pet data, response status code and response headers + def get_pet_by_id_with_http_info(pet_id : Int64) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.get_pet_by_id ..."} + end + # verify the required parameter "pet_id" is set + if @api_client.config.client_side_validation && pet_id.nil? + raise ArgumentError.new("Missing the required parameter 'pet_id' when calling PetApi.get_pet_by_id") + end + # resource path + local_var_path = "/pet/{petId}".sub("{" + "petId" + "}", URI.encode(pet_id.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "Pet" + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"PetApi.get_pet_by_id", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#get_pet_by_id\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Pet.from_json(data), status_code, headers + end + + # Update an existing pet + # @param pet [Pet] Pet object that needs to be added to the store + # @return [Pet] + def update_pet(pet : Pet) + data, _status_code, _headers = update_pet_with_http_info(pet) + data + end + + # Update an existing pet + # @param pet [Pet] Pet object that needs to be added to the store + # @return [Array<(Pet, Integer, Hash)>] Pet data, response status code and response headers + def update_pet_with_http_info(pet : Pet) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.update_pet ..."} + end + # verify the required parameter "pet" is set + if @api_client.config.client_side_validation && pet.nil? + raise ArgumentError.new("Missing the required parameter 'pet' when calling PetApi.update_pet") + end + # resource path + local_var_path = "/pet" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json", "application/xml"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(pet) + + # return_type + return_type = "Pet" + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:PUT, + local_var_path, + :"PetApi.update_pet", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#update_pet\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Pet.from_json(data), status_code, headers + end + + # Updates a pet in the store with form data + # @param pet_id [Int64] ID of pet that needs to be updated + # @return [nil] + def update_pet_with_form(pet_id : Int64, name : String?, status : String?) + update_pet_with_form_with_http_info(pet_id, name, status) + nil + end + + # Updates a pet in the store with form data + # @param pet_id [Int64] ID of pet that needs to be updated + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def update_pet_with_form_with_http_info(pet_id : Int64, name : String?, status : String?) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.update_pet_with_form ..."} + end + # verify the required parameter "pet_id" is set + if @api_client.config.client_side_validation && pet_id.nil? + raise ArgumentError.new("Missing the required parameter 'pet_id' when calling PetApi.update_pet_with_form") + end + # resource path + local_var_path = "/pet/{petId}".sub("{" + "petId" + "}", URI.encode(pet_id.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/x-www-form-urlencoded"]) + + # form parameters + form_params = Hash(Symbol, String).new + form_params[:"name"] = name + form_params[:"status"] = status + + # http body (model) + post_body = nil + + # return_type + return_type = nil + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"PetApi.update_pet_with_form", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#update_pet_with_form\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # uploads an image + # @param pet_id [Int64] ID of pet to update + # @return [ApiResponse] + def upload_file(pet_id : Int64, additional_metadata : String?, file : File?) + data, _status_code, _headers = upload_file_with_http_info(pet_id, additional_metadata, file) + data + end + + # uploads an image + # @param pet_id [Int64] ID of pet to update + # @return [Array<(ApiResponse, Integer, Hash)>] ApiResponse data, response status code and response headers + def upload_file_with_http_info(pet_id : Int64, additional_metadata : String?, file : File?) + if @api_client.config.debugging + Log.debug {"Calling API: PetApi.upload_file ..."} + end + # verify the required parameter "pet_id" is set + if @api_client.config.client_side_validation && pet_id.nil? + raise ArgumentError.new("Missing the required parameter 'pet_id' when calling PetApi.upload_file") + end + # resource path + local_var_path = "/pet/{petId}/uploadImage".sub("{" + "petId" + "}", URI.encode(pet_id.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/json"]) + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["multipart/form-data"]) + + # form parameters + form_params = Hash(Symbol, String).new + form_params[:"additionalMetadata"] = additional_metadata + form_params[:"file"] = file + + # http body (model) + post_body = nil + + # return_type + return_type = "ApiResponse" + + # auth_names + auth_names = ["petstore_auth"] + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"PetApi.upload_file", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: PetApi#upload_file\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return ApiResponse.from_json(data), status_code, headers + end + end +end diff --git a/samples/client/petstore/crystal/src/petstore/api/store_api.cr b/samples/client/petstore/crystal/src/petstore/api/store_api.cr new file mode 100644 index 000000000000..b4a469c3ee51 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/api/store_api.cr @@ -0,0 +1,256 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "uri" + +module Petstore + class StoreApi + property api_client : ApiClient + + def initialize(api_client = ApiClient.default) + @api_client = api_client + end + # Delete purchase order by ID + # For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + # @param order_id [String] ID of the order that needs to be deleted + # @return [nil] + def delete_order(order_id : String) + delete_order_with_http_info(order_id) + nil + end + + # Delete purchase order by ID + # For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + # @param order_id [String] ID of the order that needs to be deleted + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def delete_order_with_http_info(order_id : String) + if @api_client.config.debugging + Log.debug {"Calling API: StoreApi.delete_order ..."} + end + # verify the required parameter "order_id" is set + if @api_client.config.client_side_validation && order_id.nil? + raise ArgumentError.new("Missing the required parameter 'order_id' when calling StoreApi.delete_order") + end + # resource path + local_var_path = "/store/order/{orderId}".sub("{" + "orderId" + "}", URI.encode(order_id.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = nil + + # auth_names + auth_names = [] of String + + data, status_code, headers = @api_client.call_api(:DELETE, + local_var_path, + :"StoreApi.delete_order", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: StoreApi#delete_order\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Returns pet inventories by status + # Returns a map of status codes to quantities + # @return [Hash(String, Int32)] + def get_inventory() + data, _status_code, _headers = get_inventory_with_http_info() + data + end + + # Returns pet inventories by status + # Returns a map of status codes to quantities + # @return [Array<(Hash(String, Int32), Integer, Hash)>] Hash(String, Int32) data, response status code and response headers + def get_inventory_with_http_info() + if @api_client.config.debugging + Log.debug {"Calling API: StoreApi.get_inventory ..."} + end + # resource path + local_var_path = "/store/inventory" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "Hash(String, Int32)" + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"StoreApi.get_inventory", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: StoreApi#get_inventory\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Hash(String, Int32).from_json(data), status_code, headers + end + + # Find purchase order by ID + # For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + # @param order_id [Int64] ID of pet that needs to be fetched + # @return [Order] + def get_order_by_id(order_id : Int64) + data, _status_code, _headers = get_order_by_id_with_http_info(order_id) + data + end + + # Find purchase order by ID + # For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + # @param order_id [Int64] ID of pet that needs to be fetched + # @return [Array<(Order, Integer, Hash)>] Order data, response status code and response headers + def get_order_by_id_with_http_info(order_id : Int64) + if @api_client.config.debugging + Log.debug {"Calling API: StoreApi.get_order_by_id ..."} + end + # verify the required parameter "order_id" is set + if @api_client.config.client_side_validation && order_id.nil? + raise ArgumentError.new("Missing the required parameter 'order_id' when calling StoreApi.get_order_by_id") + end + if @api_client.config.client_side_validation && order_id > 5 + raise ArgumentError.new("invalid value for \"order_id\" when calling StoreApi.get_order_by_id, must be smaller than or equal to 5.") + end + + if @api_client.config.client_side_validation && order_id < 1 + raise ArgumentError.new("invalid value for \"order_id\" when calling StoreApi.get_order_by_id, must be greater than or equal to 1.") + end + + # resource path + local_var_path = "/store/order/{orderId}".sub("{" + "orderId" + "}", URI.encode(order_id.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "Order" + + # auth_names + auth_names = [] of String + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"StoreApi.get_order_by_id", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: StoreApi#get_order_by_id\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Order.from_json(data), status_code, headers + end + + # Place an order for a pet + # @param order [Order] order placed for purchasing the pet + # @return [Order] + def place_order(order : Order) + data, _status_code, _headers = place_order_with_http_info(order) + data + end + + # Place an order for a pet + # @param order [Order] order placed for purchasing the pet + # @return [Array<(Order, Integer, Hash)>] Order data, response status code and response headers + def place_order_with_http_info(order : Order) + if @api_client.config.debugging + Log.debug {"Calling API: StoreApi.place_order ..."} + end + # verify the required parameter "order" is set + if @api_client.config.client_side_validation && order.nil? + raise ArgumentError.new("Missing the required parameter 'order' when calling StoreApi.place_order") + end + # resource path + local_var_path = "/store/order" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(order) + + # return_type + return_type = "Order" + + # auth_names + auth_names = [] of String + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"StoreApi.place_order", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: StoreApi#place_order\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return Order.from_json(data), status_code, headers + end + end +end diff --git a/samples/client/petstore/crystal/src/petstore/api/user_api.cr b/samples/client/petstore/crystal/src/petstore/api/user_api.cr new file mode 100644 index 000000000000..796f571a5e40 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/api/user_api.cr @@ -0,0 +1,491 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "uri" + +module Petstore + class UserApi + property api_client : ApiClient + + def initialize(api_client = ApiClient.default) + @api_client = api_client + end + # Create user + # This can only be done by the logged in user. + # @param user [User] Created user object + # @return [nil] + def create_user(user : User) + create_user_with_http_info(user) + nil + end + + # Create user + # This can only be done by the logged in user. + # @param user [User] Created user object + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def create_user_with_http_info(user : User) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.create_user ..."} + end + # verify the required parameter "user" is set + if @api_client.config.client_side_validation && user.nil? + raise ArgumentError.new("Missing the required parameter 'user' when calling UserApi.create_user") + end + # resource path + local_var_path = "/user" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(user) + + # return_type + return_type = nil + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"UserApi.create_user", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#create_user\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Creates list of users with given input array + # @param user [Array(User)] List of user object + # @return [nil] + def create_users_with_array_input(user : Array(User)) + create_users_with_array_input_with_http_info(user) + nil + end + + # Creates list of users with given input array + # @param user [Array(User)] List of user object + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def create_users_with_array_input_with_http_info(user : Array(User)) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.create_users_with_array_input ..."} + end + # verify the required parameter "user" is set + if @api_client.config.client_side_validation && user.nil? + raise ArgumentError.new("Missing the required parameter 'user' when calling UserApi.create_users_with_array_input") + end + # resource path + local_var_path = "/user/createWithArray" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(user) + + # return_type + return_type = nil + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"UserApi.create_users_with_array_input", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#create_users_with_array_input\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Creates list of users with given input array + # @param user [Array(User)] List of user object + # @return [nil] + def create_users_with_list_input(user : Array(User)) + create_users_with_list_input_with_http_info(user) + nil + end + + # Creates list of users with given input array + # @param user [Array(User)] List of user object + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def create_users_with_list_input_with_http_info(user : Array(User)) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.create_users_with_list_input ..."} + end + # verify the required parameter "user" is set + if @api_client.config.client_side_validation && user.nil? + raise ArgumentError.new("Missing the required parameter 'user' when calling UserApi.create_users_with_list_input") + end + # resource path + local_var_path = "/user/createWithList" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(user) + + # return_type + return_type = nil + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:POST, + local_var_path, + :"UserApi.create_users_with_list_input", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#create_users_with_list_input\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Delete user + # This can only be done by the logged in user. + # @param username [String] The name that needs to be deleted + # @return [nil] + def delete_user(username : String) + delete_user_with_http_info(username) + nil + end + + # Delete user + # This can only be done by the logged in user. + # @param username [String] The name that needs to be deleted + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def delete_user_with_http_info(username : String) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.delete_user ..."} + end + # verify the required parameter "username" is set + if @api_client.config.client_side_validation && username.nil? + raise ArgumentError.new("Missing the required parameter 'username' when calling UserApi.delete_user") + end + # resource path + local_var_path = "/user/{username}".sub("{" + "username" + "}", URI.encode(username.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = nil + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:DELETE, + local_var_path, + :"UserApi.delete_user", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#delete_user\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Get user by user name + # @param username [String] The name that needs to be fetched. Use user1 for testing. + # @return [User] + def get_user_by_name(username : String) + data, _status_code, _headers = get_user_by_name_with_http_info(username) + data + end + + # Get user by user name + # @param username [String] The name that needs to be fetched. Use user1 for testing. + # @return [Array<(User, Integer, Hash)>] User data, response status code and response headers + def get_user_by_name_with_http_info(username : String) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.get_user_by_name ..."} + end + # verify the required parameter "username" is set + if @api_client.config.client_side_validation && username.nil? + raise ArgumentError.new("Missing the required parameter 'username' when calling UserApi.get_user_by_name") + end + # resource path + local_var_path = "/user/{username}".sub("{" + "username" + "}", URI.encode(username.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "User" + + # auth_names + auth_names = [] of String + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"UserApi.get_user_by_name", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#get_user_by_name\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return User.from_json(data), status_code, headers + end + + # Logs user into the system + # @param username [String] The user name for login + # @param password [String] The password for login in clear text + # @return [String] + def login_user(username : String, password : String) + data, _status_code, _headers = login_user_with_http_info(username, password) + data + end + + # Logs user into the system + # @param username [String] The user name for login + # @param password [String] The password for login in clear text + # @return [Array<(String, Integer, Hash)>] String data, response status code and response headers + def login_user_with_http_info(username : String, password : String) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.login_user ..."} + end + # verify the required parameter "username" is set + if @api_client.config.client_side_validation && username.nil? + raise ArgumentError.new("Missing the required parameter 'username' when calling UserApi.login_user") + end + pattern = Regexp.new(/^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$/) + if @api_client.config.client_side_validation && username !~ pattern + raise ArgumentError.new("invalid value for \"username\" when calling UserApi.login_user, must conform to the pattern #{pattern}.") + end + + # verify the required parameter "password" is set + if @api_client.config.client_side_validation && password.nil? + raise ArgumentError.new("Missing the required parameter 'password' when calling UserApi.login_user") + end + # resource path + local_var_path = "/user/login" + + # query parameters + query_params = Hash(Symbol, String).new + query_params[:"username"] = username + query_params[:"password"] = password + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Accept" (if needed) + header_params["Accept"] = @api_client.select_header_accept(["application/xml", "application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = "String" + + # auth_names + auth_names = [] of String + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"UserApi.login_user", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#login_user\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return String.from_json(data), status_code, headers + end + + # Logs out current logged in user session + # @return [nil] + def logout_user() + logout_user_with_http_info() + nil + end + + # Logs out current logged in user session + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def logout_user_with_http_info() + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.logout_user ..."} + end + # resource path + local_var_path = "/user/logout" + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = nil + + # return_type + return_type = nil + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:GET, + local_var_path, + :"UserApi.logout_user", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#logout_user\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + + # Updated user + # This can only be done by the logged in user. + # @param username [String] name that need to be deleted + # @param user [User] Updated user object + # @return [nil] + def update_user(username : String, user : User) + update_user_with_http_info(username, user) + nil + end + + # Updated user + # This can only be done by the logged in user. + # @param username [String] name that need to be deleted + # @param user [User] Updated user object + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def update_user_with_http_info(username : String, user : User) + if @api_client.config.debugging + Log.debug {"Calling API: UserApi.update_user ..."} + end + # verify the required parameter "username" is set + if @api_client.config.client_side_validation && username.nil? + raise ArgumentError.new("Missing the required parameter 'username' when calling UserApi.update_user") + end + # verify the required parameter "user" is set + if @api_client.config.client_side_validation && user.nil? + raise ArgumentError.new("Missing the required parameter 'user' when calling UserApi.update_user") + end + # resource path + local_var_path = "/user/{username}".sub("{" + "username" + "}", URI.encode(username.to_s).gsub("%2F", "/")) + + # query parameters + query_params = Hash(Symbol, String).new + + # header parameters + header_params = Hash(String, String).new + # HTTP header "Content-Type" + header_params["Content-Type"] = @api_client.select_header_content_type(["application/json"]) + + # form parameters + form_params = Hash(Symbol, String).new + + # http body (model) + post_body = @api_client.object_to_http_body(user) + + # return_type + return_type = nil + + # auth_names + auth_names = ["api_key"] + + data, status_code, headers = @api_client.call_api(:PUT, + local_var_path, + :"UserApi.update_user", + return_type, + post_body, + auth_names, + header_params, + query_params, + form_params) + if @api_client.config.debugging + Log.debug {"API called: UserApi#update_user\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"} + end + return nil, status_code, headers + end + end +end diff --git a/samples/client/petstore/crystal/src/petstore/api_client.cr b/samples/client/petstore/crystal/src/petstore/api_client.cr new file mode 100644 index 000000000000..2782cec8a530 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/api_client.cr @@ -0,0 +1,404 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "json" +require "time" +require "crest" + +module Petstore + class ApiClient + # The Configuration object holding settings to be used in the API client. + property config : Configuration + + # Defines the headers to be used in HTTP requests of all API calls by default. + # + # @return [Hash] + property default_headers : Hash(String, String) + + # Initializes the ApiClient + # @option config [Configuration] Configuration for initializing the object, default to Configuration.default + def initialize(config = Configuration.default) + @config = config + # TODO @user_agent = "OpenAPI-Generator/#{VERSION}/ruby" + @default_headers = { + "Content-Type" => "application/json" + #TODO: "User-Agent" => @user_agent + } + end + + def self.default + @@default ||= ApiClient.new + end + + # Check if the given MIME is a JSON MIME. + # JSON MIME examples: + # application/json + # application/json; charset=UTF8 + # APPLICATION/JSON + # */* + # @param [String] mime MIME + # @return [Boolean] True if the MIME is application/json + def json_mime?(mime) + (mime == "*/*") || !(mime =~ /Application\/.*json(?!p)(;.*)?/i).nil? + end + + # Deserialize the response to the given return type. + # + # @param [Response] response HTTP response + # @param [String] return_type some examples: "User", "Array", "Hash" + def deserialize(response, return_type) + body = response.body + + # handle file downloading - return the File instance processed in request callbacks + # note that response body is empty when the file is written in chunks in request on_body callback + if return_type == "File" + content_disposition = response.headers["Content-Disposition"].to_s + if content_disposition && content_disposition =~ /filename=/i + filename = content_disposition.match(/filename=[""]?([^""\s]+)[""]?/i).try &.[0] + prefix = sanitize_filename(filename) + else + prefix = "download-" + end + if !prefix.nil? && prefix.ends_with?("-") + prefix = prefix + "-" + end + encoding = response.headers["Content-Encoding"].to_s + + # TODO add file support + raise ApiError.new(code: 0, message: "File response not yet supported in the client.") if return_type + return nil + + #@tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) + #@tempfile.write(@stream.join.force_encoding(encoding)) + #@tempfile.close + #Log.info { "Temp file written to #{@tempfile.path}, please copy the file to a proper folder "\ + # "with e.g. `FileUtils.cp(tempfile.path, \"/new/file/path\")` otherwise the temp file "\ + # "will be deleted automatically with GC. It's also recommended to delete the temp file "\ + # "explicitly with `tempfile.delete`" } + #return @tempfile + end + + return nil if body.nil? || body.empty? + + # return response body directly for String return type + return body if return_type == "String" + + # ensuring a default content type + content_type = response.headers["Content-Type"] || "application/json" + + raise ApiError.new(code: 0, message: "Content-Type is not supported: #{content_type}") unless json_mime?(content_type) + + begin + data = JSON.parse("[#{body}]")[0] + rescue e : Exception + if %w(String Date Time).includes?(return_type) + data = body + else + raise e + end + end + + convert_to_type data, return_type + end + + # Convert data to the given return type. + # @param [Object] data Data to be converted + # @param [String] return_type Return type + # @return [Mixed] Data in a particular type + def convert_to_type(data, return_type) + return nil if data.nil? + case return_type + when "String" + data.to_s + when "Integer" + data.to_s.to_i + when "Float" + data.to_s.to_f + when "Boolean" + data == true + when "Time" + # parse date time (expecting ISO 8601 format) + Time.parse! data.to_s, "%Y-%m-%dT%H:%M:%S%Z" + when "Date" + # parse date (expecting ISO 8601 format) + Time.parse! data.to_s, "%Y-%m-%d" + when "Object" + # generic object (usually a Hash), return directly + data + when /\AArray<(.+)>\z/ + # e.g. Array + sub_type = $1 + data.map { |item| convert_to_type(item, sub_type) } + when /\AHash\\z/ + # e.g. Hash + sub_type = $1 + ({} of Symbol => String).tap do |hash| + data.each { |k, v| hash[k] = convert_to_type(v, sub_type) } + end + else + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(return_type) + klass.respond_to?(:openapi_one_of) ? klass.build(data) : klass.build_from_hash(data) + end + end + + # Sanitize filename by removing path. + # e.g. ../../sun.gif becomes sun.gif + # + # @param [String] filename the filename to be sanitized + # @return [String] the sanitized filename + def sanitize_filename(filename) + if filename.nil? + return nil + else + filename.gsub(/.*[\/\\]/, "") + end + end + + def build_request_url(path : String, operation : Symbol) + # Add leading and trailing slashes to path + path = "/#{path}".gsub(/\/+/, "/") + @config.base_url(operation) + path + end + + # Update hearder and query params based on authentication settings. + # + # @param [Hash] header_params Header parameters + # @param [Hash] query_params Query parameters + # @param [String] auth_names Authentication scheme name + def update_params_for_auth!(header_params, query_params, auth_names) + Array{auth_names}.each do |auth_name| + auth_setting = @config.auth_settings[auth_name] + next unless auth_setting + case auth_setting[:in] + when "header" then header_params[auth_setting[:key]] = auth_setting[:value] + when "query" then query_params[auth_setting[:key]] = auth_setting[:value] + else raise ArgumentError.new("Authentication token must be in `query` of `header`") + end + end + end + + # Sets user agent in HTTP header + # + # @param [String] user_agent User agent (e.g. openapi-generator/ruby/1.0.0) + def user_agent=(user_agent) + @user_agent = user_agent + @default_headers["User-Agent"] = @user_agent + end + + # Return Accept header based on an array of accepts provided. + # @param [Array] accepts array for Accept + # @return [String] the Accept header (e.g. application/json) + def select_header_accept(accepts) : String + #return nil if accepts.nil? || accepts.empty? + # use JSON when present, otherwise use all of the provided + json_accept = accepts.find { |s| json_mime?(s) } + if json_accept.nil? + accepts.join(",") + else + json_accept + end + end + + # Return Content-Type header based on an array of content types provided. + # @param [Array] content_types array for Content-Type + # @return [String] the Content-Type header (e.g. application/json) + def select_header_content_type(content_types) + # use application/json by default + return "application/json" if content_types.nil? || content_types.empty? + # use JSON when present, otherwise use the first one + json_content_type = content_types.find { |s| json_mime?(s) } + json_content_type || content_types.first + end + + # Convert object (array, hash, object, etc) to JSON string. + # @param [Object] model object to be converted into JSON string + # @return [String] JSON string representation of the object + def object_to_http_body(model) + return model if model.nil? || model.is_a?(String) + local_body = nil + if model.is_a?(Array) + local_body = model.map { |m| object_to_hash(m) } + else + local_body = object_to_hash(model) + end + local_body.to_json + end + + # Convert object(non-array) to hash. + # @param [Object] obj object to be converted into JSON string + # @return [String] JSON string representation of the object + def object_to_hash(obj) + if obj.respond_to?(:to_hash) + obj.to_hash + else + obj + end + end + + # Build parameter value according to the given collection format. + # @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi + def build_collection_param(param, collection_format) + case collection_format + when :csv + param.join(",") + when :ssv + param.join(" ") + when :tsv + param.join("\t") + when :pipes + param.join("|") + when :multi + # return the array directly as typhoeus will handle it as expected + param + else + fail "unknown collection format: #{collection_format.inspect}" + end + end + + # Call an API with given options. + # + # @return [Array<(Object, Integer, Hash)>] an array of 3 elements: + # the data deserialized from response body (could be nil), response status code and response headers. + def call_api(http_method : Symbol, path : String, operation : Symbol, return_type : String, post_body : String?, auth_names = [] of String, header_params = {} of String => String, query_params = {} of Symbol => String, form_params = {} of Symbol => String) + #ssl_options = { + # :ca_file => @config.ssl_ca_file, + # :verify => @config.ssl_verify, + # :verify_mode => @config.ssl_verify_mode, + # :client_cert => @config.ssl_client_cert, + # :client_key => @config.ssl_client_key + #} + + #connection = Faraday.new(:url => config.base_url, :ssl => ssl_options) do |conn| + # conn.basic_auth(config.username, config.password) + # if opts[:header_params]["Content-Type"] == "multipart/form-data" + # conn.request :multipart + # conn.request :url_encoded + # end + # conn.adapter(Faraday.default_adapter) + #end + + request = Crest::Request.new(http_method, + build_request_url(path, operation), + params: query_params, + headers: header_params, + #cookies: cookie_params, # TODO add cookies support + form: form_params, + logging: @config.debugging, + handle_errors: false + ) + + response = request.execute + + if @config.debugging + Log.debug {"HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"} + end + + if !response.success? + if response.status == 0 + # Errors from libcurl will be made visible here + raise ApiError.new(code: 0, + message: response.body) + else + raise ApiError.new(code: response.status_code, + response_headers: response.headers, + message: response.body) + end + end + + return response.body, response.status_code, response.headers + end + + # Builds the HTTP request + # + # @param [String] http_method HTTP method/verb (e.g. POST) + # @param [String] path URL path (e.g. /account/new) + # @option opts [Hash] :header_params Header parameters + # @option opts [Hash] :query_params Query parameters + # @option opts [Hash] :form_params Query parameters + # @option opts [Object] :body HTTP body (JSON/XML) + # @return [Typhoeus::Request] A Typhoeus Request + def build_request(http_method, path, request, opts = {} of Symbol => String) + url = build_request_url(path, opts) + http_method = http_method.to_sym.downcase + + header_params = @default_headers.merge(opts[:header_params] || {} of Symbole => String) + query_params = opts[:query_params] || {} of Symbol => String + form_params = opts[:form_params] || {} of Symbol => String + + update_params_for_auth! header_params, query_params, opts[:auth_names] + + req_opts = { + :method => http_method, + :headers => header_params, + :params => query_params, + :params_encoding => @config.params_encoding, + :timeout => @config.timeout, + :verbose => @config.debugging + } + + if [:post, :patch, :put, :delete].include?(http_method) + req_body = build_request_body(header_params, form_params, opts[:body]) + req_opts.update body: req_body + if @config.debugging + Log.debug {"HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"} + end + end + request.headers = header_params + request.body = req_body + request.url url + request.params = query_params + download_file(request) if opts[:return_type] == "File" + request + end + + # Builds the HTTP request body + # + # @param [Hash] header_params Header parameters + # @param [Hash] form_params Query parameters + # @param [Object] body HTTP body (JSON/XML) + # @return [String] HTTP body data in the form of string + def build_request_body(header_params, form_params, body) + # http form + if header_params["Content-Type"] == "application/x-www-form-urlencoded" + data = URI.encode_www_form(form_params) + elsif header_params["Content-Type"] == "multipart/form-data" + data = {} of Symbol => String + form_params.each do |key, value| + case value + when ::File, ::Tempfile + # TODO hardcode to application/octet-stream, need better way to detect content type + data[key] = Faraday::UploadIO.new(value.path, "application/octet-stream", value.path) + when ::Array, nil + # let Faraday handle Array and nil parameters + data[key] = value + else + data[key] = value.to_s + end + end + elsif body + data = body.is_a?(String) ? body : body.to_json + else + data = nil + end + data + end + + # TODO fix streaming response + #def download_file(request) + # @stream = [] + + # # handle streaming Responses + # request.options.on_data = Proc.new do |chunk, overall_received_bytes| + # @stream << chunk + # end + #end + end +end diff --git a/samples/client/petstore/crystal/src/petstore/api_error.cr b/samples/client/petstore/crystal/src/petstore/api_error.cr new file mode 100644 index 000000000000..4ae71b2b1086 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/api_error.cr @@ -0,0 +1,41 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +module Petstore + class ApiError < Exception + getter code : Int32? + getter response_headers : Hash(String, Array(String) | String)? + + # Usage examples: + # ApiError.new + # ApiError.new(message: "message") + # ApiError.new(code: 500, response_headers: {}, message: "") + # ApiError.new(code: 404, message: "Not Found") + def initialize(@code , @message, @response_headers) + end + + def initialize(@code , @message) + end + + # Override to_s to display a friendly error message + def to_s + msg = "" + msg = msg + "\nHTTP status code: #{code}" if @code + msg = msg + "\nResponse headers: #{response_headers}" if @response_headers + if @message.nil? || @message.empty? + msg = msg + "\nError message: the server returns an error but the HTTP respone body is empty." + else + msg = msg + "\nResponse body: #{@message}" + end + + msg + end + end +end diff --git a/samples/client/petstore/crystal/src/petstore/configuration.cr b/samples/client/petstore/crystal/src/petstore/configuration.cr new file mode 100644 index 000000000000..cbd2c642c0e9 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/configuration.cr @@ -0,0 +1,271 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "log" + +module Petstore + class Configuration + # Defines url scheme + property scheme : String + + # Defines url host + property host : String + + # Defines url base path + property base_path : String + + # Define server configuration index + property server_index : Int32 + + # Define server operation configuration index + property server_operation_index : Hash(Symbol, String) + + # Default server variables + property server_variables : Hash(Symbol, String) + + # Default server operation variables + property server_operation_variables : Hash(Symbol, String) + + # Defines API keys used with API Key authentications. + # + # @return [Hash] key: parameter name, value: parameter value (API key) + # + # @example parameter name is "api_key", API key is "xxx" (e.g. "api_key=xxx" in query string) + # config.api_key[:"api_key"] = "xxx" + property api_key : Hash(Symbol, String) + + # Defines API key prefixes used with API Key authentications. + # + # @return [Hash] key: parameter name, value: API key prefix + # + # @example parameter name is "Authorization", API key prefix is "Token" (e.g. "Authorization: Token xxx" in headers) + # config.api_key_prefix[:"api_key"] = "Token" + property api_key_prefix : Hash(Symbol, String) + + # Defines the username used with HTTP basic authentication. + # + # @return [String] + property username : String? + + # Defines the password used with HTTP basic authentication. + # + # @return [String] + property password : String? + + # Defines the access token (Bearer) used with OAuth2. + property access_token : String? + + # Set this to enable/disable debugging. When enabled (set to true), HTTP request/response + # details will be logged with `logger.debug` (see the `logger` attribute). + # Default to false. + # + # @return [true, false] + property debugging : Bool + + # Defines the temporary folder to store downloaded files + # (for API endpoints that have file response). + # Default to use `Tempfile`. + # + # @return [String] + property temp_folder_path : String? + + # The time limit for HTTP request in seconds. + # Default to 0 (never times out). + property timeout : Int32 + + # Set this to false to skip client side validation in the operation. + # Default to true. + # @return [true, false] + property client_side_validation : Bool + + ### TLS/SSL setting + # Set this to false to skip verifying SSL certificate when calling API from https server. + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + #TODO attr_accessor :verify_ssl + + ### TLS/SSL setting + # Set this to false to skip verifying SSL host name + # Default to true. + # + # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks. + # + # @return [true, false] + # TODO attr_accessor :verify_ssl_host + + ### TLS/SSL setting + # Set this to customize the certificate file to verify the peer. + # + # @return [String] the path to the certificate file + # + # @see The `cainfo` option of Typhoeus, `--cert` option of libcurl. Related source code: + # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145 + # TODO attr_accessor :ssl_ca_cert + + ### TLS/SSL setting + # Client certificate file (for client certificate) + # TODO attr_accessor :cert_file + + ### TLS/SSL setting + # Client private key file (for client certificate) + # TODO attr_accessor :key_file + + # Set this to customize parameters encoding of array parameter with multi collectionFormat. + # Default to Nil. + # + # @see The params_encoding option of Ethon. Related source code: + # https://github.com/typhoeus/ethon/blob/master/lib/ethon/easy/queryable.rb#L96 + #property params_encoding : String? + + def initialize + @scheme = "http" + @host = "petstore.swagger.io" + @base_path = "/v2" + @server_index = 0 + @server_operation_index = {} of Symbol => String + @server_variables = {} of Symbol => String + @server_operation_variables = {} of Symbol => String + @api_key = {} of Symbol => String + @api_key_prefix = {} of Symbol => String + @timeout = 0 + @client_side_validation = true + @verify_ssl = true + @verify_ssl_host = true + #@params_encoding = nil + #@cert_file = nil + #@key_file = nil + @debugging = false + @username = nil + @password = nil + @access_token = nil + @temp_folder_path = nil + + # TODO revise below to support block + #yield(self) if block_given? + end + + # The default Configuration object. + def self.default + @@default ||= Configuration.new + end + + def configure + yield(self) if block_given? + end + + def scheme=(scheme) + # remove :// from scheme + @scheme = scheme.sub(/:\/\//, "") + end + + def host=(host) + # remove http(s):// and anything after a slash + @host = host.sub(/https?:\/\//, "").split("/").first + end + + def base_path=(base_path) + # Add leading and trailing slashes to base_path + @base_path = "/#{base_path}".gsub(/\/+/, "/") + @base_path = "" if @base_path == "/" + end + + # Returns base URL for specified operation based on server settings + def base_url(operation = Nil) + # TODO revise below to support operation-level server setting + #index = server_operation_index.fetch(operation, server_index) + return "#{scheme}://#{[host, base_path].join("/").gsub(/\/+/, "/")}".sub(/\/+\z/, "") #if index == Nil + + #server_url(index, server_operation_variables.fetch(operation, server_variables), operation_server_settings[operation]) + end + + # Gets API key (with prefix if set). + # @param [String] param_name the parameter name of API key auth + def api_key_with_prefix(param_name) + if @api_key_prefix[param_name] + "#{@api_key_prefix[param_name]} #{@api_key[param_name]}" + else + @api_key[param_name] + end + end + + # Gets Basic Auth token string + def basic_auth_token + "Basic " + ["#{username}:#{password}"].pack("m").delete("\r\n") + end + + # Returns Auth Settings hash for api client. + def auth_settings + Hash{ "api_key" => { + type: "api_key", + in: "header", + key: "api_key", + value: api_key_with_prefix("api_key") + }, + "petstore_auth" => + { + type: "oauth2", + in: "header", + key: "Authorization", + value: "Bearer #{access_token}" + }, + } + end + + # Returns an array of Server setting + def server_settings + [ + { + url: "http://petstore.swagger.io/v2", + description: "No description provided", + } + ] + end + + def operation_server_settings + end + + # Returns URL based on server settings + # + # @param index array index of the server settings + # @param variables hash of variable and the corresponding value + def server_url(index, variables = {} of Symbol => String, servers = Nil) + servers = server_settings if servers == Nil + + # check array index out of bound + if (index < 0 || index >= servers.size) + raise ArgumentError.new("Invalid index #{index} when selecting the server. Must be less than #{servers.size}") + end + + server = servers[index] + url = server[:url] + + return url unless server.key? :variables + + # go through variable and assign a value + server[:variables].each do |name, variable| + if variables.key?(name) + if (!server[:variables][name].key?(:enum_values) || server[:variables][name][:enum_values].include?(variables[name])) + url.gsub! "{" + name.to_s + "}", variables[name] + else + raise ArgumentError.new("The variable `#{name}` in the server URL has invalid value #{variables[name]}. Must be #{server[:variables][name][:enum_values]}.") + end + else + # use default value + url.gsub! "{" + name.to_s + "}", server[:variables][name][:default_value] + end + end + + url + end + end +end diff --git a/samples/client/petstore/crystal/src/petstore/models/api_response.cr b/samples/client/petstore/crystal/src/petstore/models/api_response.cr new file mode 100644 index 000000000000..f177d75d9d2f --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/models/api_response.cr @@ -0,0 +1,188 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "time" + +module Petstore + # Describes the result of uploading an image resource + class ApiResponse include JSON::Serializable + include JSON::Serializable + @[JSON::Field(key: code, type: Int32)] + property code : Int32 + + + @[JSON::Field(key: type, type: String)] + property _type : String + + + @[JSON::Field(key: message, type: String)] + property message : String + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(@code : Int32 | Nil, @_type : String | Nil, @message : String | Nil) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = Array.new + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + true + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + code == o.code && + _type == o._type && + message == o.message + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [code, _type, message].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} of Symbol => String + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + + end + +end diff --git a/samples/client/petstore/crystal/src/petstore/models/category.cr b/samples/client/petstore/crystal/src/petstore/models/category.cr new file mode 100644 index 000000000000..9bd40aadf7eb --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/models/category.cr @@ -0,0 +1,200 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "time" + +module Petstore + # A category for a pet + class Category include JSON::Serializable + include JSON::Serializable + @[JSON::Field(key: id, type: Int64)] + property id : Int64 + + + @[JSON::Field(key: name, type: String)] + property name : String + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(@id : Int64 | Nil, @name : String | Nil) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = Array.new + pattern = Regexp.new(/^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$/) + if !@name.nil? && @name !~ pattern + invalid_properties.push("invalid value for \"name\", must conform to the pattern #{pattern}.") + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + return false if !@name.nil? && @name !~ Regexp.new(/^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$/) + true + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + pattern = Regexp.new(/^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$/) + if !name.nil? && name !~ pattern + raise ArgumentError.new("invalid value for \"name\", must conform to the pattern #{pattern}.") + end + + @name = name + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + name == o.name + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, name].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} of Symbol => String + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + + end + +end diff --git a/samples/client/petstore/crystal/src/petstore/models/order.cr b/samples/client/petstore/crystal/src/petstore/models/order.cr new file mode 100644 index 000000000000..e88c5428a6bd --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/models/order.cr @@ -0,0 +1,239 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "time" + +module Petstore + # An order for a pets from the pet store + class Order include JSON::Serializable + include JSON::Serializable + @[JSON::Field(key: id, type: Int64)] + property id : Int64 + + + @[JSON::Field(key: petId, type: Int64)] + property pet_id : Int64 + + + @[JSON::Field(key: quantity, type: Int32)] + property quantity : Int32 + + + @[JSON::Field(key: shipDate, type: Time)] + property ship_date : Time + + + # Order Status + @[JSON::Field(key: status, type: String)] + property status : String + + + @[JSON::Field(key: complete, type: Bool)] + property complete : Bool + + class EnumAttributeValidator + getter datatype : String + getter allowable_values : Array(String) + + def initialize(datatype, allowable_values) + @datatype = datatype + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(@id : Int64 | Nil, @pet_id : Int64 | Nil, @quantity : Int32 | Nil, @ship_date : Time | Nil, @status : String | Nil, @complete : Bool | Nil) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = Array.new + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + status_validator = EnumAttributeValidator.new("String", ["placed", "approved", "delivered"]) + return false unless status_validator.valid?(@status) + true + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] status Object to be assigned + def status=(status) + validator = EnumAttributeValidator.new("String", ["placed", "approved", "delivered"]) + unless validator.valid?(status) + raise ArgumentError.new("invalid value for \"status\", must be one of #{validator.allowable_values}.") + end + @status = status + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + pet_id == o.pet_id && + quantity == o.quantity && + ship_date == o.ship_date && + status == o.status && + complete == o.complete + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, pet_id, quantity, ship_date, status, complete].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} of Symbol => String + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + + end + +end diff --git a/samples/client/petstore/crystal/src/petstore/models/pet.cr b/samples/client/petstore/crystal/src/petstore/models/pet.cr new file mode 100644 index 000000000000..6b6a19ef363d --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/models/pet.cr @@ -0,0 +1,249 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "time" + +module Petstore + # A pet for sale in the pet store + class Pet include JSON::Serializable + include JSON::Serializable + @[JSON::Field(key: id, type: Int64)] + property id : Int64 + + + @[JSON::Field(key: category, type: Category)] + property category : Category + + + @[JSON::Field(key: name, type: String)] + property name : String + + + @[JSON::Field(key: photoUrls, type: Array(String))] + property photo_urls : Array(String) + + + @[JSON::Field(key: tags, type: Array(Tag))] + property tags : Array(Tag) + + + # pet status in the store + @[JSON::Field(key: status, type: String)] + property status : String + + class EnumAttributeValidator + getter datatype : String + getter allowable_values : Array(String) + + def initialize(datatype, allowable_values) + @datatype = datatype + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(@id : Int64 | Nil, @category : Category | Nil, @name : String, @photo_urls : Array(String), @tags : Array(Tag) | Nil, @status : String | Nil) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = Array.new + if @name.nil? + invalid_properties.push("invalid value for \"name\", name cannot be nil.") + end + + if @photo_urls.nil? + invalid_properties.push("invalid value for \"photo_urls\", photo_urls cannot be nil.") + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + return false if @name.nil? + return false if @photo_urls.nil? + status_validator = EnumAttributeValidator.new("String", ["available", "pending", "sold"]) + return false unless status_validator.valid?(@status) + true + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] status Object to be assigned + def status=(status) + validator = EnumAttributeValidator.new("String", ["available", "pending", "sold"]) + unless validator.valid?(status) + raise ArgumentError.new("invalid value for \"status\", must be one of #{validator.allowable_values}.") + end + @status = status + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + category == o.category && + name == o.name && + photo_urls == o.photo_urls && + tags == o.tags && + status == o.status + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, category, name, photo_urls, tags, status].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} of Symbol => String + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + + end + +end diff --git a/samples/client/petstore/crystal/src/petstore/models/tag.cr b/samples/client/petstore/crystal/src/petstore/models/tag.cr new file mode 100644 index 000000000000..8c85fbcd3c22 --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/models/tag.cr @@ -0,0 +1,183 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "time" + +module Petstore + # A tag for a pet + class Tag include JSON::Serializable + include JSON::Serializable + @[JSON::Field(key: id, type: Int64)] + property id : Int64 + + + @[JSON::Field(key: name, type: String)] + property name : String + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(@id : Int64 | Nil, @name : String | Nil) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = Array.new + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + true + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + name == o.name + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, name].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} of Symbol => String + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + + end + +end diff --git a/samples/client/petstore/crystal/src/petstore/models/user.cr b/samples/client/petstore/crystal/src/petstore/models/user.cr new file mode 100644 index 000000000000..75b75c3f800e --- /dev/null +++ b/samples/client/petstore/crystal/src/petstore/models/user.cr @@ -0,0 +1,214 @@ +# #OpenAPI Petstore +# +##This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +# +#The version of the OpenAPI document: 1.0.0 +# +#Generated by: https://openapi-generator.tech +#OpenAPI Generator version: 5.0.1-SNAPSHOT +# + +require "time" + +module Petstore + # A User who is purchasing from the pet store + class User include JSON::Serializable + include JSON::Serializable + @[JSON::Field(key: id, type: Int64)] + property id : Int64 + + + @[JSON::Field(key: username, type: String)] + property username : String + + + @[JSON::Field(key: firstName, type: String)] + property first_name : String + + + @[JSON::Field(key: lastName, type: String)] + property last_name : String + + + @[JSON::Field(key: email, type: String)] + property email : String + + + @[JSON::Field(key: password, type: String)] + property password : String + + + @[JSON::Field(key: phone, type: String)] + property phone : String + + + # User Status + @[JSON::Field(key: userStatus, type: Int32)] + property user_status : Int32 + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(@id : Int64 | Nil, @username : String | Nil, @first_name : String | Nil, @last_name : String | Nil, @email : String | Nil, @password : String | Nil, @phone : String | Nil, @user_status : Int32 | Nil) + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + invalid_properties = Array.new + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + true + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + username == o.username && + first_name == o.first_name && + last_name == o.last_name && + email == o.email && + password == o.password && + phone == o.phone && + user_status == o.user_status + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, username, first_name, last_name, email, password, phone, user_status].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + new.build_from_hash(attributes) + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + self.class.openapi_types.each_pair do |key, type| + if attributes[self.class.attribute_map[key]].nil? && self.class.openapi_nullable.include?(key) + self.send("#{key}=", nil) + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[self.class.attribute_map[key]].is_a?(Array) + self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) }) + end + elsif !attributes[self.class.attribute_map[key]].nil? + self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]])) + end + end + + self + end + + # Deserializes the data based on type + # @param string type Data type + # @param string value Value to be deserialized + # @return [Object] Deserialized data + def _deserialize(type, value) + case type.to_sym + when :Time + Time.parse(value) + when :Date + Date.parse(value) + when :String + value.to_s + when :Integer + value.to_i + when :Float + value.to_f + when :Boolean + if value.to_s =~ /\A(true|t|yes|y|1)\z/i + true + else + false + end + when :Object + # generic object (usually a Hash), return directly + value + when /\AArray<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+?), (?.+)>\z/ + k_type = Regexp.last_match[:k_type] + v_type = Regexp.last_match[:v_type] + ({} of Symbol => String).tap do |hash| + value.each do |k, v| + hash[_deserialize(k_type, k)] = _deserialize(v_type, v) + end + end + else # model + # models (e.g. Pet) or oneOf + klass = Petstore.const_get(type) + klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value) + end + end + + # Returns the string representation of the object + # @return [String] String presentation of the object + def to_s + to_hash.to_s + end + + # to_body is an alias to to_hash (backward compatibility) + # @return [Hash] Returns the object in the form of hash + def to_body + to_hash + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} of Symbol => String + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + # Outputs non-array value in the form of hash + # For object, use to_hash. Otherwise, just return the value + # @param [Object] value Any valid value + # @return [Hash] Returns the value in the form of hash + def _to_hash(value) + if value.is_a?(Array) + value.compact.map { |v| _to_hash(v) } + elsif value.is_a?(Hash) + ({} of Symbol => String).tap do |hash| + value.each { |k, v| hash[k] = _to_hash(v) } + end + elsif value.respond_to? :to_hash + value.to_hash + else + value + end + end + + end + +end From 2e0d1581ee6c2bcd396db1a03d261a1768cd4600 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 00:14:26 +0800 Subject: [PATCH 02/26] update samples --- samples/client/petstore/crystal/src/petstore/api_client.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/client/petstore/crystal/src/petstore/api_client.cr b/samples/client/petstore/crystal/src/petstore/api_client.cr index 2782cec8a530..c857d5be6722 100644 --- a/samples/client/petstore/crystal/src/petstore/api_client.cr +++ b/samples/client/petstore/crystal/src/petstore/api_client.cr @@ -26,9 +26,9 @@ module Petstore # @option config [Configuration] Configuration for initializing the object, default to Configuration.default def initialize(config = Configuration.default) @config = config - # TODO @user_agent = "OpenAPI-Generator/#{VERSION}/ruby" + # TODO @user_agent = "OpenAPI-Generator/#{VERSION}/crystal" @default_headers = { - "Content-Type" => "application/json" + #"Content-Type" => "application/json" #TODO: "User-Agent" => @user_agent } end From 3c7d25fe397ec6b3177bd3a11a594b568d39c2a4 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 00:19:22 +0800 Subject: [PATCH 03/26] update doc --- docs/generators.md | 1 + docs/generators/crystal.md | 226 +++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 docs/generators/crystal.md diff --git a/docs/generators.md b/docs/generators.md index 8a68e5295329..37d5902116f9 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -16,6 +16,7 @@ The following generators are available: * [cpp-restsdk](generators/cpp-restsdk.md) * [cpp-tizen](generators/cpp-tizen.md) * [cpp-ue4 (beta)](generators/cpp-ue4.md) +* [crystal (beta)](generators/crystal.md) * [csharp](generators/csharp.md) * [csharp-dotnet2 (deprecated)](generators/csharp-dotnet2.md) * [csharp-netcore](generators/csharp-netcore.md) diff --git a/docs/generators/crystal.md b/docs/generators/crystal.md new file mode 100644 index 000000000000..8965a800fff1 --- /dev/null +++ b/docs/generators/crystal.md @@ -0,0 +1,226 @@ +--- +title: Config Options for crystal +sidebar_label: crystal +--- + +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. + +| Option | Description | Values | Default | +| ------ | ----------- | ------ | ------- | +|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false| +|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
    **false**
    The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
    **true**
    Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
    |true| +|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true| +|legacyDiscriminatorBehavior|Set to true for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|
    **true**
    The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
    **false**
    The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
    |true| +|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| +|shardAuthor|shard author (only one is supported).| |null| +|shardAuthorEmail|shard author email (only one is supported).| |null| +|shardDescription|shard description.| |This shard maps to a REST API| +|shardHomepage|shard homepage.| |http://org.openapitools| +|shardLicense|shard license.| |unlicense| +|shardName|shard name (e.g. twitter_client| |openapi_client| +|shardVersion|shard version.| |1.0.0| +|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| +|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| + +## IMPORT MAPPING + +| Type/Alias | Imports | +| ---------- | ------- | + + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | +|array|Array| +|map|Hash| +|set|Set| + + +## LANGUAGE PRIMITIVES + +
      +
    • Array
    • +
    • Boolean
    • +
    • Date
    • +
    • File
    • +
    • Float
    • +
    • Hash
    • +
    • Integer
    • +
    • Object
    • +
    • String
    • +
    • Time
    • +
    + +## RESERVED WORDS + +
      +
    • abstract
    • +
    • alias
    • +
    • as
    • +
    • as?
    • +
    • asm
    • +
    • begin
    • +
    • break
    • +
    • case
    • +
    • class
    • +
    • def
    • +
    • do
    • +
    • else
    • +
    • elsif
    • +
    • end
    • +
    • ensure
    • +
    • enum
    • +
    • extend
    • +
    • false
    • +
    • for
    • +
    • fun
    • +
    • if
    • +
    • in
    • +
    • include
    • +
    • instance
    • +
    • is_a?
    • +
    • lib
    • +
    • macro
    • +
    • module
    • +
    • next
    • +
    • nil
    • +
    • nil?
    • +
    • of
    • +
    • out
    • +
    • pointerof
    • +
    • private
    • +
    • protected
    • +
    • require
    • +
    • rescue
    • +
    • responds_to?
    • +
    • return
    • +
    • select
    • +
    • self
    • +
    • sizeof
    • +
    • struct
    • +
    • super
    • +
    • then
    • +
    • true
    • +
    • type
    • +
    • typeof
    • +
    • uninitialized
    • +
    • union
    • +
    • unless
    • +
    • until
    • +
    • verbatim
    • +
    • when
    • +
    • while
    • +
    • with
    • +
    • yield
    • +
    + +## FEATURE SET + + +### Client Modification Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasePath|✓|ToolingExtension +|Authorizations|✗|ToolingExtension +|UserAgent|✓|ToolingExtension +|MockServer|✗|ToolingExtension + +### Data Type Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Custom|✗|OAS2,OAS3 +|Int32|✓|OAS2,OAS3 +|Int64|✓|OAS2,OAS3 +|Float|✓|OAS2,OAS3 +|Double|✓|OAS2,OAS3 +|Decimal|✓|ToolingExtension +|String|✓|OAS2,OAS3 +|Byte|✓|OAS2,OAS3 +|Binary|✓|OAS2,OAS3 +|Boolean|✓|OAS2,OAS3 +|Date|✓|OAS2,OAS3 +|DateTime|✓|OAS2,OAS3 +|Password|✓|OAS2,OAS3 +|File|✓|OAS2 +|Array|✓|OAS2,OAS3 +|Maps|✓|ToolingExtension +|CollectionFormat|✓|OAS2 +|CollectionFormatMulti|✓|OAS2 +|Enum|✓|OAS2,OAS3 +|ArrayOfEnum|✓|ToolingExtension +|ArrayOfModel|✓|ToolingExtension +|ArrayOfCollectionOfPrimitives|✓|ToolingExtension +|ArrayOfCollectionOfModel|✓|ToolingExtension +|ArrayOfCollectionOfEnum|✓|ToolingExtension +|MapOfEnum|✓|ToolingExtension +|MapOfModel|✓|ToolingExtension +|MapOfCollectionOfPrimitives|✓|ToolingExtension +|MapOfCollectionOfModel|✓|ToolingExtension +|MapOfCollectionOfEnum|✓|ToolingExtension + +### Documentation Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Readme|✓|ToolingExtension +|Model|✓|ToolingExtension +|Api|✓|ToolingExtension + +### Global Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Host|✓|OAS2,OAS3 +|BasePath|✓|OAS2,OAS3 +|Info|✓|OAS2,OAS3 +|Schemes|✗|OAS2,OAS3 +|PartialSchemes|✓|OAS2,OAS3 +|Consumes|✓|OAS2 +|Produces|✓|OAS2 +|ExternalDocumentation|✓|OAS2,OAS3 +|Examples|✓|OAS2,OAS3 +|XMLStructureDefinitions|✗|OAS2,OAS3 +|MultiServer|✗|OAS3 +|ParameterizedServer|✗|OAS3 +|ParameterStyling|✗|OAS3 +|Callbacks|✗|OAS3 +|LinkObjects|✗|OAS3 + +### Parameter Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Path|✓|OAS2,OAS3 +|Query|✓|OAS2,OAS3 +|Header|✓|OAS2,OAS3 +|Body|✓|OAS2 +|FormUnencoded|✓|OAS2 +|FormMultipart|✓|OAS2 +|Cookie|✗|OAS3 + +### Schema Support Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Simple|✓|OAS2,OAS3 +|Composite|✓|OAS2,OAS3 +|Polymorphism|✓|OAS2,OAS3 +|Union|✗|OAS3 + +### Security Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasicAuth|✓|OAS2,OAS3 +|ApiKey|✓|OAS2,OAS3 +|OpenIDConnect|✗|OAS3 +|BearerToken|✓|OAS3 +|OAuth2_Implicit|✓|OAS2,OAS3 +|OAuth2_Password|✗|OAS2,OAS3 +|OAuth2_ClientCredentials|✗|OAS2,OAS3 +|OAuth2_AuthorizationCode|✗|OAS2,OAS3 + +### Wire Format Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|JSON|✓|OAS2,OAS3 +|XML|✓|OAS2,OAS3 +|PROTOBUF|✗|ToolingExtension +|Custom|✓|OAS2,OAS3 From 208a5c0ad9c4304fdb43337d2e8c6b755788d8b3 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 11:30:32 +0800 Subject: [PATCH 04/26] regenerate spec --- .../main/resources/crystal/api_test.mustache | 2 +- .../resources/crystal/model_test.mustache | 4 +-- .../petstore/crystal/.openapi-generator/FILES | 9 +++++++ .../petstore/crystal/spec/api/pet_api_spec.cr | 25 +++++++------------ .../crystal/spec/api/store_api_spec.cr | 8 +++--- .../crystal/spec/api/user_api_spec.cr | 16 ++++++------ .../crystal/spec/models/api_response_spec.cr | 6 ++--- .../crystal/spec/models/category_spec.cr | 4 +-- .../crystal/spec/models/order_spec.cr | 12 ++++----- .../petstore/crystal/spec/models/pet_spec.cr | 12 ++++----- .../petstore/crystal/spec/models/tag_spec.cr | 6 ++--- .../petstore/crystal/spec/models/user_spec.cr | 16 ++++++------ 12 files changed, 61 insertions(+), 59 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/crystal/api_test.mustache b/modules/openapi-generator/src/main/resources/crystal/api_test.mustache index a2c6ab5f0215..f4e0eb49e648 100644 --- a/modules/openapi-generator/src/main/resources/crystal/api_test.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/api_test.mustache @@ -29,7 +29,7 @@ require "time" {{/required}}{{/allParams}} # @return [{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}nil{{/returnType}}] describe "{{operationId}} test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/modules/openapi-generator/src/main/resources/crystal/model_test.mustache b/modules/openapi-generator/src/main/resources/crystal/model_test.mustache index 08733f8000bf..3cc572b58788 100644 --- a/modules/openapi-generator/src/main/resources/crystal/model_test.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/model_test.mustache @@ -22,14 +22,14 @@ describe {{moduleName}}::{{classname}} do describe "test attribute '{{{name}}}'" do it "should work" do {{#isEnum}} - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html # validator = Petstore::EnumTest::EnumAttributeValidator.new("{{{dataType}}}", [{{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}]) # validator.allowable_values.each do |value| # expect { instance.{{name}} = value }.not_to raise_error # end {{/isEnum}} {{^isEnum}} - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html {{/isEnum}} end end diff --git a/samples/client/petstore/crystal/.openapi-generator/FILES b/samples/client/petstore/crystal/.openapi-generator/FILES index aa17a1e4b987..99795a83feb6 100644 --- a/samples/client/petstore/crystal/.openapi-generator/FILES +++ b/samples/client/petstore/crystal/.openapi-generator/FILES @@ -3,6 +3,15 @@ README.md git_push.sh shard.yml +spec/api/pet_api_spec.cr +spec/api/store_api_spec.cr +spec/api/user_api_spec.cr +spec/models/api_response_spec.cr +spec/models/category_spec.cr +spec/models/order_spec.cr +spec/models/pet_spec.cr +spec/models/tag_spec.cr +spec/models/user_spec.cr spec/spec_helper.cr src/petstore.cr src/petstore/api/pet_api.cr diff --git a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr index 43f4748f2337..eb449bed80aa 100644 --- a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr +++ b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr @@ -11,7 +11,6 @@ require "../spec_helper" require "json" require "time" -require "spec" # Unit tests for Petstore::PetApi # Automatically generated by openapi-generator (https://openapi-generator.tech) @@ -20,13 +19,7 @@ describe "PetApi" do describe "test an instance of PetApi" do it "should create an instance of PetApi" do api_instance = Petstore::PetApi.new - result = api_instance.get_pet_by_id(pet_id: 29) - result.id.should eq 29 - result.category.id.should eq 13 - result.category.name.should eq "Cainine" - result.name.should eq "Wolf" - result.photo_urls.should eq ["http://pathtoimage1"] - result.status.should eq "available" + # TODO expect(api_instance).to be_instance_of(Petstore::PetApi) end end @@ -37,7 +30,7 @@ describe "PetApi" do # @return [Pet] describe "add_pet test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -49,7 +42,7 @@ describe "PetApi" do # @return [nil] describe "delete_pet test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -61,7 +54,7 @@ describe "PetApi" do # @return [Array(Pet)] describe "find_pets_by_status test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -73,7 +66,7 @@ describe "PetApi" do # @return [Array(Pet)] describe "find_pets_by_tags test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -85,7 +78,7 @@ describe "PetApi" do # @return [Pet] describe "get_pet_by_id test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -96,7 +89,7 @@ describe "PetApi" do # @return [Pet] describe "update_pet test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -109,7 +102,7 @@ describe "PetApi" do # @return [nil] describe "update_pet_with_form test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -122,7 +115,7 @@ describe "PetApi" do # @return [ApiResponse] describe "upload_file test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/api/store_api_spec.cr b/samples/client/petstore/crystal/spec/api/store_api_spec.cr index ca48fda865ec..592747d6fe7b 100644 --- a/samples/client/petstore/crystal/spec/api/store_api_spec.cr +++ b/samples/client/petstore/crystal/spec/api/store_api_spec.cr @@ -31,7 +31,7 @@ describe "StoreApi" do # @return [nil] describe "delete_order test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -42,7 +42,7 @@ describe "StoreApi" do # @return [Hash(String, Int32)] describe "get_inventory test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -54,7 +54,7 @@ describe "StoreApi" do # @return [Order] describe "get_order_by_id test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -65,7 +65,7 @@ describe "StoreApi" do # @return [Order] describe "place_order test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/api/user_api_spec.cr b/samples/client/petstore/crystal/spec/api/user_api_spec.cr index e3e6e941261f..38a13b9d7c93 100644 --- a/samples/client/petstore/crystal/spec/api/user_api_spec.cr +++ b/samples/client/petstore/crystal/spec/api/user_api_spec.cr @@ -31,7 +31,7 @@ describe "UserApi" do # @return [nil] describe "create_user test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -42,7 +42,7 @@ describe "UserApi" do # @return [nil] describe "create_users_with_array_input test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -53,7 +53,7 @@ describe "UserApi" do # @return [nil] describe "create_users_with_list_input test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -65,7 +65,7 @@ describe "UserApi" do # @return [nil] describe "delete_user test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -76,7 +76,7 @@ describe "UserApi" do # @return [User] describe "get_user_by_name test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -88,7 +88,7 @@ describe "UserApi" do # @return [String] describe "login_user test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -98,7 +98,7 @@ describe "UserApi" do # @return [nil] describe "logout_user test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end @@ -111,7 +111,7 @@ describe "UserApi" do # @return [nil] describe "update_user test" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/models/api_response_spec.cr b/samples/client/petstore/crystal/spec/models/api_response_spec.cr index b8614edf1dd2..3a4ff0b1269e 100644 --- a/samples/client/petstore/crystal/spec/models/api_response_spec.cr +++ b/samples/client/petstore/crystal/spec/models/api_response_spec.cr @@ -25,19 +25,19 @@ describe Petstore::ApiResponse do end describe "test attribute 'code'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute '_type'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'message'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/models/category_spec.cr b/samples/client/petstore/crystal/spec/models/category_spec.cr index 73a1b2a19f6a..120fa0459f18 100644 --- a/samples/client/petstore/crystal/spec/models/category_spec.cr +++ b/samples/client/petstore/crystal/spec/models/category_spec.cr @@ -25,13 +25,13 @@ describe Petstore::Category do end describe "test attribute 'id'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'name'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/models/order_spec.cr b/samples/client/petstore/crystal/spec/models/order_spec.cr index f758c0b5e0cd..e64ada1fb51b 100644 --- a/samples/client/petstore/crystal/spec/models/order_spec.cr +++ b/samples/client/petstore/crystal/spec/models/order_spec.cr @@ -25,31 +25,31 @@ describe Petstore::Order do end describe "test attribute 'id'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'pet_id'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'quantity'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'ship_date'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'status'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html # validator = Petstore::EnumTest::EnumAttributeValidator.new("String", ["placed", "approved", "delivered"]) # validator.allowable_values.each do |value| # expect { instance.status = value }.not_to raise_error @@ -59,7 +59,7 @@ describe Petstore::Order do describe "test attribute 'complete'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/models/pet_spec.cr b/samples/client/petstore/crystal/spec/models/pet_spec.cr index 220c4297a983..d3eaf60dfc11 100644 --- a/samples/client/petstore/crystal/spec/models/pet_spec.cr +++ b/samples/client/petstore/crystal/spec/models/pet_spec.cr @@ -25,37 +25,37 @@ describe Petstore::Pet do end describe "test attribute 'id'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'category'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'name'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'photo_urls'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'tags'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'status'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html # validator = Petstore::EnumTest::EnumAttributeValidator.new("String", ["available", "pending", "sold"]) # validator.allowable_values.each do |value| # expect { instance.status = value }.not_to raise_error diff --git a/samples/client/petstore/crystal/spec/models/tag_spec.cr b/samples/client/petstore/crystal/spec/models/tag_spec.cr index a6ab4415a555..f79e12657fc5 100644 --- a/samples/client/petstore/crystal/spec/models/tag_spec.cr +++ b/samples/client/petstore/crystal/spec/models/tag_spec.cr @@ -19,19 +19,19 @@ describe Petstore::Tag do describe "test an instance of Tag" do it "should create an instance of Tag" do - instance = Petstore::Tag.new(id: 123, name: "tag 1") + #instance = Petstore::Tag.new #expect(instance).to be_instance_of(Petstore::Tag) end end describe "test attribute 'id'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'name'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end diff --git a/samples/client/petstore/crystal/spec/models/user_spec.cr b/samples/client/petstore/crystal/spec/models/user_spec.cr index f6303a4263ca..6e52fc2c5bdf 100644 --- a/samples/client/petstore/crystal/spec/models/user_spec.cr +++ b/samples/client/petstore/crystal/spec/models/user_spec.cr @@ -25,49 +25,49 @@ describe Petstore::User do end describe "test attribute 'id'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'username'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'first_name'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'last_name'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'email'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'password'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'phone'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end describe "test attribute 'user_status'" do it "should work" do - # assertion here. ref: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers + # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html end end From 11b39d1ec0d1a38e50bb1a8f28064dbc3b041769 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 11:34:34 +0800 Subject: [PATCH 05/26] regenerate spec --- .../src/main/resources/crystal/api_client.mustache | 6 ++---- .../client/petstore/crystal/spec/api/pet_api_spec.cr | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache index 204c21c36027..de8d95d30c29 100644 --- a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache @@ -16,13 +16,11 @@ module {{moduleName}} # Initializes the ApiClient # @option config [Configuration] Configuration for initializing the object, default to Configuration.default - def initialize(config = Configuration.default) - @config = config + def initialize(@config = Configuration.default) # TODO @user_agent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/#{VERSION}/crystal{{/httpUserAgent}}" @default_headers = { - #"Content-Type" => "application/json" #TODO: "User-Agent" => @user_agent - } + } of String => String end def self.default diff --git a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr index eb449bed80aa..1288fb61cd42 100644 --- a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr +++ b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr @@ -79,6 +79,16 @@ describe "PetApi" do describe "get_pet_by_id test" do it "should work" do # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html + api_instance = Petstore::PetApi.new + # create a pet to start with + pet = Petstore::Pet.new() + result = api_instance.get_pet_by_id(pet_id: 29) + result.id.should eq 29 + result.category.id.should eq 13 + result.category.name.should eq "Cainine" + result.name.should eq "Wolf" + result.photo_urls.should eq ["http://pathtoimage1"] + result.status.should eq "available" end end From c9f623bc89e8cca930984ffd86a893ff7f2a2add Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 12:08:02 +0800 Subject: [PATCH 06/26] various fixes --- .../src/main/resources/crystal/api.mustache | 2 +- .../resources/crystal/api_client.mustache | 16 ++++++++++++---- .../resources/crystal/shard_name.mustache | 2 ++ .../petstore/crystal/.openapi-generator/FILES | 9 --------- .../petstore/crystal/spec/api/pet_api_spec.cr | 19 ++++++++++++------- .../client/petstore/crystal/src/petstore.cr | 2 ++ .../crystal/src/petstore/api/pet_api.cr | 4 ++-- .../crystal/src/petstore/api/store_api.cr | 2 +- .../crystal/src/petstore/api/user_api.cr | 8 ++++---- .../crystal/src/petstore/api_client.cr | 18 ++++++++++++------ 10 files changed, 48 insertions(+), 34 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/crystal/api.mustache b/modules/openapi-generator/src/main/resources/crystal/api.mustache index 6d702115a0f4..550f017c4c62 100644 --- a/modules/openapi-generator/src/main/resources/crystal/api.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/api.mustache @@ -154,7 +154,7 @@ module {{moduleName}} {{/formParams}} # http body (model) - post_body = {{#bodyParam}}@api_client.object_to_http_body({{{paramName}}}){{/bodyParam}}{{^bodyParam}}nil{{/bodyParam}} + post_body = {{#bodyParam}}{{{paramName}}}.to_json{{/bodyParam}}{{^bodyParam}}nil{{/bodyParam}} # return_type return_type = {{#returnType}}"{{{.}}}"{{/returnType}}{{^returnType}}nil{{/returnType}} diff --git a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache index de8d95d30c29..c091a9c1dfd5 100644 --- a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache @@ -2,7 +2,6 @@ require "json" require "time" -require "crest" module {{moduleName}} class ApiClient @@ -17,10 +16,10 @@ module {{moduleName}} # Initializes the ApiClient # @option config [Configuration] Configuration for initializing the object, default to Configuration.default def initialize(@config = Configuration.default) - # TODO @user_agent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/#{VERSION}/crystal{{/httpUserAgent}}" + @user_agent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/#{VERSION}/crystal{{/httpUserAgent}}" @default_headers = { - #TODO: "User-Agent" => @user_agent - } of String => String + "User-Agent" => @user_agent + } end def self.default @@ -275,6 +274,15 @@ module {{moduleName}} # conn.adapter(Faraday.default_adapter) #end + if !post_body.nil? && !post_body.empty? + # use JSON string in the payload + form = post_body + else + # use HTTP forms in the payload + # TDOD use HTTP form encoding + form = form_params + end + request = Crest::Request.new(http_method, build_request_url(path, operation), params: query_params, diff --git a/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache b/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache index 7e8afff45d6e..7e3e9b162d4b 100644 --- a/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/shard_name.mustache @@ -7,6 +7,8 @@ require "log" module {{moduleName}} Log = ::Log.for("{{moduleName}}") # => Log for {{moduleName}} source + VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} + # Customize default settings for the SDK using block. # {{moduleName}}.configure do |config| # config.username = "xxx" diff --git a/samples/client/petstore/crystal/.openapi-generator/FILES b/samples/client/petstore/crystal/.openapi-generator/FILES index 99795a83feb6..aa17a1e4b987 100644 --- a/samples/client/petstore/crystal/.openapi-generator/FILES +++ b/samples/client/petstore/crystal/.openapi-generator/FILES @@ -3,15 +3,6 @@ README.md git_push.sh shard.yml -spec/api/pet_api_spec.cr -spec/api/store_api_spec.cr -spec/api/user_api_spec.cr -spec/models/api_response_spec.cr -spec/models/category_spec.cr -spec/models/order_spec.cr -spec/models/pet_spec.cr -spec/models/tag_spec.cr -spec/models/user_spec.cr spec/spec_helper.cr src/petstore.cr src/petstore/api/pet_api.cr diff --git a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr index 1288fb61cd42..d2b521a4a595 100644 --- a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr +++ b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr @@ -81,14 +81,19 @@ describe "PetApi" do # assertion here. ref: https://crystal-lang.org/reference/guides/testing.html api_instance = Petstore::PetApi.new # create a pet to start with - pet = Petstore::Pet.new() - result = api_instance.get_pet_by_id(pet_id: 29) - result.id.should eq 29 - result.category.id.should eq 13 - result.category.name.should eq "Cainine" - result.name.should eq "Wolf" - result.photo_urls.should eq ["http://pathtoimage1"] + pet_id = Int64.new(91829) + pet = Petstore::Pet.new(id: pet_id, category: Petstore::Category.new(id: pet_id + 10, name: "crystal category"), name: "crystal", photo_urls: ["https://crystal-lang.org"], tags: [Petstore::Tag.new(id: pet_id + 100, name: "crystal tag")], status: "available") + + api_instance.add_pet(pet) + + result = api_instance.get_pet_by_id(pet_id: pet_id) + result.id.should eq pet_id + result.category.id.should eq pet_id + 10 + result.category.name.should eq "crystal category" + result.name.should eq "category" + result.photo_urls.should eq ["https://crystal-lang.org"] result.status.should eq "available" + result.tags[0].id.should eq pet_id + 100 end end diff --git a/samples/client/petstore/crystal/src/petstore.cr b/samples/client/petstore/crystal/src/petstore.cr index a3d79328d012..551e69efe96d 100644 --- a/samples/client/petstore/crystal/src/petstore.cr +++ b/samples/client/petstore/crystal/src/petstore.cr @@ -15,6 +15,8 @@ require "log" module Petstore Log = ::Log.for("Petstore") # => Log for Petstore source + VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} + # Customize default settings for the SDK using block. # Petstore.configure do |config| # config.username = "xxx" diff --git a/samples/client/petstore/crystal/src/petstore/api/pet_api.cr b/samples/client/petstore/crystal/src/petstore/api/pet_api.cr index 5ca69c24bcc6..a263fc37db1f 100644 --- a/samples/client/petstore/crystal/src/petstore/api/pet_api.cr +++ b/samples/client/petstore/crystal/src/petstore/api/pet_api.cr @@ -53,7 +53,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(pet) + post_body = pet.to_json # return_type return_type = "Pet" @@ -347,7 +347,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(pet) + post_body = pet.to_json # return_type return_type = "Pet" diff --git a/samples/client/petstore/crystal/src/petstore/api/store_api.cr b/samples/client/petstore/crystal/src/petstore/api/store_api.cr index b4a469c3ee51..adfb59de419c 100644 --- a/samples/client/petstore/crystal/src/petstore/api/store_api.cr +++ b/samples/client/petstore/crystal/src/petstore/api/store_api.cr @@ -230,7 +230,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(order) + post_body = order.to_json # return_type return_type = "Order" diff --git a/samples/client/petstore/crystal/src/petstore/api/user_api.cr b/samples/client/petstore/crystal/src/petstore/api/user_api.cr index 796f571a5e40..db3832b3c1e1 100644 --- a/samples/client/petstore/crystal/src/petstore/api/user_api.cr +++ b/samples/client/petstore/crystal/src/petstore/api/user_api.cr @@ -53,7 +53,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(user) + post_body = user.to_json # return_type return_type = nil @@ -110,7 +110,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(user) + post_body = user.to_json # return_type return_type = nil @@ -167,7 +167,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(user) + post_body = user.to_json # return_type return_type = nil @@ -465,7 +465,7 @@ module Petstore form_params = Hash(Symbol, String).new # http body (model) - post_body = @api_client.object_to_http_body(user) + post_body = user.to_json # return_type return_type = nil diff --git a/samples/client/petstore/crystal/src/petstore/api_client.cr b/samples/client/petstore/crystal/src/petstore/api_client.cr index c857d5be6722..d2bef5bfdc40 100644 --- a/samples/client/petstore/crystal/src/petstore/api_client.cr +++ b/samples/client/petstore/crystal/src/petstore/api_client.cr @@ -10,7 +10,6 @@ require "json" require "time" -require "crest" module Petstore class ApiClient @@ -24,12 +23,10 @@ module Petstore # Initializes the ApiClient # @option config [Configuration] Configuration for initializing the object, default to Configuration.default - def initialize(config = Configuration.default) - @config = config - # TODO @user_agent = "OpenAPI-Generator/#{VERSION}/crystal" + def initialize(@config = Configuration.default) + @user_agent = "OpenAPI-Generator/#{VERSION}/crystal" @default_headers = { - #"Content-Type" => "application/json" - #TODO: "User-Agent" => @user_agent + "User-Agent" => @user_agent } end @@ -285,6 +282,15 @@ module Petstore # conn.adapter(Faraday.default_adapter) #end + if !post_body.nil? && !post_body.empty? + # use JSON string in the payload + form = post_body + else + # use HTTP forms in the payload + # TDOD use HTTP form encoding + form = form_params + end + request = Crest::Request.new(http_method, build_request_url(path, operation), params: query_params, From a655e2dc4ddb3e52868a5d732761681ef1c25425 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 12:24:01 +0800 Subject: [PATCH 07/26] test crystal in circleci --- CI/circle_parallel.sh | 11 ++++- pom.xml | 1 + samples/client/petstore/crystal/pom.xml | 56 +++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 samples/client/petstore/crystal/pom.xml diff --git a/CI/circle_parallel.sh b/CI/circle_parallel.sh index 70b541743d8b..9e1306fa1a3a 100755 --- a/CI/circle_parallel.sh +++ b/CI/circle_parallel.sh @@ -41,13 +41,22 @@ elif [ "$NODE_INDEX" = "2" ]; then curl -sSL https://get.haskellstack.org/ | sh stack upgrade stack --version - # install r + # prepare crystal + curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash + curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - + echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list + # prepare r sudo sh -c 'echo "deb http://cran.rstudio.com/bin/linux/ubuntu trusty/" >> /etc/apt/sources.list' gpg --keyserver keyserver.ubuntu.com --recv-key E084DAB9 gpg -a --export E084DAB9 | sudo apt-key add - + + # installation (crystal, curl) sudo apt-get update + sudo apt install crystal + crystal version sudo apt-get -y install r-base R --version + # install curl sudo apt-get -y build-dep libcurl4-gnutls-dev sudo apt-get -y install libcurl4-gnutls-dev diff --git a/pom.xml b/pom.xml index defb35d7add8..6be22e35a051 100644 --- a/pom.xml +++ b/pom.xml @@ -1415,6 +1415,7 @@ samples/client/petstore/R + samples/client/petstore/crystal diff --git a/samples/client/petstore/crystal/pom.xml b/samples/client/petstore/crystal/pom.xml new file mode 100644 index 000000000000..f4b890f7d802 --- /dev/null +++ b/samples/client/petstore/crystal/pom.xml @@ -0,0 +1,56 @@ + + 4.0.0 + org.openapitools + CrystalPetstoreClientTests + pom + 1.0-SNAPSHOT + Crystal OpenAPI Petstore Client + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + shards-install + pre-integration-test + + exec + + + shards + + + + crystal-spec + integration-test + + exec + + + crystal + + spec + + + + + + + + From 317f2cf2a09f902c08ced214e8e5802fa79c3de8 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 13:20:34 +0800 Subject: [PATCH 08/26] test crystal client in drone ci --- CI/.drone.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CI/.drone.yml b/CI/.drone.yml index 7df20d5e7f25..83b3952478e6 100644 --- a/CI/.drone.yml +++ b/CI/.drone.yml @@ -2,6 +2,14 @@ kind: pipeline name: default steps: +# test crystal petstore +- name: crystal-test + image: crystallang/crystal + commands: + - crystal version + - cd samples/client/petstore/crystal + - shards + - crystal spec # test protobuf schema generator - name: protobuf-schema-test image: nanoservice/protobuf-go From 7ab4a1ec1acea61ef48264e9f2ef68350ddfbe61 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 14:19:12 +0800 Subject: [PATCH 09/26] Revert "test crystal client in drone ci" This reverts commit 317f2cf2a09f902c08ced214e8e5802fa79c3de8. --- CI/.drone.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CI/.drone.yml b/CI/.drone.yml index 83b3952478e6..7df20d5e7f25 100644 --- a/CI/.drone.yml +++ b/CI/.drone.yml @@ -2,14 +2,6 @@ kind: pipeline name: default steps: -# test crystal petstore -- name: crystal-test - image: crystallang/crystal - commands: - - crystal version - - cd samples/client/petstore/crystal - - shards - - crystal spec # test protobuf schema generator - name: protobuf-schema-test image: nanoservice/protobuf-go From ca60924344a5ab7be2ae2d7ba21284391691a572 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 14:29:17 +0800 Subject: [PATCH 10/26] fix install --- CI/circle_parallel.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CI/circle_parallel.sh b/CI/circle_parallel.sh index 9e1306fa1a3a..02dd45716ba4 100755 --- a/CI/circle_parallel.sh +++ b/CI/circle_parallel.sh @@ -41,15 +41,16 @@ elif [ "$NODE_INDEX" = "2" ]; then curl -sSL https://get.haskellstack.org/ | sh stack upgrade stack --version - # prepare crystal - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list # prepare r sudo sh -c 'echo "deb http://cran.rstudio.com/bin/linux/ubuntu trusty/" >> /etc/apt/sources.list' gpg --keyserver keyserver.ubuntu.com --recv-key E084DAB9 gpg -a --export E084DAB9 | sudo apt-key add - + # prepare crystal + curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash + curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - + echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list + # installation (crystal, curl) sudo apt-get update sudo apt install crystal From 295b09e21f396fe2331c576484161bb3663a72f8 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 15:26:37 +0800 Subject: [PATCH 11/26] fix installation --- CI/circle_parallel.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CI/circle_parallel.sh b/CI/circle_parallel.sh index 02dd45716ba4..2051c57255bf 100755 --- a/CI/circle_parallel.sh +++ b/CI/circle_parallel.sh @@ -45,18 +45,17 @@ elif [ "$NODE_INDEX" = "2" ]; then sudo sh -c 'echo "deb http://cran.rstudio.com/bin/linux/ubuntu trusty/" >> /etc/apt/sources.list' gpg --keyserver keyserver.ubuntu.com --recv-key E084DAB9 gpg -a --export E084DAB9 | sudo apt-key add - + sudo apt-get update + sudo apt-get -y install r-base + R --version # prepare crystal curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list - - # installation (crystal, curl) sudo apt-get update sudo apt install crystal crystal version - sudo apt-get -y install r-base - R --version # install curl sudo apt-get -y build-dep libcurl4-gnutls-dev From 2c389e1fb1b32b0b5f71e72ab214e15bed61e061 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 16:29:55 +0800 Subject: [PATCH 12/26] test crystal in travis --- .travis.yml | 5 +++++ pom.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 52ad4a03ce7f..b48d08e2afce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,6 +79,11 @@ before_install: - docker pull swaggerapi/petstore - docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - docker ps -a + # install crystal + - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash + - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - + - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list + - sudo apt-get update # -- skip bash test to shorten build time # Add bats test framework and cURL for Bash script integration tests #- sudo add-apt-repository ppa:duggan/bats --yes diff --git a/pom.xml b/pom.xml index 6be22e35a051..7ce7745c857c 100644 --- a/pom.xml +++ b/pom.xml @@ -1181,6 +1181,7 @@ + samples/client/petstore/crystal samples/server/petstore/python-aiohttp samples/server/petstore/python-aiohttp-srclayout @@ -1415,7 +1416,6 @@ samples/client/petstore/R - samples/client/petstore/crystal From 766d525b4805e8d086109b57e52c4998e1f053d0 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 16:46:42 +0800 Subject: [PATCH 13/26] cache image --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b48d08e2afce..cea8df7bee67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ before_install: # set python 3.6.3 as default - source ~/virtualenv/python3.6/bin/activate # to run petstore server locally via docker - - docker pull swaggerapi/petstore + - docker pull --cache-from swaggerapi/petstore - docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - docker ps -a # install crystal From 388d80fa11c50cd8b58a1940ee99d834ac831b52 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 16:52:20 +0800 Subject: [PATCH 14/26] add --cache-from --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c3611c57351a..df3920e5f0b1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,7 @@ jobs: - run: node --version # - run: docker pull openapitools/openapi-petstore # - run: docker run -d -e OPENAPI_BASE_PATH=/v3 -e DISABLE_API_KEY=1 -e DISABLE_OAUTH=1 -p 80:8080 openapitools/openapi-petstore - - run: docker pull swaggerapi/petstore + - run: docker pull --cache-from swaggerapi/petstore - run: docker run --name petstore.swagger -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - run: docker ps -a - run: sleep 30 From e8d15ba2d19720d5d2f428dc6c4d3958bebbf0f3 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 16:57:44 +0800 Subject: [PATCH 15/26] update doc --- CONTRIBUTING.md | 1 + README.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9644a5cb366d..30258ec9fd6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,7 @@ Code change should conform to the programming style guide of the respective lang - C++: https://google.github.io/styleguide/cppguide.html - C++ (Tizen): https://wiki.tizen.org/Native_Platform_Coding_Idiom_and_Style_Guide#C.2B.2B_Coding_Style - Clojure: https://github.com/bbatsov/clojure-style-guide +- Crystal: https://crystal-lang.org/reference/conventions/coding_style.html - Dart: https://www.dartlang.org/guides/language/effective-dart/style - Elixir: https://github.com/christopheradams/elixir_style_guide - Eiffel: https://www.eiffel.org/doc/eiffel/Coding%20Standards diff --git a/README.md b/README.md index 718761800439..51785331a98e 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se | | Languages/Frameworks | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.0, .NET Core 2.0), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client, MicroProfile Rest Client), **k6**, **Kotlin**, **Lua**, **Nim**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types, Apollo GraphQL DataStore), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, sttp, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x, 5.x), **Typescript** (AngularJS, Angular (2.x - 8.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs) | +| **API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.0, .NET Core 2.0), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Crystal**, **Dart**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client, MicroProfile Rest Client), **k6**, **Kotlin**, **Lua**, **Nim**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types, Apollo GraphQL DataStore), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, sttp, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x, 5.x), **Typescript** (AngularJS, Angular (2.x - 8.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs) | | **Server stubs** | **Ada**, **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin), **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples), [Vert.x](https://vertx.io/)), **Kotlin** (Spring Boot, Ktor, Vertx), **PHP** (Laravel, Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** (Akka, [Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), Scalatra) | | **API documentation generators** | **HTML**, **Confluence Wiki**, **Asciidoc** | | **Configuration files** | [**Apache2**](https://httpd.apache.org/) | @@ -842,6 +842,7 @@ Here is a list of template creators: * C# (.NET Standard 1.3 ): @Gronsak * C# (.NET 4.5 refactored): @jimschubert [:heart:](https://www.patreon.com/jimschubert) * Clojure: @xhh + * Crystal: @wing328 * Dart: @yissachar * Dart (refactor): @joernahrens * Dart 2: @swipesight From ec4aa80c21cbf8925c0f6346ddce2a3d0045017b Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 18:10:49 +0800 Subject: [PATCH 16/26] run petstore with mvn --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index cea8df7bee67..20edd6823801 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,6 +62,8 @@ addons: - petstore.swagger.io before_install: + - git clone https://github.com/wing328/swagger-samples + - mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml & - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.0 - export PATH="$HOME/.yarn/bin:$PATH" # install rust @@ -76,9 +78,9 @@ before_install: # set python 3.6.3 as default - source ~/virtualenv/python3.6/bin/activate # to run petstore server locally via docker - - docker pull --cache-from swaggerapi/petstore - - docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - - docker ps -a + #- docker pull swaggerapi/petstore + #- docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore + #- docker ps -a # install crystal - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - From 58c2ec7dce8a5b42591e2e2cde24dd2c734a006a Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 18:32:57 +0800 Subject: [PATCH 17/26] install crystal --- .gitignore | 3 + .travis.yml | 1 + .../client/petstore/crystal/lib/.shards.info | 27 - .../petstore/crystal/lib/ameba/.dockerignore | 4 - .../petstore/crystal/lib/ameba/.editorconfig | 7 - .../crystal/lib/ameba/.github/FUNDING.yml | 9 - .../petstore/crystal/lib/ameba/.gitignore | 11 - .../petstore/crystal/lib/ameba/.travis.yml | 28 - .../petstore/crystal/lib/ameba/Dockerfile | 13 - .../client/petstore/crystal/lib/ameba/LICENSE | 21 - .../petstore/crystal/lib/ameba/Makefile | 21 - .../petstore/crystal/lib/ameba/README.md | 249 ----- .../crystal/lib/ameba/bench/check_sources.cr | 30 - .../petstore/crystal/lib/ameba/bin/.keep | 0 .../petstore/crystal/lib/ameba/bin/ameba.cr | 7 - samples/client/petstore/crystal/lib/ameba/lib | 1 - .../petstore/crystal/lib/ameba/shard.yml | 20 - .../lib/ameba/spec/ameba/ast/branch_spec.cr | 363 ------- .../ameba/spec/ameba/ast/branchable_spec.cr | 49 - .../spec/ameba/ast/flow_expression_spec.cr | 56 - .../lib/ameba/spec/ameba/ast/scope_spec.cr | 143 --- .../lib/ameba/spec/ameba/ast/util_spec.cr | 326 ------ .../ameba/ast/variabling/argument_spec.cr | 41 - .../ameba/ast/variabling/assignment_spec.cr | 118 --- .../ameba/ast/variabling/reference_spec.cr | 10 - .../ameba/ast/variabling/variable_spec.cr | 218 ---- .../ast/visitors/counting_visitor_spec.cr | 65 -- .../visitors/flow_expression_visitor_spec.cr | 66 -- .../ameba/ast/visitors/node_visitor_spec.cr | 16 - ...dundant_control_expression_visitor_spec.cr | 26 - .../ameba/ast/visitors/scope_visitor_spec.cr | 58 -- .../crystal/lib/ameba/spec/ameba/base_spec.cr | 75 -- .../lib/ameba/spec/ameba/cli/cmd_spec.cr | 165 --- .../lib/ameba/spec/ameba/config_spec.cr | 215 ---- .../formatter/disabled_formatter_spec.cr | 42 - .../ameba/formatter/dot_formatter_spec.cr | 107 -- .../ameba/formatter/explain_formatter_spec.cr | 71 -- .../formatter/flycheck_formatter_spec.cr | 48 - .../ameba/formatter/json_formatter_spec.cr | 99 -- .../ameba/formatter/todo_formatter_spec.cr | 127 --- .../ameba/spec/ameba/formatter/util_spec.cr | 29 - .../lib/ameba/spec/ameba/glob_utils_spec.cr | 50 - .../ameba/spec/ameba/inline_comments_spec.cr | 161 --- .../lib/ameba/spec/ameba/issue_spec.cr | 50 - .../lib/ameba/spec/ameba/reportable_spec.cr | 40 - .../lib/ameba/spec/ameba/rule/base_spec.cr | 32 - .../ameba/rule/layout/line_length_spec.cr | 43 - .../rule/layout/trailing_blank_lines_spec.cr | 58 -- .../rule/layout/trailing_whitespace_spec.cr | 28 - .../ameba/rule/lint/bad_directive_spec.cr | 66 -- .../rule/lint/comparison_to_boolean_spec.cr | 114 -- .../rule/lint/debugger_statement_spec.cr | 45 - .../spec/ameba/rule/lint/empty_ensure_spec.cr | 69 -- .../ameba/rule/lint/empty_expression_spec.cr | 127 --- .../spec/ameba/rule/lint/empty_loop_spec.cr | 88 -- .../rule/lint/hash_duplicated_key_spec.cr | 51 - .../rule/lint/literal_in_condition_spec.cr | 179 ---- .../lint/literal_in_interpolation_spec.cr | 51 - .../ameba/rule/lint/percent_arrays_spec.cr | 86 -- .../spec/ameba/rule/lint/rand_zero_spec.cr | 37 - .../lint/redundant_string_cercion_spec.cr | 97 -- .../rule/lint/redundant_with_index_spec.cr | 163 --- .../rule/lint/redundant_with_object_spec.cr | 83 -- .../ameba/rule/lint/shadowed_argument_spec.cr | 166 --- .../rule/lint/shadowed_exception_spec.cr | 176 ---- .../lint/shadowing_local_outer_var_spec.cr | 241 ----- .../rule/lint/shared_var_in_fiber_spec.cr | 236 ----- .../ameba/spec/ameba/rule/lint/syntax_spec.cr | 41 - .../lint/unneded_disable_directive_spec.cr | 102 -- .../ameba/rule/lint/unreachable_code_spec.cr | 701 ------------- .../ameba/rule/lint/unused_argument_spec.cr | 306 ------ .../ameba/rule/lint/useless_assign_spec.cr | 977 ------------------ .../lint/useless_condition_in_when_spec.cr | 46 - .../metrics/cyclomatic_complexity_spec.cr | 47 - .../rule/performance/any_after_filter_spec.cr | 73 -- .../first_last_after_filter_spec.cr | 141 --- .../performance/size_after_filter_spec.cr | 74 -- .../ameba/rule/style/constant_names_spec.cr | 54 - .../spec/ameba/rule/style/is_a_nil_spec.cr | 45 - .../ameba/rule/style/large_numbers_spec.cr | 134 --- .../ameba/rule/style/method_names_spec.cr | 56 - .../negated_conditions_in_unless_spec.cr | 69 -- .../ameba/rule/style/predicate_name_spec.cr | 60 -- .../ameba/rule/style/redundant_begin_spec.cr | 227 ---- .../ameba/rule/style/redundant_next_spec.cr | 219 ---- .../ameba/rule/style/redundant_return_spec.cr | 266 ----- .../spec/ameba/rule/style/type_names_spec.cr | 61 -- .../spec/ameba/rule/style/unless_else_spec.cr | 45 - .../ameba/rule/style/variable_names_spec.cr | 62 -- .../spec/ameba/rule/style/while_true_spec.cr | 43 - .../lib/ameba/spec/ameba/runner_spec.cr | 183 ---- .../lib/ameba/spec/ameba/severity_spec.cr | 108 -- .../lib/ameba/spec/ameba/source_spec.cr | 38 - .../lib/ameba/spec/ameba/tokenizer_spec.cr | 44 - .../crystal/lib/ameba/spec/ameba_spec.cr | 4 - .../crystal/lib/ameba/spec/spec_helper.cr | 200 ---- .../petstore/crystal/lib/ameba/src/ameba.cr | 42 - .../crystal/lib/ameba/src/ameba/ast/branch.cr | 194 ---- .../lib/ameba/src/ameba/ast/branchable.cr | 44 - .../ameba/src/ameba/ast/flow_expression.cr | 69 -- .../crystal/lib/ameba/src/ameba/ast/scope.cr | 180 ---- .../crystal/lib/ameba/src/ameba/ast/util.cr | 157 --- .../src/ameba/ast/variabling/argument.cr | 49 - .../src/ameba/ast/variabling/assignment.cr | 106 -- .../src/ameba/ast/variabling/ivariable.cr | 13 - .../src/ameba/ast/variabling/reference.cr | 10 - .../src/ameba/ast/variabling/variable.cr | 198 ---- .../src/ameba/ast/visitors/base_visitor.cr | 29 - .../ameba/ast/visitors/counting_visitor.cr | 39 - .../ast/visitors/flow_expression_visitor.cr | 67 -- .../src/ameba/ast/visitors/node_visitor.cr | 61 -- .../redundant_control_expression_visitor.cr | 62 -- .../src/ameba/ast/visitors/scope_visitor.cr | 185 ---- .../crystal/lib/ameba/src/ameba/cli/cmd.cr | 152 --- .../crystal/lib/ameba/src/ameba/config.cr | 280 ----- .../src/ameba/formatter/base_formatter.cr | 32 - .../src/ameba/formatter/disabled_formatter.cr | 17 - .../src/ameba/formatter/dot_formatter.cr | 96 -- .../src/ameba/formatter/explain_formatter.cr | 83 -- .../src/ameba/formatter/flycheck_formatter.cr | 18 - .../src/ameba/formatter/json_formatter.cr | 152 --- .../src/ameba/formatter/todo_formatter.cr | 68 -- .../lib/ameba/src/ameba/formatter/util.cr | 23 - .../crystal/lib/ameba/src/ameba/glob_utils.cr | 37 - .../lib/ameba/src/ameba/inline_comments.cr | 104 -- .../crystal/lib/ameba/src/ameba/issue.cr | 26 - .../crystal/lib/ameba/src/ameba/reportable.cr | 41 - .../crystal/lib/ameba/src/ameba/rule/base.cr | 197 ---- .../src/ameba/rule/layout/line_length.cr | 29 - .../ameba/rule/layout/trailing_blank_lines.cr | 33 - .../ameba/rule/layout/trailing_whitespace.cr | 25 - .../src/ameba/rule/lint/bad_directive.cr | 54 - .../ameba/rule/lint/comparison_to_boolean.cr | 41 - .../src/ameba/rule/lint/debugger_statement.cr | 29 - .../ameba/src/ameba/rule/lint/empty_ensure.cr | 56 - .../src/ameba/rule/lint/empty_expression.cr | 56 - .../ameba/src/ameba/rule/lint/empty_loop.cr | 68 -- .../ameba/rule/lint/hash_duplicated_key.cr | 43 - .../ameba/rule/lint/literal_in_condition.cr | 50 - .../rule/lint/literal_in_interpolation.cr | 34 - .../src/ameba/rule/lint/percent_array.cr | 74 -- .../ameba/src/ameba/rule/lint/rand_zero.cr | 44 - .../rule/lint/redundant_string_coercion.cr | 47 - .../ameba/rule/lint/redundant_with_index.cr | 58 -- .../ameba/rule/lint/redundant_with_object.cr | 51 - .../src/ameba/rule/lint/shadowed_argument.cr | 58 -- .../src/ameba/rule/lint/shadowed_exception.cr | 77 -- .../rule/lint/shadowing_local_outer_var.cr | 69 -- .../ameba/rule/lint/shared_var_in_fiber.cr | 86 -- .../lib/ameba/src/ameba/rule/lint/syntax.cr | 34 - .../rule/lint/unneeded_disable_directive.cr | 67 -- .../src/ameba/rule/lint/unreachable_code.cr | 64 -- .../src/ameba/rule/lint/unused_argument.cr | 65 -- .../src/ameba/rule/lint/useless_assign.cr | 51 - .../rule/lint/useless_condition_in_when.cr | 83 -- .../rule/metrics/cyclomatic_complexity.cr | 38 - .../rule/performance/any_after_filter.cr | 46 - .../performance/first_last_after_filter.cr | 57 - .../rule/performance/size_after_filter.cr | 61 -- .../src/ameba/rule/style/constant_names.cr | 43 - .../ameba/src/ameba/rule/style/is_a_nil.cr | 38 - .../src/ameba/rule/style/large_numbers.cr | 109 -- .../src/ameba/rule/style/method_names.cr | 63 -- .../style/negated_conditions_in_unless.cr | 55 - .../src/ameba/rule/style/predicate_name.cr | 49 - .../src/ameba/rule/style/redundant_begin.cr | 131 --- .../src/ameba/rule/style/redundant_next.cr | 119 --- .../src/ameba/rule/style/redundant_return.cr | 116 --- .../ameba/src/ameba/rule/style/type_names.cr | 90 -- .../ameba/src/ameba/rule/style/unless_else.cr | 58 -- .../src/ameba/rule/style/variable_names.cr | 51 - .../ameba/src/ameba/rule/style/while_true.cr | 41 - .../crystal/lib/ameba/src/ameba/runner.cr | 145 --- .../crystal/lib/ameba/src/ameba/severity.cr | 49 - .../crystal/lib/ameba/src/ameba/source.cr | 69 -- .../crystal/lib/ameba/src/ameba/tokenizer.cr | 111 -- .../petstore/crystal/lib/ameba/src/cli.cr | 3 - .../lib/crest/.github/workflows/crystal.yml | 49 - .../petstore/crystal/lib/crest/.gitignore | 13 - .../petstore/crystal/lib/crest/.travis.yml | 24 - .../petstore/crystal/lib/crest/CHANGELOG.md | 237 ----- .../client/petstore/crystal/lib/crest/LICENSE | 21 - .../petstore/crystal/lib/crest/README.md | 614 ----------- samples/client/petstore/crystal/lib/crest/lib | 1 - .../petstore/crystal/lib/crest/logo/icon.png | Bin 2985 -> 0 bytes .../petstore/crystal/lib/crest/logo/icon.svg | 1 - .../lib/crest/logo/logotype_horizontal.png | Bin 7246 -> 0 bytes .../lib/crest/logo/logotype_horizontal.svg | 1 - .../crystal/lib/crest/playground/requests.md | 38 - .../petstore/crystal/lib/crest/shard.yml | 27 - .../crest/spec/integration/basic_auth_spec.cr | 75 -- .../crest/spec/integration/cookies_spec.cr | 41 - .../lib/crest/spec/integration/crest_spec.cr | 73 -- .../crest/spec/integration/exception_spec.cr | 82 -- .../crest/spec/integration/headers_spec.cr | 20 - .../lib/crest/spec/integration/logger_spec.cr | 18 - .../lib/crest/spec/integration/proxy_spec.cr | 61 -- .../spec/integration/redirection_spec.cr | 77 -- .../crest/spec/integration/request_spec.cr | 175 ---- .../crest/spec/integration/resource_spec.cr | 210 ---- .../crest/spec/integration/response_spec.cr | 57 - .../crest/spec/integration/streaming_spec.cr | 120 --- .../crystal/lib/crest/spec/spec_helper.cr | 40 - .../spec/support/ext/kemal/ext/context.cr | 10 - .../crystal/lib/crest/spec/support/fff.png | Bin 106 -> 0 bytes .../crest/spec/support/kemal_basic_auth.cr | 35 - .../crystal/lib/crest/spec/support/server.cr | 229 ---- .../lib/crest/spec/support/server_cli.cr | 3 - .../crystal/lib/crest/spec/unit/crest_spec.cr | 7 - .../lib/crest/spec/unit/curlify_spec.cr | 95 -- .../lib/crest/spec/unit/data_form_spec.cr | 31 - .../lib/crest/spec/unit/exceptions_spec.cr | 11 - .../crest/spec/unit/params_encoder_spec.cr | 138 --- .../lib/crest/spec/unit/request_spec.cr | 141 --- .../lib/crest/spec/unit/resource_spec.cr | 48 - .../crest/spec/unit/urlencoded_form_spec.cr | 29 - .../petstore/crystal/lib/crest/src/crest.cr | 75 -- .../crystal/lib/crest/src/crest/curlify.cr | 93 -- .../crystal/lib/crest/src/crest/exceptions.cr | 125 --- .../crystal/lib/crest/src/crest/form.cr | 17 - .../lib/crest/src/crest/forms/data_form.cr | 43 - .../crest/src/crest/forms/urlencoded_form.cr | 21 - .../crystal/lib/crest/src/crest/logger.cr | 53 - .../crest/src/crest/loggers/common_logger.cr | 77 -- .../lib/crest/src/crest/params_encoder.cr | 130 --- .../crystal/lib/crest/src/crest/redirector.cr | 89 -- .../crystal/lib/crest/src/crest/request.cr | 327 ------ .../crystal/lib/crest/src/crest/resource.cr | 176 ---- .../crystal/lib/crest/src/crest/response.cr | 118 --- .../crystal/lib/exception_page/.editorconfig | 9 - .../crystal/lib/exception_page/.gitignore | 9 - .../crystal/lib/exception_page/.travis.yml | 28 - .../crystal/lib/exception_page/LICENSE | 21 - .../crystal/lib/exception_page/README.md | 111 -- .../petstore/crystal/lib/exception_page/lib | 1 - .../crystal/lib/exception_page/shard.yml | 18 - .../spec/exception_page_spec.cr | 34 - .../lib/exception_page/spec/frame_spec.cr | 27 - .../lib/exception_page/spec/spec_helper.cr | 26 - .../spec/support/app_exception_page.cr | 19 - .../spec/support/test_server.cr | 29 - .../lib/exception_page/src/exception_page.cr | 54 - .../src/exception_page/exception_page.ecr | 857 --------------- .../src/exception_page/frame.cr | 77 -- .../src/exception_page/frame_generator.cr | 13 - .../src/exception_page/styles.cr | 18 - .../src/exception_page/version.cr | 3 - .../lib/http-client-digest_auth/.editorconfig | 9 - .../lib/http-client-digest_auth/.gitignore | 5 - .../lib/http-client-digest_auth/.travis.yml | 5 - .../lib/http-client-digest_auth/LICENSE | 21 - .../lib/http-client-digest_auth/README.md | 69 -- .../crystal/lib/http-client-digest_auth/lib | 1 - .../samples/http_client_digest_auth.cr | 32 - .../lib/http-client-digest_auth/shard.yml | 13 - .../spec/http/client/digest_auth_spec.cr | 116 --- .../spec/spec_helper.cr | 2 - .../src/http-client-digest_auth.cr | 5 - .../src/http/client/digest_auth.cr | 168 --- .../http_proxy/.github/workflows/crystal.yml | 49 - .../crystal/lib/http_proxy/.gitignore | 10 - .../crystal/lib/http_proxy/CHANGELOG.md | 66 -- .../petstore/crystal/lib/http_proxy/LICENSE | 22 - .../petstore/crystal/lib/http_proxy/README.md | 135 --- .../petstore/crystal/lib/http_proxy/lib | 1 - .../crystal/lib/http_proxy/samples/client.cr | 40 - .../crystal/lib/http_proxy/samples/server.cr | 23 - .../samples/server_with_authentication.cr | 38 - .../http_proxy/samples/with_proxy_server.cr | 60 -- .../petstore/crystal/lib/http_proxy/shard.yml | 14 - .../lib/http_proxy/spec/client_spec.cr | 53 - .../lib/http_proxy/spec/server_spec.cr | 17 - .../lib/http_proxy/spec/spec_helper.cr | 27 - .../lib/http_proxy/src/http/proxy/client.cr | 176 ---- .../lib/http_proxy/src/http/proxy/server.cr | 53 - .../src/http/proxy/server/basic_auth.cr | 27 - .../src/http/proxy/server/context.cr | 59 -- .../src/http/proxy/server/handler.cr | 17 - .../crystal/lib/http_proxy/src/http_proxy.cr | 12 - .../petstore/crystal/lib/kemal/.ameba.yml | 13 - .../crystal/lib/kemal/.github/FUNDING.yml | 8 - .../lib/kemal/.github/ISSUE_TEMPLATE.md | 23 - .../kemal/.github/PULL_REQUEST_TEMPLATE.md | 15 - .../petstore/crystal/lib/kemal/.gitignore | 8 - .../petstore/crystal/lib/kemal/.travis.yml | 14 - .../petstore/crystal/lib/kemal/CHANGELOG.md | 385 ------- .../client/petstore/crystal/lib/kemal/LICENSE | 19 - .../petstore/crystal/lib/kemal/README.md | 67 -- samples/client/petstore/crystal/lib/kemal/lib | 1 - .../crystal/lib/kemal/samples/hello_world.cr | 8 - .../crystal/lib/kemal/samples/json_api.cr | 11 - .../lib/kemal/samples/websocket_server.cr | 11 - .../petstore/crystal/lib/kemal/shard.yml | 25 - .../crystal/lib/kemal/spec/all_spec.cr | 1 - .../crystal/lib/kemal/spec/asset/hello.ecr | 1 - .../spec/asset/hello_with_content_for.ecr | 5 - .../crystal/lib/kemal/spec/asset/layout.ecr | 1 - .../kemal/spec/asset/layout_with_yield.ecr | 6 - .../spec/asset/layout_with_yield_and_vars.ecr | 8 - .../crystal/lib/kemal/spec/config_spec.cr | 61 -- .../crystal/lib/kemal/spec/context_spec.cr | 107 -- .../lib/kemal/spec/exception_handler_spec.cr | 115 --- .../crystal/lib/kemal/spec/handler_spec.cr | 161 --- .../crystal/lib/kemal/spec/helpers_spec.cr | 173 ---- .../lib/kemal/spec/init_handler_spec.cr | 32 - .../lib/kemal/spec/log_handler_spec.cr | 21 - .../lib/kemal/spec/middleware/filters_spec.cr | 190 ---- .../lib/kemal/spec/param_parser_spec.cr | 204 ---- .../lib/kemal/spec/route_handler_spec.cr | 146 --- .../crystal/lib/kemal/spec/route_spec.cr | 25 - .../crystal/lib/kemal/spec/run_spec.cr | 46 - .../crystal/lib/kemal/spec/spec_helper.cr | 91 -- .../lib/kemal/spec/static/dir/bigger.txt | 9 - .../lib/kemal/spec/static/dir/index.html | 12 - .../lib/kemal/spec/static/dir/test.txt | 2 - .../kemal/spec/static_file_handler_spec.cr | 153 --- .../crystal/lib/kemal/spec/view_spec.cr | 62 -- .../lib/kemal/spec/websocket_handler_spec.cr | 68 -- .../petstore/crystal/lib/kemal/src/kemal.cr | 98 -- .../lib/kemal/src/kemal/base_log_handler.cr | 9 - .../crystal/lib/kemal/src/kemal/cli.cr | 55 - .../crystal/lib/kemal/src/kemal/config.cr | 168 --- .../crystal/lib/kemal/src/kemal/dsl.cr | 37 - .../lib/kemal/src/kemal/exception_handler.cr | 30 - .../lib/kemal/src/kemal/ext/context.cr | 62 -- .../lib/kemal/src/kemal/ext/response.cr | 14 - .../lib/kemal/src/kemal/file_upload.cr | 24 - .../lib/kemal/src/kemal/filter_handler.cr | 90 -- .../crystal/lib/kemal/src/kemal/handler.cr | 80 -- .../kemal/src/kemal/helpers/exception_page.cr | 38 - .../lib/kemal/src/kemal/helpers/exceptions.cr | 20 - .../lib/kemal/src/kemal/helpers/helpers.cr | 270 ----- .../lib/kemal/src/kemal/helpers/macros.cr | 98 -- .../lib/kemal/src/kemal/helpers/templates.cr | 35 - .../lib/kemal/src/kemal/helpers/utils.cr | 13 - .../lib/kemal/src/kemal/init_handler.cr | 15 - .../lib/kemal/src/kemal/log_handler.cr | 28 - .../lib/kemal/src/kemal/null_log_handler.cr | 11 - .../lib/kemal/src/kemal/param_parser.cr | 113 -- .../crystal/lib/kemal/src/kemal/route.cr | 17 - .../lib/kemal/src/kemal/route_handler.cr | 68 -- .../crystal/lib/kemal/src/kemal/ssl.cr | 17 - .../kemal/src/kemal/static_file_handler.cr | 68 -- .../crystal/lib/kemal/src/kemal/websocket.cr | 14 - .../lib/kemal/src/kemal/websocket_handler.cr | 41 - .../petstore/crystal/lib/kilt/.gitignore | 10 - .../petstore/crystal/lib/kilt/.travis.yml | 1 - .../client/petstore/crystal/lib/kilt/LICENSE | 21 - .../petstore/crystal/lib/kilt/README.md | 105 -- samples/client/petstore/crystal/lib/kilt/lib | 1 - .../petstore/crystal/lib/kilt/shard.yml | 15 - .../crystal/lib/kilt/spec/fixtures/test.ecr | 1 - .../lib/kilt/spec/fixtures/test.mustache | 1 - .../crystal/lib/kilt/spec/fixtures/test.raw | 1 - .../crystal/lib/kilt/spec/fixtures/test.slang | 1 - .../crystal/lib/kilt/spec/fixtures/test.temel | 1 - .../lib/kilt/spec/kilt/crustache_spec.cr | 26 - .../crystal/lib/kilt/spec/kilt/slang_spec.cr | 18 - .../crystal/lib/kilt/spec/kilt/temel_spec.cr | 18 - .../crystal/lib/kilt/spec/kilt_spec.cr | 28 - .../crystal/lib/kilt/spec/spec_helper.cr | 3 - .../lib/kilt/spec/support/raw_engine.cr | 5 - .../petstore/crystal/lib/kilt/src/crikey.cr | 4 - .../crystal/lib/kilt/src/crustache.cr | 4 - .../petstore/crystal/lib/kilt/src/ecr.cr | 3 - .../petstore/crystal/lib/kilt/src/kilt.cr | 35 - .../crystal/lib/kilt/src/kilt/exception.cr | 5 - .../kilt/src/kilt/helpers/temel_embedder.cr | 3 - .../crystal/lib/kilt/src/kilt/version.cr | 3 - .../petstore/crystal/lib/kilt/src/slang.cr | 4 - .../petstore/crystal/lib/kilt/src/temel.cr | 9 - .../petstore/crystal/lib/radix/.gitignore | 8 - .../petstore/crystal/lib/radix/.travis.yml | 11 - .../petstore/crystal/lib/radix/CHANGELOG.md | 102 -- .../client/petstore/crystal/lib/radix/LICENSE | 21 - .../petstore/crystal/lib/radix/Makefile | 18 - .../petstore/crystal/lib/radix/README.md | 129 --- samples/client/petstore/crystal/lib/radix/lib | 1 - .../petstore/crystal/lib/radix/shard.yml | 7 - .../crystal/lib/radix/spec/radix/node_spec.cr | 150 --- .../lib/radix/spec/radix/result_spec.cr | 76 -- .../crystal/lib/radix/spec/radix/tree_spec.cr | 626 ----------- .../lib/radix/spec/radix/version_spec.cr | 12 - .../crystal/lib/radix/spec/spec_helper.cr | 2 - .../petstore/crystal/lib/radix/src/radix.cr | 2 - .../crystal/lib/radix/src/radix/node.cr | 207 ---- .../crystal/lib/radix/src/radix/result.cr | 88 -- .../crystal/lib/radix/src/radix/tree.cr | 472 --------- .../crystal/lib/radix/src/radix/version.cr | 3 - 389 files changed, 4 insertions(+), 28760 deletions(-) delete mode 100644 samples/client/petstore/crystal/lib/.shards.info delete mode 100644 samples/client/petstore/crystal/lib/ameba/.dockerignore delete mode 100644 samples/client/petstore/crystal/lib/ameba/.editorconfig delete mode 100644 samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml delete mode 100644 samples/client/petstore/crystal/lib/ameba/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/ameba/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/ameba/Dockerfile delete mode 100644 samples/client/petstore/crystal/lib/ameba/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/ameba/Makefile delete mode 100644 samples/client/petstore/crystal/lib/ameba/README.md delete mode 100644 samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/bin/.keep delete mode 100644 samples/client/petstore/crystal/lib/ameba/bin/ameba.cr delete mode 120000 samples/client/petstore/crystal/lib/ameba/lib delete mode 100644 samples/client/petstore/crystal/lib/ameba/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr delete mode 100644 samples/client/petstore/crystal/lib/ameba/src/cli.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml delete mode 100644 samples/client/petstore/crystal/lib/crest/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/crest/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/crest/CHANGELOG.md delete mode 100644 samples/client/petstore/crystal/lib/crest/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/crest/README.md delete mode 120000 samples/client/petstore/crystal/lib/crest/lib delete mode 100644 samples/client/petstore/crystal/lib/crest/logo/icon.png delete mode 100644 samples/client/petstore/crystal/lib/crest/logo/icon.svg delete mode 100644 samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.png delete mode 100644 samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg delete mode 100644 samples/client/petstore/crystal/lib/crest/playground/requests.md delete mode 100644 samples/client/petstore/crystal/lib/crest/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/fff.png delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/server.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/form.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/logger.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/request.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/resource.cr delete mode 100644 samples/client/petstore/crystal/lib/crest/src/crest/response.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/.editorconfig delete mode 100644 samples/client/petstore/crystal/lib/exception_page/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/exception_page/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/exception_page/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/exception_page/README.md delete mode 120000 samples/client/petstore/crystal/lib/exception_page/lib delete mode 100644 samples/client/petstore/crystal/lib/exception_page/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr delete mode 100644 samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/README.md delete mode 120000 samples/client/petstore/crystal/lib/http-client-digest_auth/lib delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr delete mode 100644 samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/README.md delete mode 120000 samples/client/petstore/crystal/lib/http_proxy/lib delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/client.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/server.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr delete mode 100644 samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/.ameba.yml delete mode 100644 samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml delete mode 100644 samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md delete mode 100644 samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md delete mode 100644 samples/client/petstore/crystal/lib/kemal/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/kemal/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/kemal/CHANGELOG.md delete mode 100644 samples/client/petstore/crystal/lib/kemal/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/kemal/README.md delete mode 120000 samples/client/petstore/crystal/lib/kemal/lib delete mode 100644 samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/samples/json_api.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr delete mode 100644 samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/kilt/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/kilt/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/kilt/README.md delete mode 120000 samples/client/petstore/crystal/lib/kilt/lib delete mode 100644 samples/client/petstore/crystal/lib/kilt/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/crikey.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/crustache.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/ecr.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/slang.cr delete mode 100644 samples/client/petstore/crystal/lib/kilt/src/temel.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/.gitignore delete mode 100644 samples/client/petstore/crystal/lib/radix/.travis.yml delete mode 100644 samples/client/petstore/crystal/lib/radix/CHANGELOG.md delete mode 100644 samples/client/petstore/crystal/lib/radix/LICENSE delete mode 100644 samples/client/petstore/crystal/lib/radix/Makefile delete mode 100644 samples/client/petstore/crystal/lib/radix/README.md delete mode 120000 samples/client/petstore/crystal/lib/radix/lib delete mode 100644 samples/client/petstore/crystal/lib/radix/shard.yml delete mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/src/radix.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/node.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/result.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/tree.cr delete mode 100644 samples/client/petstore/crystal/lib/radix/src/radix/version.cr diff --git a/.gitignore b/.gitignore index 26c01a81696d..ebe0dc1ff9c8 100644 --- a/.gitignore +++ b/.gitignore @@ -258,3 +258,6 @@ samples/client/petstore/c/*.so # Ruby samples/openapi3/client/petstore/ruby/Gemfile.lock samples/openapi3/client/petstore/ruby-faraday/Gemfile.lock + +# Crystal +samples/client/petstore/crystal/lib diff --git a/.travis.yml b/.travis.yml index 20edd6823801..180875ce6939 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,6 +86,7 @@ before_install: - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list - sudo apt-get update + - sudo apt install crystal # -- skip bash test to shorten build time # Add bats test framework and cURL for Bash script integration tests #- sudo add-apt-repository ppa:duggan/bats --yes diff --git a/samples/client/petstore/crystal/lib/.shards.info b/samples/client/petstore/crystal/lib/.shards.info deleted file mode 100644 index d6e1324434fa..000000000000 --- a/samples/client/petstore/crystal/lib/.shards.info +++ /dev/null @@ -1,27 +0,0 @@ ---- -version: 1.0 -shards: - http-client-digest_auth: - git: https://github.com/mamantoha/http-client-digest_auth.git - version: 0.4.0 - http_proxy: - git: https://github.com/mamantoha/http_proxy.git - version: 0.7.2 - crest: - git: https://github.com/mamantoha/crest.git - version: 0.26.1 - radix: - git: https://github.com/luislavena/radix.git - version: 0.3.9 - kilt: - git: https://github.com/jeromegn/kilt.git - version: 0.4.0 - exception_page: - git: https://github.com/crystal-loot/exception_page.git - version: 0.1.4 - kemal: - git: https://github.com/kemalcr/kemal.git - version: 0.27.0 - ameba: - git: https://github.com/crystal-ameba/ameba.git - version: 0.13.3 diff --git a/samples/client/petstore/crystal/lib/ameba/.dockerignore b/samples/client/petstore/crystal/lib/ameba/.dockerignore deleted file mode 100644 index 69e9d6b588e3..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -.* -!Makefile -!shard.yml -!src \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/ameba/.editorconfig b/samples/client/petstore/crystal/lib/ameba/.editorconfig deleted file mode 100644 index 8f0c87a10c53..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -[*.cr] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true diff --git a/samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml b/samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml deleted file mode 100644 index a1a56190039e..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/.github/FUNDING.yml +++ /dev/null @@ -1,9 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: veelenga -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -custom: https://liberapay.com/crystal-ameba diff --git a/samples/client/petstore/crystal/lib/ameba/.gitignore b/samples/client/petstore/crystal/lib/ameba/.gitignore deleted file mode 100644 index dddc937e8824..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -/doc/ -/lib/ -/bin/ameba -/bin/ameba.dwarf -/.shards/ - -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock - -*.html \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/ameba/.travis.yml b/samples/client/petstore/crystal/lib/ameba/.travis.yml deleted file mode 100644 index 5de00985bee5..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: crystal - -crystal: - - latest - - nightly - -jobs: - allow_failures: - - crystal: nightly - -install: - - shards install - -script: - - make test - - crystal tool format --check - - sed -i -e 's:<.*>::g' README.md - - crystal docs - -deploy: - provider: pages - github_token: $GITHUB_TOKEN - project_name: ameba - skip_cleanup: true - on: - branch: master - local_dir: docs - verbose: true diff --git a/samples/client/petstore/crystal/lib/ameba/Dockerfile b/samples/client/petstore/crystal/lib/ameba/Dockerfile deleted file mode 100644 index cb4480788cef..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM alpine:3.12 as builder -RUN apk add --update crystal shards openssl-dev yaml-dev musl-dev make -RUN mkdir /ameba -WORKDIR /ameba -COPY . /ameba/ -RUN make clean && make - -FROM alpine:3.12 -RUN apk add --update openssl yaml pcre gc libevent libgcc -RUN mkdir /src -WORKDIR /src -COPY --from=builder /ameba/bin/ameba /usr/bin/ -ENTRYPOINT [ "/usr/bin/ameba" ] diff --git a/samples/client/petstore/crystal/lib/ameba/LICENSE b/samples/client/petstore/crystal/lib/ameba/LICENSE deleted file mode 100644 index 3970ca6f4df2..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018-2020 Vitalii Elenhaupt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/ameba/Makefile b/samples/client/petstore/crystal/lib/ameba/Makefile deleted file mode 100644 index e0c861151071..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -CRYSTAL_BIN ?= $(shell which crystal) -SHARDS_BIN ?= $(shell which shards) -PREFIX ?= /usr/local -SHARD_BIN ?= ../../bin - -build: bin/ameba -bin/ameba: - $(SHARDS_BIN) build $(CRFLAGS) -clean: - rm -f ./bin/ameba ./bin/ameba.dwarf -install: build - mkdir -p $(PREFIX)/bin - cp ./bin/ameba $(PREFIX)/bin -bin: build - mkdir -p $(SHARD_BIN) - cp ./bin/ameba $(SHARD_BIN) -run_file: - cp -n ./bin/ameba.cr $(SHARD_BIN) || true -test: build - $(CRYSTAL_BIN) spec - ./bin/ameba --all diff --git a/samples/client/petstore/crystal/lib/ameba/README.md b/samples/client/petstore/crystal/lib/ameba/README.md deleted file mode 100644 index 422776df548d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/README.md +++ /dev/null @@ -1,249 +0,0 @@ -

    - -

    Ameba

    -

    Code style linter for Crystal

    -

    - - (a single-celled animal that catches food and moves about by extending fingerlike projections of protoplasm) - -

    -

    - - - - -

    -

    - -- [About](#about) -- [Usage](#usage) - * [Run in parallel](#run-in-parallel) -- [Installation](#installation) - * [As a project dependency:](#as-a-project-dependency) - * [OS X](#os-x) - * [Docker](#docker) - * [From sources](#from-sources) -- [Configuration](#configuration) - * [Sources](#sources) - * [Rules](#rules) - * [Explain issues](#explain-issues) - * [Inline disabling](#inline-disabling) -- [Editors & integrations](#editors--integrations) -- [Credits & inspirations](#credits--inspirations) -- [Contributors](#contributors) - -## About - -Ameba is a static code analysis tool for the Crystal language. -It enforces a consistent [Crystal code style](https://crystal-lang.org/docs/conventions/coding_style.html), -also catches code smells and wrong code constructions. - -See also [Roadmap](https://github.com/crystal-ameba/ameba/wiki). - -## Usage - -Run `ameba` binary within your project directory to catch code issues: - -```sh -$ ameba -Inspecting 107 files. - -...............F.....................F.................................................................... - -src/ameba/formatter/flycheck_formatter.cr:4:33 -[W] Lint/UnusedArgument: Unused argument `location` -> source.issues.each do |e, location| - ^ - -src/ameba/formatter/base_formatter.cr:12:7 -[W] Lint/UselessAssign: Useless assignment to variable `s` -> return s += issues.size - ^ - -Finished in 542.64 milliseconds - -129 inspected, 2 failures. - -``` - -### Run in parallel - -Starting from 0.31.0 Crystal [supports parallelism](https://crystal-lang.org/2019/09/06/parallelism-in-crystal.html). -It allows to run linting in parallel too. -In order to take advantage of this feature you need to build ameba with preview_mt support: - -```sh -$ crystal build src/cli.cr -Dpreview_mt -o bin/ameba -$ make install -``` - -Some quick benchmark results measured while running Ameba on Crystal repo: - -```sh -$ CRYSTAL_WORKERS=1 ameba #=> 29.11 seconds -$ CRYSTAL_WORKERS=2 ameba #=> 19.49 seconds -$ CRYSTAL_WORKERS=4 ameba #=> 13.48 seconds -$ CRYSTAL_WORKERS=8 ameba #=> 10.14 seconds -``` - -## Installation - -### As a project dependency: - -Add this to your application's `shard.yml`: - -```yaml -development_dependencies: - ameba: - github: crystal-ameba/ameba - version: ~> 0.13.0 -``` - -Build `bin/ameba` binary within your project directory while running `shards install`. - -You may also want to use it on [Travis](travis-ci.org): - -```yaml -# .travis.yml -language: crystal -install: - - shards install -script: - - crystal spec - - crystal bin/ameba.cr -``` - -Using this config Ameba will inspect files just after the specs run. Travis will also fail -the build if some problems detected. - -### OS X - -```sh -$ brew tap veelenga/tap -$ brew install ameba -``` - -### Docker - -Build the image: - -```sh -$ docker build -t crystal-ameba/ameba . -``` - -To use the resulting image on a local source folder, mount the current (or target) directory into `/src`: - -```sh -$ docker run -v $(pwd):/src crystal-ameba/ameba -``` - -Also available on DockerHub: https://hub.docker.com/r/veelenga/ameba - -### From sources - -```sh -$ git clone https://github.com/crystal-ameba/ameba && cd ameba -$ make install -``` - -## Configuration - -Default configuration file is `.ameba.yml`. -It allows to configure rule properties, disable specific rules and exclude sources from the rules. - -Generate new file by running `ameba --gen-config`. - -### Sources - -**List of sources to run Ameba on can be configured globally via:** - -- `Globs` section - an array of wildcards (or paths) to include to the - inspection. Defaults to `%w(**/*.cr !lib)`, meaning it includes all project - files with `*.cr` extension except those which exist in `lib` folder. -- `Excluded` section - an array of wildcards (or paths) to exclude from the - source list defined by `Globs`. Defaults to an empty array. - -In this example we define default globs and exclude `src/compiler` folder: - -``` yaml -Globs: - - **/*.cr - - !lib - -Excluded: - - src/compiler -``` - -**Specific sources can be excluded at rule level**: - -``` yaml -Style/RedundantBegin: - Excluded: - - src/server/processor.cr - - src/server/api.cr -``` - -### Rules - -One or more rules, or a one or more group of rules can be included or excluded -via command line arguments: - -```sh -$ ameba --only Lint/Syntax # runs only Lint/Syntax rule -$ ameba --only Style,Lint # runs only rules from Style and Lint groups -$ ameba --except Lint/Syntax # runs all rules except Lint/Syntax -$ ameba --except Style,Lint # runs all rules except rules in Style and Lint groups -``` - -Or through the configuration file: - -``` yaml -Style/RedundantBegin: - Enabled: false -``` - -### Explain issues - -Ameba allows you to dig deeper into an issue, by showing you details about the issue -and the reasoning by it being reported. - -To be convenient, you can just copy-paste the `PATH:line:column` string from the -report and paste behind the `ameba` command to check it out. - -```sh -$ ameba crystal/command/format.cr:26:83 # show explanation for the issue -$ ameba --explain crystal/command/format.cr:26:83 # same thing -``` - -### Inline disabling - -One or more rules or one or more group of rules can be disabled using inline directives: - -```crystal -# ameba:disable Style/LargeNumbers -time = Time.epoch(1483859302) - -time = Time.epoch(1483859302) # ameba:disable Style/LargeNumbers, Lint/UselessAssign - -time = Time.epoch(1483859302) # ameba:disable Style, Lint -``` - -## Editors & integrations - - * Vim: [vim-crystal](https://github.com/rhysd/vim-crystal), [Ale](https://github.com/w0rp/ale) - * Emacs: [ameba.el](https://github.com/crystal-ameba/ameba.el) - * Sublime Text: [Sublime Linter Ameba](https://github.com/epergo/SublimeLinter-contrib-ameba) - * VSCode: [vscode-crystal-ameba](https://github.com/crystal-ameba/vscode-crystal-ameba) - * Codacy: [codacy-ameba](https://github.com/codacy/codacy-ameba) - * GitHub Actions: [github-action](https://github.com/crystal-ameba/github-action) - -## Credits & inspirations - -- [Crystal Language](https://crystal-lang.org) -- [Rubocop](https://rubocop.readthedocs.io/en/latest/) -- [Credo](http://credo-ci.org/) -- [Dogma](https://github.com/lpil/dogma) - -## Contributors - -- [veelenga](https://github.com/veelenga) Vitalii Elenhaupt - creator, maintainer diff --git a/samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr b/samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr deleted file mode 100644 index d66f9603dff2..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/bench/check_sources.cr +++ /dev/null @@ -1,30 +0,0 @@ -require "../src/ameba" -require "benchmark" - -private def get_files(n) - Dir["src/**/*.cr"].first(n) -end - -puts "== Compare:" -Benchmark.ips do |x| - [ - 1, - 3, - 5, - 10, - 20, - 30, - 40, - ].each do |n| - config = Ameba::Config.load - config.formatter = Ameba::Formatter::BaseFormatter.new - config.globs = get_files(n) - s = n == 1 ? "" : "s" - x.report("#{n} source#{s}") { Ameba.run config } - end -end - -puts "== Measure:" -config = Ameba::Config.load -config.formatter = Ameba::Formatter::BaseFormatter.new -puts Benchmark.measure { Ameba.run config } diff --git a/samples/client/petstore/crystal/lib/ameba/bin/.keep b/samples/client/petstore/crystal/lib/ameba/bin/.keep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/samples/client/petstore/crystal/lib/ameba/bin/ameba.cr b/samples/client/petstore/crystal/lib/ameba/bin/ameba.cr deleted file mode 100644 index d61d0ff11a5c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/bin/ameba.cr +++ /dev/null @@ -1,7 +0,0 @@ -# Require ameba cli which starts the inspection. -require "ameba/cli" - -# Require ameba extensions here which are added as project dependencies. -# Example: -# -# require "ameba-performance" diff --git a/samples/client/petstore/crystal/lib/ameba/lib b/samples/client/petstore/crystal/lib/ameba/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/ameba/shard.yml b/samples/client/petstore/crystal/lib/ameba/shard.yml deleted file mode 100644 index 68a3ae25716c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/shard.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: ameba -version: 0.13.3 - -authors: - - Vitalii Elenhaupt - -targets: - ameba: - main: src/cli.cr - -scripts: - # TODO: remove pre-compiled executable in future releases - postinstall: make bin && make run_file - -executables: - - ameba - -crystal: ">= 0.35.0" - -license: MIT diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr deleted file mode 100644 index e15f44afdf1c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branch_spec.cr +++ /dev/null @@ -1,363 +0,0 @@ -require "../../spec_helper" - -private def branch_of_assign_in_def(source) - nodes = as_nodes source - Ameba::AST::Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first) -end - -module Ameba::AST - describe Branch do - describe ".of" do - context "Crystal::If" do - it "constructs a branch in If.cond" do - branch = branch_of_assign_in_def %( - def method - if a = get_something # --> Crystal::Assign - puts a - end - end - ) - branch.to_s.should eq "a = get_something" - end - - it "constructs a branch in If.then" do - branch = branch_of_assign_in_def %( - def method - if true - a = 2 # --> Crystal::Assign - end - end - ) - branch.to_s.should eq "a = 2" - end - - it "constructs a branch in If.else" do - branch = branch_of_assign_in_def %( - def method - if true - nil - else - a = 2 # --> Crystal::Assign - end - end - ) - branch.to_s.should eq "a = 2" - end - - it "constructs a branch in inline If" do - branch = branch_of_assign_in_def %( - def method(a) - a = 0 if a == 2 # --> Crystal::Assign - end - ) - branch.to_s.should eq "a = 0" - end - end - - context "Crystal::Unless" do - it "constructs a branch in Unless.cond" do - branch = branch_of_assign_in_def %( - def method - unless a = get_something # --> Crystal::Assign - puts a - end - end - ) - branch.to_s.should eq "a = get_something" - end - - it "constructs a branch in Unless.then" do - branch = branch_of_assign_in_def %( - def method - unless true - a = 2 # --> Crystal::Assign - end - end - ) - branch.to_s.should eq "a = 2" - end - - it "constructs a new branch in Unless.else" do - branch = branch_of_assign_in_def %( - def method - unless true - nil - else - a = 2 # --> Crystal::Assign - end - end - ) - branch.to_s.should eq "a = 2" - end - - it "constructs a branch in inline Unless" do - branch = branch_of_assign_in_def %( - def method(a) - (a = 0; b = 3) unless a == 2 # --> Crystal::Expressions - end - ) - branch.to_s.should eq "(a = 0\nb = 3)" - end - end - - context "Crystal::BinaryOp" do - it "constructs a branch in left node" do - branch = branch_of_assign_in_def %( - def method(a) - (a = 2) && do_something - end - ) - branch.to_s.should eq "(a = 2)" - end - - it "constructs a branch in right node" do - branch = branch_of_assign_in_def %( - def method(a) - do_something || (a = 0) - end - ) - branch.to_s.should eq "(a = 0)" - end - end - - context "Crystal::Case" do - it "constructs a branch in cond" do - branch = branch_of_assign_in_def %( - def method(a) - case (a = 2) - when true then nil - end - end - ) - branch.to_s.should eq "(a = 2)" - end - - it "constructs a branch in when" do - branch = branch_of_assign_in_def %( - def method(a) - case a - when a = 3 then nil - end - end - ) - branch.to_s.should eq "when a = 3\n nil\n" - end - - it "constructs a branch in else" do - branch = branch_of_assign_in_def %( - def method(a) - case a - when true then nil - else a = 4 - end - end - ) - branch.to_s.should eq "a = 4" - end - end - - context "Crystal::While" do - it "constructs a branch in cond" do - branch = branch_of_assign_in_def %( - def method(a) - while a = 1 - nil - end - end - ) - branch.to_s.should eq "a = 1" - end - - it "constructs a branch in body" do - branch = branch_of_assign_in_def %( - def method(a) - while true - b = (a = 1) - end - end - ) - branch.to_s.should eq "b = (a = 1)" - end - end - - context "Crystal::Until" do - it "constructs a branch in cond" do - branch = branch_of_assign_in_def %( - def method(a) - until a = 1 - nil - end - end - ) - branch.to_s.should eq "a = 1" - end - - it "constructs a branch in body" do - branch = branch_of_assign_in_def %( - def method(a) - until false - b = (a = 1) - end - end - ) - branch.to_s.should eq "b = (a = 1)" - end - end - - context "Crystal::ExceptionHandler" do - it "constructs a branch in body" do - branch = branch_of_assign_in_def %( - def method(a) - a = 1 - rescue - nil - end - ) - branch.to_s.should eq "a = 1" - end - - it "constructs a branch in a rescue" do - branch = branch_of_assign_in_def %( - def method(a) - rescue - a = 1 - end - ) - branch.to_s.should eq "a = 1" - end - - it "constructs a branch in else" do - branch = branch_of_assign_in_def %( - def method(a) - rescue - else - a = 1 - end - ) - branch.to_s.should eq "a = 1" - end - - it "constructs a branch in ensure" do - branch = branch_of_assign_in_def %( - def method(a) - rescue - ensure - a = 1 - end - ) - branch.to_s.should eq "a = 1" - end - end - - context "Crystal::MacroIf" do - it "constructs a branch in cond" do - branch = branch_of_assign_in_def %( - def method(a) - {% if a = 2 %} - {% end %} - end - ) - branch.to_s.should eq "a = 2" - end - - it "constructs a branch in then" do - nodes = as_nodes %( - def method(a) - {% if true %} - a = 2 - {% end %} - end - ) - branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) - branch.to_s.strip.should eq "a = 2" - end - end - - context "Crystal::MacroFor" do - it "constructs a branch in body" do - nodes = as_nodes %( - def method(a) - {% for x in [1, 2, 3] %} - a = 2 - {% end %} - end - ) - branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) - branch.to_s.strip.should eq "a = 2" - end - end - - it "returns nil if branch does not exist" do - nodes = as_nodes %( - def method - a = 2 - end - ) - branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first) - branch.should be_nil - end - end - - describe "#initialize" do - it "creates new branch" do - nodes = as_nodes %( - if true - a = 2 - end - ) - branchable = Branchable.new nodes.if_nodes.first - branch = Branch.new nodes.assign_nodes.first, branchable - branch.node.should_not be_nil - end - end - - describe "delegation" do - it "delegates to_s to node" do - nodes = as_nodes %( - if true - a = 2 - end - ) - branchable = Branchable.new nodes.if_nodes.first - branch = Branch.new nodes.assign_nodes.first, branchable - branch.to_s.should eq branch.node.to_s - end - - it "delegates locations to node" do - nodes = as_nodes %( - if true - a = 2 - end - ) - branchable = Branchable.new nodes.if_nodes.first - branch = Branch.new nodes.assign_nodes.first, branchable - branch.location.should eq branch.node.location - branch.end_location.should eq branch.node.end_location - end - end - - describe "#in_loop?" do - it "returns true if branch is in a loop" do - nodes = as_nodes %( - while true - a = 1 - end - ) - branchable = Branchable.new nodes.while_nodes.first - branch = Branch.new nodes.assign_nodes.first, branchable - branch.in_loop?.should be_true - end - - it "returns false if branch is not in a loop" do - nodes = as_nodes %( - if a > 2 - a = 1 - end - ) - branchable = Branchable.new nodes.if_nodes.first - branch = Branch.new nodes.assign_nodes.first, branchable - branch.in_loop?.should be_false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr deleted file mode 100644 index d8cf7b24f00d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/branchable_spec.cr +++ /dev/null @@ -1,49 +0,0 @@ -require "../../spec_helper" - -module Ameba::AST - describe Branchable do - describe "#initialize" do - it "creates a new branchable" do - branchable = Branchable.new as_node %(a = 2 if true) - branchable.node.should_not be_nil - end - end - - describe "delegation" do - it "delegates to_s to @node" do - node = as_node %(a = 2 if true) - branchable = Branchable.new node - branchable.to_s.should eq node.to_s - end - - it "delegates locations to @node" do - node = as_node %(a = 2 if true) - branchable = Branchable.new node - branchable.location.should eq node.location - branchable.end_location.should eq node.end_location - end - end - - describe "#loop?" do - it "returns true if it is a while loop" do - branchable = Branchable.new as_node %(while true; a = 2; end) - branchable.loop?.should be_true - end - - it "returns true if it is the until loop" do - branchable = Branchable.new as_node %(until false; a = 2; end) - branchable.loop?.should be_true - end - - it "returns true if it is loop" do - branchable = Branchable.new as_node %(loop {}) - branchable.loop?.should be_true - end - - it "returns false otherwise" do - branchable = Branchable.new as_node %(a = 2 if true) - branchable.loop?.should be_false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr deleted file mode 100644 index a71e251f09c4..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/flow_expression_spec.cr +++ /dev/null @@ -1,56 +0,0 @@ -require "../../spec_helper" - -module Ameba::AST - describe FlowExpression do - describe "#initialize" do - it "creates a new flow expression" do - node = as_node("return 22") - flow_expression = FlowExpression.new node, false - flow_expression.node.should_not be_nil - flow_expression.in_loop?.should eq false - end - - describe "#delegation" do - it "delegates to_s to @node" do - node = as_node("return 22") - flow_expression = FlowExpression.new node, false - flow_expression.to_s.should eq node.to_s - end - - it "delegates locations to @node" do - node = as_node %(break if true) - flow_expression = FlowExpression.new node, false - flow_expression.location.should eq node.location - flow_expression.end_location.should eq node.end_location - end - end - - describe "#unreachable_nodes" do - it "returns unreachable nodes" do - nodes = as_nodes %( - def foobar - return - a = 1 - a = 2 - end - ) - node = nodes.expressions_nodes.first - flow_expression = FlowExpression.new node, false - flow_expression.unreachable_nodes.should eq nodes.assign_nodes - end - - it "returns nil if there is no unreachable node" do - nodes = as_nodes %( - def foobar - a = 1 - return a - end - ) - node = nodes.expressions_nodes.first - flow_expression = FlowExpression.new node, false - flow_expression.unreachable_nodes.empty?.should eq true - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr deleted file mode 100644 index 72951b14d5d8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/scope_spec.cr +++ /dev/null @@ -1,143 +0,0 @@ -require "../../spec_helper" - -module Ameba::AST - describe Scope do - describe "#initialize" do - source = "a = 2" - - it "assigns outer scope" do - root = Scope.new as_node(source) - child = Scope.new as_node(source), root - child.outer_scope.should_not be_nil - end - - it "assigns node" do - scope = Scope.new as_node(source) - scope.node.should_not be_nil - end - end - end - - describe "delegation" do - it "delegates to_s to node" do - node = as_node("def foo; end") - scope = Scope.new node - scope.to_s.should eq node.to_s - end - - it "delegates locations to node" do - node = as_node("def foo; end") - scope = Scope.new node - scope.location.should eq node.location - scope.end_location.should eq node.end_location - end - end - - describe "#references" do - it "can return an empty list of references" do - scope = Scope.new as_node("") - scope.references.should be_empty - end - - it "allows to add variable references" do - scope = Scope.new as_node("") - nodes = as_nodes "a = 2" - scope.references << Reference.new(nodes.var_nodes.first, scope) - scope.references.size.should eq 1 - end - end - - describe "#add_variable" do - it "adds a new variable to the scope" do - scope = Scope.new as_node("") - scope.add_variable(Crystal::Var.new "foo") - scope.variables.any?.should be_true - end - end - - describe "#find_variable" do - it "returns the variable in the scope by name" do - scope = Scope.new as_node("foo = 1") - scope.add_variable Crystal::Var.new "foo" - scope.find_variable("foo").should_not be_nil - end - - it "returns nil if variable not exist in this scope" do - scope = Scope.new as_node("foo = 1") - scope.find_variable("bar").should be_nil - end - end - - describe "#assign_variable" do - it "creates a new assignment" do - scope = Scope.new as_node("foo = 1") - scope.add_variable Crystal::Var.new "foo" - scope.assign_variable("foo", Crystal::Var.new "foo") - scope.find_variable("foo").not_nil!.assignments.size.should eq 1 - end - - it "does not create the assignment if variable is wrong" do - scope = Scope.new as_node("foo = 1") - scope.add_variable Crystal::Var.new "foo" - scope.assign_variable("bar", Crystal::Var.new "bar") - scope.find_variable("foo").not_nil!.assignments.size.should eq 0 - end - end - - describe "#block?" do - it "returns true if Crystal::Block" do - nodes = as_nodes %( - 3.times {} - ) - scope = Scope.new nodes.block_nodes.first - scope.block?.should be_true - end - - it "returns false otherwise" do - scope = Scope.new as_node "a = 1" - scope.block?.should be_false - end - end - - describe "#spawn_block?" do - it "returns true if a node is a spawn block" do - nodes = as_nodes %( - spawn {} - ) - scope = Scope.new nodes.block_nodes.first - scope.spawn_block?.should be_true - end - - it "returns false otherwise" do - scope = Scope.new as_node "a = 1" - scope.spawn_block?.should be_false - end - end - - describe "#in_macro?" do - it "returns true if Crystal::Macro" do - nodes = as_nodes %( - macro included - end - ) - scope = Scope.new nodes.macro_nodes.first - scope.in_macro?.should be_true - end - - it "returns true if node is nested to Crystal::Macro" do - nodes = as_nodes %( - macro included - {{@type.each do |type| a = type end}} - end - ) - outer_scope = Scope.new nodes.macro_nodes.first - scope = Scope.new nodes.block_nodes.first, outer_scope - scope.in_macro?.should be_true - end - - it "returns false otherwise" do - scope = Scope.new as_node "a = 1" - scope.in_macro?.should be_false - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr deleted file mode 100644 index d2f63de810d8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/util_spec.cr +++ /dev/null @@ -1,326 +0,0 @@ -require "../../spec_helper" -require "semantic_version" - -module Ameba::AST - struct Test - include Util - end - - subject = Test.new - - describe Util do - describe "#literal?" do - [ - Crystal::ArrayLiteral.new, - Crystal::BoolLiteral.new(false), - Crystal::CharLiteral.new('a'), - Crystal::HashLiteral.new, - Crystal::NamedTupleLiteral.new, - Crystal::NilLiteral.new, - Crystal::NumberLiteral.new(42), - Crystal::RegexLiteral.new(Crystal::StringLiteral.new("")), - Crystal::StringLiteral.new(""), - Crystal::SymbolLiteral.new(""), - Crystal::TupleLiteral.new([] of Crystal::ASTNode), - Crystal::RangeLiteral.new( - Crystal::NilLiteral.new, - Crystal::NilLiteral.new, - true), - ].each do |literal| - it "returns true if node is #{literal}" do - subject.literal?(literal).should be_true - end - end - - it "returns false if node is not a literal" do - subject.literal?(Crystal::Nop).should be_false - end - end - - describe "#node_source" do - it "returns original source of the node" do - s = %( - a = 1 - ) - node = Crystal::Parser.new(s).parse - source = subject.node_source node, s.split("\n") - source.should eq ["a = 1"] - end - - it "returns original source of multiline node" do - s = %( - if () - :ok - end - ) - node = Crystal::Parser.new(s).parse - source = subject.node_source node, s.split("\n") - source.should eq([ - "if ()", - " :ok", - " end", - ]) - end - - it "does not report source of node which has incorrect location" do - s = %q( - module MyModule - macro conditional_error_for_inline_callbacks - \{% - raise "" - %} - end - - macro before_save(x = nil) - end - end - ) - node = as_nodes(s).nil_literal_nodes.first - source = subject.node_source node, s.split("\n") - - if SemanticVersion.parse(Crystal::VERSION) <= SemanticVersion.parse("0.35.1") - source.should be_nil - else - source.should eq %w(nil) - end - end - end - - describe "#flow_command?" do - it "returns true if this is return" do - node = as_node("return 22") - subject.flow_command?(node, false).should eq true - end - - it "returns true if this is a break in a loop" do - node = as_node("break") - subject.flow_command?(node, true).should eq true - end - - it "returns false if this is a break out of loop" do - node = as_node("break") - subject.flow_command?(node, false).should eq false - end - - it "returns true if this is a next in a loop" do - node = as_node("next") - subject.flow_command?(node, true).should eq true - end - - it "returns false if this is a next out of loop" do - node = as_node("next") - subject.flow_command?(node, false).should eq false - end - - it "returns true if this is raise" do - node = as_node("raise e") - subject.flow_command?(node, false).should eq true - end - - it "returns true if this is exit" do - node = as_node("exit") - subject.flow_command?(node, false).should eq true - end - - it "returns true if this is abort" do - node = as_node("abort") - subject.flow_command?(node, false).should eq true - end - - it "returns false otherwise" do - node = as_node("foobar") - subject.flow_command?(node, false).should eq false - end - end - - describe "#flow_expression?" do - it "returns true if this is a flow command" do - node = as_node("return") - subject.flow_expression?(node, true).should eq true - end - - it "returns true if this is if-else consumed by flow expressions" do - node = as_node %( - if foo - return :foo - else - return :bar - end - ) - subject.flow_expression?(node, false).should eq true - end - - it "returns true if this is unless-else consumed by flow expressions" do - node = as_node %( - unless foo - return :foo - else - return :bar - end - ) - subject.flow_expression?(node).should eq true - end - - it "returns true if this is case consumed by flow expressions" do - node = as_node %( - case - when 1 - return 1 - when 2 - return 2 - else - return 3 - end - ) - subject.flow_expression?(node).should eq true - end - - it "returns true if this is exception handler consumed by flow expressions" do - node = as_node %( - begin - raise "exp" - rescue e - return e - end - ) - subject.flow_expression?(node).should eq true - end - - it "returns true if this while consumed by flow expressions" do - node = as_node %( - while true - return - end - ) - subject.flow_expression?(node).should eq true - end - - it "returns false if this while with break" do - node = as_node %( - while true - break - end - ) - subject.flow_expression?(node).should eq false - end - - it "returns true if this until consumed by flow expressions" do - node = as_node %( - until false - return - end - ) - subject.flow_expression?(node).should eq true - end - - it "returns false if this until with break" do - node = as_node %( - until false - break - end - ) - subject.flow_expression?(node).should eq false - end - - it "returns true if this expressions consumed by flow expressions" do - node = as_node %( - exp1 - exp2 - return - ) - subject.flow_expression?(node).should eq true - end - - it "returns false otherwise" do - node = as_node %( - exp1 - exp2 - ) - subject.flow_expression?(node).should eq false - end - end - - describe "#raise?" do - it "returns true if this is a raise method call" do - node = as_node "raise e" - subject.raise?(node).should eq true - end - - it "returns false if it has a receiver" do - node = as_node "obj.raise e" - subject.raise?(node).should eq false - end - - it "returns false if size of the arguments doesn't match" do - node = as_node "raise" - subject.raise?(node).should eq false - end - end - - describe "#exit?" do - it "returns true if this is a exit method call" do - node = as_node "exit" - subject.exit?(node).should eq true - end - - it "returns true if this is a exit method call with one argument" do - node = as_node "exit 1" - subject.exit?(node).should eq true - end - - it "returns false if it has a receiver" do - node = as_node "obj.exit" - subject.exit?(node).should eq false - end - - it "returns false if size of the arguments doesn't match" do - node = as_node "exit 1, 1" - subject.exit?(node).should eq false - end - end - - describe "#abort?" do - it "returns true if this is an abort method call" do - node = as_node "abort" - subject.abort?(node).should eq true - end - - it "returns true if this is an abort method call with one argument" do - node = as_node "abort \"message\"" - subject.abort?(node).should eq true - end - - it "returns true if this is an abort method call with two arguments" do - node = as_node "abort \"message\", 1" - subject.abort?(node).should eq true - end - - it "returns false if it has a receiver" do - node = as_node "obj.abort" - subject.abort?(node).should eq false - end - - it "returns false if size of the arguments doesn't match" do - node = as_node "abort 1, 1, 1" - subject.abort?(node).should eq false - end - end - - describe "#loop?" do - it "returns true if this is a loop method call" do - node = as_node "loop" - subject.loop?(node).should eq true - end - - it "returns false if it has a receiver" do - node = as_node "obj.loop" - subject.loop?(node).should eq false - end - - it "returns false if size of the arguments doesn't match" do - node = as_node "loop 1" - subject.loop?(node).should eq false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr deleted file mode 100644 index 6a3debe2dec8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/argument_spec.cr +++ /dev/null @@ -1,41 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - describe Argument do - arg = Crystal::Arg.new "a" - scope = Scope.new as_node "foo = 1" - variable = Variable.new(Crystal::Var.new("foo"), scope) - - describe "#initialize" do - it "creates a new argument" do - argument = Argument.new(arg, variable) - argument.node.should_not be_nil - end - end - - describe "delegation" do - it "delegates locations to node" do - argument = Argument.new(arg, variable) - argument.location.should eq arg.location - argument.end_location.should eq arg.end_location - end - - it "delegates to_s to node" do - argument = Argument.new(arg, variable) - argument.to_s.should eq arg.to_s - end - end - - describe "#ignored?" do - it "is true if arg starts with _" do - argument = Argument.new(Crystal::Arg.new("_a"), variable) - argument.ignored?.should be_true - end - - it "is false otherwise" do - argument = Argument.new(arg, variable) - argument.ignored?.should be_false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr deleted file mode 100644 index 7f50f6d1cf92..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/assignment_spec.cr +++ /dev/null @@ -1,118 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - describe Assignment do - node = Crystal::NilLiteral.new - scope = Scope.new as_node "foo = 1" - variable = Variable.new(Crystal::Var.new("foo"), scope) - - describe "#initialize" do - it "creates a new assignment with node and var" do - assignment = Assignment.new(node, variable, scope) - assignment.node.should_not be_nil - end - end - - describe "#reference=" do - it "creates a new reference" do - assignment = Assignment.new(node, variable, scope) - assignment.referenced = true - assignment.referenced?.should be_true - end - end - - describe "delegation" do - it "delegates locations" do - assignment = Assignment.new(node, variable, scope) - assignment.location.should eq node.location - assignment.end_location.should eq node.end_location - end - - it "delegates to_s" do - assignment = Assignment.new(node, variable, scope) - assignment.to_s.should eq node.to_s - end - - it "delegates scope" do - assignment = Assignment.new(node, variable, scope) - assignment.scope.should eq variable.scope - end - end - - describe "#branch" do - it "returns the branch of the assignment" do - nodes = as_nodes %( - def method(a) - if a - a = 3 # --> Crystal::Expressions - puts a - end - end - ) - - scope = Scope.new nodes.def_nodes.first - variable = Variable.new(nodes.var_nodes.first, scope) - assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) - assignment.branch.should_not be_nil - assignment.branch.not_nil!.node.class.should eq Crystal::Expressions - end - - it "returns inner branch" do - nodes = as_nodes %( - def method(a, b) - if a - if b - a = 3 # --> Crystal::Assign - end - end - end - ) - scope = Scope.new nodes.def_nodes.first - variable = Variable.new(nodes.var_nodes.first, scope) - assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) - assignment.branch.should_not be_nil - assignment.branch.not_nil!.node.class.should eq Crystal::Assign - end - - it "returns nil if assignment does not have a branch" do - nodes = as_nodes %( - def method(a) - a = 2 - end - ) - - scope = Scope.new nodes.def_nodes.first - variable = Variable.new(nodes.var_nodes.first, scope) - assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) - assignment.branch.should be_nil - end - end - - describe "#transformed?" do - it "returns false if the assignment is not transformed by the compiler" do - nodes = as_nodes %( - def method(a) - a = 2 - end - ) - - scope = Scope.new nodes.def_nodes.first - variable = Variable.new(nodes.var_nodes.first, scope) - assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) - assignment.transformed?.should be_false - end - - it "returns true if the assignment is transformed by the compiler" do - nodes = as_nodes %( - array.each do |(a, b)| - end - ) - - scope = Scope.new nodes.block_nodes.first - variable = Variable.new(nodes.var_nodes.first, scope) - assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) - assignment.transformed?.should be_true - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr deleted file mode 100644 index 155270918e1c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/reference_spec.cr +++ /dev/null @@ -1,10 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - describe Reference do - it "is derived from a Variable" do - node = Crystal::Var.new "foo" - Reference.new(node, Scope.new as_node "foo = 1").is_a?(Variable).should be_true - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr deleted file mode 100644 index fc39ef0d1cfc..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/variabling/variable_spec.cr +++ /dev/null @@ -1,218 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - describe Variable do - var_node = Crystal::Var.new("foo") - scope = Scope.new as_node "foo = 1" - - describe "#initialize" do - it "creates a new variable" do - variable = Variable.new(var_node, scope) - variable.node.should_not be_nil - end - end - - describe "delegation" do - it "delegates locations" do - variable = Variable.new(var_node, scope) - variable.location.should eq var_node.location - variable.end_location.should eq var_node.end_location - end - - it "delegates name" do - variable = Variable.new(var_node, scope) - variable.name.should eq var_node.name - end - - it "delegates to_s" do - variable = Variable.new(var_node, scope) - variable.to_s.should eq var_node.to_s - end - end - - describe "#special?" do - it "returns truthy if it is a special `$?` var" do - variable = Variable.new Crystal::Var.new("$?"), scope - variable.special?.should be_truthy - end - - it "returns falsey otherwise" do - variable = Variable.new Crystal::Var.new("a"), scope - variable.special?.should be_falsey - end - end - - describe "#assign" do - assign_node = as_node("foo=1") - - it "assigns the variable (creates a new assignment)" do - variable = Variable.new(var_node, scope) - variable.assign(assign_node, scope) - variable.assignments.any?.should be_true - end - - it "can create multiple assignments" do - variable = Variable.new(var_node, scope) - variable.assign(assign_node, scope) - variable.assign(assign_node, scope) - variable.assignments.size.should eq 2 - end - end - - describe "#reference" do - it "references the existed assignment" do - variable = Variable.new(var_node, scope) - variable.assign(as_node("foo=1"), scope) - variable.reference(var_node, scope) - variable.references.any?.should be_true - end - - it "adds a reference to the scope" do - scope = Scope.new as_node "foo = 1" - variable = Variable.new(var_node, scope) - variable.assign(as_node("foo=1"), scope) - variable.reference(var_node, scope) - scope.references.size.should eq 1 - scope.references.first.node.to_s.should eq "foo" - end - end - - describe "#captured_by_block?" do - it "returns truthy if the variable is captured by block" do - nodes = as_nodes %( - def method - a = 2 - 3.times { |i| a = a + i } - end - ) - scope = Scope.new nodes.def_nodes.first - var_node = nodes.var_nodes.first - scope.add_variable var_node - scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope) - - variable = Variable.new(var_node, scope) - variable.reference nodes.var_nodes.last, scope.inner_scopes.last - variable.captured_by_block?.should be_truthy - end - - it "returns falsey if the variable is not captured by the block" do - scope = Scope.new as_node %( - def method - a = 1 - end - ) - scope.add_variable Crystal::Var.new "a" - variable = scope.variables.first - variable.captured_by_block?.should be_falsey - end - end - - describe "#target_of?" do - it "returns true if the variable is a target of Crystal::Assign node" do - assign_node = as_nodes("foo=1").assign_nodes.last - variable = Variable.new assign_node.target.as(Crystal::Var), scope - variable.target_of?(assign_node).should be_true - end - - it "returns true if the variable is a target of Crystal::OpAssign node" do - assign_node = as_nodes("foo=1;foo+=1").op_assign_nodes.last - variable = Variable.new assign_node.target.as(Crystal::Var), scope - variable.target_of?(assign_node).should be_true - end - - it "returns true if the variable is a target of Crystal::MultiAssign node" do - assign_node = as_nodes("a,b,c={1,2,3}").multi_assign_nodes.last - assign_node.targets.size.should_not eq 0 - assign_node.targets.each do |target| - variable = Variable.new target.as(Crystal::Var), scope - variable.target_of?(assign_node).should be_true - end - end - - it "returns false if the node is not assign" do - variable = Variable.new(Crystal::Var.new("v"), scope) - variable.target_of?(as_node "nil").should be_false - end - - it "returns false if the variable is not a target of the assign" do - variable = Variable.new(Crystal::Var.new("foo"), scope) - variable.target_of?(as_node("bar = 1")).should be_false - end - end - - describe "#ignored?" do - it "is true if variable is ignored" do - variable = Variable.new(Crystal::Var.new("_var"), scope) - variable.ignored?.should be_true - end - - it "is false if variable is not ignored" do - variable = Variable.new(Crystal::Var.new("v_ar"), scope) - variable.ignored?.should be_false - end - - it "is true if variable is a black hole" do - variable = Variable.new(Crystal::Var.new("_"), scope) - variable.ignored?.should be_true - end - end - - describe "#eql?" do - var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) - variable = Variable.new var, scope - - it "is false if node is not a Crystal::Var" do - variable.eql?(as_node("nil")).should be_false - end - - it "is false if node name is different" do - variable.eql?(Crystal::Var.new "bar").should be_false - end - - it "is false if node has a different location" do - variable.eql?(Crystal::Var.new "foo").should be_false - end - - it "is true otherwise" do - variable.eql?(variable.node).should be_true - end - end - - describe "#declared_before?" do - it "is falsey if variable doesn't have location" do - var1 = Crystal::Var.new("foo") - var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 2)) - Variable.new(var1, scope).declared_before?(var2).should be_falsey - end - - it "is falsey if node doesn't have location" do - var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) - var2 = Crystal::Var.new("bar") - Variable.new(var1, scope).declared_before?(var2).should be_falsey - end - - it "is true if var's line_number below the node" do - var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) - var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 2, 2)) - Variable.new(var1, scope).declared_before?(var2).should be_true - end - - it "is true if var's column_number is after the node" do - var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2)) - var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3)) - Variable.new(var1, scope).declared_before?(var2).should be_true - end - - it "is false if var's location is before the node" do - var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2)) - var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3)) - Variable.new(var1, scope).declared_before?(var2).should be_false - end - - it "is false is the node is the same var" do - var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2)) - Variable.new(var, scope).declared_before?(var).should be_false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr deleted file mode 100644 index 5ee964ae8361..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/counting_visitor_spec.cr +++ /dev/null @@ -1,65 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - describe CountingVisitor do - describe "#visit" do - it "allow to visit ASTNode" do - node = Crystal::Parser.new("").parse - visitor = CountingVisitor.new node - node.accept visitor - end - end - - describe "#count" do - it "is 1 for an empty method" do - node = Crystal::Parser.new("def hello; end").parse - visitor = CountingVisitor.new node - - visitor.count.should eq 1 - end - - it "is 1 if there is Macro::For" do - code = %( - def initialize() - {% for c in ALL_NODES %} - true || false - {% end %} - end - ) - node = Crystal::Parser.new(code).parse - visitor = CountingVisitor.new node - visitor.count.should eq 1 - end - - it "is 1 if there is Macro::If" do - code = %( - def initialize() - {% if foo.bar? %} - true || false - {% end %} - end - ) - node = Crystal::Parser.new(code).parse - visitor = CountingVisitor.new node - visitor.count.should eq 1 - end - - {% for pair in [ - {code: "if true; end", description: "conditional"}, - {code: "while true; end", description: "while loop"}, - {code: "until 1 < 2; end", description: "until loop"}, - {code: "begin; rescue; end", description: "rescue"}, - {code: "case 1 when 1; end", description: "when"}, - {code: "true || false", description: "or"}, - {code: "true && false", description: "and"}, - ] %} - it "increases count for every {{ pair[:description].id }}" do - node = Crystal::Parser.new("def hello; {{ pair[:code].id }} end").parse - visitor = CountingVisitor.new node - - visitor.count.should eq 2 - end - {% end %} - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr deleted file mode 100644 index 2e41ce530697..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/flow_expression_visitor_spec.cr +++ /dev/null @@ -1,66 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - source = Source.new "" - - describe FlowExpressionVisitor do - it "creates an expression for return" do - rule = FlowExpressionRule.new - FlowExpressionVisitor.new rule, Source.new %( - def foo - return :bar - end - ) - rule.expressions.size.should eq 1 - end - - it "can create multiple expressions" do - rule = FlowExpressionRule.new - FlowExpressionVisitor.new rule, Source.new %( - def foo - if bar - return :baz - else - return :foobar - end - end - ) - rule.expressions.size.should eq 3 - end - - it "properly creates nested flow expressions" do - rule = FlowExpressionRule.new - FlowExpressionVisitor.new rule, Source.new %( - def foo - return( - loop do - break if a > 1 - return a - end - ) - end - ) - rule.expressions.size.should eq 4 - end - - it "creates an expression for break" do - rule = FlowExpressionRule.new - FlowExpressionVisitor.new rule, Source.new %( - while true - break - end - ) - rule.expressions.size.should eq 1 - end - - it "creates an expression for next" do - rule = FlowExpressionRule.new - FlowExpressionVisitor.new rule, Source.new %( - while true - next if something - end - ) - rule.expressions.size.should eq 1 - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr deleted file mode 100644 index 38535a9c4ca0..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/node_visitor_spec.cr +++ /dev/null @@ -1,16 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - rule = DummyRule.new - source = Source.new "" - - describe NodeVisitor do - describe "visit" do - it "allow to visit ASTNode" do - visitor = NodeVisitor.new rule, source - nodes = Crystal::Parser.new("").parse - nodes.accept visitor - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr deleted file mode 100644 index a523ad8378aa..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/redundant_control_expression_visitor_spec.cr +++ /dev/null @@ -1,26 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - source = Source.new "" - rule = RedundantControlExpressionRule.new - - describe RedundantControlExpressionVisitor do - node = as_node %( - a = 1 - b = 2 - return a + b - ) - subject = RedundantControlExpressionVisitor.new(rule, source, node) - - it "assigns valid attributes" do - subject.rule.should eq rule - subject.source.should eq source - subject.node.should eq node - end - - it "fires a callback with a valid node" do - rule.nodes.size.should eq 1 - rule.nodes.first.to_s.should eq "return a + b" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr deleted file mode 100644 index 98818fa52ef2..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/ast/visitors/scope_visitor_spec.cr +++ /dev/null @@ -1,58 +0,0 @@ -require "../../../spec_helper" - -module Ameba::AST - describe ScopeVisitor do - it "creates a scope for the def" do - rule = ScopeRule.new - ScopeVisitor.new rule, Source.new %( - def method - end - ) - rule.scopes.size.should eq 1 - end - - it "creates a scope for the proc" do - rule = ScopeRule.new - ScopeVisitor.new rule, Source.new %( - -> {} - ) - rule.scopes.size.should eq 1 - end - - it "creates a scope for the block" do - rule = ScopeRule.new - ScopeVisitor.new rule, Source.new %( - 3.times {} - ) - rule.scopes.size.should eq 2 - end - - context "inner scopes" do - it "creates scope for block inside def" do - rule = ScopeRule.new - ScopeVisitor.new rule, Source.new %( - def method - 3.times {} - end - ) - rule.scopes.size.should eq 2 - rule.scopes.last.outer_scope.should_not be_nil - rule.scopes.first.outer_scope.should eq rule.scopes.last - end - - it "creates scope for block inside block" do - rule = ScopeRule.new - ScopeVisitor.new rule, Source.new %( - 3.times do - 2.times {} - end - ) - rule.scopes.size.should eq 3 - inner_block = rule.scopes.first - outer_block = rule.scopes.last - inner_block.outer_scope.should_not eq outer_block - outer_block.outer_scope.should be_nil - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr deleted file mode 100644 index 219703207406..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/base_spec.cr +++ /dev/null @@ -1,75 +0,0 @@ -require "../spec_helper" - -module Ameba::Rule - describe Base do - context ".rules" do - it "returns a list of all rules" do - rules = Rule.rules - rules.should_not be_nil - rules.should contain DummyRule - end - end - - context "properties" do - subject = DummyRule.new - - it "is enabled by default" do - subject.enabled.should be_true - end - - it "has a description property" do - subject.description.should_not be_nil - end - - it "has excluded property" do - subject.excluded.should be_nil - end - end - - describe "#excluded?" do - it "returns false if a rule does no have a list of excluded source" do - DummyRule.new.excluded?(Source.new "", "source.cr").should_not be_true - end - - it "returns false if source is not excluded from this rule" do - rule = DummyRule.new - rule.excluded = %w(some_source.cr) - rule.excluded?(Source.new "", "another_source.cr").should_not be_true - end - - it "returns true if source is excluded from this rule" do - rule = DummyRule.new - rule.excluded = %w(source.cr) - rule.excluded?(Source.new "", "source.cr").should be_true - end - - it "returns true if source matches the wildcard" do - rule = DummyRule.new - rule.excluded = %w(**/*.cr) - rule.excluded?(Source.new "", __FILE__).should be_true - end - - it "returns false if source does not match the wildcard" do - rule = DummyRule.new - rule.excluded = %w(*_spec.cr) - rule.excluded?(Source.new "", "source.cr").should be_false - end - end - - describe ".parsed_doc" do - it "returns the parsed rule doc" do - DummyRule.parsed_doc.should eq "Dummy Rule which does nothing." - end - end - - describe "#==" do - it "returns true if rule has the same name" do - DummyRule.new.should eq(DummyRule.new) - end - - it "returns false if rule has a different name" do - DummyRule.new.should_not eq(ScopeRule.new) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr deleted file mode 100644 index f44ff6c6b177..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/cli/cmd_spec.cr +++ /dev/null @@ -1,165 +0,0 @@ -require "../../spec_helper" -require "../../../src/ameba/cli/cmd" - -module Ameba::Cli - describe "Cmd" do - describe ".run" do - it "runs ameba" do - r = Cli.run %w(-f silent file.cr) - r.should be_nil - end - end - - describe ".parse_args" do - %w(-s --silent).each do |f| - it "accepts #{f} flag" do - c = Cli.parse_args [f] - c.formatter.should eq :silent - end - end - - %w(-c --config).each do |f| - it "accepts #{f} flag" do - c = Cli.parse_args [f, "config.yml"] - c.config.should eq "config.yml" - end - end - - %w(-f --format).each do |f| - it "accepts #{f} flag" do - c = Cli.parse_args [f, "my-formatter"] - c.formatter.should eq "my-formatter" - end - end - - it "accepts --only flag" do - c = Cli.parse_args ["--only", "RULE1,RULE2"] - c.only.should eq %w(RULE1 RULE2) - end - - it "accepts --except flag" do - c = Cli.parse_args ["--except", "RULE1,RULE2"] - c.except.should eq %w(RULE1 RULE2) - end - - it "defaults all? flag to false" do - c = Cli.parse_args %w(file.cr) - c.all?.should eq false - end - - it "accepts --all flag" do - c = Cli.parse_args %w(--all) - c.all?.should eq true - end - - it "accepts --gen-config flag" do - c = Cli.parse_args %w(--gen-config) - c.formatter.should eq :todo - end - - it "accepts --no-color flag" do - c = Cli.parse_args %w(--no-color) - c.colors?.should be_false - end - - it "accepts --without-affected-code flag" do - c = Cli.parse_args %w(--without-affected-code) - c.without_affected_code?.should be_true - end - - it "doesn't disable colors by default" do - c = Cli.parse_args %w(--all) - c.colors?.should be_true - end - - it "ignores --config if --gen-config flag passed" do - c = Cli.parse_args %w(--gen-config --config my_config.yml) - c.formatter.should eq :todo - c.config.should eq "" - end - - describe "-e/--explain" do - it "configures file/line/column" do - c = Cli.parse_args %w(--explain src/file.cr:3:5) - c.location_to_explain.should_not be_nil - - location_to_explain = c.location_to_explain.not_nil! - location_to_explain[:file].should eq "src/file.cr" - location_to_explain[:line].should eq 3 - location_to_explain[:column].should eq 5 - end - - it "raises an error if location is not valid" do - expect_raises(Exception, "location should have PATH:line:column") do - Cli.parse_args %w(--explain src/file.cr:3) - end - end - - it "raises an error if line number is not valid" do - expect_raises(Exception, "location should have PATH:line:column") do - Cli.parse_args %w(--explain src/file.cr:a:3) - end - end - - it "raises an error if column number is not valid" do - expect_raises(Exception, "location should have PATH:line:column") do - Cli.parse_args %w(--explain src/file.cr:3:&) - end - end - - it "raises an error if line/column are missing" do - expect_raises(Exception, "location should have PATH:line:column") do - Cli.parse_args %w(--explain src/file.cr) - end - end - end - - context "--fail-level" do - it "configures fail level Convention" do - c = Cli.parse_args %w(--fail-level convention) - c.fail_level.should eq Severity::Convention - end - - it "configures fail level Warning" do - c = Cli.parse_args %w(--fail-level Warning) - c.fail_level.should eq Severity::Warning - end - - it "configures fail level Error" do - c = Cli.parse_args %w(--fail-level error) - c.fail_level.should eq Severity::Error - end - - it "raises if fail level is incorrect" do - expect_raises(Exception, "Incorrect severity name JohnDoe") do - Cli.parse_args %w(--fail-level JohnDoe) - end - end - end - - it "accepts unknown args as globs" do - c = Cli.parse_args %w(source1.cr source2.cr) - c.globs.should eq %w(source1.cr source2.cr) - end - - it "accepts one unknown arg as explain location if it has correct format" do - c = Cli.parse_args %w(source.cr:3:22) - c.location_to_explain.should_not be_nil - - location_to_explain = c.location_to_explain.not_nil! - location_to_explain[:file].should eq "source.cr" - location_to_explain[:line].should eq 3 - location_to_explain[:column].should eq 22 - end - - it "allows args to be blank" do - c = Cli.parse_args [] of String - c.formatter.should be_nil - c.globs.should be_nil - c.only.should be_nil - c.except.should be_nil - c.config.should eq Config::PATH - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr deleted file mode 100644 index c92768737075..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/config_spec.cr +++ /dev/null @@ -1,215 +0,0 @@ -require "../spec_helper" - -module Ameba - describe Config do - config_sample = "config/ameba.yml" - - it "should have a list of available formatters" do - Config::AVAILABLE_FORMATTERS.should_not be_nil - end - - describe ".new" do - it "loads default globs when config is empty" do - yml = YAML.parse "{}" - config = Config.new(yml) - config.globs.should eq Config::DEFAULT_GLOBS - end - - it "initializes globs as string" do - yml = YAML.parse <<-CONFIG - --- - Globs: src/*.cr - CONFIG - config = Config.new(yml) - config.globs.should eq %w(src/*.cr) - end - - it "initializes globs as array" do - yml = YAML.parse <<-CONFIG - --- - Globs: - - "src/*.cr" - - "!spec" - CONFIG - config = Config.new(yml) - config.globs.should eq %w(src/*.cr !spec) - end - - it "raises if Globs has a wrong type" do - yml = YAML.parse <<-CONFIG - --- - Globs: 100 - CONFIG - expect_raises(Exception, "incorrect 'Globs' section in a config file") { Config.new(yml) } - end - - it "initializes excluded as string" do - yml = YAML.parse <<-CONFIG - --- - Excluded: spec - CONFIG - config = Config.new(yml) - config.excluded.should eq %w(spec) - end - - it "initializes excluded as array" do - yml = YAML.parse <<-CONFIG - --- - Excluded: - - spec - - lib/*.cr - CONFIG - config = Config.new(yml) - config.excluded.should eq %w(spec lib/*.cr) - end - - it "raises if Excluded has a wrong type" do - yml = YAML.parse <<-CONFIG - --- - Excluded: true - CONFIG - expect_raises(Exception, "incorrect 'Excluded' section in a config file") { Config.new(yml) } - end - end - - describe ".load" do - it "loads custom config" do - config = Config.load config_sample - config.should_not be_nil - config.globs.should_not be_nil - config.formatter.should_not be_nil - end - - it "loads default config" do - config = Config.load - config.should_not be_nil - config.globs.should_not be_nil - config.formatter.should_not be_nil - end - end - - describe "#globs, #globs=" do - config = Config.load config_sample - - it "holds source globs" do - config.globs.should eq Config::DEFAULT_GLOBS - end - - it "allows to set globs" do - config.globs = ["file.cr"] - config.globs.should eq ["file.cr"] - end - end - - describe "#excluded, #excluded=" do - config = Config.load config_sample - - it "defaults to empty array" do - config.excluded.should be_empty - end - - it "allows to set excluded" do - config.excluded = ["spec"] - config.excluded.should eq ["spec"] - end - end - - describe "#sources" do - config = Config.load config_sample - - it "returns list of sources" do - config.sources.size.should be > 0 - config.sources.first.should be_a Source - config.sources.any? { |s| s.fullpath == __FILE__ }.should be_true - end - - it "returns a list of sources mathing globs" do - config.globs = %w(**/config_spec.cr) - config.sources.size.should eq(1) - end - - it "returns a lisf of sources excluding 'Excluded'" do - config.excluded = %w(**/config_spec.cr) - config.sources.any? { |s| s.fullpath == __FILE__ }.should be_false - end - end - - describe "#formatter, formatter=" do - config = Config.load config_sample - formatter = DummyFormatter.new - - it "contains default formatter" do - config.formatter.should_not be_nil - end - - it "allows to set formatter" do - config.formatter = formatter - config.formatter.should eq formatter - end - - it "allows to set formatter using a name" do - config.formatter = :progress - config.formatter.should_not be_nil - end - - it "raises an error if not available formatter is set" do - expect_raises(Exception) do - config.formatter = :no_such_formatter - end - end - end - - describe "#update_rule" do - config = Config.load config_sample - - it "updates enabled property" do - name = DummyRule.rule_name - config.update_rule name, enabled: false - rule = config.rules.find(&.name.== name).not_nil! - rule.enabled.should be_false - end - - it "updates excluded property" do - name = DummyRule.rule_name - excluded = %w(spec/source.cr) - config.update_rule name, excluded: excluded - rule = config.rules.find(&.name.== name).not_nil! - rule.excluded.should eq excluded - end - end - - describe "#update_rules" do - config = Config.load config_sample - - it "updates multiple rules by enabled property" do - name = DummyRule.rule_name - config.update_rules [name], enabled: false - rule = config.rules.find(&.name.== name).not_nil! - rule.enabled.should be_false - end - - it "updates multiple rules by excluded property" do - name = DummyRule.rule_name - excluded = %w(spec/source.cr) - config.update_rules [name], excluded: excluded - rule = config.rules.find(&.name.== name).not_nil! - rule.excluded.should eq excluded - end - - it "updates a group of rules by enabled property" do - group = DummyRule.group_name - config.update_rules [group], enabled: false - rule = config.rules.find(&.name.== DummyRule.rule_name).not_nil! - rule.enabled.should be_false - end - - it "updates a group by excluded property" do - name = DummyRule.group_name - excluded = %w(spec/source.cr) - config.update_rules [name], excluded: excluded - rule = config.rules.find(&.name.== DummyRule.rule_name).not_nil! - rule.excluded.should eq excluded - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr deleted file mode 100644 index 5c338ae00cdb..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/disabled_formatter_spec.cr +++ /dev/null @@ -1,42 +0,0 @@ -require "../../spec_helper" - -module Ameba::Formatter - describe DisabledFormatter do - output = IO::Memory.new - subject = DisabledFormatter.new output - - describe "#finished" do - it "writes a final message" do - subject.finished [Source.new ""] - output.to_s.should contain "Disabled rules using inline directives:" - end - - it "writes disabled rules if any" do - Colorize.enabled = false - - path = "source.cr" - s = Source.new("", path).tap do |source| - source.add_issue(ErrorRule.new, {1, 2}, message: "ErrorRule", status: :disabled) - source.add_issue(NamedRule.new, location: {2, 2}, message: "NamedRule", status: :disabled) - end - subject.finished [s] - log = output.to_s - log.should contain "#{path}:1 #{ErrorRule.rule_name}" - log.should contain "#{path}:2 #{NamedRule.rule_name}" - ensure - output.clear - Colorize.enabled = true - end - - it "does not write not-disabled rules" do - s = Source.new("", "source.cr").tap do |source| - source.add_issue(ErrorRule.new, {1, 2}, "ErrorRule") - source.add_issue(NamedRule.new, location: {2, 2}, - message: "NamedRule", status: :disabled) - end - subject.finished [s] - output.to_s.should_not contain ErrorRule.rule_name - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr deleted file mode 100644 index 444426228592..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/dot_formatter_spec.cr +++ /dev/null @@ -1,107 +0,0 @@ -require "../../spec_helper" - -module Ameba::Formatter - describe DotFormatter do - output = IO::Memory.new - subject = DotFormatter.new output - - describe "#started" do - it "writes started message" do - subject.started [Source.new ""] - output.to_s.should eq "Inspecting 1 file.\n\n" - end - end - - describe "#source_finished" do - it "writes valid source" do - subject.source_finished Source.new "" - output.to_s.should contain "." - end - - it "writes invalid source" do - s = Source.new "" - s.add_issue DummyRule.new, Crystal::Nop.new, "message" - subject.source_finished s - output.to_s.should contain "F" - end - end - - describe "#finished" do - it "writes a final message" do - subject.finished [Source.new ""] - output.to_s.should contain "1 inspected, 0 failures." - end - - it "writes the elapsed time" do - subject.finished [Source.new ""] - output.to_s.should contain "Finished in" - end - - context "when issues found" do - it "writes each issue" do - s = Source.new("").tap do |source| - source.add_issue(DummyRule.new, {1, 1}, "DummyRuleError") - source.add_issue(NamedRule.new, {1, 2}, "NamedRuleError") - end - subject.finished [s] - log = output.to_s - log.should contain "1 inspected, 2 failures." - log.should contain "DummyRuleError" - log.should contain "NamedRuleError" - end - - it "writes affected code by default" do - output.clear - s = Source.new(%( - a = 22 - puts a - )).tap do |source| - source.add_issue(DummyRule.new, {1, 5}, "DummyRuleError") - end - subject.finished [s] - log = output.to_s - log.should contain "> a = 22" - log.should contain " \e[33m^\e[0m" - end - - it "writes severity" do - output.clear - s = Source.new(%( - a = 22 - puts a - )).tap do |source| - source.add_issue(DummyRule.new, {1, 5}, "DummyRuleError") - end - subject.finished [s] - log = output.to_s - log.should contain "[C]" - end - - it "doesn't write affected code if it is disabled" do - output.clear - s = Source.new(%( - a = 22 - puts a - )).tap do |source| - source.add_issue(DummyRule.new, {1, 5}, "DummyRuleError") - end - - formatter = DotFormatter.new output - formatter.config[:without_affected_code] = true - formatter.finished [s] - log = output.to_s - log.should_not contain "> a = 22" - log.should_not contain " \e[33m^\e[0m" - end - - it "does not write disabled issues" do - s = Source.new "" - s.add_issue(DummyRule.new, location: {1, 1}, - message: "DummyRuleError", status: :disabled) - subject.finished [s] - output.to_s.should contain "1 inspected, 0 failures." - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr deleted file mode 100644 index 770d42774613..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/explain_formatter_spec.cr +++ /dev/null @@ -1,71 +0,0 @@ -require "../../spec_helper" - -module Ameba - def explanation(source) - output = IO::Memory.new - ErrorRule.new.catch(source) - location = {file: "source.cr", line: 1, column: 1} - Formatter::ExplainFormatter.new(output, location).finished([source]) - output.to_s - end - - describe Formatter::ExplainFormatter do - describe "#location" do - it "returns crystal location" do - location = Formatter::ExplainFormatter - .new(STDOUT, {file: "compiler.cr", line: 3, column: 8}).location - - location.is_a?(Crystal::Location).should be_true - location.filename.should eq "compiler.cr" - location.line_number.should eq 3 - location.column_number.should eq 8 - end - end - - describe "#output" do - it "returns io" do - output = Formatter::ExplainFormatter - .new(STDOUT, {file: "compiler.cr", line: 3, column: 8}).output - output.should eq STDOUT - end - end - - describe "#finished" do - it "writes issue info" do - source = Source.new "a = 42", "source.cr" - output = explanation(source) - output.should contain "ISSUE INFO" - output.should contain "This rule always adds an error" - output.should contain "source.cr:1:1" - end - - it "writes affected code" do - source = Source.new "a = 42", "source.cr" - output = explanation(source) - output.should contain "AFFECTED CODE" - output.should contain "a = 42" - end - - it "writes rule info" do - source = Source.new "a = 42", "source.cr" - output = explanation(source) - output.should contain "RULE INFO" - output.should contain "Convention" - output.should contain "Ameba/ErrorRule" - output.should contain "Always adds an error at 1:1" - end - - it "writes detailed description" do - source = Source.new "a = 42", "source.cr" - output = explanation(source) - output.should contain "DETAILED DESCRIPTION" - output.should contain "TO BE DONE..." - end - - it "writes nothing if location not found" do - source = Source.new "a = 42", "another_source.cr" - explanation(source).should be_empty - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr deleted file mode 100644 index d9fda5fdca48..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/flycheck_formatter_spec.cr +++ /dev/null @@ -1,48 +0,0 @@ -require "../../spec_helper" - -private def flycheck - output = IO::Memory.new - Ameba::Formatter::FlycheckFormatter.new output -end - -module Ameba::Formatter - describe FlycheckFormatter do - context "problems not found" do - it "reports nothing" do - subject = flycheck - subject.source_finished Source.new "" - subject.output.to_s.empty?.should be_true - end - end - - context "when problems found" do - it "reports an issue" do - s = Source.new "a = 1", "source.cr" - s.add_issue DummyRule.new, {1, 2}, "message" - subject = flycheck - subject.source_finished s - subject.output.to_s.should eq( - "source.cr:1:2: C: [#{DummyRule.rule_name}] message\n" - ) - end - - it "properly reports multi-line message" do - s = Source.new "a = 1", "source.cr" - s.add_issue DummyRule.new, {1, 2}, "multi\nline" - subject = flycheck - subject.source_finished s - subject.output.to_s.should eq( - "source.cr:1:2: C: [#{DummyRule.rule_name}] multi line\n" - ) - end - - it "reports nothing if location was not set" do - s = Source.new "a = 1", "source.cr" - s.add_issue DummyRule.new, Crystal::Nop.new, "message" - subject = flycheck - subject.source_finished s - subject.output.to_s.should eq "" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr deleted file mode 100644 index 6011e4596fa5..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/json_formatter_spec.cr +++ /dev/null @@ -1,99 +0,0 @@ -require "../../spec_helper" - -module Ameba - def get_result(sources = [Source.new ""]) - file = IO::Memory.new - formatter = Formatter::JSONFormatter.new file - - formatter.started sources - sources.each { |source| formatter.source_finished source } - formatter.finished sources - - JSON.parse file.to_s - end - - describe Formatter::JSONFormatter do - context "metadata" do - it "shows ameba version" do - get_result["metadata"]["ameba_version"].should eq Ameba::VERSION - end - - it "shows crystal version" do - get_result["metadata"]["crystal_version"].should eq Crystal::VERSION - end - end - - context "sources" do - it "shows path to the source" do - result = get_result [Source.new "", "source.cr"] - result["sources"][0]["path"].should eq "source.cr" - end - - it "shows rule name" do - s = Source.new "" - s.add_issue DummyRule.new, {1, 2}, "message1" - - result = get_result [s] - result["sources"][0]["issues"][0]["rule_name"].should eq DummyRule.rule_name - end - - it "shows severity" do - s = Source.new "" - s.add_issue DummyRule.new, {1, 2}, "message" - - result = get_result [s] - result["sources"][0]["issues"][0]["severity"].should eq "Convention" - end - - it "shows a message" do - s = Source.new "" - s.add_issue DummyRule.new, {1, 2}, "message" - - result = get_result [s] - result["sources"][0]["issues"][0]["message"].should eq "message" - end - - it "shows issue location" do - s = Source.new "" - s.add_issue DummyRule.new, {1, 2}, "message" - - result = get_result [s] - location = result["sources"][0]["issues"][0]["location"] - location["line"].should eq 1 - location["column"].should eq 2 - end - - it "shows issue end_location" do - s = Source.new "" - s.add_issue DummyRule.new, - Crystal::Location.new("path", 3, 3), - Crystal::Location.new("path", 5, 4), - "message" - - result = get_result [s] - end_location = result["sources"][0]["issues"][0]["end_location"] - end_location["line"].should eq 5 - end_location["column"].should eq 4 - end - end - - context "summary" do - it "shows a target sources count" do - result = get_result [Source.new(""), Source.new("")] - result["summary"]["target_sources_count"].should eq 2 - end - - it "shows issues count" do - s1 = Source.new "" - s1.add_issue DummyRule.new, {1, 2}, "message1" - s1.add_issue DummyRule.new, {1, 2}, "message2" - - s2 = Source.new "" - s2.add_issue DummyRule.new, {1, 2}, "message3" - - result = get_result [s1, s2] - result["summary"]["issues_count"].should eq 3 - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr deleted file mode 100644 index 050162afb4d4..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/todo_formatter_spec.cr +++ /dev/null @@ -1,127 +0,0 @@ -require "../../spec_helper" -require "file_utils" - -module Ameba - private def create_todo - formatter = Formatter::TODOFormatter.new IO::Memory.new - s = Source.new "a = 1", "source.cr" - s.add_issue DummyRule.new, {1, 2}, "message" - file = formatter.finished([s]) - file ? File.read(file.path) : "" - end - - describe Formatter::TODOFormatter do - Spec.after_each do - FileUtils.rm(Ameba::Config::PATH) if File.exists?(Ameba::Config::PATH) - end - - context "problems not found" do - it "does not create file" do - formatter = Formatter::TODOFormatter.new IO::Memory.new - file = formatter.finished [Source.new ""] - file.should be_nil - end - - it "reports a message saying file is not created" do - io = IO::Memory.new - formatter = Formatter::TODOFormatter.new io - formatter.finished [Source.new ""] - io.to_s.should contain "No issues found. File is not generated" - end - end - - context "problems found" do - it "prints a message saying file is created" do - io = IO::Memory.new - formatter = Formatter::TODOFormatter.new io - s = Source.new "a = 1", "source.cr" - s.add_issue DummyRule.new, {1, 2}, "message" - formatter.finished([s]) - io.to_s.should contain "Created .ameba.yml" - end - - it "creates a valid YAML document" do - YAML.parse(create_todo).should_not be_nil - end - - it "creates a todo with header" do - create_todo.should contain "# This configuration file was generated by" - end - - it "creates a todo with UTC time" do - create_todo.should match /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC/ - end - - it "creates a todo with version" do - create_todo.should contain "Ameba version #{VERSION}" - end - - it "creates a todo with a rule name" do - create_todo.should contain "DummyRule" - end - - it "creates a todo with severity" do - create_todo.should contain "Convention" - end - - it "creates a todo with problems count" do - create_todo.should contain "Problems found: 1" - end - - it "creates a todo with run details" do - create_todo.should contain "Run `ameba --only #{DummyRule.rule_name}`" - end - - it "excludes source from this rule" do - create_todo.should contain "Excluded:\n - source.cr" - end - - context "with multiple issues" do - formatter = Formatter::TODOFormatter.new IO::Memory.new - - s1 = Source.new "a = 1", "source1.cr" - s2 = Source.new "a = 1", "source2.cr" - s1.add_issue DummyRule.new, {1, 2}, "message1" - s1.add_issue NamedRule.new, {1, 2}, "message1" - s1.add_issue DummyRule.new, {2, 2}, "message1" - s2.add_issue DummyRule.new, {2, 2}, "message2" - - file = formatter.finished([s1, s2]) - content = File.read(file.not_nil!.path) - content.should contain <<-CONTENT - # Problems found: 3 - # Run `ameba --only Ameba/DummyRule` for details - Ameba/DummyRule: - Description: Dummy rule that does nothing. - Excluded: - - source1.cr - - source2.cr - Enabled: true - Severity: Convention - CONTENT - end - - context "when invalid syntax" do - it "does generate todo file" do - formatter = Formatter::TODOFormatter.new IO::Memory.new - s = Source.new "def invalid_syntax" - s.add_issue Rule::Lint::Syntax.new, {1, 2}, "message" - - file = formatter.finished [s] - file.should be_nil - end - - it "prints an error message" do - io = IO::Memory.new - formatter = Formatter::TODOFormatter.new io - s = Source.new "def invalid_syntax" - s.add_issue Rule::Lint::Syntax.new, {1, 2}, "message" - - formatter.finished [s] - io.to_s.should contain "Unable to generate TODO file" - io.to_s.should contain "Please fix syntax issues" - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr deleted file mode 100644 index e2f95423dee7..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/formatter/util_spec.cr +++ /dev/null @@ -1,29 +0,0 @@ -require "../../spec_helper" - -module Ameba::Formatter - class Subject - include Util - end - - subject = Subject.new - - describe Util do - describe "#affected_code" do - it "returns nil if there is no such a line number" do - source = Source.new %( - a = 1 - ) - location = Crystal::Location.new("filename", 2, 1) - subject.affected_code(source, location).should be_nil - end - - it "returns correct line if it is found" do - source = Source.new %( - a = 1 - ) - location = Crystal::Location.new("filename", 1, 1) - subject.affected_code(source, location).should eq "> a = 1\n \e[33m^\e[0m" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr deleted file mode 100644 index 413091952cdb..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/glob_utils_spec.cr +++ /dev/null @@ -1,50 +0,0 @@ -require "../spec_helper" - -module Ameba - struct GlobUtilsClass - include GlobUtils - end - - subject = GlobUtilsClass.new - current_file_basename = File.basename(__FILE__) - current_file_path = "spec/ameba/#{current_file_basename}" - - describe GlobUtils do - describe "#find_files_by_globs" do - it "returns a file by globs" do - subject.find_files_by_globs(["**/#{current_file_basename}"]) - .should eq [current_file_path] - end - - it "returns files by globs" do - subject.find_files_by_globs(["**/*_spec.cr"]) - .should contain current_file_path - end - - it "doesn't return rejected globs" do - subject - .find_files_by_globs(["**/*_spec.cr", "!**/#{current_file_basename}"]) - .should_not contain current_file_path - end - - it "doesn't return duplicated globs" do - subject - .find_files_by_globs(["**/*_spec.cr", "**/*_spec.cr"]) - .count(current_file_path) - .should eq 1 - end - end - - describe "#expand" do - it "expands globs" do - subject.expand(["**/#{current_file_basename}"]) - .should eq [current_file_path] - end - - it "does not list duplicated files" do - subject.expand(["**/#{current_file_basename}", "**/#{current_file_basename}"]) - .should eq [current_file_path] - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr deleted file mode 100644 index 3f3b2e182561..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/inline_comments_spec.cr +++ /dev/null @@ -1,161 +0,0 @@ -require "../spec_helper" - -module Ameba - describe InlineComments do - describe InlineComments::COMMENT_DIRECTIVE_REGEX do - subject = InlineComments::COMMENT_DIRECTIVE_REGEX - - it "allows to parse action and rule name" do - result = subject.match("# ameba:enable Group/RuleName") - result = result.should_not be_nil - result["action"].should eq "enable" - result["rules"].should eq "Group/RuleName" - end - - it "parses multiple rules" do - result = subject.match("# ameba:enable Group/RuleName, OtherRule, Foo/Bar") - result = result.should_not be_nil - result["action"].should eq "enable" - result["rules"].should eq "Group/RuleName, OtherRule, Foo/Bar" - end - - it "fails to parse directives with spaces" do - result = subject.match("# ameba : enable Group/RuleName") - result.should be_nil - end - end - - it "disables a rule with a comment directive" do - s = Source.new %Q( - # ameba:disable #{NamedRule.name} - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") - s.should be_valid - end - - it "disables a rule with a line that ends with a comment directive" do - s = Source.new %Q( - Time.epoch(1483859302) # ameba:disable #{NamedRule.name} - ) - s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") - s.should be_valid - end - - it "does not disable a rule of a different name" do - s = Source.new %Q( - # ameba:disable WrongName - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {2, 12}, message: "Error!") - s.should_not be_valid - end - - it "disables a rule if multiple rule names provided" do - s = Source.new %Q( - # ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {2, 12}, message: "") - s.should be_valid - end - - it "disables a rule if multiple rule names are separated by comma" do - s = Source.new %Q( - # ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {2, 12}, message: "") - s.should be_valid - end - - it "does not disable if multiple rule names used without required one" do - s = Source.new %( - # ameba:disable SomeRule, SomeOtherRule LargeNumbers - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {2, 12}, message: "") - s.should_not be_valid - end - - it "does not disable if comment directive has wrong place" do - s = Source.new %Q( - # ameba:disable #{NamedRule.name} - # - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {3, 12}, message: "") - s.should_not be_valid - end - - it "does not disable if comment directive added to the wrong line" do - s = Source.new %Q( - if use_epoch? # ameba:disable #{NamedRule.name} - Time.epoch(1483859302) - end - ) - s.add_issue(NamedRule.new, location: {3, 12}, message: "") - s.should_not be_valid - end - - it "does not disable if that is not a comment directive" do - s = Source.new %Q( - "ameba:disable #{NamedRule.name}" - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {3, 12}, message: "") - s.should_not be_valid - end - - it "does not disable if that is a commented out directive" do - s = Source.new %Q( - # # ameba:disable #{NamedRule.name} - Time.epoch(1483859302) - ) - s.add_issue(NamedRule.new, location: {3, 12}, message: "") - s.should_not be_valid - end - - it "does not disable if that is an inline commented out directive" do - s = Source.new %Q( - a = 1 # Disable it: # ameba:disable #{NamedRule.name} - ) - s.add_issue(NamedRule.new, location: {2, 12}, message: "") - s.should_not be_valid - end - - context "with group name" do - it "disables one rule with a group" do - s = Source.new %Q( - a = 1 # ameba:disable #{DummyRule.rule_name} - ) - s.add_issue(DummyRule.new, location: {1, 12}, message: "") - s.should be_valid - end - - it "doesn't disable others rules" do - s = Source.new %Q( - a = 1 # ameba:disable #{DummyRule.rule_name} - ) - s.add_issue(NamedRule.new, location: {2, 12}, message: "") - s.should_not be_valid - end - - it "disables a hole group of rules" do - s = Source.new %Q( - a = 1 # ameba:disable #{DummyRule.group_name} - ) - s.add_issue(DummyRule.new, location: {1, 12}, message: "") - s.should be_valid - end - - it "does not disable rules which do not belong to the group" do - s = Source.new %Q( - a = 1 # ameba:disable Lint - ) - s.add_issue(DummyRule.new, location: {2, 12}, message: "") - s.should_not be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr deleted file mode 100644 index 652f52473d02..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/issue_spec.cr +++ /dev/null @@ -1,50 +0,0 @@ -require "../spec_helper" - -module Ameba - describe Issue do - it "accepts rule and message" do - issue = Issue.new rule: DummyRule.new, - location: nil, - end_location: nil, - message: "Blah", - status: nil - - issue.rule.should_not be_nil - issue.message.should eq "Blah" - end - - it "accepts location" do - location = Crystal::Location.new("path", 3, 2) - issue = Issue.new rule: DummyRule.new, - location: location, - end_location: nil, - message: "Blah", - status: nil - - issue.location.to_s.should eq location.to_s - issue.end_location.should eq nil - end - - it "accepts end_location" do - location = Crystal::Location.new("path", 3, 2) - issue = Issue.new rule: DummyRule.new, - location: nil, - end_location: location, - message: "Blah", - status: nil - - issue.location.should eq nil - issue.end_location.to_s.should eq location.to_s - end - - it "accepts status" do - issue = Issue.new rule: DummyRule.new, - location: nil, - end_location: nil, - message: "", - status: :enabled - - issue.status.should eq :enabled - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr deleted file mode 100644 index 8d85a55aafc9..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/reportable_spec.cr +++ /dev/null @@ -1,40 +0,0 @@ -require "../spec_helper" - -module Ameba - describe Reportable do - describe "#add_issue" do - it "adds a new issue for node" do - s = Source.new "", "source.cr" - s.add_issue(DummyRule.new, Crystal::Nop.new, "Error!") - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "" - issue.message.should eq "Error!" - end - - it "adds a new issue by line and column number" do - s = Source.new "", "source.cr" - s.add_issue(DummyRule.new, {23, 2}, "Error!") - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:23:2" - issue.message.should eq "Error!" - end - end - - describe "#valid?" do - it "returns true if no issues added" do - s = Source.new "", "source.cr" - s.should be_valid - end - - it "returns false if there are issues added" do - s = Source.new "", "source.cr" - s.add_issue DummyRule.new, {22, 2}, "ERROR!" - s.should_not be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr deleted file mode 100644 index 166e1e2624a7..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/base_spec.cr +++ /dev/null @@ -1,32 +0,0 @@ -require "../../spec_helper" - -module Ameba - describe Rule::Base do - describe "#catch" do - it "accepts and returns source" do - s = Source.new "", "" - DummyRule.new.catch(s).should eq s - end - end - - describe "#name" do - it "returns name of the rule" do - DummyRule.new.name.should eq "Ameba/DummyRule" - end - end - - describe "#group" do - it "returns a group rule belongs to" do - DummyRule.new.group.should eq "Ameba" - end - end - end - - describe Rule do - describe ".rules" do - it "returns a list of all defined rules" do - Rule.rules.includes?(DummyRule).should be_true - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr deleted file mode 100644 index 10392789b39b..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/line_length_spec.cr +++ /dev/null @@ -1,43 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Layout - subject = LineLength.new - long_line = "*" * (subject.max_length + 1) - - describe LineLength do - it "passes if all lines are shorter than MaxLength symbols" do - source = Source.new "short line" - subject.catch(source).should be_valid - end - - it "passes if line consists of MaxLength symbols" do - source = Source.new "*" * subject.max_length - subject.catch(source).should be_valid - end - - it "fails if there is at least one line longer than MaxLength symbols" do - source = Source.new long_line - subject.catch(source).should_not be_valid - end - - it "reports rule, pos and message" do - source = Source.new long_line, "source.cr" - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should eq subject - issue.location.to_s.should eq "source.cr:1:#{subject.max_length + 1}" - issue.end_location.should be_nil - issue.message.should eq "Line too long" - end - - context "properties" do - it "allows to configure max length of the line" do - source = Source.new long_line - rule = LineLength.new - rule.max_length = long_line.size - rule.catch(source).should be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr deleted file mode 100644 index 4f42f9a61229..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_blank_lines_spec.cr +++ /dev/null @@ -1,58 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Layout - subject = TrailingBlankLines.new - - describe TrailingBlankLines do - it "passes if there is a blank line at the end of a source" do - source = Source.new "a = 1\n", normalize: false - subject.catch(source).should be_valid - end - - it "passes if source is empty" do - source = Source.new "" - subject.catch(source).should be_valid - end - - it "fails if there is no blank lines at the end" do - source = Source.new "no-blankline" - subject.catch(source).should_not be_valid - end - - it "fails if there more then one blank line at the end of a source" do - source = Source.new "a = 1\n \n", normalize: false - subject.catch(source).should_not be_valid - end - - it "fails if last line is not blank" do - source = Source.new "\n\n\n puts 22", normalize: false - subject.catch(source).should_not be_valid - end - - context "when unnecessary blank line has been detected" do - it "reports rule, pos and message" do - source = Source.new "a = 1\n\n", "source.cr", normalize: false - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:1" - issue.end_location.should be_nil - issue.message.should eq "Excessive trailing newline detected" - end - end - - context "when final line has been missed" do - it "reports rule, pos and message" do - source = Source.new "a = 1", "source.cr", normalize: false - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:0:1" - issue.end_location.should be_nil - issue.message.should eq "Trailing newline missing" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr deleted file mode 100644 index 59c95e25b3b3..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/layout/trailing_whitespace_spec.cr +++ /dev/null @@ -1,28 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Layout - subject = TrailingWhitespace.new - - describe TrailingWhitespace do - it "passes if all lines do not have trailing whitespace" do - source = Source.new "no-whispace" - subject.catch(source).should be_valid - end - - it "fails if there is a line with trailing whitespace" do - source = Source.new "whitespace at the end " - subject.catch(source).should_not be_valid - end - - it "reports rule, pos and message" do - source = Source.new "a = 1\n b = 2 ", "source.cr" - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:7" - issue.end_location.should be_nil - issue.message.should eq "Trailing whitespace detected" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr deleted file mode 100644 index 5667982d9637..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/bad_directive_spec.cr +++ /dev/null @@ -1,66 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe BadDirective do - subject = BadDirective.new - - it "does not report if rule is correct" do - s = Source.new %( - # ameba:disable Lint/BadDirective - ) - subject.catch(s).should be_valid - end - - it "reports if there is incorrect action" do - s = Source.new %( - # ameba:foo Lint/BadDirective - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.message.should eq( - "Bad action in comment directive: 'foo'. Possible values: disable, enable" - ) - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "" - end - - it "reports if there are incorrect rule names" do - s = Source.new %( - # ameba:enable BadRule1, BadRule2 - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.message.should eq( - "Such rules do not exist: BadRule1, BadRule2" - ) - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "" - end - - it "does not report if there no action and rules at all" do - s = Source.new %( - # ameba: - ) - subject.catch(s).should be_valid - end - - it "does not report if there are no rules" do - s = Source.new %( - # ameba:enable - # ameba:disable - ) - subject.catch(s).should be_valid - end - - it "does not report if there are group names in the directive" do - s = Source.new %( - # ameba:disable Style Performance - ) - subject.catch(s).should be_valid - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr deleted file mode 100644 index da737643c657..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/comparison_to_boolean_spec.cr +++ /dev/null @@ -1,114 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = ComparisonToBoolean.new - - describe ComparisonToBoolean do - it "passes if there is no comparison to boolean" do - source = Source.new %( - a = true - - if a - :ok - end - - if true - :ok - end - - unless s.empty? - :ok - end - - :ok if a - - :ok if a != 1 - - :ok if a == "true" - - case a - when true - :ok - when false - :not_ok - end - ) - subject.catch(source).should be_valid - end - - context "boolean on the right" do - it "fails if there is == comparison to boolean" do - source = Source.new %( - if s.empty? == true - :ok - end - ) - subject.catch(source).should_not be_valid - end - - it "fails if there is != comparison to boolean" do - source = Source.new %( - if a != false - :ok - end - ) - subject.catch(source).should_not be_valid - end - - it "fails if there is case comparison to boolean" do - source = Source.new %( - a === true - ) - subject.catch(source).should_not be_valid - end - - it "reports rule, pos and message" do - source = Source.new "a != true", "source.cr" - subject.catch(source) - - issue = source.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.message.should eq "Comparison to a boolean is pointless" - end - end - - context "boolean on the left" do - it "fails if there is == comparison to boolean" do - source = Source.new %( - if true == s.empty? - :ok - end - ) - subject.catch(source).should_not be_valid - end - - it "fails if there is != comparison to boolean" do - source = Source.new %( - if false != a - :ok - end - ) - subject.catch(source).should_not be_valid - end - - it "fails if there is case comparison to boolean" do - source = Source.new %( - true === a - ) - subject.catch(source).should_not be_valid - end - - it "reports rule, pos and message" do - source = Source.new "true != a", "source.cr" - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:9" - issue.message.should eq "Comparison to a boolean is pointless" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr deleted file mode 100644 index ff29e001744d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/debugger_statement_spec.cr +++ /dev/null @@ -1,45 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = DebuggerStatement.new - - describe DebuggerStatement do - it "passes if there is no debugger statement" do - s = Source.new %( - "this is not a debugger statement" - s = "debugger" - - def debugger(program) - end - debugger "" - - class A - def debugger - end - end - A.new.debugger - ) - subject.catch(s).should be_valid - end - - it "fails if there is a debugger statement" do - s = Source.new %( - a = 2 - debugger - a = a + 1 - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, pos and message" do - s = Source.new "debugger", "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:8" - issue.message.should eq "Possible forgotten debugger statement detected" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr deleted file mode 100644 index bba7a6d88289..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_ensure_spec.cr +++ /dev/null @@ -1,69 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe EmptyEnsure do - subject = EmptyEnsure.new - - it "passes if there is no empty ensure blocks" do - s = Source.new %( - def some_method - do_some_stuff - ensure - do_something_else - end - - begin - do_some_stuff - ensure - do_something_else - end - - def method_with_rescue - rescue - ensure - nil - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is an empty ensure in method" do - s = Source.new %( - def method - do_some_stuff - ensure - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is an empty ensure in a block" do - s = Source.new %( - begin - do_some_stuff - ensure - # nothing here - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, pos and message" do - s = Source.new %( - begin - do_some_stuff - rescue - do_some_other_stuff - ensure - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:3" - issue.end_location.to_s.should eq "source.cr:6:3" - issue.message.should eq "Empty `ensure` block detected" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr deleted file mode 100644 index f757321acbe3..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_expression_spec.cr +++ /dev/null @@ -1,127 +0,0 @@ -require "../../../spec_helper" - -module Ameba - subject = Rule::Lint::EmptyExpression.new - - def it_detects_empty_expression(code) - it "detects empty expression" do - s = Source.new code - rule = Rule::Lint::EmptyExpression.new - rule.catch(s).should_not be_valid - end - end - - describe Rule::Lint::EmptyExpression do - it "passes if there is no empty expression" do - s = Source.new %( - def method() - end - - method() - method(1, 2, 3) - method(nil) - - a = nil - a = "" - a = 0 - - nil - :any.nil? - - begin "" end - [nil] << nil - ) - subject.catch(s).should be_valid - end - - it_detects_empty_expression %(()) - it_detects_empty_expression %(((()))) - it_detects_empty_expression %(a = ()) - it_detects_empty_expression %((();())) - it_detects_empty_expression %(if (); end) - it_detects_empty_expression %( - if foo - 1 - elsif () - 2 - end - ) - it_detects_empty_expression %( - case foo - when :foo then () - end - ) - it_detects_empty_expression %( - case foo - when :foo then 1 - else - () - end - ) - it_detects_empty_expression %( - case foo - when () then 1 - end - ) - it_detects_empty_expression %( - def method - a = 1 - () - end - ) - it_detects_empty_expression %( - def method - rescue - () - end - ) - it_detects_empty_expression %( - def method - begin - end - end - ) - it_detects_empty_expression %( - begin; end - ) - it_detects_empty_expression %( - begin - nil - end - ) - it_detects_empty_expression %( - begin - () - end - ) - - it "does not report emtpy expression in macro" do - s = Source.new %q( - module MyModule - macro conditional_error_for_inline_callbacks - \{% - raise "" - %} - end - - macro before_save(x = nil) - end - end - ) - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - if () - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:4" - issue.end_location.to_s.should eq "source.cr:1:5" - issue.message.should eq "Avoid empty expressions" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr deleted file mode 100644 index 26f63fbaf2cb..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/empty_loop_spec.cr +++ /dev/null @@ -1,88 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe EmptyLoop do - subject = EmptyLoop.new - - it "does not report if there are not empty loops" do - s = Source.new %( - a = 1 - - while a < 10 - a += 1 - end - - until a == 10 - a += 1 - end - - loop do - a += 1 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is an empty while loop" do - s = Source.new %( - a = 1 - while true - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if while loop has non-literals in cond block" do - s = Source.new %( - a = 1 - while a = gets.to_s - # nothing here - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is an empty until loop" do - s = Source.new %( - do_something - until false - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if until loop has non-literals in cond block" do - s = Source.new %( - until socket_open? - end - ) - subject.catch(s).should be_valid - end - - it "reports if there an empty loop" do - s = Source.new %( - a = 1 - loop do - - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, message and location" do - s = Source.new %( - a = 1 - loop do - # comment goes here - end - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:1" - issue.end_location.to_s.should eq "source.cr:4:3" - issue.message.should eq EmptyLoop::MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr deleted file mode 100644 index 43b5a4475943..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/hash_duplicated_key_spec.cr +++ /dev/null @@ -1,51 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe HashDuplicatedKey do - subject = HashDuplicatedKey.new - - it "passes if there is no duplicated keys in a hash literals" do - s = Source.new %( - h = {"a" => 1, :a => 2, "b" => 3} - h = {"a" => 1, "b" => 2, "c" => {"a" => 3, "b" => 4}} - h = {} of String => String - ) - subject.catch(s).should be_valid - end - - it "fails if there is a duplicated key in a hash literal" do - s = Source.new %q( - h = {"a" => 1, "b" => 2, "a" => 3} - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is a duplicated key in the inner hash literal" do - s = Source.new %q( - h = {"a" => 1, "b" => {"a" => 3, "b" => 4, "a" => 5}} - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, location and message" do - s = Source.new %q( - h = {"a" => 1, "a" => 2} - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:5" - issue.end_location.to_s.should eq "source.cr:1:24" - issue.message.should eq %(Duplicated keys in hash literal: "a") - end - - it "reports multiple duplicated keys" do - s = Source.new %q( - h = {"key1" => 1, "key1" => 2, "key2" => 3, "key2" => 4} - ) - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.message.should eq %(Duplicated keys in hash literal: "key1", "key2") - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr deleted file mode 100644 index 8150ad2bd8ce..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_condition_spec.cr +++ /dev/null @@ -1,179 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = LiteralInCondition.new - - describe LiteralInCondition do - it "passes if there is not literals in conditional" do - s = Source.new %( - if a == 2 - :ok - end - - :ok unless b - - case string - when "a" - :ok - when "b" - :ok - end - - unless a.nil? - :ok - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is a predicate in if conditional" do - s = Source.new %( - if "string" - :ok - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is a predicate in unless conditional" do - s = Source.new %( - unless true - :ok - end - ) - subject.catch(s).should_not be_valid - end - - describe "range" do - it "reports range with literals" do - s = Source.new %( - case 1..2 - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report range with non-literals" do - s = Source.new %( - case (1..a) - end - ) - subject.catch(s).should be_valid - end - end - - describe "array" do - it "reports array with literals" do - s = Source.new %( - case [1, 2, 3] - when :array - :ok - when :not_array - :also_ok - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report array with non-literals" do - s = Source.new %( - a, b = 1, 2 - case [1, 2, a] - when :array - :ok - when :not_array - :also_ok - end - ) - subject.catch(s).should be_valid - end - end - - describe "hash" do - it "reports hash with literals" do - s = Source.new %( - case { "name" => 1, 33 => 'b' } - when :hash - :ok - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report hash with non-literals in keys" do - s = Source.new %( - case { a => 1, 33 => 'b' } - when :hash - :ok - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report hash with non-literals in values" do - s = Source.new %( - case { "name" => a, 33 => 'b' } - when :hash - :ok - end - ) - subject.catch(s).should be_valid - end - end - - describe "tuple" do - it "reports tuple with literals" do - s = Source.new %( - case {1, false} - when {1, _} - :ok - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report tuple with non-literals" do - s = Source.new %( - a, b = 1, 2 - case {1, b} - when {1, 2} - :ok - end - ) - subject.catch(s).should be_valid - end - end - - describe "named tuple" do - it "reports named tuple with literals" do - s = Source.new %( - case { name: 1, foo: :bar} - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report named tuple with non-literals" do - s = Source.new %( - case { name: a, foo: :bar} - end - ) - subject.catch(s).should be_valid - end - end - - it "reports rule, pos and message" do - s = Source.new %( - puts "hello" if true - ), "source.cr" - subject.catch(s).should_not be_valid - - s.issues.size.should eq 1 - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:20" - issue.message.should eq "Literal value found in conditional" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr deleted file mode 100644 index 218d94d78375..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/literal_in_interpolation_spec.cr +++ /dev/null @@ -1,51 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = LiteralInInterpolation.new - - describe LiteralInInterpolation do - it "passes with good interpolation examples" do - s = Source.new %q( - name = "Ary" - "Hello, #{name}" - - "#{name}" - - "Name size: #{name.size}" - ) - subject.catch(s).should be_valid - end - - it "fails if there is useless interpolation" do - [ - %q("#{:Ary}"), - %q("#{[1, 2, 3]}"), - %q("#{true}"), - %q("#{false}"), - %q("here are #{4} cats"), - ].each do |str| - subject.catch(Source.new str).should_not be_valid - end - end - - it "reports rule, pos and message" do - s = Source.new %q( - "Hello, #{:world} from #{:ameba}" - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:16" - issue.message.should eq "Literal value found in interpolation" - - issue = s.issues.last - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:26" - issue.end_location.to_s.should eq "source.cr:1:31" - issue.message.should eq "Literal value found in interpolation" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr deleted file mode 100644 index 42c46272ff18..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/percent_arrays_spec.cr +++ /dev/null @@ -1,86 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe PercentArrays do - subject = PercentArrays.new - - it "passes if percent arrays are written correctly" do - s = Source.new %q( - %i(one two three) - %w(one two three) - - %i(1 2 3) - %w(1 2 3) - - %i() - %w() - ) - subject.catch(s).should be_valid - end - - it "fails if string percent array has commas" do - s = Source.new %( %w(one, two) ) - subject.catch(s).should_not be_valid - end - - it "fails if string percent array has quotes" do - s = Source.new %( %w("one" "two") ) - subject.catch(s).should_not be_valid - end - - it "fails if symbols percent array has commas" do - s = Source.new %( %i(one, two) ) - subject.catch(s).should_not be_valid - end - - it "fails if symbols percent array has a colon" do - s = Source.new %( %i(:one :two) ) - subject.catch(s).should_not be_valid - end - - it "reports rule, location and message for %i" do - s = Source.new %( - %i(:one) - ), "source.cr" - - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.message.should eq( - "Symbols `,:` may be unwanted in %i array literals" - ) - end - - it "reports rule, location and message for %w" do - s = Source.new %( - %w("one") - ), "source.cr" - - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.should be_nil - issue.message.should eq( - "Symbols `,\"` may be unwanted in %w array literals" - ) - end - - context "properties" do - it "allows to configure string_array_unwanted_symbols" do - rule = PercentArrays.new - rule.string_array_unwanted_symbols = "," - s = Source.new %( %w("one") ) - rule.catch(s).should be_valid - end - - it "allows to configure symbol_array_unwanted_symbols" do - rule = PercentArrays.new - rule.symbol_array_unwanted_symbols = "," - s = Source.new %( %i(:one) ) - rule.catch(s).should be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr deleted file mode 100644 index a86d30f2780b..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/rand_zero_spec.cr +++ /dev/null @@ -1,37 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe RandZero do - subject = RandZero.new - - it "passes if it is not rand(1) or rand(0)" do - s = Source.new %( - rand(1.0) - rand(0.11) - rand(2) - ) - subject.catch(s).should be_valid - end - - it "fails if it is rand(0)" do - s = Source.new "rand(0)" - subject.catch(s).should_not be_valid - end - - it "fails if it is rand(1)" do - s = Source.new "rand(1)" - subject.catch(s).should_not be_valid - end - - it "reports rule, location and a message" do - s = Source.new "rand(1)", "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:7" - issue.message.should eq "rand(1) always returns 0" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr deleted file mode 100644 index cacb74e04a14..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_string_cercion_spec.cr +++ /dev/null @@ -1,97 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe RedundantStringCoercion do - subject = RedundantStringCoercion.new - - it "does not report if there is no redundant string coersion" do - s = Source.new %( - "Hello, #{name}" - ) - subject.catch(s).should be_valid - end - - it "reports if there is a redundant string coersion" do - s = Source.new %q( - "Hello, #{name.to_s}" - ) - subject.catch(s).should_not be_valid - end - - it "does not report if coersion is used in binary op" do - s = Source.new %q( - "Hello, #{3.to_s + 's'}" - ) - subject.catch(s).should be_valid - end - - it "reports if coercion is used with symbol literals" do - s = Source.new %q("Hello, #{:symbol.to_s}") - subject.catch(s).should_not be_valid - end - - it "reports if coercion is used with number literals" do - s = Source.new %q("Hello, #{42.to_s}") - subject.catch(s).should_not be_valid - end - - it "reports if coercion is used with boolean literals" do - s = Source.new %q("Hello, #{false.to_s}") - subject.catch(s).should_not be_valid - end - - it "reports if coercion is used with char literals" do - s = Source.new %q("Hello, #{'t'.to_s}") - subject.catch(s).should_not be_valid - end - - it "reports redundant coercion in regex" do - s = Source.new %q( - /\w #{name.to_s}/ - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if Object#to_s is called with arguments" do - s = Source.new %q( - /\w #{name.to_s(io)}/ - ) - subject.catch(s).should be_valid - end - - it "doesn't report if Object#to_s is called without receiver" do - s = Source.new %q( - /\w #{to_s}/ - ) - subject.catch(s).should be_valid - end - - it "doesn't report if Object#to_s is called with named args" do - s = Source.new %q( - "0x#{250.to_s base: 16}" - ) - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - s = Source.new %q( - "Hello, #{name1.to_s}" - "Hello, #{name2.to_s}" - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - - issue = s.issues[0] - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:17" - issue.end_location.to_s.should eq "source.cr:1:20" - issue.message.should eq RedundantStringCoercion::MSG - - issue = s.issues[1] - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:17" - issue.end_location.to_s.should eq "source.cr:2:20" - issue.message.should eq RedundantStringCoercion::MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr deleted file mode 100644 index 494ea4eff9e8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_index_spec.cr +++ /dev/null @@ -1,163 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe RedundantWithIndex do - subject = RedundantWithIndex.new - - context "with_index" do - it "does not report if there is index argument" do - s = Source.new %( - collection.each.with_index do |e, i| - e += i - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is not index argument" do - s = Source.new %( - collection.each.with_index do |e| - e += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is underscored index argument" do - s = Source.new %( - collection.each.with_index do |e, _| - e += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is no args" do - s = Source.new %( - collection.each.with_index do - puts :nothing - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if there is no block" do - s = Source.new %( - collection.each.with_index - ) - subject.catch(s).should be_valid - end - - it "does not report if first argument is underscored" do - s = Source.new %( - collection.each.with_index do |_, i| - puts i - end - ) - subject.catch(s).should be_valid - end - - it "does not report if there are more than 2 args" do - s = Source.new %( - tup.each.with_index do |key, value, index| - puts i - end - ) - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - def valid? - collection.each.with_index do |e| - end - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:19" - issue.end_location.to_s.should eq "source.cr:2:29" - issue.message.should eq "Remove redundant with_index" - end - end - - context "each_with_index" do - it "does not report if there is index argument" do - s = Source.new %( - collection.each_with_index do |e, i| - e += i - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is not index argument" do - s = Source.new %( - collection.each_with_index do |e| - e += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is underscored index argument" do - s = Source.new %( - collection.each_with_index do |e, _| - e += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is no args" do - s = Source.new %( - collection.each_with_index do - puts :nothing - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if there is no block" do - s = Source.new %( - collection.each_with_index(1) - ) - subject.catch(s).should be_valid - end - - it "does not report if first argument is underscored" do - s = Source.new %( - collection.each_with_index do |_, i| - puts i - end - ) - subject.catch(s).should be_valid - end - - it "does not report if there are more than 2 args" do - s = Source.new %( - tup.each_with_index do |key, value, index| - puts i - end - ) - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - def valid? - collection.each_with_index do |e| - end - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:14" - issue.end_location.to_s.should eq "source.cr:2:29" - issue.message.should eq "Use each instead of each_with_index" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr deleted file mode 100644 index f6adc130ec94..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/redundant_with_object_spec.cr +++ /dev/null @@ -1,83 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe RedundantWithObject do - subject = RedundantWithObject.new - - it "does not report if there is index argument" do - s = Source.new %( - collection.each_with_object(0) do |e, obj| - obj += i - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is not index argument" do - s = Source.new %( - collection.each_with_object(0) do |e| - e += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is underscored index argument" do - s = Source.new %( - collection.each_with_object(0) do |e, _| - e += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is no args" do - s = Source.new %( - collection.each_with_object(0) do - puts :nothing - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if there is no block" do - s = Source.new %( - collection.each_with_object(0) - ) - subject.catch(s).should be_valid - end - - it "does not report if first argument is underscored" do - s = Source.new %( - collection.each_with_object(0) do |_, obj| - puts i - end - ) - subject.catch(s).should be_valid - end - - it "does not report if there are more than 2 args" do - s = Source.new %( - tup.each_with_object(0) do |key, value, obj| - puts i - end - ) - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - def valid? - collection.each_with_object(0) do |e| - end - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:14" - issue.end_location.to_s.should eq "source.cr:2:30" - issue.message.should eq "Use each instead of each_with_object" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr deleted file mode 100644 index 540f83d8cd11..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_argument_spec.cr +++ /dev/null @@ -1,166 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe ShadowedArgument do - subject = ShadowedArgument.new - - it "doesn't report if there is not a shadowed argument" do - s = Source.new %( - def foo(bar) - baz = 1 - end - - 3.times do |i| - a = 1 - end - - proc = -> (a : Int32) { - b = 2 - } - ) - subject.catch(s).should be_valid - end - - it "reports if there is a shadowed method argument" do - s = Source.new %( - def foo(bar) - bar = 1 - bar - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is a shadowed block argument" do - s = Source.new %( - 3.times do |i| - i = 2 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is a shadowed proc argument" do - s = Source.new %( - ->(x : Int32) { - x = 20 - x - } - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if the argument is referenced before the assignment" do - s = Source.new %( - def foo(bar) - bar - bar = 1 - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if the argument is conditionally reassigned" do - s = Source.new %( - def foo(bar = nil) - bar ||= true - bar - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if the op assign is followed by another assignment" do - s = Source.new %( - def foo(bar) - bar ||= 3 - bar = 43 - bar - end - ) - subject.catch(s).should be_valid - end - - it "reports if the shadowing assignment is followed by op assign" do - s = Source.new %( - def foo(bar) - bar = 42 - bar ||= 43 - bar - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if the argument is unused" do - s = Source.new %( - def foo(bar) - end - ) - subject.catch(s).should be_valid - end - - it "reports if the argument is shadowed before super" do - s = Source.new %( - def foo(bar) - bar = 1 - super - end - ) - subject.catch(s).should_not be_valid - end - - context "branch" do - it "doesn't report if the argument is not shadowed in a condition" do - s = Source.new %( - def foo(bar, baz) - bar = 1 if baz - bar - end - ) - subject.catch(s).should be_valid - end - - it "reports if the argument is shadowed after the condition" do - s = Source.new %( - def foo(foo) - if something - foo = 42 - end - foo = 43 - foo - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if the argument is conditionally assigned in a branch" do - s = Source.new %( - def foo(bar) - if something - bar ||= 22 - end - bar - end - ) - subject.catch(s).should be_valid - end - end - - it "reports rule, location and message" do - s = Source.new %( - def foo(bar) - bar = 22 - bar - end - ), "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:3" - issue.end_location.to_s.should eq "source.cr:2:10" - issue.message.should eq "Argument `bar` is assigned before it is used" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr deleted file mode 100644 index 5b7d49330b82..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowed_exception_spec.cr +++ /dev/null @@ -1,176 +0,0 @@ -require "../../../spec_helper" - -private def check_shadowed(source, exceptions) - s = Ameba::Source.new source - Ameba::Rule::Lint::ShadowedException.new.catch(s).should_not be_valid - s.issues.first.message.should contain exceptions.join(", ") -end - -module Ameba::Rule::Lint - describe ShadowedException do - subject = ShadowedException.new - - it "passes if there isn't shadowed exception" do - s = Source.new %( - def method - do_something - rescue ArgumentError - handle_argument_error_exception - rescue Exception - handle_exception - end - - def method - rescue Exception - handle_exception - end - - def method - rescue e : ArgumentError - handle_argument_error_exception - rescue e : Exception - handle_exception - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is a shadowed exception" do - check_shadowed %( - begin - do_something - rescue Exception - handle_exception - rescue ArgumentError - handle_argument_error_exception - end - ), %w(ArgumentError) - end - - it "fails if there is a custom shadowed exceptions" do - check_shadowed %( - begin - 1 - rescue Exception - 2 - rescue MySuperException - 3 - end - ), %w(MySuperException) - end - - it "fails if there is a shadowed exception in a type list" do - check_shadowed %( - begin - rescue Exception | IndexError - end - ), %w(IndexError) - end - - it "fails if there is a first shadowed exception in a type list" do - check_shadowed %( - begin - rescue IndexError | Exception - rescue Exception - rescue - end - ), %w(IndexError) - end - - it "fails if there is a shadowed duplicated exception" do - check_shadowed %( - begin - rescue IndexError - rescue ArgumentError - rescue IndexError - end - ), %w(IndexError) - end - - it "fails if there is a shadowed duplicated exception in a type list" do - check_shadowed %( - begin - rescue IndexError - rescue ArgumentError | IndexError - end - ), %w(IndexError) - end - - it "fails if there is only shadowed duplicated exceptions" do - check_shadowed %( - begin - rescue IndexError - rescue IndexError - end - ), %w(IndexError) - end - - it "fails if there is only shadowed duplicated exceptions in a type list" do - check_shadowed %( - begin - rescue IndexError | IndexError - end - ), %w(IndexError) - end - - it "fails if all rescues are shadowed and there is a catch-all rescue" do - check_shadowed %( - begin - rescue Exception - rescue ArgumentError - rescue IndexError - rescue KeyError | IO::Error - rescue - end - ), %w(IndexError KeyError IO::Error) - end - - it "fails if there are shadowed exception with args" do - check_shadowed %( - begin - rescue Exception - rescue ex : IndexError - rescue - end - ), %w(IndexError) - end - - it "fails if there are multiple shadowed exceptions" do - check_shadowed %( - begin - rescue Exception - rescue ArgumentError - rescue IndexError - end - ), %w(ArgumentError IndexError) - end - - it "fails if there are multiple shadowed exceptions in a type list" do - check_shadowed %( - begin - rescue Exception - rescue ArgumentError | IndexError - rescue IO::Error - end - ), %w(ArgumentError IndexError IO::Error) - end - - it "reports rule, location and a message" do - s = Source.new %q( - begin - do_something - rescue Exception | IndexError - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:3" - issue.end_location.to_s.should eq "source.cr:4:3" - issue.message.should eq( - "Exception handler has shadowed exceptions: IndexError" - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr deleted file mode 100644 index 2ed9112ddef4..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr +++ /dev/null @@ -1,241 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe ShadowingOuterLocalVar do - subject = ShadowingOuterLocalVar.new - - it "doesn't report if there is no shadowing" do - source = Source.new %( - def some_method - foo = 1 - - 3.times do |bar| - bar - end - - -> (baz : Int32) {} - - -> (bar : String) {} - end - ) - - subject.catch(source).should be_valid - end - - it "reports if there is a shadowing in a block" do - source = Source.new %( - def some_method - foo = 1 - - 3.times do |foo| - end - end - ) - subject.catch(source).should_not be_valid - end - - it "does not report outer vars declared below shadowed block" do - source = Source.new %( - methods = klass.methods.select { |m| m.annotation(MyAnn) } - m = methods.last - ) - subject.catch(source).should be_valid - end - - it "reports if there is a shadowing in a proc" do - source = Source.new %( - def some_method - foo = 1 - - -> (foo : Int32) {} - end - ) - subject.catch(source).should_not be_valid - end - - it "reports if there is a shadowing in an inner scope" do - source = Source.new %( - def foo - foo = 1 - - 3.times do |i| - 3.times { |foo| foo } - end - end - ) - subject.catch(source).should_not be_valid - end - - it "reports if variable is shadowed twice" do - source = Source.new %( - foo = 1 - - 3.times do |foo| - -> (foo : Int32) { foo + 1 } - end - ) - subject.catch(source).should_not be_valid - - source.issues.size.should eq 2 - end - - it "reports if a splat block argument shadows local var" do - source = Source.new %( - foo = 1 - - 3.times do |*foo| - end - ) - subject.catch(source).should_not be_valid - end - - it "reports if a &block argument is shadowed" do - source = Source.new %( - def method_with_block(a, &block) - 3.times do |block| - end - end - ) - subject.catch(source).should_not be_valid - source.issues.first.message.should eq "Shadowing outer local variable `block`" - end - - it "reports if there are multiple args and one shadows local var" do - source = Source.new %( - foo = 1 - [1, 2, 3].each_with_index do |i, foo| - i + foo - end - ) - subject.catch(source).should_not be_valid - source.issues.first.message.should eq "Shadowing outer local variable `foo`" - end - - it "doesn't report if an outer var is reassigned in a block" do - source = Source.new %( - def foo - foo = 1 - 3.times do |i| - foo = 2 - end - end - ) - subject.catch(source).should be_valid - end - - it "doesn't report if an argument is a black hole '_'" do - source = Source.new %( - _ = 1 - 3.times do |_| - end - ) - subject.catch(source).should be_valid - end - - it "doesn't report if it shadows record type declaration" do - source = Source.new %( - class FooBar - record Foo, index : String - - def bar - 3.times do |index| - end - end - end - ) - subject.catch(source).should be_valid - end - - it "doesn't report if it shadows throwaway arguments" do - source = Source.new %( - data = [{1, "a"}, {2, "b"}, {3, "c"}] - - data.each do |_, string| - data.each do |number, _| - puts string, number - end - end - ) - subject.catch(source).should be_valid - end - - it "does not report if argument shadows an ivar assignment" do - s = Source.new %( - def bar(@foo) - @foo.try do |foo| - end - end - ) - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - source = Source.new %( - foo = 1 - 3.times { |foo| foo + 1 } - ), "source.cr" - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:12" - issue.end_location.should be_nil - issue.message.should eq "Shadowing outer local variable `foo`" - end - - context "macro" do - it "does not report shadowed vars in outer scope" do - source = Source.new %( - macro included - def foo - {% for ivar in instance_vars %} - {% ann = ivar.annotation(Name) %} - {% end %} - end - - def bar - {% instance_vars.reject { |ivar| ivar } %} - end - end - ) - subject.catch(source).should be_valid - end - - it "does not report shadowed vars in macro withing the same scope" do - source = Source.new %( - {% methods = klass.methods.select { |m| m.annotation(MyAnn) } %} - - {% for m, m_idx in methods %} - {% if d = m.annotation(MyAnn) %} - {% d %} - {% end %} - {% end %} - ) - subject.catch(source).should be_valid - end - - it "does not report shadowed vars withing nested macro" do - source = Source.new %( - module Foo - macro included - def foo - {% for ann in instance_vars %} - {% pos_args = ann.args.empty? ? "Tuple.new".id : ann.args %} - {% end %} - end - - def bar - {{@type.instance_vars.map do |ivar| - ivar.annotations(Name).each do |ann| - puts ann.args - end - end}} - end - end - end - ) - subject.catch(source).should be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr deleted file mode 100644 index c1179dc36514..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/shared_var_in_fiber_spec.cr +++ /dev/null @@ -1,236 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe SharedVarInFiber do - subject = SharedVarInFiber.new - - it "doesn't report if there is only local shared var in fiber" do - s = Source.new %( - spawn do - i = 1 - puts i - end - - Fiber.yield - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is only block shared var in fiber" do - s = Source.new %( - 10.times do |i| - spawn do - puts i - end - end - - Fiber.yield - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there a spawn macro is used" do - s = Source.new %( - i = 0 - while i < 10 - spawn puts(i) - i += 1 - end - - Fiber.yield - ) - subject.catch(s).should be_valid - end - - it "reports if there is a shared var in spawn" do - s = Source.new %( - i = 0 - while i < 10 - spawn do - puts(i) - end - i += 1 - end - - Fiber.yield - ) - subject.catch(s).should_not be_valid - end - - it "reports reassigned reference to shared var in spawn" do - s = Source.new %( - channel = Channel(String).new - n = 0 - - while n < 10 - n = n + 1 - spawn do - m = n - channel.send m - end - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report reassigned reference to shared var in block" do - s = Source.new %( - channel = Channel(String).new - n = 0 - - while n < 3 - n = n + 1 - m = n - spawn do - channel.send m - end - end - ) - subject.catch(s).should be_valid - end - - it "does not report block is called in a spawn" do - s = Source.new %( - def method(block) - spawn do - block.call(10) - end - end - ) - subject.catch(s).should be_valid - end - - it "reports multiple shared variables in spawn" do - s = Source.new %( - foo, bar, baz = 0, 0, 0 - while foo < 10 - baz += 1 - spawn do - puts foo - puts foo + bar + baz - end - foo += 1 - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 3 - s.issues[0].location.to_s.should eq ":5:10" - s.issues[0].end_location.to_s.should eq ":5:12" - s.issues[0].message.should eq "Shared variable `foo` is used in fiber" - - s.issues[1].location.to_s.should eq ":6:10" - s.issues[1].end_location.to_s.should eq ":6:12" - s.issues[1].message.should eq "Shared variable `foo` is used in fiber" - - s.issues[2].location.to_s.should eq ":6:22" - s.issues[2].end_location.to_s.should eq ":6:24" - s.issues[2].message.should eq "Shared variable `baz` is used in fiber" - end - - it "doesn't report if variable is passed to the proc" do - s = Source.new %( - i = 0 - while i < 10 - proc = ->(x : Int32) do - spawn do - puts(x) - end - end - proc.call(i) - i += 1 - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if a channel is declared in outer scope" do - s = Source.new %( - channel = Channel(Nil).new - spawn { channel.send(nil) } - channel.receive - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is a loop in spawn" do - s = Source.new %( - channel = Channel(String).new - - spawn do - server = TCPServer.new("0.0.0.0", 8080) - socket = server.accept - while line = socket.gets - channel.send(line) - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if a var is mutated in spawn and referenced outside" do - s = Source.new %( - def method - foo = 1 - spawn { foo = 2 } - foo - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if variable is changed without iterations" do - s = Source.new %( - def foo - i = 0 - i += 1 - spawn { i } - end - ), "source.cr" - - subject.catch(s).should be_valid - end - - it "doesn't report if variable is in a loop inside spawn" do - s = Source.new %( - i = 0 - spawn do - while i < 10 - i += 1 - end - end - ) - - subject.catch(s).should be_valid - end - - it "doesn't report if variable declared inside loop" do - s = Source.new %( - while true - i = 0 - spawn { i += 1 } - end - ) - - subject.catch(s).should be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - i = 0 - while true - i += 1 - spawn { i } - end - ), "source.cr" - - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:4:11" - issue.end_location.to_s.should eq "source.cr:4:11" - issue.message.should eq "Shared variable `i` is used in fiber" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr deleted file mode 100644 index f7402e760698..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/syntax_spec.cr +++ /dev/null @@ -1,41 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe Syntax do - subject = Syntax.new - - it "passes if there is no invalid syntax" do - s = Source.new %( - def hello - puts "totally valid" - rescue e: Exception - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is an invalid syntax" do - s = Source.new %( - def hello - puts "invalid" - rescue Exception => e - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, location and message" do - s = Source.new "def hello end", "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.message.should eq "unexpected token: end (expected ';' or newline)" - end - - it "has highest severity" do - subject.severity.should eq Severity::Error - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr deleted file mode 100644 index cc21a00f35db..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unneded_disable_directive_spec.cr +++ /dev/null @@ -1,102 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe UnneededDisableDirective do - subject = UnneededDisableDirective.new - - it "passes if there are no comments" do - s = Source.new %( - a = 1 - ) - subject.catch(s).should be_valid - end - - it "passes if there is disable directive" do - s = Source.new %( - a = 1 # my super var - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is disable directive and it is needed" do - s = Source.new %Q( - # ameba:disable #{NamedRule.name} - a = 1 - ) - s.add_issue NamedRule.new, location: {2, 1}, - message: "Useless assignment", status: :disabled - subject.catch(s).should be_valid - end - - it "passes if there is inline disable directive and it is needed" do - s = Source.new %Q( - a = 1 # ameba:disable #{NamedRule.name} - ) - s.add_issue NamedRule.new, location: {1, 1}, - message: "Alarm!", status: :disabled - subject.catch(s).should be_valid - end - - it "ignores commented out disable directive" do - s = Source.new %Q( - # # ameba:disable #{NamedRule.name} - a = 1 - ) - s.add_issue NamedRule.new, location: {3, 1}, - message: "Alarm!", status: :disabled - subject.catch(s).should be_valid - end - - it "fails if there is unneeded directive" do - s = Source.new %Q( - # ameba:disable #{NamedRule.name} - a = 1 - ) - subject.catch(s).should_not be_valid - s.issues.first.message.should eq( - "Unnecessary disabling of #{NamedRule.name}" - ) - end - - it "fails if there is inline unneeded directive" do - s = Source.new %Q(a = 1 # ameba:disable #{NamedRule.name}) - subject.catch(s).should_not be_valid - s.issues.first.message.should eq( - "Unnecessary disabling of #{NamedRule.name}" - ) - end - - it "detects mixed inline directives" do - s = Source.new %Q( - # ameba:disable Rule1, Rule2 - a = 1 # ameba:disable Rule3 - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.message.should contain "Rule1, Rule2" - s.issues.last.message.should contain "Rule3" - end - - it "fails if there is disabled UnneededDisableDirective" do - s = Source.new %Q( - # ameba:disable #{UnneededDisableDirective.rule_name} - a = 1 - ), "source.cr" - s.add_issue UnneededDisableDirective.new, location: {3, 1}, - message: "Alarm!", status: :disabled - subject.catch(s).should_not be_valid - end - - it "reports issue, location and message" do - s = Source.new %Q( - # ameba:disable Rule1, Rule2 - a = 1 - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.message.should eq "Unnecessary disabling of Rule1, Rule2" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr deleted file mode 100644 index 5e50d05fca04..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unreachable_code_spec.cr +++ /dev/null @@ -1,701 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = UnreachableCode.new - - describe UnreachableCode do - context "return" do - it "reports if there is unreachable code after return" do - s = Source.new %( - def foo - a = 1 - return false - b = 2 - end - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":4:3" - end - - it "doesn't report if there is return in if" do - s = Source.new %( - def foo - a = 1 - return false if bar - b = 2 - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there are returns in if-then-else" do - s = Source.new %( - if a > 0 - return :positive - else - return :negative - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is no else in if" do - s = Source.new %( - if a > 0 - return :positive - end - :reachable - ) - subject.catch(s).should be_valid - end - - it "doesn't report return in on-line if" do - s = Source.new %( - return :positive if a > 0 - ) - subject.catch(s).should be_valid - end - - it "doesn't report if return is used in a block" do - s = Source.new %( - def foo - bar = obj.try do - if something - a = 1 - end - return nil - end - - bar - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is unreachable code after if-then-else" do - s = Source.new %( - def foo - if a > 0 - return :positive - else - return :negative - end - - :unreachable - end - ) - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.location.to_s.should eq ":8:3" - end - - it "reports if there is unreachable code after if-then-else-if" do - s = Source.new %( - def foo - if a > 0 - return :positive - elsif a != 0 - return :negative - else - return :zero - end - - :unreachable - end - ) - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.location.to_s.should eq ":10:3" - end - - it "doesn't report if there is no unreachable code after if-then-else" do - s = Source.new %( - def foo - if a > 0 - return :positive - else - return :negative - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is no unreachable in inner branch" do - s = Source.new %( - def foo - if a > 0 - return :positive if a != 1 - else - return :negative - end - - :not_unreachable - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is no unreachable in exception handler" do - s = Source.new %( - def foo - puts :bar - rescue Exception - raise "Error!" - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is multiple conditions with return" do - s = Source.new %( - if :foo - if :bar - return :foobar - else - return :foobaz - end - elsif :fox - return :foofox - end - - return :reachable - ) - subject.catch(s).should be_valid - end - - it "reports if there is unreachable code after unless" do - s = Source.new %( - unless :foo - return :bar - else - return :foo - end - - :unreachable - ) - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.location.to_s.should eq ":7:1" - end - - it "doesn't report if there is no unreachable code after unless" do - s = Source.new %( - unless :foo - return :bar - end - - :reachable - ) - subject.catch(s).should be_valid - end - end - - context "binary op" do - it "reports unreachable code in a binary operator" do - s = Source.new %( - (return 22) && puts "a" - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":1:16" - end - - it "reports unreachable code in inner binary operator" do - s = Source.new %( - do_something || (return 22) && puts "a" - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":1:32" - end - - it "reports unreachable code after the binary op" do - s = Source.new %( - (return 22) && break - :unreachable - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":2:1" - end - - it "doesn't report if return is not the right" do - s = Source.new %( - puts "a" && return - ) - subject.catch(s).should be_valid - end - - it "doesn't report unreachable code in multiple binary expressions" do - s = Source.new %( - foo || bar || baz - ) - subject.catch(s).should be_valid - end - end - - context "case" do - it "reports if there is unreachable code after case" do - s = Source.new %( - def foo - case cond - when 1 - something - return - when 2 - something2 - return - else - something3 - return - end - :unreachable - end - ) - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.location.to_s.should eq ":13:3" - end - - it "doesn't report if case does not have else" do - s = Source.new %( - def foo - case cond - when 1 - something - return - when 2 - something2 - return - end - :reachable - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if one when does not return" do - s = Source.new %( - def foo - case cond - when 1 - something - return - when 2 - something2 - else - something3 - return - end - :reachable - end - ) - subject.catch(s).should be_valid - end - end - - context "exception handler" do - it "reports unreachable code if it returns in body and rescues" do - s = Source.new %( - def foo - begin - return false - rescue Error - return false - rescue Exception - return false - end - :unreachable - end - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":9:3" - end - - it "reports unreachable code if it returns in rescues and else" do - s = Source.new %( - def foo - begin - do_something - rescue Error - return :error - else - return true - end - :unreachable - end - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":9:3" - end - - it "doesn't report if there is no else and ensure doesn't return" do - s = Source.new %( - def foo - begin - return false - rescue Error - puts "error" - rescue Exception - return false - end - :reachable - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is no else and body doesn't return" do - s = Source.new %( - def foo - begin - do_something - rescue Error - return true - rescue Exception - return false - end - :reachable - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is else and ensure doesn't return" do - s = Source.new %( - def foo - begin - do_something - rescue Error - puts "yo" - else - return true - end - :reachable - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there is else and it doesn't return" do - s = Source.new %( - def foo - begin - do_something - rescue Error - return false - else - puts "yo" - end - :reachable - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is unreachable code in rescue" do - s = Source.new %( - def method - rescue - return 22 - :unreachable - end - ) - - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":4:3" - end - end - - context "while/until" do - it "reports if there is unreachable code after while" do - s = Source.new %( - def method - while something - if :foo - return :foo - else - return :foobar - end - end - :unreachable - end - ) - - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":9:3" - end - - it "reports if there is unreachable code after until" do - s = Source.new %( - def method - until something - if :foo - return :foo - else - return :foobar - end - end - :unreachable - end - ) - - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":9:3" - end - - it "doesn't report if there is reachable code after while with break" do - s = Source.new %( - while something - break - end - :reachable - ) - - subject.catch(s).should be_valid - end - end - - context "rescue" do - it "reports unreachable code in rescue" do - s = Source.new %( - begin - - rescue e - raise e - :unreachable - end - ) - - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":5:3" - end - - it "doesn't report if there is no unreachable code in rescue" do - s = Source.new %( - begin - - rescue e - raise e - end - ) - - subject.catch(s).should be_valid - end - end - - context "when" do - it "reports unreachable code in when" do - s = Source.new %( - case - when valid? - return 22 - :unreachable - else - - end - ) - - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":4:3" - end - - it "doesn't report if there is no unreachable code in when" do - s = Source.new %( - case - when valid? - return 22 - else - end - ) - - subject.catch(s).should be_valid - end - end - - context "break" do - it "reports if there is unreachable code after break" do - s = Source.new %( - def foo - loop do - break - a = 1 - end - end - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":4:5" - end - - it "doesn't report if break is in a condition" do - s = Source.new %( - a = -100 - while true - break if a > 0 - a += 1 - end - ) - subject.catch(s).should be_valid - end - end - - context "next" do - it "reports if there is unreachable code after next" do - s = Source.new %( - a = 1 - while a < 5 - next - puts a - end - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":4:3" - end - - it "doesn't report if next is in a condition" do - s = Source.new %( - a = 1 - while a < 5 - if a == 3 - next - end - puts a - end - ) - subject.catch(s).should be_valid - end - end - - context "raise" do - it "reports if there is unreachable code after raise" do - s = Source.new %( - a = 1 - raise "exception" - b = 2 - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":3:1" - end - - it "doesn't report if raise is in a condition" do - s = Source.new %( - a = 1 - raise "exception" if a > 0 - b = 2 - ) - subject.catch(s).should be_valid - end - end - - context "exit" do - it "reports if there is unreachable code after exit without args" do - s = Source.new %( - a = 1 - exit - b = 2 - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":3:1" - end - - it "reports if there is unreachable code after exit with exit code" do - s = Source.new %( - a = 1 - exit 1 - b = 2 - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":3:1" - end - - it "doesn't report if exit is in a condition" do - s = Source.new %( - a = 1 - exit if a > 0 - b = 2 - ) - subject.catch(s).should be_valid - end - end - - context "abort" do - it "reports if there is unreachable code after abort with one argument" do - s = Source.new %( - a = 1 - abort "abort" - b = 2 - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":3:1" - end - - it "reports if there is unreachable code after abort with two args" do - s = Source.new %( - a = 1 - abort "abort", 1 - b = 2 - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":3:1" - end - - it "doesn't report if abort is in a condition" do - s = Source.new %( - a = 1 - abort "abort" if a > 0 - b = 2 - ) - subject.catch(s).should be_valid - end - end - - it "reports message, rule, location" do - s = Source.new %( - return - :unreachable - ), "source.cr" - - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:1" - issue.end_location.to_s.should eq "source.cr:2:12" - issue.message.should eq "Unreachable code detected" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr deleted file mode 100644 index 69bac6ea6e04..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/unused_argument_spec.cr +++ /dev/null @@ -1,306 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = UnusedArgument.new - subject.ignore_defs = false - - describe UnusedArgument do - it "doesn't report if arguments are used" do - s = Source.new %( - def method(a, b, c) - a + b + c - end - - 3.times do |i| - i + 1 - end - - ->(i : Int32) { i + 1 } - ) - subject.catch(s).should be_valid - end - - it "reports if method argument is unused" do - s = Source.new %( - def method(a, b, c) - a + b - end - ) - subject.catch(s).should_not be_valid - s.issues.first.message.should eq "Unused argument `c`. If it's necessary, use `_c` " \ - "as an argument name to indicate that it won't be used." - end - - it "reports if block argument is unused" do - s = Source.new %( - [1,2].each_with_index do |a, i| - a - end - ) - subject.catch(s).should_not be_valid - s.issues.first.message.should eq "Unused argument `i`. If it's necessary, use `_` " \ - "as an argument name to indicate that it won't be used." - end - - it "reports if proc argument is unused" do - s = Source.new %( - -> (a : Int32, b : String) do - a = a + 1 - end - ) - subject.catch(s).should_not be_valid - s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \ - "as an argument name to indicate that it won't be used." - end - - it "reports multiple unused args" do - s = Source.new %( - def method(a, b, c) - nil - end - ) - subject.catch(s).should_not be_valid - s.issues[0].message.should eq "Unused argument `a`. If it's necessary, use `_a` " \ - "as an argument name to indicate that it won't be used." - s.issues[1].message.should eq "Unused argument `b`. If it's necessary, use `_b` " \ - "as an argument name to indicate that it won't be used." - s.issues[2].message.should eq "Unused argument `c`. If it's necessary, use `_c` " \ - "as an argument name to indicate that it won't be used." - end - - it "doesn't report if it is an instance var argument" do - s = Source.new %( - class A - def method(@name) - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if a typed argument is used" do - s = Source.new %( - def method(x : Int32) - 3.times do - puts x - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if an argument with default value is used" do - s = Source.new %( - def method(x = 1) - puts x - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if argument starts with a _" do - s = Source.new %( - def method(_x) - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if it is a block and used" do - s = Source.new %( - def method(&block) - block.call - end - ) - subject.catch(s).should be_valid - end - - it "reports if block arg is not used" do - s = Source.new %( - def method(&block) - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if unused and there is yield" do - s = Source.new %( - def method(&block) - yield 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if variable is referenced implicitly" do - s = Source.new %( - class Bar < Foo - def method(a, b) - super - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if arg if referenced in case" do - s = Source.new %( - def foo(a) - case a - when /foo/ - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if enum in a record" do - s = Source.new %( - class Class - record Record do - enum Enum - CONSTANT - end - end - end - ) - subject.catch(s).should be_valid - end - - context "super" do - it "reports if variable is not referenced implicitly by super" do - s = Source.new %( - class Bar < Foo - def method(a, b) - super a - end - end - ) - subject.catch(s).should_not be_valid - s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \ - "as an argument name to indicate that it won't be used." - end - - it "reports rule, location and message" do - s = Source.new %( - def method(a) - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.message.should eq "Unused argument `a`. If it's necessary, use `_a` " \ - "as an argument name to indicate that it won't be used." - issue.location.to_s.should eq "source.cr:1:12" - end - end - - context "macro" do - it "doesn't report if it is a used macro argument" do - s = Source.new %( - macro my_macro(arg) - {% arg %} - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if it is a used macro block argument" do - s = Source.new %( - macro my_macro(&block) - {% block %} - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report used macro args with equal names in record" do - s = Source.new %( - record X do - macro foo(a, b) - {{a}} + {{b}} - end - - macro bar(a, b, c) - {{a}} + {{b}} + {{c}} - end - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report used args in macro literals" do - s = Source.new %( - def print(f : Array(U)) forall U - f.size.times do |i| - {% if U == Float64 %} - puts f[i].round(3) - {% else %} - puts f[i] - {% end %} - end - end - ) - subject.catch(s).should be_valid - end - end - - context "properties" do - describe "#ignore_defs" do - it "lets the rule to ignore def scopes if true" do - subject.ignore_defs = true - s = Source.new %( - def method(a) - end - ) - subject.catch(s).should be_valid - end - - it "lets the rule not to ignore def scopes if false" do - subject.ignore_defs = false - s = Source.new %( - def method(a) - end - ) - subject.catch(s).should_not be_valid - end - end - - context "#ignore_blocks" do - it "lets the rule to ignore block scopes if true" do - subject.ignore_blocks = true - s = Source.new %( - 3.times { |i| puts "yo!" } - ) - subject.catch(s).should be_valid - end - - it "lets the rule not to ignore block scopes if false" do - subject.ignore_blocks = false - s = Source.new %( - 3.times { |i| puts "yo!" } - ) - subject.catch(s).should_not be_valid - end - end - - context "#ignore_procs" do - it "lets the rule to ignore proc scopes if true" do - subject.ignore_procs = true - s = Source.new %( - ->(a : Int32) {} - ) - subject.catch(s).should be_valid - end - - it "lets the rule not to ignore proc scopes if false" do - subject.ignore_procs = false - s = Source.new %( - ->(a : Int32) {} - ) - subject.catch(s).should_not be_valid - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr deleted file mode 100644 index bd5fdb2dddb3..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_assign_spec.cr +++ /dev/null @@ -1,977 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - describe UselessAssign do - subject = UselessAssign.new - - it "does not report used assigments" do - s = Source.new %( - def method - a = 2 - a - end - ) - subject.catch(s).should be_valid - end - - it "reports a useless assignment in a method" do - s = Source.new %( - def method - a = 2 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports a useless assignment in a proc" do - s = Source.new %( - ->() { - a = 2 - } - ) - subject.catch(s).should_not be_valid - end - - it "reports a useless assignment in a block" do - s = Source.new %( - def method - 3.times do - a = 1 - end - end - ) - subject.catch(s).should_not be_valid - end - - it "reports a useless assignment in a proc inside def" do - s = Source.new %( - def method - ->() { - a = 2 - } - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report ignored assigments" do - s = Source.new %( - payload, _header = decode - puts payload - ) - subject.catch(s).should be_valid - end - - it "reports a useless assignment in a proc inside a block" do - s = Source.new %( - def method - 3.times do - ->() { - a = 2 - } - end - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, position and a message" do - s = Source.new %( - def method - a = 2 - end - ), "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:3" - issue.message.should eq "Useless assignment to variable `a`" - end - - it "does not report useless assignment of instance var" do - s = Source.new %( - class Cls - def initialize(@name) - end - end - ) - subject.catch(s).should be_valid - end - - it "does not report if assignment used in the inner block scope" do - s = Source.new %( - def method - var = true - 3.times { var = false } - end - ) - subject.catch(s).should be_valid - end - - it "reports if assigned is not referenced in the inner block scope" do - s = Source.new %( - def method - var = true - 3.times {} - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if assignment in referenced in inner block" do - s = Source.new %( - def method - two = true - - 3.times do - mutex.synchronize do - two = 2 - end - end - - two.should be_true - end - ) - subject.catch(s).should be_valid - end - - it "reports if first assignment is useless" do - s = Source.new %( - def method - var = true - var = false - var - end - ) - subject.catch(s).should_not be_valid - s.issues.first.location.to_s.should eq ":2:3" - end - - it "reports if variable reassigned and not used" do - s = Source.new %( - def method - var = true - var = false - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if variable used in a condition" do - s = Source.new %( - def method - a = 1 - if a - nil - end - end - ) - subject.catch(s).should be_valid - end - - it "reports second assignment as useless" do - s = Source.new %( - def method - a = 1 - a = a + 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if variable is referenced in other assignment" do - s = Source.new %( - def method - if f = get_something - @f = f - end - end - ) - subject.catch(s).should be_valid - end - - it "does not report if variable is referenced in a setter" do - s = Source.new %( - def method - foo = 2 - table[foo] ||= "bar" - end - ) - subject.catch(s).should be_valid - end - - it "does not report if variable is reassigned but not referenced" do - s = Source.new %( - def method - foo = 1 - puts foo - foo = 2 - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if variable is referenced in a call" do - s = Source.new %( - def method - if f = FORMATTER - @formatter = f.new - end - end - ) - subject.catch(s).should be_valid - end - - it "does not report if a setter is invoked with operator assignment" do - s = Source.new %( - def method - obj = {} of Symbol => Int32 - obj[:name] = 3 - end - ) - subject.catch(s).should be_valid - end - - context "when transformed" do - it "does not report if the first arg is transformed and not used" do - s = Source.new %( - collection.each do |(a, b)| - puts b - end - ) - subject.catch(s).should be_valid - end - - it "does not report if the second arg is transformed and not used" do - s = Source.new %( - collection.each do |(a, b)| - puts a - end - ) - subject.catch(s).should be_valid - end - - it "does not report if all transformed args are not used in a block" do - s = Source.new %( - collection.each do |(foo, bar), (baz, _qux), index, object| - end - ) - subject.catch(s).should be_valid - end - end - - it "does not report if global var" do - s = Source.new %( - def method - $1 = 3 - end - ) - subject.catch(s).should be_valid - end - - it "does not report if assignment is referenced in a proc" do - s = Source.new %( - def method - called = false - ->() { called = true } - called - end - ) - subject.catch(s).should be_valid - end - - it "reports if variable is shadowed in inner scope" do - s = Source.new %( - def method - i = 1 - 3.times do |i| - i + 1 - end - end - ) - subject.catch(s).should_not be_valid - end - - it "does not report if parameter is referenced after the branch" do - s = Source.new %( - def method(param) - 3.times do - param = 3 - end - param - end - ) - subject.catch(s).should be_valid - end - - context "op assigns" do - it "does not report if variable is referenced below the op assign" do - s = Source.new %( - def method - a = 1 - a += 1 - a - end - ) - subject.catch(s).should be_valid - end - - it "does not report if variable is referenced in op assign few times" do - s = Source.new %( - def method - a = 1 - a += 1 - a += 1 - a = a + 1 - a - end - ) - subject.catch(s).should be_valid - end - - it "reports if variable is not referenced below the op assign" do - s = Source.new %( - def method - a = 1 - a += 1 - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, location and a message" do - s = Source.new %( - def method - b = 2 - a = 3 - a += 1 - end - ), "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.last - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:4:3" - issue.message.should eq "Useless assignment to variable `a`" - end - end - - context "multi assigns" do - it "does not report if all assigns are referenced" do - s = Source.new %( - def method - a, b = {1, 2} - a + b - end - ) - subject.catch(s).should be_valid - end - - it "reports if one assign is not referenced" do - s = Source.new %( - def method - a, b = {1, 2} - a - end - ) - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.location.to_s.should eq ":2:6" - issue.message.should eq "Useless assignment to variable `b`" - end - - it "reports if both assigns are reassigned and useless" do - s = Source.new %( - def method - a, b = {1, 2} - a, b = {3, 4} - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if both assigns are not referenced" do - s = Source.new %( - def method - a, b = {1, 2} - end - ) - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.location.to_s.should eq ":2:3" - issue.message.should eq "Useless assignment to variable `a`" - - issue = s.issues.last - issue.location.to_s.should eq ":2:6" - issue.message.should eq "Useless assignment to variable `b`" - end - end - - context "top level" do - it "reports if assignment is not referenced" do - s = Source.new %( - a = 1 - a = 2 - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":1:1" - s.issues.last.location.to_s.should eq ":2:1" - end - - it "doesn't report if assignments are referenced" do - s = Source.new %( - a = 1 - a += 1 - a - - b, c = {1, 2} - b - c - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is captured by block" do - s = Source.new %( - a = 1 - - 3.times do - a = 2 - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment initialized and captured by block" do - s = Source.new %( - a : String? = nil - - 1.times do - a = "Fotis" - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if this is a record declaration" do - s = Source.new %( - record Foo, foo = "foo" - ) - subject.catch(s).should be_valid - end - - it "does not report if assignment is referenced after the record declaration" do - s = Source.new %( - foo = 2 - record Bar, foo = 3 # foo = 3 is not parsed as assignment - puts foo - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is not referenced after the record declaration" do - s = Source.new %( - foo = 2 - record Bar, foo = 3 - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.location.to_s.should eq "source.cr:1:1" - issue.message.should eq "Useless assignment to variable `foo`" - end - end - - context "branching" do - context "if-then-else" do - it "doesn't report if assignment is consumed by branches" do - s = Source.new %( - def method - a = 0 - if something - a = 1 - else - a = 2 - end - a - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is in one branch" do - s = Source.new %( - def method - a = 0 - if something - a = 1 - else - nil - end - a - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is in one line branch" do - s = Source.new %( - def method - a = 0 - a = 1 if something - a - end - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is useless in the branch" do - s = Source.new %( - def method(a) - if a - a = 2 - end - end - ) - subject.catch(s).should_not be_valid - end - - it "reports if only last assignment is referenced in a branch" do - s = Source.new %( - def method(a) - a = 1 - if a - a = 2 - a = 3 - end - a - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":4:5" - end - - it "does not report of assignments are referenced in all branches" do - s = Source.new %( - def method - if matches - matches = owner.lookup_matches signature - else - matches = owner.lookup_matches signature - end - - matches - end - ) - subject.catch(s).should be_valid - end - - it "does not report referenced assignments in inner branches" do - s = Source.new %( - def method - has_newline = false - - if something - do_something unless false - has_newline = false - else - do_something if true - has_newline = true - end - - has_newline - end - ) - subject.catch(s).should be_valid - end - end - - context "unless-then-else" do - it "doesn't report if assignment is consumed by branches" do - s = Source.new %( - def method - a = 0 - unless something - a = 1 - else - a = 2 - end - a - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is a useless assignment in a branch" do - s = Source.new %( - def method - a = 0 - unless something - a = 1 - a = 2 - else - a = 2 - end - a - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":4:5" - end - end - - context "case" do - it "does not report if assignment is referenced" do - s = Source.new %( - def method(a) - case a - when /foo/ - a = 1 - when /bar/ - a = 2 - end - puts a - end - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is useless" do - s = Source.new %( - def method(a) - case a - when /foo/ - a = 1 - when /bar/ - a = 2 - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":4:5" - s.issues.last.location.to_s.should eq ":6:5" - end - - it "doesn't report if assignment is referenced in cond" do - s = Source.new %( - def method - a = 2 - case a - when /foo/ - end - end - ) - subject.catch(s).should be_valid - end - end - - context "binary operator" do - it "does not report if assignment is referenced" do - s = Source.new %( - def method(a) - (a = 1) && (b = 1) - a + b - end - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is useless" do - s = Source.new %( - def method(a) - (a = 1) || (b = 1) - a - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:15" - end - end - - context "while" do - it "does not report if assignment is referenced" do - s = Source.new %( - def method(a) - while a < 10 - a = a + 1 - end - a - end - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is useless" do - s = Source.new %( - def method(a) - while a < 10 - b = a - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":3:5" - end - - it "does not report if assignment is referenced in a loop" do - s = Source.new %( - def method - a = 3 - result = 0 - - while result < 10 - result += a - a = a + 1 - end - result - end - ) - subject.catch(s).should be_valid - end - - it "does not report if assignment is referenced as param in a loop" do - s = Source.new %( - def method(a) - result = 0 - - while result < 10 - result += a - a = a + 1 - end - result - end - ) - subject.catch(s).should be_valid - end - - it "does not report if assignment is referenced in loop and inner branch" do - s = Source.new %( - def method(a) - result = 0 - - while result < 10 - result += a - if result > 0 - a = a + 1 - else - a = 3 - end - end - result - end - ) - subject.catch(s).should be_valid - end - - it "works properly if there is branch with blank node" do - s = Source.new %( - def visit - count = 0 - while true - break if count == 1 - case something - when :any - else - :anything_else - end - count += 1 - end - end - ) - subject.catch(s).should be_valid - end - end - - context "until" do - it "does not report if assignment is referenced" do - s = Source.new %( - def method(a) - until a > 10 - a = a + 1 - end - a - end - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is useless" do - s = Source.new %( - def method(a) - until a > 10 - b = a + 1 - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":3:5" - end - end - - context "exception handler" do - it "does not report if assignment is referenced in body" do - s = Source.new %( - def method(a) - a = 2 - rescue - a - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is referenced in ensure" do - s = Source.new %( - def method(a) - a = 2 - ensure - a - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is referenced in else" do - s = Source.new %( - def method(a) - a = 2 - rescue - else - a - end - ) - subject.catch(s).should be_valid - end - - it "reports if assignment is useless" do - s = Source.new %( - def method(a) - rescue - a = 2 - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":3:3" - end - end - end - - context "typeof" do - it "reports useless assigments in typeof" do - s = Source.new %( - typeof(begin - foo = 1 - bar = 2 - end) - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":2:3" - s.issues.first.message.should eq "Useless assignment to variable `foo`" - - s.issues.last.location.to_s.should eq ":3:3" - s.issues.last.message.should eq "Useless assignment to variable `bar`" - end - end - - context "macro" do - it "doesn't report if assignment is referenced in macro" do - s = Source.new %( - def method - a = 2 - {% if flag?(:bits64) %} - a.to_s - {% else %} - a - {% end %} - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report referenced assignments in macro literal" do - s = Source.new %( - def method - a = 2 - {% if flag?(:bits64) %} - a = 3 - {% else %} - a = 4 - {% end %} - puts a - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is referenced in macro def" do - s = Source.new %( - macro macro_call - puts x - end - - def foo - x = 1 - macro_call - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if assignment is referenced in a macro below" do - s = Source.new %( - class Foo - def foo - a = 1 - macro_call - end - - macro macro_call - puts a - end - end - ) - subject.catch(s).should be_valid - end - end - - context "uninitialized" do - it "reports if uninitialized assignment is not referenced at a top level" do - s = Source.new %( - a = uninitialized U - ) - subject.catch(s).should_not be_valid - end - - it "reports if uninitialized assignment is not referenced in a method" do - s = Source.new %( - def foo - a = uninitialized U - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if uninitialized assignment is referenced" do - s = Source.new %( - def foo - a = uninitialized U - a - end - ) - subject.catch(s).should be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr deleted file mode 100644 index 3dadc3bb0033..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/lint/useless_condition_in_when_spec.cr +++ /dev/null @@ -1,46 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Lint - subject = UselessConditionInWhen.new - - describe UselessConditionInWhen do - it "passes if there is not useless condition" do - s = Source.new %( - case - when utc? - io << " UTC" - when local? - Format.new(" %:z").format(self, io) if utc? - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is useless if condition" do - s = Source.new %( - case - when utc? - io << " UTC" if utc? - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - case - when String - puts "hello" - when can_generate? - generate if can_generate? - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:5:15" - issue.end_location.to_s.should eq "source.cr:5:27" - issue.message.should eq "Useless condition in when detected" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr deleted file mode 100644 index bc81fc7d7f6c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/metrics/cyclomatic_complexity_spec.cr +++ /dev/null @@ -1,47 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Metrics - subject = CyclomaticComplexity.new - complex_method = <<-CODE - def hello(a, b, c) - if a && b && c - begin - while true - return if false && b - end - "" - rescue - "" - end - end - end - CODE - - describe CyclomaticComplexity do - it "passes for empty methods" do - source = Source.new %( - def hello - end - ) - subject.catch(source).should be_valid - end - - it "reports one issue for a complex method" do - subject.max_complexity = 5 - source = Source.new(complex_method, "source.cr") - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.rule.should eq subject - issue.location.to_s.should eq "source.cr:1:5" - issue.end_location.to_s.should eq "source.cr:1:10" - issue.message.should eq "Cyclomatic complexity too high [8/5]" - end - - it "doesn't report an issue for an increased threshold" do - subject.max_complexity = 100 - source = Source.new(complex_method, "source.cr") - subject.catch(source).should be_valid - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr deleted file mode 100644 index d78722c2b959..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/any_after_filter_spec.cr +++ /dev/null @@ -1,73 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Performance - subject = AnyAfterFilter.new - - describe AnyAfterFilter do - it "passes if there is no potential performance improvements" do - source = Source.new %( - [1, 2, 3].select { |e| e > 1 }.any?(&.zero?) - [1, 2, 3].reject { |e| e > 1 }.any?(&.zero?) - [1, 2, 3].select { |e| e > 1 } - [1, 2, 3].reject { |e| e > 1 } - [1, 2, 3].any? { |e| e > 1 } - ) - subject.catch(source).should be_valid - end - - it "reports if there is select followed by any? without a block" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.any? - ) - subject.catch(source).should_not be_valid - end - - it "reports if there is reject followed by any? without a block" do - source = Source.new %( - [1, 2, 3].reject { |e| e > 2 }.any? - ) - subject.catch(source).should_not be_valid - end - - it "does not report if any? calls contains a block" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.any?(&.zero?) - [1, 2, 3].reject { |e| e > 2 }.any?(&.zero?) - ) - subject.catch(source).should be_valid - end - - context "properties" do - it "allows to configure object_call_names" do - source = Source.new %( - [1, 2, 3].reject { |e| e > 2 }.any? - ) - rule = Rule::Performance::AnyAfterFilter.new - rule.filter_names = %w(select) - rule.catch(source).should be_valid - end - end - - context "macro" do - it "reports in macro scope" do - source = Source.new %( - {{ [1, 2, 3].reject { |e| e > 2 }.any? }} - ) - subject.catch(source).should_not be_valid - end - end - - it "reports rule, pos and message" do - s = Source.new %( - [1, 2, 3].reject { |e| e > 2 }.any? - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:36" - issue.message.should eq "Use `any? {...}` instead of `reject {...}.any?`" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr deleted file mode 100644 index d3320ccb2a33..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/first_last_after_filter_spec.cr +++ /dev/null @@ -1,141 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Performance - subject = FirstLastAfterFilter.new - - describe FirstLastAfterFilter do - it "passes if there is no potential performance improvements" do - source = Source.new %( - [1, 2, 3].select { |e| e > 1 } - [1, 2, 3].reverse.select { |e| e > 1 } - [1, 2, 3].reverse.last - [1, 2, 3].reverse.first - [1, 2, 3].reverse.first - ) - subject.catch(source).should be_valid - end - - it "reports if there is select followed by last" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.last - ) - subject.catch(source).should_not be_valid - end - - it "reports if there is select followed by last?" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.last? - ) - subject.catch(source).should_not be_valid - end - - it "reports if there is select followed by first" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.first - ) - subject.catch(source).should_not be_valid - end - - it "does not report if there is selected followed by first with arguments" do - source = Source.new %( - [1, 2, 3].select { |n| n % 2 == 0 }.first(2) - ) - subject.catch(source).should be_valid - end - - it "reports if there is select followed by first?" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.first? - ) - subject.catch(source).should_not be_valid - end - - it "does not report if there is select followed by any other call" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.size - [1, 2, 3].select { |e| e > 2 }.any? - ) - subject.catch(source).should be_valid - end - - context "properties" do - it "allows to configure object_call_names" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.first - ) - rule = Rule::Performance::FirstLastAfterFilter.new - rule.filter_names = %w(reject) - rule.catch(source).should be_valid - end - end - - it "reports rule, pos and message" do - s = Source.new %( - [1, 2, 3].select { |e| e > 2 }.first - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:37" - - issue.message.should eq "Use `find {...}` instead of `select {...}.first`" - end - - it "reports a correct message for first?" do - s = Source.new %( - [1, 2, 3].select { |e| e > 2 }.first? - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:38" - - issue.message.should eq "Use `find {...}` instead of `select {...}.first?`" - end - - it "reports rule, pos and reverse message" do - s = Source.new %( - [1, 2, 3].select { |e| e > 2 }.last - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:36" - - issue.message.should eq "Use `reverse_each.find {...}` instead of `select {...}.last`" - end - - context "macro" do - it "doesn't report in macro scope" do - source = Source.new %( - {{[1, 2, 3].select { |e| e > 2 }.last }} - ) - subject.catch(source).should be_valid - end - end - - it "reports a correct message for last?" do - s = Source.new %( - [1, 2, 3].select { |e| e > 2 }.last? - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:37" - - issue.message.should eq "Use `reverse_each.find {...}` instead of `select {...}.last?`" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr deleted file mode 100644 index ccf958d8702b..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/performance/size_after_filter_spec.cr +++ /dev/null @@ -1,74 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Performance - subject = SizeAfterFilter.new - - describe SizeAfterFilter do - it "passes if there is no potential performance improvements" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 } - [1, 2, 3].reject { |e| e < 2 } - [1, 2, 3].count { |e| e > 2 && e.odd? } - [1, 2, 3].count { |e| e < 2 && e.even? } - - User.select("field AS name").count - Company.select(:value).count - ) - subject.catch(source).should be_valid - end - - it "reports if there is a select followed by size" do - source = Source.new %( - [1, 2, 3].select { |e| e > 2 }.size - ) - subject.catch(source).should_not be_valid - end - - it "reports if there is a reject followed by size" do - source = Source.new %( - [1, 2, 3].reject { |e| e < 2 }.size - ) - subject.catch(source).should_not be_valid - end - - it "reports if a block shorthand used" do - source = Source.new %( - [1, 2, 3].reject(&.empty?).size - ) - subject.catch(source).should_not be_valid - end - - context "properties" do - it "allows to configure object caller names" do - source = Source.new %( - [1, 2, 3].reject(&.empty?).size - ) - rule = Rule::Performance::SizeAfterFilter.new - rule.filter_names = %w(select) - rule.catch(source).should be_valid - end - end - - context "macro" do - it "doesn't report in macro scope" do - source = Source.new %( - {{[1, 2, 3].select { |v| v > 1 }.size}} - ) - subject.catch(source).should be_valid - end - end - - it "reports rule, pos and message" do - s = Source.new %( - lines.split("\n").reject(&.empty?).size - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:4" - issue.end_location.to_s.should eq "source.cr:2:25" - issue.message.should eq "Use `count {...}` instead of `reject {...}.size`." - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr deleted file mode 100644 index f31814969084..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/constant_names_spec.cr +++ /dev/null @@ -1,54 +0,0 @@ -require "../../../spec_helper" - -module Ameba - subject = Rule::Style::ConstantNames.new - - private def it_reports_constant(code, expected) - it "reports constant name #{expected}" do - s = Source.new code - Rule::Style::ConstantNames.new.catch(s).should_not be_valid - s.issues.first.message.should contain expected - end - end - - describe Rule::Style::ConstantNames do - it "passes if type names are screaming-cased" do - s = Source.new %( - LUCKY_NUMBERS = [3, 7, 11] - DOCUMENTATION_URL = "http://crystal-lang.org/docs" - - Int32 - - s : String = "str" - - def works(n : Int32) - end - - Log = ::Log.for("db") - - a = 1 - myVar = 2 - m_var = 3 - ) - subject.catch(s).should be_valid - end - - # it_reports_constant "MyBadConstant=1", "MYBADCONSTANT" - it_reports_constant "Wrong_NAME=2", "WRONG_NAME" - it_reports_constant "Wrong_Name=3", "WRONG_NAME" - - it "reports rule, pos and message" do - s = Source.new %( - Const_Name = 1 - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:10" - issue.message.should eq( - "Constant name should be screaming-cased: CONST_NAME, not Const_Name" - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr deleted file mode 100644 index a3bf0ee6c2a5..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/is_a_nil_spec.cr +++ /dev/null @@ -1,45 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - describe IsANil do - subject = IsANil.new - - it "doesn't report if there are no is_a?(Nil) calls" do - s = Source.new %( - a = 1 - a.nil? - a.is_a?(NilLiteral) - a.is_a?(Custom::Nil) - ) - subject.catch(s).should be_valid - end - - it "reports if there is a call to is_a?(Nil) without receiver" do - s = Source.new %( - is_a?(Nil) - ) - subject.catch(s).should_not be_valid - end - - it "reports if there is a call to is_a?(Nil) with receiver" do - s = Source.new %( - a.is_a?(Nil) - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, location and message" do - s = Source.new %( - nil.is_a? Nil - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:11" - issue.end_location.to_s.should eq "source.cr:1:13" - issue.message.should eq IsANil::MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr deleted file mode 100644 index cd1f14f64d6a..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/large_numbers_spec.cr +++ /dev/null @@ -1,134 +0,0 @@ -require "../../../spec_helper" - -module Ameba - subject = Rule::Style::LargeNumbers.new - - private def it_transforms(number, expected) - it "transforms large number #{number}" do - s = Source.new number - Rule::Style::LargeNumbers.new.catch(s).should_not be_valid - s.issues.first.message.should contain expected - end - end - - describe Rule::Style::LargeNumbers do - it "passes if large number does not require underscore" do - s = Source.new %q( - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - 16 17 18 19 20 30 40 50 60 70 80 90 - 100 - 999 - 1000 - 1_000 - 9999 - 9_999 - 10_000 - 100_000 - 200_000 - 300_000 - 400_000 - 500_000 - 600_000 - 700_000 - 800_000 - 900_000 - 1_000_000 - - -9_223_372_036_854_775_808 - 9_223_372_036_854_775_807 - - 141_592_654 - 141_592_654.0 - 141_592_654.001 - 141_592_654.001_2 - 141_592_654.001_23 - 141_592_654.001_234 - 141_592_654.001_234_5 - - 0b1101 - 0o123 - 0xFE012D - 0xfe012d - 0xfe012dd11 - - 1_i8 - 12_i16 - 123_i32 - 1_234_i64 - - 12_u8 - 123_u16 - 1_234_u32 - 9_223_372_036_854_775_808_u64 - 9_223_372_036_854_775_808.000_123_456_789_f64 - - +100_u32 - -900_000_i32 - - 1_234.5e-7 - 11_234e10_f32 - +1.123 - -0.000_5 - - 1200.0 - 1200.01 - 1200.012 - ) - subject.catch(s).should be_valid - end - - it_transforms "10000", "10_000" - it_transforms "+10000", "+10_000" - it_transforms "-10000", "-10_000" - - it_transforms "9223372036854775808", "9_223_372_036_854_775_808" - it_transforms "-9223372036854775808", "-9_223_372_036_854_775_808" - it_transforms "+9223372036854775808", "+9_223_372_036_854_775_808" - - it_transforms "1_00000", "100_000" - - it_transforms "10000_i16", "10_000_i16" - it_transforms "10000_i32", "10_000_i32" - it_transforms "10000_i64", "10_000_i64" - - it_transforms "10000_u16", "10_000_u16" - it_transforms "10000_u32", "10_000_u32" - it_transforms "10000_u64", "10_000_u64" - - it_transforms "123456_f32", "123_456_f32" - it_transforms "123456_f64", "123_456_f64" - - it_transforms "123456.5e-7_f32", "123_456.5e-7_f32" - it_transforms "123456e10_f64", "123_456e10_f64" - - it_transforms "123456.5e-7", "123_456.5e-7" - it_transforms "123456e10", "123_456e10" - - it_transforms "3.00_1", "3.001" - it_transforms "3.0012", "3.001_2" - it_transforms "3.00123", "3.001_23" - it_transforms "3.001234", "3.001_234" - it_transforms "3.0012345", "3.001_234_5" - - it "reports rule, pos and message" do - s = Source.new %q( - 1200000 - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.should be_nil - issue.message.should match /1_200_000/ - end - - context "properties" do - it "allows to configure integer min digits" do - s = Source.new %q(1200000) - rule = Rule::Style::LargeNumbers.new - rule.int_min_digits = 10 - rule.catch(s).should be_valid - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr deleted file mode 100644 index 4f828b5830a7..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/method_names_spec.cr +++ /dev/null @@ -1,56 +0,0 @@ -require "../../../spec_helper" - -module Ameba - subject = Rule::Style::MethodNames.new - - private def it_reports_method_name(code, expected) - it "reports method name #{expected}" do - s = Source.new code - Rule::Style::MethodNames.new.catch(s).should_not be_valid - s.issues.first.message.should contain expected - end - end - - describe Rule::Style::MethodNames do - it "passes if method names are underscore-cased" do - s = Source.new %( - class Person - def first_name - end - - def date_of_birth - end - - def homepage_url - end - - def valid? - end - - def name - end - end - ) - subject.catch(s).should be_valid - end - - it_reports_method_name %(def firstName; end), "first_name" - it_reports_method_name %(def date_of_Birth; end), "date_of_birth" - it_reports_method_name %(def homepageURL; end), "homepage_url" - - it "reports rule, pos and message" do - s = Source.new %( - def bad_Name(a) - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:5" - issue.end_location.to_s.should eq "source.cr:1:12" - issue.message.should eq( - "Method name should be underscore-cased: bad_name, not bad_Name" - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr deleted file mode 100644 index e8a042dcdd4a..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/negated_conditions_in_unless_spec.cr +++ /dev/null @@ -1,69 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - subject = NegatedConditionsInUnless.new - - describe NegatedConditionsInUnless do - it "passes with a unless without negated condition" do - s = Source.new %( - unless a - :ok - end - - :ok unless b - - unless s.empty? - :ok - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is a negated condition in unless" do - s = Source.new %( - unless !a - :nok - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if one of AND conditions is negated" do - s = Source.new %( - unless a && !b - :nok - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if one of OR conditions is negated" do - s = Source.new %( - unless a || !b - :nok - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if one of inner conditions is negated" do - s = Source.new %( - unless a && (b || !c) - :nok - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, pos and message" do - s = Source.new ":nok unless !s.empty?", "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:21" - issue.message.should eq "Avoid negated conditions in unless blocks" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr deleted file mode 100644 index eb797a221ea8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/predicate_name_spec.cr +++ /dev/null @@ -1,60 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - subject = PredicateName.new - - describe PredicateName do - it "passes if predicate name is correct" do - s = Source.new %q( - def valid?(x) - end - - class Image - def picture?(x) - end - end - - def allow_this_picture? - end - ) - subject.catch(s).should be_valid - end - - it "fails if predicate name is wrong" do - s = Source.new %q( - def is_valid?(x) - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, pos and message" do - s = Source.new %q( - class Image - def has_picture?(x) - true - end - end - ), "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:3" - issue.end_location.to_s.should eq "source.cr:4:5" - issue.message.should eq( - "Favour method name 'picture?' over 'has_picture?'") - end - - it "ignores if alternative name isn't valid syntax" do - s = Source.new %q( - class Image - def is_404?(x) - true - end - end - ) - subject.catch(s).should be_valid - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr deleted file mode 100644 index cbd18eb47d2d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_begin_spec.cr +++ /dev/null @@ -1,227 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - describe RedundantBegin do - subject = RedundantBegin.new - - it "passes if there is no redundant begin blocks" do - s = Source.new %( - def method - do_something - rescue - do_something_else - end - - def method - do_something - do_something_else - ensure - handle_something - end - - def method - yield - rescue - end - - def method; end - def method; a = 1; rescue; end - def method; begin; rescue; end; end - ) - subject.catch(s).should be_valid - end - - it "passes if there is a correct begin block in a handler" do - s = Source.new %q( - def handler_and_expression - begin - open_file - rescue - close_file - end - do_some_stuff - end - - def multiple_handlers - begin - begin1 - rescue - end - - begin - begin2 - rescue - end - rescue - do_something_else - end - - def assign_and_begin - @result ||= - begin - do_something - do_something_else - returnit - end - rescue - end - - def inner_handler - s = begin - rescue - end - rescue - end - - def begin_and_expression - begin - a = 1 - b = 2 - end - expr - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is a redundant begin block" do - s = Source.new %q( - def method(a : String) : String - begin - open_file - do_some_stuff - ensure - close_file - end - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is a redundant begin block in a method without args" do - s = Source.new %q( - def method - begin - open_file - ensure - close_file - end - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is a redundant block in a method with return type" do - s = Source.new %q( - def method : String - begin - open_file - ensure - close_file - end - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is a redundant block in a method with multiple args" do - s = Source.new %q( - def method(a : String, - b : String) - begin - open_file - ensure - close_file - end - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is a redundant block in a method with multiple args" do - s = Source.new %q( - def method(a : String, - b : String - ) - begin - open_file - ensure - close_file - end - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if there is an inner redundant block" do - s = Source.new %q( - def method - begin - open_file - ensure - close_file - end - rescue - end - ) - subject.catch(s).should be_valid - end - - it "fails if there is a redundant block with yield" do - s = Source.new %q( - def method - begin - yield - ensure - close_file - end - end - ) - subject.catch(s).should_not be_valid - end - - it "fails if there is top level redundant block in a method" do - s = Source.new %q( - def method - begin - a = 1 - b = 2 - end - end - ) - subject.catch(s).should_not be_valid - end - - it "doesn't report if begin-end block in a proc literal" do - s = Source.new %q( - foo = ->{ - begin - raise "Foo!" - rescue ex - pp ex - end - } - ) - subject.catch(s).should be_valid - end - - it "reports rule, pos and message" do - s = Source.new %q( - def method - begin - open_connection - ensure - close_connection - end - end - ), "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:7:3" - issue.message.should eq "Redundant `begin` block detected" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr deleted file mode 100644 index 460d2dcac8ea..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_next_spec.cr +++ /dev/null @@ -1,219 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - subject = RedundantNext.new - - describe RedundantNext do - it "does not report if there is no redundant next" do - s = Source.new %( - array.map { |x| x + 1 } - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant next with argument in the block" do - s = Source.new %( - block do |v| - next v + 1 - end - ) - subject.catch(s).should_not be_valid - end - - context "if" do - it "doesn't report if there is not redundant next in if branch" do - s = Source.new %( - block do |v| - next if v > 10 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant next in if/else branch" do - s = Source.new %( - block do |a| - if a > 0 - next a + 1 - else - next a + 2 - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":3:5" - s.issues.last.location.to_s.should eq ":5:5" - end - end - - context "unless" do - it "doesn't report if there is no redundant next in unless branch" do - s = Source.new %( - block do |v| - next unless v > 10 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant next in unless/else branch" do - s = Source.new %( - block do |a| - unless a > 0 - next a + 1 - else - next a + 2 - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":3:5" - s.issues.last.location.to_s.should eq ":5:5" - end - end - - context "expressions" do - it "doesn't report if there is no redundant next in expressions" do - s = Source.new %( - block do |v| - a = 1 - a + v - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant next in expressions" do - s = Source.new %( - block do |a| - a = 1 - next a - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":3:3" - end - end - - context "binary-op" do - it "doesn't report if there is no redundant next in binary op" do - s = Source.new %( - block do |v| - a && v - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant next in binary op" do - s = Source.new %( - block do |a| - a && next a - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:8" - end - end - - context "expception handler" do - it "doesn't report if there is no redundant next in exception handler" do - s = Source.new %( - block do |v| - v + 1 - rescue e - next v if v > 0 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant next in exception handler" do - s = Source.new %( - block do |a| - next a + 1 - rescue ArgumentError - next a + 2 - rescue Exception - a + 2 - next a - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 3 - s.issues[0].location.to_s.should eq ":2:3" - s.issues[1].location.to_s.should eq ":4:3" - s.issues[2].location.to_s.should eq ":7:3" - end - end - - it "reports correct rule, error message and position" do - s = Source.new %( - block do |v| - next v + 1 - end - ), "source.cr" - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:2:3" - issue.end_location.to_s.should eq "source.cr:2:12" - issue.message.should eq "Redundant `next` detected" - end - - context "properties" do - context "#allow_multi_next=" do - it "allows multi next statements by default" do - s = Source.new %( - block do |a, b| - next a, b - end - ) - subject.catch(s).should be_valid - end - - it "allows to configure multi next statements" do - s = Source.new %( - block do |a, b| - next a, b - end - ) - rule = Rule::Style::RedundantNext.new - rule.allow_multi_next = false - rule.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:3" - end - end - - context "#allow_empty_next" do - it "allows empty next statements by default" do - s = Source.new %( - block do - next - end - ) - subject.catch(s).should be_valid - end - - it "allows to configure empty next statements" do - s = Source.new %( - block do - next - end - ) - rule = Rule::Style::RedundantNext.new - rule.allow_empty_next = false - rule.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:3" - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr deleted file mode 100644 index 4638dfc9e20d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/redundant_return_spec.cr +++ /dev/null @@ -1,266 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - subject = RedundantReturn.new - - describe RedundantReturn do - it "does not report if there is no return" do - s = Source.new %( - def inc(a) - a + 1 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is redundant return in method body" do - s = Source.new %( - def inc(a) - return a + 1 - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:3" - end - - it "doesn't report if it returns tuple literal" do - s = Source.new %( - def foo(a) - return a, a + 2 - end - ) - subject.catch(s).should be_valid - end - - it "doesn't report if there are other expressions after control flow" do - s = Source.new %( - def method(a) - case a - when true then return true - when .nil? then return :nil - end - false - rescue - nil - end - ) - subject.catch(s).should be_valid - end - - context "if" do - it "doesn't report if there is return in if branch" do - s = Source.new %( - def inc(a) - return a + 1 if a > 0 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there are returns in if/else branch" do - s = Source.new %( - def inc(a) - do_something(a) - if a > 0 - return :positive - else - return :negative - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":4:5" - s.issues.last.location.to_s.should eq ":6:5" - end - end - - context "unless" do - it "doesn't report if there is return in unless branch" do - s = Source.new %( - def inc(a) - return a + 1 unless a > 0 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there are returns in unless/else branch" do - s = Source.new %( - def inc(a) - do_something(a) - unless a < 0 - return :positive - else - return :negative - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":4:5" - s.issues.last.location.to_s.should eq ":6:5" - end - end - - context "binary op" do - it "doesn't report if there is no return in the right binary op node" do - s = Source.new %( - def can_create?(a) - valid? && a > 0 - end - ) - subject.catch(s).should be_valid - end - - it "reports if there is return in the right binary op node" do - s = Source.new %( - def can_create?(a) - valid? && return a > 0 - end - ) - subject.catch(s).should_not be_valid - end - end - - context "case" do - it "reports if there are returns in whens" do - s = Source.new %( - def foo(a) - case a - when .nil? - puts "blah" - return nil - when .blank? - return "" - when true - true - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":5:5" - s.issues.last.location.to_s.should eq ":7:5" - end - - it "reports if there is return in else" do - s = Source.new %( - def foo(a) - do_something_with(a) - - case a - when true - true - else - return false - end - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":8:5" - end - end - - context "exception handler" do - it "reports if there are returns in body" do - s = Source.new %( - def foo(a) - return true - rescue - false - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:3" - end - - it "reports if there are returns in rescues" do - s = Source.new %( - def foo(a) - true - rescue ArgumentError - return false - rescue RuntimeError - "" - rescue Exception - return nil - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 2 - s.issues.first.location.to_s.should eq ":4:3" - s.issues.last.location.to_s.should eq ":8:3" - end - - it "reports if there are returns in else" do - s = Source.new %( - def foo(a) - true - rescue Exception - nil - else - puts "else branch" - return :bar - end - ) - subject.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":7:3" - end - end - - context "properties" do - context "#allow_multi_return=" do - it "allows multi returns by default" do - s = Source.new %( - def method(a, b) - return a, b - end - ) - subject.catch(s).should be_valid - end - - it "allows to configure multi returns" do - s = Source.new %( - def method(a, b) - return a, b - end - ) - rule = Rule::Style::RedundantReturn.new - rule.allow_multi_return = false - rule.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:3" - end - end - - context "#allow_empty_return" do - it "allows empty returns by default" do - s = Source.new %( - def method - return - end - ) - subject.catch(s).should be_valid - end - - it "allows to configure empty returns" do - s = Source.new %( - def method - return - end - ) - rule = Rule::Style::RedundantReturn.new - rule.allow_empty_return = false - rule.catch(s).should_not be_valid - s.issues.size.should eq 1 - s.issues.first.location.to_s.should eq ":2:3" - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr deleted file mode 100644 index b5064411e31a..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/type_names_spec.cr +++ /dev/null @@ -1,61 +0,0 @@ -require "../../../spec_helper" - -module Ameba - subject = Rule::Style::TypeNames.new - - private def it_reports_name(code, expected) - it "reports type name #{expected}" do - s = Source.new code - Rule::Style::TypeNames.new.catch(s).should_not be_valid - s.issues.first.message.should contain expected - end - end - - describe Rule::Style::TypeNames do - it "passes if type names are camelcased" do - s = Source.new %( - class ParseError < Exception - end - - module HTTP - class RequestHandler - end - end - - alias NumericValue = Float32 | Float64 | Int32 | Int64 - - lib LibYAML - end - - struct TagDirective - end - - enum Time::DayOfWeek - end - ) - subject.catch(s).should be_valid - end - - it_reports_name "class My_class; end", "MyClass" - it_reports_name "module HTT_p; end", "HTTP" - it_reports_name "alias Numeric_value = Int32", "NumericValue" - it_reports_name "lib Lib_YAML; end", "LibYAML" - it_reports_name "struct Tag_directive; end", "TagDirective" - it_reports_name "enum Time_enum::Day_of_week; end", "TimeEnum::DayOfWeek" - - it "reports rule, pos and message" do - s = Source.new %( - class My_class - end - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:2:3" - issue.message.should eq( - "Type name should be camelcased: MyClass, but it was My_class" - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr deleted file mode 100644 index bb8b6b25849f..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/unless_else_spec.cr +++ /dev/null @@ -1,45 +0,0 @@ -require "../../../spec_helper" - -module Ameba::Rule::Style - subject = UnlessElse.new - - describe UnlessElse do - it "passes if unless hasn't else" do - s = Source.new %( - unless something - :ok - end - ) - subject.catch(s).should be_valid - end - - it "fails if unless has else" do - s = Source.new %( - unless something - :one - else - :two - end - ) - subject.catch(s).should_not be_valid - end - - it "reports rule, pos and message" do - s = Source.new %( - unless something - :one - else - :two - end - ), "source.cr" - subject.catch(s).should_not be_valid - - issue = s.issues.first - issue.should_not be_nil - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:5:3" - issue.message.should eq "Favour if over unless with else" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr deleted file mode 100644 index 6ec6ae9f19b6..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/variable_names_spec.cr +++ /dev/null @@ -1,62 +0,0 @@ -require "../../../spec_helper" - -module Ameba - subject = Rule::Style::VariableNames.new - - private def it_reports_var_name(code, expected) - it "reports method name #{expected}" do - s = Source.new code - Rule::Style::VariableNames.new.catch(s).should_not be_valid - s.issues.first.message.should contain expected - end - end - - describe Rule::Style::VariableNames do - it "passes if var names are underscore-cased" do - s = Source.new %( - class Greeting - @@default_greeting = "Hello world" - - def initialize(@custom_greeting = nil) - end - - def print_greeting - greeting = @custom_greeting || @@default_greeting - puts greeting - end - end - ) - subject.catch(s).should be_valid - end - - it_reports_var_name %(myBadNamedVar = 1), "my_bad_named_var" - it_reports_var_name %(wrong_Name = 'y'), "wrong_name" - - it_reports_var_name %( - class Greeting - def initialize(@badNamed = nil) - end - end - ), "bad_named" - - it_reports_var_name %( - class Greeting - @@defaultGreeting = "Hello world" - end - ), "default_greeting" - - it "reports rule, pos and message" do - s = Source.new %( - badName = "Yeah" - ), "source.cr" - subject.catch(s).should_not be_valid - issue = s.issues.first - issue.rule.should_not be_nil - issue.location.to_s.should eq "source.cr:1:1" - issue.end_location.to_s.should eq "source.cr:1:7" - issue.message.should eq( - "Var name should be underscore-cased: bad_name, not badName" - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr deleted file mode 100644 index 6d9d85da208e..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/rule/style/while_true_spec.cr +++ /dev/null @@ -1,43 +0,0 @@ -require "../../../spec_helper" - -valid_source = <<-EOF -a = 1 -loop do - a += 1 - break if a > 5 -end -EOF - -invalid_source = <<-EOF -a = 1 -while true - a += 1 - break if a > 5 -end -EOF - -module Ameba::Rule::Style - subject = WhileTrue.new - - describe WhileTrue do - it "passes if there is no `while true`" do - source = Source.new valid_source - subject.catch(source).should be_valid - end - - it "fails if there is `while true`" do - source = Source.new invalid_source - subject.catch(source).should_not be_valid - end - - it "reports rule, pos and message" do - source = Source.new invalid_source, "source.cr" - subject.catch(source).should_not be_valid - - issue = source.issues.first - issue.location.to_s.should eq "source.cr:2:1" - issue.end_location.to_s.should eq "source.cr:5:3" - issue.message.should eq "While statement using true literal as condition" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr deleted file mode 100644 index 16957e687790..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/runner_spec.cr +++ /dev/null @@ -1,183 +0,0 @@ -require "../spec_helper" - -module Ameba - private def runner(files = [__FILE__], formatter = DummyFormatter.new) - config = Config.load - config.formatter = formatter - config.globs = files - - config.update_rule ErrorRule.rule_name, enabled: false - - Runner.new(config) - end - - describe Runner do - formatter = DummyFormatter.new - default_severity = Severity::Convention - - describe "#run" do - it "returns self" do - runner.run.should_not be_nil - end - - it "calls started callback" do - runner(formatter: formatter).run - formatter.started_sources.should_not be_nil - end - - it "calls finished callback" do - runner(formatter: formatter).run - formatter.finished_sources.should_not be_nil - end - - it "calls source_started callback" do - runner(formatter: formatter).run - formatter.started_source.should_not be_nil - end - - it "calls source_finished callback" do - runner(formatter: formatter).run - formatter.finished_source.should_not be_nil - end - - it "skips rule check if source is excluded" do - path = "source.cr" - source = Source.new "", path - - all_rules = ([] of Rule::Base).tap do |rules| - rule = ErrorRule.new - rule.excluded = [path] - rules << rule - end - - Runner.new(all_rules, [source], formatter, default_severity).run.success?.should be_true - end - - context "exception in rule" do - it "raises an exception raised in fiber while running a rule" do - rule = RaiseRule.new - rule.should_raise = true - rules = [rule] of Rule::Base - source = Source.new "", "source.cr" - - expect_raises(Exception, "something went wrong") do - Runner.new(rules, [source], formatter, default_severity).run - end - end - end - - context "invalid syntax" do - it "reports a syntax error" do - rules = [Rule::Lint::Syntax.new] of Rule::Base - source = Source.new "def bad_syntax" - - Runner.new(rules, [source], formatter, default_severity).run - source.should_not be_valid - source.issues.first.rule.name.should eq Rule::Lint::Syntax.rule_name - end - - it "does not run other rules" do - rules = [Rule::Lint::Syntax.new, Rule::Style::ConstantNames.new] of Rule::Base - source = Source.new %q( - MyBadConstant = 1 - - when my_bad_syntax - ) - - Runner.new(rules, [source], formatter, default_severity).run - source.should_not be_valid - source.issues.size.should eq 1 - end - end - - context "unneeded disables" do - it "reports an issue if such disable exists" do - rules = [Rule::Lint::UnneededDisableDirective.new] of Rule::Base - source = Source.new %( - a = 1 # ameba:disable LineLength - ) - - Runner.new(rules, [source], formatter, default_severity).run - source.should_not be_valid - source.issues.first.rule.name.should eq Rule::Lint::UnneededDisableDirective.rule_name - end - end - end - - describe "#explain" do - io = IO::Memory.new - - it "writes nothing if sources are valid" do - io.clear - runner = runner(formatter: formatter).run - runner.explain({file: "source.cr", line: 1, column: 2}, io) - io.to_s.should be_empty - end - - it "writes the explanation if sources are not valid and location found" do - io.clear - rules = [ErrorRule.new] of Rule::Base - source = Source.new %( - a = 1 - ), "source.cr" - - runner = Runner.new(rules, [source], formatter, default_severity).run - runner.explain({file: "source.cr", line: 1, column: 1}, io) - io.to_s.should_not be_empty - end - - it "writes nothing if sources are not valid and location is not found" do - io.clear - rules = [ErrorRule.new] of Rule::Base - source = Source.new %( - a = 1 - ), "source.cr" - - runner = Runner.new(rules, [source], formatter, default_severity).run - runner.explain({file: "source.cr", line: 1, column: 2}, io) - io.to_s.should be_empty - end - end - - describe "#success?" do - it "returns true if runner has not been run" do - runner.success?.should be_true - end - - it "returns true if all sources are valid" do - runner.run.success?.should be_true - end - - it "returns false if there are invalid sources" do - rules = Rule.rules.map &.new.as(Rule::Base) - s = Source.new %q( - WrongConstant = 5 - ) - Runner.new(rules, [s], formatter, default_severity).run.success?.should be_false - end - - it "depends on the level of severity" do - rules = Rule.rules.map &.new.as(Rule::Base) - s = Source.new %q(WrongConstant = 5) - Runner.new(rules, [s], formatter, Severity::Error).run.success?.should be_true - Runner.new(rules, [s], formatter, Severity::Warning).run.success?.should be_true - Runner.new(rules, [s], formatter, Severity::Convention).run.success?.should be_false - end - - it "returns false if issue is disabled" do - rules = [NamedRule.new] of Rule::Base - source = Source.new %( - def foo - bar = 1 # ameba:disable #{NamedRule.name} - end - ) - source.add_issue NamedRule.new, location: {2, 1}, - message: "Useless assignment" - - Runner - .new(rules, [source], formatter, default_severity) - .run.success?.should be_true - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr deleted file mode 100644 index 94f3b7304cd2..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/severity_spec.cr +++ /dev/null @@ -1,108 +0,0 @@ -require "../spec_helper" - -module Ameba - describe Severity do - describe "#symbol" do - it "returns the symbol for each severity in the enum" do - Severity.values.each do |severity| - severity.symbol.should_not be_nil - end - end - - it "returns symbol for Error" do - Severity::Error.symbol.should eq 'E' - end - - it "returns symbol for Warning" do - Severity::Warning.symbol.should eq 'W' - end - - it "returns symbol for Convention" do - Severity::Convention.symbol.should eq 'C' - end - end - - describe ".parse" do - it "creates error severity by name" do - Severity.parse("Error").should eq Severity::Error - end - - it "creates warning severity by name" do - Severity.parse("Warning").should eq Severity::Warning - end - - it "creates convention severity by name" do - Severity.parse("Convention").should eq Severity::Convention - end - - it "raises when name is incorrect" do - expect_raises(Exception, "Incorrect severity name BadName. Try one of [Error, Warning, Convention]") do - Severity.parse("BadName") - end - end - end - end - - struct SeverityConvertable - include YAML::Serializable - - @[YAML::Field(converter: Ameba::SeverityYamlConverter)] - property severity : Severity - end - - describe SeverityYamlConverter do - describe ".from_yaml" do - it "converts from yaml to Severity::Error" do - yaml = {severity: "error"}.to_yaml - converted = SeverityConvertable.from_yaml(yaml) - converted.severity.should eq Severity::Error - end - - it "converts from yaml to Severity::Warning" do - yaml = {severity: "warning"}.to_yaml - converted = SeverityConvertable.from_yaml(yaml) - converted.severity.should eq Severity::Warning - end - - it "converts from yaml to Severity::Convention" do - yaml = {severity: "convention"}.to_yaml - converted = SeverityConvertable.from_yaml(yaml) - converted.severity.should eq Severity::Convention - end - - it "raises if severity is not a scalar" do - yaml = {severity: {convention: true}}.to_yaml - expect_raises(Exception, "Severity must be a scalar") do - SeverityConvertable.from_yaml(yaml) - end - end - - it "raises if severity has a wrong type" do - yaml = {severity: [1, 2, 3]}.to_yaml - expect_raises(Exception, "Severity must be a scalar") do - SeverityConvertable.from_yaml(yaml) - end - end - end - - describe ".to_yaml" do - it "converts Severity::Error to yaml" do - yaml = {severity: "error"}.to_yaml - converted = SeverityConvertable.from_yaml(yaml).to_yaml - converted.should eq "---\nseverity: Error\n" - end - - it "converts Severity::Warning to yaml" do - yaml = {severity: "warning"}.to_yaml - converted = SeverityConvertable.from_yaml(yaml).to_yaml - converted.should eq "---\nseverity: Warning\n" - end - - it "converts Severity::Convention to yaml" do - yaml = {severity: "convention"}.to_yaml - converted = SeverityConvertable.from_yaml(yaml).to_yaml - converted.should eq "---\nseverity: Convention\n" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr deleted file mode 100644 index cd8af8759757..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/source_spec.cr +++ /dev/null @@ -1,38 +0,0 @@ -require "../spec_helper" - -module Ameba - describe Source do - describe ".new" do - it "allows to create a source by code and path" do - s = Source.new("code", "path") - s.path.should eq "path" - s.code.should eq "code" - s.lines.should eq ["code"] - end - end - - describe "#fullpath" do - it "returns a relative path of the source" do - s = Source.new "", "./source_spec.cr" - s.fullpath.should contain "source_spec.cr" - end - - it "returns fullpath if path is blank" do - s = Source.new "", "" - s.fullpath.should_not be_nil - end - end - - describe "#matches_path?" do - it "returns true if source's path is matched" do - s = Source.new "", "source.cr" - s.matches_path?("source.cr").should be_true - end - - it "returns false if source's path is not matched" do - s = Source.new "", "source.cr" - s.matches_path?("new_source.cr").should be_false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr deleted file mode 100644 index 2970f9d83194..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba/tokenizer_spec.cr +++ /dev/null @@ -1,44 +0,0 @@ -require "../spec_helper" - -module Ameba - private def it_tokenizes(str, expected) - it "tokenizes #{str}" do - ([] of Symbol).tap do |token_types| - Tokenizer.new(Source.new str, normalize: false) - .run { |token| token_types << token.type } - .should be_true - end.should eq expected - end - end - - describe Tokenizer do - describe "#run" do - it_tokenizes %("string"), %i(DELIMITER_START STRING DELIMITER_END EOF) - it_tokenizes %(100), %i(NUMBER EOF) - it_tokenizes %('a'), %i(CHAR EOF) - it_tokenizes %([]), %i([] EOF) - it_tokenizes %([] of String), %i([] SPACE IDENT SPACE CONST EOF) - it_tokenizes %q("str #{3}"), %i( - DELIMITER_START STRING INTERPOLATION_START NUMBER } DELIMITER_END EOF - ) - - it_tokenizes %(%w(1 2)), - %i(STRING_ARRAY_START STRING STRING STRING_ARRAY_END EOF) - - it_tokenizes %(%i(one two)), - %i(SYMBOL_ARRAY_START STRING STRING STRING_ARRAY_END EOF) - - it_tokenizes %( - class A - def method - puts "hello" - end - end - ), %i( - NEWLINE SPACE IDENT SPACE CONST NEWLINE SPACE IDENT SPACE IDENT - NEWLINE SPACE IDENT SPACE DELIMITER_START STRING DELIMITER_END - NEWLINE SPACE IDENT NEWLINE SPACE IDENT NEWLINE SPACE EOF - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr b/samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr deleted file mode 100644 index 48e203552980..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/ameba_spec.cr +++ /dev/null @@ -1,4 +0,0 @@ -require "./spec_helper" - -describe Ameba do -end diff --git a/samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr deleted file mode 100644 index a1739beccb47..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/spec/spec_helper.cr +++ /dev/null @@ -1,200 +0,0 @@ -require "spec" -require "../src/ameba" - -module Ameba - # Dummy Rule which does nothing. - struct DummyRule < Rule::Base - properties do - description : String = "Dummy rule that does nothing." - end - - def test(source) - end - end - - class Source - def initialize(code : String, @path = "", normalize = true) - @code = normalize ? normalize_source(code) : code - end - - private def normalize_source(code, separator = "\n") - lines = code.split(separator) - - # remove unneeded first blank lines if any - lines.shift if lines[0].blank? && lines.size > 1 - - # find the minimum indentation - min_indent = lines.min_of do |line| - line.blank? ? code.size : line.size - line.lstrip.size - end - - # remove the width of minimum indentation in each line - lines - .map! { |line| line.blank? ? line : line[min_indent..-1] } - .join(separator) - end - end - - struct NamedRule < Rule::Base - properties do - description "A rule with a custom name." - end - - def self.name - "BreakingRule" - end - end - - struct ErrorRule < Rule::Base - properties do - description "Always adds an error at 1:1" - end - - def test(source) - issue_for({1, 1}, "This rule always adds an error") - end - end - - struct ScopeRule < Rule::Base - @[YAML::Field(ignore: true)] - getter scopes = [] of AST::Scope - - properties do - description "Internal rule to test scopes" - end - - def test(source, node : Crystal::ASTNode, scope : AST::Scope) - @scopes << scope - end - end - - struct FlowExpressionRule < Rule::Base - @[YAML::Field(ignore: true)] - getter expressions = [] of AST::FlowExpression - - properties do - description "Internal rule to test flow expressions" - end - - def test(source, node, flow_expression : AST::FlowExpression) - @expressions << flow_expression - end - end - - struct RedundantControlExpressionRule < Rule::Base - @[YAML::Field(ignore: true)] - getter nodes = [] of Crystal::ASTNode - - properties do - description "Internal rule to test redundant control expressions" - end - - def test(source, node, visitor : AST::RedundantControlExpressionVisitor) - nodes << node - end - end - - # A rule that always raises an error - struct RaiseRule < Rule::Base - property should_raise = false - - properties do - description "Internal rule that always raises" - end - - def test(source) - should_raise && raise "something went wrong" - end - end - - class DummyFormatter < Formatter::BaseFormatter - property started_sources : Array(Source)? - property finished_sources : Array(Source)? - property started_source : Source? - property finished_source : Source? - - def started(sources) - @started_sources = sources - end - - def source_finished(source : Source) - @started_source = source - end - - def source_started(source : Source) - @finished_source = source - end - - def finished(sources) - @finished_sources = sources - end - end - - struct BeValidExpectation - def match(source) - source.valid? - end - - def failure_message(source) - String.build do |str| - str << "Source expected to be valid, but there are issues: \n\n" - source.issues.reject(&.disabled?).each do |e| - str << " * #{e.rule.name}: #{e.message}\n" - end - end - end - - def negative_failure_message(source) - "Source expected to be invalid, but it is valid." - end - end - - class TestNodeVisitor < Crystal::Visitor - NODES = [ - Crystal::NilLiteral, - Crystal::Var, - Crystal::Assign, - Crystal::OpAssign, - Crystal::MultiAssign, - Crystal::Block, - Crystal::Macro, - Crystal::Def, - Crystal::If, - Crystal::While, - Crystal::MacroLiteral, - Crystal::Expressions, - Crystal::ControlExpression, - ] - - def initialize(node) - node.accept self - end - - def visit(node : Crystal::ASTNode) - true - end - - {% for node in NODES %} - {{getter_name = node.stringify.split("::").last.underscore + "_nodes"}} - - getter {{getter_name.id}} = [] of {{node}} - - def visit(node : {{node}}) - {{getter_name.id}} << node - true - end - {% end %} - end -end - -def be_valid - Ameba::BeValidExpectation.new -end - -def as_node(source) - Crystal::Parser.new(source).parse -end - -def as_nodes(source) - Ameba::TestNodeVisitor.new(as_node source) -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba.cr deleted file mode 100644 index 4649b00c1585..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba.cr +++ /dev/null @@ -1,42 +0,0 @@ -require "./ameba/*" -require "./ameba/ast/**" -require "./ameba/rule/**" -require "./ameba/formatter/*" - -# Ameba's entry module. -# -# To run the linter with default parameters: -# -# ``` -# Ameba.run -# ``` -# -# To configure and run it: -# -# ``` -# config = Ameba::Config.load -# config.formatter = formatter -# config.files = file_paths -# -# Ameba.run config -# ``` -# -module Ameba - extend self - - VERSION = {{ `shards version "#{__DIR__}"`.chomp.stringify }} - - # Initializes `Ameba::Runner` and runs it. - # Can be configured via `config` parameter. - # - # Examples: - # - # ``` - # Ameba.run - # Ameba.run config - # ``` - # - def run(config = Config.load) - Runner.new(config).run - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr deleted file mode 100644 index dcca21aa8a9f..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branch.cr +++ /dev/null @@ -1,194 +0,0 @@ -module Ameba::AST - # Represents the branch in Crystal code. - # Branch is a part of a branchable statement. - # For example, the branchable if statement contains 3 branches: - # - # ``` - # if a = something # --> Branch A - # a = 1 # --> Branch B - # put a if out # --> Branch C - # else - # do_something a # --> Branch D - # end - # ``` - # - class Branch - # The actual branch node. - getter node : Crystal::ASTNode - - # The parent branchable. - getter parent : Branchable - - delegate to_s, to: @node - delegate location, to: @node - delegate end_location, to: @node - - def_equals_and_hash node, location - - # Creates a new branch. - # - # ``` - # Branch.new(if_node) - # ``` - def initialize(@node, @parent) - end - - # Returns true if current branch is in a loop, false - otherwise. - # For example, this branch is in a loop: - # - # ``` - # while true - # handle_input # this branch is in a loop - # if wrong_input - # show_message # this branch is also in a loop. - # end - # end - # ``` - # - def in_loop? - @parent.loop? - end - - # Constructs a new branch based on the node in scope. - # - # ``` - # Branch.of(assign_node, scope) - # ``` - def self.of(node : Crystal::ASTNode, scope : Scope) - of(node, scope.node) - end - - # Constructs a new branch based on the node some parent scope. - # - # ``` - # Branch.of(assign_node, def_node) - # ``` - def self.of(node : Crystal::ASTNode, parent_node : Crystal::ASTNode) - BranchVisitor.new(node).tap(&.accept parent_node).branch - end - - # :nodoc: - private class BranchVisitor < Crystal::Visitor - @current_branch : Crystal::ASTNode? - - property branchable : Branchable? - property branch : Branch? - - def initialize(@node : Crystal::ASTNode) - end - - private def on_branchable_start(node, *branches) - on_branchable_start(node, branches) - end - - private def on_branchable_start(node, branches : Array | Tuple) - @branchable = Branchable.new(node, @branchable) - - branches.each do |branch_node| - break if branch # branch found - @current_branch = branch_node if branch_node && !branch_node.nop? - branch_node.try &.accept(self) - end - - false - end - - private def on_branchable_end(node) - @branchable = @branchable.try &.parent - end - - def visit(node : Crystal::ASTNode) - return false if branch - - if node.class == @node.class && - node.location == @node.location && - (branchable = @branchable) && - (branch = @current_branch) - @branch = Branch.new(branch, branchable) - end - - true - end - - def visit(node : Crystal::If) - on_branchable_start node, node.cond, node.then, node.else - end - - def end_visit(node : Crystal::If) - on_branchable_end node - end - - def visit(node : Crystal::Unless) - on_branchable_start node, node.cond, node.then, node.else - end - - def end_visit(node : Crystal::Unless) - on_branchable_end node - end - - def visit(node : Crystal::BinaryOp) - on_branchable_start node, node.left, node.right - end - - def end_visit(node : Crystal::BinaryOp) - on_branchable_end node - end - - def visit(node : Crystal::Case) - on_branchable_start node, [node.cond, node.whens, node.else].flatten - end - - def end_visit(node : Crystal::Case) - on_branchable_end node - end - - def visit(node : Crystal::While) - on_branchable_start node, node.cond, node.body - end - - def end_visit(node : Crystal::While) - on_branchable_end node - end - - def visit(node : Crystal::Until) - on_branchable_start node, node.cond, node.body - end - - def end_visit(node : Crystal::Until) - on_branchable_end node - end - - def visit(node : Crystal::ExceptionHandler) - on_branchable_start node, [node.body, node.rescues, node.else, node.ensure].flatten - end - - def end_visit(node : Crystal::ExceptionHandler) - on_branchable_end node - end - - def visit(node : Crystal::Rescue) - on_branchable_start node, node.body - end - - def end_visit(node : Crystal::Rescue) - on_branchable_end node - end - - def visit(node : Crystal::MacroIf) - on_branchable_start node, node.cond, node.then, node.else - end - - def end_visit(node : Crystal::MacroIf) - on_branchable_end node - end - - def visit(node : Crystal::MacroFor) - on_branchable_start node, node.exp, node.body - end - - def end_visit(node : Crystal::MacroFor) - on_branchable_end node - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr deleted file mode 100644 index 2495dbb2ab81..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/branchable.cr +++ /dev/null @@ -1,44 +0,0 @@ -require "./util" - -module Ameba::AST - # A generic entity to represent a branchable Crystal node. - # For example, `Crystal::If`, `Crystal::Unless`, `Crystal::While` - # are branchables. - # - # ``` - # white a > 100 # Branchable A - # if b > 2 # Branchable B - # a += 1 - # end - # end - # ``` - class Branchable - include Util - - getter branches = [] of Crystal::ASTNode - - # The actual Crystal node. - getter node : Crystal::ASTNode - - # Parent branchable (if any) - getter parent : Branchable? - - delegate to_s, to: @node - delegate location, to: @node - delegate end_location, to: @node - - # Creates a new branchable - # - # ``` - # Branchable.new(node, parent_branchable) - # ``` - def initialize(@node, @parent = nil) - end - - # Returns true if this node or one of the parent branchables is a loop, false otherwise. - def loop? - return true if loop?(node) - parent.try(&.loop?) || false - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr deleted file mode 100644 index 6b21496224c8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/flow_expression.cr +++ /dev/null @@ -1,69 +0,0 @@ -require "./util" - -module Ameba::AST - # Represents a flow expression in Crystal code. - # For example, - # - # ``` - # def foobar - # a = 3 - # return 42 # => flow expression - # a + 1 - # end - # ``` - # - # Flow expression contains an actual node of a control expression and - # a parent node, which allows easily search through the related statement - # (i.e. find unreachable code) - class FlowExpression - include Util - - # Is true only if some of the nodes parents is a loop. - getter? in_loop : Bool - - # The actual node of the flow expression. - getter node : Crystal::ASTNode - - delegate to_s, to: @node - delegate location, to: @node - delegate end_location, to: @node - - # Creates a new flow expression. - # - # ``` - # FlowExpression.new(node, parent_node) - # ``` - def initialize(@node, @in_loop) - end - - # Returns nodes which can't be reached because of a flow command inside. - # For example: - # - # ``` - # def foobar - # a = 1 - # return 42 - # - # a + 2 # => unreachable assign node - # end - # ``` - def unreachable_nodes - unreachable_nodes = [] of Crystal::ASTNode - - case current_node = node - when Crystal::Expressions - control_flow_found = false - current_node.expressions.each do |exp| - unreachable_nodes << exp if control_flow_found - control_flow_found ||= flow_expression?(exp, in_loop?) - end - when Crystal::BinaryOp - unreachable_nodes << current_node.right if flow_expression?(current_node.left, in_loop?) - else - # nop - end - - unreachable_nodes - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr deleted file mode 100644 index 3627ef73e067..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/scope.cr +++ /dev/null @@ -1,180 +0,0 @@ -require "./variabling/*" - -module Ameba::AST - # Represents a context of the local variable visibility. - # This is where the local variables belong to. - class Scope - # Link to local variables - getter variables = [] of Variable - - # Link to all variable references in currency scope - getter references = [] of Reference - - # Link to the arguments in current scope - getter arguments = [] of Argument - - # Link to the instance variables used in current scope - getter ivariables = [] of InstanceVariable - - # Link to the outer scope - getter outer_scope : Scope? - - # List of inner scopes - getter inner_scopes = [] of Scope - - # The actual AST node that represents a current scope. - getter node : Crystal::ASTNode - - delegate to_s, to: node - delegate location, to: node - delegate end_location, to: node - - def_equals_and_hash node, location - - # Creates a new scope. Accepts the AST node and the outer scope. - # - # ``` - # scope = Scope.new(class_node, nil) - # ``` - def initialize(@node, @outer_scope = nil) - @outer_scope.try &.inner_scopes.<<(self) - end - - # Creates a new variable in the current scope. - # - # ``` - # scope = Scope.new(class_node, nil) - # scope.add_variable(var_node) - # ``` - def add_variable(node) - variables << Variable.new(node, self) - end - - # Creates a new argument in the current scope. - # - # ``` - # scope = Scope.new(class_node, nil) - # scope.add_argument(arg_node) - # ``` - def add_argument(node) - add_variable Crystal::Var.new(node.name).at(node) - arguments << Argument.new(node, variables.last) - end - - # Adds a new instance variable to the current scope. - # - # ``` - # scope = Scope.new(class_node, nil) - # scope.add_ivariable(ivar_node) - # ``` - def add_ivariable(node) - ivariables << InstanceVariable.new(node) - end - - # Returns variable by its name or nil if it does not exist. - # - # ``` - # scope = Scope.new(class_node, nil) - # scope.find_variable("foo") - # ``` - def find_variable(name : String) - variables.find { |v| v.name == name } || outer_scope.try &.find_variable(name) - end - - # Creates a new assignment for the variable. - # - # ``` - # scope = Scope.new(class_node, nil) - # scope.assign_variable(var_name, assign_node) - # ``` - def assign_variable(name, node) - find_variable(name).try &.assign(node, self) - end - - # Returns true if current scope represents a block (or proc), - # false if not. - def block? - node.is_a?(Crystal::Block) || node.is_a?(Crystal::ProcLiteral) - end - - # Returns true if current scope represents a spawn block, e. g. - # - # ``` - # spawn do - # # ... - # end - # ``` - def spawn_block? - return false unless node.is_a?(Crystal::Block) - call = node.as(Crystal::Block).call - call.try(&.name) == "spawn" - end - - # Returns true if current scope sits inside a macro. - def in_macro? - node.is_a?(Crystal::Macro) || !!outer_scope.try(&.in_macro?) - end - - # Returns true instance variable assinged in this scope. - def assigns_ivar?(name) - arguments.find { |arg| arg.name == name } && - ivariables.find { |var| var.name == "@#{name}" } - end - - # Returns true if and only if current scope represents some - # type definition, for example a class. - def type_definition? - node.is_a?(Crystal::ClassDef) || - node.is_a?(Crystal::ModuleDef) || - node.is_a?(Crystal::LibDef) || - node.is_a?(Crystal::FunDef) || - node.is_a?(Crystal::TypeDef) || - node.is_a?(Crystal::CStructOrUnionDef) - end - - # Returns true if current scope (or any of inner scopes) references variable, - # false if not. - def references?(variable : Variable) - variable.references.any? do |reference| - reference.scope == self || - inner_scopes.any?(&.references? variable) - end || variable.used_in_macro? - end - - # Returns true if current scope is a def, false if not. - def def? - node.is_a? Crystal::Def - end - - # Returns true if this scope is a top level scope, false if not. - def top_level? - outer_scope.nil? - end - - # Returns true if var is an argument in current scope, false if not. - def arg?(var) - case current_node = node - when Crystal::Def - var.is_a?(Crystal::Arg) && any_arg?(current_node.args, var) - when Crystal::Block - var.is_a?(Crystal::Var) && any_arg?(current_node.args, var) - when Crystal::ProcLiteral - var.is_a?(Crystal::Var) && any_arg?(current_node.def.args, var) - else - false - end - end - - private def any_arg?(args, var) - args.any? { |arg| arg.name == var.name && arg.location == var.location } - end - - # Returns true if the `node` represents exactly - # the same Crystal node as `@node`. - def eql?(node) - node == @node && - !node.location.nil? && - node.location == @node.location - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr deleted file mode 100644 index 28f55dd0ac1e..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/util.cr +++ /dev/null @@ -1,157 +0,0 @@ -# Utility module for Ameba's rules. -module Ameba::AST::Util - # Returns true if current `node` is a literal, false otherwise. - def literal?(node) - case node - when Crystal::NilLiteral, - Crystal::BoolLiteral, - Crystal::NumberLiteral, - Crystal::CharLiteral, - Crystal::StringLiteral, - Crystal::SymbolLiteral, - Crystal::RegexLiteral, - Crystal::ProcLiteral, - Crystal::MacroLiteral - true - when Crystal::RangeLiteral - literal?(node.from) && literal?(node.to) - when Crystal::ArrayLiteral, - Crystal::TupleLiteral - node.elements.all? { |el| literal?(el) } - when Crystal::HashLiteral - node.entries.all? { |entry| literal?(entry.key) && literal?(entry.value) } - when Crystal::NamedTupleLiteral - node.entries.all? { |entry| literal?(entry.value) } - else - false - end - end - - # Returns a source code for the current node. - # This method uses `node.location` and `node.end_location` - # to determine and cut a piece of source of the node. - def node_source(node, code_lines) - loc, end_loc = node.location, node.end_location - - return unless loc && end_loc - - line, column = loc.line_number - 1, loc.column_number - 1 - end_line, end_column = end_loc.line_number - 1, end_loc.column_number - 1 - node_lines = code_lines[line..end_line] - first_line, last_line = node_lines[0]?, node_lines[-1]? - - return if first_line.nil? || last_line.nil? - return if first_line.size < column # compiler reports incorrection location - - node_lines[0] = first_line.sub(0...column, "") - - if line == end_line # one line - end_column = end_column - column - last_line = node_lines[0] - end - - return if last_line.size < end_column + 1 - node_lines[-1] = last_line.sub(end_column + 1...last_line.size, "") - - node_lines - end - - # Returns true if node is a flow command, false - otherwise. - # Node represents a flow command if it is a control expression, - # or special call node that interrupts execution (i.e. raise, exit, abort). - def flow_command?(node, in_loop) - case node - when Crystal::Return - true - when Crystal::Break, Crystal::Next - in_loop - when Crystal::Call - raise?(node) || exit?(node) || abort?(node) - else - false - end - end - - # Returns true if node is a flow expression, false if not. - # Node represents a flow expression if it is full-filled by a flow command. - # - # For example, this node is a flow expression, because each branch contains - # a flow command `return`: - # - # ``` - # if a > 0 - # return :positive - # elsif a < 0 - # return :negative - # else - # return :zero - # end - # ``` - # - # This node is a not a flow expression: - # - # ``` - # if a > 0 - # return :positive - # end - # ``` - # - # That's because not all branches return(i.e. `else` is missing). - # - def flow_expression?(node, in_loop = false) - return true if flow_command? node, in_loop - - case node - when Crystal::If, Crystal::Unless - flow_expressions? [node.then, node.else], in_loop - when Crystal::BinaryOp - flow_expression? node.left, in_loop - when Crystal::Case - flow_expressions? [node.whens, node.else].flatten, in_loop - when Crystal::ExceptionHandler - flow_expressions? [node.else || node.body, node.rescues].flatten, in_loop - when Crystal::While, Crystal::Until - flow_expression? node.body, in_loop - when Crystal::Rescue, Crystal::When - flow_expression? node.body, in_loop - when Crystal::Expressions - node.expressions.any? { |exp| flow_expression? exp, in_loop } - else - false - end - end - - private def flow_expressions?(nodes, in_loop) - nodes.all? { |exp| flow_expression? exp, in_loop } - end - - # Returns true if node represents `raise` method call. - def raise?(node) - node.is_a?(Crystal::Call) && - node.name == "raise" && node.args.size == 1 && node.obj.nil? - end - - # Returns true if node represents `exit` method call. - def exit?(node) - node.is_a?(Crystal::Call) && - node.name == "exit" && node.args.size <= 1 && node.obj.nil? - end - - # Returns true if node represents `abort` method call. - def abort?(node) - node.is_a?(Crystal::Call) && - node.name == "abort" && node.args.size <= 2 && node.obj.nil? - end - - # Returns true if node represents a loop. - def loop?(node) - case node - when Crystal::While, Crystal::Until - true - when Crystal::Call - node.name == "loop" && node.args.size == 0 && node.obj.nil? - else - false - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr deleted file mode 100644 index 91204f0e2f77..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/argument.cr +++ /dev/null @@ -1,49 +0,0 @@ -module Ameba::AST - # Represents the argument of some node. - # Holds the reference to the variable, thus to scope. - # - # For example, all these vars are arguments: - # - # ``` - # def method(a, b, c = 10, &block) - # 3.times do |i| - # end - # - # ->(x : Int32) {} - # end - # ``` - class Argument - # The actual node. - getter node : Crystal::Var | Crystal::Arg - - # Variable of this argument (may be the same node) - getter variable : Variable - - delegate location, to: @node - delegate end_location, to: @node - delegate to_s, to: @node - - # Creates a new argument. - # - # ``` - # Argument.new(node, variable) - # ``` - def initialize(@node, @variable) - end - - # Returns true if the name starts with '_', false if not. - def ignored? - name.starts_with? '_' - end - - # Name of the argument. - def name - case current_node = node - when Crystal::Var then current_node.name - when Crystal::Arg then current_node.name - else - raise ArgumentError.new "invalid node" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr deleted file mode 100644 index 6908572a20e2..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/assignment.cr +++ /dev/null @@ -1,106 +0,0 @@ -require "./reference" -require "./variable" - -module Ameba::AST - # Represents the assignment to the variable. - # Holds the assign node and the variable. - class Assignment - property? referenced = false - - # The actual assignment node. - getter node : Crystal::ASTNode - - # Variable of this assignment. - getter variable : Variable - - # Branch of this assignment. - getter branch : Branch? - - # A scope assignment belongs to - getter scope : Scope - - delegate to_s, to: @node - delegate location, to: @node - delegate end_location, to: @node - - # Creates a new assignment. - # - # ``` - # Assignment.new(node, variable, scope) - # ``` - # - def initialize(@node, @variable, @scope) - if scope = @variable.scope - @branch = Branch.of(@node, scope) - @referenced = true if @variable.special? || - @variable.scope.type_definition? || - referenced_in_loop? - end - end - - def referenced_in_loop? - @variable.referenced? && @branch.try &.in_loop? - end - - # Returns true if this assignment is an op assign, false if not. - # For example, this is an op assign: - # - # ``` - # a ||= 1 - # ``` - def op_assign? - node.is_a? Crystal::OpAssign - end - - # Returns true if this assignment is in a branch, false if not. - # For example, this assignment is in a branch: - # - # ``` - # a = 1 if a.nil? - # ``` - def in_branch? - !branch.nil? - end - - # Returns the target node of the variable in this assignment. - def target_node - case assign = node - when Crystal::Assign then assign.target - when Crystal::OpAssign then assign.target - when Crystal::UninitializedVar then assign.var - when Crystal::MultiAssign - assign.targets.find(node) do |target| - target.is_a?(Crystal::Var) && target.name == variable.name - end - else - node - end - end - - # Indicates whether the node is a transformed assignment by the compiler. - # i.e. - # - # ``` - # collection.each do |(a, b)| - # puts b - # end - # ``` - # - # is transformed to: - # - # ``` - # collection.each do |__arg0| - # a = __arg0[0] - # b = __arg0[1] - # puts(b) - # end - # ``` - # - def transformed? - return false unless (assign = node).is_a?(Crystal::Assign) - return false unless (value = assign.value).is_a?(Crystal::Call) - return false unless (obj = value.obj).is_a?(Crystal::Var) - obj.name.starts_with? "__arg" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr deleted file mode 100644 index 836ad345fabb..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/ivariable.cr +++ /dev/null @@ -1,13 +0,0 @@ -module Ameba::AST - class InstanceVariable - getter node : Crystal::InstanceVar - - delegate location, to: @node - delegate end_location, to: @node - delegate name, to: @node - delegate to_s, to: @node - - def initialize(@node) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr deleted file mode 100644 index 885234d9b11e..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/reference.cr +++ /dev/null @@ -1,10 +0,0 @@ -require "./variable" - -module Ameba::AST - # Represents a reference to the variable. - # It behaves like a variable is used to distinguish a - # the variable from its reference. - class Reference < Variable - property? explicit = true - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr deleted file mode 100644 index 1a7eab42a3ed..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/variabling/variable.cr +++ /dev/null @@ -1,198 +0,0 @@ -module Ameba::AST - # Represents the existence of the local variable. - # Holds the var node and variable assigments. - class Variable - # List of the assigments of this variable. - getter assignments = [] of Assignment - - # List of the references of this variable. - getter references = [] of Reference - - # The actual var node. - getter node : Crystal::Var - - # Scope of this variable. - getter scope : Scope - - # Node of the first assignment which can be available before any reference. - getter assign_before_reference : Crystal::ASTNode? - - delegate location, to: @node - delegate end_location, to: @node - delegate name, to: @node - delegate to_s, to: @node - - # Creates a new variable(in the scope). - # - # ``` - # Variable.new(node, scope) - # ``` - # - def initialize(@node, @scope) - end - - # Returns true if it is a special variable, i.e `$?`. - def special? - @node.special_var? - end - - # Assigns the variable (creates a new assignment). - # Variable may have multiple assignments. - # - # ``` - # variable = Variable.new(node, scope) - # variable.assign(node1) - # variable.assign(node2) - # variable.assignment.size # => 2 - # ``` - # - def assign(node, scope) - assignments << Assignment.new(node, self, scope) - - update_assign_reference! - end - - # Returns true if variable has any reference. - # - # ``` - # variable = Variable.new(node, scope) - # variable.reference(var_node) - # variable.referenced? # => true - # ``` - def referenced? - references.any? - end - - # Creates a reference to this variable in some scope. - # - # ``` - # variable = Variable.new(node, scope) - # variable.reference(var_node, some_scope) - # ``` - # - def reference(node : Crystal::Var, scope : Scope) - Reference.new(node, scope).tap do |reference| - references << reference - scope.references << reference - end - end - - # Reference variable's assignments. - # - # ``` - # variable = Variable.new(node, scope) - # variable.assign(assign_node) - # variable.reference_assignments! - # ``` - def reference_assignments! - consumed_branches = Set(Branch).new - - assignments.reverse_each do |assignment| - next if consumed_branches.includes?(assignment.branch) - assignment.referenced = true - - break unless assignment.branch - consumed_branches << assignment.branch.not_nil! - end - end - - # Returns true if the current var is referenced in - # in the block. For example this variable is captured - # by block: - # - # ``` - # a = 1 - # 3.times { |i| a = a + i } - # ``` - # - # And this variable is not captured by block. - # - # ``` - # i = 1 - # 3.times { |i| i + 1 } - # ``` - def captured_by_block?(scope = @scope) - scope.inner_scopes.each do |inner_scope| - return true if inner_scope.block? && inner_scope.references?(self) - return true if captured_by_block?(inner_scope) - end - - false - end - - # Returns true if current variable potentially referenced in a macro literal, - # false if not. - def used_in_macro?(scope = @scope) - scope.inner_scopes.each do |inner_scope| - return true if MacroLiteralFinder.new(inner_scope.node).references? node - end - return true if (outer_scope = scope.outer_scope) && used_in_macro?(outer_scope) - false - end - - # Returns true if the variable is a target (on the left) of the assignment, - # false otherwise. - def target_of?(assign) - case assign - when Crystal::Assign then eql?(assign.target) - when Crystal::OpAssign then eql?(assign.target) - when Crystal::MultiAssign then assign.targets.any? { |t| eql?(t) } - when Crystal::UninitializedVar then eql?(assign.var) - else - false - end - end - - # Returns true if the name starts with '_', false if not. - def ignored? - name.starts_with? '_' - end - - # Returns true if the `node` represents exactly - # the same Crystal node as `@node`. - def eql?(node) - node.is_a?(Crystal::Var) && - node.name == @node.name && - node.location == @node.location - end - - # Returns true if the variable is delcared before the `node`. - def declared_before?(node) - var_location, node_location = location, node.location - - return if var_location.nil? || node_location.nil? - - (var_location.line_number < node_location.line_number) || - (var_location.line_number == node_location.line_number && - var_location.column_number < node_location.column_number) - end - - private class MacroLiteralFinder < Crystal::Visitor - @macro_literals = [] of Crystal::MacroLiteral - - def initialize(node) - node.accept self - end - - def references?(node : Crystal::Var) - @macro_literals.any? { |literal| literal.value.includes? node.name } - end - - def visit(node : Crystal::ASTNode) - true - end - - def visit(node : Crystal::MacroLiteral) - @macro_literals << node - end - end - - private def update_assign_reference! - if @assign_before_reference.nil? && - references.size <= assignments.size && - assignments.none? { |ass| ass.op_assign? } - @assign_before_reference = assignments.find { |ass| !ass.in_branch? }.try &.node - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr deleted file mode 100644 index 20a2ef5afc20..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/base_visitor.cr +++ /dev/null @@ -1,29 +0,0 @@ -require "compiler/crystal/syntax/*" - -# A module that helps to traverse Crystal AST using `Crystal::Visitor`. -module Ameba::AST - # An abstract base visitor that utilizes general logic for all visitors. - abstract class BaseVisitor < Crystal::Visitor - # A corresponding rule that uses this visitor. - @rule : Rule::Base - - # A source that needs to be traversed. - @source : Source - - # Creates instance of this visitor. - # - # ``` - # visitor = Ameba::AST::NodeVisitor.new(rule, source) - # ``` - # - def initialize(@rule, @source) - @source.ast.accept self - end - - # A main visit method that accepts `Crystal::ASTNode`. - # Returns true meaning all child nodes will be traversed. - def visit(node : Crystal::ASTNode) - true - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr deleted file mode 100644 index 24e560e18be1..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/counting_visitor.cr +++ /dev/null @@ -1,39 +0,0 @@ -module Ameba::AST - # AST Visitor that counts occurrences of certain keywords - class CountingVisitor < Crystal::Visitor - DEFAULT_COMPLEXITY = 1 - getter macro_condition = false - - # Creates a new counting visitor - def initialize(@scope : Crystal::ASTNode) - @complexity = DEFAULT_COMPLEXITY - end - - # :nodoc: - def visit(node : Crystal::ASTNode) - true - end - - # Returns the number of keywords that were found in the node - def count - @scope.accept(self) - @complexity - end - - # Uses the same logic than rubocop. See - # https://github.com/rubocop-hq/rubocop/blob/master/lib/rubocop/cop/metrics/cyclomatic_complexity.rb#L21 - # Except "for", because crystal doesn't have a "for" loop. - {% for node in %i(if while until rescue when or and) %} - # :nodoc: - def visit(node : Crystal::{{ node.id.capitalize }}) - @complexity += 1 unless macro_condition - end - {% end %} - - def visit(node : Crystal::MacroIf | Crystal::MacroFor) - @macro_condition = true - @complexity = DEFAULT_COMPLEXITY - false - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr deleted file mode 100644 index f75e0d6473cc..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/flow_expression_visitor.cr +++ /dev/null @@ -1,67 +0,0 @@ -require "../util" -require "./base_visitor" - -module Ameba::AST - # AST Visitor that traverses all the flow expressions. - class FlowExpressionVisitor < BaseVisitor - include Util - - @loop_stack = [] of Crystal::ASTNode - - # Creates a new flow expression visitor. - def initialize(@rule, @source) - @source.ast.accept self - end - - # :nodoc: - def visit(node) - if flow_expression?(node, in_loop?) - @rule.test @source, node, FlowExpression.new(node, in_loop?) - end - - true - end - - # :nodoc: - def visit(node : Crystal::While) - on_loop_started(node) - end - - # :nodoc: - def visit(node : Crystal::Until) - on_loop_started(node) - end - - # :nodoc: - def visit(node : Crystal::Call) - on_loop_started(node) if loop?(node) - end - - # :nodoc: - def end_visit(node : Crystal::While) - on_loop_ended(node) - end - - # :nodoc: - def end_visit(node : Crystal::Until) - on_loop_ended(node) - end - - # :nodoc: - def end_visit(node : Crystal::Call) - on_loop_ended(node) if loop?(node) - end - - private def on_loop_started(node) - @loop_stack.push(node) - end - - private def on_loop_ended(node) - @loop_stack.pop? - end - - private def in_loop? - @loop_stack.any? - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr deleted file mode 100644 index 47bb343da8e8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/node_visitor.cr +++ /dev/null @@ -1,61 +0,0 @@ -require "./base_visitor" - -module Ameba::AST - # List of nodes to be visited by Ameba's rules. - NODES = [ - Alias, - IsA, - Assign, - Call, - Block, - Case, - ClassDef, - ClassVar, - Def, - EnumDef, - ExceptionHandler, - Expressions, - HashLiteral, - If, - InstanceVar, - LibDef, - ModuleDef, - NilLiteral, - StringInterpolation, - Unless, - Var, - When, - While, - Until, - ] - - # An AST Visitor that traverses the source and allows all nodes - # to be inspected by rules. - # - # ``` - # visitor = Ameba::AST::NodeVisitor.new(rule, source) - # ``` - # - class NodeVisitor < BaseVisitor - @skip : Array(Crystal::ASTNode.class)? - - def initialize(@rule, @source, skip = nil) - @skip = skip.try &.map { |el| el.as(Crystal::ASTNode.class) } - super @rule, @source - end - - {% for name in NODES %} - # A visit callback for `Crystal::{{name}}` node. - # Returns true meaning that child nodes will be traversed as well. - def visit(node : Crystal::{{name}}) - @rule.test @source, node - true - end - {% end %} - - def visit(node) - return true unless (skip = @skip) - !skip.includes?(node.class) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr deleted file mode 100644 index a1ef1c3dfd6a..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/redundant_control_expression_visitor.cr +++ /dev/null @@ -1,62 +0,0 @@ -module Ameba::AST - # A class that utilizes a logic to traverse AST nodes and - # fire a source test callback if a redundant `Crystal::ControlExpression` - # is reached. - class RedundantControlExpressionVisitor - # A corresponding rule that uses this visitor. - getter rule : Rule::Base - - # A source that needs to be traversed. - getter source : Source - - # A node to run traversal on. - getter node : Crystal::ASTNode - - def initialize(@rule, @source, @node) - traverse_node node - end - - private def traverse_control_expression(node) - @rule.test(@source, node, self) - end - - private def traverse_node(node) - case node - when Crystal::ControlExpression then traverse_control_expression node - when Crystal::Expressions then traverse_expressions node - when Crystal::If, Crystal::Unless then traverse_condition node - when Crystal::Case then traverse_case node - when Crystal::BinaryOp then traverse_binary_op node - when Crystal::ExceptionHandler then traverse_exception_handler node - else - # ok - end - end - - private def traverse_expressions(node) - traverse_node node.expressions.last? - end - - private def traverse_condition(node) - return if node.else.nil? || node.else.nop? - - traverse_node(node.then) - traverse_node(node.else) - end - - private def traverse_case(node) - node.whens.each { |n| traverse_node n.body } - traverse_node(node.else) - end - - private def traverse_binary_op(node) - traverse_node(node.right) - end - - private def traverse_exception_handler(node) - traverse_node node.body - traverse_node node.else - node.rescues.try &.each { |n| traverse_node n.body } - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr deleted file mode 100644 index 084e83aea242..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/ast/visitors/scope_visitor.cr +++ /dev/null @@ -1,185 +0,0 @@ -require "./base_visitor" - -module Ameba::AST - # AST Visitor that traverses the source and constructs scopes. - class ScopeVisitor < BaseVisitor - SUPER_NODE_NAME = "super" - RECORD_NODE_NAME = "record" - - @scope_queue = [] of Scope - - @current_scope : Scope - - def initialize(@rule, @source) - @current_scope = Scope.new(@source.ast) # top level scope - @source.ast.accept self - @scope_queue.each { |scope| @rule.test @source, scope.node, scope } - end - - private def on_scope_enter(node) - @current_scope = Scope.new(node, @current_scope) - end - - private def on_scope_end(node) - @scope_queue << @current_scope - - # go up if this is not a top level scope - if outer_scope = @current_scope.outer_scope - @current_scope = outer_scope - end - end - - private def on_assign_end(target, node) - target.is_a?(Crystal::Var) && @current_scope.assign_variable(target.name, node) - end - - # :nodoc: - def end_visit(node : Crystal::ASTNode) - on_scope_end(node) if @current_scope.eql?(node) - end - - # :nodoc: - def visit(node : Crystal::ClassDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::ModuleDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::EnumDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::LibDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::FunDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::TypeDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::TypeOf) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::CStructOrUnionDef) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::Def) - node.name == "->" || on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::ProcLiteral) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::Block) - on_scope_enter(node) - end - - # :nodoc: - def visit(node : Crystal::Macro) - on_scope_enter(node) - end - - @current_assign : Crystal::ASTNode? - - # :nodoc: - def visit(node : Crystal::Assign | Crystal::OpAssign | Crystal::MultiAssign | Crystal::UninitializedVar) - @current_assign = node - end - - # :nodoc: - def end_visit(node : Crystal::Assign | Crystal::OpAssign) - on_assign_end(node.target, node) - @current_assign = nil - on_scope_end(node) if @current_scope.eql?(node) - end - - # :nodoc: - def end_visit(node : Crystal::MultiAssign) - node.targets.each { |target| on_assign_end(target, node) } - @current_assign = nil - on_scope_end(node) if @current_scope.eql?(node) - end - - # :nodoc: - def end_visit(node : Crystal::UninitializedVar) - on_assign_end(node.var, node) - @current_assign = nil - on_scope_end(node) if @current_scope.eql?(node) - end - - # :nodoc: - def visit(node : Crystal::TypeDeclaration) - if !@current_scope.type_definition? && (var = node.var).is_a?(Crystal::Var) - @current_scope.add_variable var - end - end - - # :nodoc: - def visit(node : Crystal::Arg) - @current_scope.add_argument node - end - - # :nodoc: - def visit(node : Crystal::InstanceVar) - @current_scope.add_ivariable(node) - end - - # :nodoc: - def visit(node : Crystal::Var) - variable = @current_scope.find_variable node.name - - if @current_scope.arg?(node) # node is an argument - @current_scope.add_argument(node) - elsif variable.nil? && @current_assign # node is a variable - @current_scope.add_variable(node) - elsif variable # node is a reference - reference = variable.reference node, @current_scope - if @current_assign.is_a?(Crystal::OpAssign) || !reference.target_of?(@current_assign) - variable.reference_assignments! - end - end - end - - # :nodoc: - def visit(node : Crystal::Call) - case - when @current_scope.def? - if node.name == SUPER_NODE_NAME && node.args.empty? - @current_scope.arguments.each do |arg| - variable = arg.variable - variable.reference(variable.node, @current_scope).explicit = false - end - end - - true - when @current_scope.top_level? && record_macro?(node) - false - else - true - end - end - - private def record_macro?(node) - node.name == RECORD_NODE_NAME && node.args.first?.is_a?(Crystal::Path) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr deleted file mode 100644 index 42778da287b3..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/cli/cmd.cr +++ /dev/null @@ -1,152 +0,0 @@ -require "../../ameba" -require "option_parser" - -# :nodoc: -module Ameba::Cli - extend self - - def run(args = ARGV) - opts = parse_args args - config = Config.load opts.config, opts.colors? - config.globs = opts.globs.not_nil! if opts.globs - config.severity = opts.fail_level.not_nil! if opts.fail_level - - configure_formatter(config, opts) - configure_rules(config, opts) - - runner = Ameba.run(config) - - if location = opts.location_to_explain - runner.explain(location) - else - exit 1 unless runner.success? - end - rescue e - puts "Error: #{e.message}" - exit 255 - end - - private class Opts - property config = Config::PATH - property formatter : Symbol | String | Nil - property globs : Array(String)? - property only : Array(String)? - property except : Array(String)? - property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)? - property fail_level : Severity? - property? all = false - property? colors = true - property? without_affected_code = false - end - - def parse_args(args, opts = Opts.new) - OptionParser.parse(args) do |parser| - parser.banner = "Usage: ameba [options] [file1 file2 ...]" - - parser.on("-v", "--version", "Print version") { print_version } - parser.on("-h", "--help", "Show this help") { show_help parser } - parser.on("-s", "--silent", "Disable output") { opts.formatter = :silent } - parser.unknown_args do |f| - if f.size == 1 && f.first =~ /.+:\d+:\d+/ - configure_explain_opts(f.first, opts) - else - opts.globs = f if f.any? - end - end - - parser.on("-c", "--config PATH", - "Specify a configuration file") do |path| - opts.config = path unless opts.config.empty? - end - - parser.on("-f", "--format FORMATTER", - "Choose an output formatter: #{Config.formatter_names}") do |formatter| - opts.formatter = formatter - end - - parser.on("--only RULE1,RULE2,...", - "Run only given rules (or groups)") do |rules| - opts.only = rules.split "," - end - - parser.on("--except RULE1,RULE2,...", - "Disable the given rules (or groups)") do |rules| - opts.except = rules.split "," - end - - parser.on("--all", "Enables all available rules") do - opts.all = true - end - - parser.on("--gen-config", - "Generate a configuration file acting as a TODO list") do - opts.formatter = :todo - opts.config = "" - end - - parser.on("--fail-level SEVERITY", "Change the level of failure to exit. Defaults to Convention") do |level| - opts.fail_level = Severity.parse(level) - end - - parser.on("-e", "--explain PATH:line:column", - "Explain an issue at a specified location") do |loc| - configure_explain_opts(loc, opts) - end - - parser.on("--without-affected-code", - "Stop showing affected code while using a default formatter") do - opts.without_affected_code = true - end - - parser.on("--no-color", "Disable colors") do - opts.colors = false - end - end - - opts - end - - private def configure_rules(config, opts) - if only = opts.only - config.rules.map! { |r| r.enabled = false; r } - config.update_rules(only, enabled: true) - elsif opts.all? - config.rules.map! { |r| r.enabled = true; r } - end - - config.update_rules(opts.except, enabled: false) - end - - private def configure_formatter(config, opts) - if name = opts.formatter - config.formatter = name - end - config.formatter.config[:without_affected_code] = opts.without_affected_code? - end - - private def configure_explain_opts(loc, opts) - location_to_explain = parse_explain_location(loc) - opts.location_to_explain = location_to_explain - opts.globs = [location_to_explain[:file]] - opts.formatter = :silent - end - - private def parse_explain_location(arg) - location = arg.split(":", remove_empty: true).map &.strip - raise ArgumentError.new unless location.size === 3 - file, line, column = location - {file: file, line: line.to_i, column: column.to_i} - rescue - raise "location should have PATH:line:column format" - end - - private def print_version - puts VERSION - exit 0 - end - - private def show_help(parser) - puts parser - exit 0 - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr deleted file mode 100644 index 1ecbd3a8cea8..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/config.cr +++ /dev/null @@ -1,280 +0,0 @@ -require "yaml" -require "./glob_utils" - -# A configuration entry for `Ameba::Runner`. -# -# Config can be loaded from configuration YAML file and adjusted. -# -# ``` -# config = Config.load -# config.formatter = my_formatter -# ``` -# -# By default config loads `.ameba.yml` file in a current directory. -# -class Ameba::Config - include GlobUtils - - AVAILABLE_FORMATTERS = { - progress: Formatter::DotFormatter, - todo: Formatter::TODOFormatter, - flycheck: Formatter::FlycheckFormatter, - silent: Formatter::BaseFormatter, - disabled: Formatter::DisabledFormatter, - json: Formatter::JSONFormatter, - } - - PATH = ".ameba.yml" - - DEFAULT_GLOBS = %w( - **/*.cr - !lib - ) - - setter formatter : Formatter::BaseFormatter? - getter rules : Array(Rule::Base) - property severity = Severity::Convention - - # Returns a list of paths (with wildcards) to files. - # Represents a list of sources to be inspected. - # If globs are not set, it will return default list of files. - # - # ``` - # config = Ameba::Config.load - # config.globs = ["**/*.cr"] - # config.globs - # ``` - property globs : Array(String) - - # Represents a list of paths to exclude from globs. - # Can have wildcards. - # - # ``` - # config = Ameba::Config.load - # config.excluded = ["spec", "src/server/*.cr"] - # ``` - property excluded : Array(String) - - @rule_groups : Hash(String, Array(Rule::Base)) - - # Creates a new instance of `Ameba::Config` based on YAML parameters. - # - # `Config.load` uses this constructor to instantiate new config by YAML file. - protected def initialize(config : YAML::Any) - @rules = Rule.rules.map &.new(config).as(Rule::Base) - @rule_groups = @rules.group_by &.group - @excluded = load_array_section(config, "Excluded") - @globs = load_array_section(config, "Globs", DEFAULT_GLOBS) - - self.formatter = load_formatter_name(config) - end - - # Loads YAML configuration file by `path`. - # - # ``` - # config = Ameba::Config.load - # ``` - # - def self.load(path = PATH, colors = true) - Colorize.enabled = colors - content = File.exists?(path) ? File.read path : "{}" - Config.new YAML.parse(content) - rescue e - raise "Config file is invalid: #{e.message}" - end - - def self.formatter_names - AVAILABLE_FORMATTERS.keys.join("|") - end - - # Returns a list of sources matching globs and excluded sections. - # - # ``` - # config = Ameba::Config.load - # config.sources # => list of default sources - # config.globs = ["**/*.cr"] - # config.excluded = ["spec"] - # config.sources # => list of sources pointing to files found by the wildcards - # ``` - # - def sources - (find_files_by_globs(globs) - find_files_by_globs(excluded)) - .map { |path| Source.new File.read(path), path } - end - - # Returns a formatter to be used while inspecting files. - # If formatter is not set, it will return default formatter. - # - # ``` - # config = Ameba::Config.load - # config.formatter = custom_formatter - # config.formatter - # ``` - # - def formatter - @formatter ||= Formatter::DotFormatter.new - end - - # Sets formatter by name. - # - # ``` - # config = Ameba::Config.load - # config.formatter = :progress - # ``` - # - def formatter=(name : String | Symbol) - if f = AVAILABLE_FORMATTERS[name]? - @formatter = f.new - else - raise "Unknown formatter `#{name}`. Use one of #{Config.formatter_names}." - end - end - - # Updates rule properties. - # - # ``` - # config = Ameba::Config.load - # config.update_rule "MyRuleName", enabled: false - # ``` - # - def update_rule(name, enabled = true, excluded = nil) - index = @rules.index { |r| r.name == name } - raise ArgumentError.new("Rule `#{name}` does not exist") unless index - - rule = @rules[index] - rule.enabled = enabled - rule.excluded = excluded - @rules[index] = rule - end - - # Updates rules properties. - # - # ``` - # config = Ameba::Config.load - # config.update_rules %w(Rule1 Rule2), enabled: true - # ``` - # - # also it allows to update groups of rules: - # - # ``` - # config.update_rules %w(Group1 Group2), enabled: true - # ``` - # - def update_rules(names, **args) - names.try &.each do |name| - if group = @rule_groups[name]? - group.each { |rule| update_rule(rule.name, **args) } - else - update_rule name, **args - end - end - end - - private def load_formatter_name(config) - name = config["Formatter"]?.try &.["Name"]? - name ? name.to_s : nil - end - - private def load_array_section(config, section_name, default = [] of String) - case value = config[section_name]? - when .nil? then default - when .as_s? then [value.to_s] - when .as_a? then value.as_a.map(&.as_s) - else - raise "incorrect '#{section_name}' section in a config files" - end - end - - # :nodoc: - module RuleConfig - macro properties(&block) - {% definitions = [] of NamedTuple %} - {% if block.body.is_a? Assign %} - {% definitions << {var: block.body.target, value: block.body.value} %} - {% elsif block.body.is_a? Call %} - {% definitions << {var: block.body.name, value: block.body.args.first} %} - {% elsif block.body.is_a? TypeDeclaration %} - {% definitions << {var: block.body.var, value: block.body.value, type: block.body.type} %} - {% elsif block.body.is_a? Expressions %} - {% for prop in block.body.expressions %} - {% if prop.is_a? Assign %} - {% definitions << {var: prop.target, value: prop.value} %} - {% elsif prop.is_a? Call %} - {% definitions << {var: prop.name, value: prop.args.first} %} - {% elsif prop.is_a? TypeDeclaration %} - {% definitions << {var: prop.var, value: prop.value, type: prop.type} %} - {% end %} - {% end %} - {% end %} - - {% properties = {} of MacroId => NamedTuple %} - {% for df in definitions %} - {% name = df[:var].id %} - {% key = name.camelcase.stringify %} - {% value = df[:value] %} - {% type = df[:type] %} - {% converter = nil %} - - {% if key == "Severity" %} - {% type = Severity %} - {% converter = SeverityYamlConverter %} - {% end %} - - {% if type == nil %} - {% if value.is_a? BoolLiteral %} - {% type = Bool %} - {% elsif value.is_a? StringLiteral %} - {% type = String %} - {% elsif value.is_a? NumberLiteral %} - {% if value.kind == :i32 %} - {% type = Int32 %} - {% elsif value.kind == :i64 %} - {% type = Int64 %} - {% elsif value.kind == :f32 %} - {% type = Float32 %} - {% elsif value.kind == :f64 %} - {% type = Float64 %} - {% end %} - {% end %} - - {% type = Nil if type == nil %} - {% end %} - - {% properties[name] = {key: key, default: value, type: type, converter: converter} %} - - @[YAML::Field(key: {{key}}, converter: {{converter}}, type: {{type}})] - property {{name}} : {{type}} = {{value}} - {% end %} - - {% if properties["enabled".id] == nil %} - @[YAML::Field(key: "Enabled")] - property enabled = true - {% end %} - - {% if properties["severity".id] == nil %} - {% default = @type.name.starts_with?("Ameba::Rule::Lint") ? "Ameba::Severity::Warning".id : "Ameba::Severity::Convention".id %} - @[YAML::Field(key: "Severity", converter: Ameba::SeverityYamlConverter)] - property severity = {{default}} - {% end %} - - {% if properties["excluded".id] == nil %} - @[YAML::Field(key: "Excluded")] - property excluded : Array(String)? - {% end %} - end - - macro included - macro inherited - include YAML::Serializable - include YAML::Serializable::Strict - - def self.new(config = nil) - if (raw = config.try &.raw).is_a? Hash - yaml = raw[rule_name]?.try &.to_yaml - end - from_yaml yaml || "{}" - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr deleted file mode 100644 index 4009c9921efb..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/base_formatter.cr +++ /dev/null @@ -1,32 +0,0 @@ -require "./util" - -# A module that utilizes Ameba's formatters. -module Ameba::Formatter - # A base formatter for all formatters. It uses `output` IO - # to report results and also implements stub methods for - # callbacks in `Ameba::Runner#run` method. - class BaseFormatter - # TODO: allow other IOs - getter output : IO::FileDescriptor | IO::Memory - getter config = {} of Symbol => String | Bool - - def initialize(@output = STDOUT) - end - - # Callback that indicates when inspecting is started. - # A list of sources to inspect is passed as an argument. - def started(sources); end - - # Callback that indicates when source inspection is finished. - # A corresponding source is passed as an argument. - def source_finished(source : Source); end - - # Callback that indicates when source inspection is finished. - # A corresponding source is passed as an argument. - def source_started(source : Source); end - - # Callback that indicates when inspection is finished. - # A list of inspected sources is passed as an argument. - def finished(sources); end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr deleted file mode 100644 index cbe7905b4a61..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/disabled_formatter.cr +++ /dev/null @@ -1,17 +0,0 @@ -module Ameba::Formatter - # A formatter that shows all disabled lines by inline directives. - class DisabledFormatter < BaseFormatter - def finished(sources) - output << "Disabled rules using inline directives: \n\n" - - sources.each do |source| - source.issues.select(&.disabled?).each do |e| - if loc = e.location - output << "#{source.path}:#{loc.line_number}".colorize(:cyan) - output << " #{e.rule.name}\n" - end - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr deleted file mode 100644 index 396069e53bc6..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/dot_formatter.cr +++ /dev/null @@ -1,96 +0,0 @@ -require "./util" - -module Ameba::Formatter - # A formatter that shows a progress of inspection in a terminal using dots. - # It is similar to Crystal's dot formatter for specs. - class DotFormatter < BaseFormatter - include Util - - @started_at : Time? - @mutex = Thread::Mutex.new - - # Reports a message when inspection is started. - def started(sources) - @started_at = Time.utc # Time.monotonic - - output << started_message(sources.size) - end - - # Reports a result of the inspection of a corresponding source. - def source_finished(source : Source) - sym = source.valid? ? ".".colorize(:green) : "F".colorize(:red) - @mutex.synchronize { output << sym } - end - - # Reports a message when inspection is finished. - def finished(sources) - output.flush - output << "\n\n" - - show_affected_code = !config[:without_affected_code]? - failed_sources = sources.reject &.valid? - - failed_sources.each do |source| - source.issues.each do |issue| - next if issue.disabled? - next if (location = issue.location).nil? - - output << "#{location}\n".colorize(:cyan) - output << "[#{issue.rule.severity.symbol}] #{issue.rule.name}: #{issue.message}\n".colorize(:red) - - if show_affected_code && (code = affected_code(source, location)) - output << code.colorize(:default) - end - - output << "\n" - end - end - - output << finished_in_message(@started_at, Time.utc) # Time.monotonic - output << final_message(sources, failed_sources) - end - - private def started_message(size) - if size == 1 - "Inspecting 1 file.\n\n".colorize(:default) - else - "Inspecting #{size} files.\n\n".colorize(:default) - end - end - - private def finished_in_message(started, finished) - if started && finished - "Finished in #{to_human(finished - started)} \n\n".colorize(:default) - end - end - - private def to_human(span : Time::Span) - total_milliseconds = span.total_milliseconds - if total_milliseconds < 1 - return "#{(span.total_milliseconds * 1_000).round.to_i} microseconds" - end - - total_seconds = span.total_seconds - if total_seconds < 1 - return "#{span.total_milliseconds.round(2)} milliseconds" - end - - if total_seconds < 60 - return "#{total_seconds.round(2)} seconds" - end - - minutes = span.minutes - seconds = span.seconds - "#{minutes}:#{seconds < 10 ? "0" : ""}#{seconds} minutes" - end - - private def final_message(sources, failed_sources) - total = sources.size - failures = failed_sources.map { |f| f.issues.size }.sum - color = failures == 0 ? :green : :red - s = failures != 1 ? "s" : "" - - "#{total} inspected, #{failures} failure#{s}.\n".colorize color - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr deleted file mode 100644 index 6670245ba576..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/explain_formatter.cr +++ /dev/null @@ -1,83 +0,0 @@ -require "./util" - -module Ameba::Formatter - # A formatter that shows the detailed explanation of the issue at - # a specific location. - class ExplainFormatter - LINE_BREAK = "\n" - HEADING = "## " - PREFIX = " " - - include Util - - getter output : IO::FileDescriptor | IO::Memory - getter location : Crystal::Location - - # Creates a new instance of ExplainFormatter. - # Accepts *output* which indicates the io where the explanation will be wrtitten to. - # Second argument is *location* which indicates the location to explain. - # - # ``` - # ExplainFormatter.new output, - # {file: path, line: line_number, column: column_number} - # ``` - # - def initialize(@output, loc) - @location = Crystal::Location.new(loc[:file], loc[:line], loc[:column]) - end - - # Reports the explainations at the *@location*. - def finished(sources) - source = sources.find { |s| s.path == @location.filename } - - return unless source - - source.issues.each do |issue| - if (location = issue.location) && - location.line_number == @location.line_number && - location.column_number == @location.column_number - explain(source, issue) - end - end - end - - private def explain(source, issue) - rule = issue.rule - - output_title "ISSUE INFO" - output_paragraph [ - issue.message.colorize(:red).to_s, - @location.to_s.colorize(:cyan).to_s, - ] - - if affected_code = affected_code(source, @location) - output_title "AFFECTED CODE" - output_paragraph affected_code - end - - if rule.responds_to?(:description) - output_title "RULE INFO" - output_paragraph [rule.severity.to_s, rule.name, rule.description] - end - - output_title "DETAILED DESCRIPTION" - output_paragraph(rule.class.parsed_doc || "TO BE DONE...") - end - - private def output_title(title) - output << HEADING.colorize(:yellow) << title.colorize(:yellow) << LINE_BREAK - output << LINE_BREAK - end - - private def output_paragraph(paragraph : String) - output_paragraph(paragraph.split(LINE_BREAK)) - end - - private def output_paragraph(paragraph : Array(String)) - paragraph.each do |line| - output << PREFIX << line << LINE_BREAK - end - output << LINE_BREAK - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr deleted file mode 100644 index 89423ff39843..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/flycheck_formatter.cr +++ /dev/null @@ -1,18 +0,0 @@ -module Ameba::Formatter - class FlycheckFormatter < BaseFormatter - @mutex = Mutex.new - - def source_finished(source : Source) - source.issues.each do |e| - next if e.disabled? - if loc = e.location - @mutex.synchronize do - output.printf "%s:%d:%d: %s: [%s] %s\n", - source.path, loc.line_number, loc.column_number, e.rule.severity.symbol, - e.rule.name, e.message.gsub("\n", " ") - end - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr deleted file mode 100644 index 211dabb98555..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/json_formatter.cr +++ /dev/null @@ -1,152 +0,0 @@ -require "json" - -module Ameba::Formatter - # A formatter that produces the result in a json format. - # - # Example: - # - # ``` - # { - # "metadata": { - # "ameba_version": "x.x.x", - # "crystal_version": "x.x.x", - # }, - # "sources": [ - # { - # "issues": [ - # { - # "location": { - # "column": 7, - # "line": 17, - # }, - # "end_location": { - # "column": 20, - # "line": 17, - # }, - # "message": "Useless assignment to variable `a`", - # "rule_name": "UselessAssign", - # "severity": "Convention", - # }, - # { - # "location": { - # "column": 7, - # "line": 18, - # }, - # "end_location": { - # "column": 8, - # "line": 18, - # }, - # "message": "Useless assignment to variable `a`", - # "rule_name": "UselessAssign", - # }, - # { - # "location": { - # "column": 7, - # "line": 19, - # }, - # "end_location": { - # "column": 9, - # "line": 19, - # }, - # "message": "Useless assignment to variable `a`", - # "rule_name": "UselessAssign", - # "severity": "Convention", - # }, - # ], - # "path": "src/ameba/formatter/json_formatter.cr", - # }, - # ], - # "summary": { - # "issues_count": 3, - # "target_sources_count": 1, - # }, - # } - # ``` - # - class JSONFormatter < BaseFormatter - def initialize(@output = STDOUT) - @result = AsJSON::Result.new - end - - def started(sources) - @result.summary.target_sources_count = sources.size - end - - def source_finished(source : Source) - json_source = AsJSON::Source.new source.path - - source.issues.each do |e| - next if e.disabled? - json_source.issues << AsJSON::Issue.new(e.rule.name, e.rule.severity.to_s, e.location, e.end_location, e.message) - @result.summary.issues_count += 1 - end - - @result.sources << json_source - end - - def finished(sources) - @result.to_json @output - end - end - - private module AsJSON - record Result, - sources = [] of Source, - metadata = Metadata.new, - summary = Summary.new do - def to_json(json) - {sources: sources, metadata: metadata, summary: summary}.to_json(json) - end - end - - record Source, - path : String, - issues = [] of Issue do - def to_json(json) - {path: path, issues: issues}.to_json(json) - end - end - - record Issue, - rule_name : String, - severity : String, - location : Crystal::Location?, - end_location : Crystal::Location?, - message : String do - def to_json(json) - json.object do - json.field :rule_name, rule_name - json.field :severity, severity - json.field :message, message - json.field :location, - {line: location.try &.line_number, column: location.try &.column_number} - json.field :end_location, - {line: end_location.try &.line_number, column: end_location.try &.column_number} - end - end - end - - record Metadata, - ameba_version : String = Ameba::VERSION, - crystal_version : String = Crystal::VERSION do - def to_json(json) - json.object do - json.field :ameba_version, ameba_version - json.field :crystal_version, crystal_version - end - end - end - - class Summary - property target_sources_count = 0 - property issues_count = 0 - - def to_json(json) - json.object do - json.field :target_sources_count, target_sources_count - json.field :issues_count, issues_count - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr deleted file mode 100644 index fd0b81000213..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/todo_formatter.cr +++ /dev/null @@ -1,68 +0,0 @@ -module Ameba::Formatter - # A formatter that creates a todo config. - # Basically, it takes all issues reported and disables corresponding rules - # or excludes failed sources from these rules. - class TODOFormatter < DotFormatter - def initialize(@output = STDOUT) - end - - def finished(sources) - super - issues = sources.map(&.issues).flatten - unless issues.any? { |issue| !issue.disabled? } - @output << "No issues found. File is not generated.\n" - return - end - - if issues.any? { |issue| issue.syntax? } - @output << "Unable to generate TODO file. Please fix syntax issues.\n" - return - end - - file = generate_todo_config issues - @output << "Created #{file.path}\n" - file - end - - private def generate_todo_config(issues) - file = File.new(Config::PATH, mode: "w") - file << header - rule_issues_map(issues).each do |rule, rule_issues| - file << "\n# Problems found: #{rule_issues.size}" - file << "\n# Run `ameba --only #{rule.name}` for details" - file << rule_todo(rule, rule_issues).gsub("---", "") - end - file - ensure - file.close if file - end - - private def rule_issues_map(issues) - Hash(Rule::Base, Array(Issue)).new.tap do |h| - issues.each do |issue| - next if issue.disabled? || issue.rule.is_a? Rule::Lint::Syntax - (h[issue.rule] ||= Array(Issue).new) << issue - end - end - end - - private def header - <<-HEADER - # This configuration file was generated by `ameba --gen-config` - # on #{Time.utc} using Ameba version #{VERSION}. - # The point is for the user to remove these configuration records - # one by one as the reported problems are removed from the code base. - - HEADER - end - - private def rule_todo(rule, issues) - rule.excluded = - issues.map(&.location.try &.filename.try &.to_s) - .compact - .uniq! - - {rule.name => rule}.to_yaml - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr deleted file mode 100644 index 7219a67bbb9e..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/formatter/util.cr +++ /dev/null @@ -1,23 +0,0 @@ -module Ameba::Formatter - module Util - def affected_code(source, location, max_length = 100, placeholder = " ...", prompt = "> ") - line, column = location.line_number, location.column_number - affected_line = source.lines[line - 1]? - - return if affected_line.nil? || affected_line.strip.empty? - - if affected_line.size > max_length && column < max_length - affected_line = affected_line[0, max_length - placeholder.size - 1] + placeholder - end - - stripped = affected_line.lstrip - position = column - (affected_line.size - stripped.size) + prompt.size - - String.build do |str| - str << prompt << stripped << "\n" - str << " " * (position - 1) - str << "^".colorize(:yellow) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr deleted file mode 100644 index a714f8851c8a..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/glob_utils.cr +++ /dev/null @@ -1,37 +0,0 @@ -module Ameba - # Helper module that is utilizes helpers for working with globs. - module GlobUtils - # Returns all files that match specified globs. - # Globs can have wildcards or be rejected: - # - # ``` - # find_files_by_globs(["**/*.cr", "!lib"]) - # ``` - # - def find_files_by_globs(globs) - rejected = rejected_globs(globs) - selected = globs - rejected - - expand(selected) - expand(rejected.map! { |p| p[1..-1] }) - end - - # Expands globs. Globs can point to files or even directories. - # - # ``` - # expand(["spec/*.cr", "src"]) # => all files in src folder + first level specs - # ``` - # - def expand(globs) - globs.map do |glob| - glob += "/**/*.cr" if File.directory?(glob) - Dir[glob] - end.flatten.uniq! - end - - private def rejected_globs(globs) - globs.select do |glob| - glob.starts_with?('!') && !File.exists?(glob) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr deleted file mode 100644 index cece57fec648..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/inline_comments.cr +++ /dev/null @@ -1,104 +0,0 @@ -module Ameba - # A module that utilizes inline comments parsing and processing logic. - module InlineComments - COMMENT_DIRECTIVE_REGEX = /# ameba:(?\w+) (?\w+(?:\/\w+)?(?:,? \w+(?:\/\w+)?)*)/ - - # Available actions in the inline comments - enum Action - Disable - Enable - end - - # Returns true if current location is disabled for a particular rule, - # false otherwise. - # - # Location is disabled in two cases: - # 1. The line of the location ends with a comment directive. - # 2. The line above the location is a comment directive. - # - # For example, here are two examples of disabled location: - # - # ``` - # # ameba:disable Style/LargeNumbers - # Time.epoch(1483859302) - # - # Time.epoch(1483859302) # ameba:disable Style/LargeNumbers - # ``` - # - # But here are examples which are not considered as disabled location: - # - # ``` - # # ameba:disable Style/LargeNumbers - # # - # Time.epoch(1483859302) - # - # if use_epoch? # ameba:disable Style/LargeNumbers - # Time.epoch(1483859302) - # end - # ``` - # - def location_disabled?(location, rule) - return false if Rule::SPECIAL.includes?(rule.name) - return false unless line_number = location.try &.line_number.try &.- 1 - return false unless line = lines[line_number]? - - line_disabled?(line, rule) || - (line_number > 0 && - (prev_line = lines[line_number - 1]) && - comment?(prev_line) && - line_disabled?(prev_line, rule)) - end - - # Parses inline comment directive. Returns a tuple that consists of - # an action and parsed rules if directive found, nil otherwise. - # - # ``` - # line = "# ameba:disable Rule1, Rule2" - # directive = parse_inline_directive(line) - # directive[:action] # => "disable" - # directive[:rules] # => ["Rule1", "Rule2"] - # ``` - # - # It ignores the directive if it is commented out. - # - # ``` - # line = "# # ameba:disable Rule1, Rule2" - # parse_inline_directive(line) # => nil - # ``` - # - def parse_inline_directive(line) - if directive = COMMENT_DIRECTIVE_REGEX.match(line) - return if commented_out?(line.gsub(directive[0], "")) - { - action: directive["action"], - rules: directive["rules"].split(/[\s,]/, remove_empty: true), - } - end - end - - # Returns true if the line at the given `line_number` is a comment. - def comment?(line_number : Int32) - if line = lines[line_number]? - comment?(line) - end - end - - private def comment?(line : String) - line.lstrip.starts_with? '#' - end - - private def line_disabled?(line, rule) - return false unless directive = parse_inline_directive(line) - Action.parse?(directive[:action]).try(&.disable?) && - (directive[:rules].includes?(rule.name) || directive[:rules].includes?(rule.group)) - end - - private def commented_out?(line) - commented = false - - lexer = Crystal::Lexer.new(line).tap(&.comments_enabled = true) - Tokenizer.new(lexer).run { |t| commented = true if t.type == :COMMENT } - commented - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr deleted file mode 100644 index 03e95165bb91..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/issue.cr +++ /dev/null @@ -1,26 +0,0 @@ -module Ameba - # Represents an issue reported by Ameba. - record Issue, - # A rule that triggers this issue. - rule : Rule::Base, - - # Location of the issue. - location : Crystal::Location?, - - # End location of the issue. - end_location : Crystal::Location?, - - # Issue message. - message : String, - - # Issue status. - status : Symbol? do - def disabled? - status == :disabled - end - - def syntax? - rule.is_a?(Rule::Lint::Syntax) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr deleted file mode 100644 index 354539506d62..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/reportable.cr +++ /dev/null @@ -1,41 +0,0 @@ -module Ameba - # Represents a module used to report issues. - module Reportable - # List of reported issues. - getter issues = [] of Issue - - # Adds a new issue to the list of issues. - def add_issue(rule, location, end_location, message, status = nil) - status ||= :disabled if location_disabled?(location, rule) - issues << Issue.new rule, location, end_location, message, status - end - - # Adds a new issue for AST *node*. - def add_issue(rule, node : Crystal::ASTNode, message, **args) - add_issue rule, node.location, node.end_location, message, **args - end - - # Adds a new issue for Crystal *token*. - def add_issue(rule, token : Crystal::Token, message, **args) - add_issue rule, token.location, nil, message, **args - end - - # Adds a new issue for *location* defined by line and column numbers. - def add_issue(rule, location : Tuple(Int32, Int32), message, **args) - location = Crystal::Location.new path, *location - add_issue rule, location, nil, message, **args - end - - # Adds a new issue for *location* and *end_location* defined by line and column numbers. - def add_issue(rule, location : Tuple(Int32, Int32), end_location : Tuple(Int32, Int32), message, **args) - location = Crystal::Location.new path, *location - end_location = Crystal::Location.new path, *end_location - add_issue rule, location, end_location, message, **args - end - - # Returns true if the list of not disabled issues is empty, false otherwise. - def valid? - issues.reject(&.disabled?).empty? - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr deleted file mode 100644 index c895499a2ad5..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/base.cr +++ /dev/null @@ -1,197 +0,0 @@ -module Ameba::Rule - # List of names of the special rules, which - # behave differently than usual rules. - SPECIAL = [ - Lint::Syntax.rule_name, - Lint::UnneededDisableDirective.rule_name, - ] - - # Represents a base of all rules. In other words, all rules - # inherits from this struct: - # - # ``` - # struct MyRule < Ameba::Rule::Base - # def test(source) - # if invalid?(source) - # issue_for line, column, "Something wrong." - # end - # end - # - # private def invalid?(source) - # # ... - # end - # end - # ``` - # - # Enforces rules to implement an abstract `#test` method which - # is designed to test the source passed in. If source has issues - # that are tested by this rule, it should add an issue. - # - abstract struct Base - include Config::RuleConfig - - # This method is designed to test the source passed in. If source has issues - # that are tested by this rule, it should add an issue. - # - # Be default it uses a node visitor to traverse all the nodes in the source. - # Must be overriten for other type of rules. - def test(source : Source) - AST::NodeVisitor.new self, source - end - - def test(source : Source, node : Crystal::ASTNode, *opts) - # can't be abstract - end - - # A convenient addition to `#test` method that does the same - # but returns a passed in `source` as an addition. - # - # ``` - # source = MyRule.new.catch(source) - # source.valid? - # ``` - # - def catch(source : Source) - source.tap { |s| test s } - end - - # Returns a name of this rule, which is basically a class name. - # - # ``` - # struct MyRule < Ameba::Rule::Base - # def test(source) - # end - # end - # - # MyRule.new.name # => "MyRule" - # ``` - # - def name - {{@type}}.rule_name - end - - # Returns a group this rule belong to. - # - # ``` - # struct MyGroup::MyRule < Ameba::Rule::Base - # # ... - # end - # - # MyGroup::MyRule.new.group # => "MyGroup" - # ``` - # - def group - {{@type}}.group_name - end - - # Checks whether the source is excluded from this rule. - # It searches for a path in `excluded` property which matches - # the one of the given source. - # - # ``` - # my_rule.excluded?(source) # => true or false - # ``` - # - def excluded?(source) - excluded.try &.any? do |path| - source.matches_path?(path) || - Dir.glob(path).any? { |glob| source.matches_path? glob } - end - end - - # Returns true if this rule is special and behaves differently than - # usual rules. - # - # ``` - # my_rule.special? # => true or false - # ``` - # - def special? - SPECIAL.includes? name - end - - def ==(other) - name == other.try &.name - end - - def hash - name.hash - end - - macro issue_for(*args) - source.add_issue self, {{*args}} - end - - protected def self.rule_name - name.gsub("Ameba::Rule::", "").gsub("::", "/") - end - - protected def self.group_name - rule_name.split("/")[0...-1].join("/") - end - - protected def self.subclasses - {{ @type.subclasses }} - end - - macro inherited - protected def self.path_to_source_file - __FILE__ - end - end - - # Returns documentation for this rule if any. - # - # ``` - # module Ameba - # # This is a test rule. - # # Does nothing. - # struct MyRule < Ameba::Rule::Base - # def test(source) - # end - # end - # end - # - # MyRule.parsed_doc # => "This is a test rule.\nDoes nothing." - # ``` - def self.parsed_doc - source = File.read(path_to_source_file) - nodes = Crystal::Parser.new(source).tap(&.wants_doc = true).parse - type_name = rule_name.split("/").last? - DocFinder.new(nodes, type_name).doc - end - - # :nodoc: - private class DocFinder < Crystal::Visitor - getter doc : String? - getter type_name : String? - - def initialize(nodes, @type_name) - self.accept(nodes) - end - - def visit(node : Crystal::ASTNode) - return false if @doc - - if node.responds_to?(:name) && - (name = node.name) && - name.is_a?(Crystal::Path) && - name.names.last? == @type_name - @doc = node.doc - end - - true - end - end - end - - # Returns a list of all available rules. - # - # ``` - # Ameba::Rule.rules # => [Rule1, Rule2, ....] - # ``` - # - def self.rules - Base.subclasses - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr deleted file mode 100644 index a4c24d37dffd..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/line_length.cr +++ /dev/null @@ -1,29 +0,0 @@ -module Ameba::Rule::Layout - # A rule that disallows lines longer than `max_length` number of symbols. - # - # YAML configuration example: - # - # ``` - # Layout/LineLength: - # Enabled: true - # MaxLength: 100 - # ``` - # - struct LineLength < Base - properties do - enabled false - description "Disallows lines longer than `MaxLength` number of symbols" - max_length 140 - end - - MSG = "Line too long" - - def test(source) - source.lines.each_with_index do |line, index| - next unless line.size > max_length - - issue_for({index + 1, max_length + 1}, MSG) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr deleted file mode 100644 index 24499e9c0099..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_blank_lines.cr +++ /dev/null @@ -1,33 +0,0 @@ -module Ameba::Rule::Layout - # A rule that disallows trailing blank lines at the end of the source file. - # - # YAML configuration example: - # - # ``` - # Layout/TrailingBlankLines: - # Enabled: true - # ``` - # - struct TrailingBlankLines < Base - properties do - description "Disallows trailing blank lines" - end - - MSG = "Excessive trailing newline detected" - MSG_FINAL_NEWLINE = "Trailing newline missing" - - def test(source) - source_lines = source.lines - return if source_lines.empty? - - last_source_line = source_lines.last - source_lines_size = source_lines.size - return if source_lines_size == 1 && last_source_line.empty? - - last_line_not_empty = !last_source_line.empty? - if source_lines_size >= 1 && (source_lines.last(2).join.strip.empty? || last_line_not_empty) - issue_for({source_lines_size - 1, 1}, last_line_not_empty ? MSG_FINAL_NEWLINE : MSG) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr deleted file mode 100644 index a4df797ee138..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/layout/trailing_whitespace.cr +++ /dev/null @@ -1,25 +0,0 @@ -module Ameba::Rule::Layout - # A rule that disallows trailing whitespaces. - # - # YAML configuration example: - # - # ``` - # Layout/TrailingWhitespace: - # Enabled: true - # ``` - # - struct TrailingWhitespace < Base - properties do - description "Disallows trailing whitespaces" - end - - MSG = "Trailing whitespace detected" - - def test(source) - source.lines.each_with_index do |line, index| - next unless line =~ /\s$/ - issue_for({index + 1, line.size}, MSG) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr deleted file mode 100644 index 76b6d3589ed7..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/bad_directive.cr +++ /dev/null @@ -1,54 +0,0 @@ -module Ameba::Rule::Lint - # A rule that reports incorrect comment directives for Ameba. - # - # For example, the user can mistakenly add a directive - # to disable a rule that even doesn't exist: - # - # ``` - # # ameba:disable BadRuleName - # def foo - # :bar - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/BadDirective: - # Enabled: true - # ``` - # - struct BadDirective < Base - properties do - description "Reports bad comment directives" - end - - AVAILABLE_ACTIONS = InlineComments::Action.names.map(&.downcase) - ALL_RULE_NAMES = Rule.rules.map(&.rule_name) - ALL_GROUP_NAMES = Rule.rules.map(&.group_name).uniq! - - def test(source) - Tokenizer.new(source).run do |token| - next unless token.type == :COMMENT - next unless directive = source.parse_inline_directive(token.value.to_s) - - check_action source, token, directive[:action] - check_rules source, token, directive[:rules] - end - end - - private def check_action(source, token, action) - return if InlineComments::Action.parse?(action) - - issue_for token, - "Bad action in comment directive: '%s'. Possible values: %s" % { - action, AVAILABLE_ACTIONS.join(", "), - } - end - - private def check_rules(source, token, rules) - bad_names = rules - ALL_RULE_NAMES - ALL_GROUP_NAMES - issue_for token, "Such rules do not exist: %s" % bad_names.join(", ") unless bad_names.empty? - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr deleted file mode 100644 index 313db5a22dd4..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/comparison_to_boolean.cr +++ /dev/null @@ -1,41 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows comparison to booleans. - # - # For example, these are considered invalid: - # - # ``` - # foo == true - # bar != false - # false === baz - # ``` - # - # This is because these expressions evaluate to `true` or `false`, so you - # could get the same result by using either the variable directly, - # or negating the variable. - # - # YAML configuration example: - # - # ``` - # Lint/ComparisonToBoolean: - # Enabled: true - # ``` - # - struct ComparisonToBoolean < Base - properties do - enabled false - description "Disallows comparison to booleans" - end - - MSG = "Comparison to a boolean is pointless" - - def test(source, node : Crystal::Call) - comparison = %w(== != ===).includes?(node.name) - to_boolean = node.args.first?.try &.is_a?(Crystal::BoolLiteral) || - node.obj.is_a?(Crystal::BoolLiteral) - - return unless comparison && to_boolean - - issue_for node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr deleted file mode 100644 index 976744901fa9..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/debugger_statement.cr +++ /dev/null @@ -1,29 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows calls to debugger. - # - # This is because we don't want debugger breakpoints accidentally being - # committed into our codebase. - # - # YAML configuration example: - # - # ``` - # Lint/DebuggerStatement: - # Enabled: true - # ``` - # - struct DebuggerStatement < Base - properties do - description "Disallows calls to debugger" - end - - MSG = "Possible forgotten debugger statement detected" - - def test(source, node : Crystal::Call) - return unless node.name == "debugger" && - node.args.empty? && - node.obj.nil? - - issue_for node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr deleted file mode 100644 index f8fdeb48e033..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_ensure.cr +++ /dev/null @@ -1,56 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows empty ensure statement. - # - # For example, this is considered invalid: - # - # ``` - # def some_method - # do_some_stuff - # ensure - # end - # - # begin - # do_some_stuff - # ensure - # end - # ``` - # - # And it should be written as this: - # - # - # ``` - # def some_method - # do_some_stuff - # ensure - # do_something_else - # end - # - # begin - # do_some_stuff - # ensure - # do_something_else - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/EmptyEnsure - # Enabled: true - # ``` - # - struct EmptyEnsure < Base - properties do - description "Disallows empty ensure statement" - end - - MSG = "Empty `ensure` block detected" - - def test(source, node : Crystal::ExceptionHandler) - node_ensure = node.ensure - return if node_ensure.nil? || !node_ensure.nop? - - issue_for node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr deleted file mode 100644 index 682ed8a359ba..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_expression.cr +++ /dev/null @@ -1,56 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows empty expressions. - # - # This is considered invalid: - # - # ``` - # foo = () - # - # if () - # bar - # end - # ``` - # - # And this is valid: - # - # ``` - # foo = (some_expression) - # - # if (some_expression) - # bar - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/EmptyExpression: - # Enabled: true - # ``` - # - struct EmptyExpression < Base - include AST::Util - - properties do - description "Disallows empty expressions" - enabled false - end - - MSG = "Avoid empty expression %s" - MSG_EXRS = "Avoid empty expressions" - - def test(source, node : Crystal::NilLiteral) - exp = node_source(node, source.lines).try &.join - - return if exp.nil? || exp == "nil" - - issue_for node, MSG % exp - end - - def test(source, node : Crystal::Expressions) - if node.expressions.size == 1 && node.expressions.first.nop? - issue_for node, MSG_EXRS - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr deleted file mode 100644 index 19eafd2feebc..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/empty_loop.cr +++ /dev/null @@ -1,68 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows empty loops. - # - # This is considered invalid: - # - # ``` - # while false - # end - # - # until 10 - # end - # - # loop do - # # nothing here - # end - # ``` - # - # And this is valid: - # - # ``` - # a = 1 - # while a < 10 - # a += 1 - # end - # - # until socket_opened? - # end - # - # loop do - # do_something_here - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/EmptyLoop: - # Enabled: true - # ``` - struct EmptyLoop < Base - include AST::Util - - properties do - description "Disallows empty loops" - end - - MSG = "Empty loop detected" - - def test(source, node : Crystal::Call) - return unless loop?(node) - - check_node(source, node, node.block) - end - - def test(source, node : Crystal::While) - check_node(source, node, node.body) if literal?(node.cond) - end - - def test(source, node : Crystal::Until) - check_node(source, node, node.body) if literal?(node.cond) - end - - private def check_node(source, node, loop_body) - body = loop_body.is_a?(Crystal::Block) ? loop_body.body : loop_body - issue_for node, MSG if body.nil? || body.nop? - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr deleted file mode 100644 index c9de6a788a45..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/hash_duplicated_key.cr +++ /dev/null @@ -1,43 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows duplicated keys in hash literals. - # - # This is considered invalid: - # - # ``` - # h = {"foo" => 1, "bar" => 2, "foo" => 3} - # ``` - # - # And it has to written as this instead: - # - # ``` - # h = {"foo" => 1, "bar" => 2} - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/HashDuplicatedKey: - # Enabled: true - # ``` - # - struct HashDuplicatedKey < Base - properties do - description "Disallows duplicated keys in hash literals" - end - - MSG = "Duplicated keys in hash literal: %s" - - def test(source, node : Crystal::HashLiteral) - return unless (keys = duplicated_keys(node.entries)).any? - - issue_for node, MSG % keys.join(", ") - end - - private def duplicated_keys(entries) - entries.map(&.key) - .group_by(&.itself) - .select { |_, v| v.size > 1 } - .map { |k, _| k } - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr deleted file mode 100644 index 2dc2e6004361..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_condition.cr +++ /dev/null @@ -1,50 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows useless conditional statements that contain a literal - # in place of a variable or predicate function. - # - # This is because a conditional construct with a literal predicate will - # always result in the same behaviour at run time, meaning it can be - # replaced with either the body of the construct, or deleted entirely. - # - # This is considered invalid: - # ``` - # if "something" - # :ok - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/LiteralInCondition: - # Enabled: true - # ``` - # - struct LiteralInCondition < Base - include AST::Util - - properties do - description "Disallows useless conditional statements that contain \ - a literal in place of a variable or predicate function" - end - - MSG = "Literal value found in conditional" - - def check_node(source, node) - return unless literal?(node.cond) - issue_for node, MSG - end - - def test(source, node : Crystal::If) - check_node source, node - end - - def test(source, node : Crystal::Unless) - check_node source, node - end - - def test(source, node : Crystal::Case) - check_node source, node - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr deleted file mode 100644 index df674f74f997..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/literal_in_interpolation.cr +++ /dev/null @@ -1,34 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows useless string interpolations - # that contain a literal value instead of a variable or function. - # - # For example: - # - # ``` - # "Hello, #{:Ary}" - # "There are #{4} cats" - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/LiteralInInterpolation - # Enabled: true - # ``` - # - struct LiteralInInterpolation < Base - include AST::Util - - properties do - description "Disallows useless string interpolations" - end - - MSG = "Literal value found in interpolation" - - def test(source, node : Crystal::StringInterpolation) - node.expressions - .select { |e| !e.is_a?(Crystal::StringLiteral) && literal?(e) } - .each { |n| issue_for n, MSG } - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr deleted file mode 100644 index 0f1d3de82b48..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/percent_array.cr +++ /dev/null @@ -1,74 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows some unwanted symbols in percent array literals. - # - # For example, this is usually written by mistake: - # - # ``` - # %i(:one, :two) - # %w("one", "two") - # ``` - # - # And the expected example is: - # - # ``` - # %i(one two) - # %w(one two) - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/PercentArrays: - # Enabled: true - # StringArrayUnwantedSymbols: ',"' - # SymbolArrayUnwantedSymbols: ',:' - # ``` - # - struct PercentArrays < Base - properties do - description "Disallows some unwanted symbols in percent array literals" - string_array_unwanted_symbols ",\"" - symbol_array_unwanted_symbols ",:" - end - - MSG = "Symbols `%s` may be unwanted in %s array literals" - - def test(source) - issue = start_token = nil - - Tokenizer.new(source).run do |token| - case token.type - when :STRING_ARRAY_START, :SYMBOL_ARRAY_START - start_token = token.dup - when :STRING - if start_token && issue.nil? - issue = array_entry_invalid?(token.value, start_token.not_nil!.raw) - end - when :STRING_ARRAY_END, :SYMBOL_ARRAY_END - if issue - issue_for start_token.not_nil!, issue.not_nil! - end - issue = start_token = nil - else - # nop - end - end - end - - private def array_entry_invalid?(entry, array_type) - case array_type - when .starts_with? "%w" - check_array_entry entry, string_array_unwanted_symbols, "%w" - when .starts_with? "%i" - check_array_entry entry, symbol_array_unwanted_symbols, "%i" - else - # nop - end - end - - private def check_array_entry(entry, symbols, literal) - return unless entry =~ /[#{symbols}]/ - MSG % {symbols, literal} - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr deleted file mode 100644 index 6c2b70f286d1..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/rand_zero.cr +++ /dev/null @@ -1,44 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows `rand(0)` and `rand(1)` calls. - # Such calls always return `0`. - # - # For example: - # - # ``` - # rand(1) - # ``` - # - # Should be written as: - # - # ``` - # rand - # # or - # rand(2) - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/RandZero: - # Enabled: true - # ``` - # - struct RandZero < Base - properties do - description "Disallows rand zero calls" - end - - MSG = "%s always returns 0" - - def test(source, node : Crystal::Call) - return unless node.name == "rand" && - node.args.size == 1 && - (arg = node.args.first) && - (arg.is_a? Crystal::NumberLiteral) && - (value = arg.value) && - (value == "0" || value == "1") - - issue_for node, MSG % node - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr deleted file mode 100644 index f21484a22146..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_string_coercion.cr +++ /dev/null @@ -1,47 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows string conversion in string interpolation, - # which is redundant. - # - # For example, this is considered invalid: - # - # ``` - # "Hello, #{name.to_s}" - # ``` - # - # And this is valid: - # - # ``` - # "Hello, #{name}" - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/RedundantStringCoersion - # Enabled: true - # ``` - # - struct RedundantStringCoercion < Base - include AST::Util - - properties do - description "Disallows redundant string conversions in interpolation" - end - - MSG = "Redundant use of `Object#to_s` in interpolation" - - def test(source, node : Crystal::StringInterpolation) - string_coercion_nodes(node).each { |n| issue_for n.name_location, n.end_location, MSG } - end - - private def string_coercion_nodes(node) - node.expressions.select do |e| - e.is_a?(Crystal::Call) && - e.name == "to_s" && - e.args.size.zero? && - e.named_args.nil? && - e.obj - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr deleted file mode 100644 index f67766d6d81b..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_index.cr +++ /dev/null @@ -1,58 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows redundant `with_index` calls. - # - # For example, this is considered invalid: - # ``` - # collection.each.with_index do |e| - # # ... - # end - # - # collection.each_with_index do |e, _| - # # ... - # end - # ``` - # - # and it should be written as follows: - # - # ``` - # collection.each do |e| - # # ... - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/RedundantWithIndex: - # Enabled: true - # ``` - # - struct RedundantWithIndex < Base - properties do - description "Disallows redundant `with_index` calls" - end - - def test(source, node : Crystal::Call) - args, block = node.args, node.block - - return if args.size > 1 || block.nil? || with_index_arg?(block.not_nil!) - - case node.name - when "with_index" - report source, node, "Remove redundant with_index" - when "each_with_index" - report source, node, "Use each instead of each_with_index" - else - # nop - end - end - - private def with_index_arg?(block : Crystal::Block) - block.args.size >= 2 && block.args.last.name != "_" - end - - private def report(source, node, msg) - issue_for node.name_location, node.name_end_location, msg - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr deleted file mode 100644 index 357e45d2467d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/redundant_with_object.cr +++ /dev/null @@ -1,51 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows redundant `each_with_object` calls. - # - # For example, this is considered invalid: - # - # ``` - # collection.each_with_object(0) do |e| - # # ... - # end - # - # collection.each_with_object(0) do |e, _| - # # ... - # end - # ``` - # - # and it should be written as follows: - # - # ``` - # collection.each do |e| - # # ... - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/RedundantWithObject: - # Enabled: true - # ``` - # - struct RedundantWithObject < Base - properties do - description "Disallows redundant `with_object` calls" - end - - def test(source, node : Crystal::Call) - return if node.name != "each_with_object" || - node.args.size != 1 || - node.block.nil? || - with_index_arg?(node.block.not_nil!) - - issue_for node.name_location, - node.name_end_location, - "Use each instead of each_with_object" - end - - private def with_index_arg?(block : Crystal::Block) - block.args.size >= 2 && block.args.last.name != "_" - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr deleted file mode 100644 index 0c27c25df87f..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_argument.cr +++ /dev/null @@ -1,58 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows shadowed arguments. - # - # For example, this is considered invalid: - # - # ``` - # do_something do |foo| - # foo = 1 # shadows block argument - # foo - # end - # - # def do_something(foo) - # foo = 1 # shadows method argument - # foo - # end - # ``` - # - # and it should be written as follows: - # - # ``` - # do_something do |foo| - # foo = foo + 42 - # foo - # end - # - # def do_something(foo) - # foo = foo + 42 - # foo - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/ShadowedArgument: - # Enabled: true - # ``` - # - struct ShadowedArgument < Base - properties do - description "Disallows shadowed arguments" - end - - MSG = "Argument `%s` is assigned before it is used" - - def test(source) - AST::ScopeVisitor.new self, source - end - - def test(source, node, scope : AST::Scope) - scope.arguments.each do |arg| - next unless assign = arg.variable.assign_before_reference - - issue_for assign, MSG % arg.name - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr deleted file mode 100644 index 5357034070ea..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowed_exception.cr +++ /dev/null @@ -1,77 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows a rescued exception that get shadowed by a - # less specific exception being rescued before a more specific - # exception is rescued. - # - # For example, this is invalid: - # - # ``` - # begin - # do_something - # rescue Exception - # handle_exception - # rescue ArgumentError - # handle_argument_error_exception - # end - # ``` - # - # And it has to be written as follows: - # - # ``` - # begin - # do_something - # rescue ArgumentError - # handle_argument_error_exception - # rescue Exception - # handle_exception - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/ShadowedException: - # Enabled: true - # ``` - # - struct ShadowedException < Base - properties do - description "Disallows rescued exception that get shadowed" - end - - MSG = "Exception handler has shadowed exceptions: %s" - - def test(source, node : Crystal::ExceptionHandler) - return unless excs = node.rescues - - if (excs = shadowed excs.map(&.types)).any? - issue_for node, MSG % excs.join(", ") - end - end - - private def shadowed(exceptions, exception_found = false) - previous_exceptions = [] of String - - exceptions.reduce([] of String) do |shadowed, excs| - excs = excs ? excs.map(&.to_s) : ["Exception"] - - if exception_found - shadowed.concat excs - previous_exceptions.concat excs - else - exception_found ||= excs.any? &.== "Exception" - excs.each do |exc| - if exception_found && exc != "Exception" - shadowed << exc - else - shadowed << exc if previous_exceptions.any? &.== exc - end - previous_exceptions << exc - end - end - - shadowed - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr deleted file mode 100644 index 846cc4840666..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shadowing_local_outer_var.cr +++ /dev/null @@ -1,69 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows the usage of the same name as outer local variables - # for block or proc arguments. - # - # For example, this is considered incorrect: - # - # ``` - # def some_method - # foo = 1 - # - # 3.times do |foo| # shadowing outer `foo` - # end - # end - # ``` - # - # and should be written as: - # - # ``` - # def some_method - # foo = 1 - # - # 3.times do |bar| - # end - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/ShadowingOuterLocalVar: - # Enabled: true - # ``` - # - struct ShadowingOuterLocalVar < Base - properties do - description "Disallows the usage of the same name as outer local variables" \ - " for block or proc arguments." - end - - MSG = "Shadowing outer local variable `%s`" - - def test(source) - AST::ScopeVisitor.new self, source - end - - def test(source, node : Crystal::ProcLiteral, scope : AST::Scope) - find_shadowing source, scope - end - - def test(source, node : Crystal::Block, scope : AST::Scope) - find_shadowing source, scope - end - - private def find_shadowing(source, scope) - outer_scope = scope.outer_scope - - return if outer_scope.nil? || outer_scope.in_macro? - - scope.arguments.reject(&.ignored?).each do |arg| - variable = outer_scope.find_variable(arg.name) - - next if variable.nil? || !variable.declared_before?(arg) - next if outer_scope.assigns_ivar?(arg.name) - - issue_for arg.node, MSG % arg.name - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr deleted file mode 100644 index 8011fa036446..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/shared_var_in_fiber.cr +++ /dev/null @@ -1,86 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows using shared variables in fibers, - # which are mutated during iterations. - # - # In most cases it leads to unexpected behaviour and is undesired. - # - # For example, having this example: - # - # ``` - # n = 0 - # channel = Channel(Int32).new - # - # while n < 3 - # n = n + 1 - # spawn { channel.send n } - # end - # - # 3.times { puts channel.receive } # => # 3, 3, 3 - # ``` - # - # The problem is there is only one shared between fibers variable `n` - # and when `channel.receive` is executed its value is `3`. - # - # To solve this, the code above needs to be rewritten to the following: - # - # ``` - # n = 0 - # channel = Channel(Int32).new - # - # while n < 3 - # n = n + 1 - # m = n - # spawn do { channel.send m } - # end - # - # 3.times { puts channel.receive } # => # 1, 2, 3 - # ``` - # - # This rule is able to find the shared variables between fibers, which are mutated - # during iterations. So it reports the issue on the first sample and passes on - # the second one. - # - # There are also other technics to solve the problem above which are - # [officially documented](https://crystal-lang.org/reference/guides/concurrency.html) - # - # YAML configuration example: - # - # ``` - # Lint/SharedVarInFiber: - # Enabled: true - # ``` - # - struct SharedVarInFiber < Base - properties do - description "Disallows shared variables in fibers." - end - - MSG = "Shared variable `%s` is used in fiber" - - def test(source) - AST::ScopeVisitor.new self, source - end - - def test(source, node, scope : AST::Scope) - return unless scope.spawn_block? - - scope.references.each do |ref| - next if (variable = scope.find_variable(ref.name)).nil? - next if variable.scope == scope || !mutated_in_loop?(variable) - - issue_for ref.node, MSG % variable.name - end - end - - # Variable is mutated in loop if it was declared above the loop and assigned inside. - private def mutated_in_loop?(variable) - declared_in = variable.assignments.first?.try &.branch - - variable.assignments - .reject { |assign| assign.scope.spawn_block? } - .any? do |assign| - assign.branch.try(&.in_loop?) && assign.branch != declared_in - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr deleted file mode 100644 index 8d135401ed76..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/syntax.cr +++ /dev/null @@ -1,34 +0,0 @@ -module Ameba::Rule::Lint - # A rule that reports invalid Crystal syntax. - # - # For example, this syntax is invalid: - # - # ``` - # def hello - # do_something - # rescue Exception => e - # end - # ``` - # - # And should be properly written: - # - # ``` - # def hello - # do_something - # rescue e : Exception - # end - # ``` - # - struct Syntax < Base - properties do - description "Reports invalid Crystal syntax" - severity Ameba::Severity::Error - end - - def test(source) - source.ast - rescue e : Crystal::SyntaxException - issue_for({e.line_number, e.column_number}, e.message.to_s) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr deleted file mode 100644 index 6128fbeba8f4..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unneeded_disable_directive.cr +++ /dev/null @@ -1,67 +0,0 @@ -module Ameba::Rule::Lint - # A rule that reports unneeded disable directives. - # For example, this is considered invalid: - # - # ``` - # # ameba:disable Style/PredicateName - # def comment? - # do_something - # end - # ``` - # - # as the predicate name is correct and the comment directive does not - # have any effect, the snippet should be written as the following: - # - # ``` - # def comment? - # do_something - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/UnneededDisableDirective - # Enabled: true - # ``` - # - struct UnneededDisableDirective < Base - properties do - description "Reports unneeded disable directives in comments" - end - - MSG = "Unnecessary disabling of %s" - - def test(source) - Tokenizer.new(source).run do |token| - next unless token.type == :COMMENT - next unless directive = source.parse_inline_directive(token.value.to_s) - next unless names = unneeded_disables(source, directive, token.location) - next unless names.any? - - issue_for token, MSG % names.join(", ") - end - end - - private def unneeded_disables(source, directive, location) - return unless directive[:action] == "disable" - - directive[:rules].reject do |rule_name| - source.issues.any? do |issue| - issue.rule.name == rule_name && - issue.disabled? && - issue_at_location?(source, issue, location) - end && rule_name != self.name - end - end - - private def issue_at_location?(source, issue, location) - return false unless issue_line_number = issue.location.try(&.line_number) - - issue_line_number == location.line_number || - ((prev_line_number = issue_line_number - 1) && - prev_line_number == location.line_number && - source.comment?(prev_line_number - 1)) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr deleted file mode 100644 index ded09bf57ba4..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unreachable_code.cr +++ /dev/null @@ -1,64 +0,0 @@ -module Ameba::Rule::Lint - # A rule that reports unreachable code. - # - # For example, this is considered invalid: - # - # ``` - # def method(a) - # return 42 - # a + 1 - # end - # ``` - # - # ``` - # a = 1 - # loop do - # break - # a += 1 - # end - # ``` - # - # And has to be written as the following: - # - # ``` - # def method(a) - # return 42 if a == 0 - # a + 1 - # end - # ``` - # - # ``` - # a = 1 - # loop do - # break a > 3 - # a += 1 - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/UnreachableCode: - # Enabled: true - # ``` - # - struct UnreachableCode < Base - include AST::Util - - properties do - description "Reports unreachable code" - end - - MSG = "Unreachable code detected" - - def test(source) - AST::FlowExpressionVisitor.new self, source - end - - def test(source, node, flow_expression : AST::FlowExpression) - if unreachable_node = flow_expression.unreachable_nodes.first? - issue_for unreachable_node, MSG - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr deleted file mode 100644 index 6abadca80b86..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/unused_argument.cr +++ /dev/null @@ -1,65 +0,0 @@ -module Ameba::Rule::Lint - # A rule that reports unused arguments. - # For example, this is considered invalid: - # - # ``` - # def method(a, b, c) - # a + b - # end - # ``` - # and should be written as: - # - # ``` - # def method(a, b) - # a + b - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/UnusedArgument: - # Enabled: true - # IgnoreDefs: true - # IgnoreBlocks: false - # IgnoreProcs: false - # ``` - # - struct UnusedArgument < Base - properties do - description "Disallows unused arguments" - - ignore_defs true - ignore_blocks false - ignore_procs false - end - - MSG = "Unused argument `%s`. If it's necessary, use `%s` " \ - "as an argument name to indicate that it won't be used." - - def test(source) - AST::ScopeVisitor.new self, source - end - - def test(source, node : Crystal::ProcLiteral, scope : AST::Scope) - ignore_procs || find_unused_arguments source, scope - end - - def test(source, node : Crystal::Block, scope : AST::Scope) - ignore_blocks || find_unused_arguments source, scope - end - - def test(source, node : Crystal::Def, scope : AST::Scope) - ignore_defs || find_unused_arguments source, scope - end - - private def find_unused_arguments(source, scope) - scope.arguments.each do |argument| - next if argument.ignored? || scope.references?(argument.variable) - - name_suggestion = scope.node.is_a?(Crystal::Block) ? '_' : "_#{argument.name}" - issue_for argument.node, MSG % {argument.name, name_suggestion} - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr deleted file mode 100644 index bff181889f3c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_assign.cr +++ /dev/null @@ -1,51 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows useless assignments. - # - # For example, this is considered invalid: - # - # ``` - # def method - # var = 1 - # do_something - # end - # ``` - # - # And has to be written as the following: - # - # ``` - # def method - # var = 1 - # do_something(var) - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/UselessAssign: - # Enabled: true - # ``` - # - struct UselessAssign < Base - properties do - description "Disallows useless variable assignments" - end - - MSG = "Useless assignment to variable `%s`" - - def test(source) - AST::ScopeVisitor.new self, source - end - - def test(source, node, scope : AST::Scope) - scope.variables.each do |var| - next if var.captured_by_block? || var.used_in_macro? || var.ignored? - - var.assignments.each do |assign| - next if assign.referenced? || assign.transformed? - issue_for assign.target_node, MSG % var.name - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr deleted file mode 100644 index 4b2fb36cff72..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/lint/useless_condition_in_when.cr +++ /dev/null @@ -1,83 +0,0 @@ -module Ameba::Rule::Lint - # A rule that disallows useless conditions in when clause - # where it is guaranteed to always return the same result. - # - # For example, this is considered invalid: - # - # ``` - # case - # when utc? - # io << " UTC" - # when local? - # Format.new(" %:z").format(self, io) if local? - # end - # ``` - # - # And has to be written as the following: - # - # ``` - # case - # when utc? - # io << " UTC" - # when local? - # Format.new(" %:z").format(self, io) - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Lint/UselessConditionInWhen: - # Enabled: true - # ``` - # - struct UselessConditionInWhen < Base - properties do - description "Disallows useless conditions in when" - end - - MSG = "Useless condition in when detected" - - # TODO: condition.cond may be a complex ASTNode with - # useless inner conditions. We might need to improve this - # simple implementation in future. - protected def check_node(source, when_node, cond) - cond_s = cond.to_s - return if when_node - .conds - .map(&.to_s) - .none? { |c| c == cond_s } - - issue_for cond, MSG - end - - def test(source, node : Crystal::When) - ConditionInWhenVisitor.new self, source, node - end - - # :nodoc: - private class ConditionInWhenVisitor < Crystal::Visitor - @source : Source - @rule : UselessConditionInWhen - @parent : Crystal::When - - def initialize(@rule, @source, @parent) - @parent.accept self - end - - def visit(node : Crystal::If) - @rule.check_node(@source, @parent, node.cond) - true - end - - def visit(node : Crystal::Unless) - @rule.check_node(@source, @parent, node.cond) - true - end - - def visit(node : Crystal::ASTNode) - true - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr deleted file mode 100644 index fb2e2fe22594..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/metrics/cyclomatic_complexity.cr +++ /dev/null @@ -1,38 +0,0 @@ -module Ameba::Rule::Metrics - # A rule that disallows methods with a cyclomatic complexity higher than `MaxComplexity` - # - # YAML configuration example: - # - # ``` - # Metrics/CyclomaticComplexity: - # Enabled: true - # MaxComplexity: 10 - # ``` - # - struct CyclomaticComplexity < Base - properties do - description "Disallows methods with a cyclomatic complexity higher than `MaxComplexity`" - max_complexity 10 - end - - MSG = "Cyclomatic complexity too high [%d/%d]" - - def test(source, node : Crystal::Def) - complexity = AST::CountingVisitor.new(node).count - - if complexity > max_complexity && (location = node.name_location) - issue_for( - location, - def_name_end_location(node), - MSG % {complexity, max_complexity} - ) - end - end - - private def def_name_end_location(node) - return unless location = node.name_location - line_number, column_number = location.line_number, location.column_number - Crystal::Location.new(location.filename, line_number, column_number + node.name.size) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr deleted file mode 100644 index 5e4ae0772f44..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/any_after_filter.cr +++ /dev/null @@ -1,46 +0,0 @@ -module Ameba::Rule::Performance - # This rule is used to identify usage of `any?` calls that follow filters. - # - # For example, this is considered invalid: - # - # ``` - # [1, 2, 3].select { |e| e > 2 }.any? - # [1, 2, 3].reject { |e| e >= 2 }.any? - # ``` - # - # And it should be written as this: - # - # ``` - # [1, 2, 3].any? { |e| e > 2 } - # [1, 2, 3].any? { |e| e < 2 } - # ``` - # - # YAML configuration example: - # - # ``` - # Performance/AnyAfterFilter: - # Enabled: true - # FilterNames: - # - select - # - reject - # ``` - # - struct AnyAfterFilter < Base - ANY_NAME = "any?" - MSG = "Use `#{ANY_NAME} {...}` instead of `%s {...}.#{ANY_NAME}`" - - properties do - filter_names : Array(String) = %w(select reject) - description "Identifies usage of `any?` calls that follow filters." - end - - def test(source, node : Crystal::Call) - return unless node.name == ANY_NAME && (obj = node.obj) - - if node.block.nil? && obj.is_a?(Crystal::Call) && - filter_names.includes?(obj.name) && !obj.block.nil? - issue_for obj.name_location, node.name_end_location, MSG % obj.name - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr deleted file mode 100644 index a359942302ad..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/first_last_after_filter.cr +++ /dev/null @@ -1,57 +0,0 @@ -module Ameba::Rule::Performance - # This rule is used to identify usage of `first/last/first?/last?` calls that follow filters. - # - # For example, this is considered inefficient: - # - # ``` - # [-1, 0, 1, 2].select { |e| e > 0 }.first? - # [-1, 0, 1, 2].select { |e| e > 0 }.last? - # ``` - # - # And can be written as this: - # - # ``` - # [-1, 0, 1, 2].find { |e| e > 0 } - # [-1, 0, 1, 2].reverse_each.find { |e| e > 0 } - # ``` - # - # YAML configuration example: - # - # ``` - # Performance/FirstLastAfterFilter - # Enabled: true - # FilterNames: - # - select - # ``` - # - struct FirstLastAfterFilter < Base - CALL_NAMES = %w(first last first? last?) - MSG = "Use `find {...}` instead of `%s {...}.%s`" - MSG_REVERSE = "Use `reverse_each.find {...}` instead of `%s {...}.%s`" - - properties do - filter_names : Array(String) = %w(select) - description "Identifies usage of `first/last/first?/last?` calls that follow filters." - end - - def test(source) - AST::NodeVisitor.new self, source, skip: [ - Crystal::Macro, - Crystal::MacroExpression, - Crystal::MacroIf, - Crystal::MacroFor, - ] - end - - def test(source, node : Crystal::Call) - return unless CALL_NAMES.includes?(node.name) && (obj = node.obj) - return if node.args.any? - - if node.block.nil? && obj.is_a?(Crystal::Call) && - filter_names.includes?(obj.name) && !obj.block.nil? - message = node.name.includes?(CALL_NAMES.first) ? MSG : MSG_REVERSE - issue_for obj.name_location, node.name_end_location, message % {obj.name, node.name} - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr deleted file mode 100644 index 1fe26359b8b2..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/performance/size_after_filter.cr +++ /dev/null @@ -1,61 +0,0 @@ -module Ameba::Rule::Performance - # This rule is used to identify usage of `size` calls that follow filter. - # - # For example, this is considered invalid: - # - # ``` - # [1, 2, 3].select { |e| e > 2 }.size - # [1, 2, 3].reject { |e| e < 2 }.size - # [1, 2, 3].select(&.< 2).size - # [0, 1, 2].select(&.zero?).size - # [0, 1, 2].reject(&.zero?).size - # ``` - # - # And it should be written as this: - # - # ``` - # [1, 2, 3].count { |e| e > 2 } - # [1, 2, 3].count { |e| e >= 2 } - # [1, 2, 3].count(&.< 2) - # [0, 1, 2].count(&.zero?) - # [0, 1, 2].count(&.!= 0) - # ``` - # - # YAML configuration example: - # - # ``` - # Performance/SizeAfterFilter: - # Enabled: true - # FilterNames: - # - select - # - reject - # ``` - # - struct SizeAfterFilter < Base - SIZE_NAME = "size" - MSG = "Use `count {...}` instead of `%s {...}.#{SIZE_NAME}`." - - properties do - filter_names : Array(String) = %w(select reject) - description "Identifies usage of `size` calls that follow filter" - end - - def test(source) - AST::NodeVisitor.new self, source, skip: [ - Crystal::Macro, - Crystal::MacroExpression, - Crystal::MacroIf, - Crystal::MacroFor, - ] - end - - def test(source, node : Crystal::Call) - return unless node.name == SIZE_NAME && (obj = node.obj) - - if obj.is_a?(Crystal::Call) && - filter_names.includes?(obj.name) && !obj.block.nil? - issue_for obj.name_location, node.name_end_location, MSG % obj.name - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr deleted file mode 100644 index 3628006e7c5f..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/constant_names.cr +++ /dev/null @@ -1,43 +0,0 @@ -module Ameba::Rule::Style - # A rule that enforces constant names to be in screaming case. - # - # For example, these constant names are considered valid: - # - # ``` - # LUCKY_NUMBERS = [3, 7, 11] - # DOCUMENTATION_URL = "http://crystal-lang.org/docs" - # ``` - # - # And these are invalid names: - # - # ``` - # myBadConstant = 1 - # Wrong_NAME = 2 - # ``` - # - # YAML configuration example: - # - # ``` - # Style/ConstantNames: - # Enabled: true - # ``` - # - struct ConstantNames < Base - properties do - description "Enforces constant names to be in screaming case" - end - - MSG = "Constant name should be screaming-cased: %s, not %s" - - def test(source, node : Crystal::Assign) - if (target = node.target).is_a? Crystal::Path - name = target.names.first - expected = name.upcase - - return if expected == name || name.camelcase == name - - issue_for target, MSG % {expected, name} - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr deleted file mode 100644 index 18653bbc22be..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/is_a_nil.cr +++ /dev/null @@ -1,38 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows calls to `is_a?(Nil)` in favor of `nil?`. - # - # This is considered bad: - # - # ``` - # var.is_a? Nil - # ``` - # - # And needs to be written as: - # - # ``` - # var.nil? - # ``` - # - # YAML configuration example: - # - # ``` - # Style/IsANil: - # Enabled: true - # ``` - # - struct IsANil < Base - properties do - description "Disallows calls to `is_a?(Nil)` in favor of `nil?`" - end - - MSG = "Use `nil?` instead of `is_a?(Nil)`" - PATH_NIL_NAMES = %w(Nil) - - def test(source, node : Crystal::IsA) - return if node.nil_check? - - const = node.const - issue_for const, MSG if const.is_a?(Crystal::Path) && const.names == PATH_NIL_NAMES - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr deleted file mode 100644 index a9a2b33dbc25..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/large_numbers.cr +++ /dev/null @@ -1,109 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows usage of large numbers without underscore. - # These do not affect the value of the number, but can help read - # large numbers more easily. - # - # For example, these are considered invalid: - # - # ``` - # 10000 - # 141592654 - # 5.12345 - # ``` - # - # And has to be rewritten as the following: - # - # ``` - # 10_000 - # 141_592_654 - # 5.123_45 - # ``` - # - # YAML configuration example: - # - # ``` - # Style/LargeNumbers: - # Enabled: true - # IntMinDigits: 5 # i.e. integers higher than 9999 - # ``` - # - struct LargeNumbers < Base - properties do - description "Disallows usage of large numbers without underscore" - int_min_digits 5 - enabled false - end - - MSG = "Large numbers should be written with underscores: %s" - - def test(source) - Tokenizer.new(source).run do |token| - next unless token.type == :NUMBER && decimal?(token.raw) - - parsed = parse_number token.raw - - if allowed?(*parsed) && (expected = underscored *parsed) != token.raw - issue_for token, MSG % expected - end - end - end - - private def decimal?(value) - value !~ /^0(x|b|o)/ - end - - private def allowed?(_sign, value, fraction, _suffix) - return true if !fraction.nil? && fraction.size > 3 - - digits = value.chars.select &.to_s.=~ /[0-9]/ - digits.size >= int_min_digits - end - - private def underscored(sign, value, fraction, suffix) - value = slice_digits(value.reverse) { |slice| slice }.reverse - fraction = "." + slice_digits(fraction) { |slice| slice } if fraction - - "#{sign}#{value}#{fraction}#{suffix}" - end - - private def slice_digits(value, by = 3) - ([] of String).tap do |slices| - value.chars.reject(&.== '_').each_slice(by) do |slice| - slices << (yield slice).join - end - end.join("_") - end - - private def parse_number(value) - value, sign = parse_sign(value) - value, suffix = parse_suffix(value) - value, fraction = parse_fraction(value) - - {sign, value, fraction, suffix} - end - - private def parse_sign(value) - if "+-".includes?(value[0]) - sign = value[0] - value = value[1..-1] - end - {value, sign} - end - - private def parse_suffix(value) - if pos = (value =~ /e/ || value =~ /_?(i|u|f)/) - suffix = value[pos..-1] - value = value[0..pos - 1] - end - {value, suffix} - end - - private def parse_fraction(value) - if comma = value.index('.') - fraction = value[comma + 1..-1] - value = value[0..comma - 1] - end - {value, fraction} - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr deleted file mode 100644 index 905de3735d92..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/method_names.cr +++ /dev/null @@ -1,63 +0,0 @@ -module Ameba::Rule::Style - # A rule that enforces method names to be in underscored case. - # - # For example, these are considered valid: - # - # ``` - # class Person - # def first_name - # end - # - # def date_of_birth - # end - # - # def homepage_url - # end - # end - # ``` - # - # And these are invalid method names: - # - # ``` - # class Person - # def firstName - # end - # - # def date_of_Birth - # end - # - # def homepageURL - # end - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Style/MethodNames: - # Enabled: true - # ``` - # - struct MethodNames < Base - properties do - description "Enforces method names to be in underscored case" - end - - MSG = "Method name should be underscore-cased: %s, not %s" - - def test(source, node : Crystal::Def) - return if (expected = node.name.underscore) == node.name - - line_number = node.location.try &.line_number - column_number = node.name_location.try &.column_number - - return if line_number.nil? || column_number.nil? - - issue_for( - {line_number, column_number}, - {line_number, column_number + node.name.size - 1}, - MSG % {expected, node.name} - ) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr deleted file mode 100644 index 8781b36a9771..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/negated_conditions_in_unless.cr +++ /dev/null @@ -1,55 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows negated conditions in unless. - # - # For example, this is considered invalid: - # - # ``` - # unless !s.empty? - # :ok - # end - # ``` - # - # And should be rewritten to the following: - # - # ``` - # if s.emtpy? - # :ok - # end - # ``` - # - # It is pretty difficult to wrap your head around a block of code - # that is executed if a negated condition is NOT met. - # - # YAML configuration example: - # - # ``` - # Style/NegatedConditionsInUnless: - # Enabled: true - # ``` - # - struct NegatedConditionsInUnless < Base - properties do - description "Disallows negated conditions in unless" - end - - MSG = "Avoid negated conditions in unless blocks" - - def test(source, node : Crystal::Unless) - return unless negated_condition? node.cond - issue_for node, MSG - end - - private def negated_condition?(node) - case node - when Crystal::BinaryOp - negated_condition?(node.left) || negated_condition?(node.right) - when Crystal::Expressions - node.expressions.any? { |e| negated_condition? e } - when Crystal::Not - true - else - false - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr deleted file mode 100644 index b23d938c38e1..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/predicate_name.cr +++ /dev/null @@ -1,49 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows tautological predicate names, meaning those that - # start with the prefix `has_` or the prefix `is_`. Ignores if the alternative isn't valid Crystal code (e.g. `is_404?`). - # - # Favour these: - # - # ``` - # def valid?(x) - # end - # - # def picture?(x) - # end - # ``` - # - # Over these: - # - # ``` - # def is_valid?(x) - # end - # - # def has_picture?(x) - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Style/PredicateName: - # Enabled: true - # ``` - # - struct PredicateName < Base - properties do - description "Disallows tautological predicate names" - enabled false - end - - MSG = "Favour method name '%s?' over '%s'" - - def test(source, node : Crystal::Def) - if node.name =~ /^(is|has)_(\w+)\?/ - alternative = $2 - return unless alternative =~ /^[a-z][a-zA-Z_0-9]*$/ - - issue_for node, MSG % {alternative, node.name} - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr deleted file mode 100644 index bfb028a6354d..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_begin.cr +++ /dev/null @@ -1,131 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows redundant begin blocks. - # - # Currently it is able to detect: - # - # 1. Exception handler block that can be used as a part of the method. - # - # For example, this: - # - # ``` - # def method - # begin - # read_content - # rescue - # close_file - # end - # end - # ``` - # - # should be rewritten as: - # - # ``` - # def method - # read_content - # rescue - # close_file - # end - # ``` - # - # 2. begin..end block as a top level block in a method. - # - # For example this is considered invalid: - # - # ``` - # def method - # begin - # a = 1 - # b = 2 - # end - # end - # ``` - # - # and has to be written as the following: - # - # ``` - # def method - # a = 1 - # b = 2 - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Style/RedundantBegin: - # Enabled: true - # ``` - # - struct RedundantBegin < Base - include AST::Util - properties do - description "Disallows redundant begin blocks" - end - - MSG = "Redundant `begin` block detected" - - def test(source, node : Crystal::Def) - return unless redundant_begin?(source, node) - - issue_for node, MSG - end - - private def redundant_begin?(source, node) - case body = node.body - when Crystal::ExceptionHandler - redundant_begin_in_handler?(source, body, node) - when Crystal::Expressions - redundant_begin_in_expressions?(body) - else - # nop - end - end - - private def redundant_begin_in_expressions?(node) - node.keyword == :begin - end - - private def redundant_begin_in_handler?(source, handler, node) - return false if begin_exprs_in_handler?(handler) || inner_handler?(handler) - - code = node_source(node, source.lines).try &.join("\n") - def_redundant_begin? code if code - rescue - false - end - - private def inner_handler?(handler) - handler.body.is_a?(Crystal::ExceptionHandler) - end - - private def begin_exprs_in_handler?(handler) - if (body = handler.body).is_a?(Crystal::Expressions) - body.expressions.first.is_a?(Crystal::ExceptionHandler) - end - end - - private def def_redundant_begin?(code) - lexer = Crystal::Lexer.new code - in_body = in_argument_list = false - loop do - token = lexer.next_token - - case token.type - when :EOF, :"->" - break - when :IDENT - return token.value == :begin if in_body - when :"(" - in_argument_list = true - when :")" - in_argument_list = false - when :NEWLINE - in_body = true unless in_argument_list - when :SPACE - else - return false if in_body - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr deleted file mode 100644 index fdf62d435242..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_next.cr +++ /dev/null @@ -1,119 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows redundant next expressions. A `next` keyword allows - # a block to skip to the next iteration early, however, it is considered - # redundant in cases where it is the last expression in a block or combines - # into the node which is the last in a block. - # - # For example, this is considered invalid: - # - # ``` - # block do |v| - # next v + 1 - # end - # ``` - # - # ``` - # block do |v| - # case v - # when .nil? - # next "nil" - # when .blank? - # next "blank" - # else - # next "empty" - # end - # end - # ``` - # - # And has to be written as the following: - # - # ``` - # block do |v| - # v + 1 - # end - # ``` - # - # ``` - # block do |v| - # case arg - # when .nil? - # "nil" - # when .blank? - # "blank" - # else - # "empty" - # end - # end - # ``` - # - # ### Configuration params - # - # 1. *allow_multi_next*, default: true - # - # Allows end-user to configure whether to report or not the next statements - # which yield tuple literals i.e. - # - # ``` - # block do - # next a, b - # end - # ``` - # - # If this param equals to `false`, the block above will be forced to be written as: - # - # ``` - # block do - # {a, b} - # end - # ``` - # - # 2. *allow_empty_next*, default: true - # - # Allows end-user to configure whether to report or not the next statements - # without arguments. Sometimes such statements are used to yild the `nil` value explicitly. - # - # ``` - # block do - # @foo = :empty - # next - # end - # ``` - # - # If this param equals to `false`, the block above will be forced to be written as: - # - # ``` - # block do - # @foo = :empty - # nil - # end - # ``` - # - # ### YAML config example - # - # ``` - # Style/RedundantNext: - # Enabled: true - # AllowMultiNext: true - # AllowEmptyNext: true - # ``` - struct RedundantNext < Base - properties do - description "Reports redundant next expressions" - allow_multi_next true - allow_empty_next true - end - - MSG = "Redundant `next` detected" - - def test(source, node : Crystal::Block) - AST::RedundantControlExpressionVisitor.new(self, source, node.body) - end - - def test(source, node : Crystal::Next, visitor : AST::RedundantControlExpressionVisitor) - return if allow_multi_next && node.exp.is_a?(Crystal::TupleLiteral) - return if allow_empty_next && (node.exp.nil? || node.exp.not_nil!.nop?) - - source.try &.add_issue self, node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr deleted file mode 100644 index be226bbbd507..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/redundant_return.cr +++ /dev/null @@ -1,116 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows redundant return expressions. - # - # For example, this is considered invalid: - # - # ``` - # def foo - # return :bar - # end - # ``` - # - # ``` - # def bar(arg) - # case arg - # when .nil? - # return "nil" - # when .blank? - # return "blank" - # else - # return "empty" - # end - # end - # ``` - # - # And has to be written as the following: - # - # ``` - # def foo - # :bar - # end - # ``` - # - # ``` - # def bar(arg) - # case arg - # when .nil? - # "nil" - # when .blank? - # "blank" - # else - # "empty" - # end - # end - # ``` - # - # ### Configuration params - # - # 1. *allow_multi_return*, default: true - # - # Allows end-user to configure whether to report or not the return statements - # which return tuple literals i.e. - # - # ``` - # def method(a, b) - # return a, b - # end - # ``` - # - # If this param equals to `false`, the method above has to be written as: - # - # ``` - # def method(a, b) - # {a, b} - # end - # ``` - # - # 2. *allow_empty_return*, default: true - # - # Allows end-user to configure whether to report or not the return statements - # without arguments. Sometimes such returns are used to return the `nil` value explicitly. - # - # ``` - # def method - # @foo = :empty - # return - # end - # ``` - # - # If this param equals to `false`, the method above has to be written as: - # - # ``` - # def method - # @foo = :empty - # nil - # end - # ``` - # - # ### YAML config example - # - # ``` - # Style/RedundantReturn: - # Enabled: true - # AllowMutliReturn: true - # AllowEmptyReturn: true - # ``` - struct RedundantReturn < Base - properties do - description "Reports redundant return expressions" - allow_multi_return true - allow_empty_return true - end - - MSG = "Redundant `return` detected" - - def test(source, node : Crystal::Def) - AST::RedundantControlExpressionVisitor.new(self, source, node.body) - end - - def test(source, node : Crystal::Return, visitor : AST::RedundantControlExpressionVisitor) - return if allow_multi_return && node.exp.is_a?(Crystal::TupleLiteral) - return if allow_empty_return && (node.exp.nil? || node.exp.not_nil!.nop?) - - source.try &.add_issue self, node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr deleted file mode 100644 index 2daa28f84e6c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/type_names.cr +++ /dev/null @@ -1,90 +0,0 @@ -module Ameba::Rule::Style - # A rule that enforces type names in camelcase manner. - # - # For example, these are considered valid: - # - # ``` - # class ParseError < Exception - # end - # - # module HTTP - # class RequestHandler - # end - # end - # - # alias NumericValue = Float32 | Float64 | Int32 | Int64 - # - # lib LibYAML - # end - # - # struct TagDirective - # end - # - # enum Time::DayOfWeek - # end - # ``` - # - # And these are invalid type names - # - # ``` - # class My_class - # end - # - # module HTT_p - # end - # - # alias Numeric_value = Int32 - # - # lib Lib_YAML - # end - # - # struct Tag_directive - # end - # - # enum Time_enum::Day_of_week - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Style/TypeNames: - # Enabled: true - # ``` - # - struct TypeNames < Base - properties do - description "Enforces type names in camelcase manner" - end - - MSG = "Type name should be camelcased: %s, but it was %s" - - private def check_node(source, node) - name = node.name.to_s - expected = name.camelcase - return if expected == name - - issue_for node, MSG % {expected, name} - end - - def test(source, node : Crystal::ClassDef) - check_node(source, node) - end - - def test(source, node : Crystal::Alias) - check_node(source, node) - end - - def test(source, node : Crystal::LibDef) - check_node(source, node) - end - - def test(source, node : Crystal::EnumDef) - check_node(source, node) - end - - def test(source, node : Crystal::ModuleDef) - check_node(source, node) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr deleted file mode 100644 index 55909cdc747e..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/unless_else.cr +++ /dev/null @@ -1,58 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows the use of an `else` block with the `unless`. - # - # For example, the rule considers these valid: - # - # ``` - # unless something - # :ok - # end - # - # if something - # :one - # else - # :two - # end - # ``` - # - # But it considers this one invalid as it is an `unless` with an `else`: - # - # ``` - # unless something - # :one - # else - # :two - # end - # ``` - # - # The solution is to swap the order of the blocks, and change the `unless` to - # an `if`, so the previous invalid example would become this: - # - # ``` - # if something - # :two - # else - # :one - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Style/UnlessElse: - # Enabled: true - # ``` - # - struct UnlessElse < Base - properties do - description "Disallows the use of an `else` block with the `unless`" - end - - MSG = "Favour if over unless with else" - - def test(source, node : Crystal::Unless) - return if node.else.nop? - issue_for node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr deleted file mode 100644 index 108c54d0c71a..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/variable_names.cr +++ /dev/null @@ -1,51 +0,0 @@ -module Ameba::Rule::Style - # A rule that enforces variable names to be in underscored case. - # - # For example, these variable names are considered valid: - # - # ``` - # var_name = 1 - # name = 2 - # _another_good_name = 3 - # ``` - # - # And these are invalid variable names: - # - # ``` - # myBadNamedVar = 1 - # wrong_Name = 2 - # ``` - # - # YAML configuration example: - # - # ``` - # Style/VariableNames: - # Enabled: true - # ``` - # - struct VariableNames < Base - properties do - description "Enforces variable names to be in underscored case" - end - - MSG = "Var name should be underscore-cased: %s, not %s" - - private def check_node(source, node) - return if (expected = node.name.underscore) == node.name - - issue_for node, MSG % {expected, node.name} - end - - def test(source, node : Crystal::Var) - check_node source, node - end - - def test(source, node : Crystal::InstanceVar) - check_node source, node - end - - def test(source, node : Crystal::ClassVar) - check_node source, node - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr deleted file mode 100644 index f57289e0ad6c..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/rule/style/while_true.cr +++ /dev/null @@ -1,41 +0,0 @@ -module Ameba::Rule::Style - # A rule that disallows the use of `while true` instead of using the idiomatic `loop` - # - # For example, this is considered invalid: - # - # ``` - # while true - # do_something - # break if some_condition - # end - # ``` - # - # And should be replaced by the following: - # - # ``` - # loop do - # do_something - # break if some_condition - # end - # ``` - # - # YAML configuration example: - # - # ``` - # Style/WhileTrue: - # Enabled: true - # ``` - # - struct WhileTrue < Base - properties do - description "Disallows while statements with a true literal as condition" - end - - MSG = "While statement using true literal as condition" - - def test(source, node : Crystal::While) - return unless node.cond.true_literal? - issue_for node, MSG - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr deleted file mode 100644 index ece391877e18..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/runner.cr +++ /dev/null @@ -1,145 +0,0 @@ -module Ameba - # Represents a runner for inspecting sources files. - # Holds a list of rules to do inspection based on, - # list of sources to run inspection on and a formatter - # to prepare a report. - # - # ``` - # config = Ameba::Config.load - # runner = Ameba::Runner.new config - # runner.run.success? # => true or false - # ``` - # - class Runner - # A list of rules to do inspection based on. - @rules : Array(Rule::Base) - - # A list of sources to run inspection on. - getter sources : Array(Source) - - # A level of severity to be reported. - @severity : Severity - - # A formatter to prepare report. - @formatter : Formatter::BaseFormatter - - # A syntax rule which always inspects a source first - @syntax_rule = Rule::Lint::Syntax.new - - # Checks for unneeded disable directives. Always inspects a source last - @unneeded_disable_directive_rule : Rule::Base? - - # Instantiates a runner using a `config`. - # - # ``` - # config = Ameba::Config.load - # config.files = files - # config.formatter = formatter - # - # Ameba::Runner.new config - # ``` - # - def initialize(config : Config) - @sources = config.sources - @formatter = config.formatter - @severity = config.severity - @rules = config.rules.select(&.enabled).reject!(&.special?) - - @unneeded_disable_directive_rule = - config.rules - .find &.name.==(Rule::Lint::UnneededDisableDirective.rule_name) - end - - # :nodoc: - protected def initialize(@rules, @sources, @formatter, @severity) - end - - # Performs the inspection. Iterates through all sources and test it using - # list of rules. If a specific rule fails on a specific source, it adds - # an issue to that source. - # - # This action also notifies formatter when inspection is started/finished, - # and when a specific source started/finished to be inspected. - # - # ``` - # runner = Ameba::Runner.new config - # runner.run # => returns runner again - # ``` - # - def run - @formatter.started @sources - channels = @sources.map { Channel(Exception?).new } - @sources.each_with_index do |source, idx| - channel = channels[idx] - spawn do - run_source(source) - rescue e - channel.send(e) - else - channel.send(nil) - end - end - - channels.each do |c| - e = c.receive - raise e unless e.nil? - end - - self - ensure - @formatter.finished @sources - end - - private def run_source(source) - @formatter.source_started source - - if @syntax_rule.catch(source).valid? - @rules.each do |rule| - next if rule.excluded?(source) - rule.test(source) - end - check_unneeded_directives(source) - end - - @formatter.source_finished source - end - - # Explains an issue at a specified *location*. - # - # Runner should perform inspection before doing the explain. - # This is necessary to be able to find the issue at a specified location. - # - # ``` - # runner = Ameba::Runner.new config - # runner.run - # runner.explain({file: file, line: l, column: c}) - # ``` - # - def explain(location, output = STDOUT) - Formatter::ExplainFormatter.new(output, location).finished @sources - end - - # Indicates whether the last inspection successful or not. - # It returns true if no issues matching severity in sources found, false otherwise. - # - # ``` - # runner = Ameba::Runner.new config - # runner.run - # runner.success? # => true or false - # ``` - # - def success? - @sources.all? do |source| - source.issues - .reject(&.disabled?) - .none? { |issue| issue.rule.severity <= @severity } - end - end - - private def check_unneeded_directives(source) - if (rule = @unneeded_disable_directive_rule) && rule.enabled - rule.test(source) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr deleted file mode 100644 index 917cf4e98492..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/severity.cr +++ /dev/null @@ -1,49 +0,0 @@ -module Ameba - enum Severity - Error - Warning - Convention - - # Returns a symbol uniquely indicating severity. - # - # ``` - # Severity::Warning.symbol # => 'W' - # ``` - def symbol - to_s[0] - end - - # Creates Severity by the name. - # - # ``` - # Severity.parse("convention") # => Severity::Convention - # Severity.parse("foo-bar") # => Exception: Incorrect severity name - # ``` - # - def self.parse(name : String) - super name - rescue ArgumentError - raise "Incorrect severity name #{name}. Try one of #{values}" - end - end - - # Converter for `YAML.mapping` which converts severity enum to and from YAML. - class SeverityYamlConverter - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) - unless node.is_a?(YAML::Nodes::Scalar) - raise "Severity must be a scalar, not #{node.class}" - end - - case value = node.value - when String then Severity.parse(value) - when Nil then nil - else - raise "Incorrect severity: #{value}" - end - end - - def self.to_yaml(value : Severity, yaml : YAML::Nodes::Builder) - yaml.scalar value - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr deleted file mode 100644 index 3fd8d751d921..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/source.cr +++ /dev/null @@ -1,69 +0,0 @@ -module Ameba - # An entity that represents a Crystal source file. - # Has path, lines of code and issues reported by rules. - class Source - include InlineComments - include Reportable - - # Path to the source file. - getter path : String - - # Crystal code (content of a source file). - getter code : String - - @lines : Array(String)? - @ast : Crystal::ASTNode? - @fullpath : String? - - # Creates a new source by `code` and `path`. - # - # For example: - # - # ``` - # path = "./src/source.cr" - # Ameba::Source.new File.read(path), path - # ``` - # - def initialize(@code : String, @path = "") - end - - # Returns lines of code splitted by new line character. - # Since `code` is immutable and can't be changed, this - # method caches lines in an instance variable, so calling - # it second time will not perform a split, but will return - # lines instantly. - # - # ``` - # source = Ameba::Source.new "a = 1\nb = 2", path - # source.lines # => ["a = 1", "b = 2"] - # ``` - # - def lines - @lines ||= @code.split("\n") - end - - # Returns AST nodes constructed by `Crystal::Parser`. - # - # ``` - # source = Ameba::Source.new code, path - # source.ast - # ``` - # - def ast - @ast ||= - Crystal::Parser.new(code) - .tap { |parser| parser.wants_doc = true } - .tap { |parser| parser.filename = @path } - .parse - end - - def fullpath - @fullpath ||= File.expand_path @path - end - - # Returns true if *filepath* matches the source's path, false if it does not. - def matches_path?(filepath) - path == filepath || path == File.expand_path(filepath) - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr b/samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr deleted file mode 100644 index 070e5b552c90..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/ameba/tokenizer.cr +++ /dev/null @@ -1,111 +0,0 @@ -require "compiler/crystal/syntax/*" - -module Ameba - # Represents Crystal syntax tokenizer based on `Crystal::Lexer`. - # - # ``` - # source = Ameba::Source.new code, path - # tokenizer = Ameba::Tokenizer.new(source) - # tokenizer.run do |token| - # puts token - # end - # ``` - # - class Tokenizer - # Instantiates Tokenizer using a `source`. - # - # ``` - # source = Ameba::Source.new code, path - # Ameba::Tokenizer.new(source) - # ``` - # - def initialize(source) - @lexer = Crystal::Lexer.new source.code - @lexer.count_whitespace = true - @lexer.comments_enabled = true - @lexer.wants_raw = true - @lexer.filename = source.path - end - - # Instantiates Tokenizer using a `lexer`. - # - # ``` - # lexer = Crystal::Lexer.new(code) - # Ameba::Tokenizer.new(lexer) - # ``` - # - def initialize(@lexer : Crystal::Lexer) - end - - # Runs the tokenizer and yields each token as a block argument. - # - # ``` - # Ameba::Tokenizer.new(source).run do |token| - # puts token - # end - # ``` - # - def run(&block : Crystal::Token -> _) - run_normal_state @lexer, &block - true - rescue e : Crystal::SyntaxException - # puts e - false - end - - private def run_normal_state(lexer, break_on_rcurly = false, - &block : Crystal::Token -> _) - loop do - token = @lexer.next_token - block.call token - - case token.type - when :DELIMITER_START - run_delimiter_state lexer, token, &block - when :STRING_ARRAY_START, :SYMBOL_ARRAY_START - run_array_state lexer, token, &block - when :EOF - break - when :"}" - break if break_on_rcurly - else - # go on - end - end - end - - private def run_delimiter_state(lexer, token, &block : Crystal::Token -> _) - loop do - token = @lexer.next_string_token(token.delimiter_state) - block.call token - - case token.type - when :DELIMITER_END - break - when :INTERPOLATION_START - run_normal_state lexer, break_on_rcurly: true, &block - when :EOF - break - else - # go on - end - end - end - - private def run_array_state(lexer, token, &block : Crystal::Token -> _) - loop do - lexer.next_string_array_token - block.call token - - case token.type - when :STRING_ARRAY_END - break - when :EOF - break - else - # go on - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/ameba/src/cli.cr b/samples/client/petstore/crystal/lib/ameba/src/cli.cr deleted file mode 100644 index 4bb38cf9be98..000000000000 --- a/samples/client/petstore/crystal/lib/ameba/src/cli.cr +++ /dev/null @@ -1,3 +0,0 @@ -require "./ameba/cli/cmd" - -Ameba::Cli.run diff --git a/samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml b/samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml deleted file mode 100644 index 02d3e3da9875..000000000000 --- a/samples/client/petstore/crystal/lib/crest/.github/workflows/crystal.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Crystal CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - check_format: - runs-on: ubuntu-latest - container: - image: crystallang/crystal - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Check format - run: crystal tool format --check - check_ameba: - runs-on: ubuntu-latest - container: - image: crystallang/crystal - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Check ameba - run: ./bin/ameba - test_latest: - runs-on: ubuntu-latest - container: - image: crystallang/crystal - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Run tests - run: crystal spec - test_nightly: - runs-on: ubuntu-latest - container: - image: crystallang/crystal:nightly - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Run tests - run: crystal spec diff --git a/samples/client/petstore/crystal/lib/crest/.gitignore b/samples/client/petstore/crystal/lib/crest/.gitignore deleted file mode 100644 index fb1aac0ac59c..000000000000 --- a/samples/client/petstore/crystal/lib/crest/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -/doc/ -/lib/ -/bin/ -/.shards/ -/.idea/ -/tmp/ -/samples/ -/docs/ -/.vscode - -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock diff --git a/samples/client/petstore/crystal/lib/crest/.travis.yml b/samples/client/petstore/crystal/lib/crest/.travis.yml deleted file mode 100644 index 702cd6ea17ba..000000000000 --- a/samples/client/petstore/crystal/lib/crest/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -dist: xenial -language: crystal -crystal: - - latest - - nightly - -script: - - crystal spec - - crystal tool format --check - - bin/ameba - - crystal docs - -jobs: - allow_failures: - - crystal: nightly - -deploy: - provider: pages - skip_cleanup: true - github_token: $GITHUB_TOKEN - project_name: crest - local_dir: docs - on: - branch: master diff --git a/samples/client/petstore/crystal/lib/crest/CHANGELOG.md b/samples/client/petstore/crystal/lib/crest/CHANGELOG.md deleted file mode 100644 index bcf20d99a4fc..000000000000 --- a/samples/client/petstore/crystal/lib/crest/CHANGELOG.md +++ /dev/null @@ -1,237 +0,0 @@ -# Changelog - -## [...] - -## [0.26.1][] (2020-07-07) - -* Fixed compatibility with Crystal nightly - -## [0.26.0][] (2020-06-18) - -* Crystal 0.35.0 required -* Use [http_proxy](https://github.com/mamantoha/http_proxy) shard instead of build-in implementation - -## [0.25.1][] (2020-06-02) - -* Bug fixes and other improvements - -## [0.25.0][] (2020-04-07) - -* Crystal 0.34.0 required -* Rewrite `Crest::Logger` class -* Fix redirects when "Location" header is downcased - -## [0.24.1][] (2020-03-29) - -* Fix `handle_errors` is ignored for redirect errors ([#132](https://github.com/mamantoha/crest/issues/132)) - -## [0.24.0][] (2020-03-13) - -* Add `Crest#ParamsEncoder` module to encode/decode URI querystring -* Replace `Crest::Utils#encode_query_string` `with Crest::ParamsEncoder#encode` -* Allow `Boolean` in params - -## [0.23.2][] (2020-01-03) - -* Fix an issue with wrong "Content-Type" header - -## [0.23.1][] (2019-12-14) - -* Add a more descriptive crest user agent - -## [0.23.0][] (2019-12-12) - -* Add methods `to_s` and `inspect` to `Crest::Response` -* Support Crystal 0.32.0 - -## [0.22.0][] (2019-09-17) - -* Support Crystal 0.31.0 -* Digest access authentication support ([#127](https://github.com/mamantoha/crest/pull/127)) -* Add proxy to `to_curl` method - -## [0.21.1][] (2019-08-13) - -* **(breaking-change)** Require Crystal 0.30.1 - -## [0.21.0][] (2019-08-02) - -* **(breaking-change)** Require Crystal 0.30.0 -* **(breaking-change)** Rename `Crest::Response#successful?` to `Crest::Response#success?` -* Add method `Crest::Response#status` as `HTTP::Status` - -## [0.20.0][] (2019-06-14) - -* Tested with Crystal 0.29.0 -* Improve testing process ([#120](https://github.com/mamantoha/crest/pull/120)) - -## [0.19.1][] (2019-05-09) - -* Delegate method `to_curl` to `Crest::Response` instance -* Fix an issue in `Resource` when base url ends with `/` - -## [0.19.0][] (2019-04-18) - -* Add method `head` ([#116](https://github.com/mamantoha/crest/pull/116)) -* Tested with Crystal 0.28.0 - -## [0.18.3][] (2019-02-06) - -* Tested with Crystal 0.27.2 - -## [0.18.2][] (2019-02-03) - -* Tested with Crystal 0.27.1 - -## [0.18.1][] (2019-01-16) - -* Fix extracting filename from Content-Disposition header - -## [0.18.0][] (2019-01-06) - -* **(breaking-change)** Streaming support. `Crest`, `Crest::Request` and `Crest::Resource` verb methods(`get`, `post`, etc.) yields the `Crest::Response` as stream to the block ([#110](https://github.com/mamantoha/crest/pull/110)) -* **(breaking-change)** Needs to specify `form`, `headers` and `params` arguments for `Crest::Resource` methods ([#112](https://github.com/mamantoha/crest/pull/112)) -* Add `Crest::Response#filename` method ([#111](https://github.com/mamantoha/crest/pull/111)) -* Add response helper methods (`successful?`, `redirection?`, etc) ([#107](https://github.com/mamantoha/crest/pull/107)) -* Extract redirection logic into `Crest::Redirector` class ([#109](https://github.com/mamantoha/crest/pull/109)) - -## [0.17.0][] (2018-11-17) - -* **(breaking-change)** `Crest` and `Crest::Request` verb methods(`get`, `post`, etc.) yields the `Crest::Response` to the block -* Refactor proxy client - -## [0.16.1][] (2018-11-05) - -* Update to Kemal 0.25.1 - -## [0.16.0][] (2018-11-03) - -* Tested with Crystal 0.27.0 - -## [0.15.0][] (2018-10-12) - -* SSL/TLS support ([#100](https://github.com/mamantoha/crest/pull/100)) -* Tested with Crystal 0.26.1 - -## [0.14.0][] (2018-08-14) - -* Tested with Crystal 0.26.0 - -## [0.13.0][] (2018-08-13) - -* Add `Crest::Request#to_curl` to convert request to cURL command ([#95](https://github.com/mamantoha/crest/pull/95)) -* Bug fixes and other improvements - -## [0.12.0][] (2018-07-17) - -* **(breaking-change)** Rename `Request#payload` to `Request#form` -* Use `application/x-www-form-urlencoded` for forms by default. And `multipart/form-data` when a form includes any `` elements. -* Fix serialize query to string representation as http url-encoded - -## [0.11.0][] (2018-07-14) - -* Add `Logger#filter` method to filter sensitive information from logs with a regex matcher -* Allow to do request with `suburl` through `Request#http_verb(suburl)` method -* Bug fixes and other improvements - -## [0.10.2][] (2018-06-15) - -* Tested with Crystal 0.25.0 - -## [0.10.1][] (2018-05-14) - -* Fix `Crest::Utils.flatten_params` method ([#85](https://github.com/mamantoha/crest/pull/85)) -* Reduce the false positiveness in code as much as possible ([#83](https://github.com/mamantoha/crest/pull/83), thanks @veelenga) - -## [0.10.0][] (2018-04-24) - -* Add HTTP verb methods (`get`, `post`, etc) to `Crest::Request` -* `Crest` and `Crest::Request` verb methods(`get`, `post`, etc.) can yields the `Crest::Request` to the block -* `Crest::Request` and `Crest::Resource` initializer can accept block -* Access instance of `HTTP::Client` via `Crest::Request#http_client` -* Access instance of `HTTP::Client` via `Crest::Resource#http_client` -* `Crest::Request` and `Crest::Resource` initializer can accept `HTTP::Client` as `http_client` -* Add method `options` to `HTTP::Resource` - -## [0.9.10][] (2018-04-08) - -* Add option `:handle_errors` to don't raise exceptions but return the `Response` -* Add custom exceptions for each status code - -## [0.9.9][] (2018-04-03) - -* Add method `OPTIONS` -* Fix `Crest::Response#headers` method to return response headers - -## [0.9.8][] (2018-03-18) - -* Tested with Crystal 0.24.2 -* Fix Basic Authentication - -## [0.9.7][] (2018-03-05) - -* Allow `Crest::Resource` to accept default `params` and `headers` -* Allow `Crest::Resource` to accept more parameters(proxy authentication credentials, logging setup) -* Refactor exceptions class -* Setup GitHub Pages branch to host docs - -## [0.9.6][] (2018-01-05) - -* Proxy on redirects -* Logger in redirects - -## [0.9.5][] (2017-12-30) - -* Bug fixes and performance improvements - -## [0.9.4][] (2017-12-25) - -* Tested with Crystal 0.24.1 - -## [0.9.3][] (2017-12-19) - -* Add logging - -## 0.9.2 (2017-11-01) - -* First release :tada: - -[...]: https://github.com/mamantoha/crest/compare/v0.26.1...HEAD -[0.26.1]: https://github.com/mamantoha/crest/compare/v0.26.0...v0.26.1 -[0.26.0]: https://github.com/mamantoha/crest/compare/v0.25.1...v0.26.0 -[0.25.1]: https://github.com/mamantoha/crest/compare/v0.25.0...v0.25.1 -[0.25.0]: https://github.com/mamantoha/crest/compare/v0.24.1...v0.25.0 -[0.24.1]: https://github.com/mamantoha/crest/compare/v0.24.0...v0.24.1 -[0.24.0]: https://github.com/mamantoha/crest/compare/v0.23.2...v0.24.0 -[0.23.2]: https://github.com/mamantoha/crest/compare/v0.23.1...v0.23.2 -[0.23.1]: https://github.com/mamantoha/crest/compare/v0.23.0...v0.23.1 -[0.23.0]: https://github.com/mamantoha/crest/compare/v0.22.0...v0.23.0 -[0.22.0]: https://github.com/mamantoha/crest/compare/v0.21.1...v0.22.0 -[0.21.1]: https://github.com/mamantoha/crest/compare/v0.21.0...v0.21.1 -[0.21.0]: https://github.com/mamantoha/crest/compare/v0.20.0...v0.21.0 -[0.20.0]: https://github.com/mamantoha/crest/compare/v0.19.1...v0.20.0 -[0.19.1]: https://github.com/mamantoha/crest/compare/v0.19.0...v0.19.1 -[0.19.0]: https://github.com/mamantoha/crest/compare/v0.18.3...v0.19.0 -[0.18.3]: https://github.com/mamantoha/crest/compare/v0.18.2...v0.18.3 -[0.18.2]: https://github.com/mamantoha/crest/compare/v0.18.1...v0.18.2 -[0.18.1]: https://github.com/mamantoha/crest/compare/v0.18.0...v0.18.1 -[0.18.0]: https://github.com/mamantoha/crest/compare/v0.17.0...v0.18.0 -[0.17.0]: https://github.com/mamantoha/crest/compare/v0.16.1...v0.17.0 -[0.16.1]: https://github.com/mamantoha/crest/compare/v0.16.0...v0.16.1 -[0.16.0]: https://github.com/mamantoha/crest/compare/v0.15.0...v0.16.0 -[0.15.0]: https://github.com/mamantoha/crest/compare/v0.14.0...v0.15.0 -[0.14.0]: https://github.com/mamantoha/crest/compare/v0.13.0...v0.14.0 -[0.13.0]: https://github.com/mamantoha/crest/compare/v0.12.0...v0.13.0 -[0.12.0]: https://github.com/mamantoha/crest/compare/v0.11.0...v0.12.0 -[0.11.0]: https://github.com/mamantoha/crest/compare/v0.10.2...v0.11.0 -[0.10.2]: https://github.com/mamantoha/crest/compare/v0.10.1...v0.10.2 -[0.10.1]: https://github.com/mamantoha/crest/compare/v0.10.0...v0.10.1 -[0.10.0]: https://github.com/mamantoha/crest/compare/v0.9.10...v0.10.0 -[0.9.10]: https://github.com/mamantoha/crest/compare/v0.9.9...v0.9.10 -[0.9.9]: https://github.com/mamantoha/crest/compare/v0.9.8...v0.9.9 -[0.9.8]: https://github.com/mamantoha/crest/compare/v0.9.7...v0.9.8 -[0.9.7]: https://github.com/mamantoha/crest/compare/v0.9.6...v0.9.7 -[0.9.6]: https://github.com/mamantoha/crest/compare/v0.9.5...v0.9.6 -[0.9.5]: https://github.com/mamantoha/crest/compare/v0.9.4...v0.9.5 -[0.9.4]: https://github.com/mamantoha/crest/compare/v0.9.3...v0.9.4 -[0.9.3]: https://github.com/mamantoha/crest/compare/v0.9.2...v0.9.3 diff --git a/samples/client/petstore/crystal/lib/crest/LICENSE b/samples/client/petstore/crystal/lib/crest/LICENSE deleted file mode 100644 index e4c15b8ceaa7..000000000000 --- a/samples/client/petstore/crystal/lib/crest/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017-2020 Anton Maminov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/crest/README.md b/samples/client/petstore/crystal/lib/crest/README.md deleted file mode 100644 index 5319e8783019..000000000000 --- a/samples/client/petstore/crystal/lib/crest/README.md +++ /dev/null @@ -1,614 +0,0 @@ -# - -

    crest

    - -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/16e439ef2706472988306ef13da91a51)](https://app.codacy.com/app/mamantoha/crest?utm_source=github.com&utm_medium=referral&utm_content=mamantoha/crest&utm_campaign=Badge_Grade_Dashboard) -[![Build Status](https://travis-ci.org/mamantoha/crest.svg?branch=master)](https://travis-ci.org/mamantoha/crest) -[![GitHub release](https://img.shields.io/github/release/mamantoha/crest.svg)](https://github.com/mamantoha/crest/releases) -[![Commits Since Last Release](https://img.shields.io/github/commits-since/mamantoha/crest/latest.svg)](https://github.com/mamantoha/crest/pulse) -[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://mamantoha.github.io/crest/) -[![License](https://img.shields.io/github/license/mamantoha/crest.svg)](https://github.com/mamantoha/crest/blob/master/LICENSE) - -HTTP and REST client for Crystal, inspired by the Ruby's RestClient gem. - -Beloved features: - -* Redirects support. -* HTTP(S) proxy support. -* Elegant Key/Value headers, cookies, params, and payload. -* Multipart file uploads. -* Digest access authentication. -* Logging. - -Hopefully, someday I can remove this shard though. Ideally, Crystal's standard library would do all this already. - -## Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - crest: - github: mamantoha/crest -``` - -## Usage - -```crystal -require "crest" -``` - -Basic usage: - -```crystal -Crest.get( - "http://httpbin.org/get", - params: {:lang => "en"} -) -# curl -L "http://httpbin.org/get?lang=en" - -Crest.post( - "http://httpbin.org/post", - form: {:age => 27, :name => {:first => "Kurt", :last => "Cobain"}} -) -# curl -L --data "age=27&name[first]=Kurt&name[last]=Cobain" -X POST "http://httpbin.org/post" - -Crest.post( - "http://httpbin.org/post", - form: {"file" => File.open("avatar.png"), "name" => "John"} -) -# curl -X POST http://httpbin.org/post -F 'file=@avatar.png' -F 'name=John' -H 'Content-Type: multipart/form-data' -``` - -### Request - -`Crest::Request` accept next parameters: - -Mandatory parameters: - -* `:method` - HTTP method (`:get`. `:post`, `:put`, `:patch`, `:delete`, `:options`, `head`) -* `:url` - URL (e.g.: `http://httpbin.org/ip`) - -Optional parameters: - -* `:headers` - a hash containing the request headers -* `:cookies` - a hash containing the request cookies -* `:form` - a hash containing form params -* `:params` - a hash that represent query-string separated from the preceding part by a question mark (`?`) a sequence of attribute–value pairs separated by a delimiter (`&`) -* `auth` - access authentication method `basic` or `digest` (default to `basic`) -* `:user` and `:password` - for authentication -* `:tls` - client certificates, you can pass in a custom `OpenSSL::SSL::Context::Client` (default to `nil`) -* `:p_addr`, `:p_port`, `:p_user`, and `:p_pass` - specify a per-request proxy by passing these parameters -* `:max_redirects` - maximum number of redirections (default to 10) -* `:logging` - enable logging (default to `false`) -* `:logger` - set logger (default to `Crest::CommonLogger`) -* `:handle_errors` - error handling (default to `true`) -* `:http_client` - instance of `HTTP::Client` - -More detailed examples: - -```crystal -request = Crest::Request.new(:post, - "http://httpbin.org/post", - headers: {"Content-Type" => "application/json"}, - form: {:width => 640, "height" => "480"} -) -request.execute -# curl -L --data "width=640&height=480" --header "Content-Type: application/json" -X POST "http://httpbin.org/post" -``` - -```crystal -Crest::Request.execute(:get, - "http://httpbin.org/get", - params: {:width => 640, "height" => "480"}, - headers: {"Content-Type" => "application/json"}) -) -# curl -L --header "Content-Type: application/json" "http://httpbin.org/get?width=640&height=480" -``` - -```crystal -Crest::Request.get( - "http://httpbin.org/get", - p_addr: "127.0.0.1", - p_port: 3128, - p_user: "admin", - p_pass: "1234" -) -# curl -L --proxy http://127.0.0.1:3128 --proxy-user admin:1234 "http://httpbin.org/get" -``` - -A block can be passed to the `Crest::Request` initializer. - -This block will then be called with the `Crest::Request`. - -```crystal -request = Crest::Request.new(:get, "http://httpbin.org/headers") do |request| - request.headers.add("foo", "bar") -end - -request.execute -# curl -L --header "foo: bar" http://httpbin.org/headers -``` - -### Resource - -A `Crest::Resource` class can be instantiated for access to a RESTful resource, -including authentication, proxy and logging. - -Additionally, you can set default `params` and `headers` separately. -So can use `Crest::Resource` to share common `headers` and `params`. - -The final `headers` and `params` consist of: - -* default headers from initializer -* headers provided in call method (`get`, `post`, etc) - -This is especially useful if you wish to define your site in one place and -call it in multiple locations. - -```crystal -resource = Crest::Resource.new( - "http://httpbin.org", - params: {"key" => "value"}, - headers: {"Content-Type" => "application/json"} -) - -resource["/get"].get( - headers: {"Auth-Token" => "secret"} -) - -resource["/post"].post( - form: {:height => 100, "width" => "100"}, - params: {:secret => "secret"} -) -``` - -Use the `[]` syntax to allocate subresources: - -```crystal -site = Crest::Resource.new("http://httpbin.org") - -site["/post"].post(form: {:param1 => "value1", :param2 => "value2"}) -# curl -L --data "param1=value1¶m2=value2" -X POST http://httpbin.org/post -``` - -You can pass `suburl` through `Request#http_verb` methods: - -```crystal -site = Crest::Resource.new("http://httpbin.org") - -site.post("/post", form: {:param1 => "value1", :param2 => "value2"}) -# curl -L --data "param1=value1¶m2=value2" -X POST http://httpbin.org/post - -site.get("/get", params: {:status => "active"}) -# curl -L http://httpbin.org/get?status=active -``` - -A block can be passed to the `Crest::Resource` instance. - -This block will then be called with the `Crest::Resource`. - -```crystal -resource = Crest::Resource.new("http://httpbin.org") do |resource| - resource.headers.merge!({"foo" => "bar"}) -end - -resource["/headers"].get -``` - -With HTTP basic authentication: - -```crystal -resource = Crest::Resource.new( - "http://httpbin.org/basic-auth/user/passwd", - user: "user", - password: "passwd" -) -``` - -With Proxy authentication: - -```crystal -resource = Crest::Resource.new( - "http://httpbin.org/get", - p_host: "localhost", - p_port: 3128 -) -``` - -### Result handling - -The result of a `Crest::Request` and `Crest::Resource` is a `Crest::Response` object. - -Response objects have several useful methods: - -* `Response#body`: The response body as a `String` -* `Response#body_io`: The response body as a `IO` -* `Response#status`: The response status as a `HTTP::Status` -* `Response#status_code`: The HTTP response code -* `Response#headers`: A hash of HTTP response headers -* `Response#cookies`: A hash of HTTP cookies set by the server -* `Response#request`: The `Crest::Request` object used to make the request -* `Response#http_client_res`: The `HTTP::Client::Response` object -* `Response#history`: A list of each response received in a redirection chain - -### Exceptions - -* for result codes between `200` and `207`, a `Crest::Response` will be returned -* for result codes `301`, `302`, `303` or `307`, the redirection will be followed and the request transformed into a `GET` -* for other cases, a `Crest::RequestFailed` holding the Response will be raised -* call `.response` on the exception to get the server's response - -```crystal -Crest.get("http://httpbin.org/status/404") -# => HTTP status code 404: Not Found (Crest::NotFound) - -begin - Crest.get("http://httpbin.org/status/404") -rescue ex : Crest::NotFound - puts ex.response -end -``` - -To not raise exceptions but return the `Crest::Response` you can set `:handle_errors => false`. - -```crystal -response = Crest.get("http://httpbin.org/status/404", handle_errors: false) do |resp| - case resp - when .success? - puts resp.body_io.gets_to_end - when .client_error? - puts "Client error" - when .server_error? - puts "Server error" - end -end -# => Client error - -response.status_code # => 404 -``` - -But note that it may be more straightforward to use exceptions to handle different HTTP error response cases: - -```crystal -response = begin - Crest.get("http://httpbin.org/status/404") -rescue ex : Crest::NotFound - puts "Not found" - ex.response -rescue ex : Crest::InternalServerError - puts "Internal server error" - ex.response -end -# => Not found - -response.status_code # => 404 -``` - -### Streaming responses - -Normally, when you use `Crest`, `Crest::Request` or `Crest::Resource` methods to retrieve data, the entire response is buffered in memory and returned as the response to the call. - -However, if you are retrieving a large amount of data, for example an iso, or any other large file, you may want to stream the response directly to disk rather than loading it in memory. If you have a very large file, it may become impossible to load it into memory. - -If you want to stream the data from the response to a file as it comes, rather than entirely in memory, you can pass a block to which you pass a additional logic, which you can use to stream directly to a file as each chunk is received. - -With a block, an `Crest::Response` body is returned and the response's body is available as an `IO` by invoking `Crest::Response#body_io`. - -The following is an example: - -```crystal -Crest.get("https://github.com/crystal-lang/crystal/archive/0.27.0.zip") do |resp| - filename = resp.filename || "crystal.zip" - - File.open(filename, "w") do |file| - IO.copy(resp.body_io, file) - end -end -``` - -### Advanced Usage - -This section covers some of `crest` more advanced features. - -#### Parameters serializer - -Under the hood `crest` uses `Crest::ParamsEncoder` module to encode param. - -The encoder affect both how `crest` processes query strings and how it serializes POST bodies. - -`Crest::ParamsEncoder` provides 2 methods: - -* `#encode` - converts the given param into a URI querystring - - ```crystal - Crest::ParamsEncoder.encode({"a" => ["one", "two", "three"], "b" => true, "c" => "C", "d" => 1}) - # => 'a[]=one&a[]=two&a[]=three&b=true&c=C&d=1' - ``` - -* `#decode` - converts the given URI querystring into a hash - - ```crystal - Crest::ParamsEncoder.decode("a[]=one&a[]=two&a[]=three&b=true&c=C&d=1") - # => {"a" => ["one", "two", "three"], "b" => "true", "c" => "C", "d" => "1"} - ``` - -#### Multipart - -Yeah, that's right! This does multipart sends for you! - -```crystal -file = File.open("#{__DIR__}/example.png") -Crest.post("http://httpbin.org/post", form: {:image => file}) -``` - -```crystal -file = File.open("#{__DIR__}/example.png") -resource = Crest::Resource.new("https://httpbin.org") -response = resource["/post"].post(form: {:image => file}) -``` - -#### JSON payload - -`crest` does not speak JSON natively, so serialize your *form* to a string before passing it to `crest`. - -```crystal -Crest.post( - "http://httpbin.org/post", - headers: {"Content-Type" => "application/json"}, - form: {:foo => "bar"}.to_json -) -``` - -#### Headers - -Request headers can be set by passing a hash containing keys and values representing header names and values: - -```crystal -response = Crest.get( - "http://httpbin.org/bearer", - headers: {"Authorization" => "Bearer cT0febFoD5lxAlNAXHo6g"} -) -response.headers -# => {"Authorization" => ["Bearer cT0febFoD5lxAlNAXHo6g"]} -``` - -#### Cookies - -`Request` and `Response` objects know about HTTP cookies, and will automatically extract and set headers for them as needed: - -```crystal -response = Crest.get( - "http://httpbin.org/cookies/set", - params: {"k1" => "v1", "k2" => "v2"} -) -response.cookies -# => {"k1" => "v1", "k2" => "v2"} -``` - -```crystal -response = Crest.get( - "http://httpbin.org/cookies", - cookies: {"k1" => "v1"} -) -response.cookies -# => {"k1" => "v1"} -``` - -#### Basic access authentication - -For basic access authentication for an HTTP user agent you should to provide a `user` name and `password` when making a request. - -```crystal -Crest.get( - "http://httpbin.org/basic-auth/user/passwd", - user: "user", - password: "passwd" -) -# curl -L --user user:passwd http://httpbin.org/basic-auth/user/passwd -``` - -#### Digest access authentication - -For digest access authentication for an HTTP user agent you should to provide a `user` name and `password` when making a request. - -```crystal -Crest.get( - "https://httpbin.org/digest-auth/auth/user/passwd/MD5", - auth: "digest", - user: "user", - password: "passwd" -) -# curl -L --digest --user user:passwd https://httpbin.org/digest-auth/auth/user/passwd/MD5 -``` - -#### SSL/TLS support - -If `tls` is given it will be used: - -```crystal -Crest.get("https://expired.badssl.com", tls: OpenSSL::SSL::Context::Client.insecure) -``` - -#### Proxy - -If you need to use a proxy, you can configure individual requests with the proxy host and port arguments to any request method: - -```crystal -Crest.get( - "http://httpbin.org/ip", - p_addr: "localhost", - p_port: 3128 -) -``` - -To use authentication with your proxy, use next syntax: - -```crystal -Crest.get( - "http://httpbin.org/ip", - p_addr: "localhost", - p_port: 3128, - p_user: "user", - p_pass: "qwerty" -) -``` - -#### Logging - -> `Logger` class is completely taken from [halite](https://github.com/icyleaf/halite) shard. -> Thanks [icyleaf](https://github.com/icyleaf)! - -By default, the `Crest` does not enable logging. You can enable it per request by setting `logging: true`: - -```crystal -Crest.get("http://httpbin.org/get", logging: true) -``` - -##### Filter sensitive information from logs with a regex matcher - -```crystal -resource = Crest::Request.get("http://httpbin.org/get", params: {api_key => "secret"}, logging: true) do |request| - request.logger.filter(/(api_key=)(\w+)/, "\\1[REMOVED]") -end - -# => crest | 2018-07-04 14:49:49 | GET | http://httpbin.org/get?api_key=[REMOVED] -``` - -##### Customize logger - -You can create the custom logger by integration `Crest::Logger` abstract class. -Here has two methods must be implement: `Crest::Logger.request` and `Crest::Logger.response`. - -```crystal -class MyLogger < Crest::Logger - def request(request) - @logger.info { ">> | %s | %s" % [request.method, request.url] } - end - - def response(response) - @logger.info { "<< | %s | %s" % [response.status_code, response.url] } - end -end - -Crest.get("http://httpbin.org/get", logging: true, logger: MyLogger.new) -``` - -#### Redirection - -By default, `crest` will follow HTTP 30x redirection requests. - -To disable automatic redirection, set `:max_redirects => 0`. - -```crystal -Crest::Request.execute(method: :get, url: "http://httpbin.org/redirect/1", max_redirects: 0) -# => Crest::Found: 302 Found -``` - -#### Access HTTP::Client - -You can access `HTTP::Client` via the `http_client` instance method. - -This is usually used to set additional options (e.g. read timeout, authorization header etc.) - -```crystal -client = HTTP::Client.new("httpbin.org") -client.read_timeout = 1.second - -begin - Crest::Request.new(:get, - "http://httpbin.org/delay/10", - http_client: client - ) -rescue IO::TimeoutError - puts "Timeout!" -end -``` - -```crystal -client = HTTP::Client.new("httpbin.org") -client.read_timeout = 1.second - -begin - resource = Crest::Resource.new("http://httpbin.org", http_client: client) - resource.get("/delay/10") -rescue IO::TimeoutError - puts "Timeout!" -end -``` - -#### Convert Request object to cURL command - -Use `to_curl` method on instance of `Crest::Request` to convert request to cURL command. - -```crystal -request = Crest::Request.new( - :post, - "http://httpbin.org/post", - form: {"title" => "New Title", "author" => "admin"} -) -request.to_curl -# => curl -X POST http://httpbin.org/post -d 'title=New+Title&author=admin' -H 'Content-Type: application/x-www-form-urlencoded' -``` - -```crystal -request = Crest::Request.new( - :get, - "http://httpbin.org/basic-auth/user/passwd", - user: "user", - password: "passwd" -) -request.to_curl -# => curl -X GET http://httpbin.org/basic-auth/user/passwd --user user:passwd -``` - -Also you can directly use `Crest::Curlify` which accept instance of `Crest::Request` - -```crystal -request = Crest::Request.new(:get, "http://httpbin.org") -Crest::Curlify.new(request).to_curl -# => curl -X GET http://httpbin.org -``` - -## Development - -Install dependencies: - -```console -shards -``` - -To run test: - -```console -crystal spec -``` - -### Workbook - -```console -crystal play -open http://localhost:8080 -``` - -Then select the Workbook -> Requests from the menu. - -## Contributing - -1. Fork it () -2. Create your feature branch (git checkout -b my-new-feature) -3. Commit your changes (git commit -am 'Add some feature') -4. Push to the branch (git push origin my-new-feature) -5. Create a new Pull Request - -## Contributors - -* [mamantoha](https://github.com/mamantoha) Anton Maminov - creator, maintainer -* [icyleaf](https://github.com/icyleaf) Icyleaf Wang - logging support -* [psikoz](https://github.com/psikoz) Logo design - -## License - -Copyright: 2017-2020 Anton Maminov (anton.maminov@gmail.com) - -This library is distributed under the MIT license. Please see the LICENSE file. diff --git a/samples/client/petstore/crystal/lib/crest/lib b/samples/client/petstore/crystal/lib/crest/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/crest/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/crest/logo/icon.png b/samples/client/petstore/crystal/lib/crest/logo/icon.png deleted file mode 100644 index 0de8645e839eac562f19de1ac0b2fe3f49b1aa1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2985 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 Jte*?HvYr+5k diff --git a/samples/client/petstore/crystal/lib/crest/logo/icon.svg b/samples/client/petstore/crystal/lib/crest/logo/icon.svg deleted file mode 100644 index faf0bc7eed49..000000000000 --- a/samples/client/petstore/crystal/lib/crest/logo/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.png b/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.png deleted file mode 100644 index d16513385d23875cf65a9eba09a2bb2391cb7404..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7246 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 diff --git a/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg b/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg deleted file mode 100644 index 1227fb165643..000000000000 --- a/samples/client/petstore/crystal/lib/crest/logo/logotype_horizontal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/crest/playground/requests.md b/samples/client/petstore/crystal/lib/crest/playground/requests.md deleted file mode 100644 index fb4a2e4f81c0..000000000000 --- a/samples/client/petstore/crystal/lib/crest/playground/requests.md +++ /dev/null @@ -1,38 +0,0 @@ -# Making HTTP requests using Crest - -This workbook demonstrates how to make requests using `Crest` - -## Make request - -```playground -require "./src/crest" - -form = {:fizz => "buz"} - -Crest.post( - "http://httpbin.org/post", - headers: {"Access-Token" => ["secret1", "secret2"]}, - form: form, - logging: true, -) -``` - -## Set cookies - -```playground -require "./src/crest" - -url = "http://httpbin.org/cookies" -res = Crest.get(url, cookies: {"v1" => "k1"}, logging: true) - -params = {"k1" => "v1", "k2" => "v2"} -url = "http://httpbin.org/cookies/set" -res = Crest.get(url, params: params, logging: true) - -url = "http://httpbin.org/headers" -res = Crest.get( - url, - headers: {"Authorization" => "Bearer cT0febFoD5lxAlNAXHo6g"}, - logging: true -) -``` diff --git a/samples/client/petstore/crystal/lib/crest/shard.yml b/samples/client/petstore/crystal/lib/crest/shard.yml deleted file mode 100644 index cb607c172dc0..000000000000 --- a/samples/client/petstore/crystal/lib/crest/shard.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: crest -version: 0.26.1 - -authors: - - Anton Maminov - -description: | - HTTP and REST client for Crystal - -crystal: ">= 0.35.1" - -dependencies: - http-client-digest_auth: - github: mamantoha/http-client-digest_auth - version: ~> 0.4.0 - http_proxy: - github: mamantoha/http_proxy - version: ~> 0.7.1 - -development_dependencies: - kemal: - github: kemalcr/kemal - branch: master - ameba: - github: crystal-ameba/ameba - -license: MIT diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr deleted file mode 100644 index e04cd5bf6ac1..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/basic_auth_spec.cr +++ /dev/null @@ -1,75 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "Basic Auth" do - context Crest::Request do - it "should be unsuccessful without credentials" do - expect_raises Crest::RequestFailed, "HTTP status code 401" do - Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret", user: "root", password: "qwerty") - end - end - - it "should be successful with valid credentials" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret", user: "username", password: "password") - (response.status_code).should eq(200) - (response.body).should eq("Secret World!") - end - - it "should be successful in the initializer block" do - request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/secret") do |req| - req.user = "username" - req.password = "password" - end - - response = request.execute - - (response.status_code).should eq(200) - (response.body).should eq("Secret World!") - end - - it "should set basic auth in requets initiliazer block" do - request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/secret") do |req| - req.user = "username" - req.password = "password" - end - - response = request.execute - - (response.status_code).should eq(200) - (response.body).should eq("Secret World!") - end - - it "should be successful with valid credentials on redirect" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret_redirect", user: "username", password: "password") - (response.status_code).should eq(200) - (response.body).should eq("Secret World!") - (response.history.size).should eq(1) - (response.history.first.url).should eq("#{TEST_SERVER_URL}/secret_redirect") - (response.history.first.status_code).should eq(302) - end - - it "should be unsuccessful with invalid credentials" do - expect_raises Crest::RequestFailed, "HTTP status code 401" do - Crest::Request.execute(:get, "#{TEST_SERVER_URL}/secret", user: "root", password: "qwerty") - end - end - end - - context Crest do - it "should be successful with valid credentials" do - response = Crest.get("#{TEST_SERVER_URL}/secret", user: "username", password: "password") - (response.status_code).should eq(200) - (response.body).should eq("Secret World!") - end - end - - context Crest::Resource do - it "should be successful with valid credentials" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/secret", user: "username", password: "password") - response = resource.get - (response.status_code).should eq(200) - (response.body).should eq("Secret World!") - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr deleted file mode 100644 index e7a9310469b6..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/cookies_spec.cr +++ /dev/null @@ -1,41 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "Cookies" do - context Crest::Request do - it "should set cookies" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}", cookies: {"k1" => "v1", "k2" => "v2"}) - (response.status_code).should eq(200) - (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) - end - - it "should set cookies in the block" do - request = Crest::Request.new(:get, TEST_SERVER_URL, cookies: {"k1" => "v1"}) do |req| - req.cookies["k2"] = "v2" - end - - response = request.execute - - (response.status_code).should eq(200) - (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) - end - - it "should access cookies from the server" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/cookies/set", params: {"k1" => "v1", "k2" => "v2"}) - - (response.status_code).should eq(200) - (JSON.parse(response.body)).should eq({"cookies" => {"k1" => "v1", "k2" => "v2"}}) - (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) - end - - it "should set cookies from server with redirect" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/cookies/set_redirect", params: {"k1" => "v1", "k2" => "v2"}) - - (response.status_code).should eq(200) - (response.cookies).should eq({"k1" => "v1", "k2" => "v2"}) - (response.headers.[]("Cookie")).should eq("k1=v1; k2=v2") - (JSON.parse(response.body)).should eq({"cookies" => {"k1" => "v1", "k2" => "v2"}}) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr deleted file mode 100644 index 941e74adaea6..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/crest_spec.cr +++ /dev/null @@ -1,73 +0,0 @@ -require "../spec_helper" - -describe Crest do - it "do GET request" do - response = Crest.get("#{TEST_SERVER_URL}") - (response.body).should eq("Hello World!") - end - - it "do GET request with params" do - response = Crest.get("#{TEST_SERVER_URL}/resize", params: {:width => 100, :height => 100}) - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with different params" do - response = Crest.get("#{TEST_SERVER_URL}/resize", params: {"width" => 100, :height => "100"}) - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with params with nil" do - response = Crest.get("#{TEST_SERVER_URL}/add_key", params: {:json => nil, :key => 123}) - (response.body).should eq("JSON: key[123]") - end - - it "do POST request" do - response = Crest.post("#{TEST_SERVER_URL}/post/1/comments", form: {:title => "Title"}) - (response.body).should eq("Post with title `Title` created") - end - - it "upload file" do - file = File.open("#{__DIR__}/../support/fff.png") - response = Crest.post("#{TEST_SERVER_URL}/upload", form: {:file => file}) - (response.body).should match(/Upload OK/) - end - - it "do POST nested params" do - response = Crest.post("#{TEST_SERVER_URL}/post_nested", form: {:params1 => "one", :nested => {:params2 => "two"}}) - (response.body).should eq("params1=one&nested%5Bparams2%5D=two") - end - - it "do PUT request" do - response = Crest.put("#{TEST_SERVER_URL}/post/1/comments/1", form: {:title => "Put Update"}) - (response.body).should eq("Update Comment `1` for Post `1` with title `Put Update`") - end - - it "do PATCH request" do - response = Crest.patch("#{TEST_SERVER_URL}/post/1/comments/1", form: {:title => "Patch Update"}) - (response.body).should eq("Update Comment `1` for Post `1` with title `Patch Update`") - end - - it "do DELETE request" do - response = Crest.delete("#{TEST_SERVER_URL}/post/1/comments/1") - (response.body).should eq("Delete Comment `1` for Post `1`") - end - - it "do GET request with block without handle errors" do - body = "" - - Crest.get("#{TEST_SERVER_URL}/404", handle_errors: false) do |resp| - case resp - when .success? - body = resp.body_io.gets_to_end - when .client_error? - body = "Client error" - when .server_error? - body = "Server error" - else - raise "Unknown response with code #{resp.status_code}" - end - end - - body.should eq("Client error") - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr deleted file mode 100644 index f384e1256ac2..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/exception_spec.cr +++ /dev/null @@ -1,82 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "Raise exception" do - it "404" do - expect_raises Crest::RequestFailed, "HTTP status code 404: Not Found" do - Crest.get("#{TEST_SERVER_URL}/404") - end - end - - it "request with rescue" do - response = begin - Crest.get("#{TEST_SERVER_URL}/500") - rescue ex : Crest::NotFound - ex.response - rescue ex : Crest::InternalServerError - ex.response - end - - response.body.should eq("500 error") - end - - it "404 with Resource" do - expect_raises Crest::RequestFailed, "HTTP status code 404: Not Found" do - resource = Crest::Resource.new(TEST_SERVER_URL) - resource["/404"].get - end - end - - it "404 raises NotFound" do - expect_raises Crest::NotFound, "HTTP status code 404: Not Found" do - resource = Crest::Resource.new(TEST_SERVER_URL) - resource["/404"].get - end - end - - it "500" do - expect_raises Crest::RequestFailed, "HTTP status code 500" do - Crest.get("#{TEST_SERVER_URL}/500") - end - end - - it "500 raises InternalServerError" do - expect_raises Crest::InternalServerError, "HTTP status code 500" do - Crest.get("#{TEST_SERVER_URL}/500") - end - end - - it "call .response on the exception to get the server's response" do - response = - begin - Crest.get("#{TEST_SERVER_URL}/404") - rescue ex : Crest::RequestFailed - ex.response - end - - (response.status_code).should eq(404) - end - - context "with handle_errors: false" do - it "do not raise error for Crest" do - response = Crest.get("#{TEST_SERVER_URL}/404", handle_errors: false) - - (response.status_code).should eq(404) - end - - it "do not raise error for Request" do - request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/404", handle_errors: false) - response = request.execute - - (response.status_code).should eq(404) - end - - it "do not raise error for Resource" do - resource = Crest::Resource.new(TEST_SERVER_URL, handle_errors: false) - response = resource["/404"].get - - (response.status_code).should eq(404) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr deleted file mode 100644 index 6d50bdb25af9..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/headers_spec.cr +++ /dev/null @@ -1,20 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "Headers" do - context Crest::Request do - it "should set headers" do - response = Crest.get("#{TEST_SERVER_URL}/headers", headers: {"Access-Token" => ["secret1", "secret2"]}) - (response.status_code).should eq(200) - (response.headers.[]("Access-Token")).should eq(["secret1", "secret2"]) - (JSON.parse(response.body)["headers"]["Access-Token"]).should eq("secret1;secret2") - end - - it "should get request headers" do - response = Crest.get("#{TEST_SERVER_URL}/headers/set", params: {"foo" => "bar"}) - (response.status_code).should eq(200) - (response.headers.[]("foo")).should eq("bar") - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr deleted file mode 100644 index 34b46b30179f..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/logger_spec.cr +++ /dev/null @@ -1,18 +0,0 @@ -require "../spec_helper" - -describe Crest::Logger do - describe "filters" do - it "filter logs by regex" do - IO.pipe do |r, w| - params = {:width => "100", :height => 100, :api_key => "secret"} - logger = Crest::CommonLogger.new(w) - logger.filter(/(api_key=)(\w+)/, "\\1[REMOVED]") - - Crest::Request.get("#{TEST_SERVER_URL}/resize", params: params, logger: logger, logging: true) - - r.gets.should match(/[REMOVED]/) - r.gets.should match(/[REMOVED]/) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr deleted file mode 100644 index 3bed6fbaefb5..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/proxy_spec.cr +++ /dev/null @@ -1,61 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "With proxy server" do - it "should make request" do - with_proxy_server do |host, port, wants_close| - response = Crest.get("#{TEST_SERVER_URL}/", p_addr: host, p_port: port) - (response.status_code).should eq(200) - (response.body).should contain("Hello World!") - (response.request.p_addr).should eq("127.0.0.1") - (response.request.p_port).should eq(8080) - ensure - wants_close.send(nil) - end - end - - it "should redirect with proxy" do - with_proxy_server do |_, _, wants_close| - response = Crest.get("#{TEST_SERVER_URL}/redirect/1", p_addr: "127.0.0.1", p_port: 8080) - (response.status_code).should eq(200) - (response.body).should contain("Hello World!") - (response.url).should eq("#{TEST_SERVER_URL}/") - (response.history.size).should eq(1) - (response.history.first.url).should eq("#{TEST_SERVER_URL}/redirect/1") - (response.history.first.status_code).should eq(302) - (response.request.p_addr).should eq("127.0.0.1") - (response.request.p_port).should eq(8080) - ensure - wants_close.send(nil) - end - end - end - - describe Crest::Resource do - it "should make request" do - with_proxy_server do |host, port, wants_close| - resource = Crest::Resource.new("#{TEST_SERVER_URL}", p_addr: host, p_port: port) - response = resource.get - - (response.status_code).should eq(200) - (response.body).should contain("Hello World!") - (response.request.p_addr).should eq("127.0.0.1") - (response.request.p_port).should eq(8080) - ensure - wants_close.send(nil) - end - end - - it "should make suburl request" do - with_proxy_server do |host, port, wants_close| - resource = Crest::Resource.new("#{TEST_SERVER_URL}", p_addr: host, p_port: port) - response = resource["/post/1/comments"].get - - (response.status_code).should eq(200) - (response.body).should contain("Post 1: comments") - ensure - wants_close.send(nil) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr deleted file mode 100644 index eb24f97fa590..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/redirection_spec.cr +++ /dev/null @@ -1,77 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "Redirects handling" do - it "should redirect" do - response = Crest.get("#{TEST_SERVER_URL}/redirect/1") - (response.status_code).should eq(200) - (response.url).should eq("#{TEST_SERVER_URL}/") - (response.body).should eq("Hello World!") - (response.history.size).should eq(1) - (response.history.first.url).should eq("#{TEST_SERVER_URL}/redirect/1") - (response.history.first.status_code).should eq(302) - end - - it "should redirect and save history" do - response = Crest.get("#{TEST_SERVER_URL}/redirect/2") - (response.status_code).should eq(200) - (response.history.size).should eq(2) - (response.history.first.status_code).should eq(302) - end - - it "should redirect with logger" do - response = Crest.get("#{TEST_SERVER_URL}/redirect/1", logging: true) - (response.request.logging).should eq(true) - (response.request.logger).should be_a(Crest::Logger) - end - - it "should raise error when too many redirects" do - expect_raises Crest::RequestFailed, "HTTP status code 302" do - Crest.get("#{TEST_SERVER_URL}/redirect/circle1") - end - end - - it "should raise last error" do - expect_raises Crest::RequestFailed, "HTTP status code 404" do - Crest.get("#{TEST_SERVER_URL}/redirect/not_found") - end - end - - it "should not raise last error if handle_errors is false" do - response = Crest.get("#{TEST_SERVER_URL}/redirect/not_found", handle_errors: false) - - (response.status_code).should eq(404) - (response.history.first.status_code).should eq(302) - end - - it "should not follow redirection when max_redirects is 0" do - expect_raises Crest::RequestFailed, "HTTP status code 302" do - Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0) - end - end - - it "should not follow redirection when max_redirects is 0 and raise Crest::Found" do - expect_raises Crest::Found, "HTTP status code 302" do - Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0) - end - end - - it "should not raise exception when handle_errors is false" do - response = Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0, handle_errors: false) - (response.status_code).should eq(302) - (response.body).should eq("Redirecting to /") - end - - it "should not raise exception in the block when handle_errors is false" do - body = status_code = nil - - Crest.get("#{TEST_SERVER_URL}/redirect/1", max_redirects: 0, handle_errors: false) do |response| - status_code = response.status_code - body = response.body_io.gets_to_end - end - - body.should eq("Redirecting to /") - status_code.should eq(302) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr deleted file mode 100644 index a43299e2970f..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/request_spec.cr +++ /dev/null @@ -1,175 +0,0 @@ -require "../spec_helper" - -describe Crest::Request do - it "initialize and do request" do - request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/post/1/comments") - response = request.execute - - (response.body).should eq("Post 1: comments") - end - - it "do GET request" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/post/1/comments") - - (response.body).should eq("Post 1: comments") - end - - it "call get method" do - response = Crest::Request.get("#{TEST_SERVER_URL}/post/1/comments") - - (response.body).should eq("Post 1: comments") - end - - it "do GET request with params" do - response = Crest::Request.execute(:get, - "#{TEST_SERVER_URL}/resize", - params: {:width => 100, :height => 100} - ) - - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with different params" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/resize", params: {"width" => 100, :height => "100"}) - - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with params with nil" do - response = Crest::Request.execute(:get, "#{TEST_SERVER_URL}/add_key", params: {:json => nil, :key => 123}) - (response.body).should eq("JSON: key[123]") - end - - it "should accept block on initialize as request" do - url = "#{TEST_SERVER_URL}/headers" - - request = Crest::Request.new(:get, url) do |req| - req.headers.add("foo", "bar") - end - - response = request.execute - - (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") - end - - it "initializer can accept HTTP::Client as http_client" do - url = "#{TEST_SERVER_URL}/headers" - uri = URI.parse(TEST_SERVER_URL) - - client = HTTP::Client.new(uri) - client.before_request do |request| - request.headers.add("foo", "bar") - end - - response = Crest::Request.execute(:get, url, http_client: client) - (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") - end - - it "access http_client in instance of Crest::Request" do - url = "#{TEST_SERVER_URL}/headers" - - request = Crest::Request.new(:get, url) - - request.http_client.before_request do |req| - req.headers.add("foo", "bar") - end - - response = request.execute - - (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") - end - - it "change HTTP::Client in Crest::Request" do - url = "#{TEST_SERVER_URL}/delay/2" - uri = URI.parse(TEST_SERVER_URL) - - client = HTTP::Client.new(uri) - client.read_timeout = 5.minutes - - request = Crest::Request.new(:get, url, http_client: client) - - request.http_client.read_timeout = 1.second - - expect_raises IO::TimeoutError do - request.execute - end - end - - it "do POST request" do - response = Crest::Request.execute(:post, "#{TEST_SERVER_URL}/post/1/comments", form: {:title => "Title"}) - - (response.body).should eq("Post with title `Title` created") - end - - it "do POST request and encode form" do - response = Crest::Request.execute(:post, "#{TEST_SERVER_URL}/post/1/comments", form: {:title => "New @Title"}) - - (response.body).should eq("Post with title `New @Title` created") - end - - it "call post method" do - response = Crest::Request.post("#{TEST_SERVER_URL}/post/1/comments", form: {:title => "Title"}) - - (response.body).should eq("Post with title `Title` created") - end - - it "call post method with json content type" do - response = Crest::Request.post( - "#{TEST_SERVER_URL}/post/1/json", - headers: {"Content-Type" => "application/json"}, - form: {:title => "Title"}.to_json - ) - - (response.body).should eq("Post with title `Title` created") - end - - it "upload file" do - file = File.open("#{__DIR__}/../support/fff.png") - response = Crest::Request.post("#{TEST_SERVER_URL}/upload", form: {:file => file}) - (response.body).should match(/Upload OK/) - end - - it "do OPTIONS request" do - response = Crest::Request.execute(:options, "#{TEST_SERVER_URL}") - - (response.headers["Allow"]).should eq("OPTIONS, GET") - end - - it "call options method" do - response = Crest::Request.options("#{TEST_SERVER_URL}") - - (response.headers["Allow"]).should eq("OPTIONS, GET") - end - - it "should skip 'Content-Type' in headers for requests with form" do - response = Crest::Request.post( - "#{TEST_SERVER_URL}/post/1/comments", - headers: {"Content-Type" => "application/json"}, - form: {:title => "Title"} - ) - - (response.body).should eq("Post with title `Title` created") - end - - describe "User-Agent" do - it "should have default user agent" do - url = "#{TEST_SERVER_URL}/headers" - - request = Crest::Request.new(:get, url) - - response = request.execute - - (JSON.parse(response.body)["headers"]["User-Agent"]).should eq("Crest/#{Crest::VERSION} (Crystal/#{Crystal::VERSION})") - end - - it "change user agent" do - url = "#{TEST_SERVER_URL}/headers" - - request = Crest::Request.new(:get, url, headers: {"User-Agent" => "Crest"}) - - response = request.execute - - (JSON.parse(response.body)["headers"]["User-Agent"]).should eq("Crest") - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr deleted file mode 100644 index 9ea92f4b8577..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/resource_spec.cr +++ /dev/null @@ -1,210 +0,0 @@ -require "../spec_helper" - -describe Crest::Response do - it "do GET request" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments") - response = resource.get - (response.body).should eq("Post 1: comments") - end - - it "do GET request when base url ends with /" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/") - response = resource.get("/post/1/comments") - (response.body).should eq("Post 1: comments") - end - - it "do GET request when path does not start with /" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}") - response = resource.get("post/1/comments") - (response.body).should eq("Post 1: comments") - end - - it "do GET request with []" do - site = Crest::Resource.new("#{TEST_SERVER_URL}") - response = site["/post/1/comments"].get - (response.body).should eq("Post 1: comments") - end - - it "do GET request with [] when base url ends with /" do - site = Crest::Resource.new("#{TEST_SERVER_URL}/") - response = site["/post/1/comments"].get - (response.body).should eq("Post 1: comments") - end - - it "do multiple GET requests with []" do - site = Crest::Resource.new("#{TEST_SERVER_URL}") - - response1 = site["/post/1/comments"].get - response2 = site["/post/2/comments"].get - - (response1.body).should eq("Post 1: comments") - (response2.body).should eq("Post 2: comments") - end - - it "do GET request with params" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/resize") - response = resource.get(params: {:width => "100", :height => 100}) - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with [] and params" do - resource = Crest::Resource.new(TEST_SERVER_URL) - response = resource["/resize"].get(params: {:width => 100, :height => 100}) - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with suburl and params" do - resource = Crest::Resource.new(TEST_SERVER_URL) - response = resource.get("resize", params: {:width => 100, :height => 100}) - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with [] and default params" do - resource = Crest::Resource.new( - TEST_SERVER_URL, - params: {:width => 100, :height => 100} - ) - response = resource["/resize"].get - (response.body).should eq("Width: 100, height: 100") - end - - it "do GET request with suburl and default params" do - resource = Crest::Resource.new( - TEST_SERVER_URL, - params: {:width => 100} - ) - response = resource.get("/resize", params: {:height => 100}) - (response.body).should eq("Width: 100, height: 100") - end - - it "should accept block" do - resource = Crest::Resource.new(TEST_SERVER_URL) do |res| - res.headers.merge!({"foo" => "bar"}) - end - - response = resource["/headers"].get - - (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") - end - - it "initializer can accept HTTP::Client as http_client" do - uri = URI.parse(TEST_SERVER_URL) - - client = HTTP::Client.new(uri) - client.before_request do |request| - request.headers.add("foo", "bar") - end - - resource = Crest::Resource.new(TEST_SERVER_URL, http_client: client) - response = resource["/headers"].get - - (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") - end - - it "access http_client in instance of Crest::Resource" do - resource = Crest::Resource.new(TEST_SERVER_URL) - resource.http_client.before_request do |req| - req.headers.add("foo", "bar") - end - - response = resource["/headers"].get - - (JSON.parse(response.body)["headers"]["foo"]).should eq("bar") - end - - it "change HTTP::Client in Crest::Resource" do - uri = URI.parse(TEST_SERVER_URL) - - client = HTTP::Client.new(uri) - client.read_timeout = 5.minutes - - resource = Crest::Resource.new(TEST_SERVER_URL, http_client: client) - - resource.http_client.read_timeout = 1.second - - expect_raises IO::TimeoutError do - resource["/delay/2"].get - end - end - - it "do POST request" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments") - response = resource.post(form: {:title => "Title"}) - (response.body).should eq("Post with title `Title` created") - end - - it "do POST request with []" do - site = Crest::Resource.new(TEST_SERVER_URL) - response = site["/post/1/comments"].post(form: {:title => "Title"}) - (response.body).should eq("Post with title `Title` created") - end - - it "do POST request with suburl" do - site = Crest::Resource.new(TEST_SERVER_URL) - response = site.post("/post/1/comments", form: {:title => "Title"}) - (response.body).should eq("Post with title `Title` created") - end - - it "do POST request with [] and default params" do - site = Crest::Resource.new(TEST_SERVER_URL, params: {"key" => "key"}) - response = site["/resize"].post( - form: {:height => 100, "width" => "100"}, - params: {:secret => "secret"} - ) - (response.body).should eq("Width: 100, height: 100. Key: key, secret: secret") - end - - it "upload file" do - file = File.open("#{__DIR__}/../support/fff.png") - resource = Crest::Resource.new("#{TEST_SERVER_URL}/upload") - response = resource.post(form: {:file => file}) - - (response.body).should match(/Upload OK/) - end - - it "upload file with []" do - file = File.open("#{__DIR__}/../support/fff.png") - resource = Crest::Resource.new("#{TEST_SERVER_URL}") - response = resource["/upload"].post(form: {:file => file}) - - (response.body).should match(/Upload OK/) - end - - it "do PUT request" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments/1") - response = resource.put(form: {:title => "Put Update"}) - (response.body).should eq("Update Comment `1` for Post `1` with title `Put Update`") - end - - it "do PATCH request" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments/1") - response = resource.patch(form: {:title => "Patch Update"}) - (response.body).should eq("Update Comment `1` for Post `1` with title `Patch Update`") - end - - it "do DELETE request" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}/post/1/comments/1") - response = resource.delete - (response.body).should eq("Delete Comment `1` for Post `1`") - end - - it "do GET request with logging" do - resource = Crest::Resource.new(TEST_SERVER_URL, logging: true) - response = resource["/post/1/comments"].get - (response.body).should eq("Post 1: comments") - end - - it "do OPTIONS request" do - resource = Crest::Resource.new(TEST_SERVER_URL) - response = resource.options - - (response.headers["Allow"]).should eq("OPTIONS, GET") - end - - it "#to_curl" do - resource = Crest::Resource.new("#{TEST_SERVER_URL}") - response = resource["/post/1/comments"].get - - (response.to_curl).should eq("curl -X GET #{TEST_SERVER_URL}/post/1/comments") - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr deleted file mode 100644 index 19b9727905f2..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/response_spec.cr +++ /dev/null @@ -1,57 +0,0 @@ -require "../spec_helper" - -describe Crest::Response do - it "response instance should respond to helper methods" do - response = Crest.get("#{TEST_SERVER_URL}") - (response.body).should eq("Hello World!") - (response.invalid?).should be_false - (response.informational?).should be_false - (response.success?).should be_true - (response.redirection?).should be_false - (response.redirect?).should be_false - (response.client_error?).should be_false - (response.server_error?).should be_false - end - - it "response instance should have status and status_code" do - response = Crest.get("#{TEST_SERVER_URL}") - - (response.status).should be_a(HTTP::Status) - (response.status_code).should be_a(Int32) - end - - it "response instance should have filename if available" do - filename = "filename.jpg" - response = Crest.get("#{TEST_SERVER_URL}/download?filename=#{filename}") - - (response.filename).should eq(filename) - end - - it "response instance should have filename nil unless available" do - response = Crest.get("#{TEST_SERVER_URL}") - - (response.filename).should be_nil - end - - it "#to_curl" do - response = Crest.get("#{TEST_SERVER_URL}") - - (response.to_curl).should eq("curl -X GET #{TEST_SERVER_URL}") - end - - describe "to_s" do - it "does to_s" do - response = Crest.get("#{TEST_SERVER_URL}") - - response.to_s.should eq("Hello World!") - end - end - - describe "inspect" do - it "does inspect" do - response = Crest.get("#{TEST_SERVER_URL}") - - (response.inspect).should eq("") - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr deleted file mode 100644 index f6ed035a1c37..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/integration/streaming_spec.cr +++ /dev/null @@ -1,120 +0,0 @@ -require "../spec_helper" - -describe Crest do - describe "Streaming" do - it "should stream Response#execute" do - count = 5 - body = String::Builder.new - request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/stream/#{count}") - - request.execute do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - - it "should stream Request.execute" do - count = 5 - body = String::Builder.new - Crest::Request.get("#{TEST_SERVER_URL}/stream/#{count}") do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - - it "should stream Request.get" do - count = 5 - body = String::Builder.new - Crest::Request.execute(:get, "#{TEST_SERVER_URL}/stream/#{count}") do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - - it "should stream Crest#get" do - count = 5 - body = String::Builder.new - Crest.get("#{TEST_SERVER_URL}/stream/#{count}") do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - - it "should stream Resource#get with []" do - count = 5 - body = String::Builder.new - resource = Crest::Resource.new(TEST_SERVER_URL) - resource["/stream/#{count}"].get do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - - it "should stream Resource#get" do - count = 5 - body = String::Builder.new - resource = Crest::Resource.new(TEST_SERVER_URL) - resource.get("/stream/#{count}") do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - - it "should stream Crest#get with redirects" do - body = String::Builder.new - - Crest.get("#{TEST_SERVER_URL}/redirect/2") do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!") - end - - it "should stream Response#execute with redirects" do - count = 5 - body = String::Builder.new - request = Crest::Request.new(:get, "#{TEST_SERVER_URL}/redirect_stream/#{count}") - - request.execute do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * 5) - end - - it "should stream Resource#get with redirects" do - count = 5 - body = String::Builder.new - resource = Crest::Resource.new(TEST_SERVER_URL) - resource["/redirect_stream/#{count}"].get do |resp| - while line = resp.body_io.gets - body << line - end - end - - body.to_s.should eq("Hello World!" * count) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr deleted file mode 100644 index 5aed9dbd45a4..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/spec_helper.cr +++ /dev/null @@ -1,40 +0,0 @@ -require "spec" -require "json" -require "http_proxy" -require "../src/crest" -require "./support/server" - -TEST_SERVER_HOST = "0.0.0.0" -TEST_SERVER_PORT = 4567 -TEST_SERVER_URL = "http://#{TEST_SERVER_HOST}:#{TEST_SERVER_PORT}" - -kemal_config = Kemal.config -kemal_config.env = "development" -kemal_config.logging = false - -spawn do - Kemal.run(port: TEST_SERVER_PORT) -end - -until Kemal.config.running - sleep 1.millisecond -end - -def with_proxy_server(host = "127.0.0.1", port = 8080) - wants_close = Channel(Nil).new - server = HTTP::Proxy::Server.new - - spawn do - server.bind_tcp(host, port) - server.listen - end - - spawn do - wants_close.receive - server.close - end - - Fiber.yield - - yield host, port, wants_close -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr b/samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr deleted file mode 100644 index 68308b60e622..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/support/ext/kemal/ext/context.cr +++ /dev/null @@ -1,10 +0,0 @@ -class HTTP::Server - class Context - # TODO https://github.com/kemalcr/kemal/pull/561 - def redirect(url : String, status_code : Int32 = 302, *, body : String = "") - @response.headers.add "Location", url - @response.write(body.to_slice) - @response.status_code = status_code - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/fff.png b/samples/client/petstore/crystal/lib/crest/spec/support/fff.png deleted file mode 100644 index a3e71629295905dafa0d7d4c769ad57d32d4ff23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)gaYymzYu0Z<#|Nl#G&c6#}aTa() v7BevL9RXp+soH$fKtV1~7sn8enaK%2HWLHmm%@}~Ko*0itDnm{r-UW|njsiP diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr b/samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr deleted file mode 100644 index 17e81ad3f820..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/support/kemal_basic_auth.cr +++ /dev/null @@ -1,35 +0,0 @@ -require "kemal" -require "base64" - -module KemalBasicAuth - class Handler < Kemal::Handler - BASIC = "Basic" - AUTH = "Authorization" - AUTH_MESSAGE = "Could not verify your access level for that URL.\nYou have to login with proper credentials" - HEADER_LOGIN_REQUIRED = "Basic realm=\"Login Required\"" - - def initialize(@username : String, @password : String) - end - - def call(env) - if env.request.headers[AUTH]? - if value = env.request.headers[AUTH] - if value.size > 0 && value.starts_with?(BASIC) - if authorize?(value) - return call_next(env) - end - end - end - end - - env.response.status_code = 401 - env.response.headers["WWW-Authenticate"] = HEADER_LOGIN_REQUIRED - env.response.print(AUTH_MESSAGE) - end - - private def authorize?(value) : String? - username, password = Base64.decode_string(value[BASIC.size + 1..-1]).split(":") - [@username, @password] == [username, password] ? "" : nil - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/server.cr b/samples/client/petstore/crystal/lib/crest/spec/support/server.cr deleted file mode 100644 index aa4f8c4af0a1..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/support/server.cr +++ /dev/null @@ -1,229 +0,0 @@ -require "compress/deflate" -require "compress/gzip" -require "compress/zlib" -require "kemal" -require "./kemal_basic_auth" -require "./ext/kemal/ext/context" - -class BasicAuthHandler < KemalBasicAuth::Handler - only ["/secret", "/secret_redirect"] - - def call(env) - return call_next(env) unless only_match?(env) - - super - end -end - -add_handler BasicAuthHandler.new("username", "password") - -error 404 do - "404 error" -end - -error 500 do - "500 error" -end - -get "/" do - "Hello World!" -end - -options "/" do |env| - env.response.headers["Allow"] = "OPTIONS, GET" -end - -get "/secret" do - "Secret World!" -end - -get "/secret_redirect" do |env| - env.redirect("/secret") -end - -post "/upload" do |env| - file = env.params.files["file"].tempfile - - File.open(file.path, "w") do |f| - IO.copy(file, f) - end - - "Upload OK - #{file.path}" -end - -post "/post_nested" do |env| - params = env.params - params.body.to_s -end - -# Comments -# -# index -get "/post/:id/comments" do |env| - "Post #{env.params.url["id"]}: comments" -end - -# create -post "/post/:id/comments" do |env| - "Post with title `#{env.params.body["title"]}` created" -end - -# update -put "/post/:post_id/comments/:id" do |env| - "Update Comment `#{env.params.url["id"]}` for Post `#{env.params.url["post_id"]}` with title `#{env.params.body["title"]}`" -end - -# update -patch "/post/:post_id/comments/:id" do |env| - "Update Comment `#{env.params.url["id"]}` for Post `#{env.params.url["post_id"]}` with title `#{env.params.body["title"]}`" -end - -# delete -delete "/post/:post_id/comments/:id" do |env| - "Delete Comment `#{env.params.url["id"]}` for Post `#{env.params.url["post_id"]}`" -end -### - -# Matches /resize?width=200&height=200 -get "/resize" do |env| - width = env.params.query["width"] - height = env.params.query["height"] - - "Width: #{width}, height: #{height}" -end - -# Matches /resize?key=secter -post "/resize" do |env| - height = env.params.body.[]("height") - width = env.params.body.[]("width") - key = env.params.query["key"] - secret = env.params.query["secret"] - - "Width: #{width}, height: #{height}. Key: #{key}, secret: #{secret}" -end - -# Matches /add_key?json&key=123 -get "/add_key" do |env| - key = env.params.query["key"] - - "JSON: key[#{key}]" -end - -post "/post/:id/json" do |env| - title = env.params.json["title"].as(String) - "Post with title `#{title}` created" -end - -get "/404" do |env| - env.response.status_code = 404 -end - -get "/500" do |env| - env.response.status_code = 500 -end - -get "/stream/:count" do |env| - count = env.params.url["count"].to_i - - count.times do - env.response.puts("Hello World!") - end - - env -end - -# Redirects -# -get "/redirect/1" do |env| - env.redirect("/", body: "Redirecting to /") -end - -get "/redirect/2" do |env| - env.redirect("/redirect/1") -end - -get "/redirect/circle1" do |env| - env.redirect("/redirect/circle2") -end - -get "/redirect/circle2" do |env| - env.redirect("/redirect/circle1") -end - -get "/redirect/not_found" do |env| - env.redirect("/404") -end - -get "/redirect_stream/:count" do |env| - count = env.params.url["count"].to_i - - env.redirect("/stream/#{count}") -end - -# Return request headers -get "/headers" do |env| - result = {} of String => String - env.request.headers.each do |key, value| - result[key] = value.join(";") - end - - {"headers" => result}.to_json -end - -# Set response headers -get "/headers/set" do |env| - env.params.query.each do |param| - env.response.headers[param[0]] = param[1] - end - - "" -end - -# Returns cookies data -get "/cookies" do |env| - result = {} of String => String - env.request.cookies.to_h.each do |_, cookie| - result[cookie.name] = cookie.value - end - - {"cookies" => result}.to_json -end - -# /cookies/set?name=value Sets one or more simple cookies. -get "/cookies/set" do |env| - env.params.query.each do |param| - env.response.cookies << HTTP::Cookie.new(name: param[0], value: param[1]) - end - - result = {} of String => String - env.response.cookies.to_h.each do |_, cookie| - result[cookie.name] = cookie.value - end - - {"cookies" => result}.to_json -end - -# /cookies/set_redirect?name=value Sets one or more simple cookies and redirect. -get "/cookies/set_redirect" do |env| - env.params.query.each do |param| - env.response.cookies << HTTP::Cookie.new(name: param[0], value: param[1]) - end - - env.redirect("/cookies") -end - -# Delays responding for `:seconds` seconds. -get "/delay/:seconds" do |env| - seconds = env.params.url["seconds"].to_i - sleep seconds - - "Delay #{seconds} seconds" -end - -# Matches /download?filename=foo.bar -get "/download" do |env| - filename = env.params.query["filename"]? || "foo.bar" - file = File.open("#{__DIR__}/fff.png") - - send_file env, file.path, mime_type: "Application/octet-stream", filename: filename -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr b/samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr deleted file mode 100644 index 696e520f4827..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/support/server_cli.cr +++ /dev/null @@ -1,3 +0,0 @@ -require "./server" - -Kemal.run diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr deleted file mode 100644 index b895485fc3fe..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/crest_spec.cr +++ /dev/null @@ -1,7 +0,0 @@ -require "../spec_helper" - -describe Crest::VERSION do - it "should have version" do - (Crest::VERSION).should be_a(String) - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr deleted file mode 100644 index 80c51b649383..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/curlify_spec.cr +++ /dev/null @@ -1,95 +0,0 @@ -require "../spec_helper" - -def curlify(request : Crest::Request) - Crest::Curlify.new(request).to_curl -end - -describe Crest::Curlify do - it "converts simple GET request" do - request = Crest::Request.new(:get, "http://httpbin.org/get") - - result = "curl -X GET http://httpbin.org/get" - (request.to_curl).should eq(result) - end - - it "converts GET request with params" do - request = Crest::Request.new(:get, "http://httpbin.org/get", params: {"foo" => "bar"}) - - result = "curl -X GET http://httpbin.org/get?foo=bar" - curlify(request).should eq(result) - end - - it "converts a request with basic auth as parameters" do - request = Crest::Request.new(:get, "http://httpbin.org/basic-auth/user/passwd", user: "user", password: "passwd") - - result = "curl -X GET http://httpbin.org/basic-auth/user/passwd --user user:passwd" - (request.to_curl).should eq(result) - end - - it "converts a request with digest auth as parameters" do - request = Crest::Request.new(:get, "http://httpbin.org/digest-auth/auth/user/passwd/MD5", auth: "digest", user: "user", password: "passwd") - - result = "curl -X GET http://httpbin.org/digest-auth/auth/user/passwd/MD5 --digest --user user:passwd" - (request.to_curl).should eq(result) - end - - it "converts a request to invalid domain with digest auth" do - request = Crest::Request.new(:get, "https://domain.invalid", auth: "digest", user: "user", password: "passwd") - - result = "curl -X GET https://domain.invalid --digest --user user:passwd" - (request.to_curl).should eq(result) - end - - it "converts a request with basic auth in headers" do - request = Crest::Request.new(:get, "http://httpbin.org/basic-auth/user/passwd", headers: {"Authorization" => "Basic dXNlcjpwYXNzd2Q="}) - - result = "curl -X GET http://httpbin.org/basic-auth/user/passwd -H 'Authorization: Basic dXNlcjpwYXNzd2Q='" - (request.to_curl).should eq(result) - end - - it "converts a request with proxy" do - request = Crest::Request.new(:get, "http://httpbin.org", p_addr: "127.0.0.1", p_port: 8080) - - result = "curl -X GET http://httpbin.org --proxy 127.0.0.1:8080" - (request.to_curl).should eq(result) - end - - it "converts a request with proxy with authentication" do - request = Crest::Request.new(:get, "http://httpbin.org", p_addr: "127.0.0.1", p_port: 8080, p_user: "user", p_pass: "pass") - - result = "curl -X GET http://httpbin.org --proxy 127.0.0.1:8080 --proxy-user user:pass" - (request.to_curl).should eq(result) - end - - it "converts POST request" do - request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"title" => "New Title", "author" => "admin"}) - - result = "curl -X POST http://httpbin.org/post -d 'title=New+Title&author=admin' -H 'Content-Type: application/x-www-form-urlencoded'" - curlify(request).should eq(result) - end - - it "converts POST request with multipart" do - current_dir = __DIR__ - file = File.open("#{current_dir}/../support/fff.png") - - request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"title" => "New Title", "file" => file}) - - result = "curl -X POST http://httpbin.org/post -F 'title=New Title' -F 'file=@#{"#{current_dir}/../support/fff.png"}' -H 'Content-Type: multipart/form-data'" - curlify(request).should eq(result) - end - - it "converts POST request with headers" do - request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"param1" => "value1"}, headers: {"user-agent" => "crest"}) - - result = "curl -X POST http://httpbin.org/post -d 'param1=value1' -H 'user-agent: crest' -H 'Content-Type: application/x-www-form-urlencoded'" - curlify(request).should eq(result) - end - - it "converts POST request with json" do - request = Crest::Request.new(:post, "http://httpbin.org/post", form: {:foo => "bar"}.to_json, headers: {"Content-Type" => "application/json"}) - - result = "curl -X POST http://httpbin.org/post -d '{\"foo\":\"bar\"}' -H 'Content-Type: application/json'" - - curlify(request).should eq(result) - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr deleted file mode 100644 index 3129e0092a30..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/data_form_spec.cr +++ /dev/null @@ -1,31 +0,0 @@ -require "../spec_helper" - -describe Crest::DataForm do - describe "#generate" do - it "generate form" do - input = {:file => {"one" => "one", "two" => "two"}} - parsed_input = [{"file[one]", "one"}, {"file[two]", "two"}] - content_type = "multipart/form-data" - - form = Crest::DataForm.generate(input) - - form.content_type.should contain(content_type) - form.params.should eq(input) - form.parsed_params.should eq(parsed_input) - end - - it "generate form with multipart" do - file = File.open("#{__DIR__}/../support/fff.png") - input = {:file => file, "param1" => "value1"} - - parsed_input = [{"file", file}, {"param1", "value1"}] - content_type = "multipart/form-data" - - form = Crest::DataForm.generate(input) - - form.content_type.should contain(content_type) - form.params.should eq(input) - form.parsed_params.should eq(parsed_input) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr deleted file mode 100644 index 55d2a7cc0346..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/exceptions_spec.cr +++ /dev/null @@ -1,11 +0,0 @@ -require "../spec_helper" - -describe Crest::RequestFailed do - it "should return descendant class for existing status code" do - (Crest::RequestFailed.subclass_by_status_code(404)).should eq(Crest::NotFound) - end - - it "should return self for nonexistent status code" do - (Crest::RequestFailed.subclass_by_status_code(777)).should eq(Crest::RequestFailed) - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr deleted file mode 100644 index 0b5435125b85..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/params_encoder_spec.cr +++ /dev/null @@ -1,138 +0,0 @@ -require "../spec_helper" - -describe Crest::ParamsEncoder do - describe "#encode" do - it "encodes hash" do - input = {:foo => "123", :bar => "456"} - output = "foo=123&bar=456" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - - it "encodes hash as http url-encoded" do - input = {:email => "user@example.com", :title => "Hello world!"} - output = "email=user%40example.com&title=Hello+world%21" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - - it "encodes hash with nil" do - input = {:foo => nil, :bar => "2"} - output = "foo=&bar=2" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - - it "encodes hash with boolean" do - input = {:foo => true, :bar => "2"} - output = "foo=true&bar=2" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - - it "encodes hash with symbol" do - input = {:foo => :bar} - output = "foo=bar" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - - it "encodes hash with numeric values" do - input = {:foo => 1, :bar => 2} - output = "foo=1&bar=2" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - - it "encodes complex objects" do - input = {"a" => ["one", "two", "three"], "b" => true, "c" => "C", "d" => 1} - output = "a%5B%5D=one&a%5B%5D=two&a%5B%5D=three&b=true&c=C&d=1" - - Crest::ParamsEncoder.encode(input).should eq(output) - end - end - - describe "#decode" do - it "decodes simple params" do - query = "foo=1&bar=2" - params = {"foo" => "1", "bar" => "2"} - - Crest::ParamsEncoder.decode(query).should eq(params) - end - - it "decodes params with nil" do - query = "foo=&bar=2" - params = {"foo" => nil, "bar" => "2"} - - Crest::ParamsEncoder.decode(query).should eq(params) - end - - it "decodes array " do - query = "a[]=one&a[]=two&a[]=three" - params = {"a" => ["one", "two", "three"]} - - Crest::ParamsEncoder.decode(query).should eq(params) - end - - it "decodes hashes" do - query = "user[login]=admin" - params = {"user" => {"login" => "admin"}} - - Crest::ParamsEncoder.decode(query).should eq(params) - end - - it "decodes escaped string" do - query = "user%5Blogin%5D=admin" - params = {"user" => {"login" => "admin"}} - - Crest::ParamsEncoder.decode(query).should eq(params) - end - end - - describe "#flatten_params" do - it "transform nested param" do - input = {:key1 => {:key2 => "123"}} - output = [{"key1[key2]", "123"}] - - Crest::ParamsEncoder.flatten_params(input).should eq(output) - end - - it "transform deeply nested param" do - input = {:key1 => {:key2 => {:key3 => "123"}}} - output = [{"key1[key2][key3]", "123"}] - - Crest::ParamsEncoder.flatten_params(input).should eq(output) - end - - it "transform deeply nested param with file" do - file = File.open("#{__DIR__}/../support/fff.png") - input = {:key1 => {:key2 => {:key3 => file}}} - output = [{"key1[key2][key3]", file}] - - Crest::ParamsEncoder.flatten_params(input).should eq(output) - end - - it "transform nested param with array" do - input = {:key1 => {:arr => ["1", "2", "3"]}, :key2 => "123"} - output = [{"key1[arr][]", "1"}, {"key1[arr][]", "2"}, {"key1[arr][]", "3"}, {"key2", "123"}] - - Crest::ParamsEncoder.flatten_params(input).should eq(output) - end - - it "parse nested params with files" do - file = File.open("#{__DIR__}/../support/fff.png") - - input = {:key1 => {:key2 => file}} - output = [{"key1[key2]", file}] - - Crest::ParamsEncoder.flatten_params(input).should eq(output) - end - - it "parse simple params with nil value" do - input = {:key1 => nil} - output = [{"key1", nil}] - - Crest::ParamsEncoder.flatten_params(input).should eq(output) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr deleted file mode 100644 index 71d4cfae368c..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/request_spec.cr +++ /dev/null @@ -1,141 +0,0 @@ -require "../spec_helper" - -describe Crest::Request do - describe "#initialize" do - it "new HTTP request" do - request = Crest::Request.new(:get, "http://localhost/get") - (request.url).should eq("http://localhost/get") - (request.max_redirects).should eq(10) - (request.host).should eq("localhost") - (request.port).should eq(80) - (request.tls?).should eq(nil) - (request.user).should be_nil - (request.password).should be_nil - (request.proxy).should be_nil - (request.logging).should be_false - end - - it "new HTTPS request" do - request = Crest::Request.new(:get, "https://localhost/get") - (request.url).should eq("https://localhost/get") - (request.host).should eq("localhost") - (request.port).should eq(443) - (request.tls?).should be_a(OpenSSL::SSL::Context::Client) - end - - it "initialize the GET request" do - request = Crest::Request.new(:get, "http://localhost", headers: {"Content-Type" => "application/json"}) - (request.method).should eq("GET") - (request.url).should eq("http://localhost") - (request.headers).should eq(HTTP::Headers{"Content-Type" => "application/json"}) - (request.form_data).should eq(nil) - end - - it "initialize the GET request with params" do - request = Crest::Request.new(:get, "http://localhost", params: {:foo => "hello world", :bar => 456}) - (request.method).should eq("GET") - (request.url).should eq("http://localhost?foo=hello+world&bar=456") - (request.form_data).should eq(nil) - end - - it "initialize the GET request with params in url" do - request = Crest::Request.new(:get, "http://localhost?json", params: {:key => 123}) - (request.method).should eq("GET") - (request.url).should eq("http://localhost?json&key=123") - (request.form_data).should eq(nil) - end - - it "initialize the GET request with nil value in params" do - request = Crest::Request.new(:get, "http://localhost", params: {:json => nil, :key => 123}) - (request.method).should eq("GET") - (request.url).should eq("http://localhost?json=&key=123") - (request.form_data).should eq(nil) - end - - it "initialize the GET request with cookies" do - request = Crest::Request.new(:get, "http://localhost", cookies: {:foo => "123", :bar => 456}) - (request.headers).should eq(HTTP::Headers{"Cookie" => "foo=123; bar=456"}) - end - - it "initialize the POST request with form" do - request = Crest::Request.new(:post, "http://localhost", form: {:foo => "bar"}) - (request.method).should eq("POST") - (request.url).should eq("http://localhost") - (request.headers["Content-Type"]).should eq("application/x-www-form-urlencoded") - (request.form_data.to_s).should eq("foo=bar") - end - - it "initialize the POST request with form as a string" do - request = Crest::Request.new(:post, "http://localhost", headers: {"Content-Type" => "application/json"}, form: {:foo => "bar"}.to_json) - (request.method).should eq("POST") - (request.url).should eq("http://localhost") - (request.headers["Content-Type"]).should eq("application/json") - (request.form_data.to_s).should eq("{\"foo\":\"bar\"}") - end - - it "initialize the POST and encode string" do - request = Crest::Request.new(:post, "http://localhost", form: {:title => "New @Title"}) - (request.method).should eq("POST") - (request.url).should eq("http://localhost") - (request.form_data).should eq("title=New+%40Title") - end - - it "initialize the POST request with multipart" do - file = File.open("#{__DIR__}/../support/fff.png") - request = Crest::Request.new(:post, "http://localhost", form: {:file => file}) - (request.method).should eq("POST") - (request.url).should eq("http://localhost") - (request.headers["Content-Type"]).should contain("multipart/form-data; boundary=") - (request.form_data.to_s).should contain("form-data; name=\"file\"; filename=") - end - - it "POST request with nested hashes" do - request = Crest::Request.new(:post, "http://localhost", form: {:params1 => "one", :nested => {:params2 => "two"}}) - (request.headers["Content-Type"]).should eq("application/x-www-form-urlencoded") - (request.form_data.to_s).should eq("params1=one&nested%5Bparams2%5D=two") - end - - it "initialize the PUT request with form" do - request = Crest::Request.new(:put, "http://localhost", form: {:foo => "bar"}) - (request.method).should eq("PUT") - (request.url).should eq("http://localhost") - (request.headers["Content-Type"]).should eq("application/x-www-form-urlencoded") - (request.form_data.to_s).should eq("foo=bar") - end - - it "initialize the OPTIONS request" do - request = Crest::Request.new(:options, "http://localhost") - (request.method).should eq("OPTIONS") - (request.url).should eq("http://localhost") - end - - it "initialize Request with :max_redirects" do - request = Crest::Request.new(:get, "http://localhost", max_redirects: 3) - (request.max_redirects).should eq(3) - end - - it "initialize Request with basic auth params" do - request = Crest::Request.new(:get, "http://localhost", user: "user", password: "password") - (request.user).should eq("user") - (request.password).should eq("password") - end - - it "initialize Request with proxy params" do - request = Crest::Request.new(:get, "http://localhost", p_addr: "localhost", p_port: 3128) - (request.proxy).should be_a(HTTP::Proxy::Client) - end - - it "initialize Request with :logging and logger" do - request = Crest::Request.new(:get, "http://localhost", logging: true) - (request.logging).should eq(true) - (request.logger).should be_a(Crest::Logger) - end - end - - describe "#to_curl" do - it "converts request to cURL command" do - request = Crest::Request.new(:get, "http://localhost") - (request.to_curl).should eq("curl -X GET http://localhost") - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr deleted file mode 100644 index 3adff233f23e..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/resource_spec.cr +++ /dev/null @@ -1,48 +0,0 @@ -require "../spec_helper" - -describe Crest::Resource do - describe "#initialize" do - it "initialize new resource" do - resource = Crest::Resource.new("http://localhost", headers: {"X-Something" => "1"}) - - resource.url.should eq("http://localhost") - resource.headers.should eq({"X-Something" => "1"}) - end - - it "initialize new resource with proxy params" do - resource = Crest::Resource.new("http://localhost", p_addr: "localhost", p_port: 3128) - - resource.p_addr.should eq("localhost") - resource.p_port.should eq(3128) - end - - it "initialize new resource with logger" do - resource = Crest::Resource.new("http://localhost", logging: true) - - (resource.logging).should eq(true) - (resource.logger).should be_a(Crest::Logger) - end - - it "initialize new resource without headers" do - resource = Crest::Resource.new("http://localhost") - - resource.url.should eq("http://localhost") - resource.headers.should eq({} of String => String) - end - - it "initialize new resource with []" do - site = Crest::Resource.new("http://localhost", headers: {"X-Something" => "1"}) - resource = site["/resource"] - - resource.url.should eq("http://localhost/resource") - resource.headers.should eq({"X-Something" => "1"}) - end - - it "initialize new resource with params" do - resource = Crest::Resource.new("http://localhost", params: {"foo" => "123", "bar" => "456"}) - - resource.url.should eq("http://localhost") - resource.params.should eq({"foo" => "123", "bar" => "456"}) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr b/samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr deleted file mode 100644 index a399824ca1e6..000000000000 --- a/samples/client/petstore/crystal/lib/crest/spec/unit/urlencoded_form_spec.cr +++ /dev/null @@ -1,29 +0,0 @@ -require "../spec_helper" - -describe Crest::UrlencodedForm do - describe "#generate" do - it "generate nested form" do - input = {"year" => "2017 - today", "param2" => 2} - parsed_input = "year=2017+-+today¶m2=2" - content_type = "application/x-www-form-urlencoded" - - form = Crest::UrlencodedForm.generate(input) - - form.content_type.should contain(content_type) - form.params.should eq(input) - form.form_data.should eq(parsed_input) - end - - it "generate nested form" do - input = {:file => {"one" => "one", "two" => "two"}} - parsed_input = "file%5Bone%5D=one&file%5Btwo%5D=two" - content_type = "application/x-www-form-urlencoded" - - form = Crest::UrlencodedForm.generate(input) - - form.content_type.should contain(content_type) - form.params.should eq(input) - form.form_data.should eq(parsed_input) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest.cr b/samples/client/petstore/crystal/lib/crest/src/crest.cr deleted file mode 100644 index 833e0a469478..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest.cr +++ /dev/null @@ -1,75 +0,0 @@ -require "http" -require "uri" -require "base64" -require "http-client-digest_auth" -require "http_proxy" - -# This module's static methods are the entry point for using the Crest client. -# -# Suported HTTP methods: `get`, `put`, `post`, `patch` `delete`, `options`, `head` -# -# Examples: -# -# ```crystal -# Crest.get( -# "http://httpbin.org/get", -# headers: {"Content-Type" => "image/jpg"}, -# params: {"lang" => "en"} -# ) -# -# Crest.post( -# "http://httpbin.org/post", -# headers: {"Access-Token" => ["secret1", "secret2"]}, -# form: {:fizz => "buz"}, -# logging: true, -# ) -# -# Crest.get("http://httpbin.org/stream/5") do |response| -# while line = response.body_io.gets -# puts line -# end -# end -# ``` -module Crest - VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} - - alias TextValue = String | Symbol | Int32 | Bool | Nil - - alias Params = Hash(Symbol | String, Int32 | String) | - Hash(String, String | Int32) | - Hash(Symbol, String | Int32) | - Hash(String, String) | - Hash(String, Int32) | - Hash(Symbol, String) | - Hash(Symbol, Int32) - - HTTP_METHODS = %w{get delete post put patch options head} - - {% for method in Crest::HTTP_METHODS %} - # Execute a {{method.id.upcase}} request and and yields the `Crest::Response` to the block. - # - # ```crystal - # Crest.{{method.id}}("http://httpbin.org/{{method.id}}") do |response| - # while line = response.body_io.gets - # puts line - # end - # end - # ``` - def self.{{method.id}}(url : String, **args, &block : Crest::Response ->) : Nil - request = Request.new(:{{method.id}}, url, **args) - request.execute(&block) - end - - # Execute a {{method.id.upcase}} request and returns a `Crest::Response`. - # - # ```crystal - # Crest.{{method.id}}("http://httpbin.org/{{method.id}}") - # ``` - def self.{{method.id}}(url : String, **args) : Crest::Response - request = Request.new(:{{method.id}}, url, **args) - request.execute - end - {% end %} -end - -require "./crest/**" diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr b/samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr deleted file mode 100644 index d5fe21b4d4f1..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/curlify.cr +++ /dev/null @@ -1,93 +0,0 @@ -module Crest - # Class to convert `Crest::Request` object to cURL command - # - # ```crystal - # request = Crest::Request.new(:post, "http://httpbin.org/post", form: {"title" => "New Title"}) - # Crest::Curlify.to_curl(request) - # => "curl -X POST http://httpbin.org/post -d 'title=New+Title' -H 'Content-Type: application/x-www-form-urlencoded'" - # ``` - class Curlify - # Returns string with cURL command by provided `Crest::Request` object - def self.to_curl(request : Crest::Request) - new(request).to_curl - end - - def initialize(@request : Crest::Request) - end - - def to_curl - ["curl", method, url, proxy, basic_auth, form_data, headers].reject(&.empty?).join(" ") - end - - private def method - "-X #{@request.method}" - end - - private def url - "#{@request.url}" - end - - private def headers : String - headers = [] of String - @request.headers.each do |k, v| - next if k == "Authorization" && basic_auth? && includes_authorization_header? - - value = v.is_a?(Array) ? v.first.split(";").first : v - headers << "-H '#{k}: #{value}'" - end - - headers.join(" ") - end - - private def form_data : String - form_data = [] of String - - HTTP::FormData.parse(@request.http_request) do |part| - value = part.filename ? "@#{part.filename}" : part.body.gets_to_end - - form_data << "-F '#{part.name}=#{value}'" - end - - form_data.join(" ") - rescue HTTP::FormData::Error - body = @request.http_request.body.to_s - - body.empty? ? "" : "-d '#{body}'" - end - - private def basic_auth : String - return "" unless basic_auth? - - params = [] of String - - params << "--digest" if @request.auth == "digest" - params << "--user #{@request.user}:#{@request.password}" - - params.join(" ") - end - - private def proxy : String - return "" unless @request.proxy - - params = [] of String - - params << "--proxy #{@request.p_addr}:#{@request.p_port}" - params << "--proxy-user #{@request.p_user}:#{@request.p_pass}" if proxy_auth? - - params.join(" ") - end - - private def basic_auth? : Bool - @request.user && @request.password ? true : false - end - - private def proxy_auth? : Bool - @request.p_user && @request.p_pass ? true : false - end - - private def includes_authorization_header? - @request.headers.includes_word?("Authorization", "Basic") || - @request.headers.includes_word?("Authorization", "Digest") - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr b/samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr deleted file mode 100644 index 5f1aba4a6c84..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/exceptions.cr +++ /dev/null @@ -1,125 +0,0 @@ -require "./response" - -module Crest - # Hash of HTTP status code => message. - STATUSES = {100 => "Continue", - 101 => "Switching Protocols", - 102 => "Processing", # WebDAV - - 200 => "OK", - 201 => "Created", - 202 => "Accepted", - 203 => "Non-Authoritative Information", # http/1.1 - 204 => "No Content", - 205 => "Reset Content", - 206 => "Partial Content", - 207 => "Multi-Status", # WebDAV - 208 => "Already Reported", # RFC5842 - 226 => "IM Used", # RFC3229 - - 300 => "Multiple Choices", - 301 => "Moved Permanently", - 302 => "Found", - 303 => "See Other", # http/1.1 - 304 => "Not Modified", - 305 => "Use Proxy", # http/1.1 - 306 => "Switch Proxy", # no longer used - 307 => "Temporary Redirect", # http/1.1 - 308 => "Permanent Redirect", # RFC7538 - - 400 => "Bad Request", - 401 => "Unauthorized", - 402 => "Payment Required", - 403 => "Forbidden", - 404 => "Not Found", - 405 => "Method Not Allowed", - 406 => "Not Acceptable", - 407 => "Proxy Authentication Required", - 408 => "Request Timeout", - 409 => "Conflict", - 410 => "Gone", - 411 => "Length Required", - 412 => "Precondition Failed", - 413 => "Payload Too Large", # RFC7231 (renamed, see below) - 414 => "URI Too Long", # RFC7231 (renamed, see below) - 415 => "Unsupported Media Type", - 416 => "Range Not Satisfiable", # RFC7233 (renamed, see below) - 417 => "Expectation Failed", - 418 => "I\"m A Teapot", # RFC2324 - 421 => "Too Many Connections From This IP", - 422 => "Unprocessable Entity", # WebDAV - 423 => "Locked", # WebDAV - 424 => "Failed Dependency", # WebDAV - 425 => "Unordered Collection", # WebDAV - 426 => "Upgrade Required", - 428 => "Precondition Required", # RFC6585 - 429 => "Too Many Requests", # RFC6585 - 431 => "Request Header Fields Too Large", # RFC6585 - 449 => "Retry With", # Microsoft - 450 => "Blocked By Windows Parental Controls", # Microsoft - - 500 => "Internal Server Error", - 501 => "Not Implemented", - 502 => "Bad Gateway", - 503 => "Service Unavailable", - 504 => "Gateway Timeout", - 505 => "HTTP Version Not Supported", - 506 => "Variant Also Negotiates", - 507 => "Insufficient Storage", # WebDAV - 508 => "Loop Detected", # RFC5842 - 509 => "Bandwidth Limit Exceeded", # Apache - 510 => "Not Extended", - 511 => "Network Authentication Required", # RFC6585 - } - - # This is the base `Crest` exception class. Rescue it if you want to - # catch any exception that your request might raise - # You can see anything about the response via `e.response`. - # For example, the entire result body (which is - # probably an HTML error page) is `e.response.body`. - # - # Hash of HTTP status `code => message`. - # - # * **1xx**: Informational - Request received, continuing process - # * **2xx**: Success - The action was successfully received, understood, and - # accepted - # * **3xx**: Redirection - Further action must be taken in order to complete the - # request - # * **4xx**: Client Error - The request contains bad syntax or cannot be fulfilled - # * **5xx**: Server Error - The server failed to fulfill an apparently valid - # request - # - # See [HTTP Status Code Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) - # for more Information. - class RequestFailed < Exception - getter response - - @response : Crest::Response - - def self.subclass_by_status_code(status_code) - EXCEPTIONS_MAP.fetch(status_code, self) - end - - def initialize(response) - @response = response - end - - def http_code - @response.status_code.to_i - end - - def message - "HTTP status code #{http_code}: #{STATUSES[http_code]}" - end - end - - EXCEPTIONS_MAP = {} of Int32 => Crest::RequestFailed.class - - {% for code, status in STATUSES %} - # :nodoc: - class {{status.gsub(/\W/, "").id}} < RequestFailed - end - - EXCEPTIONS_MAP[{{code.id}}] = Crest::{{status.gsub(/\W/, "").id}} - {% end %} -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/form.cr b/samples/client/petstore/crystal/lib/crest/src/crest/form.cr deleted file mode 100644 index e6898e4f3cbe..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/form.cr +++ /dev/null @@ -1,17 +0,0 @@ -module Crest - abstract class Form(T) - @form_data : String = "" - @content_type : String = "" - - getter params, form_data, content_type - - def self.generate(params : Hash) - new(params).generate - end - - def initialize(@params : T) - end - - abstract def generate - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr b/samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr deleted file mode 100644 index 201968affb7a..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/forms/data_form.cr +++ /dev/null @@ -1,43 +0,0 @@ -require "../form" - -module Crest - # This class lets `crest` emulate a filled-in form - # in which a user has pressed the submit button. - # This causes `crest` to POST data using the - # "Content-Type" `multipart/form-data according` to RFC 2388. - # This enables uploading of binary files etc. - class DataForm(T) < Crest::Form(T) - def generate - content_type_ch = Channel(String).new(1) - io = IO::Memory.new - - HTTP::FormData.build(io) do |formdata| - content_type_ch.send(formdata.content_type) - - # Creates an `HTTP::FormData` instance from the key-value - # pairs of the given `params`. - parsed_params.each do |name, value| - add_field(formdata, name.to_s, value) - end - end - - @form_data = io.to_s - @content_type = content_type_ch.receive - - self - end - - def parsed_params - Crest::ParamsEncoder.flatten_params(@params) - end - - private def add_field(formdata : HTTP::FormData::Builder, name : String | Symbol, value : TextValue) - formdata.field(name.to_s, value.to_s) - end - - private def add_field(formdata : HTTP::FormData::Builder, name : String | Symbol, value : File) - metadata = HTTP::FormData::FileMetadata.new(filename: value.path) - formdata.file(name.to_s, value, metadata) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr b/samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr deleted file mode 100644 index 335b0597d9d3..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/forms/urlencoded_form.cr +++ /dev/null @@ -1,21 +0,0 @@ -require "../form" - -module Crest - # This class lets `crest` emulate a filled-in form - # in which a user has pressed the submit button. - # This causes `crest` to POST data using the - # "Content-Type" `application/x-www-form-urlencoded`. - class UrlencodedForm(T) < Crest::Form(T) - @content_type : String = "application/x-www-form-urlencoded" - - def generate - @form_data = parsed_params - - self - end - - def parsed_params - Crest::ParamsEncoder.encode(@params) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/logger.cr b/samples/client/petstore/crystal/lib/crest/src/crest/logger.cr deleted file mode 100644 index c69617f06e4a..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/logger.cr +++ /dev/null @@ -1,53 +0,0 @@ -require "log" - -module Crest - abstract class Logger - def self.new(filename : String) - new(File.open(filename, "w")) - end - - forward_missing_to @logger - - def initialize(@io : IO = STDOUT) - backend = Log::IOBackend.new - backend.io = @io - - @logger = Log.new("crest", backend, Log::Severity::Info) - @logger.backend.as(Log::IOBackend).formatter = default_formatter - - @filters = [] of Array(String | Regex) - end - - abstract def request(request : Crest::Request) : Nil - abstract def response(response : Crest::Response) : Nil - - def default_formatter : Log::Formatter - Log::Formatter.new do |entry, io| - io << entry.source - io << " | " << entry.timestamp.to_s("%F %T") - io << " " << entry.message - end - end - - def info(message : String) - @logger.info { apply_filters(message) } - end - - def filter(patern : String | Regex, replacement : String) - @filters.push([patern, replacement]) - end - - private def apply_filters(output : String) : String - @filters.each do |f| - patern = f[0] - replacement = f[1] - - output = output.gsub(patern, replacement) - end - - output - end - end -end - -require "./loggers/*" diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr b/samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr deleted file mode 100644 index 8e0ae14c1b74..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/loggers/common_logger.cr +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2017 icyleaf -# Licensed under The MIT License (MIT) -# http://opensource.org/licenses/MIT - -require "colorize" -require "../logger" - -module Crest - class CommonLogger < Logger - def request(request : Crest::Request) : Nil - message = String.build do |io| - io << "| " << colorful_method(request.method) - io << " | " << request.url - io << " | " << request.form_data.to_s.inspect unless request.form_data.nil? - end.to_s - - info(message) - - message - end - - def response(response : Crest::Response) : Nil - message = String.build do |io| - io << "| " << colorful_status_code(response.status_code) - io << " | " << response.url - io << " | " << response.body.inspect - end.to_s - - info(message) - - message - end - - private def colorful_method(method) - fore, back = - case method - when "GET" - {:white, :blue} - when "POST" - {:white, :cyan} - when "PUT" - {:white, :yellow} - when "DELETE" - {:white, :red} - when "PATCH" - {:white, :green} - when "HEAD" - {:white, :magenta} - else - {:dark_gray, :white} - end - - colorful((" %-7s" % method), fore, back) - end - - private def colorful_status_code(status_code) - fore, back = - case status_code - when 300..399 - {:dark_gray, :white} - when 400..499 - {:white, :yellow} - when 500..599 - {:white, :red} - else - {:white, :green} - end - - colorful((" %-7s" % status_code), fore, back) - end - - private def colorful(message, fore, back) - Colorize.enabled = !@io.is_a?(File) - message.colorize.fore(fore).back(back) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr b/samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr deleted file mode 100644 index b4eaa4042989..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/params_encoder.cr +++ /dev/null @@ -1,130 +0,0 @@ -module Crest - module ParamsEncoder - extend self - - alias Type = Nil | String | Array(Type) | Hash(String, Type) - - SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/ - ARRAY_REGEX = /[\[\]]+\Z/ - - # Converts the given params into a URI querystring. Keys and values - # will converted to strings and appropriately escaped for the URI. - # - # ``` - # Crest::ParamsEncoder.encode({"a" => ["one", "two", "three"], "b" => true, "c" => "C", "d" => 1}) - # # => 'a[]=one&a[]=two&a[]=three&b=true&c=C&d=1' - # ``` - def encode(params : Hash) : String - HTTP::Params.build do |form| - flatten_params(params).each do |name, value| - form.add(name.to_s, value.to_s) - end - end - end - - # Converts the given URI querystring into a hash. - # - # ``` - # Crest::ParamsEncoder.decode("a[]=one&a[]=two&a[]=three&b=true&c=C&d=1") - # # => {"a" => ["one", "two", "three"], "b" => "true", "c" => "C", "d" => "1"} - # ``` - def decode(query : String) : Hash - params = {} of String => Type - - query.split("&").each do |pair| - key, value = pair.split("=", 2) - key = URI.decode(key) - value = URI.decode(value) - - decode_pair(params, key, value) - end - - params - end - - private def decode_pair(context, key : String, value : String) - subkeys = key.scan(SUBKEYS_REGEX) - - subkeys.each_with_index do |subkey, i| - is_array = false - is_last_subkey = (i == subkeys.size - 1) - subkey = subkey[0] - - if match = subkey.match(ARRAY_REGEX) - is_array = true - subkey = match.pre_match - end - - context = prepare_context(context, subkey, is_array, is_last_subkey) - add_to_context(context, value, subkey) if is_last_subkey - end - end - - private def prepare_context(context, subkey : String, is_array : Bool, is_last_subkey : Bool) - if !is_last_subkey || is_array - context = new_context(context, subkey, is_array) - end - - context - end - - private def new_context(context, subkey : String, is_array : Bool) - value_type = is_array ? Array(Type) : Hash(String, Type) - - if context.is_a?(Hash) - context[subkey] ||= value_type.new - end - end - - private def add_to_context(context, value : String, subkey : String) - value = value.empty? ? nil : value - - if context.is_a?(Hash) - context[subkey] = value - elsif context.is_a?(Array) - context << value - end - end - - # Transform deeply nested params containers into a flat hash of `key => value`. - # - # ``` - # Crest::ParamsEncoder.flatten_params({:key1 => {:key2 => "123"}}) - # # => [{"key1[key2]", "123"}] - # ``` - def flatten_params(object : Hash, parent_key = nil) - object.reduce([] of Tuple(String, TextValue | File)) do |memo, item| - k, v = item - - processed_key = parent_key ? "#{parent_key}[#{k}]" : k.to_s - - case v - when Hash, Array - memo += flatten_params(v, processed_key) - else - memo << {processed_key, v} - end - - memo - end - end - - # Transform deeply nested params containers into a flat hash of `key => value`. - # - # ``` - # Crest::ParamsEncoder.flatten_params({:key1 => {:arr => ["1", "2", "3"]}}) - # # => [{"key1[arr][]", "1"}, {"key1[arr][]", "2"}, {"key1[arr][]", "3"}] - # ``` - def flatten_params(object : Array, parent_key = nil) - object.reduce([] of Tuple(String, TextValue | File)) do |memo, item| - k = :"" - v = item - - processed_key = parent_key ? "#{parent_key}[#{k}]" : k.to_s - memo << {processed_key, v} - - memo - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr b/samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr deleted file mode 100644 index 9452b818c774..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/redirector.cr +++ /dev/null @@ -1,89 +0,0 @@ -module Crest - class Redirector - def initialize(@response : Crest::Response, @request : Crest::Request) - end - - def follow : Crest::Response - case @response - when .success? - @response - when .redirect? - check_max_redirects - - @request.max_redirects > 0 ? follow_redirection : @response - else - raise_exception! if @request.handle_errors - @response - end - end - - def follow(&block : Crest::Response ->) - case @response - when .success? - @response - when .redirect? - check_max_redirects - - @request.max_redirects > 0 ? follow_redirection(&block) : @response - else - raise_exception! if @request.handle_errors - @response - end - end - - private def check_max_redirects - raise_exception! if @request.max_redirects <= 0 && @request.handle_errors - end - - # Follow a redirection response by making a new HTTP request to the - # redirection target. - private def follow_redirection : Crest::Response - url = extract_url_from_headers - - new_request = prepare_new_request(url) - new_request.redirection_history = @response.history + [@response] - new_request.execute - end - - private def follow_redirection(&block : Crest::Response ->) - url = extract_url_from_headers - - new_request = prepare_new_request(url) - new_request.redirection_history = @response.history + [@response] - new_request.execute(&block) - end - - private def extract_url_from_headers - location_url = @response.http_client_res.headers["location"] - location_uri = URI.parse(location_url) - - return location_url if location_uri.absolute? - - uri = URI.parse(@request.url) - port = uri.port ? ":#{uri.port}" : "" - - "#{uri.scheme}://#{uri.host}#{port}#{location_url}" - end - - private def prepare_new_request(url) - Request.new( - method: :get, - url: url, - headers: @request.headers.to_h, - max_redirects: @request.max_redirects - 1, - cookies: @response.cookies, - logging: @request.logging, - logger: @request.logger, - handle_errors: @request.handle_errors, - p_addr: @request.p_addr, - p_port: @request.p_port, - p_user: @request.p_user, - p_pass: @request.p_pass - ) - end - - private def raise_exception! - raise RequestFailed.subclass_by_status_code(@response.status_code).new(@response) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/request.cr b/samples/client/petstore/crystal/lib/crest/src/crest/request.cr deleted file mode 100644 index fc9df3c26a17..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/request.cr +++ /dev/null @@ -1,327 +0,0 @@ -require "../crest" - -module Crest - # A class that used to make the requests - # The result of a `Crest::Request` is a `Crest::Response` object. - # - # Simple example: - # - # ```crystal - # request = Crest::Request.new(method: :post, url: "http://httpbin.org/post", form: {:age => 27}, params: {:name => "Kurt"}) - # request.execute - # - # Crest::Request.execute(method: :post, url: "http://httpbin.org/post", form: {:age => 27}.to_json) - # - # Crest::Request.post(url: http://httpbin.org/post", form: {:age => 27}.to_json) - # ``` - # - # Block style: - # - # ```crystal - # request = Crest::Request.new(:get, http://httpbin.org/get") do |request| - # request.headers.add("foo", "bar") - # request.user = "username" - # request.password = "password" - # end - # - # response = request.execute - # ``` - # - # Mandatory parameters: - # * `method` - # * `url` - # - # Optional parameters: - # * `headers` a hash containing the request headers - # * `cookies` a hash containing the request cookies - # * `form` a hash containing form params (or a raw string) - # * `params` a hash that represent query-string separated from the preceding part by a question mark (?) - # a sequence of attribute–value pairs separated by a delimiter (&). - # * `auth` access authentication method `basic` or `digest` (default to `basic`) - # * `user` and `password` for authentication - # * `tls` configuring TLS settings - # * `p_addr`, `p_port`, `p_user`, `p_pass` for proxy - # * `max_redirects` maximum number of redirections (default to `10`) - # * `logging` enable logging (default to `false`) - # * `logger` set logger (default to `Crest::CommonLogger`) - # * `handle_errors` error handling (default to `true`) - # * `http_client` instance of `HTTP::Client` - class Request - @method : String - @url : String - @tls : OpenSSL::SSL::Context::Client? - @http_client : HTTP::Client - @http_request : HTTP::Request - @headers : HTTP::Headers - @cookies : HTTP::Cookies - @form_data : String? - @max_redirects : Int32 - @auth : String - @user : String? - @password : String? - @proxy : HTTP::Proxy::Client? - @p_addr : String? - @p_port : Int32? - @p_user : String? - @p_pass : String? - @logger : Crest::Logger - @logging : Bool - @handle_errors : Bool - - getter http_client, http_request, method, url, form_data, headers, cookies, - max_redirects, logging, logger, handle_errors, auth, - proxy, p_addr, p_port, p_user, p_pass - - property redirection_history, user, password - - delegate host, port, tls?, to: @http_client - - def self.execute(method, url, **args) : Crest::Response - request = new(method, url, **args) - request.execute - end - - def self.execute(method, url, **args, &block : Crest::Response ->) : Nil - request = new(method, url, **args) - request.execute(&block) - end - - def initialize( - method : Symbol, - url : String, - *, - headers = {} of String => String, - cookies = {} of String => String, - form = {} of String => String, - params = {} of String => String, - max_redirects = 10, - **options - ) - @method = parse_verb(method) - @url = url - @headers = HTTP::Headers.new - @cookies = HTTP::Cookies.new - @redirection_history = [] of Crest::Response - - set_headers!(headers) - set_cookies!(cookies) unless cookies.empty? - generate_form_data!(form) if form - - unless params.empty? - @url = url + process_url_params(params) - end - - @max_redirects = max_redirects - - @tls = options.fetch(:tls, nil).as(OpenSSL::SSL::Context::Client | Nil) - @http_client = options.fetch(:http_client, new_http_client).as(HTTP::Client) - @auth = options.fetch(:auth, "basic").as(String) - @user = options.fetch(:user, nil).as(String | Nil) - @password = options.fetch(:password, nil).as(String | Nil) - @p_addr = options.fetch(:p_addr, nil).as(String | Nil) - @p_port = options.fetch(:p_port, nil).as(Int32 | Nil) - @p_user = options.fetch(:p_user, nil).as(String | Nil) - @p_pass = options.fetch(:p_pass, nil).as(String | Nil) - @logger = options.fetch(:logger, Crest::CommonLogger.new).as(Crest::Logger) - @logging = options.fetch(:logging, false).as(Bool) - @handle_errors = options.fetch(:handle_errors, true).as(Bool) - - @http_request = HTTP::Request.new(@method, @url, body: @form_data, headers: @headers) - - set_proxy!(@p_addr, @p_port, @p_user, @p_pass) - - yield self - end - - # When block is not given. - def initialize(method : Symbol, url : String, **args) - initialize(method, url, **args) { } - end - - {% for method in Crest::HTTP_METHODS %} - # Execute a {{method.id.upcase}} request and and yields the `Crest::Response` to the block. - # - # ```crystal - # Crest::Request.{{method.id}}("http://httpbin.org/{{method.id}}") do |resp| - # while line = resp.body_io.gets - # puts line - # end - # end - # ``` - def self.{{method.id}}(url : String, **args, &block : Crest::Response ->) : Nil - request = Request.new(:{{method.id}}, url, **args) - - response = request.execute(&block) - end - - # Execute a {{method.id.upcase}} request and returns a `Crest::Response`. - # - # ```crystal - # Crest::Request.{{method.id}}("http://httpbin.org/{{method.id}}") - # ``` - def self.{{method.id}}(url : String, **args) : Crest::Response - request = Request.new(:{{method.id}}, url, **args) - - request.execute - end - {% end %} - - # Execute HTTP request - def execute : Crest::Response - @http_client.set_proxy(@proxy) - authenticate! - @logger.request(self) if @logging - - @http_request = new_http_request(@method, @url, @headers, @form_data) - - http_response = @http_client.exec(@http_request) - - process_result(http_response) - end - - # Execute streaming HTTP request - def execute(&block : Crest::Response ->) : Nil - @http_client.set_proxy(@proxy) - authenticate! - @logger.request(self) if @logging - - @http_request = new_http_request(@method, @url, @headers, @form_data) - - @http_client.exec(@http_request) do |http_response| - response = process_result(http_response, &block) - - if response - yield response - end - end - end - - private def process_result(http_client_res) : Crest::Response - response = Response.new(http_client_res, self) - logger.response(response) if logging - response.return! - end - - private def process_result(http_client_res, &block : Crest::Response ->) - response = Response.new(http_client_res, self) - logger.response(response) if logging - response.return!(&block) - end - - # Convert `Request` object to cURL command - def to_curl - Crest::Curlify.to_curl(self) - end - - private def new_http_client : HTTP::Client - uri = URI.parse(@url) - HTTP::Client.new(uri, tls: @tls) - end - - private def new_http_request(method, path, headers, body) : HTTP::Request - HTTP::Request.new(method, path, headers, body).tap do |request| - request.headers["Host"] ||= host_header - request.headers["User-Agent"] ||= "Crest/#{Crest::VERSION} (Crystal/#{Crystal::VERSION})" - end - end - - private def host_header - if (tls? && port != 443) || (!tls? && port != 80) - "#{host}:#{port}" - else - host - end - end - - private def parse_verb(method : String | Symbol) : String - method.to_s.upcase - end - - private def multipart?(form : Hash) : Bool - form.any? { |_, v| v.is_a?(File) } - end - - private def generate_form_data!(form : Hash) : String? - return if form.empty? - - form_class = multipart?(form) ? Crest::DataForm : Crest::UrlencodedForm - form = form_class.generate(form) - - @form_data = form.form_data - content_type = form.content_type - - @headers["Content-Type"] = content_type - - @form_data - end - - private def generate_form_data!(form : String) : String? - @form_data = form - end - - private def set_headers!(params) : HTTP::Headers - params.each do |key, value| - @headers.add(key, value) - end - - @headers - end - - # Adds "Cookie" headers for the cookies in this collection to the @header instance and returns it - private def set_cookies!(cookies) : HTTP::Headers - cookies.each do |k, v| - @cookies << HTTP::Cookie.new(k.to_s, v.to_s) - end - - @cookies.add_request_headers(@headers) - end - - protected def authenticate! - return unless @user && @password - - if @auth == "basic" - basic_auth! - else - digest_auth! - end - end - - private def basic_auth! - auth = "Basic #{Base64.strict_encode("#{@user}:#{@password}")}" - - @headers.add("Authorization", auth) - end - - private def digest_auth! - uri = URI.parse(@url) - uri.user = @user - uri.password = @password - - response = @http_client.exec(@method, uri.full_path) - - www_authenticate = response.headers["WWW-Authenticate"] - - digest_auth = HTTP::Client::DigestAuth.new - auth = digest_auth.auth_header(uri, www_authenticate, @method) - - @headers.add("Authorization", auth) - end - - private def set_proxy!(p_addr, p_port, p_user, p_pass) - return unless p_addr && p_port - - @proxy = HTTP::Proxy::Client.new(p_addr, p_port, username: p_user, password: p_pass) - end - - # Extract the query parameters and append them to the url - private def process_url_params(url_params) : String - query_string = Crest::ParamsEncoder.encode(url_params) - - if url.includes?("?") - "&" + query_string - else - "?" + query_string - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/resource.cr b/samples/client/petstore/crystal/lib/crest/src/crest/resource.cr deleted file mode 100644 index d061244b8d17..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/resource.cr +++ /dev/null @@ -1,176 +0,0 @@ -require "../crest" - -module Crest - # A class that can be instantiated for access to a RESTful resource, - # including authentication, proxy and logging. - # - # Simple example: - # - # ```crystal - # resource = Crest::Resource.new("https://httpbin.org/get") - # response = resource.get - # ``` - # - # Block style: - # - # ```crystal - # resource = Crest::Resource.new("http://httpbin.org") do |res| - # res.headers.merge!({"foo" => "bar"}) - # end - # - # response = resource["/headers"].get - # ``` - # - # With HTTP basic authentication: - # - # ```crystal - # resource = Crest::Resource.new("https://httpbin.org/get", user: "user", password: "password") - # ``` - # - # Use the `[]` syntax to allocate subresources: - # - # ```crystal - # resource = Crest::Resource.new("https://httpbin.org") - # resource["/get"].get - # ``` - # - # You can pass advanced parameters like default `params` or `headers`: - # - # ```crystal - # resource = Crest::Resource.new( - # "https://httpbin.org", - # params: {"key" => "key"}, - # headers: {"Content-Type" => "application/json"} - # ) - # response = response["/post"].post( - # form: {:height => 100, "width" => "100"}, - # params: {:secret => "secret"} - # ) - # ``` - # If you want to stream the data from the response you can pass a block: - # - # ```crystal - # resource = Crest::Resource.new("http://httpbin.org") - # resource["/stream/5"].get do |response| - # while line = response.body_io.gets - # puts line - # end - # end - # ``` - class Resource - getter http_client, url, user, password, headers, params, - logging, logger, handle_errors, p_addr, p_port, p_user, p_pass - - def initialize( - @url : String, - *, - @headers = {} of String => String, - @params : Params = {} of String => String, - **options - ) - @base_url = @url - @tls = options.fetch(:tls, nil).as(OpenSSL::SSL::Context::Client | Nil) - @http_client = options.fetch(:http_client, new_http_client).as(HTTP::Client) - @user = options.fetch(:user, nil).as(String | Nil) - @password = options.fetch(:password, nil).as(String | Nil) - @p_addr = options.fetch(:p_addr, nil).as(String | Nil) - @p_port = options.fetch(:p_port, nil).as(Int32 | Nil) - @p_user = options.fetch(:p_user, nil).as(String | Nil) - @p_pass = options.fetch(:p_pass, nil).as(String | Nil) - @logger = options.fetch(:logger, Crest::CommonLogger.new).as(Crest::Logger) - @logging = options.fetch(:logging, false).as(Bool) - @handle_errors = options.fetch(:handle_errors, true).as(Bool) - - yield self - end - - # When block is not given. - def initialize(@url : String, **args) - initialize(@url, **args) { } - end - - {% for method in Crest::HTTP_METHODS %} - # Execute a {{method.id.upcase}} request and returns a `Crest::Response`. - def {{method.id}}( - suburl : String? = nil, - *, - form = {} of String => String, - headers = {} of String => String, - params = {} of String => String - ) : Crest::Response - @url = concat_urls(@base_url, suburl) if suburl - @headers = @headers.merge(headers) - @params = merge_params(params) - - execute_request(:{{method.id}}, form) - end - - # Execute a {{method.id.upcase}} request and and yields the `Crest::Response` to the block. - def {{method.id}}( - suburl : String? = nil, - *, - form = {} of String => String, - headers = {} of String => String, - params = {} of String => String, - &block : Crest::Response -> - ) : Nil - @url = concat_urls(@base_url, suburl) if suburl - @headers = @headers.merge(headers) - @params = merge_params(params) - - execute_request(:{{method.id}}, form, &block) - end - {% end %} - - def [](suburl) - @url = concat_urls(@base_url, suburl) - - self - end - - private def new_http_client : HTTP::Client - uri = URI.parse(@url) - HTTP::Client.new(uri, tls: @tls) - end - - private def execute_request(method : Symbol, form = {} of String => String) - Request.execute(**request_params(method, form)) - end - - private def execute_request(method : Symbol, form = {} of String => String, &block : Crest::Response ->) - Request.execute(**request_params(method, form), &block) - end - - private def request_params(method : Symbol, form = {} of String => String) - { - method: method, - form: form, - url: @url, - params: @params, - headers: @headers, - tls: @tls, - user: @user, - password: @password, - p_addr: @p_addr, - p_port: @p_port, - p_user: @p_user, - p_pass: @p_pass, - logging: @logging, - logger: @logger, - handle_errors: @handle_errors, - http_client: @http_client, - } - end - - private def merge_params(other = {} of String => String) - @params.try do |params| - other = params.merge(other) - end - other - end - - private def concat_urls(url : String, suburl : String) : String - File.join(url, suburl) - end - end -end diff --git a/samples/client/petstore/crystal/lib/crest/src/crest/response.cr b/samples/client/petstore/crystal/lib/crest/src/crest/response.cr deleted file mode 100644 index 342e1ea0a90c..000000000000 --- a/samples/client/petstore/crystal/lib/crest/src/crest/response.cr +++ /dev/null @@ -1,118 +0,0 @@ -require "http" -require "../crest" -require "../crest/redirector" - -module Crest - # Response objects have several useful methods: - # - # * `body`: The response body as a `String` - # * `body_io`: The response body as a `IO` - # * `status`: The response status as a `HTTP::Status` - # * `status_code`: The HTTP response code - # * `headers`: A hash of HTTP response headers - # * `cookies`: A hash of HTTP cookies set by the server - # * `request`: The `Crest::Request` object used to make the request - # * `http_client_res`: The `HTTP::Client::Response` object - # * `history`: A list of each response received in a redirection chain - class Response - getter http_client_res, request - - delegate body, to: http_client_res - delegate body_io, to: http_client_res - delegate status, to: http_client_res - delegate status_code, to: http_client_res - delegate informational?, success?, redirection?, client_error?, server_error?, to: status - delegate to_curl, to: request - - def initialize(@http_client_res : HTTP::Client::Response, @request : Crest::Request) - end - - def return! : Crest::Response - redirector = Redirector.new(self, request) - redirector.follow - end - - def return!(&block : Crest::Response ->) - redirector = Redirector.new(self, request) - redirector.follow(&block) - end - - def url : String - @request.url - end - - def headers - headers = @request.headers.dup.merge!(http_client_res.headers) - - normalize_headers(headers) - end - - def cookies - request_cookies.merge(response_cookies) - end - - def history : Array - @request.redirection_history - end - - # Extracts filename from Content-Disposition header - def filename : String? - filename_regex = /filename\*?=['"]?(?:UTF-\d['"]*)?([^;\r\n"']*)['"]?;?/xi - - if match_data = headers.fetch("Content-Disposition", "").as(String).match(filename_regex) - return match_data[1] - end - end - - def invalid? - status_code < 100 || status_code >= 600 - end - - def redirect? - [301, 302, 303, 307, 308].includes?(status_code) - end - - def to_s(io : IO) : Nil - io.write_utf8(body.to_slice) - end - - def inspect - "" - end - - private def raise_exception! - raise RequestFailed.subclass_by_status_code(status_code).new(self) - end - - private def request_cookies - cookies_to_h(@request.cookies) - end - - private def response_cookies - cookies_to_h(@http_client_res.cookies) - end - - private def normalize_headers(headers : HTTP::Headers) - headers.map do |header| - key, value = header - - if value.is_a?(Array) && value.size == 1 - value = value.first - end - {key, value} - end.to_h - end - - private def cookies_to_h(cookies : HTTP::Cookies) - cookies.to_h.map { |e| [e[1].name.to_s, URI.encode(e[1].value)] }.to_h - end - - private def body_truncated(size) - if body.size > size - body[0..size] + "..." - else - body - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/exception_page/.editorconfig b/samples/client/petstore/crystal/lib/exception_page/.editorconfig deleted file mode 100644 index 163eb75c8525..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*.cr] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true diff --git a/samples/client/petstore/crystal/lib/exception_page/.gitignore b/samples/client/petstore/crystal/lib/exception_page/.gitignore deleted file mode 100644 index e29dae78f635..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/docs/ -/lib/ -/bin/ -/.shards/ -*.dwarf - -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock diff --git a/samples/client/petstore/crystal/lib/exception_page/.travis.yml b/samples/client/petstore/crystal/lib/exception_page/.travis.yml deleted file mode 100644 index 26b28c99eae6..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: crystal - -crystal: - - latest - - nightly - -matrix: - allow_failures: - - crystal: nightly - -addons: - chrome: stable - -services: - - xvfb - -before_install: - # Setup chromedriver for LuckyFlow - - npm -g install chromedriver --detect_chromedriver_version - - export DISPLAY=:99.0 - -install: - - shards install - -script: - - crystal spec - - crystal tool format --check - - bin/ameba diff --git a/samples/client/petstore/crystal/lib/exception_page/LICENSE b/samples/client/petstore/crystal/lib/exception_page/LICENSE deleted file mode 100644 index 41e891668f45..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 Paul Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/exception_page/README.md b/samples/client/petstore/crystal/lib/exception_page/README.md deleted file mode 100644 index 07466fd65ddf..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Exception Page - -A library for displaying exceptional exception pages for easier debugging. - -![screen shot 2018-06-29 at 2 39 18 pm](https://user-images.githubusercontent.com/22394/42109073-6e767d06-7baa-11e8-9ec9-0a2afce605be.png) - -## Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - exception_page: - github: crystal-loot/exception_page -``` - -## Usage - -Require the shard: - -```crystal -require "exception_page" -``` - -Create an exception page: - -```crystal -class MyApp::ExceptionPage < ExceptionPage - def styles : Styles - ExceptionPage::Styles.new( - accent: "purple", # Choose the HTML color value. Can be hex - ) - end -end -``` - -Render the HTML when an exception occurs: - -```crystal -class MyErrorHandler - include HTTP::Handler - - def call_next(context) - begin - # Normally you'd call some code to handle the request - # We're hard-coding an error here to show you how to use the lib. - raise SomeError.new("Something went wrong") - rescue e - context.response.status_code = 500 - context.response.print MyApp::ExceptionPage.for_runtime_exception(context, e).to_s - end - end -``` - -## Customizing the page - -```crystal -class MyApp::ExceptionPage < ExceptionPage - def styles : Styles - ExceptionPage::Styles.new( - accent: "purple", # Required - highlight: "gray", # Optional - flash_highlight: "red", # Optional - logo_uri: "base64_encoded_data_uri" # Optional. Defaults to Crystal logo. Generate a logo here: https://dopiaza.org/tools/datauri/index.php - ) - end - - # Optional. If provided, clicking the logo will open this page - def project_url - "https://myproject.com" - end - - # Optional - def stack_trace_heading_html - <<-HTML - Say hi - HTML - end - - # Optional - def extra_javascript - <<-JAVASCRIPT - window.sayHi = function() { - alert("Say Hi!"); - } - JAVASCRIPT - end -end -``` - -## Development - -TODO: Write development instructions here - -## Contributing - -1. Fork it () -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create a new Pull Request - -## Contributors - -- [@paulcsmith](https://github.com/paulcsmith) Paul Smith -- [@faustinoaq](https://github.com/faustinoaq) Faustino Aigular - Wrote the initial [Amber PR adding exception pages](https://github.com/amberframework/amber/pull/864) - -## Special Thanks - -This exception page is heavily based on the [Phoenix error page](https://github.com/phoenixframework/phoenix/issues/1776) -by [@rstacruz](https://github.com/rstacruz). Thanks to the Phoenix team and @rstacruz! diff --git a/samples/client/petstore/crystal/lib/exception_page/lib b/samples/client/petstore/crystal/lib/exception_page/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/exception_page/shard.yml b/samples/client/petstore/crystal/lib/exception_page/shard.yml deleted file mode 100644 index ca27bb1df867..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/shard.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: exception_page -version: 0.1.4 - -authors: - - Paul Smith - - Faustino Aguilar - -development_dependencies: - lucky_flow: - github: luckyframework/lucky_flow - version: ~> 0.6.1 - ameba: - github: crystal-ameba/ameba - version: ~> 0.11.0 - -crystal: 0.33.0 - -license: MIT diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr b/samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr deleted file mode 100644 index f2867217019a..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/spec/exception_page_spec.cr +++ /dev/null @@ -1,34 +0,0 @@ -require "./spec_helper" - -describe ExceptionPage do - it "allows debugging the exception page" do - flow = ErrorDebuggingFlow.new - - flow.view_error_page - flow.should_have_information_for_debugging - flow.show_all_frames - flow.should_be_able_to_view_other_frames - end -end - -class ErrorDebuggingFlow < LuckyFlow - def view_error_page - visit "/" - end - - def should_have_information_for_debugging - el("@exception-title", text: "Something went very wrong").should be_on_page - el("@code-frames", text: "test_server.cr").should be_on_page - click("@see-raw-error-message") - el("@raw-error-message").should be_on_page - end - - def show_all_frames - el("@show-all-frames").click - end - - def should_be_able_to_view_other_frames - el("@code-frame-file", "request_processor.cr").click - el("@code-frame-summary", text: "request_processor.cr").should be_on_page - end -end diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr b/samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr deleted file mode 100644 index 5a47be002142..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/spec/frame_spec.cr +++ /dev/null @@ -1,27 +0,0 @@ -require "./spec_helper" - -describe "Frame parsing" do - it "returns the correct label" do - frame = frame_for("from usr/crystal-lang/frame_spec.cr:6:7 in '->'") - frame.label.should eq("crystal") - - frame = frame_for("from usr/crystal/frame_spec.cr:6:7 in '->'") - frame.label.should eq("crystal") - - frame = frame_for("from lib/exception_page/spec/frame_spec.cr:6:7 in '->'") - frame.label.should eq("exception_page") - - frame = frame_for("from lib/exception_page/frame_spec.cr:6:7 in '->'") - frame.label.should eq("exception_page") - - frame = frame_for("from lib/frame_spec.cr:6:7 in '->'") - frame.label.should eq("app") - - frame = frame_for("from src/frame_spec.cr:6:7 in '->'") - frame.label.should eq("app") - end -end - -private def frame_for(backtrace_line) - ExceptionPage::FrameGenerator.generate_frames(backtrace_line).first -end diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr deleted file mode 100644 index b4dd080e00af..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/spec/spec_helper.cr +++ /dev/null @@ -1,26 +0,0 @@ -require "http" -require "lucky_flow" -require "../src/exception_page" -require "./support/*" - -include LuckyFlow::Expectations - -server = TestServer.new - -LuckyFlow.configure do |settings| - settings.base_uri = "http://#{server.addr}" - settings.stop_retrying_after = 40.milliseconds -end - -spawn do - server.listen -end - -at_exit do - LuckyFlow.shutdown - server.close -end - -Habitat.raise_if_missing_settings! - -require "spec" diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr b/samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr deleted file mode 100644 index 4b3b33c153b4..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/spec/support/app_exception_page.cr +++ /dev/null @@ -1,19 +0,0 @@ -class MyApp::ExceptionPage < ExceptionPage - def styles : Styles - Styles.new(accent: "purple") - end - - def stack_trace_heading_html - <<-HTML - Say hi - HTML - end - - def extra_javascript - <<-JAVASCRIPT - window.sayHi = function() { - alert("Say Hi!"); - } - JAVASCRIPT - end -end diff --git a/samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr b/samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr deleted file mode 100644 index 7eda8b3f1372..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/spec/support/test_server.cr +++ /dev/null @@ -1,29 +0,0 @@ -class TestServer - getter addr : Socket::IPAddress - - delegate :listen, :close, - to: @server - - def initialize(port : Int32? = nil) - @server = HTTP::Server.new do |context| - if context.request.resource == "/favicon.ico" - context.response.print "" - else - begin - raise CustomException.new("Something went very wrong") - rescue e : CustomException - context.response.content_type = "text/html" - context.response.print MyApp::ExceptionPage.for_runtime_exception(context, e).to_s - end - end - end - if port - @addr = @server.bind_tcp(port: port) - else - @addr = @server.bind_unused_port - end - end -end - -class CustomException < Exception -end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr deleted file mode 100644 index 6bbf5039e19a..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/src/exception_page.cr +++ /dev/null @@ -1,54 +0,0 @@ -abstract class ExceptionPage -end - -require "ecr" -require "./exception_page/*" - -# :nodoc: -abstract class ExceptionPage - @params : Hash(String, String) - @headers : Hash(String, Array(String)) - @session : Hash(String, HTTP::Cookie) - @method : String - @path : String - @message : String - @query : String - @frames = [] of Frame - @title : String - - abstract def styles : Styles - - # Add an optional link to your project - def project_url : String? - nil - end - - # Override this method to add extra HTML to the top of the stack trace heading - def stack_trace_heading_html - "" - end - - # Override this method to add extra javascript to the page - def extra_javascript - "" - end - - # :nodoc: - def initialize(context : HTTP::Server::Context, @message, @title, @frames) - @params = context.request.query_params.to_h - @headers = context.response.headers.to_h - @method = context.request.method - @path = context.request.path - @url = "#{context.request.host_with_port}#{context.request.path}" - @query = context.request.query_params.to_s - @session = context.response.cookies.to_h - end - - def self.for_runtime_exception(context : HTTP::Server::Context, ex : Exception) - title = "Error #{context.response.status_code}" - frames = FrameGenerator.generate_frames(ex.inspect_with_backtrace) - new(context, ex.message.to_s, title: title, frames: frames) - end - - ECR.def_to_s "#{__DIR__}/exception_page/exception_page.ecr" -end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr deleted file mode 100644 index 5b84e8e1358e..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/exception_page.ecr +++ /dev/null @@ -1,857 +0,0 @@ - -<%- -monospace_font = "menlo, consolas, monospace" --%> - - - - <% - details = @message.lines - headline = details.first? || "Error" - %> - - <%= @title %> at <%= @method %> <%= @path %> - <%= headline %> - - - - - -
    - <%- if project_url -%> - - <%- else %> - - <%- end %> -
    -
    - <%= @title %> - at <%= @method %> <%= @path %> -
    -

    <%= HTML.escape(headline).gsub("'", '\'').gsub(""", '"') %>

    -
    - - See raw message - -
    -                    <%- details.each do |detail| -%>
    -                        <%= HTML.escape(detail).gsub("'", '\'').gsub(""", '"') %>
    -                    <%- end -%>
    -                
    -
    -
    - <% if !@frames.empty? %> -
    -
    - <% @frames.each do |frame| %> -
    - - - <%- if !frame.snippets.empty? -%> -
    -                        <%- frame.snippets.each do |snippet| -%>
    -                          <%= snippet.line %><%= HTML.escape(snippet.code.rstrip).gsub("'", '\'').gsub(""", '"') %>
    -                        <%- end -%>
    -                      
    - <%- else -%> -
    No code available.
    - <%- end -%> - - <% if !frame.args.blank? %> -
    - - <%= frame.label %> - <%= frame.filename %> - -
    <%= HTML.escape(frame.args).gsub("'", '\'').gsub(""", '"') %>
    -
    - <% else %> -
    -
    - <%= frame.label %> - <%= frame.filename %> -
    -
    - <% end %> -
    - <% end %> -
    - -
    -
    - <%= stack_trace_heading_html %> - -
    - -
      - <% @frames.each do |frame| %> -
    • - -
    • - <% end %> -
    -
    -
    - <% end %> -
    - -
    - <% if @params && !@params.empty? %> -
    - Params - <% @params.each do |key, value| %> -
    -
    <%= HTML.escape(key) %>
    -
    <%= HTML.escape(value.inspect) %>
    -
    - <% end %> -
    - <% end %> - -
    - Request info - -
    -
    URI:
    -
    <%= HTML.escape(@url) %>
    -
    - -
    -
    Query string:
    -
    <%= HTML.escape(@query) %>
    -
    -
    - -
    - Headers - <% @headers.each do |key, value| %> -
    -
    <%= HTML.escape(key) %>
    -
    <%= HTML.escape(value.inspect) %>
    -
    - <% end %> -
    - - <% if (session = @session) && !session.empty? %> -
    - Session - <% session.each do |key, value| %> -
    -
    <%= HTML.escape(key) %>
    -
    <%= HTML.escape(value.inspect) %>
    -
    - <% end %> -
    - <% end %> -
    - - - - diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr deleted file mode 100644 index 693fbcdad9e2..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame.cr +++ /dev/null @@ -1,77 +0,0 @@ -# :nodoc: -struct ExceptionPage::Frame - property index : Int32, raw_frame : Regex::MatchData - - def initialize(@raw_frame, @index) - end - - def snippets : Array(Snippet) - snippets = [] of Snippet - if File.exists?(file) - lines = File.read_lines(file) - lines.each_with_index do |code, code_index| - if line_is_nearby?(code_index) - highlight = (code_index + 1 == line) ? true : false - snippets << Snippet.new( - line: code_index + 1, - code: code, - highlight: highlight - ) - end - end - end - snippets - end - - private def line_is_nearby?(code_index : Int32) - (code_index + 1) <= (line + 5) && (code_index + 1) >= (line - 5) - end - - def file : String - raw_frame[1] - end - - def filename : String - file.split('/').last - end - - def line : Int32 - raw_frame[2].to_i - end - - def args - "#{file}:#{line}#{column_with_surrounding_method_name}" - end - - private def column_with_surrounding_method_name - raw_frame[3] - end - - def label : String - case file - when .includes?("/crystal/"), .includes?("/crystal-lang/") - "crystal" - when /lib\/(?[^\/]+)\/.+/ - $~["name"] - else - "app" - end - end - - def context : String - if label == "app" - "app" - else - "all" - end - end - - struct Snippet - property line : Int32, - code : String, - highlight : Bool - - def initialize(@line, @code, @highlight) - end - end -end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr deleted file mode 100644 index c731b59da624..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/frame_generator.cr +++ /dev/null @@ -1,13 +0,0 @@ -# :nodoc: -class ExceptionPage::FrameGenerator - def self.generate_frames(message) - generated_frames = [] of Frame - if raw_frames = message.scan(/\s([^\s\:]+):(\d+)([^\n]+)/) - raw_frames.each_with_index do |frame, index| - generated_frames << Frame.new(raw_frame: frame, index: index) - end - end - - generated_frames - end -end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr deleted file mode 100644 index e2554f877acf..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/styles.cr +++ /dev/null @@ -1,18 +0,0 @@ -class ExceptionPage::Styles - getter accent : String, - highlight : String, - flash_highlight : String, - logo_uri : String? - - def initialize( - @accent, - @highlight = "#e5e5e5", - @flash_highlight = "#ffdc93", - @logo_uri = crystal_logo - ) - end - - private def crystal_logo - "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAxOTMuMiAyMDYuNyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTkzLjIgMjA2Ljc7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOm5vbmU7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwYXRoIGQ9Ik0xNjUuNCwxMjJsLTUwLDQ5LjljLTAuMiwwLjItMC41LDAuMy0wLjcsMC4ybC02OC4zLTE4LjNjLTAuMy0wLjEtMC41LTAuMy0wLjUtMC41TDI3LjUsODUuMWMtMC4xLTAuMywwLTAuNSwwLjItMC43DQoJCWw1MC00OS45YzAuMi0wLjIsMC41LTAuMywwLjctMC4ybDY4LjMsMTguM2MwLjMsMC4xLDAuNSwwLjMsMC41LDAuNWwxOC4zLDY4LjJDMTY1LjcsMTIxLjYsMTY1LjYsMTIxLjgsMTY1LjQsMTIyeiBNOTguNCw2Ny43DQoJCUwzMS4zLDg1LjZjLTAuMSwwLTAuMiwwLjItMC4xLDAuM2w0OS4xLDQ5YzAuMSwwLjEsMC4zLDAuMSwwLjMtMC4xbDE4LTY3Qzk4LjcsNjcuOCw5OC41LDY3LjYsOTguNCw2Ny43eiIvPg0KCTxnPg0KCQk8cmVjdCBjbGFzcz0ic3QwIiB3aWR0aD0iMTkzLjIiIGhlaWdodD0iMjA2LjciLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==" - end -end diff --git a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr b/samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr deleted file mode 100644 index 3f0cb7b97458..000000000000 --- a/samples/client/petstore/crystal/lib/exception_page/src/exception_page/version.cr +++ /dev/null @@ -1,3 +0,0 @@ -class ExceptionPage - VERSION = "0.1.4" -end diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig b/samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig deleted file mode 100644 index 163eb75c8525..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*.cr] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore b/samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore deleted file mode 100644 index 0bb75ea03f73..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/docs/ -/lib/ -/bin/ -/.shards/ -*.dwarf diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml b/samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml deleted file mode 100644 index 227f158d989c..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: crystal - -script: - - crystal spec - - crystal tool format --check diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE b/samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE deleted file mode 100644 index 914fdd2fa0fe..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2019-2020 Anton Maminov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/README.md b/samples/client/petstore/crystal/lib/http-client-digest_auth/README.md deleted file mode 100644 index 3b2f1ae1a42b..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# HTTP::Client::DigestAuth - -[![Build Status](https://travis-ci.org/mamantoha/http-client-digest_auth.svg?branch=master)](https://travis-ci.org/mamantoha/http-client-digest_auth) -[![GitHub release](https://img.shields.io/github/release/mamantoha/http-client-digest_auth.svg)](https://github.com/mamantoha/http-client-digest_auth/releases) -[![License](https://img.shields.io/github/license/mamantoha/http-client-digest_auth.svg)](https://github.com/mamantoha/http-client-digest_auth/blob/master/LICENSE) - -An implementation of RFC 2617 - Digest Access Authentication. At this time -this library does not drop in to `HTTP::Client` and can be used for with other HTTP -clients. - -[crest](https://github.com/mamantoha/crest) uses this shard to provide _Digest Access Authentication_ support out of the box. - -In order to use `http-client-digest_auth` you'll need to perform some request -wrangling on your own. See the class documentation at `HTTP::Client::DigestAuth` -for an example. - -Ported from Ruby's [net-http-digest_auth](https://github.com/drbrain/net-http-digest_auth) gem. - -## Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - http-client-digest_auth: - github: mamantoha/http-client-digest_auth -``` - -## Usage - -```crystal -require "http/client" -require "uri" -require "http-client-digest_auth" - -url = "https://httpbin.org/digest-auth/auth/admin/passwd/MD5" - -uri = URI.parse(url) -uri.user = "admin" -uri.password = "passwd" - -client = HTTP::Client.new(uri) - -response = client.get(uri.full_path) -# response is a 401 response with a WWW-Authenticate header - -www_authenticate = response.headers["WWW-Authenticate"] - -digest_auth = HTTP::Client::DigestAuth.new -auth = digest_auth.auth_header(uri, www_authenticate, "GET") - -http_headers = HTTP::Headers.new -http_headers["Authorization"] = auth - -# re-issue request with Authorization -response = client.get(uri.full_path, http_headers) -``` - -## Contributing - -1. Fork it () -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create a new Pull Request - -## Contributors - -- [Anton Maminov](https://github.com/mamantoha) - creator and maintainer diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/lib b/samples/client/petstore/crystal/lib/http-client-digest_auth/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr deleted file mode 100644 index 2b653b064b3a..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/samples/http_client_digest_auth.cr +++ /dev/null @@ -1,32 +0,0 @@ -require "http/client" -require "uri" -require "../src/http-client-digest_auth" - -path = "auth" # auth or auth-int -algorithm = "MD5" # MD5, SHA-256, SHA-512 -user = "user" -password = "pass" -url = "https://httpbin.org/digest-auth/#{path}/#{user}/#{password}/#{algorithm}" - -uri = URI.parse(url) -uri.user = user -uri.password = password - -client = HTTP::Client.new(uri) - -response = client.get(uri.full_path) -# response is a 401 response with a WWW-Authenticate header - -www_authenticate = response.headers["WWW-Authenticate"] - -digest_auth = HTTP::Client::DigestAuth.new -auth = digest_auth.auth_header(uri, www_authenticate, "GET") - -http_headers = HTTP::Headers.new -http_headers["Authorization"] = auth - -# re-issue request with Authorization -response = client.get(uri.full_path, http_headers) - -puts response.status_code -puts response.body diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml b/samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml deleted file mode 100644 index 96ef7cab932e..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/shard.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: http-client-digest_auth -version: 0.4.0 - -authors: - - Anton Maminov - -targets: - http-client-digest_auth: - main: src/http-client-digest_auth.cr - -crystal: 0.34.0 - -license: MIT diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr deleted file mode 100644 index 06830abd38a3..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/http/client/digest_auth_spec.cr +++ /dev/null @@ -1,116 +0,0 @@ -require "../../spec_helper" - -class HTTP::Client::DigestAuth - # Hardcode nonce value for specs - private def make_cnonce - "9ea5ff3bd34554a4165bbdc1df91dcff" - end -end - -uri = URI.parse("http://www.example.com/") -cnonce = "" -header = "" -expected = [] of String - -da = HTTP::Client::DigestAuth.new - -describe "HTTP::Client::DigestAuth" do - Spec.before_each do - uri = URI.parse("http://www.example.com/") - uri.user = "user" - uri.password = "password" - - cnonce = "9ea5ff3bd34554a4165bbdc1df91dcff" - - header = [ - "Digest qop=\"auth\"", - "realm=\"www.example.com\"", - "nonce=\"4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9\"", - ].join ", " - - expected = [ - "Digest username=\"user\"", - "realm=\"www.example.com\"", - "algorithm=MD5", - "uri=\"/\"", - "nonce=\"4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9\"", - "response=\"67be92a5e7b38d08679957db04f5da04\"", - "qop=auth", - "nc=00000000", - "cnonce=\"9ea5ff3bd34554a4165bbdc1df91dcff\"", - ] - - da = HTTP::Client::DigestAuth.new - end - - it "test auth_header" do - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - - expected[7] = "nc=00000001" - expected[5] = "response=\"1f5f0cd1588690c1303737f081c0b9bb\"" - - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - end - - it "test auth_header iis" do - expected[6] = "qop=\"auth\"" - - (da.auth_header(uri, header, "GET", true)).should eq(expected.join(", ")) - end - - it "test auth_header no qop" do - header = header.sub(" qop=\"auth\",", "") - - expected[5] = "response=\"32f6ca1631ccf7c42a8075deff44e470\"" - expected.delete("qop=auth") - expected.delete("cnonce=\"9ea5ff3bd34554a4165bbdc1df91dcff\"") - expected.delete("nc=00000000") - - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - end - - it "test auth_header opaque" do - expected << "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"" - header = header + "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"" - - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - end - - it "test auth_header post" do - expected[5] = "response=\"d82219e1e5430b136bbae1670fa51d48\"" - - (da.auth_header(uri, header, "POST")).should eq(expected.join(", ")) - end - - it "test auth_header sess" do - header = header + ", algorithm=MD5-sess" - - expected[2] = "algorithm=MD5-sess" - expected[5] = "response=\"c22c5bd9112a86ca78ddc1ae772daeeb\"" - - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - end - - it "test auth_header sha1" do - expected[2] = "algorithm=SHA1" - expected[5] = "response=\"2cb62fc18f7b0ebdc34543f896bb77686b4115e4\"" - - header = header + "algorithm=SHA1" - - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - end - - it "test auth_header unknown algorithm" do - header = header + "algorithm=bogus" - - expect_raises HTTP::Client::DigestAuth::Error, "unknown algorithm \"bogus\"" do - da.auth_header(uri, header, "GET") - end - end - - it "test auth_header quoted algorithm" do - header = header + "algorithm=\"MD5\"" - - (da.auth_header(uri, header, "GET")).should eq(expected.join(", ")) - end -end diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr deleted file mode 100644 index d152e869daf6..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/spec/spec_helper.cr +++ /dev/null @@ -1,2 +0,0 @@ -require "spec" -require "../src/http-client-digest_auth" diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr deleted file mode 100644 index 6e42367d80bf..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http-client-digest_auth.cr +++ /dev/null @@ -1,5 +0,0 @@ -require "./http/client/digest_auth" - -class Http::Client::DigestAuth - VERSION = "0.4.0" -end diff --git a/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr b/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr deleted file mode 100644 index 2dd77da85c9b..000000000000 --- a/samples/client/petstore/crystal/lib/http-client-digest_auth/src/http/client/digest_auth.cr +++ /dev/null @@ -1,168 +0,0 @@ -require "http/client" -require "uri" -require "digest" - -# An implementation of RFC 2617 Digest Access Authentication. -# http://www.rfc-editor.org/rfc/rfc2617.txt -# -# Here is a sample usage of `DigestAuth` on `HTTP::Client`: -# -# ``` -# url = "https://httpbin.org/digest-auth/auth/user/password/MD5" -# -# uri = URI.parse(url) -# uri.user = "user" -# uri.password = "password" -# -# client = HTTP::Client.new(uri) -# -# response = client.get(uri.full_path) -# # response is a 401 response with a WWW-Authenticate header -# -# www_authenticate = response.headers["WWW-Authenticate"] -# -# digest_auth = HTTP::Client::DigestAuth.new -# auth = digest_auth.auth_header(uri, www_authenticate, "GET") -# -# http_headers = HTTP::Headers.new -# http_headers["Authorization"] = auth -# -# # re-issue request with Authorization -# response = client.get(uri.full_path, http_headers) -# ``` -class HTTP::Client::DigestAuth - class Error < Exception; end - - def initialize - @nonce_count = -1 - end - - # Creates a digest auth header for `uri` from the `www_authenticate` header - # for HTTP method `method`. - # - # The result of this method should be sent along with the HTTP request as - # the "Authorization" header. - # - # IIS servers handle the "qop" parameter of digest authentication - # differently so you may need to set `iis` to true for such servers. - def auth_header(uri : URI, www_authenticate : String, method : String, iis = false) : String - nonce_count = next_nonce - - user = uri.user - password = uri.password - - header = "" - params = {} of String => String - - if m = www_authenticate.match(/^(\w+) (.*)/) - challenge = m[2] - - challenge.scan(/(\w+)="(.*?)"/) do |m| - params[m[1]] = m[2] - end - - if m = challenge.match(/algorithm="?(.*?)"?([, ]|$)/) - params["algorithm"] = m[1] - else - params["algorithm"] = "MD5" - end - - if m = params["algorithm"].match(/(.*?)(-sess)?$/) - algorithm = choose_digest_algorithm(m[1]) - sess = m[2]? - end - - algorithm = algorithm.not_nil! - - qop = params["qop"]? - cnonce = make_cnonce if qop || sess - - a1 = - if sess - [ - hexdigest(algorithm, "#{user}:#{params["realm"]}:#{password}"), - params["nonce"], - cnonce, - ].join(":") - else - "#{user}:#{params["realm"]}:#{password}" - end - - ha1 = hexdigest(algorithm, a1) - ha2 = hexdigest(algorithm, "#{method}:#{uri.full_path}") - - request_digest = [] of String | Nil - request_digest.push(ha1, params["nonce"]) - request_digest.push(("%08x" % nonce_count), cnonce, qop) if qop - request_digest << ha2 - request_digest = request_digest.join(":") - - response_digest = hexdigest(algorithm, request_digest) - - header = [ - "Digest username=\"#{user}\"", - "realm=\"#{params["realm"]}\"", - "algorithm=#{params["algorithm"]}", - "uri=\"#{uri.full_path}\"", - "nonce=\"#{params["nonce"]}\"", - "response=\"#{response_digest}\"", - ] - - if qop - if iis - header << "qop=\"#{qop}\"" - else - header << "qop=#{qop}" - end - header << "nc=#{"%08x" % nonce_count}" - header << "cnonce=\"#{cnonce}\"" - else - end - - if params.has_key?("opaque") - header << "opaque=\"#{params["opaque"]}\"" - end - - header = header.compact.join(", ") - else - raise Error.new("Digest auth method not found or syntax error in auth header: #{www_authenticate}") - end - - header - end - - private def next_nonce - @nonce_count += 1 - end - - private def choose_digest_algorithm(algorithm : String) : String - case algorithm - when "MD5" then "MD5" - when "SHA1" then "SHA1" - when "SHA-256" then "SHA256" - when "SHA-512" then "SHA512" - else - raise Error.new("unknown algorithm \"#{algorithm}\"") - end - end - - private def hexdigest(algorithm, data) - digest = OpenSSL::Digest.new(algorithm) - digest << data - {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} - digest.final.hexstring - {% else %} - digest.hexdigest - {% end %} - end - - # Creates a client nonce value that is used across all requests based on the - # current time, process id and a random number - private def make_cnonce - Digest::MD5.hexdigest([ - Time.utc.to_unix, - Process.pid, - Random::Secure.rand(2_i64 ** 32), - ].join(":")) - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml b/samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml deleted file mode 100644 index 02d3e3da9875..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/.github/workflows/crystal.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Crystal CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - check_format: - runs-on: ubuntu-latest - container: - image: crystallang/crystal - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Check format - run: crystal tool format --check - check_ameba: - runs-on: ubuntu-latest - container: - image: crystallang/crystal - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Check ameba - run: ./bin/ameba - test_latest: - runs-on: ubuntu-latest - container: - image: crystallang/crystal - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Run tests - run: crystal spec - test_nightly: - runs-on: ubuntu-latest - container: - image: crystallang/crystal:nightly - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: shards install --ignore-crystal-version - - name: Run tests - run: crystal spec diff --git a/samples/client/petstore/crystal/lib/http_proxy/.gitignore b/samples/client/petstore/crystal/lib/http_proxy/.gitignore deleted file mode 100644 index 40005bad64b5..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/doc/ -/lib/ -/bin/ -/.shards/ - -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock - -/http_proxy diff --git a/samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md b/samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md deleted file mode 100644 index 60566d72439b..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/CHANGELOG.md +++ /dev/null @@ -1,66 +0,0 @@ -# Changelog - -## 0.7.2 - -* Allow to set HTTP headers for proxy client ([#23](https://github.com/mamantoha/http_proxy/pull/23)) - -## 0.7.1 - -* Fixed compatibility with Crystal nightly - -## 0.7.0 - -* **(breaking-change)** Sending full URL instead of path when using HTTP proxy is not required - -## 0.6.0 - -* **(breaking-change)** Change `HTTP::Proxy::Server` implementation. - `HTTP::Proxy::Server.new` not require `host` and `port` parameters - -## 0.5.0 - -* Compatibility with Crystal 0.35.0 - -## 0.4.0 - -* Compatibility with Crystal 0.30 - -## 0.3.6 - -* Updates to Crystal 0.27 -* Refactor Proxy Client - -## 0.3.5 - -* Updates to Crystal 0.25 - -## 0.3.3 - -* Fixed an issue with HTTP resource when proxy server which initialized without handlers can't properly read a response - -## 0.3.2 - -* Fix compatibility with Kemal - -## 0.3.1 - -* minor bug fixes - -## 0.3.0 - -* Detach the fork and turn it into a standalone repository on GitHub -* Change shard name to `http_proxy` -* Add proxy client -* Add project to Travis CI - -## 0.2.1 - -* Bug fixes - -## 0.2.0 - -* First release after fork -* Change shard name to `http_proxy_server` -* Update to Crystal 0.23.0 -* New project structure -* Bug fixes and performance improvement diff --git a/samples/client/petstore/crystal/lib/http_proxy/LICENSE b/samples/client/petstore/crystal/lib/http_proxy/LICENSE deleted file mode 100644 index 5d8ccaf773ae..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2018 Anton Maminov -Copyright (c) 2017 Theo Li - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/http_proxy/README.md b/samples/client/petstore/crystal/lib/http_proxy/README.md deleted file mode 100644 index 4b726404018b..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/README.md +++ /dev/null @@ -1,135 +0,0 @@ -# HTTP::Proxy - -![Crystal CI](https://github.com/mamantoha/http_proxy/workflows/Crystal%20CI/badge.svg) -[![GitHub release](https://img.shields.io/github/release/mamantoha/http_proxy.svg)](https://github.com/mamantoha/http_proxy/releases) -[![License](https://img.shields.io/github/license/mamantoha/http_proxy.svg)](https://github.com/mamantoha/http_proxy/blob/master/LICENSE) - -A HTTP Proxy server and client written in Crystal - -## Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - http_proxy: - github: mamantoha/http_proxy -``` - -## Usage - -### Server - -```crystal -require "http_proxy" - -host = "127.0.0.1" -port = 8080 - -server = HTTP::Proxy::Server.new - -address = server.bind_tcp(host, port) -puts "Listening on http://#{address}" -server.listen -``` - -```crystal -require "http_proxy" -require "option_parser" - -host = "192.168.0.1" -port = 3128 - -OptionParser.parse! do |opts| - opts.on("-h HOST", "--host HOST", "define host to run server") do |opt| - host = opt - end - - opts.on("-p PORT", "--port PORT", "define port to run server") do |opt| - port = opt.to_i - end -end - -server = HTTP::Proxy::Server.new(handlers: [ - HTTP::LogHandler.new, -]) do |context| - context.perform -end - -address = server.bind_tcp(host, port) -puts "Listening on http://#{address}" -server.listen -``` - -#### Basic Authentication - -```crystal -server = HTTP::Proxy::Server.new(handlers: [ - HTTP::LogHandler.new, - HTTP::Proxy::Server::BasicAuth.new("user", "passwd"), -]) do |context| - context.request.headers.add("X-Forwarded-For", "127.0.0.1") - context.perform -end -``` - -### Client - -#### Make request with proxy - -```crystal -require "http_proxy" - -proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080) - -uri = URI.parse("http://httpbin.org") -client = HTTP::Client.new(uri) -client.set_proxy(proxy_client) -response = client.get("/get") -``` - -#### Client Authentication - -```crystal -uri = URI.parse("https://httpbin.org") -proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "passwd") - -response = HTTP::Client.new(uri) do |client| - client.set_proxy(proxy_client) - client.get("/get") -end - -puts response.status_code -puts response.body -``` - -## Development - -### Proxy server - -* [x] Basic HTTP Proxy: GET, POST, PUT, DELETE support -* [x] Basic HTTP Proxy: OPTIONS support -* [x] HTTPS Proxy: CONNECT support -* [x] Make context.request & context.response writable -* [x] Basic Authentication -* [ ] MITM HTTPS Proxy - -### Proxy client - -* [x] Basic HTTP Proxy: GET, POST, PUT, DELETE support -* [x] Basic HTTP Proxy: OPTIONS support -* [x] HTTPS Proxy: CONNECT support -* [x] Basic Authentication - -## Contributing - -1. Fork it () -2. Create your feature branch (git checkout -b my-new-feature) -3. Commit your changes (git commit -am 'Add some feature') -4. Push to the branch (git push origin my-new-feature) -5. Create a new Pull Request - -## Contributors - -* [bbtfr](https://github.com/bbtfr) Theo Li - creator, maintainer -* [mamantoha](https://github.com/mamantoha) Anton Maminov - maintainer diff --git a/samples/client/petstore/crystal/lib/http_proxy/lib b/samples/client/petstore/crystal/lib/http_proxy/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/client.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/client.cr deleted file mode 100644 index 0721d0373459..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/samples/client.cr +++ /dev/null @@ -1,40 +0,0 @@ -require "../src/http_proxy" -require "option_parser" - -host = "127.0.0.1" -port = 8080 - -OptionParser.parse! do |opts| - opts.on("-h HOST", "--host HOST", "define host") do |opt| - host = opt - end - - opts.on("-p PORT", "--port PORT", "define port") do |opt| - port = opt.to_i - end -end - -proxy_client = HTTP::Proxy::Client.new(host, port) - -puts "Make HTTPs request w/o proxy" -uri = URI.parse("https://httpbin.org") -client = HTTP::Client.new(uri) -response = client.get("/get") -puts response.status_code -puts response.body - -puts "Make HTTPS request" -uri = URI.parse("https://httpbin.org") -client = HTTP::Client.new(uri) -client.set_proxy(proxy_client) -response = client.get("/get") -puts response.status_code -puts response.body - -puts "Make HTTP request" -uri = URI.parse("http://httpbin.org") -client = HTTP::Client.new(uri) -client.set_proxy(proxy_client) -response = client.get("http://httpbin.org/get") -puts response.status_code -puts response.body diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/server.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/server.cr deleted file mode 100644 index de5de05bffdd..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/samples/server.cr +++ /dev/null @@ -1,23 +0,0 @@ -require "../src/http_proxy" -require "option_parser" - -host = "127.0.0.1" -port = 8080 - -OptionParser.parse do |opts| - opts.on("-h HOST", "--host HOST", "define host to run server") do |opt| - host = opt - end - - opts.on("-p PORT", "--port PORT", "define port to run server") do |opt| - port = opt.to_i - end -end - -server = HTTP::Proxy::Server.new(handlers: [ - HTTP::LogHandler.new, -]) - -address = server.bind_tcp(host, port) -puts "Listening on http://#{address}" -server.listen diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr deleted file mode 100644 index 227c65868c74..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/samples/server_with_authentication.cr +++ /dev/null @@ -1,38 +0,0 @@ -require "../src/http_proxy" -require "option_parser" - -host = "127.0.0.1" -port = 8080 -username = "user" -password = "1234" - -OptionParser.parse do |opts| - opts.on("-h HOST", "--host HOST", "define host to run server") do |opt| - host = opt - end - - opts.on("-p PORT", "--port PORT", "define port to run server") do |opt| - port = opt.to_i - end - - opts.on("-u USER", "--user USER", "define user for authentication") do |opt| - username = opt - end - - opts.on("--pass PASSWORD", "define password for authentication") do |opt| - password = opt - end -end - -server = HTTP::Proxy::Server.new(handlers: [ - HTTP::LogHandler.new, - HTTP::Proxy::Server::BasicAuth.new(username, password), -]) do |context| - context.request.headers.add("X-Forwarded-For", host) - context.perform -end - -address = server.bind_tcp(host, port) -puts "Listening on http://#{address}" -puts "Use #{username}:#{password} for authentication" -server.listen diff --git a/samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr b/samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr deleted file mode 100644 index 6e99f05c132b..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/samples/with_proxy_server.cr +++ /dev/null @@ -1,60 +0,0 @@ -require "../src/http_proxy" - -def with_proxy_server(host = "127.0.0.1", port = 8080) - wants_close = Channel(Nil).new - - server = HTTP::Proxy::Server.new(handlers: [ - HTTP::LogHandler.new, - HTTP::Proxy::Server::BasicAuth.new("user", "passwd"), - ]) do |context| - context.request.headers.add("X-Forwarded-For", host) - end - - spawn do - server.bind_tcp(host, port) - puts "start proxy server" - server.listen - end - - spawn do - wants_close.receive - puts "exit proxy server" - server.close - end - - Fiber.yield - - yield host, port, wants_close -end - -with_proxy_server do |_host, _port, wants_close| - puts "start proxy client" - - puts "HTTP Request:" - uri = URI.parse("http://httpbin.org") - proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "passwd") - - response = HTTP::Client.new(uri) do |client| - client.set_proxy(proxy_client) - client.get("http://httpbin.org/get") - end - - puts response.status_code - puts response.body - - puts "HTTPS Request:" - uri = URI.parse("https://httpbin.org") - proxy_client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "passwd") - - response = HTTP::Client.new(uri) do |client| - client.set_proxy(proxy_client) - client.get("/get") - end - - puts response.status_code - puts response.body - - puts "exit proxy client" -ensure - wants_close.send(nil) -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/shard.yml b/samples/client/petstore/crystal/lib/http_proxy/shard.yml deleted file mode 100644 index f41e1b723b6f..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/shard.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: http_proxy -version: 0.7.2 - -authors: - - Theo Li - - Anton Maminov - -crystal: ">= 0.35.0" - -development_dependencies: - ameba: - github: crystal-ameba/ameba - -license: MIT diff --git a/samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr b/samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr deleted file mode 100644 index ae541dd4dd3b..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/spec/client_spec.cr +++ /dev/null @@ -1,53 +0,0 @@ -require "./spec_helper" - -describe HTTP::Proxy::Client do - describe "#initialize" do - it "with host and port" do - client = HTTP::Proxy::Client.new("127.0.0.1", 8080) - (client.host).should eq("127.0.0.1") - (client.port).should eq(8080) - (client.username).should eq(nil) - (client.password).should eq(nil) - end - - it "with username and password" do - client = HTTP::Proxy::Client.new("127.0.0.1", 8080, username: "user", password: "password") - (client.username).should eq("user") - (client.password).should eq("password") - end - - context "HTTP::Client#set_proxy" do - it "should make HTTP request with proxy" do - with_proxy_server do |host, port, wants_close| - proxy_client = HTTP::Proxy::Client.new(host, port) - - uri = URI.parse("http://httpbin.org") - client = HTTP::Client.new(uri) - client.set_proxy(proxy_client) - response = client.get("/get") - - (client.proxy?).should eq(true) - (response.status_code).should eq(200) - ensure - wants_close.send(nil) - end - end - - it "should make HTTPS request with proxy" do - with_proxy_server do |host, port, wants_close| - proxy_client = HTTP::Proxy::Client.new(host, port) - - uri = URI.parse("https://httpbin.org") - client = HTTP::Client.new(uri) - client.set_proxy(proxy_client) - response = client.get("/get") - - (client.proxy?).should eq(true) - (response.status_code).should eq(200) - ensure - wants_close.send(nil) - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr b/samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr deleted file mode 100644 index 9b00efdb67d4..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/spec/server_spec.cr +++ /dev/null @@ -1,17 +0,0 @@ -require "./spec_helper" - -describe HTTP::Proxy::Server do - describe "#initialize" do - it "with params" do - server = HTTP::Proxy::Server.new - server.should be_a(HTTP::Proxy::Server) - end - - it "with BasicAuth handler" do - server = HTTP::Proxy::Server.new([ - HTTP::Proxy::Server::BasicAuth.new("user", "passwd"), - ]) - server.should be_a(HTTP::Proxy::Server) - end - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr deleted file mode 100644 index 98a174ae6820..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/spec/spec_helper.cr +++ /dev/null @@ -1,27 +0,0 @@ -require "spec" -require "../src/http_proxy" - -describe HTTP::Proxy do - it "should have version" do - (HTTP::Proxy::VERSION).should_not be_nil - end -end - -def with_proxy_server(host = "127.0.0.1", port = 8080) - wants_close = Channel(Nil).new - server = HTTP::Proxy::Server.new - - spawn do - server.bind_tcp(host, port) - server.listen - end - - spawn do - wants_close.receive - server.close - end - - Fiber.yield - - yield host, port, wants_close -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr deleted file mode 100644 index f28de7bc0e1f..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/client.cr +++ /dev/null @@ -1,176 +0,0 @@ -require "http" -require "socket" -require "base64" - -{% if !flag?(:without_openssl) %} - require "openssl" -{% end %} - -module HTTP - # :nodoc: - module Proxy - # Represents a proxy client with all its attributes. - # Provides convenient access and modification of them. - class Client - getter host : String - getter port : Int32 - property username : String? - property password : String? - property headers : HTTP::Headers - - getter tls : OpenSSL::SSL::Context::Client? - - @dns_timeout : Float64? - @connect_timeout : Float64? - @read_timeout : Float64? - - record Response, - version : String, - code : Int32, - reason : String, - headers = {} of String => String - - # Creates a new socket factory that tunnels via the given host and port. - # The following optional arguments are supported: - # - # * :headers - additional headers, which will be used for tls connections, which is useful to supply a User-Agent header - # * :username - the user name to use when authenticating to the proxy - # * :password - the password to use when authenticating - def initialize(@host, @port, *, headers : HTTP::Headers? = nil, @username = nil, @password = nil) - @headers = headers || HTTP::Headers.new - @headers["User-Agent"] ||= "Crystal, HTTP::Proxy/#{HTTP::Proxy::VERSION}" - end - - # Returns a new socket connected to the given host and port via the - # proxy that was requested when the socket factory was instantiated. - def open(host, port, tls = nil, *, @dns_timeout, @connect_timeout, @read_timeout) - socket = TCPSocket.new(@host, @port, @dns_timeout, @connect_timeout) - socket.read_timeout = @read_timeout if @read_timeout - socket.sync = true - - if tls - socket << "CONNECT #{host}:#{port} HTTP/1.1\r\n" - @headers.each do |name, values| - values.each do |value| - socket << "#{name}: #{value}\r\n" - end - end - socket << "Host: #{host}:#{port}\r\n" - - if @username - credentials = Base64.strict_encode("#{@username}:#{@password}") - credentials = "#{credentials}\n".gsub(/\s/, "") - socket << "Proxy-Authorization: Basic #{credentials}\r\n" - end - - socket << "\r\n" - - resp = parse_response(socket) - - if resp.code == 200 - {% if !flag?(:without_openssl) %} - if tls - tls_socket = OpenSSL::SSL::Socket::Client.new(socket, context: tls, sync_close: true, hostname: host) - socket = tls_socket - end - {% end %} - - return socket - else - socket.close - raise IO::Error.new(resp.inspect) - end - end - - socket - end - - private def parse_response(socket) : Response? - version, code, reason = socket.gets.as(String).chomp.split(/ /, 3) - - headers = {} of String => String - - while (line = socket.gets.as(String)) && (line.chomp != "") - name, value = line.split(/:/, 2) - headers[name.strip] = value.strip - end - - Response.new(version, code.to_i, reason, headers) - end - end - end - - class Client - getter proxy : Bool = false - - def set_proxy(proxy : HTTP::Proxy::Client?) - return unless proxy - - begin - {% if compare_versions(Crystal::VERSION, "1.0.0-0") >= 0 %} - @io = proxy.open( - host: @host, - port: @port, - tls: @tls, - dns_timeout: @dns_timeout, - connect_timeout: @connect_timeout, - read_timeout: @read_timeout - ) - {% else %} - @socket = proxy.open( - host: @host, - port: @port, - tls: @tls, - dns_timeout: @dns_timeout, - connect_timeout: @connect_timeout, - read_timeout: @read_timeout - ) - {% end %} - rescue ex : IO::Error - raise IO::Error.new("Failed to open TCP connection to #{@host}:#{@port} (#{ex.message})") - end - - @proxy = true - - if proxy.username && proxy.password - proxy_basic_auth(proxy.username, proxy.password) - end - - {% if compare_versions(Crystal::VERSION, "1.0.0-0") >= 0 %} - @io - {% else %} - @socket - {% end %} - end - - # True if requests for this connection will be proxied - def proxy? - @proxy - end - - private def new_request(method, path, headers, body : BodyType) - # Use full URL instead of path when using HTTP proxy - if proxy? && !@tls - path = "http://#{host_with_port}#{path}" - end - - HTTP::Request.new(method, path, headers, body) - end - - private def host_with_port - host = @host - host = "[#{host}]" if host.includes?(":") - default_port = @tls ? URI.default_port("https") : URI.default_port("http") - default_port == @port ? host : "#{host}:#{@port}" - end - - # Configures this client to perform proxy basic authentication in every - # request. - private def proxy_basic_auth(username : String?, password : String?) - header = "Basic #{Base64.strict_encode("#{username}:#{password}")}" - before_request do |request| - request.headers["Proxy-Authorization"] = header - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr deleted file mode 100644 index b05f7d9c8b25..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server.cr +++ /dev/null @@ -1,53 +0,0 @@ -require "./server/handler" -require "./server/basic_auth" - -# A concurrent Proxy server implementation. -# -# ``` -# require "http_proxy" -# -# server = HTTP::Proxy::Server.new -# address = server.bind_tcp("127.0.0.1", 8080) -# puts "Listening on http://#{address}" -# server.listen -# ``` -# -class HTTP::Proxy::Server - def initialize - handler = build_middleware - @processor = RequestProcessor.new(handler) - end - - def initialize(&handler : Context ->) - @processor = RequestProcessor.new(handler) - end - - def initialize(handlers : Array(HTTP::Handler), &handler : Context ->) - handler = build_middleware(handlers, handler) - @processor = RequestProcessor.new(handler) - end - - def initialize(handlers : Array(HTTP::Handler)) - handler = build_middleware(handlers) - @processor = RequestProcessor.new(handler) - end - - def initialize(handler : HTTP::Handler | HTTP::Handler::HandlerProc) - @processor = RequestProcessor.new(handler) - end - - private def build_middleware(handler : (Context ->)? = nil) - proxy_handler = Handler.new - proxy_handler.next = handler if handler - proxy_handler - end - - private def build_middleware(handlers, last_handler : (Context ->)? = nil) - proxy_handler = build_middleware(last_handler) - return proxy_handler if handlers.empty? - - 0.upto(handlers.size - 2) { |i| handlers[i].next = handlers[i + 1] } - handlers.last.next = proxy_handler if proxy_handler - handlers.first - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr deleted file mode 100644 index e9a3fcb8b8fb..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/basic_auth.cr +++ /dev/null @@ -1,27 +0,0 @@ -class HTTP::Proxy::Server - class BasicAuth - include HTTP::Handler - - def initialize(@username : String, @password : String) - end - - def call(context) - if context.request.headers.has_key?("Proxy-Authorization") - _, enc = context.request.headers["Proxy-Authorization"].split - username, password = Base64.decode_string(enc).split(":") - if authorized?(username, password) - context.request.headers.delete("Proxy-Authorization") - return call_next(context) - end - end - - context.response.headers.add("Proxy-Authenticate", "Basic realm=\"hello\"") - context.response.status_code = 407 - context.response.puts("") - end - - private def authorized?(username, password) - username == @username && password == @password - end - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr deleted file mode 100644 index d9cf8367aac7..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/context.cr +++ /dev/null @@ -1,59 +0,0 @@ -class HTTP::Proxy::Server < HTTP::Server - class Context < HTTP::Server::Context - def perform - case @request.method - when "OPTIONS" - @response.headers["Allow"] = "OPTIONS,GET,HEAD,POST,PUT,DELETE,CONNECT" - when "CONNECT" - handle_tunneling - else - handle_http - end - end - - private def handle_tunneling - host, port = @request.resource.split(":", 2) - upstream = TCPSocket.new(host, port) - - @response.upgrade do |downstream| - channel = Channel(Nil).new(2) - - downstream = downstream.as(TCPSocket) - downstream.sync = true - - spawn do - transfer(upstream, downstream, channel) - transfer(downstream, upstream, channel) - end - - 2.times { channel.receive } - end - end - - private def transfer(destination, source, channel) - spawn do - IO.copy(destination, source) - rescue ex - Log.error(exception: ex) { "Unhandled exception on HTTP::Proxy::Server::Context" } - ensure - channel.send(nil) - end - end - - private def handle_http - uri = URI.parse(@request.resource) - client = HTTP::Client.new(uri) - - @request.headers.delete("Accept-Encoding") - - response = client.exec(@request) - - response.headers.delete("Transfer-Encoding") - response.headers.delete("Content-Encoding") - - @response.headers.merge!(response.headers) - @response.status_code = response.status_code - @response.puts(response.body) - end - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr deleted file mode 100644 index de078cb1575b..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/src/http/proxy/server/handler.cr +++ /dev/null @@ -1,17 +0,0 @@ -require "./context" - -class HTTP::Proxy::Server::Handler - include HTTP::Handler - - property next : HTTP::Handler | Proc | Nil - - alias Proc = Context -> - - def call(context) - request = context.request - response = context.response - context = Context.new(request, response) - - context.perform - end -end diff --git a/samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr b/samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr deleted file mode 100644 index edc48854791a..000000000000 --- a/samples/client/petstore/crystal/lib/http_proxy/src/http_proxy.cr +++ /dev/null @@ -1,12 +0,0 @@ -require "http" -require "socket" -require "base64" - -require "./http/proxy/server" -require "./http/proxy/client" - -module HTTP - module Proxy - VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/.ameba.yml b/samples/client/petstore/crystal/lib/kemal/.ameba.yml deleted file mode 100644 index 9cc083d6f85e..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/.ameba.yml +++ /dev/null @@ -1,13 +0,0 @@ -# This configuration file was generated by `ameba --gen-config` -# on 2019-08-25 09:29:24 UTC using Ameba version 0.10.0. -# 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: 7 -# Run `ameba --only Lint/UselessAssign` for details -Lint/UselessAssign: - Description: Disallows useless variable assignments - Enabled: true - Severity: Warning - Excluded: - - spec/view_spec.cr diff --git a/samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml b/samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml deleted file mode 100644 index 3452e4af2f91..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/.github/FUNDING.yml +++ /dev/null @@ -1,8 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: sdogruyol -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -custom: # Replace with a single custom sponsorship URL diff --git a/samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md b/samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 42a81d927ad7..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ -### Description - -[Description of the issue] - -### Steps to Reproduce - -1. [First Step] -2. [Second Step] -3. [and so on...] - -**Expected behavior:** [What you expect to happen] - -**Actual behavior:** [What actually happens] - -**Reproduces how often:** [What percentage of the time does it reproduce?] - -### Versions - -You can get this information from copy and pasting the output of `crystal --version`.Also, please include the OS and what version of the OS you're running. - -### Additional Information - -Any additional information, configuration or data that might be necessary to reproduce the issue. diff --git a/samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md b/samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index c97ebc981cb5..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,15 +0,0 @@ -### Description of the Change - - - -### Alternate Designs - - - -### Benefits - - - -### Possible Drawbacks - - diff --git a/samples/client/petstore/crystal/lib/kemal/.gitignore b/samples/client/petstore/crystal/lib/kemal/.gitignore deleted file mode 100644 index 6c364e446777..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/lib/ -/.crystal/ -/.shards/ -*.log -/bin/ -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/.travis.yml b/samples/client/petstore/crystal/lib/kemal/.travis.yml deleted file mode 100644 index 9883f5e9ef49..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: crystal -crystal: - - latest - - nightly - -script: - - crystal spec - - crystal spec --release --no-debug - - crystal tool format --check - - bin/ameba src - -matrix: - allow_failures: - - crystal: nightly diff --git a/samples/client/petstore/crystal/lib/kemal/CHANGELOG.md b/samples/client/petstore/crystal/lib/kemal/CHANGELOG.md deleted file mode 100644 index 48d21fa8a3df..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/CHANGELOG.md +++ /dev/null @@ -1,385 +0,0 @@ -# 0.27.0 (28-11-2020) - -- Crystal 0.35.x support :tada: Thanks @bcardiff :pray: -- Fix issues with responding with long strings [#576](https://github.com/kemalcr/kemal/pull/576). Thanks @mamantoha :pray: -- Fix broken WebSocket support in 0.35.0 [#577](https://github.com/kemalcr/kemal/pull/577). Thanks @mamantoha :pray: -- Allow to set optional response body on redirects [#561](https://github.com/kemalcr/kemal/pull/561). Thanks @mamantoha :pray: - -# 0.26.1 (01-12-2019) - -- Fix process request when a response already closed [#550](https://github.com/kemalcr/kemal/pull/550). Thanks @mamantoha :pray: -- Switch to new Ameba repository [#549](https://github.com/kemalcr/kemal/pull/549). Thanks @mamantoha :pray: -- Check for `KEMAL_ENV` variable already in `Config#initialize`[#552](https://github.com/kemalcr/kemal/pull/552). Thanks @Sija :pray: -- Cleanup Ameba warnings [#551](https://github.com/kemalcr/kemal/pull/551). Thanks @Sija :pray: -- Flush io buffer after each write to log [#554](https://github.com/kemalcr/kemal/pull/554). Thanks @mang :pray: - -# 0.26.0 (05-08-2019) - -- Crystal 0.30.0 support :tada: [#548](https://github.com/kemalcr/kemal/pull/548) and [#544](https://github.com/kemalcr/kemal/pull/544). Thanks @bcardiff and @straight-shoota :pray: -- Add support for serving files greater than 2^31 bytes [#546](https://github.com/kemalcr/kemal/pull/546). Thanks @omarroth :pray: -- Properly measure request time using `Time.monotonic` [#527](https://github.com/kemalcr/kemal/pull/527). Thanks @spinscale :pray: - -# 0.25.2 (08-02-2019) - -- Add option to config to parse or not command line parameters [#483](https://github.com/kemalcr/kemal/pull/483). Thanks @diegogub :pray: - -- Allow to set filename for `send_file` [#512](https://github.com/kemalcr/kemal/pull/512). Thanks @mamantoha :pray: - - -```ruby -send_file env, "./asset/image.jpeg", filename: "image.jpg" -``` - -- Set `status_code` before response [#513](https://github.com/kemalcr/kemal/pull/513). Thanks @mamantohoa :pray: - -- Use Crystal MIME registry. [#516](https://github.com/kemalcr/kemal/pull/516) Thanks @Sija :pray: - -# 0.25.1 (06-10-2018) - -- Fix `params.files` memoization https://github.com/kemalcr/kemal/pull/503. Thanks @mamantoha :pray: - -# 0.25.0 (05-10-2018) - -- Crystal 0.27.0 support. -- *[breaking change]* Added back `env.params.files`. - -Here's a fully working sample for reading a image file upload `image1` and saving it under `public/uploads`. - -```crystal -post "/upload" do |env| - file = env.params.files["image1"].tempfile - file_path = ::File.join [Kemal.config.public_folder, "uploads/", File.basename(file.path)] - File.open(file_path, "w") do |f| - IO.copy(file, f) - end - "Upload ok" -end -``` - -To test - -`curl -F "image1=@/Users/serdar/Downloads/kemal.png" http://localhost:3000/upload` - -- Cache HTTP routes to increase performance :rocket: https://github.com/kemalcr/kemal/pull/493 - -# 0.24.0 (14-08-2018) - -- *[breaking change]* Removed `env.params.files`. You can use Crystal's built-in `HTTP::FormData.parse` instead - -```ruby -post "/upload" do |env| - HTTP::FormData.parse(env.request) do |upload| - filename = file.filename - - if !filename.is_a?(String) - "No filename included in upload" - else - file_path = ::File.join [Kemal.config.public_folder, "uploads/", filename] - File.open(file_path, "w") do |f| - IO.copy(file.tmpfile, f) - end - "Upload OK" - end -end -``` - -- *[breaking change]* From now on to access dynamic url params in a WebSocket route you have to use: - -```ruby -ws "/:id" do |socket, context| - id = context.ws_route_lookup.params["id"] -end -``` - -- *[breaking change]* Removed `_method` magic param. - -- Added new exception page [#466](https://github.com/kemalcr/kemal/pull/466). Thanks @mamantoha 🙏 - -- Support custom port binding. Thanks @straight-shoota 🙏 - -```ruby -Kemal.run do |config| - server = config.server.not_nil! - server.bind_tcp "127.0.0.1", 3000, reuse_port: true - server.bind_tcp "0.0.0.0", 3001, reuse_port: true -end -``` - -# 0.23.0 (17-06-2018) - -- Crystal 0.25.0 support 🎉 -- Add `Kemal::Context.get?` to safely access context storage :sunglasses: -- [Security] Don't serve 404 image dynamically :thumbsup: -- Disable `X-Powered-By` header [#449](https://github.com/kemalcr/kemal/pull/449). Thanks @Blacksmoke16 🙏 - -# 0.22.0 (29-12-2017) - -- Crystal 0.24.1 support 🎉 -- Only return string from route.[#408](https://github.com/kemalcr/kemal/pull/408) thanks @crisward 🙏 -- Don't crash on empty path when compiled in --release. [#407](https://github.com/kemalcr/kemal/pull/407) thanks @crisward 🙏 -- Rename `Kemal::CommonLogHandler` to `Kemal::LogHandler` and `Kemal::CommonExceptionHandler` to `Kemal::ExceptionHandler`. -- Allow videos to be opened with correct mime type. [#406](https://github.com/kemalcr/kemal/pull/406) thanks @crisward 🙏 -- Add webm mime type.[#413](https://github.com/kemalcr/kemal/pull/413) thanks @reindeer-cafe 🙏 - - -# 0.21.0 (05-09-2017) - -- Dynamically insert handlers :muscle: Fixes [#376](https://github.com/kemalcr/kemal/pull/376). -- Add context to WebSocket. This allows one to use `HTTP::Server::Context` in `ws` declarations :heart_eyes: Fixes [#349](https://github.com/kemalcr/kemal/pull/349). - -```ruby -ws "/:room_name" do |socket, env| - env.params.url["room_name"] -end -``` - -- Add support for customizing the headers of built-in `Kemal::StaticFileHandler` :hammer: Useful for supporting `CORS` for single page applications :clap: - -```ruby -static_headers do |response, filepath, filestat| - if filepath =~ /\.html$/ - response.headers.add("Access-Control-Allow-Origin", "*") - end - response.headers.add("Content-Size", filestat.size.to_s) - end -end -``` - -- Allow %w in Handler macros [#385](https://github.com/kemalcr/kemal/pull/385). Thanks @will :pray: - -- Security: X-Content-Type-Options: nosniff for static files. Fixes [#379](https://github.com/kemalcr/kemal/issues/379). Thanks @crisward :pray: - -- Performance: [Remove tempfile management to OS](https://github.com/kemalcr/kemal/commit/a1520de7ed3865fa73258343a80fad4f20666a99). This brings %10 - 15 performance boost to Kemal :rocket: - -# 0.20.0 (01-07-2017) - -- Crystal 0.23.0 support! As always, Kemal is compatible with the latest major release of Crystal 💎 -- Great news everyone 🎉 All handlers are now completely ***customizable***!. Use the default `Kemal` handlers or go wild, it's all up to you ⛏ - -```ruby -# Don't forget to add `Kemal::RouteHandler::INSTANCE` or your routes won't work! -Kemal.config.handlers = [Kemal::InitHandler.new, YourHandler.new, Kemal::RouteHandler::INSTANCE] -``` - -You can also insert a handler into a specific position. - -```ruby -# This adds MyCustomHandler instance to 1 position. Be aware that the index starts from 0. -add_handler MyCustomHandler.new, 1 -``` -- Updated [Kilt](https://github.com/jeromegn/kilt) to v0.4.0. -- Make `Route` a `Struct`. This improves the performance of route declarations. - -# 0.19.0 (09-05-2017) - -- Return no body for head route fixes #323. (thanks @crisward) -- Update `radix` to `0.3.8`. (thanks @waghanza) -- User defined context store types. (thanks @neovitange) - -```ruby -class User - property name -end - -add_context_storage_type(User) -``` - -- Prevent `send_file returning filesize. (thanks @crisward) -- Dont call setup in `config#add_filter_handler` fixes #338. - -# 0.18.3 (07-03-2017) - -- Remove `Gzip::Header` monkey patch since it's fixed in `Crystal 0.21.1`. - -# 0.18.2 (24-02-2017) - -- Fix [Gzip in Kemal Seems broken for static files](https://github.com/kemalcr/kemal/issues/316). This was caused by `Gzip::Writer` in `Crystal 0.21.0` and currently mitigated by monkey patching `Gzip::Header`. - -# 0.18.1 (21-02-2017) - -- Crystal 0.21.0 support -- Drop `multipart.cr` dependency. `multipart` support is now built-into Crystal <3 -- Since Crystal 0.21.0 comes built-in with `multipart` there are some improvements and deprecations. - -`meta` has been removed from `FileUpload` and it has the following properties - - + `tmpfile`: This is temporary file for file upload. Useful for saving the upload file. - + `filename`: File name of the file upload. (logo.png, images.zip e.g) - + `headers`: Headers for the file upload. - + `creation_time`: Creation time of the file upload. - + `modification_time`: Last Modification time of the file upload. - + `read_time`: Read time of the file upload. - + `size`: Size of the file upload. - - -# 0.18.0 (11-02-2017) - -- Simpler file upload. File uploads can now be access from `HTTP::Server::Context` like `env.params.files["filename"]`. - -`env.params.files["filename"]` has 5 methods - -- `tmpfile`: This is temporary file for file upload. Useful for saving the upload file. -- `tmpfile_path`: File path of `tmpfile`. -- `filename`: File name of the file upload. (logo.png, images.zip e.g) -- `meta`: Meta information for the file upload. -- `headers`: Headers for the file upload. - -Here's a fully working sample for reading a image file upload `image1` and saving it under `public/uploads`. - - ```crystal -post "/upload" do |env| - file = env.params.files["image1"].tmpfile - file_path = ::File.join [Kemal.config.public_folder, "uploads/", file.filename] - File.open(file_path, "w") do |f| - IO.copy(file, f) - end - "Upload ok" -end - ``` - -To test - -`curl -F "image1=@/Users/serdar/Downloads/kemal.png" http://localhost:3000/upload` - -- RF7233 support a.k.a file streaming. (https://github.com/kemalcr/kemal/pull/299) (thanks @denysvitali) - -- Update Radix to 0.3.7. Fixes https://github.com/kemalcr/kemal/issues/293 -- Configurable startup / shutdown logging. https://github.com/kemalcr/kemal/issues/291 and https://github.com/kemalcr/kemal/issues/292 (thanks @twisterghost). - -# 0.17.5 (09-01-2017) - -- Update multipart.cr to 0.1.2. Fixes #285 related to multipart.cr - -# 0.17.4 (24-12-2016) - -- Support for Crystal 0.20.3 -- Add `Kemal.stop`. Fixes #269. -- `HTTP::Handler` is not a class anymore, it's a module. See https://github.com/crystal-lang/crystal/releases/tag/0.20.3 - -# 0.17.3 (03-12-2016) - -- Handle missing 404 image. Fixes #263 -- Remove basic auth middleware from core and move to [kemalcr/kemal-basic-auth](https://github.com/kemalcr/kemal-basic-auth). - -# 0.17.2 (25-11-2016) - -- Use body.gets_to_end for parse_json. Fixes #260. -- Update Radix to 0.3.5 and lock pessimistically. (thanks @luislavena) - -# 0.17.1 (24-11-2016) - -- Treat `HTTP::Request` body as an `IO`. Fixes [#257](https://github.com/sdogruyol/kemal/issues/257) - -# 0.17.0 (23-11-2016) - -- Reimplemented Request middleware / filter routing. - -Now all requests will first go through the Middleware stack then Filters (before_*) and will finally reach the matching route. - -Which is illustrated as, - -``` -Request -> Middleware -> Filter -> Route -``` - -- Rename `return_with` as `halt`. -- Route declaration must start with `/`. Fixes [#242](https://github.com/sdogruyol/kemal/issues/242) -- Set default exception Content-Type to text/html. Fixes [#202](https://github.com/sdogruyol/kemal/issues/242) -- Add `only` and `exclude` paths for `Kemal::Handler`. This change requires that all handlers must inherit from `Kemal::Handler`. - -For example this handler will only work on `/` path. By default the HTTP method is `GET`. - - -```crystal -class OnlyHandler < Kemal::Handler - only ["/"] - - def call(env) - return call_next(env) unless only_match?(env) - puts "If the path is / i will be doing some processing here." - end -end -``` - -The handlers using `exclude` will work on the paths that isn't specified. For example this handler will work on any routes other than `/`. - -```crystal -class ExcludeHandler < Kemal::Handler - exclude ["/"] - - def call(env) - return call_next(env) unless only_match?(env) - puts "If the path is NOT / i will be doing some processing here." - end -end -``` - -- Close response on `halt`. (thanks @samueleaton). -- Update `Radix` to `v0.3.4`. -- `error` handler now also yields error. For example you can get the error mesasage like - -```crystal - error 500 do |env, err| - err.message - end -``` - -- Update `multipart.cr` to `v0.1.1` - -# 0.16.1 (12-10-2016) - -- Improved Multipart support with more info on parsed files. `parse_multipart(env)` now yields -an `UploadFile` object which has the following properties `field`,`data`,`meta`,`headers. - -```crystal -post "/upload" do |env| - parse_multipart(env) do |f| - image1 = f.data if f.field == "image1" - image2 = f.data if f.field == "image2" - puts f.meta - puts f.headers - "Upload complete" - end -end -``` - -# 0.16.0 - -- Multipart support <3 (thanks @RX14). Now you can handle file uploads. - -```crystal -post "/upload" do |env| - parse_multipart(env) do |field, data| - image1 = data if field == "image1" - image2 = data if field == "image2" - "Upload complete" - end -end -``` - -- Make session configurable. Now you can specify session name and expire time wit - -```crystal -Kemal.config.session["name"] = "your_app" -Kemal.config.session["expire_time"] = 48.hours -``` - -- Session now supports more types. (String, Int32, Float64, Bool) -- Add `gzip` helper to enable / disable gzip compression on responses. -- Static file caching with etag and gzip (thanks @crisward) -- `Kemal.run` now accepts port to listen. - -# 0.15.1 (05-09-2016) - -- Don't forget to call_next on NullLogHandler - -# 0.15.0 (03-09-2016) - -- Add context store -- `KEMAL_ENV` respects to `Kemal.config.env` and needs to be explicitly set. -- `Kemal::InitHandler` is introduced. Adds initial configuration, headers like `X-Powered-By`. -- Add `send_file` to helpers. -- Add mime types. -- Fix parsing JSON params when "charset" is present in "Content-Type" header. -- Use http-only cookie for session -- Inject STDOUT by default in CommonLogHandler diff --git a/samples/client/petstore/crystal/lib/kemal/LICENSE b/samples/client/petstore/crystal/lib/kemal/LICENSE deleted file mode 100644 index 4b1bea44c498..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2016 Serdar Doğruyol - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE diff --git a/samples/client/petstore/crystal/lib/kemal/README.md b/samples/client/petstore/crystal/lib/kemal/README.md deleted file mode 100644 index 068cc25a3850..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/README.md +++ /dev/null @@ -1,67 +0,0 @@ - -[![Kemal](https://avatars3.githubusercontent.com/u/15321198?v=3&s=200)](http://kemalcr.com) - -# Kemal - -Lightning Fast, Super Simple web framework. - -[![Build Status](https://travis-ci.org/kemalcr/kemal.svg?branch=master)](https://travis-ci.org/kemalcr/kemal) -[![Join the chat at https://gitter.im/sdogruyol/kemal](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sdogruyol/kemal?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -# Super Simple ⚡️ - -```ruby -require "kemal" - -# Matches GET "http://host:port/" -get "/" do - "Hello World!" -end - -# Creates a WebSocket handler. -# Matches "ws://host:port/socket" -ws "/socket" do |socket| - socket.send "Hello from Kemal!" -end - -Kemal.run -``` - -Start your application! - -``` -crystal src/kemal_sample.cr -``` -Go to *http://localhost:3000* - -Check [documentation](http://kemalcr.com) or [samples](https://github.com/kemalcr/kemal/tree/master/samples) for more. - -# Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - kemal: - github: kemalcr/kemal -``` - -See also [Getting Started](http://kemalcr.com/guide/). - -# Features - -- Support all REST verbs -- Websocket support -- Request/Response context, easy parameter handling -- Middleware support -- Built-in JSON support -- Built-in static file serving -- Built-in view templating via [Kilt](https://github.com/jeromegn/kilt) - -# Documentation - -You can read the documentation at the official site [kemalcr.com](http://kemalcr.com) - -## Thanks - -Thanks to Manas for their awesome work on [Frank](https://github.com/manastech/frank). diff --git a/samples/client/petstore/crystal/lib/kemal/lib b/samples/client/petstore/crystal/lib/kemal/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr b/samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr deleted file mode 100644 index c04f1d588d08..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/samples/hello_world.cr +++ /dev/null @@ -1,8 +0,0 @@ -require "kemal" - -# Set root. If not specified the default content_type is 'text' -get "/" do - "Hello Kemal!" -end - -Kemal.run diff --git a/samples/client/petstore/crystal/lib/kemal/samples/json_api.cr b/samples/client/petstore/crystal/lib/kemal/samples/json_api.cr deleted file mode 100644 index 0132c149a7d9..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/samples/json_api.cr +++ /dev/null @@ -1,11 +0,0 @@ -require "kemal" -require "json" - -# You can easily access the context and set content_type like 'application/json'. -# Look how easy to build a JSON serving API. -get "/" do |env| - env.response.content_type = "application/json" - {name: "Serdar", age: 27}.to_json -end - -Kemal.run diff --git a/samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr b/samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr deleted file mode 100644 index 61a0802984d5..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/samples/websocket_server.cr +++ /dev/null @@ -1,11 +0,0 @@ -require "kemal" - -ws "/" do |socket| - socket.send "Hello from Kemal!" - - socket.on_message do |message| - socket.send "Echo back from server #{message}" - end -end - -Kemal.run diff --git a/samples/client/petstore/crystal/lib/kemal/shard.yml b/samples/client/petstore/crystal/lib/kemal/shard.yml deleted file mode 100644 index 57ce8bbc6504..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/shard.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: kemal -version: 0.27.0 - -authors: - - Serdar Dogruyol - -dependencies: - radix: - github: luislavena/radix - version: ~> 0.3.8 - kilt: - github: jeromegn/kilt - version: ~> 0.4.0 - exception_page: - github: crystal-loot/exception_page - version: ~> 0.1.1 - -development_dependencies: - ameba: - github: crystal-ameba/ameba - version: ~> 0.12.0 - -crystal: 0.35.0 - -license: MIT diff --git a/samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr deleted file mode 100644 index 938dadf6753a..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/all_spec.cr +++ /dev/null @@ -1 +0,0 @@ -require "./*" diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr deleted file mode 100644 index 1cc8d41475fe..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/asset/hello.ecr +++ /dev/null @@ -1 +0,0 @@ -Hello <%= name %> diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr deleted file mode 100644 index 149b29448a57..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/asset/hello_with_content_for.ecr +++ /dev/null @@ -1,5 +0,0 @@ -Hello <%= name %> - -<% content_for "custom" do %> -

    Hello from otherside

    -<% end %> \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr deleted file mode 100644 index d493b59d919d..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout.ecr +++ /dev/null @@ -1 +0,0 @@ -<%= content %> diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr deleted file mode 100644 index f6cd6736e65d..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield.ecr +++ /dev/null @@ -1,6 +0,0 @@ - - - <%= content %> - <%= yield_content "custom" %> - - \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr b/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr deleted file mode 100644 index 3a82a7a65a98..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/asset/layout_with_yield_and_vars.ecr +++ /dev/null @@ -1,8 +0,0 @@ - - - <%= content %> - <%= yield_content "custom" %> - <%= var1 %> - <%= var2 %> - - \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr deleted file mode 100644 index 31a84380b04c..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/config_spec.cr +++ /dev/null @@ -1,61 +0,0 @@ -require "./spec_helper" - -describe "Config" do - it "sets default port to 3000" do - Kemal::Config.new.port.should eq 3000 - end - - it "sets default environment to development" do - Kemal::Config.new.env.should eq "development" - end - - it "sets environment to production" do - config = Kemal.config - config.env = "production" - config.env.should eq "production" - end - - it "sets default powered_by_header to true" do - Kemal::Config.new.powered_by_header.should be_true - end - - it "sets host binding" do - config = Kemal.config - config.host_binding = "127.0.0.1" - config.host_binding.should eq "127.0.0.1" - end - - it "adds a custom handler" do - config = Kemal.config - config.add_handler CustomTestHandler.new - Kemal.config.setup - config.handlers.size.should eq(7) - end - - it "toggles the shutdown message" do - config = Kemal.config - config.shutdown_message = false - config.shutdown_message.should eq false - config.shutdown_message = true - config.shutdown_message.should eq true - end - - it "adds custom options" do - config = Kemal.config - ARGV.push("--test") - ARGV.push("FOOBAR") - test_option = nil - - config.extra_options do |parser| - parser.on("--test TEST_OPTION", "Test an option") do |opt| - test_option = opt - end - end - Kemal::CLI.new ARGV - test_option.should eq("FOOBAR") - end - - it "gets the version from shards.yml" do - Kemal::VERSION.should_not be("") - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr deleted file mode 100644 index c972926652b7..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/context_spec.cr +++ /dev/null @@ -1,107 +0,0 @@ -require "./spec_helper" - -describe "Context" do - context "headers" do - it "sets content type" do - get "/" do |env| - env.response.content_type = "application/json" - "Hello" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.headers["Content-Type"].should eq("application/json") - end - - it "parses headers" do - get "/" do |env| - name = env.request.headers["name"] - "Hello #{name}" - end - headers = HTTP::Headers.new - headers["name"] = "kemal" - request = HTTP::Request.new("GET", "/", headers) - client_response = call_request_on_app(request) - client_response.body.should eq "Hello kemal" - end - - it "sets response headers" do - get "/" do |env| - env.response.headers.add "Accept-Language", "tr" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.headers["Accept-Language"].should eq "tr" - end - end - - context "storage" do - it "can store primitive types" do - before_get "/" do |env| - env.set "before_get", "Kemal" - env.set "before_get_int", 123 - env.set "before_get_float", 3.5 - end - - get "/" do |env| - { - before_get: env.get("before_get"), - before_get_int: env.get("before_get_int"), - before_get_float: env.get("before_get_float"), - } - end - - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::FilterHandler::INSTANCE.call(context) - Kemal::RouteHandler::INSTANCE.call(context) - - context.get("before_get").should eq "Kemal" - context.get("before_get_int").should eq 123 - context.get("before_get_float").should eq 3.5 - end - - it "can store custom types" do - before_get "/" do |env| - t = TestContextStorageType.new - t.id = 32 - a = AnotherContextStorageType.new - - env.set "before_get_context_test", t - env.set "another_context_test", a - end - - get "/" do |env| - { - before_get_context_test: env.get("before_get_context_test"), - another_context_test: env.get("another_context_test"), - } - end - - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::FilterHandler::INSTANCE.call(context) - Kemal::RouteHandler::INSTANCE.call(context) - - context.get("before_get_context_test").as(TestContextStorageType).id.should eq 32 - context.get("another_context_test").as(AnotherContextStorageType).name.should eq "kemal-context" - end - - it "fetches non-existent keys from store with get?" do - get "/" { } - - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::FilterHandler::INSTANCE.call(context) - Kemal::RouteHandler::INSTANCE.call(context) - - context.get?("non_existent_key").should be_nil - context.get?("another_non_existent_key").should be_nil - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr deleted file mode 100644 index b9519e9dd4a7..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/exception_handler_spec.cr +++ /dev/null @@ -1,115 +0,0 @@ -require "./spec_helper" - -describe "Kemal::ExceptionHandler" do - it "renders 404 on route not found" do - get "/" do - "Hello" - end - - request = HTTP::Request.new("GET", "/asd") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::ExceptionHandler::INSTANCE.call(context) - response.close - io.rewind - response = HTTP::Client::Response.from_io(io, decompress: false) - response.status_code.should eq 404 - end - - it "renders custom error" do - error 403 do - "403 error" - end - get "/" do |env| - env.response.status_code = 403 - end - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE - Kemal::ExceptionHandler::INSTANCE.call(context) - response.close - io.rewind - response = HTTP::Client::Response.from_io(io, decompress: false) - response.status_code.should eq 403 - response.headers["Content-Type"].should eq "text/html" - response.body.should eq "403 error" - end - - it "renders custom 500 error" do - error 500 do - "Something happened" - end - get "/" do |env| - env.response.status_code = 500 - end - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE - Kemal::ExceptionHandler::INSTANCE.call(context) - response.close - io.rewind - response = HTTP::Client::Response.from_io(io, decompress: false) - response.status_code.should eq 500 - response.headers["Content-Type"].should eq "text/html" - response.body.should eq "Something happened" - end - - it "keeps the specified error Content-Type" do - error 500 do - "Something happened" - end - get "/" do |env| - env.response.content_type = "application/json" - env.response.status_code = 500 - end - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE - Kemal::ExceptionHandler::INSTANCE.call(context) - response.close - io.rewind - response = HTTP::Client::Response.from_io(io, decompress: false) - response.status_code.should eq 500 - response.headers["Content-Type"].should eq "application/json" - response.body.should eq "Something happened" - end - - it "renders custom error with env and error" do - error 500 do |_, err| - err.message - end - get "/" do |env| - env.response.content_type = "application/json" - env.response.status_code = 500 - end - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE - Kemal::ExceptionHandler::INSTANCE.call(context) - response.close - io.rewind - response = HTTP::Client::Response.from_io(io, decompress: false) - response.status_code.should eq 500 - response.headers["Content-Type"].should eq "application/json" - response.body.should eq "Rendered error with 500" - end - - it "does not do anything on a closed io" do - get "/" do |env| - halt env, status_code: 404 - end - - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq 404 - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr deleted file mode 100644 index 9b1019fcc368..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/handler_spec.cr +++ /dev/null @@ -1,161 +0,0 @@ -require "./spec_helper" - -class CustomTestHandler < Kemal::Handler - def call(env) - env.response << "Kemal" - call_next env - end -end - -class OnlyHandler < Kemal::Handler - only ["/only"] - - def call(env) - return call_next(env) unless only_match?(env) - env.response.print "Only" - call_next env - end -end - -class ExcludeHandler < Kemal::Handler - exclude ["/exclude"] - - def call(env) - return call_next(env) if exclude_match?(env) - env.response.print "Exclude" - call_next env - end -end - -class PostOnlyHandler < Kemal::Handler - only ["/only", "/route1", "/route2"], "POST" - - def call(env) - return call_next(env) unless only_match?(env) - env.response.print "Only" - call_next env - end -end - -class PostExcludeHandler < Kemal::Handler - exclude ["/exclude"], "POST" - - def call(env) - return call_next(env) if exclude_match?(env) - env.response.print "Exclude" - call_next env - end -end - -class ExcludeHandlerPercentW < Kemal::Handler - exclude %w[/exclude] - - def call(env) - return call_next(env) if exclude_match?(env) - env.response.print "Exclude" - call_next env - end -end - -class PostOnlyHandlerPercentW < Kemal::Handler - only %w[/only /route1 /route2], "POST" - - def call(env) - return call_next(env) unless only_match?(env) - env.response.print "Only" - call_next env - end -end - -describe "Handler" do - it "adds custom handler before before_*" do - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("GET", "/", :before) do |env| - env.response << " is" - end - - filter_middleware._add_route_filter("GET", "/", :before) do |env| - env.response << " so" - end - add_handler CustomTestHandler.new - - get "/" do - " Great" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq(200) - client_response.body.should eq("Kemal is so Great") - end - - it "runs specified only_routes in middleware" do - get "/only" do - "Get" - end - add_handler OnlyHandler.new - request = HTTP::Request.new("GET", "/only") - client_response = call_request_on_app(request) - client_response.body.should eq "OnlyGet" - end - - it "doesn't run specified exclude_routes in middleware" do - get "/" do - "Get" - end - get "/exclude" do - "Exclude" - end - add_handler ExcludeHandler.new - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.body.should eq "ExcludeGet" - end - - it "runs specified only_routes with method in middleware" do - post "/only" do - "Post" - end - get "/only" do - "Get" - end - add_handler PostOnlyHandler.new - request = HTTP::Request.new("POST", "/only") - client_response = call_request_on_app(request) - client_response.body.should eq "OnlyPost" - end - - it "doesn't run specified exclude_routes with method in middleware" do - post "/exclude" do - "Post" - end - post "/only" do - "Post" - end - add_handler PostOnlyHandler.new - add_handler PostExcludeHandler.new - request = HTTP::Request.new("POST", "/only") - client_response = call_request_on_app(request) - client_response.body.should eq "OnlyExcludePost" - end - - it "adds a handler at given position" do - post_handler = PostOnlyHandler.new - add_handler post_handler, 1 - Kemal.config.setup - Kemal.config.handlers[1].should eq post_handler - end - - it "assigns custom handlers" do - post_only_handler = PostOnlyHandler.new - post_exclude_handler = PostExcludeHandler.new - Kemal.config.handlers = [post_only_handler, post_exclude_handler] - Kemal.config.handlers.should eq [post_only_handler, post_exclude_handler] - end - - it "is able to use %w in macros" do - post_only_handler = PostOnlyHandlerPercentW.new - exclude_handler = ExcludeHandlerPercentW.new - Kemal.config.handlers = [post_only_handler, exclude_handler] - Kemal.config.handlers.should eq [post_only_handler, exclude_handler] - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr deleted file mode 100644 index 4d45a666d4df..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/helpers_spec.cr +++ /dev/null @@ -1,173 +0,0 @@ -require "./spec_helper" -require "./handler_spec" - -describe "Macros" do - describe "#public_folder" do - it "sets public folder" do - public_folder "/some/path/to/folder" - Kemal.config.public_folder.should eq("/some/path/to/folder") - end - end - - describe "#add_handler" do - it "adds a custom handler" do - add_handler CustomTestHandler.new - Kemal.config.setup - Kemal.config.handlers.size.should eq 7 - end - end - - describe "#logging" do - it "sets logging status" do - logging false - Kemal.config.logging.should eq false - end - - it "sets a custom logger" do - config = Kemal::Config::INSTANCE - logger CustomLogHandler.new - config.logger.should be_a(CustomLogHandler) - end - end - - describe "#halt" do - it "can break block with halt macro" do - get "/non-breaking" do - "hello" - "world" - end - request = HTTP::Request.new("GET", "/non-breaking") - client_response = call_request_on_app(request) - client_response.status_code.should eq(200) - client_response.body.should eq("world") - - get "/breaking" do |env| - halt env, 404, "hello" - "world" - end - request = HTTP::Request.new("GET", "/breaking") - client_response = call_request_on_app(request) - client_response.status_code.should eq(404) - client_response.body.should eq("hello") - end - - it "can break block with halt macro using default values" do - get "/" do |env| - halt env - "world" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq(200) - client_response.body.should eq("") - end - end - - describe "#callbacks" do - it "can break block with halt macro from before_* callback" do - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("GET", "/", :before) do |env| - halt env, status_code: 400, response: "Missing origin." - end - - get "/" do |_env| - "Hello world" - end - - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq(400) - client_response.body.should eq("Missing origin.") - end - end - - describe "#headers" do - it "can add headers" do - get "/headers" do |env| - env.response.headers.add "Content-Type", "image/png" - headers env, { - "Access-Control-Allow-Origin" => "*", - "Content-Type" => "text/plain", - } - end - request = HTTP::Request.new("GET", "/headers") - response = call_request_on_app(request) - response.headers["Access-Control-Allow-Origin"].should eq("*") - response.headers["Content-Type"].should eq("text/plain") - end - end - - describe "#send_file" do - it "sends file with given path and default mime-type" do - get "/" do |env| - send_file env, "./spec/asset/hello.ecr" - end - - request = HTTP::Request.new("GET", "/") - response = call_request_on_app(request) - response.status_code.should eq(200) - response.headers["Content-Type"].should eq("application/octet-stream") - response.headers["Content-Length"].should eq("18") - end - - it "sends file with given path and given mime-type" do - get "/" do |env| - send_file env, "./spec/asset/hello.ecr", "image/jpeg" - end - - request = HTTP::Request.new("GET", "/") - response = call_request_on_app(request) - response.status_code.should eq(200) - response.headers["Content-Type"].should eq("image/jpeg") - response.headers["Content-Length"].should eq("18") - end - - it "sends file with binary stream" do - get "/" do |env| - send_file env, "Serdar".to_slice - end - - request = HTTP::Request.new("GET", "/") - response = call_request_on_app(request) - response.status_code.should eq(200) - response.headers["Content-Type"].should eq("application/octet-stream") - response.headers["Content-Length"].should eq("6") - end - - it "sends file with given path and given filename" do - get "/" do |env| - send_file env, "./spec/asset/hello.ecr", filename: "image.jpg" - end - - request = HTTP::Request.new("GET", "/") - response = call_request_on_app(request) - response.status_code.should eq(200) - response.headers["Content-Disposition"].should eq("attachment; filename=\"image.jpg\"") - end - end - - describe "#gzip" do - it "adds HTTP::CompressHandler to handlers" do - gzip true - Kemal.config.setup - Kemal.config.handlers[4].should be_a(HTTP::CompressHandler) - end - end - - describe "#serve_static" do - it "should disable static file hosting" do - serve_static false - Kemal.config.serve_static.should eq false - end - - it "should disble enable gzip and dir_listing" do - serve_static({"gzip" => true, "dir_listing" => true}) - conf = Kemal.config.serve_static - conf.is_a?(Hash).should eq true - if conf.is_a?(Hash) - conf["gzip"].should eq true - conf["dir_listing"].should eq true - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr deleted file mode 100644 index 601bbc1d1df6..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/init_handler_spec.cr +++ /dev/null @@ -1,32 +0,0 @@ -require "./spec_helper" - -describe "Kemal::InitHandler" do - it "initializes context with Content-Type: text/html" do - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::InitHandler::INSTANCE.next = ->(_context : HTTP::Server::Context) {} - Kemal::InitHandler::INSTANCE.call(context) - context.response.headers["Content-Type"].should eq "text/html" - end - - it "initializes context with X-Powered-By: Kemal" do - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::InitHandler::INSTANCE.call(context) - context.response.headers["X-Powered-By"].should eq "Kemal" - end - - it "does not initialize context with X-Powered-By: Kemal if disabled" do - Kemal.config.powered_by_header = false - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - Kemal::InitHandler::INSTANCE.call(context) - context.response.headers["X-Powered-By"]?.should be_nil - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr deleted file mode 100644 index 5ee9c86323c7..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/log_handler_spec.cr +++ /dev/null @@ -1,21 +0,0 @@ -require "./spec_helper" - -describe "Kemal::LogHandler" do - it "logs to the given IO" do - io = IO::Memory.new - logger = Kemal::LogHandler.new io - logger.write "Something" - io.to_s.should eq "Something" - end - - it "creates log message for each request" do - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - context_io = IO::Memory.new - response = HTTP::Server::Response.new(context_io) - context = HTTP::Server::Context.new(request, response) - logger = Kemal::LogHandler.new io - logger.call(context) - io.to_s.should_not be nil - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr deleted file mode 100644 index 9bc2564c100c..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/middleware/filters_spec.cr +++ /dev/null @@ -1,190 +0,0 @@ -require "../spec_helper" - -describe "Kemal::FilterHandler" do - it "executes code before home request" do - test_filter = FilterTest.new - test_filter.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("GET", "/greetings", :before) { test_filter.modified = "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - - test_filter.modified.should eq("false") - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - end - - it "executes code before GET home request but not POST home request" do - test_filter = FilterTest.new - test_filter.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("GET", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - kemal.add_route "POST", "/greetings" { test_filter.modified } - - test_filter.modified.should eq("false") - - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - - request = HTTP::Request.new("POST", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - end - - it "executes code before all GET/POST home request" do - test_filter = FilterTest.new - test_filter.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - filter_middleware._add_route_filter("GET", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - filter_middleware._add_route_filter("POST", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - kemal.add_route "POST", "/greetings" { test_filter.modified } - - test_filter.modified.should eq("false") - - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("false") - - request = HTTP::Request.new("POST", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("false") - end - - it "executes code after home request" do - test_filter = FilterTest.new - test_filter.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("GET", "/greetings", :after) { test_filter.modified = "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - - test_filter.modified.should eq("false") - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - end - - it "executes code after GET home request but not POST home request" do - test_filter = FilterTest.new - test_filter.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("GET", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - kemal.add_route "POST", "/greetings" { test_filter.modified } - - test_filter.modified.should eq("false") - - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - - request = HTTP::Request.new("POST", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - end - - it "executes code after all GET/POST home request" do - test_filter = FilterTest.new - test_filter.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("ALL", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - filter_middleware._add_route_filter("GET", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - filter_middleware._add_route_filter("POST", "/greetings", :after) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - kemal.add_route "POST", "/greetings" { test_filter.modified } - - test_filter.modified.should eq("false") - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("false") - - request = HTTP::Request.new("POST", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("false") - end - - it "executes 3 differents blocks after all request" do - test_filter = FilterTest.new - test_filter.modified = "false" - test_filter_second = FilterTest.new - test_filter_second.modified = "false" - test_filter_third = FilterTest.new - test_filter_third.modified = "false" - - filter_middleware = Kemal::FilterHandler.new - filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } - filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter_second.modified = test_filter_second.modified == "true" ? "false" : "true" } - filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter_third.modified = test_filter_third.modified == "true" ? "false" : "true" } - - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "GET", "/greetings" { test_filter.modified } - kemal.add_route "POST", "/greetings" { test_filter_second.modified } - kemal.add_route "PUT", "/greetings" { test_filter_third.modified } - - test_filter.modified.should eq("false") - test_filter_second.modified.should eq("false") - test_filter_third.modified.should eq("false") - request = HTTP::Request.new("GET", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - - request = HTTP::Request.new("POST", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("false") - - request = HTTP::Request.new("PUT", "/greetings") - create_request_and_return_io_and_context(filter_middleware, request) - io_with_context = create_request_and_return_io_and_context(kemal, request)[0] - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.body.should eq("true") - end -end - -class FilterTest - property modified : String? -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr deleted file mode 100644 index d63a2298c986..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/param_parser_spec.cr +++ /dev/null @@ -1,204 +0,0 @@ -require "./spec_helper" - -describe "ParamParser" do - it "parses query params" do - Route.new "POST", "/" do |env| - hasan = env.params.query["hasan"] - "Hello #{hasan}" - end - request = HTTP::Request.new("POST", "/?hasan=cemal") - query_params = Kemal::ParamParser.new(request).query - query_params["hasan"].should eq "cemal" - end - - it "parses multiple values for query params" do - Route.new "POST", "/" do |env| - hasan = env.params.query["hasan"] - "Hello #{hasan}" - end - request = HTTP::Request.new("POST", "/?hasan=cemal&hasan=lamec") - query_params = Kemal::ParamParser.new(request).query - query_params.fetch_all("hasan").should eq ["cemal", "lamec"] - end - - it "parses url params" do - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "POST", "/hello/:hasan" do |env| - "hello #{env.params.url["hasan"]}" - end - request = HTTP::Request.new("POST", "/hello/cemal") - # Radix tree MUST be run to parse url params. - context = create_request_and_return_io_and_context(kemal, request)[1] - url_params = Kemal::ParamParser.new(request, context.route_lookup.params).url - url_params["hasan"].should eq "cemal" - end - - it "decodes url params" do - kemal = Kemal::RouteHandler::INSTANCE - kemal.add_route "POST", "/hello/:email/:money/:spanish" do |env| - email = env.params.url["email"] - money = env.params.url["money"] - spanish = env.params.url["spanish"] - "Hello, #{email}. You have #{money}. The spanish word of the day is #{spanish}." - end - request = HTTP::Request.new("POST", "/hello/sam%2Bspec%40gmail.com/%2419.99/a%C3%B1o") - # Radix tree MUST be run to parse url params. - context = create_request_and_return_io_and_context(kemal, request)[1] - url_params = Kemal::ParamParser.new(request, context.route_lookup.params).url - url_params["email"].should eq "sam+spec@gmail.com" - url_params["money"].should eq "$19.99" - url_params["spanish"].should eq "año" - end - - it "parses request body" do - Route.new "POST", "/" do |env| - name = env.params.query["name"] - age = env.params.query["age"] - hasan = env.params.body["hasan"] - "Hello #{name} #{hasan} #{age}" - end - - request = HTTP::Request.new( - "POST", - "/?hasan=cemal", - body: "name=serdar&age=99", - headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded"}, - ) - - query_params = Kemal::ParamParser.new(request).query - {"hasan" => "cemal"}.each do |k, v| - query_params[k].should eq(v) - end - - body_params = Kemal::ParamParser.new(request).body - {"name" => "serdar", "age" => "99"}.each do |k, v| - body_params[k].should eq(v) - end - end - - it "parses multiple values in request body" do - Route.new "POST", "/" do |env| - hasan = env.params.body["hasan"] - "Hello #{hasan}" - end - - request = HTTP::Request.new( - "POST", - "/", - body: "hasan=cemal&hasan=lamec", - headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded"}, - ) - - body_params = Kemal::ParamParser.new(request).body - body_params.fetch_all("hasan").should eq(["cemal", "lamec"]) - end - - context "when content type is application/json" do - it "parses request body" do - Route.new "POST", "/" { } - - request = HTTP::Request.new( - "POST", - "/", - body: "{\"name\": \"Serdar\"}", - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - - json_params = Kemal::ParamParser.new(request).json - json_params.should eq({"name" => "Serdar"}) - end - - it "parses request body when passed charset" do - Route.new "POST", "/" { } - - request = HTTP::Request.new( - "POST", - "/", - body: "{\"name\": \"Serdar\"}", - headers: HTTP::Headers{"Content-Type" => "application/json; charset=utf-8"}, - ) - - json_params = Kemal::ParamParser.new(request).json - json_params.should eq({"name" => "Serdar"}) - end - - it "parses request body for array" do - Route.new "POST", "/" { } - - request = HTTP::Request.new( - "POST", - "/", - body: "[1]", - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - - json_params = Kemal::ParamParser.new(request).json - json_params.should eq({"_json" => [1]}) - end - - it "parses request body and query params" do - Route.new "POST", "/" { } - - request = HTTP::Request.new( - "POST", - "/?foo=bar", - body: "[1]", - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - - query_params = Kemal::ParamParser.new(request).query - {"foo" => "bar"}.each do |k, v| - query_params[k].should eq(v) - end - - json_params = Kemal::ParamParser.new(request).json - json_params.should eq({"_json" => [1]}) - end - - it "handles no request body" do - Route.new "GET", "/" { } - - request = HTTP::Request.new( - "GET", - "/", - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - - url_params = Kemal::ParamParser.new(request).url - url_params.should eq({} of String => String) - - query_params = Kemal::ParamParser.new(request).query - query_params.to_s.should eq("") - - body_params = Kemal::ParamParser.new(request).body - body_params.to_s.should eq("") - - json_params = Kemal::ParamParser.new(request).json - json_params.should eq({} of String => Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Any) | Array(JSON::Any)) - end - end - - context "when content type is incorrect" do - it "does not parse request body" do - Route.new "POST", "/" do |env| - name = env.params.body["name"] - age = env.params.body["age"] - hasan = env.params.query["hasan"] - "Hello #{name} #{hasan} #{age}" - end - - request = HTTP::Request.new( - "POST", - "/?hasan=cemal", - body: "name=serdar&age=99", - headers: HTTP::Headers{"Content-Type" => "text/plain"}, - ) - - query_params = Kemal::ParamParser.new(request).query - query_params["hasan"].should eq("cemal") - - body_params = Kemal::ParamParser.new(request).body - body_params.to_s.should eq("") - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr deleted file mode 100644 index 93cf0f06cddc..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/route_handler_spec.cr +++ /dev/null @@ -1,146 +0,0 @@ -require "./spec_helper" - -describe "Kemal::RouteHandler" do - it "routes" do - get "/" do - "hello" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.body.should eq("hello") - end - - it "routes with long response body" do - long_response_body = "string" * 10_000 - - get "/" do - long_response_body - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.body.should eq(long_response_body) - end - - it "routes should only return strings" do - get "/" do - 100 - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.body.should eq("") - end - - it "routes request with query string" do - get "/" do |env| - "hello #{env.params.query["message"]}" - end - request = HTTP::Request.new("GET", "/?message=world") - client_response = call_request_on_app(request) - client_response.body.should eq("hello world") - end - - it "routes request with multiple query strings" do - get "/" do |env| - "hello #{env.params.query["message"]} time #{env.params.query["time"]}" - end - request = HTTP::Request.new("GET", "/?message=world&time=now") - client_response = call_request_on_app(request) - client_response.body.should eq("hello world time now") - end - - it "route parameter has more precedence than query string arguments" do - get "/:message" do |env| - "hello #{env.params.url["message"]}" - end - request = HTTP::Request.new("GET", "/world?message=coco") - client_response = call_request_on_app(request) - client_response.body.should eq("hello world") - end - - it "parses simple JSON body" do - post "/" do |env| - name = env.params.json["name"] - age = env.params.json["age"] - "Hello #{name} Age #{age}" - end - - json_payload = {"name": "Serdar", "age": 26} - request = HTTP::Request.new( - "POST", - "/", - body: json_payload.to_json, - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - client_response = call_request_on_app(request) - client_response.body.should eq("Hello Serdar Age 26") - end - - it "parses JSON with string array" do - post "/" do |env| - skills = env.params.json["skills"].as(Array) - "Skills #{skills.each.join(',')}" - end - - json_payload = {"skills": ["ruby", "crystal"]} - request = HTTP::Request.new( - "POST", - "/", - body: json_payload.to_json, - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - client_response = call_request_on_app(request) - client_response.body.should eq("Skills ruby,crystal") - end - - it "parses JSON with json object array" do - post "/" do |env| - skills = env.params.json["skills"].as(Array) - skills_from_languages = skills.map do |skill| - skill["language"] - end - "Skills #{skills_from_languages.each.join(',')}" - end - - json_payload = {"skills": [{"language": "ruby"}, {"language": "crystal"}]} - request = HTTP::Request.new( - "POST", - "/", - body: json_payload.to_json, - headers: HTTP::Headers{"Content-Type" => "application/json"}, - ) - - client_response = call_request_on_app(request) - client_response.body.should eq("Skills ruby,crystal") - end - - it "can process HTTP HEAD requests for defined GET routes" do - get "/" do - "Hello World from GET" - end - request = HTTP::Request.new("HEAD", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq(200) - end - - it "redirects user to provided url" do - get "/" do |env| - env.redirect "/login" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq(302) - client_response.body.should eq("") - client_response.headers.has_key?("Location").should eq(true) - end - - it "redirects with body" do - get "/" do |env| - env.redirect "/login", body: "Redirecting to /login" - end - request = HTTP::Request.new("GET", "/") - client_response = call_request_on_app(request) - client_response.status_code.should eq(302) - client_response.body.should eq("Redirecting to /login") - client_response.headers.has_key?("Location").should eq(true) - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr deleted file mode 100644 index 7634d51dc763..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/route_spec.cr +++ /dev/null @@ -1,25 +0,0 @@ -require "./spec_helper" - -describe "Route" do - describe "match?" do - it "matches the correct route" do - get "/route1" do - "Route 1" - end - get "/route2" do - "Route 2" - end - request = HTTP::Request.new("GET", "/route2") - client_response = call_request_on_app(request) - client_response.body.should eq("Route 2") - end - - it "doesn't allow a route declaration start without /" do - expect_raises Kemal::Exceptions::InvalidPathStartException, "Route declaration get \"route\" needs to start with '/', should be get \"/route\"" do - get "route" do - "Route 1" - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr deleted file mode 100644 index ec89d233c8f6..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/run_spec.cr +++ /dev/null @@ -1,46 +0,0 @@ -require "./spec_helper" - -private def run(code) - code = <<-CR - require "./src/kemal" - #{code} - CR - - stdout = IO::Memory.new - stderr = IO::Memory.new - status = Process.new("crystal", ["eval"], input: IO::Memory.new(code), output: stdout, error: stderr).wait - fail(stderr.to_s) unless status.success? - stdout.to_s -end - -describe "Run" do - it "runs a code block after starting" do - run(<<-CR).should eq "started\nstopped\n" - Kemal.config.env = "test" - Kemal.run do - puts "started" - Kemal.stop - puts "stopped" - end - CR - end - - it "runs without a block being specified" do - run(<<-CR).should eq "[test] Kemal is ready to lead at http://0.0.0.0:3000\ntrue\n" - Kemal.config.env = "test" - Kemal.run - puts Kemal.config.running - CR - end - - it "allows custom HTTP::Server bind" do - run(<<-CR).should eq "[test] Kemal is ready to lead at http://127.0.0.1:3000, http://0.0.0.0:3001\n" - Kemal.config.env = "test" - Kemal.run do |config| - server = config.server.not_nil! - server.bind_tcp "127.0.0.1", 3000, reuse_port: true - server.bind_tcp "0.0.0.0", 3001, reuse_port: true - end - CR - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr deleted file mode 100644 index 803aacf5a495..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/spec_helper.cr +++ /dev/null @@ -1,91 +0,0 @@ -require "spec" -require "../src/*" - -include Kemal - -class CustomLogHandler < Kemal::BaseLogHandler - def call(env) - call_next env - end - - def write(message) - end -end - -class TestContextStorageType - property id - @id = 1 - - def to_s - @id - end -end - -class AnotherContextStorageType - property name - @name = "kemal-context" -end - -add_context_storage_type(TestContextStorageType) -add_context_storage_type(AnotherContextStorageType) - -def create_request_and_return_io_and_context(handler, request) - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - handler.call(context) - response.close - io.rewind - {io, context} -end - -def create_ws_request_and_return_io_and_context(handler, request) - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - begin - handler.call context - rescue IO::Error - # Raises because the IO::Memory is empty - end - {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} - response.upgrade_handler.try &.call(io) - {% end %} - io.rewind - {io, context} -end - -def call_request_on_app(request) - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - main_handler = build_main_handler - main_handler.call context - response.close - io.rewind - HTTP::Client::Response.from_io(io, decompress: false) -end - -def build_main_handler - Kemal.config.setup - main_handler = Kemal.config.handlers.first - current_handler = main_handler - Kemal.config.handlers.each do |handler| - current_handler.next = handler - current_handler = handler - end - main_handler -end - -Spec.before_each do - config = Kemal.config - config.env = "development" - config.logging = false -end - -Spec.after_each do - Kemal.config.clear - Kemal::RouteHandler::INSTANCE.routes = Radix::Tree(Route).new - Kemal::RouteHandler::INSTANCE.cached_routes = Hash(String, Radix::Result(Route)).new - Kemal::WebSocketHandler::INSTANCE.routes = Radix::Tree(WebSocket).new -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt deleted file mode 100644 index 36281ae8f9f3..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/bigger.txt +++ /dev/null @@ -1,9 +0,0 @@ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse posuere cursus consectetur. Donec mauris lorem, sodales a eros a, ultricies convallis ante. Quisque elementum lacus purus, sagittis mollis justo dignissim ac. Suspendisse potenti. Cras non mauris accumsan mi porttitor congue. Quisque posuere aliquam tellus sit amet ultrices. Sed at tortor sed libero fringilla luctus vitae quis magna. In maximus congue felis, et porta tortor egestas sed. Phasellus orci eros, finibus sed ipsum eget, euismod bibendum nisl. Etiam ultrices facilisis diam in gravida. Praesent lobortis leo vitae aliquet volutpat. Praesent vel blandit risus. In suscipit eget nunc at ultrices. Proin dapibus feugiat diam ut tincidunt. Donec lectus diam, ornare ut consequat nec, gravida sit amet metus. - -Nunc a viverra urna, quis ullamcorper augue. Morbi posuere auctor nibh, tempor luctus massa mollis laoreet. Pellentesque sagittis leo eu felis interdum finibus. Pellentesque porttitor lobortis arcu, eu mollis dui iaculis nec. Vestibulum sit amet sodales erat. Nullam quis mi massa. Suspendisse sit amet elit auctor, feugiat ipsum a, placerat metus. Vestibulum quis felis a lectus blandit aliquam. Nam consectetur iaculis nulla. Mauris sit amet condimentum erat, in vestibulum dui. Nullam nec mattis tortor, non viverra nunc. Proin eget congue augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed ut hendrerit nulla. Etiam cursus sagittis metus, et feugiat ligula molestie sit amet. Aliquam laoreet auctor sagittis. - -Aliquam tempor urna non consectetur tincidunt. Maecenas porttitor augue diam, ac lobortis nulla suscipit eget. Ut quis lacus facilisis, euismod lacus non, ullamcorper urna. Cras pretium fringilla pharetra. Praesent sed nunc at elit vulputate elementum. Suspendisse ac molestie nunc, sit amet consectetur nunc. Cras placerat ligula tortor, non bibendum massa tempus ut. Etiam eros erat, gravida id felis eget, congue suscipit ipsum. Sed condimentum erat at facilisis dictum. Cras venenatis vitae turpis vitae sagittis. Proin id posuere est, non ornare sem. Donec vitae sollicitudin dolor, a pulvinar ex. Integer porta velit lectus, et imperdiet enim commodo a. - -Donec sit amet ipsum tempus, tincidunt neque eget, luctus massa. Praesent vel nulla pretium, bibendum enim a, pulvinar enim. Vestibulum non libero eu est dignissim cursus. Nullam commodo tellus imperdiet feugiat placerat. Sed sed dolor ut nibh blandit maximus ac eget neque. Ut sit amet augue maximus, lacinia eros non, faucibus eros. Suspendisse ac bibendum libero, eu lobortis nulla. Mauris arcu nulla, tempus eu varius eu, bibendum at nibh. Donec id libero consequat, volutpat ex vitae, molestie velit. Aliquam aliquam sem ac arcu pellentesque, placerat bibendum enim dapibus. Duis consectetur ligula non placerat euismod. - -Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin commodo ullamcorper venenatis. Cras ac lorem sit amet augue varius convallis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris dolor nisi, efficitur id aliquet ut, ultricies sed elit. Proin ultricies turpis dolor, in auctor velit aliquet nec. Praesent vehicula aliquam viverra. Suspendisse potenti. Donec aliquet iaculis ultricies. Proin dignissim vitae nisl at rutrum. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html deleted file mode 100644 index 32d977f9cc9f..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - title - - - - - - - \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt b/samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt deleted file mode 100644 index 9db7df02b602..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/static/dir/test.txt +++ /dev/null @@ -1,2 +0,0 @@ -hello -world \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr deleted file mode 100644 index 1aac161bb7c9..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/static_file_handler_spec.cr +++ /dev/null @@ -1,153 +0,0 @@ -require "./spec_helper" - -private def handle(request, fallthrough = true) - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - handler = Kemal::StaticFileHandler.new "#{__DIR__}/static", fallthrough - handler.call context - response.close - io.rewind - HTTP::Client::Response.from_io(io) -end - -describe Kemal::StaticFileHandler do - file = File.open "#{__DIR__}/static/dir/test.txt" - file_size = file.size - - it "should serve a file with content type and etag" do - response = handle HTTP::Request.new("GET", "/dir/test.txt") - response.status_code.should eq(200) - response.headers["Content-Type"].should eq "text/plain" - response.headers["Etag"].should contain "W/\"" - response.body.should eq(File.read("#{__DIR__}/static/dir/test.txt")) - end - - it "should respond with 304 if file has not changed" do - response = handle HTTP::Request.new("GET", "/dir/test.txt") - response.status_code.should eq(200) - etag = response.headers["Etag"] - - headers = HTTP::Headers{"If-None-Match" => etag} - response = handle HTTP::Request.new("GET", "/dir/test.txt", headers) - response.headers["Content-Type"]?.should be_nil - response.status_code.should eq(304) - response.body.should eq "" - end - - it "should not list directory's entries" do - serve_static({"gzip" => true, "dir_listing" => false}) - response = handle HTTP::Request.new("GET", "/dir/") - response.status_code.should eq(404) - end - - it "should list directory's entries when config is set" do - serve_static({"gzip" => true, "dir_listing" => true}) - response = handle HTTP::Request.new("GET", "/dir/") - response.status_code.should eq(200) - response.body.should match(/test.txt/) - end - - it "should gzip a file if config is true, headers accept gzip and file is > 880 bytes" do - serve_static({"gzip" => true, "dir_listing" => true}) - headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} - response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers) - response.status_code.should eq(200) - response.headers["Content-Encoding"].should eq "gzip" - end - - it "should not gzip a file if config is true, headers accept gzip and file is < 880 bytes" do - serve_static({"gzip" => true, "dir_listing" => true}) - headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} - response = handle HTTP::Request.new("GET", "/dir/test.txt", headers) - response.status_code.should eq(200) - response.headers["Content-Encoding"]?.should be_nil - end - - it "should not gzip a file if config is false, headers accept gzip and file is > 880 bytes" do - serve_static({"gzip" => false, "dir_listing" => true}) - headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} - response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers) - response.status_code.should eq(200) - response.headers["Content-Encoding"]?.should be_nil - end - - it "should not serve a not found file" do - response = handle HTTP::Request.new("GET", "/not_found_file.txt") - response.status_code.should eq(404) - end - - it "should not serve a not found directory" do - response = handle HTTP::Request.new("GET", "/not_found_dir/") - response.status_code.should eq(404) - end - - it "should not serve a file as directory" do - response = handle HTTP::Request.new("GET", "/dir/test.txt/") - response.status_code.should eq(404) - end - - it "should handle only GET and HEAD method" do - %w(GET HEAD).each do |method| - response = handle HTTP::Request.new(method, "/dir/test.txt") - response.status_code.should eq(200) - end - - %w(POST PUT DELETE).each do |method| - response = handle HTTP::Request.new(method, "/dir/test.txt") - response.status_code.should eq(404) - response = handle HTTP::Request.new(method, "/dir/test.txt"), false - response.status_code.should eq(405) - response.headers["Allow"].should eq("GET, HEAD") - end - end - - it "should send part of files when requested (RFC7233)" do - %w(POST PUT DELETE HEAD).each do |method| - headers = HTTP::Headers{"Range" => "0-100"} - response = handle HTTP::Request.new(method, "/dir/test.txt", headers) - response.status_code.should_not eq(206) - response.headers.has_key?("Content-Range").should eq(false) - end - - %w(GET).each do |method| - headers = HTTP::Headers{"Range" => "0-100"} - response = handle HTTP::Request.new(method, "/dir/test.txt", headers) - response.status_code.should eq(206 || 200) - if response.status_code == 206 - response.headers.has_key?("Content-Range").should eq true - match = response.headers["Content-Range"].match(/bytes (\d+)-(\d+)\/(\d+)/) - match.should_not be_nil - if match - start_range = match[1].to_i { 0 } - end_range = match[2].to_i { 0 } - range_size = match[3].to_i { 0 } - - range_size.should eq file_size - (end_range < file_size).should eq true - (start_range < end_range).should eq true - end - end - end - end - - it "should handle setting custom headers" do - headers = Proc(HTTP::Server::Response, String, File::Info, Void).new do |response, path, stat| - if path =~ /\.html$/ - response.headers.add("Access-Control-Allow-Origin", "*") - end - response.headers.add("Content-Size", stat.size.to_s) - end - - static_headers(&headers) - - response = handle HTTP::Request.new("GET", "/dir/test.txt") - response.headers.has_key?("Access-Control-Allow-Origin").should be_false - response.headers["Content-Size"].should eq( - File.info("#{__DIR__}/static/dir/test.txt").size.to_s - ) - - response = handle HTTP::Request.new("GET", "/dir/index.html") - response.headers["Access-Control-Allow-Origin"].should eq("*") - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr deleted file mode 100644 index d09f4de4281f..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/view_spec.cr +++ /dev/null @@ -1,62 +0,0 @@ -require "./spec_helper" - -macro render_with_base_and_layout(filename) - render "spec/asset/#{{{filename}}}", "spec/asset/layout.ecr" -end - -describe "Views" do - it "renders file" do - get "/view/:name" do |env| - name = env.params.url["name"] - render "spec/asset/hello.ecr" - end - request = HTTP::Request.new("GET", "/view/world") - client_response = call_request_on_app(request) - client_response.body.should contain("Hello world") - end - - it "renders file with dynamic variables" do - get "/view/:name" do |env| - name = env.params.url["name"] - render_with_base_and_layout "hello.ecr" - end - request = HTTP::Request.new("GET", "/view/world") - client_response = call_request_on_app(request) - client_response.body.should contain("Hello world") - end - - it "renders layout" do - get "/view/:name" do |env| - name = env.params.url["name"] - render "spec/asset/hello.ecr", "spec/asset/layout.ecr" - end - request = HTTP::Request.new("GET", "/view/world") - client_response = call_request_on_app(request) - client_response.body.should contain("Hello world") - end - - it "renders layout with variables" do - get "/view/:name" do |env| - name = env.params.url["name"] - var1 = "serdar" - var2 = "kemal" - render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield_and_vars.ecr" - end - request = HTTP::Request.new("GET", "/view/world") - client_response = call_request_on_app(request) - client_response.body.should contain("Hello world") - client_response.body.should contain("serdar") - client_response.body.should contain("kemal") - end - - it "renders layout with content_for" do - get "/view/:name" do |env| - name = env.params.url["name"] - render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield.ecr" - end - request = HTTP::Request.new("GET", "/view/world") - client_response = call_request_on_app(request) - client_response.body.should contain("Hello world") - client_response.body.should contain("

    Hello from otherside

    ") - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr b/samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr deleted file mode 100644 index bc02d3c26634..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/spec/websocket_handler_spec.cr +++ /dev/null @@ -1,68 +0,0 @@ -require "./spec_helper" - -describe "Kemal::WebSocketHandler" do - it "doesn't match on wrong route" do - handler = Kemal::WebSocketHandler::INSTANCE - handler.next = Kemal::RouteHandler::INSTANCE - ws "/" { } - headers = HTTP::Headers{ - "Upgrade" => "websocket", - "Connection" => "Upgrade", - "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==", - } - request = HTTP::Request.new("GET", "/asd", headers) - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - - expect_raises(Kemal::Exceptions::RouteNotFound) do - handler.call context - end - end - - it "matches on given route" do - handler = Kemal::WebSocketHandler::INSTANCE - ws "/" { |socket| socket.send("Match") } - ws "/no_match" { |socket| socket.send "No Match" } - headers = HTTP::Headers{ - "Upgrade" => "websocket", - "Connection" => "Upgrade", - "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==", - "Sec-WebSocket-Version" => "13", - } - request = HTTP::Request.new("GET", "/", headers) - - io_with_context = create_ws_request_and_return_io_and_context(handler, request)[0] - io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n\x81\u0005Match") - end - - it "fetches named url parameters" do - handler = Kemal::WebSocketHandler::INSTANCE - ws "/:id" { |_, c| c.ws_route_lookup.params["id"] } - headers = HTTP::Headers{ - "Upgrade" => "websocket", - "Connection" => "Upgrade", - "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==", - "Sec-WebSocket-Version" => "13", - } - request = HTTP::Request.new("GET", "/1234", headers) - io_with_context = create_ws_request_and_return_io_and_context(handler, request)[0] - io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n") - end - - it "matches correct verb" do - handler = Kemal::WebSocketHandler::INSTANCE - handler.next = Kemal::RouteHandler::INSTANCE - ws "/" { } - get "/" { "get" } - request = HTTP::Request.new("GET", "/") - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - handler.call(context) - response.close - io.rewind - client_response = HTTP::Client::Response.from_io(io, decompress: false) - client_response.body.should eq("get") - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal.cr deleted file mode 100644 index 009337e254fe..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal.cr +++ /dev/null @@ -1,98 +0,0 @@ -require "http" -require "json" -require "uri" -require "./kemal/*" -require "./kemal/ext/*" -require "./kemal/helpers/*" - -module Kemal - # Overload of `self.run` with the default startup logging. - def self.run(port : Int32?, args = ARGV) - self.run(port, args) { } - end - - # Overload of `self.run` without port. - def self.run(args = ARGV) - self.run(nil, args: args) - end - - # Overload of `self.run` to allow just a block. - def self.run(args = ARGV, &block) - self.run(nil, args: args, &block) - end - - # The command to run a `Kemal` application. - # - # If *port* is not given Kemal will use `Kemal::Config#port` - # - # To use custom command line arguments, set args to nil - # - def self.run(port : Int32? = nil, args = ARGV, &block) - Kemal::CLI.new args - config = Kemal.config - config.setup - config.port = port if port - - # Test environment doesn't need to have signal trap and logging. - if config.env != "test" - setup_404 - setup_trap_signal - end - - server = config.server ||= HTTP::Server.new(config.handlers) - - config.running = true - - yield config - - # Abort if block called `Kemal.stop` - return unless config.running - - unless server.each_address { |_| break true } - {% if flag?(:without_openssl) %} - server.bind_tcp(config.host_binding, config.port) - {% else %} - if ssl = config.ssl - server.bind_tls(config.host_binding, config.port, ssl) - else - server.bind_tcp(config.host_binding, config.port) - end - {% end %} - end - - display_startup_message(config, server) - - server.listen unless config.env == "test" - end - - def self.display_startup_message(config, server) - addresses = server.addresses.map { |address| "#{config.scheme}://#{address}" }.join ", " - log "[#{config.env}] Kemal is ready to lead at #{addresses}" - end - - def self.stop - raise "Kemal is already stopped." if !config.running - if server = config.server - server.close unless server.closed? - config.running = false - else - raise "Kemal.config.server is not set. Please use Kemal.run to set the server." - end - end - - private def self.setup_404 - unless Kemal.config.error_handlers.has_key?(404) - error 404 do - render_404 - end - end - end - - private def self.setup_trap_signal - Signal::INT.trap do - log "Kemal is going to take a rest!" if Kemal.config.shutdown_message - Kemal.stop - exit - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr deleted file mode 100644 index 37ee980b9caf..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/base_log_handler.cr +++ /dev/null @@ -1,9 +0,0 @@ -module Kemal - # All loggers must inherit from `Kemal::BaseLogHandler`. - abstract class BaseLogHandler - include HTTP::Handler - - abstract def call(context : HTTP::Server::Context) - abstract def write(message : String) - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr deleted file mode 100644 index b1667089e2b8..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/cli.cr +++ /dev/null @@ -1,55 +0,0 @@ -require "option_parser" - -module Kemal - # Handles all the initialization from the command line. - class CLI - def initialize(args) - @ssl_enabled = false - @key_file = "" - @cert_file = "" - @config = Kemal.config - if args - parse args - end - configure_ssl - end - - private def parse(args : Array(String)) - OptionParser.parse args do |opts| - opts.on("-b HOST", "--bind HOST", "Host to bind (defaults to 0.0.0.0)") do |host_binding| - @config.host_binding = host_binding - end - opts.on("-p PORT", "--port PORT", "Port to listen for connections (defaults to 3000)") do |opt_port| - @config.port = opt_port.to_i - end - opts.on("-s", "--ssl", "Enables SSL") do - @ssl_enabled = true - end - opts.on("--ssl-key-file FILE", "SSL key file") do |key_file| - @key_file = key_file - end - opts.on("--ssl-cert-file FILE", "SSL certificate file") do |cert_file| - @cert_file = cert_file - end - opts.on("-h", "--help", "Shows this help") do - puts opts - exit 0 - end - @config.extra_options.try &.call(opts) - end - end - - private def configure_ssl - {% if !flag?(:without_openssl) %} - if @ssl_enabled - abort "SSL Key Not Found" if !@key_file - abort "SSL Certificate Not Found" if !@cert_file - ssl = Kemal::SSL.new - ssl.key_file = @key_file.not_nil! - ssl.cert_file = @cert_file.not_nil! - Kemal.config.ssl = ssl.context - end - {% end %} - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr deleted file mode 100644 index 2efcd66ab485..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/config.cr +++ /dev/null @@ -1,168 +0,0 @@ -module Kemal - VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }} - - # Stores all the configuration options for a Kemal application. - # It's a singleton and you can access it like. - # - # ``` - # Kemal.config - # ``` - class Config - INSTANCE = Config.new - HANDLERS = [] of HTTP::Handler - CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) - FILTER_HANDLERS = [] of HTTP::Handler - ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context, Exception -> String - - {% if flag?(:without_openssl) %} - @ssl : Bool? - {% else %} - @ssl : OpenSSL::SSL::Context::Server? - {% end %} - - property host_binding, ssl, port, env, public_folder, logging, running - property always_rescue, server : HTTP::Server?, extra_options, shutdown_message - property serve_static : (Bool | Hash(String, Bool)) - property static_headers : (HTTP::Server::Response, String, File::Info -> Void)? - property powered_by_header : Bool = true - - def initialize - @host_binding = "0.0.0.0" - @port = 3000 - @env = ENV["KEMAL_ENV"]? || "development" - @serve_static = {"dir_listing" => false, "gzip" => true} - @public_folder = "./public" - @logging = true - @logger = nil - @error_handler = nil - @always_rescue = true - @router_included = false - @default_handlers_setup = false - @running = false - @shutdown_message = true - @handler_position = 0 - end - - def logger - @logger.not_nil! - end - - def logger=(logger : Kemal::BaseLogHandler) - @logger = logger - end - - def scheme - ssl ? "https" : "http" - end - - def clear - @powered_by_header = true - @router_included = false - @handler_position = 0 - @default_handlers_setup = false - HANDLERS.clear - CUSTOM_HANDLERS.clear - FILTER_HANDLERS.clear - ERROR_HANDLERS.clear - end - - def handlers - HANDLERS - end - - def handlers=(handlers : Array(HTTP::Handler)) - clear - HANDLERS.replace(handlers) - end - - def add_handler(handler : HTTP::Handler) - CUSTOM_HANDLERS << {nil, handler} - end - - def add_handler(handler : HTTP::Handler, position : Int32) - CUSTOM_HANDLERS << {position, handler} - end - - def add_filter_handler(handler : HTTP::Handler) - FILTER_HANDLERS << handler - end - - def error_handlers - ERROR_HANDLERS - end - - def add_error_handler(status_code : Int32, &handler : HTTP::Server::Context, Exception -> _) - ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } - end - - def extra_options(&@extra_options : OptionParser ->) - end - - def setup - unless @default_handlers_setup && @router_included - setup_init_handler - setup_log_handler - setup_error_handler - setup_static_file_handler - setup_custom_handlers - setup_filter_handlers - @default_handlers_setup = true - @router_included = true - HANDLERS.insert(HANDLERS.size, Kemal::WebSocketHandler::INSTANCE) - HANDLERS.insert(HANDLERS.size, Kemal::RouteHandler::INSTANCE) - end - end - - private def setup_init_handler - HANDLERS.insert(@handler_position, Kemal::InitHandler::INSTANCE) - @handler_position += 1 - end - - private def setup_log_handler - @logger ||= if @logging - Kemal::LogHandler.new - else - Kemal::NullLogHandler.new - end - HANDLERS.insert(@handler_position, @logger.not_nil!) - @handler_position += 1 - end - - private def setup_error_handler - if @always_rescue - @error_handler ||= Kemal::ExceptionHandler.new - HANDLERS.insert(@handler_position, @error_handler.not_nil!) - @handler_position += 1 - end - end - - private def setup_static_file_handler - if @serve_static.is_a?(Hash) - HANDLERS.insert(@handler_position, Kemal::StaticFileHandler.new(@public_folder)) - @handler_position += 1 - end - end - - private def setup_custom_handlers - CUSTOM_HANDLERS.each do |ch0, ch1| - position = ch0 - HANDLERS.insert (position || @handler_position), ch1 - @handler_position += 1 - end - end - - private def setup_filter_handlers - FILTER_HANDLERS.each do |h| - HANDLERS.insert(@handler_position, h) - end - end - end - - def self.config - yield Config::INSTANCE - end - - def self.config - Config::INSTANCE - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr deleted file mode 100644 index 15b3742455ca..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/dsl.cr +++ /dev/null @@ -1,37 +0,0 @@ -# Kemal DSL is defined here and it's baked into global scope. -# -# The DSL currently consists of: -# -# - get post put patch delete options -# - WebSocket(ws) -# - before_* -# - error -HTTP_METHODS = %w(get post put patch delete options) -FILTER_METHODS = %w(get post put patch delete options all) - -{% for method in HTTP_METHODS %} - def {{method.id}}(path : String, &block : HTTP::Server::Context -> _) - raise Kemal::Exceptions::InvalidPathStartException.new({{method}}, path) unless Kemal::Utils.path_starts_with_slash?(path) - Kemal::RouteHandler::INSTANCE.add_route({{method}}.upcase, path, &block) - end -{% end %} - -def ws(path : String, &block : HTTP::WebSocket, HTTP::Server::Context -> Void) - raise Kemal::Exceptions::InvalidPathStartException.new("ws", path) unless Kemal::Utils.path_starts_with_slash?(path) - Kemal::WebSocketHandler::INSTANCE.add_route path, &block -end - -def error(status_code : Int32, &block : HTTP::Server::Context, Exception -> _) - Kemal.config.add_error_handler status_code, &block -end - -# All the helper methods available are: -# - before_all, before_get, before_post, before_put, before_patch, before_delete, before_options -# - after_all, after_get, after_post, after_put, after_patch, after_delete, after_options -{% for type in ["before", "after"] %} - {% for method in FILTER_METHODS %} - def {{type.id}}_{{method.id}}(path : String = "*", &block : HTTP::Server::Context -> _) - Kemal::FilterHandler::INSTANCE.{{type.id}}({{method}}.upcase, path, &block) - end - {% end %} -{% end %} diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr deleted file mode 100644 index eee6eecd6a3a..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/exception_handler.cr +++ /dev/null @@ -1,30 +0,0 @@ -module Kemal - # Handles all the exceptions, including 404, custom errors and 500. - class ExceptionHandler - include HTTP::Handler - INSTANCE = new - - def call(context : HTTP::Server::Context) - call_next(context) - rescue ex : Kemal::Exceptions::RouteNotFound - call_exception_with_status_code(context, ex, 404) - rescue ex : Kemal::Exceptions::CustomException - call_exception_with_status_code(context, ex, context.response.status_code) - rescue ex : Exception - log("Exception: #{ex.inspect_with_backtrace}") - return call_exception_with_status_code(context, ex, 500) if Kemal.config.error_handlers.has_key?(500) - verbosity = Kemal.config.env == "production" ? false : true - render_500(context, ex, verbosity) - end - - private def call_exception_with_status_code(context : HTTP::Server::Context, exception : Exception, status_code : Int32) - return if context.response.closed? - if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(status_code) - context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") - context.response.status_code = status_code - context.response.print Kemal.config.error_handlers[status_code].call(context, exception) - context - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr deleted file mode 100644 index c2e51c6b3178..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/context.cr +++ /dev/null @@ -1,62 +0,0 @@ -# `HTTP::Server::Context` is the class which holds `HTTP::Request` and -# `HTTP::Server::Response` alongside with information such as request params, -# request/response content_type, session data and alike. -# -# Instances of this class are passed to an `HTTP::Server` handler. -class HTTP::Server - class Context - # :nodoc: - STORE_MAPPINGS = [Nil, String, Int32, Int64, Float64, Bool] - - macro finished - alias StoreTypes = Union({{ *STORE_MAPPINGS }}) - @store = {} of String => StoreTypes - end - - def params - @params ||= Kemal::ParamParser.new(@request, route_lookup.params) - end - - def redirect(url : String, status_code : Int32 = 302, *, body : String? = nil) - @response.headers.add "Location", url - @response.status_code = status_code - @response.print(body) if body - end - - def route - route_lookup.payload - end - - def websocket - ws_route_lookup.payload - end - - def route_lookup - Kemal::RouteHandler::INSTANCE.lookup_route(@request.method.as(String), @request.path) - end - - def route_found? - route_lookup.found? - end - - def ws_route_lookup - Kemal::WebSocketHandler::INSTANCE.lookup_ws_route(@request.path) - end - - def ws_route_found? - ws_route_lookup.found? - end - - def get(name : String) - @store[name] - end - - def set(name : String, value : StoreTypes) - @store[name] = value - end - - def get?(name : String) - @store[name]? - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr deleted file mode 100644 index 6fd4c01946bf..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/ext/response.cr +++ /dev/null @@ -1,14 +0,0 @@ -class HTTP::Server::Response - class Output - def close - # ameba:disable Style/NegatedConditionsInUnless - unless response.wrote_headers? && !response.headers.has_key?("Content-Range") - response.content_length = @out_count - end - - ensure_headers_written - - previous_def - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr deleted file mode 100644 index 30eb26aa609e..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/file_upload.cr +++ /dev/null @@ -1,24 +0,0 @@ -module Kemal - struct FileUpload - getter tempfile : File - getter filename : String? - getter headers : HTTP::Headers - getter creation_time : Time? - getter modification_time : Time? - getter read_time : Time? - getter size : UInt64? - - def initialize(upload) - @tempfile = File.tempfile - ::File.open(@tempfile.path, "w") do |file| - IO.copy(upload.body, file) - end - @filename = upload.filename - @headers = upload.headers - @creation_time = upload.creation_time - @modification_time = upload.modification_time - @read_time = upload.read_time - @size = upload.size - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr deleted file mode 100644 index 6d28680a7420..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/filter_handler.cr +++ /dev/null @@ -1,90 +0,0 @@ -module Kemal - # :nodoc: - class FilterHandler - include HTTP::Handler - INSTANCE = new - - # This middleware is lazily instantiated and added to the handlers as soon as a call to `after_X` or `before_X` is made. - def initialize - @tree = Radix::Tree(Array(FilterBlock)).new - Kemal.config.add_filter_handler(self) - end - - # The call order of the filters is `before_all -> before_x -> X -> after_x -> after_all`. - def call(context : HTTP::Server::Context) - return call_next(context) unless context.route_found? - call_block_for_path_type("ALL", context.request.path, :before, context) - call_block_for_path_type(context.request.method, context.request.path, :before, context) - if Kemal.config.error_handlers.has_key?(context.response.status_code) - raise Kemal::Exceptions::CustomException.new(context) - end - call_next(context) - call_block_for_path_type(context.request.method, context.request.path, :after, context) - call_block_for_path_type("ALL", context.request.path, :after, context) - context - end - - # :nodoc: This shouldn't be called directly, it's not private because - # I need to call it for testing purpose since I can't call the macros in the spec. - # It adds the block for the corresponding verb/path/type combination to the tree. - def _add_route_filter(verb : String, path, type, &block : HTTP::Server::Context -> _) - lookup = lookup_filters_for_path_type(verb, path, type) - if lookup.found? && lookup.payload.is_a?(Array(FilterBlock)) - lookup.payload << FilterBlock.new(&block) - else - @tree.add radix_path(verb, path, type), [FilterBlock.new(&block)] - end - end - - # This can be called directly but it's simpler to just use the macros, - # it will check if another filter is not already defined for this - # verb/path/type and proceed to call `add_route_filter` - def before(verb : String, path : String = "*", &block : HTTP::Server::Context -> _) - _add_route_filter verb, path, :before, &block - end - - # This can be called directly but it's simpler to just use the macros, - # it will check if another filter is not already defined for this - # verb/path/type and proceed to call `add_route_filter` - def after(verb : String, path : String = "*", &block : HTTP::Server::Context -> _) - _add_route_filter verb, path, :after, &block - end - - # This will fetch the block for the verb/path/type from the tree and call it. - private def call_block_for_path_type(verb : String?, path : String, type, context : HTTP::Server::Context) - lookup = lookup_filters_for_path_type(verb, path, type) - if lookup.found? && lookup.payload.is_a? Array(FilterBlock) - blocks = lookup.payload - blocks.each &.call(context) - end - end - - # This checks is filter is already defined for the verb/path/type combination - private def filter_for_path_type_defined?(verb : String, path : String, type) - lookup = @tree.find radix_path(verb, path, type) - lookup.found? && lookup.payload.is_a? FilterBlock - end - - # This returns a lookup for verb/path/type - private def lookup_filters_for_path_type(verb : String?, path : String, type) - @tree.find radix_path(verb, path, type) - end - - private def radix_path(verb : String?, path : String, type : Symbol) - "#{type}/#{verb}/#{path}" - end - - # :nodoc: - class FilterBlock - property block : HTTP::Server::Context -> String - - def initialize(&block : HTTP::Server::Context -> _) - @block = ->(context : HTTP::Server::Context) { block.call(context).to_s } - end - - def call(context : HTTP::Server::Context) - @block.call(context) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr deleted file mode 100644 index bb5923810acc..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/handler.cr +++ /dev/null @@ -1,80 +0,0 @@ -module Kemal - # `Kemal::Handler` is a subclass of `HTTP::Handler`. - # - # It adds `only`, `only_match?`, `exclude`, `exclude_match?`. - # These methods are useful for the conditional execution of custom handlers . - class Handler - include HTTP::Handler - - @@only_routes_tree = Radix::Tree(String).new - @@exclude_routes_tree = Radix::Tree(String).new - - macro only(paths, method = "GET") - class_name = {{@type.name}} - method_downcase = {{method.downcase}} - class_name_method = "#{class_name}/#{method_downcase}" - ({{paths}}).each do |path| - @@only_routes_tree.add class_name_method + path, '/' + method_downcase + path - end - end - - macro exclude(paths, method = "GET") - class_name = {{@type.name}} - method_downcase = {{method.downcase}} - class_name_method = "#{class_name}/#{method_downcase}" - ({{paths}}).each do |path| - @@exclude_routes_tree.add class_name_method + path, '/' + method_downcase + path - end - end - - def call(env : HTTP::Server::Context) - call_next(env) - end - - # Processes the path based on `only` paths which is a `Array(String)`. - # If the path is not found on `only` conditions the handler will continue processing. - # If the path is found in `only` conditions it'll stop processing and will pass the request - # to next handler. - # - # However this is not done automatically. All handlers must inherit from `Kemal::Handler`. - # - # ``` - # class OnlyHandler < Kemal::Handler - # only ["/"] - # - # def call(env) - # return call_next(env) unless only_match?(env) - # puts "If the path is / i will be doing some processing here." - # end - # end - # ``` - def only_match?(env : HTTP::Server::Context) - @@only_routes_tree.find(radix_path(env.request.method, env.request.path)).found? - end - - # Processes the path based on `exclude` paths which is a `Array(String)`. - # If the path is not found on `exclude` conditions the handler will continue processing. - # If the path is found in `exclude` conditions it'll stop processing and will pass the request - # to next handler. - # - # However this is not done automatically. All handlers must inherit from `Kemal::Handler`. - # - # ``` - # class ExcludeHandler < Kemal::Handler - # exclude ["/"] - # - # def call(env) - # return call_next(env) if exclude_match?(env) - # puts "If the path is not / i will be doing some processing here." - # end - # end - # ``` - def exclude_match?(env : HTTP::Server::Context) - @@exclude_routes_tree.find(radix_path(env.request.method, env.request.path)).found? - end - - private def radix_path(method : String, path : String) - "#{self.class}/#{method.downcase}#{path}" - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr deleted file mode 100644 index 4ec180c9bf73..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exception_page.cr +++ /dev/null @@ -1,38 +0,0 @@ -require "exception_page" - -module Kemal - class ExceptionPage < ExceptionPage - def styles : ExceptionPage::Styles - ExceptionPage::Styles.new( - accent: "red", - logo_uri: "data:image/png;base64,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" - ) - end - - def project_url - "https://kemalcr.com/" - end - - def self.for_production_exception - <<-HTML - - - - - - -

    Kemal has encountered an error. (500)

    -

    Something wrong with the server :(

    - - - HTML - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr deleted file mode 100644 index 53520b3acaae..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/exceptions.cr +++ /dev/null @@ -1,20 +0,0 @@ -# Exceptions for 404 and custom errors are defined here. -module Kemal::Exceptions - class InvalidPathStartException < Exception - def initialize(method : String, path : String) - super "Route declaration #{method} \"#{path}\" needs to start with '/', should be #{method} \"/#{path}\"" - end - end - - class RouteNotFound < Exception - def initialize(context : HTTP::Server::Context) - super "Requested path: '#{context.request.method}:#{context.request.path}' was not found." - end - end - - class CustomException < Exception - def initialize(context : HTTP::Server::Context) - super "Rendered error with #{context.response.status_code}" - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr deleted file mode 100644 index bf212a21b84a..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/helpers.cr +++ /dev/null @@ -1,270 +0,0 @@ -{% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} - require "compress/deflate" - require "compress/gzip" -{% end %} -require "mime" - -# Adds given `Kemal::Handler` to handlers chain. -# There are 5 handlers by default and all the custom handlers -# goes between the first 4 and the last `Kemal::RouteHandler`. -# -# - `Kemal::InitHandler` -# - `Kemal::LogHandler` -# - `Kemal::ExceptionHandler` -# - `Kemal::StaticFileHandler` -# - Here goes custom handlers -# - `Kemal::RouteHandler` -def add_handler(handler : HTTP::Handler) - Kemal.config.add_handler handler -end - -def add_handler(handler : HTTP::Handler, position : Int32) - Kemal.config.add_handler handler, position -end - -# Sets public folder from which the static assets will be served. -# -# By default this is `/public` not `src/public`. -def public_folder(path : String) - Kemal.config.public_folder = path -end - -# Logs the output via `logger`. -# This is the built-in `Kemal::LogHandler` by default which uses STDOUT. -def log(message : String) - Kemal.config.logger.write "#{message}\n" -end - -# Enables / Disables logging. -# This is enabled by default. -# -# ``` -# logging false -# ``` -def logging(status : Bool) - Kemal.config.logging = status -end - -# This is used to replace the built-in `Kemal::LogHandler` with a custom logger. -# -# A custom logger must inherit from `Kemal::BaseLogHandler` and must implement -# `call(env)`, `write(message)` methods. -# -# ``` -# class MyCustomLogger < Kemal::BaseLogHandler -# def call(env) -# puts "I'm logging some custom stuff here." -# call_next(env) # => This calls the next handler -# end -# -# # This is used from `log` method. -# def write(message) -# STDERR.puts message # => Logs the output to STDERR -# end -# end -# ``` -# -# Now that we have a custom logger here's how we use it -# -# ``` -# logger MyCustomLogger.new -# ``` -def logger(logger : Kemal::BaseLogHandler) - Kemal.config.logger = logger - Kemal.config.add_handler logger -end - -# Enables / Disables static file serving. -# This is enabled by default. -# -# ``` -# serve_static false -# ``` -# -# Static server also have some advanced customization options like `dir_listing` and -# `gzip`. -# -# ``` -# serve_static {"gzip" => true, "dir_listing" => false} -# ``` -def serve_static(status : (Bool | Hash)) - Kemal.config.serve_static = status -end - -# Helper for easily modifying response headers. -# This can be used to modify a response header with the given hash. -# -# ``` -# def call(env) -# headers(env, {"X-Custom-Header" => "This is a custom value"}) -# end -# ``` -def headers(env : HTTP::Server::Context, additional_headers : Hash(String, String)) - env.response.headers.merge!(additional_headers) -end - -# Send a file with given path and base the mime-type on the file extension -# or default `application/octet-stream` mime_type. -# -# ``` -# send_file env, "./path/to/file" -# ``` -# -# Optionally you can override the mime_type -# -# ``` -# send_file env, "./path/to/file", "image/jpeg" -# ``` -# -# Also you can set the filename and the disposition -# -# ``` -# send_file env, "./path/to/file", filename: "image.jpg", disposition: "attachment" -# ``` -def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil, *, filename : String? = nil, disposition : String? = nil) - config = Kemal.config.serve_static - file_path = File.expand_path(path, Dir.current) - mime_type ||= MIME.from_filename(file_path, "application/octet-stream") - env.response.content_type = mime_type - env.response.headers["Accept-Ranges"] = "bytes" - env.response.headers["X-Content-Type-Options"] = "nosniff" - minsize = 860 # http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits ?? - request_headers = env.request.headers - filesize = File.size(file_path) - filestat = File.info(file_path) - attachment(env, filename, disposition) - - Kemal.config.static_headers.try(&.call(env.response, file_path, filestat)) - - File.open(file_path) do |file| - if env.request.method == "GET" && env.request.headers.has_key?("Range") - next multipart(file, env) - end - - condition = config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Kemal::Utils.zip_types(file_path) - if condition && request_headers.includes_word?("Accept-Encoding", "gzip") - env.response.headers["Content-Encoding"] = "gzip" - {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} - Compress::Gzip::Writer.open(env.response) do |deflate| - IO.copy(file, deflate) - end - {% else %} - Gzip::Writer.open(env.response) do |deflate| - IO.copy(file, deflate) - end - {% end %} - elsif condition && request_headers.includes_word?("Accept-Encoding", "deflate") - env.response.headers["Content-Encoding"] = "deflate" - {% if compare_versions(Crystal::VERSION, "0.35.0-0") >= 0 %} - Compress::Deflate::Writer.open(env.response) do |deflate| - IO.copy(file, deflate) - end - {% else %} - Flate::Writer.open(env.response) do |deflate| - IO.copy(file, deflate) - end - {% end %} - else - env.response.content_length = filesize - IO.copy(file, env.response) - end - end - return -end - -# Send a file with given data and default `application/octet-stream` mime_type. -# -# ``` -# send_file env, data_slice -# ``` -# -# Optionally you can override the mime_type -# -# ``` -# send_file env, data_slice, "image/jpeg" -# ``` -# -# Also you can set the filename and the disposition -# -# ``` -# send_file env, data_slice, filename: "image.jpg", disposition: "attachment" -# ``` -def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil, *, filename : String? = nil, disposition : String? = nil) - mime_type ||= "application/octet-stream" - env.response.content_type = mime_type - env.response.content_length = data.bytesize - attachment(env, filename, disposition) - env.response.write data -end - -private def multipart(file, env : HTTP::Server::Context) - # See http://httpwg.org/specs/rfc7233.html - fileb = file.size - startb = endb = 0_i64 - - if match = env.request.headers["Range"].match /bytes=(\d{1,})-(\d{0,})/ - startb = match[1].to_i64 { 0_i64 } if match.size >= 2 - endb = match[2].to_i64 { 0_i64 } if match.size >= 3 - end - - endb = fileb - 1 if endb == 0 - - if startb < endb < fileb - content_length = 1_i64 + endb - startb - env.response.status_code = 206 - env.response.content_length = content_length - env.response.headers["Accept-Ranges"] = "bytes" - env.response.headers["Content-Range"] = "bytes #{startb}-#{endb}/#{fileb}" # MUST - - if startb > 1024 - skipped = 0_i64 - # file.skip only accepts values less or equal to 1024 (buffer size, undocumented) - until (increase_skipped = skipped + 1024_i64) > startb - file.skip(1024) - skipped = increase_skipped - end - if (skipped_minus_startb = skipped - startb) > 0 - file.skip skipped_minus_startb - end - else - file.skip(startb) - end - - IO.copy(file, env.response, content_length) - else - env.response.content_length = fileb - env.response.status_code = 200 # Range not satisfable, see 4.4 Note - IO.copy(file, env.response) - end -end - -# Set the Content-Disposition to "attachment" with the specified filename, -# instructing the user agents to prompt to save. -private def attachment(env : HTTP::Server::Context, filename : String? = nil, disposition : String? = nil) - disposition = "attachment" if disposition.nil? && filename - if disposition && filename - env.response.headers["Content-Disposition"] = "#{disposition}; filename=\"#{File.basename(filename)}\"" - end -end - -# Configures an `HTTP::Server::Response` to compress the response -# output, either using gzip or deflate, depending on the `Accept-Encoding` request header. -# -# Disabled by default. -def gzip(status : Bool = false) - add_handler HTTP::CompressHandler.new if status -end - -# Adds headers to `Kemal::StaticFileHandler`. This is especially useful for `CORS`. -# -# ``` -# static_headers do |response, filepath, filestat| -# if filepath =~ /\.html$/ -# response.headers.add("Access-Control-Allow-Origin", "*") -# end -# response.headers.add("Content-Size", filestat.size.to_s) -# end -# ``` -def static_headers(&headers : HTTP::Server::Response, String, File::Info -> Void) - Kemal.config.static_headers = headers -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr deleted file mode 100644 index 4b5e3090fffe..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/macros.cr +++ /dev/null @@ -1,98 +0,0 @@ -require "kilt" - -CONTENT_FOR_BLOCKS = Hash(String, Tuple(String, Proc(String))).new - -# `content_for` is a set of helpers that allows you to capture -# blocks inside views to be rendered later during the request. The most -# common use is to populate different parts of your layout from your view. -# -# The currently supported engines are: ecr and slang. -# -# ## Usage -# -# You call `content_for`, generally from a view, to capture a block of markup -# giving it an identifier: -# -# ``` -# # index.ecr -# <% content_for "some_key" do %> -# ... -# <% end %> -# ``` -# -# Then, you call `yield_content` with that identifier, generally from a -# layout, to render the captured block: -# -# ``` -# # layout.ecr -# <%= yield_content "some_key" %> -# ``` -# -# ## And How Is This Useful? -# -# For example, some of your views might need a few javascript tags and -# stylesheets, but you don't want to force this files in all your pages. -# Then you can put `<%= yield_content :scripts_and_styles %>` on your -# layout, inside the tag, and each view can call `content_for` -# setting the appropriate set of tags that should be added to the layout. -macro content_for(key, file = __FILE__) - %proc = ->() { - __kilt_io__ = IO::Memory.new - {{ yield }} - __kilt_io__.to_s - } - - CONTENT_FOR_BLOCKS[{{key}}] = Tuple.new {{file}}, %proc - nil -end - -# Yields content for the given key if a `content_for` block exists for that key. -macro yield_content(key) - if CONTENT_FOR_BLOCKS.has_key?({{key}}) - __caller_filename__ = CONTENT_FOR_BLOCKS[{{key}}][0] - %proc = CONTENT_FOR_BLOCKS[{{key}}][1] - %proc.call if __content_filename__ == __caller_filename__ - end -end - -# Render view with a layout as the superview. -# -# ``` -# render "src/views/index.ecr", "src/views/layout.ecr" -# ``` -macro render(filename, layout) - __content_filename__ = {{filename}} - content = render {{filename}} - render {{layout}} -end - -# Render view with the given filename. -macro render(filename) - Kilt.render({{filename}}) -end - -# Halt execution with the current context. -# Returns 200 and an empty response by default. -# -# ``` -# halt env, status_code: 403, response: "Forbidden" -# ``` -macro halt(env, status_code = 200, response = "") - {{env}}.response.status_code = {{status_code}} - {{env}}.response.print {{response}} - {{env}}.response.close - next -end - -# Extends context storage with user defined types. -# -# ``` -# class User -# property name -# end -# -# add_context_storage_type(User) -# ``` -macro add_context_storage_type(type) - {{ HTTP::Server::Context::STORE_MAPPINGS.push(type) }} -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr deleted file mode 100644 index b343fc8a3cd2..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/templates.cr +++ /dev/null @@ -1,35 +0,0 @@ -# This file contains the built-in view templates that Kemal uses. -# Currently it contains templates for 404 and 500 error codes. - -def render_404 - <<-HTML - - - - - - -

    Kemal doesn't know this way.

    - - - - HTML -end - -def render_500(context, exception, verbosity) - context.response.status_code = 500 - - template = if verbosity - Kemal::ExceptionPage.for_runtime_exception(context, exception).to_s - else - Kemal::ExceptionPage.for_production_exception - end - - context.response.print template - context -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr deleted file mode 100644 index 3ece5d448015..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/helpers/utils.cr +++ /dev/null @@ -1,13 +0,0 @@ -module Kemal - module Utils - ZIP_TYPES = {".htm", ".html", ".txt", ".css", ".js", ".svg", ".json", ".xml", ".otf", ".ttf", ".woff", ".woff2"} - - def self.path_starts_with_slash?(path : String) - path.starts_with? '/' - end - - def self.zip_types(path : String) # https://github.com/h5bp/server-configs-nginx/blob/master/nginx.conf - ZIP_TYPES.includes? File.extname(path) - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr deleted file mode 100644 index 881325b6cc5c..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/init_handler.cr +++ /dev/null @@ -1,15 +0,0 @@ -module Kemal - # Initializes the context with default values, such as - # *Content-Type* or *X-Powered-By* headers. - class InitHandler - include HTTP::Handler - - INSTANCE = new - - def call(context : HTTP::Server::Context) - context.response.headers.add "X-Powered-By", "Kemal" if Kemal.config.powered_by_header - context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") - call_next context - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr deleted file mode 100644 index ce08e571a4ad..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/log_handler.cr +++ /dev/null @@ -1,28 +0,0 @@ -module Kemal - # Uses `STDOUT` by default and handles the logging of request/response process time. - class LogHandler < Kemal::BaseLogHandler - def initialize(@io : IO = STDOUT) - end - - def call(context : HTTP::Server::Context) - elapsed_time = Time.measure { call_next(context) } - elapsed_text = elapsed_text(elapsed_time) - @io << Time.utc << ' ' << context.response.status_code << ' ' << context.request.method << ' ' << context.request.resource << ' ' << elapsed_text << '\n' - @io.flush - context - end - - def write(message : String) - @io << message - @io.flush - @io - end - - private def elapsed_text(elapsed) - millis = elapsed.total_milliseconds - return "#{millis.round(2)}ms" if millis >= 1 - - "#{(millis * 1000).round(2)}µs" - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr deleted file mode 100644 index 9f3e03abc30b..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/null_log_handler.cr +++ /dev/null @@ -1,11 +0,0 @@ -module Kemal - # This is here to represent the logger corresponding to Null Object Pattern. - class NullLogHandler < Kemal::BaseLogHandler - def call(context : HTTP::Server::Context) - call_next(context) - end - - def write(message : String) - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr deleted file mode 100644 index 5d87ba0e9bf2..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/param_parser.cr +++ /dev/null @@ -1,113 +0,0 @@ -module Kemal - # Parses the request contents including query_params and body - # and converts them into a params hash which you can use within - # the environment context. - class ParamParser - URL_ENCODED_FORM = "application/x-www-form-urlencoded" - APPLICATION_JSON = "application/json" - MULTIPART_FORM = "multipart/form-data" - PARTS = %w(url query body json files) - # :nodoc: - alias AllParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Any) | Array(JSON::Any) - getter files - - def initialize(@request : HTTP::Request, @url : Hash(String, String) = {} of String => String) - @query = HTTP::Params.new({} of String => Array(String)) - @body = HTTP::Params.new({} of String => Array(String)) - @json = {} of String => AllParamTypes - @files = {} of String => FileUpload - @url_parsed = false - @query_parsed = false - @body_parsed = false - @json_parsed = false - @files_parsed = false - end - - private def unescape_url_param(value : String) - value.empty? ? value : URI.decode(value) - rescue - value - end - - {% for method in PARTS %} - def {{method.id}} - # check memoization - return @{{method.id}} if @{{method.id}}_parsed - - parse_{{method.id}} - # memoize - @{{method.id}}_parsed = true - @{{method.id}} - end - {% end %} - - private def parse_body - content_type = @request.headers["Content-Type"]? - - return unless content_type - - if content_type.try(&.starts_with?(URL_ENCODED_FORM)) - @body = parse_part(@request.body) - return - end - - if content_type.try(&.starts_with?(MULTIPART_FORM)) - parse_files - end - end - - private def parse_query - @query = parse_part(@request.query) - end - - private def parse_url - @url.each { |key, value| @url[key] = unescape_url_param(value) } - end - - private def parse_files - return if @files_parsed - - HTTP::FormData.parse(@request) do |upload| - next unless upload - - filename = upload.filename - - if !filename.nil? - @files[upload.name] = FileUpload.new(upload) - else - @body.add(upload.name, upload.body.gets_to_end) - end - end - - @files_parsed = true - end - - # Parses JSON request body if Content-Type is `application/json`. - # - # - If request body is a JSON `Hash` then all the params are parsed and added into `params`. - # - If request body is a JSON `Array` it's added into `params` as `_json` and can be accessed like `params["_json"]`. - private def parse_json - return unless @request.body && @request.headers["Content-Type"]?.try(&.starts_with?(APPLICATION_JSON)) - - body = @request.body.not_nil!.gets_to_end - case json = JSON.parse(body).raw - when Hash - json.each do |key, value| - @json[key] = value.raw - end - when Array - @json["_json"] = json - else - # Ignore non Array or Hash json values - end - end - - private def parse_part(part : IO?) - HTTP::Params.parse(part ? part.gets_to_end : "") - end - - private def parse_part(part : String?) - HTTP::Params.parse part.to_s - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr deleted file mode 100644 index cfcf29ec30c4..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/route.cr +++ /dev/null @@ -1,17 +0,0 @@ -module Kemal - # Route is the main building block of Kemal. - # - # It takes 3 parameters: http *method*, *path* and a *handler* to specify - # what action to be done if the route is matched. - struct Route - getter method, path, handler - @handler : HTTP::Server::Context -> String - - def initialize(@method : String, @path : String, &handler : HTTP::Server::Context -> _) - @handler = ->(context : HTTP::Server::Context) do - output = handler.call(context) - output.is_a?(String) ? output : "" - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr deleted file mode 100644 index 216616a97cb2..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/route_handler.cr +++ /dev/null @@ -1,68 +0,0 @@ -require "radix" - -module Kemal - class RouteHandler - include HTTP::Handler - - INSTANCE = new - CACHED_ROUTES_LIMIT = 1024 - property routes, cached_routes - - def initialize - @routes = Radix::Tree(Route).new - @cached_routes = Hash(String, Radix::Result(Route)).new - end - - def call(context : HTTP::Server::Context) - process_request(context) - end - - # Adds a given route to routing tree. As an exception each `GET` route additionaly defines - # a corresponding `HEAD` route. - def add_route(method : String, path : String, &handler : HTTP::Server::Context -> _) - add_to_radix_tree method, path, Route.new(method, path, &handler) - add_to_radix_tree("HEAD", path, Route.new("HEAD", path) { }) if method == "GET" - end - - # Looks up the route from the Radix::Tree for the first time and caches to improve performance. - def lookup_route(verb : String, path : String) - lookup_path = radix_path(verb, path) - - if cached_route = @cached_routes[lookup_path]? - return cached_route - end - - route = @routes.find(lookup_path) - - if route.found? - @cached_routes.clear if @cached_routes.size == CACHED_ROUTES_LIMIT - @cached_routes[lookup_path] = route - end - - route - end - - # Processes the route if it's a match. Otherwise renders 404. - private def process_request(context) - raise Kemal::Exceptions::RouteNotFound.new(context) unless context.route_found? - return if context.response.closed? - content = context.route.handler.call(context) - - if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(context.response.status_code) - raise Kemal::Exceptions::CustomException.new(context) - end - - context.response.print(content) - context - end - - private def radix_path(method, path) - '/' + method.downcase + path - end - - private def add_to_radix_tree(method, path, route) - node = radix_path method, path - @routes.add node, route - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr deleted file mode 100644 index e205b18b2584..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/ssl.cr +++ /dev/null @@ -1,17 +0,0 @@ -module Kemal - class SSL - getter context - - def initialize - @context = OpenSSL::SSL::Context::Server.new - end - - def key_file=(key_file : String) - @context.private_key = key_file - end - - def cert_file=(cert_file : String) - @context.certificate_chain = cert_file - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr deleted file mode 100644 index 109e971bbeb2..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/static_file_handler.cr +++ /dev/null @@ -1,68 +0,0 @@ -module Kemal - class StaticFileHandler < HTTP::StaticFileHandler - # ameba:disable Metrics/CyclomaticComplexity - def call(context : HTTP::Server::Context) - return call_next(context) if context.request.path.not_nil! == "/" - - case context.request.method - when "GET", "HEAD" - else - if @fallthrough - call_next(context) - else - context.response.status_code = 405 - context.response.headers.add("Allow", "GET, HEAD") - end - return - end - - config = Kemal.config.serve_static - original_path = context.request.path.not_nil! - request_path = URI.decode(original_path) - - # File path cannot contains '\0' (NUL) because all filesystem I know - # don't accept '\0' character as file name. - if request_path.includes? '\0' - context.response.status_code = 400 - return - end - - expanded_path = File.expand_path(request_path, "/") - is_dir_path = if original_path.ends_with?('/') && !expanded_path.ends_with? '/' - expanded_path = expanded_path + '/' - true - else - expanded_path.ends_with? '/' - end - - file_path = File.join(@public_dir, expanded_path) - is_dir = Dir.exists? file_path - - if request_path != expanded_path - redirect_to context, expanded_path - elsif is_dir && !is_dir_path - redirect_to context, expanded_path + '/' - end - - if Dir.exists?(file_path) - if config.is_a?(Hash) && config["dir_listing"] == true - context.response.content_type = "text/html" - directory_listing(context.response, request_path, file_path) - else - call_next(context) - end - elsif File.exists?(file_path) - last_modified = modification_time(file_path) - add_cache_headers(context.response.headers, last_modified) - - if cache_request?(context, last_modified) - context.response.status_code = 304 - return - end - send_file(context, file_path) - else - call_next(context) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr deleted file mode 100644 index 2b65f8ec3230..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket.cr +++ /dev/null @@ -1,14 +0,0 @@ -module Kemal - # Takes 2 parameters: *path* and a *handler* to specify - # what action to be done if the route is matched. - class WebSocket < HTTP::WebSocketHandler - getter proc - - def initialize(@path : String, &@proc : HTTP::WebSocket, HTTP::Server::Context -> Void) - end - - def call(context : HTTP::Server::Context) - super - end - end -end diff --git a/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr b/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr deleted file mode 100644 index b1432a01d935..000000000000 --- a/samples/client/petstore/crystal/lib/kemal/src/kemal/websocket_handler.cr +++ /dev/null @@ -1,41 +0,0 @@ -module Kemal - class WebSocketHandler - include HTTP::Handler - - INSTANCE = new - property routes - - def initialize - @routes = Radix::Tree(WebSocket).new - end - - def call(context : HTTP::Server::Context) - return call_next(context) unless context.ws_route_found? && websocket_upgrade_request?(context) - context.websocket.call(context) - end - - def lookup_ws_route(path : String) - @routes.find "/ws" + path - end - - def add_route(path : String, &handler : HTTP::WebSocket, HTTP::Server::Context -> Void) - add_to_radix_tree path, WebSocket.new(path, &handler) - end - - private def add_to_radix_tree(path, websocket) - node = radix_path "ws", path - @routes.add node, websocket - end - - private def radix_path(method, path) - '/' + method.downcase + path - end - - private def websocket_upgrade_request?(context) - return unless upgrade = context.request.headers["Upgrade"]? - return unless upgrade.compare("websocket", case_insensitive: true) == 0 - - context.request.headers.includes_word?("Connection", "Upgrade") - end - end -end diff --git a/samples/client/petstore/crystal/lib/kilt/.gitignore b/samples/client/petstore/crystal/lib/kilt/.gitignore deleted file mode 100644 index 2e5d65e9a41b..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/doc/ -/lib/ -/.crystal/ -/.shards/ - - -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock - diff --git a/samples/client/petstore/crystal/lib/kilt/.travis.yml b/samples/client/petstore/crystal/lib/kilt/.travis.yml deleted file mode 100644 index ffc7b6ac56d1..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: crystal diff --git a/samples/client/petstore/crystal/lib/kilt/LICENSE b/samples/client/petstore/crystal/lib/kilt/LICENSE deleted file mode 100644 index f7175effc64c..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Jerome Gravel-Niquet - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/kilt/README.md b/samples/client/petstore/crystal/lib/kilt/README.md deleted file mode 100644 index 43272988e597..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Kilt [![Build Status](https://travis-ci.org/jeromegn/kilt.svg?branch=master)](https://travis-ci.org/jeromegn/kilt) [![Dependency Status](https://shards.rocks/badge/github/jeromegn/kilt/status.svg)](https://shards.rocks/github/jeromegn/kilt) [![devDependency Status](https://shards.rocks/badge/github/jeromegn/kilt/dev_status.svg)](https://shards.rocks/github/jeromegn/kilt) - -Generic templating interface for Crystal. - -## Goal - -Simplify developers' lives by abstracting template rendering for multiple template languages. - -## Supported out of the box - -| Language | File extensions | Required libraries | Maintainer | -| -------- | --------------- | ------------------ | ---------- | -| ECR | .ecr | none (part of the stdlib) | | -| Mustache | .mustache | [crustache](https://github.com/MakeNowJust/crustache) | [@MakeNowJust](https://github.com/MakeNowJust) | -| Slang | .slang | [slang](https://github.com/jeromegn/slang) | [@jeromegn](https://github.com/jeromegn) | -| Temel | .temel | [temel](https://github.com/f/temel) | [@f](https://github.com/f) | -| Crikey | .crikey | [crikey](https://github.com/domgetter/crikey) | [@domgetter](https://github.com/domgetter) | - -See also: -[Registering your own template engine](#registering-your-own-template-engine). - -## Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - kilt: - github: jeromegn/kilt - - # Any other template languages Crystal shard -``` - -## Usage - -- Kilt essentially adds two macros `Kilt.embed` and `Kilt.file`, the code is really simple. -- Add template language dependencies, as listed in the support table above. - -Both macros take a `filename` and a `io_name` (the latter defaults to `"__kilt_io__"`) - -### Example - -```crystal -require "kilt" - -# For slang, add: -require "kilt/slang" - -# With a Class - -class YourView - Kilt.file("path/to/template.ecr") # Adds a to_s method -end -puts YourView.new.to_s # => - - -# Embedded - -str = Kilt.render "path/to/template.slang" - -# or - -str = String.build do |__kilt_io__| - Kilt.embed "path/to/template.slang" -end - -puts str # => -``` - -## Registering your own template engine - -Use `Kilt.register_engine(extension, embed_command)` macro: - -```crystal -require "kilt" - -module MyEngine - macro embed(filename, io_name) - # .... - end -end - -Kilt.register_engine("myeng", MyEngine.embed) -``` - -This can be part of your own `my-engine` library: in this case it should depend -on `kilt` directly, or this could be a part of adapter library, like: -`kilt-my-engine`, which will depend on both `kilt` and `my-engine`. - -## Contributing - -Please contribute your own "adapter" if you create a template language for Crystal that's not yet supported here! - -1. Fork it ( https://github.com/jeromegn/kilt/fork ) -2. Create your feature branch (git checkout -b my-awesome-template-language) -3. Commit your changes (git commit -am 'Add my-awesome-template-language') -4. Push to the branch (git push origin my-awesome-template-language) -5. Create a new Pull Request - -## Contributors - -- [jeromegn](https://github.com/jeromegn) Jerome Gravel-Niquet - creator, maintainer -- [waterlink](https://github.com/waterlink) Oleksii Fedorov -- [MakeNowJust](https://github.com/MakeNowJust) TSUYUSATO Kitsune -- [f](https://github.com/f) Fatih Kadir Akın diff --git a/samples/client/petstore/crystal/lib/kilt/lib b/samples/client/petstore/crystal/lib/kilt/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/shard.yml b/samples/client/petstore/crystal/lib/kilt/shard.yml deleted file mode 100644 index 562cb39c63f9..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/shard.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: kilt -version: 0.4.0 - -authors: - - Jerome Gravel-Niquet - -license: MIT - -development_dependencies: - slang: - github: jeromegn/slang - crustache: - github: MakeNowJust/crustache - temel: - github: f/temel diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr deleted file mode 100644 index 7ae325564d73..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.ecr +++ /dev/null @@ -1 +0,0 @@ -<%= Process.pid %> \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache deleted file mode 100644 index f31f09dc559c..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.mustache +++ /dev/null @@ -1 +0,0 @@ -{{pid}} \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw deleted file mode 100644 index c57eff55ebc0..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.raw +++ /dev/null @@ -1 +0,0 @@ -Hello World! \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang deleted file mode 100644 index e8325c711106..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.slang +++ /dev/null @@ -1 +0,0 @@ -span = Process.pid \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel b/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel deleted file mode 100644 index b214e1f4633a..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/fixtures/test.temel +++ /dev/null @@ -1 +0,0 @@ -span Process.pid diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr deleted file mode 100644 index f62b7d26e9a6..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/kilt/crustache_spec.cr +++ /dev/null @@ -1,26 +0,0 @@ -require "../spec_helper" -require "../../src/crustache" - -class MustacheView - def has_key?(name) - name == "pid" - end - - def [](name) - name == "pid" ? Process.pid : nil - end - - Kilt.file "spec/fixtures/test.mustache", "__kilt_io__", self -end - -describe "kilt/crustache" do - - it "renders crustache" do - Kilt.render("spec/fixtures/test.mustache", { "pid" => Process.pid }).should eq("#{Process.pid}") - end - - it "works with classes" do - MustacheView.new.to_s.should eq("#{Process.pid}") - end - -end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr deleted file mode 100644 index 4dbf66128183..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/kilt/slang_spec.cr +++ /dev/null @@ -1,18 +0,0 @@ -require "../spec_helper" -require "../../src/slang" - -class SlangView - Kilt.file "spec/fixtures/test.slang" -end - -describe "kilt/slang" do - - it "renders slang" do - Kilt.render("spec/fixtures/test.slang").should eq("#{Process.pid}") - end - - it "works with classes" do - SlangView.new.to_s.should eq("#{Process.pid}") - end - -end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr deleted file mode 100644 index aed4b48b3468..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/kilt/temel_spec.cr +++ /dev/null @@ -1,18 +0,0 @@ -# require "../spec_helper" -# require "../../src/temel" - -# class TemelView -# Kilt.file "spec/fixtures/test.temel" -# end - -# describe "kilt/temel" do - -# it "renders temel" do -# Kilt.render("spec/fixtures/test.temel").should eq("#{Process.pid}") -# end - -# it "works with classes" do -# TemelView.new.to_s.should eq("#{Process.pid}") -# end - -# end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr b/samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr deleted file mode 100644 index dc3a3e503ac6..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/kilt_spec.cr +++ /dev/null @@ -1,28 +0,0 @@ -require "./spec_helper" - -class View - Kilt.file "spec/fixtures/test.ecr" -end - -describe Kilt do - - it "renders ecr" do - Kilt.render("spec/fixtures/test.ecr").should eq("#{Process.pid}") - end - - it "works with classes" do - View.new.to_s.should eq("#{Process.pid}") - end - - it "raises with unsupported filetype" do - expect_raises(Kilt::Exception, "Unsupported template engine for extension: \"abc\"") { - Kilt.render("test.abc") - } - end - - it "renders registered engine" do - Kilt.register_engine "raw", Raw.embed - Kilt.render("spec/fixtures/test.raw").should eq("Hello World!") - end - -end diff --git a/samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr deleted file mode 100644 index d69d98e36c4c..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/spec_helper.cr +++ /dev/null @@ -1,3 +0,0 @@ -require "spec" -require "../src/kilt" -require "./support/raw_engine" \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr b/samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr deleted file mode 100644 index 2e0f9dc66e95..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/spec/support/raw_engine.cr +++ /dev/null @@ -1,5 +0,0 @@ -module Raw - macro embed(filename, io) - {{ io.id }} << {{`cat #{filename}`.stringify}} - end -end \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/src/crikey.cr b/samples/client/petstore/crystal/lib/kilt/src/crikey.cr deleted file mode 100644 index f20b9e2129ba..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/crikey.cr +++ /dev/null @@ -1,4 +0,0 @@ -require "./kilt" -require "crikey" - -Kilt.register_engine "crikey", Crikey.embed diff --git a/samples/client/petstore/crystal/lib/kilt/src/crustache.cr b/samples/client/petstore/crystal/lib/kilt/src/crustache.cr deleted file mode 100644 index e766e6b40736..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/crustache.cr +++ /dev/null @@ -1,4 +0,0 @@ -require "./kilt" -require "crustache" - -Kilt.register_engine "mustache", Mustache.embed \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/src/ecr.cr b/samples/client/petstore/crystal/lib/kilt/src/ecr.cr deleted file mode 100644 index 2360c0d42e19..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/ecr.cr +++ /dev/null @@ -1,3 +0,0 @@ -require "ecr/macros" - -Kilt.register_engine("ecr", ECR.embed) diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt.cr deleted file mode 100644 index 6dfbb0e820b2..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/kilt.cr +++ /dev/null @@ -1,35 +0,0 @@ -require "./kilt/version" -require "./kilt/exception" - -module Kilt - # macro only constant - ENGINES = {} of String => Int32 - - macro register_engine(ext, embed_macro) - {% Kilt::ENGINES[ext] = embed_macro.id %} - end - - macro embed(filename, io_name = "__kilt_io__", *args) - {% ext = filename.split(".").last %} - - {% if Kilt::ENGINES[ext] %} - {{Kilt::ENGINES[ext]}}({{filename}}, {{io_name}}, {{*args}}) - {% else %} - raise Kilt::Exception.new("Unsupported template engine for extension: \"" + {{ext}} + "\"") - {% end %} - end - - macro render(filename, *args) - String.build do |__kilt_io__| - Kilt.embed({{filename}}, "__kilt_io__", {{*args}}) - end - end - - macro file(filename, io_name = "__kilt_io__", *args) - def to_s({{io_name.id}}) - Kilt.embed({{filename}}, {{io_name}}, {{*args}}) - end - end -end - -require "./ecr" diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr deleted file mode 100644 index 2e43dfd3f47a..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/kilt/exception.cr +++ /dev/null @@ -1,5 +0,0 @@ -module Kilt - class Exception < ::Exception - # Nothing special - end -end \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr deleted file mode 100644 index 38967c2556ec..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/kilt/helpers/temel_embedder.cr +++ /dev/null @@ -1,3 +0,0 @@ -require "temel" - -puts File.read(ARGV[0]).to_s STDOUT diff --git a/samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr b/samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr deleted file mode 100644 index 2153f5ad1e8d..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/kilt/version.cr +++ /dev/null @@ -1,3 +0,0 @@ -module Kilt - VERSION = "0.4.0" -end diff --git a/samples/client/petstore/crystal/lib/kilt/src/slang.cr b/samples/client/petstore/crystal/lib/kilt/src/slang.cr deleted file mode 100644 index 249af60310fc..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/slang.cr +++ /dev/null @@ -1,4 +0,0 @@ -require "./kilt" -require "slang" - -Kilt.register_engine "slang", Slang.embed diff --git a/samples/client/petstore/crystal/lib/kilt/src/temel.cr b/samples/client/petstore/crystal/lib/kilt/src/temel.cr deleted file mode 100644 index 13a6423eb918..000000000000 --- a/samples/client/petstore/crystal/lib/kilt/src/temel.cr +++ /dev/null @@ -1,9 +0,0 @@ -require "./kilt" -require "temel" - -macro embed_temel(filename, __kilt_io__) - __kilt_io__ << {{ run("./kilt/helpers/temel_embedder.cr", filename) }} - __kilt_io__ -end - -Kilt.register_engine "temel", embed_temel diff --git a/samples/client/petstore/crystal/lib/radix/.gitignore b/samples/client/petstore/crystal/lib/radix/.gitignore deleted file mode 100644 index 591e49cd520c..000000000000 --- a/samples/client/petstore/crystal/lib/radix/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/doc/ -/lib/ -/.crystal/ -/.shards/ - -# Libraries don't need dependency lock -# Dependencies will be locked in application that uses them -/shard.lock diff --git a/samples/client/petstore/crystal/lib/radix/.travis.yml b/samples/client/petstore/crystal/lib/radix/.travis.yml deleted file mode 100644 index 33cd2975b2eb..000000000000 --- a/samples/client/petstore/crystal/lib/radix/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: crystal -crystal: - - latest - - nightly -matrix: - allow_failures: - - crystal: nightly - -notifications: - email: - on_success: never diff --git a/samples/client/petstore/crystal/lib/radix/CHANGELOG.md b/samples/client/petstore/crystal/lib/radix/CHANGELOG.md deleted file mode 100644 index 81ddc75bbdc2..000000000000 --- a/samples/client/petstore/crystal/lib/radix/CHANGELOG.md +++ /dev/null @@ -1,102 +0,0 @@ -# Change Log - -All notable changes to Radix project will be documented in this file. -This project aims to comply with [Semantic Versioning](http://semver.org/), -so please check *Changed* and *Removed* notes before upgrading. - -## [Unreleased] - -## [0.3.9] - 2019-01-02 -### Fixed -- Correct catch-all issue caused when paths differ [#26](https://github.com/luislavena/radix/pull/26) (@silasb) - -## [0.3.8] - 2017-03-12 -### Fixed -- Correct lookup issue caused by incorrect comparison of shared key [#21](https://github.com/luislavena/radix/issues/21) -- Improve support for non-ascii keys in a tree. - -## [0.3.7] - 2017-02-04 -### Fixed -- Correct prioritization of node's children using combination of kind and - priority, allowing partial shared keys to coexist and resolve lookup. - -## [0.3.6] - 2017-01-18 -### Fixed -- Correct lookup issue caused by similar priority between named paramter and - shared partial key [kemalcr/kemal#293](https://github.com/kemalcr/kemal/issues/293) - -## [0.3.5] - 2016-11-24 -### Fixed -- Correct lookup issue when dealing with catch all and shared partial key (@crisward) - -## [0.3.4] - 2016-11-12 -### Fixed -- Ensure catch all parameter can be used as optional globbing (@jwoertink) - -## [0.3.3] - 2016-11-12 [YANKED] -### Fixed -- Ensure catch all parameter can be used as optional globbing (@jwoertink) - -## [0.3.2] - 2016-11-05 -### Fixed -- Do not force adding paths with shared named parameter in an specific order (@jwoertink) -- Give proper name to `Radix::VERSION` spec when running in verbose mode. -- Ensure code samples in docs can be executed. - -## [0.3.1] - 2016-07-29 -### Added -- Introduce `Radix::VERSION` so library version can be used at runtime. - -## [0.3.0] - 2016-04-16 -### Fixed -- Improve forward compatibility with newer versions of the compiler by adding - missing types to solve type inference errors. - -### Changed -- `Radix::Tree` now requires the usage of a type which will be used as node's - payload. See [README](README.md) for details. - -## [0.2.1] - 2016-03-15 -### Fixed -- Correct `Result#key` incorrect inferred type. - -### Removed -- Attempt to use two named parameters at the same level will raise - `Radix::Tree::SharedKeyError` - -## [0.2.0] - 2016-03-15 [YANKED] -### Removed -- Attempt to use two named parameters at the same level will raise - `Radix::Tree::SharedKeyError` - -## [0.1.2] - 2016-03-10 -### Fixed -- No longer split named parameters that share same level (@alsm) - -### Changed -- Attempt to use two named parameters at same level will display a - deprecation warning. Future versions will raise `Radix::Tree::SharedKeyError` - -## [0.1.1] - 2016-02-29 -### Fixed -- Fix named parameter key names extraction. - -## [0.1.0] - 2016-01-24 -### Added -- Initial release based on code extracted from Beryl. - -[Unreleased]: https://github.com/luislavena/radix/compare/v0.3.9...HEAD -[0.3.9]: https://github.com/luislavena/radix/compare/v0.3.8...v0.3.9 -[0.3.8]: https://github.com/luislavena/radix/compare/v0.3.7...v0.3.8 -[0.3.7]: https://github.com/luislavena/radix/compare/v0.3.6...v0.3.7 -[0.3.6]: https://github.com/luislavena/radix/compare/v0.3.5...v0.3.6 -[0.3.5]: https://github.com/luislavena/radix/compare/v0.3.4...v0.3.5 -[0.3.4]: https://github.com/luislavena/radix/compare/v0.3.3...v0.3.4 -[0.3.3]: https://github.com/luislavena/radix/compare/v0.3.2...v0.3.3 -[0.3.2]: https://github.com/luislavena/radix/compare/v0.3.1...v0.3.2 -[0.3.1]: https://github.com/luislavena/radix/compare/v0.3.0...v0.3.1 -[0.3.0]: https://github.com/luislavena/radix/compare/v0.2.1...v0.3.0 -[0.2.1]: https://github.com/luislavena/radix/compare/v0.2.0...v0.2.1 -[0.2.0]: https://github.com/luislavena/radix/compare/v0.1.2...v0.2.0 -[0.1.2]: https://github.com/luislavena/radix/compare/v0.1.1...v0.1.2 -[0.1.1]: https://github.com/luislavena/radix/compare/v0.1.0...v0.1.1 diff --git a/samples/client/petstore/crystal/lib/radix/LICENSE b/samples/client/petstore/crystal/lib/radix/LICENSE deleted file mode 100644 index 15bbbc167af5..000000000000 --- a/samples/client/petstore/crystal/lib/radix/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Luis Lavena - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/samples/client/petstore/crystal/lib/radix/Makefile b/samples/client/petstore/crystal/lib/radix/Makefile deleted file mode 100644 index d5c063221459..000000000000 --- a/samples/client/petstore/crystal/lib/radix/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -CRYSTAL ?= crystal - -profile ?= ## Display profiling information after specs execution -verbose ?= ## Run specs in verbose mode - -SPEC_FLAGS := $(if $(profile),--profile )$(if $(verbose),--verbose ) - -.PHONY: default autospec spec - -default: spec - -# `autospec` task uses `watchexec` external dependency: -# https://github.com/mattgreen/watchexec -autospec: - watchexec --exts cr --watch spec --watch src --clear $(CRYSTAL) spec $(SPEC_FLAGS) - -spec: - $(CRYSTAL) spec $(SPEC_FLAGS) \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/radix/README.md b/samples/client/petstore/crystal/lib/radix/README.md deleted file mode 100644 index 0fe266dec643..000000000000 --- a/samples/client/petstore/crystal/lib/radix/README.md +++ /dev/null @@ -1,129 +0,0 @@ -# Radix Tree - -[Radix tree](https://en.wikipedia.org/wiki/Radix_tree) implementation for -Crystal language - -[![Build Status](https://img.shields.io/travis/luislavena/radix/master.svg)](https://travis-ci.org/luislavena/radix) -[![Latest Release](https://img.shields.io/github/release/luislavena/radix.svg)](https://github.com/luislavena/radix/releases) - -## Installation - -Add this to your application's `shard.yml`: - -```yaml -dependencies: - radix: - github: luislavena/radix -``` - -## Usage - -### Building Trees - -You can associate a *payload* with each path added to the tree: - -```crystal -require "radix" - -tree = Radix::Tree(Symbol).new -tree.add "/products", :products -tree.add "/products/featured", :featured - -result = tree.find "/products/featured" - -if result.found? - puts result.payload # => :featured -end -``` - -The types allowed for payload are defined on Tree definition: - -```crystal -tree = Radix::Tree(Symbol).new - -# Good, since Symbol is allowed as payload -tree.add "/", :root - -# Compilation error, Int32 is not allowed -tree.add "/meaning-of-life", 42 -``` - -Can combine multiple types if needed: - -```crystal -tree = Radix::Tree(Int32 | String | Symbol).new - -tree.add "/", :root -tree.add "/meaning-of-life", 42 -tree.add "/hello", "world" -``` - -### Lookup and placeholders - -You can also extract values from placeholders (as named segments or globbing): - -```crystal -tree.add "/products/:id", :product - -result = tree.find "/products/1234" - -if result.found? - puts result.params["id"]? # => "1234" -end -``` - -Please see `Radix::Tree#add` documentation for more usage examples. - -## Caveats - -Pretty much all Radix implementations have their limitations and this project -is no exception. - -When designing and adding *paths* to a Tree, please consider that two different -named parameters cannot share the same level: - -```crystal -tree.add "/", :root -tree.add "/:post", :post -tree.add "/:category/:post", :category_post # => Radix::Tree::SharedKeyError -``` - -This is because different named parameters at the same level will result in -incorrect `params` when lookup is performed, and sometimes the value for -`post` or `category` parameters will not be stored as expected. - -To avoid this issue, usage of explicit keys that differentiate each path is -recommended. - -For example, following a good SEO practice will be consider `/:post` as -absolute permalink for the post and have a list of categories which links to -the permalinks of the posts under that category: - -```crystal -tree.add "/", :root -tree.add "/:post", :post # this is post permalink -tree.add "/categories", :categories # list of categories -tree.add "/categories/:category", :category # listing of posts under each category -``` - -## Implementation - -This project has been inspired and adapted from -[julienschmidt/httprouter](https://github.com/julienschmidt/httprouter) and -[spriet2000/vertx-http-router](https://github.com/spriet2000/vertx-http-router) -Go and Java implementations, respectively. - -Changes to logic and optimizations have been made to take advantage of -Crystal's features. - -## Contributing - -1. Fork it ( https://github.com/luislavena/radix/fork ) -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create a new Pull Request - -## Contributors - -- [Luis Lavena](https://github.com/luislavena) - creator, maintainer diff --git a/samples/client/petstore/crystal/lib/radix/lib b/samples/client/petstore/crystal/lib/radix/lib deleted file mode 120000 index a96aa0ea9d8c..000000000000 --- a/samples/client/petstore/crystal/lib/radix/lib +++ /dev/null @@ -1 +0,0 @@ -.. \ No newline at end of file diff --git a/samples/client/petstore/crystal/lib/radix/shard.yml b/samples/client/petstore/crystal/lib/radix/shard.yml deleted file mode 100644 index 70565cd6549f..000000000000 --- a/samples/client/petstore/crystal/lib/radix/shard.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: radix -version: 0.3.9 - -authors: - - Luis Lavena - -license: MIT diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr deleted file mode 100644 index e43ffaa96969..000000000000 --- a/samples/client/petstore/crystal/lib/radix/spec/radix/node_spec.cr +++ /dev/null @@ -1,150 +0,0 @@ -require "../spec_helper" - -module Radix - describe Node do - describe "#glob?" do - it "returns true when key contains a glob parameter (catch all)" do - node = Node(Nil).new("a") - node.glob?.should be_false - - node = Node(Nil).new("*filepath") - node.glob?.should be_true - end - end - - describe "#key=" do - it "accepts change of key after initialization" do - node = Node(Nil).new("abc") - node.key.should eq("abc") - - node.key = "xyz" - node.key.should eq("xyz") - end - - it "also changes kind when modified" do - node = Node(Nil).new("abc") - node.normal?.should be_true - - node.key = ":query" - node.normal?.should be_false - node.named?.should be_true - end - end - - describe "#named?" do - it "returns true when key contains a named parameter" do - node = Node(Nil).new("a") - node.named?.should be_false - - node = Node(Nil).new(":query") - node.named?.should be_true - end - end - - describe "#normal?" do - it "returns true when key does not contain named or glob parameters" do - node = Node(Nil).new("a") - node.normal?.should be_true - - node = Node(Nil).new(":query") - node.normal?.should be_false - - node = Node(Nil).new("*filepath") - node.normal?.should be_false - end - end - - describe "#payload" do - it "accepts any form of payload" do - node = Node.new("abc", :payload) - node.payload?.should be_truthy - node.payload.should eq(:payload) - - node = Node.new("abc", 1_000) - node.payload?.should be_truthy - node.payload.should eq(1_000) - end - - # This example focuses on the internal representation of `payload` - # as inferred from supplied types and default values. - # - # We cannot compare `typeof` against `property!` since it excludes `Nil` - # from the possible types. - it "makes optional to provide a payload" do - node = Node(Int32).new("abc") - node.payload?.should be_falsey - typeof(node.@payload).should eq(Int32 | Nil) - end - end - - describe "#priority" do - it "calculates it based on key length" do - node = Node(Nil).new("a") - node.priority.should eq(1) - - node = Node(Nil).new("abc") - node.priority.should eq(3) - end - - it "considers key length up until named parameter presence" do - node = Node(Nil).new("/posts/:id") - node.priority.should eq(7) - - node = Node(Nil).new("/u/:username") - node.priority.should eq(3) - end - - it "considers key length up until glob parameter presence" do - node = Node(Nil).new("/search/*query") - node.priority.should eq(8) - - node = Node(Nil).new("/*anything") - node.priority.should eq(1) - end - - it "changes when key changes" do - node = Node(Nil).new("a") - node.priority.should eq(1) - - node.key = "abc" - node.priority.should eq(3) - - node.key = "/src/*filepath" - node.priority.should eq(5) - - node.key = "/search/:query" - node.priority.should eq(8) - end - end - - describe "#sort!" do - it "orders children" do - root = Node(Int32).new("/") - node1 = Node(Int32).new("a", 1) - node2 = Node(Int32).new("bc", 2) - node3 = Node(Int32).new("def", 3) - - root.children.push(node1, node2, node3) - root.sort! - - root.children[0].should eq(node3) - root.children[1].should eq(node2) - root.children[2].should eq(node1) - end - - it "orders catch all and named parameters lower than normal nodes" do - root = Node(Int32).new("/") - node1 = Node(Int32).new("*filepath", 1) - node2 = Node(Int32).new("abc", 2) - node3 = Node(Int32).new(":query", 3) - - root.children.push(node1, node2, node3) - root.sort! - - root.children[0].should eq(node2) - root.children[1].should eq(node3) - root.children[2].should eq(node1) - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr deleted file mode 100644 index 89ad2267651c..000000000000 --- a/samples/client/petstore/crystal/lib/radix/spec/radix/result_spec.cr +++ /dev/null @@ -1,76 +0,0 @@ -require "../spec_helper" - -module Radix - describe Result do - describe "#found?" do - context "a new instance" do - it "returns false when no payload is associated" do - result = Result(Nil).new - result.found?.should be_false - end - end - - context "with a payload" do - it "returns true" do - node = Node(Symbol).new("/", :root) - result = Result(Symbol).new - result.use node - - result.found?.should be_true - end - end - end - - describe "#key" do - context "a new instance" do - it "returns an empty key" do - result = Result(Nil).new - result.key.should eq("") - end - end - - context "given one used node" do - it "returns the node key" do - node = Node(Symbol).new("/", :root) - result = Result(Symbol).new - result.use node - - result.key.should eq("/") - end - end - - context "using multiple nodes" do - it "combines the node keys" do - node1 = Node(Symbol).new("/", :root) - node2 = Node(Symbol).new("about", :about) - result = Result(Symbol).new - result.use node1 - result.use node2 - - result.key.should eq("/about") - end - end - end - - describe "#use" do - it "uses the node payload" do - node = Node(Symbol).new("/", :root) - result = Result(Symbol).new - result.payload?.should be_falsey - - result.use node - result.payload?.should be_truthy - result.payload.should eq(node.payload) - end - - it "allow not to assign payload" do - node = Node(Symbol).new("/", :root) - result = Result(Symbol).new - result.payload?.should be_falsey - - result.use node, payload: false - result.payload?.should be_falsey - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr deleted file mode 100644 index 760de45f82c7..000000000000 --- a/samples/client/petstore/crystal/lib/radix/spec/radix/tree_spec.cr +++ /dev/null @@ -1,626 +0,0 @@ -require "../spec_helper" - -# Silence deprecation warnings when running specs and allow -# capture them for inspection. -module Radix - class Tree(T) - @show_deprecations = false - @stderr : IO::Memory? - - def show_deprecations! - @show_deprecations = true - end - - private def deprecation(message) - if @show_deprecations - @stderr ||= IO::Memory.new - @stderr.not_nil!.puts message - end - end - end -end - -# Simple Payload class -record Payload - -module Radix - describe Tree do - context "a new instance" do - it "contains a root placeholder node" do - tree = Tree(Symbol).new - tree.root.should be_a(Node(Symbol)) - tree.root.payload?.should be_falsey - tree.root.placeholder?.should be_true - end - end - - describe "#add" do - context "on a new instance" do - it "replaces placeholder with new node" do - tree = Tree(Symbol).new - tree.add "/abc", :abc - tree.root.should be_a(Node(Symbol)) - tree.root.placeholder?.should be_false - tree.root.payload?.should be_truthy - tree.root.payload.should eq(:abc) - end - end - - context "shared root" do - it "inserts properly adjacent nodes" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/a", :a - tree.add "/bc", :bc - - # / (:root) - # +-bc (:bc) - # \-a (:a) - tree.root.children.size.should eq(2) - tree.root.children[0].key.should eq("bc") - tree.root.children[0].payload.should eq(:bc) - tree.root.children[1].key.should eq("a") - tree.root.children[1].payload.should eq(:a) - end - - it "inserts nodes with shared parent" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/abc", :abc - tree.add "/axyz", :axyz - - # / (:root) - # +-a - # +-xyz (:axyz) - # \-bc (:abc) - tree.root.children.size.should eq(1) - tree.root.children[0].key.should eq("a") - tree.root.children[0].children.size.should eq(2) - tree.root.children[0].children[0].key.should eq("xyz") - tree.root.children[0].children[1].key.should eq("bc") - end - - it "inserts multiple parent nodes" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/admin/users", :users - tree.add "/admin/products", :products - tree.add "/blog/tags", :tags - tree.add "/blog/articles", :articles - - # / (:root) - # +-admin/ - # | +-products (:products) - # | \-users (:users) - # | - # +-blog/ - # +-articles (:articles) - # \-tags (:tags) - tree.root.children.size.should eq(2) - tree.root.children[0].key.should eq("admin/") - tree.root.children[0].payload?.should be_falsey - tree.root.children[0].children[0].key.should eq("products") - tree.root.children[0].children[1].key.should eq("users") - tree.root.children[1].key.should eq("blog/") - tree.root.children[1].payload?.should be_falsey - tree.root.children[1].children[0].key.should eq("articles") - tree.root.children[1].children[0].payload?.should be_truthy - tree.root.children[1].children[1].key.should eq("tags") - tree.root.children[1].children[1].payload?.should be_truthy - end - - it "inserts multiple nodes with mixed parents" do - tree = Tree(Symbol).new - tree.add "/authorizations", :authorizations - tree.add "/authorizations/:id", :authorization - tree.add "/applications", :applications - tree.add "/events", :events - - # / - # +-events (:events) - # +-a - # +-uthorizations (:authorizations) - # | \-/:id (:authorization) - # \-pplications (:applications) - tree.root.children.size.should eq(2) - tree.root.children[1].key.should eq("a") - tree.root.children[1].children.size.should eq(2) - tree.root.children[1].children[0].payload.should eq(:authorizations) - tree.root.children[1].children[1].payload.should eq(:applications) - end - - it "supports insertion of mixed routes out of order" do - tree = Tree(Symbol).new - tree.add "/user/repos", :my_repos - tree.add "/users/:user/repos", :user_repos - tree.add "/users/:user", :user - tree.add "/user", :me - - # /user (:me) - # +-/repos (:my_repos) - # \-s/:user (:user) - # \-/repos (:user_repos) - tree.root.key.should eq("/user") - tree.root.payload?.should be_truthy - tree.root.payload.should eq(:me) - tree.root.children.size.should eq(2) - tree.root.children[0].key.should eq("/repos") - tree.root.children[1].key.should eq("s/:user") - tree.root.children[1].payload.should eq(:user) - tree.root.children[1].children[0].key.should eq("/repos") - end - end - - context "mixed payloads" do - it "allows node with different payloads" do - payload1 = Payload.new - payload2 = Payload.new - - tree = Tree(Payload | Symbol).new - tree.add "/", :root - tree.add "/a", payload1 - tree.add "/bc", payload2 - - # / (:root) - # +-bc (payload2) - # \-a (payload1) - tree.root.children.size.should eq(2) - tree.root.children[0].key.should eq("bc") - tree.root.children[0].payload.should eq(payload2) - tree.root.children[1].key.should eq("a") - tree.root.children[1].payload.should eq(payload1) - end - end - - context "dealing with unicode" do - it "inserts properly adjacent parent nodes" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/日本語", :japanese - tree.add "/素晴らしい", :amazing - - # / (:root) - # +-素晴らしい (:amazing) - # \-日本語 (:japanese) - tree.root.children.size.should eq(2) - tree.root.children[0].key.should eq("素晴らしい") - tree.root.children[1].key.should eq("日本語") - end - - it "inserts nodes with shared parent" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/日本語", :japanese - tree.add "/日本は難しい", :japanese_is_difficult - - # / (:root) - # \-日本語 (:japanese) - # \-日本は難しい (:japanese_is_difficult) - tree.root.children.size.should eq(1) - tree.root.children[0].key.should eq("日本") - tree.root.children[0].children.size.should eq(2) - tree.root.children[0].children[0].key.should eq("は難しい") - tree.root.children[0].children[1].key.should eq("語") - end - end - - context "dealing with duplicates" do - it "does not allow same path be defined twice" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/abc", :abc - - expect_raises Tree::DuplicateError do - tree.add "/", :other - end - - tree.root.children.size.should eq(1) - end - end - - context "dealing with catch all and named parameters" do - it "prioritizes nodes correctly" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/*filepath", :all - tree.add "/products", :products - tree.add "/products/:id", :product - tree.add "/products/:id/edit", :edit - tree.add "/products/featured", :featured - - # / (:all) - # +-products (:products) - # | \-/ - # | +-featured (:featured) - # | \-:id (:product) - # | \-/edit (:edit) - # \-*filepath (:all) - tree.root.children.size.should eq(2) - tree.root.children[0].key.should eq("products") - tree.root.children[0].children[0].key.should eq("/") - - nodes = tree.root.children[0].children[0].children - nodes.size.should eq(2) - nodes[0].key.should eq("featured") - nodes[1].key.should eq(":id") - nodes[1].children[0].key.should eq("/edit") - - tree.root.children[1].key.should eq("*filepath") - end - - it "does not split named parameters across shared key" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/:category", :category - tree.add "/:category/:subcategory", :subcategory - - # / (:root) - # +-:category (:category) - # \-/:subcategory (:subcategory) - tree.root.children.size.should eq(1) - tree.root.children[0].key.should eq(":category") - - # inner children - tree.root.children[0].children.size.should eq(1) - tree.root.children[0].children[0].key.should eq("/:subcategory") - end - - it "does allow same named parameter in different order of insertion" do - tree = Tree(Symbol).new - tree.add "/members/:id/edit", :member_edit - tree.add "/members/export", :members_export - tree.add "/members/:id/videos", :member_videos - - # /members/ - # +-export (:members_export) - # \-:id/ - # +-videos (:members_videos) - # \-edit (:members_edit) - tree.root.key.should eq("/members/") - tree.root.children.size.should eq(2) - - # first level children nodes - tree.root.children[0].key.should eq("export") - tree.root.children[1].key.should eq(":id/") - - # inner children - nodes = tree.root.children[1].children - nodes[0].key.should eq("videos") - nodes[1].key.should eq("edit") - end - - it "does not allow different named parameters sharing same level" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/:post", :post - - expect_raises Tree::SharedKeyError do - tree.add "/:category/:post", :category_post - end - end - end - end - - describe "#find" do - context "a single node" do - it "does not find when using different path" do - tree = Tree(Symbol).new - tree.add "/about", :about - - result = tree.find "/products" - result.found?.should be_false - end - - it "finds when key and path matches" do - tree = Tree(Symbol).new - tree.add "/about", :about - - result = tree.find "/about" - result.found?.should be_true - result.key.should eq("/about") - result.payload?.should be_truthy - result.payload.should eq(:about) - end - - it "finds when path contains trailing slash" do - tree = Tree(Symbol).new - tree.add "/about", :about - - result = tree.find "/about/" - result.found?.should be_true - result.key.should eq("/about") - end - - it "finds when key contains trailing slash" do - tree = Tree(Symbol).new - tree.add "/about/", :about - - result = tree.find "/about" - result.found?.should be_true - result.key.should eq("/about/") - result.payload.should eq(:about) - end - end - - context "nodes with shared parent" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/abc", :abc - tree.add "/axyz", :axyz - - result = tree.find("/abc") - result.found?.should be_true - result.key.should eq("/abc") - result.payload.should eq(:abc) - end - - it "finds matching path across separator" do - tree = Tree(Symbol).new - tree.add "/products", :products - tree.add "/product/new", :product_new - - result = tree.find("/products") - result.found?.should be_true - result.key.should eq("/products") - result.payload.should eq(:products) - end - - it "finds matching path across parents" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/admin/users", :users - tree.add "/admin/products", :products - tree.add "/blog/tags", :tags - tree.add "/blog/articles", :articles - - result = tree.find("/blog/tags/") - result.found?.should be_true - result.key.should eq("/blog/tags") - result.payload.should eq(:tags) - end - end - - context "unicode nodes with shared parent" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/日本語", :japanese - tree.add "/日本日本語は難しい", :japanese_is_difficult - - result = tree.find("/日本日本語は難しい/") - result.found?.should be_true - result.key.should eq("/日本日本語は難しい") - end - end - - context "dealing with catch all" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/*filepath", :all - tree.add "/about", :about - - result = tree.find("/src/file.png") - result.found?.should be_true - result.key.should eq("/*filepath") - result.payload.should eq(:all) - end - - it "returns catch all in parameters" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/*filepath", :all - tree.add "/about", :about - - result = tree.find("/src/file.png") - result.found?.should be_true - result.params.has_key?("filepath").should be_true - result.params["filepath"].should eq("src/file.png") - end - - it "returns optional catch all after slash" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/search/*extra", :extra - - result = tree.find("/search") - result.found?.should be_true - result.key.should eq("/search/*extra") - result.params.has_key?("extra").should be_true - result.params["extra"].empty?.should be_true - end - - it "returns optional catch all by globbing" do - tree = Tree(Symbol).new - tree.add "/members*trailing", :members_catch_all - - result = tree.find("/members") - result.found?.should be_true - result.key.should eq("/members*trailing") - result.params.has_key?("trailing").should be_true - result.params["trailing"].empty?.should be_true - end - - it "does not find when catch all is not full match" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/search/public/*query", :search - - result = tree.find("/search") - result.found?.should be_false - end - - it "does not find when path search has been exhausted" do - tree = Tree(Symbol).new - tree.add "/members/*trailing", :members_catch_all - - result = tree.find("/members2") - result.found?.should be_false - end - - it "does prefer specific path over catch all if both are present" do - tree = Tree(Symbol).new - tree.add "/members", :members - tree.add "/members*trailing", :members_catch_all - - result = tree.find("/members") - result.found?.should be_true - result.key.should eq("/members") - end - - it "does prefer catch all over specific key with partially shared key" do - tree = Tree(Symbol).new - tree.add "/orders/*anything", :orders_catch_all - tree.add "/orders/closed", :closed_orders - - result = tree.find("/orders/cancelled") - result.found?.should be_true - result.key.should eq("/orders/*anything") - result.params.has_key?("anything").should be_true - result.params["anything"].should eq("cancelled") - end - end - - context "dealing with named parameters" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/products", :products - tree.add "/products/:id", :product - tree.add "/products/:id/edit", :edit - - result = tree.find("/products/10") - result.found?.should be_true - result.key.should eq("/products/:id") - result.payload.should eq(:product) - end - - it "does not find partial matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/products", :products - tree.add "/products/:id/edit", :edit - - result = tree.find("/products/10") - result.found?.should be_false - end - - it "returns named parameters in result" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/products", :products - tree.add "/products/:id", :product - tree.add "/products/:id/edit", :edit - - result = tree.find("/products/10/edit") - result.found?.should be_true - result.params.has_key?("id").should be_true - result.params["id"].should eq("10") - end - - it "returns unicode values in parameters" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/language/:name", :language - tree.add "/language/:name/about", :about - - result = tree.find("/language/日本語") - result.found?.should be_true - result.params.has_key?("name").should be_true - result.params["name"].should eq("日本語") - end - - it "does prefer specific path over named parameters one if both are present" do - tree = Tree(Symbol).new - tree.add "/tag-edit/:tag", :edit_tag - tree.add "/tag-edit2", :alternate_tag_edit - - result = tree.find("/tag-edit2") - result.found?.should be_true - result.key.should eq("/tag-edit2") - end - - it "does prefer named parameter over specific key with partially shared key" do - tree = Tree(Symbol).new - tree.add "/orders/:id", :specific_order - tree.add "/orders/closed", :closed_orders - - result = tree.find("/orders/10") - result.found?.should be_true - result.key.should eq("/orders/:id") - result.params.has_key?("id").should be_true - result.params["id"].should eq("10") - end - end - - context "dealing with multiple named parameters" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/:section/:page", :static_page - - result = tree.find("/about/shipping") - result.found?.should be_true - result.key.should eq("/:section/:page") - result.payload.should eq(:static_page) - end - - it "returns named parameters in result" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/:section/:page", :static_page - - result = tree.find("/about/shipping") - result.found?.should be_true - - result.params.has_key?("section").should be_true - result.params["section"].should eq("about") - - result.params.has_key?("page").should be_true - result.params["page"].should eq("shipping") - end - end - - context "dealing with both catch all and named parameters" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/", :root - tree.add "/*filepath", :all - tree.add "/products", :products - tree.add "/products/:id", :product - tree.add "/products/:id/edit", :edit - tree.add "/products/featured", :featured - - result = tree.find("/products/1000") - result.found?.should be_true - result.key.should eq("/products/:id") - result.payload.should eq(:product) - - result = tree.find("/admin/articles") - result.found?.should be_true - result.key.should eq("/*filepath") - result.params["filepath"].should eq("admin/articles") - - result = tree.find("/products/featured") - result.found?.should be_true - result.key.should eq("/products/featured") - result.payload.should eq(:featured) - end - end - - context "dealing with named parameters and shared key" do - it "finds matching path" do - tree = Tree(Symbol).new - tree.add "/one/:id", :one - tree.add "/one-longer/:id", :two - - result = tree.find "/one-longer/10" - result.found?.should be_true - result.key.should eq("/one-longer/:id") - result.params["id"].should eq("10") - end - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr b/samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr deleted file mode 100644 index 30ddc0847bd5..000000000000 --- a/samples/client/petstore/crystal/lib/radix/spec/radix/version_spec.cr +++ /dev/null @@ -1,12 +0,0 @@ -require "../spec_helper" -require "yaml" - -describe "Radix::VERSION" do - it "matches version defined in shard.yml" do - contents = File.read(File.expand_path("../../../shard.yml", __FILE__)) - meta = YAML.parse(contents) - - meta["version"]?.should_not be_falsey - Radix::VERSION.should eq(meta["version"].as_s) - end -end diff --git a/samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr b/samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr deleted file mode 100644 index 6fa0c75d7598..000000000000 --- a/samples/client/petstore/crystal/lib/radix/spec/spec_helper.cr +++ /dev/null @@ -1,2 +0,0 @@ -require "spec" -require "../src/radix" diff --git a/samples/client/petstore/crystal/lib/radix/src/radix.cr b/samples/client/petstore/crystal/lib/radix/src/radix.cr deleted file mode 100644 index 97ff540e5ce7..000000000000 --- a/samples/client/petstore/crystal/lib/radix/src/radix.cr +++ /dev/null @@ -1,2 +0,0 @@ -require "./radix/tree" -require "./radix/version" diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/node.cr b/samples/client/petstore/crystal/lib/radix/src/radix/node.cr deleted file mode 100644 index a87d39ebd656..000000000000 --- a/samples/client/petstore/crystal/lib/radix/src/radix/node.cr +++ /dev/null @@ -1,207 +0,0 @@ -module Radix - # A Node represents one element in the structure of a [Radix tree](https://en.wikipedia.org/wiki/Radix_tree) - # - # Carries a *payload* and might also contain references to other nodes - # down in the organization inside *children*. - # - # Each node also carries identification in relation to the kind of key it - # contains, which helps with characteristics of the node like named - # parameters or catch all kind (globbing). - # - # Is not expected direct usage of a node but instead manipulation via - # methods within `Tree`. - class Node(T) - include Comparable(self) - - # :nodoc: - enum Kind : UInt8 - Normal - Named - Glob - end - - getter key - getter? placeholder - property children = [] of Node(T) - property! payload : T | Nil - - # :nodoc: - protected getter kind = Kind::Normal - - # Returns the priority of the Node based on it's *key* - # - # This value will be directly associated to the key size up until a - # special elements is found. - # - # ``` - # Radix::Node(Nil).new("a").priority - # # => 1 - # - # Radix::Node(Nil).new("abc").priority - # # => 3 - # - # Radix::Node(Nil).new("/src/*filepath").priority - # # => 5 - # - # Radix::Node(Nil).new("/search/:query").priority - # # => 8 - # ``` - getter priority : Int32 - - # Instantiate a Node - # - # - *key* - A `String` that represents this node. - # - *payload* - An optional payload for this node. - # - # When *payload* is not supplied, ensure the type of the node is provided - # instead: - # - # ``` - # # Good, node type is inferred from payload (Symbol) - # node = Radix::Node.new("/", :root) - # - # # Good, node type is now Int32 but payload is optional - # node = Radix::Node(Int32).new("/") - # - # # Error, node type cannot be inferred (compiler error) - # node = Radix::Node.new("/") - # ``` - def initialize(@key : String, @payload : T? = nil, @placeholder = false) - @priority = compute_priority - end - - # Compares this node against *other*, returning `-1`, `0` or `1` depending - # on whether this node differentiates from *other*. - # - # Comparison is done combining node's `kind` and `priority`. Nodes of - # same kind are compared by priority. Nodes of different kind are - # ranked. - # - # ### Normal nodes - # - # ``` - # node1 = Radix::Node(Nil).new("a") # normal - # node2 = Radix::Node(Nil).new("bc") # normal - # node1 <=> node2 # => 1 - # ``` - # - # ### Normal vs named or glob nodes - # - # ``` - # node1 = Radix::Node(Nil).new("a") # normal - # node2 = Radix::Node(Nil).new(":query") # named - # node3 = Radix::Node(Nil).new("*filepath") # glob - # node1 <=> node2 # => -1 - # node1 <=> node3 # => -1 - # ``` - # - # ### Named vs glob nodes - # - # ``` - # node1 = Radix::Node(Nil).new(":query") # named - # node2 = Radix::Node(Nil).new("*filepath") # glob - # node1 <=> node2 # => -1 - # ``` - def <=>(other : self) - result = kind <=> other.kind - return result if result != 0 - - other.priority <=> priority - end - - # Returns `true` if the node key contains a glob parameter in it - # (catch all) - # - # ``` - # node = Radix::Node(Nil).new("*filepath") - # node.glob? # => true - # - # node = Radix::Node(Nil).new("abc") - # node.glob? # => false - # ``` - def glob? - kind.glob? - end - - # Changes current *key* - # - # ``` - # node = Radix::Node(Nil).new("a") - # node.key - # # => "a" - # - # node.key = "b" - # node.key - # # => "b" - # ``` - # - # This will also result in change of node's `priority` - # - # ``` - # node = Radix::Node(Nil).new("a") - # node.priority - # # => 1 - # - # node.key = "abcdef" - # node.priority - # # => 6 - # ``` - def key=(@key) - # reset kind on change of key - @kind = Kind::Normal - @priority = compute_priority - end - - # Returns `true` if the node key contains a named parameter in it - # - # ``` - # node = Radix::Node(Nil).new(":query") - # node.named? # => true - # - # node = Radix::Node(Nil).new("abc") - # node.named? # => false - # ``` - def named? - kind.named? - end - - # Returns `true` if the node key does not contain an special parameter - # (named or glob) - # - # ``` - # node = Radix::Node(Nil).new("a") - # node.normal? # => true - # - # node = Radix::Node(Nil).new(":query") - # node.normal? # => false - # ``` - def normal? - kind.normal? - end - - # :nodoc: - private def compute_priority - reader = Char::Reader.new(@key) - - while reader.has_next? - case reader.current_char - when '*' - @kind = Kind::Glob - break - when ':' - @kind = Kind::Named - break - else - reader.next_char - end - end - - reader.pos - end - - # :nodoc: - protected def sort! - @children.sort! - end - end -end diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/result.cr b/samples/client/petstore/crystal/lib/radix/src/radix/result.cr deleted file mode 100644 index ad0a0bb5049a..000000000000 --- a/samples/client/petstore/crystal/lib/radix/src/radix/result.cr +++ /dev/null @@ -1,88 +0,0 @@ -require "./node" - -module Radix - # A Result is the comulative output of walking our [Radix tree](https://en.wikipedia.org/wiki/Radix_tree) - # `Radix::Tree` implementation. - # - # It provides helpers to retrieve the information obtained from walking - # our tree using `Radix::Tree#find` - # - # This information can be used to perform actions in case of the *path* - # that was looked on the Tree was found. - # - # A Result is also used recursively by `Radix::Tree#find` when collecting - # extra information like *params*. - class Result(T) - @key : String? - - getter params - getter! payload : T? - - # :nodoc: - def initialize - @nodes = [] of Node(T) - @params = {} of String => String - end - - # Returns whatever a *payload* was found by `Tree#find` and is part of - # the result. - # - # ``` - # result = Radix::Result(Symbol).new - # result.found? - # # => false - # - # root = Radix::Node(Symbol).new("/", :root) - # result.use(root) - # result.found? - # # => true - # ``` - def found? - payload? ? true : false - end - - # Returns a String built based on the nodes used in the result - # - # ``` - # node1 = Radix::Node(Symbol).new("/", :root) - # node2 = Radix::Node(Symbol).new("about", :about) - # - # result = Radix::Result(Symbol).new - # result.use node1 - # result.use node2 - # - # result.key - # # => "/about" - # ``` - # - # When no node has been used, returns an empty String. - # - # ``` - # result = Radix::Result(Nil).new - # result.key - # # => "" - # ``` - def key - @key ||= begin - String.build { |io| - @nodes.each do |node| - io << node.key - end - } - end - end - - # Adjust result information by using the details of the given `Node`. - # - # * Collect `Node` for future references. - # * Use *payload* if present. - def use(node : Node(T), payload = true) - # collect nodes - @nodes << node - - if payload && node.payload? - @payload = node.payload - end - end - end -end diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/tree.cr b/samples/client/petstore/crystal/lib/radix/src/radix/tree.cr deleted file mode 100644 index d6f4a9697f2a..000000000000 --- a/samples/client/petstore/crystal/lib/radix/src/radix/tree.cr +++ /dev/null @@ -1,472 +0,0 @@ -require "./node" -require "./result" - -module Radix - # A [Radix tree](https://en.wikipedia.org/wiki/Radix_tree) implementation. - # - # It allows insertion of *path* elements that will be organized inside - # the tree aiming to provide fast retrieval options. - # - # Each inserted *path* will be represented by a `Node` or segmented and - # distributed within the `Tree`. - # - # You can associate a *payload* at insertion which will be return back - # at retrieval time. - class Tree(T) - # :nodoc: - class DuplicateError < Exception - def initialize(path) - super("Duplicate trail found '#{path}'") - end - end - - # :nodoc: - class SharedKeyError < Exception - def initialize(new_key, existing_key) - super("Tried to place key '#{new_key}' at same level as '#{existing_key}'") - end - end - - # Returns the root `Node` element of the Tree. - # - # On a new tree instance, this will be a placeholder. - getter root : Node(T) - - def initialize - @root = Node(T).new("", placeholder: true) - end - - # Inserts given *path* into the Tree - # - # * *path* - An `String` representing the pattern to be inserted. - # * *payload* - Required associated element for this path. - # - # If no previous elements existed in the Tree, this will replace the - # defined placeholder. - # - # ``` - # tree = Radix::Tree(Symbol).new - # - # # / (:root) - # tree.add "/", :root - # - # # / (:root) - # # \-abc (:abc) - # tree.add "/abc", :abc - # - # # / (:root) - # # \-abc (:abc) - # # \-xyz (:xyz) - # tree.add "/abcxyz", :xyz - # ``` - # - # Nodes inside the tree will be adjusted to accommodate the different - # segments of the given *path*. - # - # ``` - # tree = Radix::Tree(Symbol).new - # - # # / (:root) - # tree.add "/", :root - # - # # / (:root) - # # \-products/:id (:product) - # tree.add "/products/:id", :product - # - # # / (:root) - # # \-products/ - # # +-featured (:featured) - # # \-:id (:product) - # tree.add "/products/featured", :featured - # ``` - # - # Catch all (globbing) and named parameters *path* will be located with - # lower priority against other nodes. - # - # ``` - # tree = Radix::Tree(Symbol).new - # - # # / (:root) - # tree.add "/", :root - # - # # / (:root) - # # \-*filepath (:all) - # tree.add "/*filepath", :all - # - # # / (:root) - # # +-about (:about) - # # \-*filepath (:all) - # tree.add "/about", :about - # ``` - def add(path : String, payload : T) - root = @root - - # replace placeholder with new node - if root.placeholder? - @root = Node(T).new(path, payload) - else - add path, payload, root - end - end - - # :nodoc: - private def add(path : String, payload : T, node : Node(T)) - key_reader = Char::Reader.new(node.key) - path_reader = Char::Reader.new(path) - - # move cursor position to last shared character between key and path - while path_reader.has_next? && key_reader.has_next? - break if path_reader.current_char != key_reader.current_char - - path_reader.next_char - key_reader.next_char - end - - # determine split point difference between path and key - # compare if path is larger than key - if path_reader.pos == 0 || - (path_reader.pos < path.bytesize && path_reader.pos >= node.key.bytesize) - # determine if a child of this node contains the remaining part - # of the path - added = false - - new_key = path_reader.string.byte_slice(path_reader.pos) - node.children.each do |child| - # if child's key starts with named parameter, compare key until - # separator (if present). - # Otherwise, compare just first character - if child.key[0]? == ':' && new_key[0]? == ':' - unless _same_key?(new_key, child.key) - raise SharedKeyError.new(new_key, child.key) - end - else - next unless child.key[0]? == new_key[0]? - end - - # when found, add to this child - added = true - add new_key, payload, child - break - end - - # if no existing child shared part of the key, add a new one - unless added - node.children << Node(T).new(new_key, payload) - end - - # adjust priorities - node.sort! - elsif path_reader.pos == path.bytesize && path_reader.pos == node.key.bytesize - # determine if path matches key and potentially be a duplicate - # and raise if is the case - - if node.payload? - raise DuplicateError.new(path) - else - # assign payload since this is an empty node - node.payload = payload - end - elsif path_reader.pos > 0 && path_reader.pos < node.key.bytesize - # determine if current node key needs to be split to accomodate new - # children nodes - - # build new node with partial key and adjust existing one - new_key = node.key.byte_slice(path_reader.pos) - swap_payload = node.payload? ? node.payload : nil - - new_node = Node(T).new(new_key, swap_payload) - new_node.children.replace(node.children) - - # clear payload and children (this is no longer and endpoint) - node.payload = nil - node.children.clear - - # adjust existing node key to new partial one - node.key = path_reader.string.byte_slice(0, path_reader.pos) - node.children << new_node - node.sort! - - # determine if path still continues - if path_reader.pos < path.bytesize - new_key = path.byte_slice(path_reader.pos) - node.children << Node(T).new(new_key, payload) - node.sort! - - # clear payload (no endpoint) - node.payload = nil - else - # this is an endpoint, set payload - node.payload = payload - end - end - end - - # Returns a `Result` instance after walking the tree looking up for - # *path* - # - # It will start walking the tree from the root node until a matching - # endpoint is found (or not). - # - # ``` - # tree = Radix::Tree(Symbol).new - # tree.add "/about", :about - # - # result = tree.find "/products" - # result.found? - # # => false - # - # result = tree.find "/about" - # result.found? - # # => true - # - # result.payload - # # => :about - # ``` - def find(path : String) - result = Result(T).new - root = @root - - # walk the tree from root (first time) - find path, result, root, first: true - - result - end - - # :nodoc: - private def find(path : String, result : Result, node : Node, first = false) - # special consideration when comparing the first node vs. others - # in case of node key and path being the same, return the node - # instead of walking character by character - if first && (path.bytesize == node.key.bytesize && path == node.key) && node.payload? - result.use node - return - end - - key_reader = Char::Reader.new(node.key) - path_reader = Char::Reader.new(path) - - # walk both path and key while both have characters and they continue - # to match. Consider as special cases named parameters and catch all - # rules. - while key_reader.has_next? && path_reader.has_next? && - (key_reader.current_char == '*' || - key_reader.current_char == ':' || - path_reader.current_char == key_reader.current_char) - case key_reader.current_char - when '*' - # deal with catch all (globbing) parameter - # extract parameter name from key (exclude *) and value from path - name = key_reader.string.byte_slice(key_reader.pos + 1) - value = path_reader.string.byte_slice(path_reader.pos) - - # add this to result - result.params[name] = value - - result.use node - return - when ':' - # deal with named parameter - # extract parameter name from key (from : until / or EOL) and - # value from path (same rules as key) - key_size = _detect_param_size(key_reader) - path_size = _detect_param_size(path_reader) - - # obtain key and value using calculated sizes - # for name: skip ':' by moving one character forward and compensate - # key size. - name = key_reader.string.byte_slice(key_reader.pos + 1, key_size - 1) - value = path_reader.string.byte_slice(path_reader.pos, path_size) - - # add this information to result - result.params[name] = value - - # advance readers positions - key_reader.pos += key_size - path_reader.pos += path_size - else - # move to the next character - key_reader.next_char - path_reader.next_char - end - end - - # check if we reached the end of the path & key - if !path_reader.has_next? && !key_reader.has_next? - # check endpoint - if node.payload? - result.use node - return - end - end - - # still path to walk, check for possible trailing slash or children - # nodes - if path_reader.has_next? - # using trailing slash? - if node.key.bytesize > 0 && - path_reader.pos + 1 == path.bytesize && - path_reader.current_char == '/' - result.use node - return - end - - # not found in current node, check inside children nodes - new_path = path_reader.string.byte_slice(path_reader.pos) - node.children.each do |child| - # check if child key is a named parameter, catch all or shares parts - # with new path - if (child.key[0]? == '*' || child.key[0]? == ':') || - _shared_key?(new_path, child.key) - # consider this node for key but don't use payload - result.use node, payload: false - - find new_path, result, child - return - end - end - - # path differs from key, no use searching anymore - return - end - - # key still contains characters to walk - if key_reader.has_next? - # determine if there is just a trailing slash? - if key_reader.pos + 1 == node.key.bytesize && - key_reader.current_char == '/' - result.use node - return - end - - # check if remaining part is catch all - if key_reader.pos < node.key.bytesize && - ((key_reader.current_char == '/' && key_reader.peek_next_char == '*') || - key_reader.current_char == '*') - # skip to '*' only if necessary - unless key_reader.current_char == '*' - key_reader.next_char - end - - # deal with catch all, but since there is nothing in the path - # return parameter as empty - name = key_reader.string.byte_slice(key_reader.pos + 1) - - result.params[name] = "" - - result.use node - return - end - end - end - - # :nodoc: - private def _detect_param_size(reader) - # save old position - old_pos = reader.pos - - # move forward until '/' or EOL is detected - while reader.has_next? - break if reader.current_char == '/' - - reader.next_char - end - - # calculate the size - count = reader.pos - old_pos - - # restore old position - reader.pos = old_pos - - count - end - - # Internal: allow inline comparison of *char* against 3 defined markers: - # - # - Path separator (`/`) - # - Named parameter (`:`) - # - Catch all (`*`) - @[AlwaysInline] - private def _check_markers(char) - (char == '/' || char == ':' || char == '*') - end - - # Internal: Compares *path* against *key* for differences until the - # following criteria is met: - # - # - End of *path* or *key* is reached. - # - A separator (`/`) is found. - # - A character between *path* or *key* differs - # - # ``` - # _same_key?("foo", "bar") # => false (mismatch at 1st character) - # _same_key?("foo/bar", "foo/baz") # => true (only `foo` is compared) - # _same_key?("zipcode", "zip") # => false (`zip` is shorter) - # ``` - private def _same_key?(path, key) - path_reader = Char::Reader.new(path) - key_reader = Char::Reader.new(key) - - different = false - - while (path_reader.has_next? && path_reader.current_char != '/') && - (key_reader.has_next? && key_reader.current_char != '/') - if path_reader.current_char != key_reader.current_char - different = true - break - end - - path_reader.next_char - key_reader.next_char - end - - (!different) && - (path_reader.current_char == '/' || !path_reader.has_next?) - end - - # Internal: Compares *path* against *key* for equality until one of the - # following criterias is met: - # - # - End of *path* or *key* is reached. - # - A separator (`/`) is found. - # - A named parameter (`:`) or catch all (`*`) is found. - # - A character in *path* differs from *key* - # - # ``` - # _shared_key?("foo", "bar") # => false (mismatch at 1st character) - # _shared_key?("foo/bar", "foo/baz") # => true (only `foo` is compared) - # _shared_key?("zipcode", "zip") # => true (only `zip` is compared) - # _shared_key?("s", "/new") # => false (1st character is a separator) - # ``` - private def _shared_key?(path, key) - path_reader = Char::Reader.new(path) - key_reader = Char::Reader.new(key) - - if (path_reader.current_char != key_reader.current_char) && - _check_markers(key_reader.current_char) - return false - end - - different = false - - while (path_reader.has_next? && !_check_markers(path_reader.current_char)) && - (key_reader.has_next? && !_check_markers(key_reader.current_char)) - if path_reader.current_char != key_reader.current_char - different = true - break - end - - path_reader.next_char - key_reader.next_char - end - - (!different) && - (!key_reader.has_next? || _check_markers(key_reader.current_char)) - end - - # :nodoc: - private def deprecation(message : String) - STDERR.puts message - STDERR.flush - end - end -end diff --git a/samples/client/petstore/crystal/lib/radix/src/radix/version.cr b/samples/client/petstore/crystal/lib/radix/src/radix/version.cr deleted file mode 100644 index f29f1a7c50a5..000000000000 --- a/samples/client/petstore/crystal/lib/radix/src/radix/version.cr +++ /dev/null @@ -1,3 +0,0 @@ -module Radix - VERSION = "0.3.9" -end From 41933c2ce07997073bc811e1e0d1f49294589f09 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 18:36:34 +0800 Subject: [PATCH 18/26] fix install crystal --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 180875ce6939..252d905b0745 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,6 +62,12 @@ addons: - petstore.swagger.io before_install: + # install crystal + - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash + - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - + - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list + - sudo apt-get update + - sudo apt install crystal - git clone https://github.com/wing328/swagger-samples - mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml & - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.0 @@ -81,12 +87,6 @@ before_install: #- docker pull swaggerapi/petstore #- docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore #- docker ps -a - # install crystal - - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - - - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list - - sudo apt-get update - - sudo apt install crystal # -- skip bash test to shorten build time # Add bats test framework and cURL for Bash script integration tests #- sudo add-apt-repository ppa:duggan/bats --yes From 48cb60c401b5188f4210d6b686803a92a9826347 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 19:04:36 +0800 Subject: [PATCH 19/26] sudo mvn --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 252d905b0745..f225b66a1ae2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,14 +62,14 @@ addons: - petstore.swagger.io before_install: + - git clone https://github.com/wing328/swagger-samples + - sudo mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml > /dev/null 2>&1 & # install crystal - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list - sudo apt-get update - sudo apt install crystal - - git clone https://github.com/wing328/swagger-samples - - mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml & - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.0 - export PATH="$HOME/.yarn/bin:$PATH" # install rust From fc79a8c46daba8b334763e65d3742e9a92bac46a Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sat, 2 Jan 2021 23:55:47 +0800 Subject: [PATCH 20/26] run server --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f225b66a1ae2..778ee4a16295 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ addons: before_install: - git clone https://github.com/wing328/swagger-samples - - sudo mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml > /dev/null 2>&1 & + - sudo mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml # install crystal - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - From 09fae5dae4a7fbb720cb5f527c6720a2d648037f Mon Sep 17 00:00:00 2001 From: William Cheng Date: Sun, 3 Jan 2021 00:16:02 +0800 Subject: [PATCH 21/26] using mvnw --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 778ee4a16295..fb9c507eab31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ addons: before_install: - git clone https://github.com/wing328/swagger-samples - - sudo mvn jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml + - sudo ./mvnw jetty:run -f swagger-samples/java/java-jersey-jaxrs-ci/pom.xml & # install crystal - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - From 1e364f463afadf9013d72848dcc804290c59d467 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 4 Jan 2021 14:31:53 +0800 Subject: [PATCH 22/26] fix form or body parameter --- .../src/main/resources/crystal/api_client.mustache | 6 +++--- samples/client/petstore/crystal/src/petstore/api_client.cr | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache index c091a9c1dfd5..5352175b00e4 100644 --- a/modules/openapi-generator/src/main/resources/crystal/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/crystal/api_client.mustache @@ -276,11 +276,11 @@ module {{moduleName}} if !post_body.nil? && !post_body.empty? # use JSON string in the payload - form = post_body + form_or_body = post_body else # use HTTP forms in the payload # TDOD use HTTP form encoding - form = form_params + form_or_body = form_params end request = Crest::Request.new(http_method, @@ -288,7 +288,7 @@ module {{moduleName}} params: query_params, headers: header_params, #cookies: cookie_params, # TODO add cookies support - form: form_params, + form: form_or_body, logging: @config.debugging, handle_errors: false ) diff --git a/samples/client/petstore/crystal/src/petstore/api_client.cr b/samples/client/petstore/crystal/src/petstore/api_client.cr index d2bef5bfdc40..d78cb31fc637 100644 --- a/samples/client/petstore/crystal/src/petstore/api_client.cr +++ b/samples/client/petstore/crystal/src/petstore/api_client.cr @@ -284,11 +284,11 @@ module Petstore if !post_body.nil? && !post_body.empty? # use JSON string in the payload - form = post_body + form_or_body = post_body else # use HTTP forms in the payload # TDOD use HTTP form encoding - form = form_params + form_or_body = form_params end request = Crest::Request.new(http_method, @@ -296,7 +296,7 @@ module Petstore params: query_params, headers: header_params, #cookies: cookie_params, # TODO add cookies support - form: form_params, + form: form_or_body, logging: @config.debugging, handle_errors: false ) From 7f33e5914cbf291b08be662e5a18e6cdbfea169c Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 4 Jan 2021 14:33:56 +0800 Subject: [PATCH 23/26] remove crystal from circleci --- CI/circle_parallel.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CI/circle_parallel.sh b/CI/circle_parallel.sh index 2051c57255bf..25705a08528d 100755 --- a/CI/circle_parallel.sh +++ b/CI/circle_parallel.sh @@ -49,14 +49,6 @@ elif [ "$NODE_INDEX" = "2" ]; then sudo apt-get -y install r-base R --version - # prepare crystal - curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash - curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - - echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list - sudo apt-get update - sudo apt install crystal - crystal version - # install curl sudo apt-get -y build-dep libcurl4-gnutls-dev sudo apt-get -y install libcurl4-gnutls-dev From 1e314066f34abaab2c1dbd2c8c128ca0deacec1b Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 4 Jan 2021 14:52:21 +0800 Subject: [PATCH 24/26] fix test --- samples/client/petstore/crystal/spec/api/pet_api_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr index d2b521a4a595..23bdfa818477 100644 --- a/samples/client/petstore/crystal/spec/api/pet_api_spec.cr +++ b/samples/client/petstore/crystal/spec/api/pet_api_spec.cr @@ -90,7 +90,7 @@ describe "PetApi" do result.id.should eq pet_id result.category.id.should eq pet_id + 10 result.category.name.should eq "crystal category" - result.name.should eq "category" + result.name.should eq "crystal" result.photo_urls.should eq ["https://crystal-lang.org"] result.status.should eq "available" result.tags[0].id.should eq pet_id + 100 From 51b598b0b18f7540ef3861513bf96b73df309e7f Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 4 Jan 2021 15:15:26 +0800 Subject: [PATCH 25/26] remove cache-from --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index df3920e5f0b1..c3611c57351a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,7 @@ jobs: - run: node --version # - run: docker pull openapitools/openapi-petstore # - run: docker run -d -e OPENAPI_BASE_PATH=/v3 -e DISABLE_API_KEY=1 -e DISABLE_OAUTH=1 -p 80:8080 openapitools/openapi-petstore - - run: docker pull --cache-from swaggerapi/petstore + - run: docker pull swaggerapi/petstore - run: docker run --name petstore.swagger -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - run: docker ps -a - run: sleep 30 From 6450e7e5ae41339ccba0e9072a32d9fb7677fc49 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Mon, 4 Jan 2021 16:40:29 +0800 Subject: [PATCH 26/26] update doc --- docs/contributing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing.md b/docs/contributing.md index 482ffecf4dd5..a791b0d6f1ba 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -53,6 +53,7 @@ Code change should conform to the programming style guide of the respective lang - C++: https://google.github.io/styleguide/cppguide.html - C++ (Tizen): https://wiki.tizen.org/Native_Platform_Coding_Idiom_and_Style_Guide#C.2B.2B_Coding_Style - Clojure: https://github.com/bbatsov/clojure-style-guide +- Crystal: https://crystal-lang.org/reference/conventions/coding_style.html - Dart: https://www.dartlang.org/guides/language/effective-dart/style - Elixir: https://github.com/christopheradams/elixir_style_guide - Eiffel: https://www.eiffel.org/doc/eiffel/Coding%20Standards

    m+=12=9k&=d&7^p|Bm!(&7ZaY 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

  • $xC{#v7+N0p9amkVWI=`4^;7&jmSYZYGU- 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