diff --git a/.ameba.yml b/.ameba.yml new file mode 100644 index 000000000..664da22b2 --- /dev/null +++ b/.ameba.yml @@ -0,0 +1,3 @@ +Excluded: + - tmp/ + - myapp/ \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..71d82b97a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +on: + push: +jobs: + amber-spec: + + runs-on: ubuntu-latest + + container: + image: crystallang/crystal + + services: + redis: + image: redis + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + postgres: + image: postgres + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Update & install sqlite3 + run: apt update && apt install -y libsqlite3-dev redis + + - name: Download source + uses: actions/checkout@v3 + + - name: Install shards + run: shards install + + - name: Run tests + run: bin/amber_spec + env: + REDIS_URL: "redis://redis:6379" diff --git a/shard.yml b/shard.yml index cdba2d9dd..e4bcb95dc 100644 --- a/shard.yml +++ b/shard.yml @@ -1,6 +1,6 @@ name: amber -version: 1.2.1 +version: 1.3.0 authors: - Amber Team and Contributors @@ -52,7 +52,7 @@ dependencies: redis: github: stefanwille/crystal-redis - version: ~> 2.7.0 + version: ~> 2.8.0 shell-table: github: luckyframework/shell-table.cr @@ -85,4 +85,4 @@ dependencies: development_dependencies: ameba: github: crystal-ameba/ameba - version: ~> 0.13.4 + version: ~> 1.0.0 diff --git a/spec/amber/cli/commands/pipelines/pipelines_spec.cr b/spec/amber/cli/commands/pipelines/pipelines_spec.cr index 83b1d0208..4b5e1c482 100644 --- a/spec/amber/cli/commands/pipelines/pipelines_spec.cr +++ b/spec/amber/cli/commands/pipelines/pipelines_spec.cr @@ -59,7 +59,7 @@ module Amber::CLI end pipe_plugs.each do |pipe_name, plugs| - output_plugs = output_lines.select { |line| line.includes?(pipe_name) } + output_plugs = output_lines.select(&.includes?(pipe_name)) plugs.each_with_index do |plug, index| output_plug = output_plugs[index] (output_plug != nil).should be_true diff --git a/spec/amber/cli/commands/routes/routes_spec.cr b/spec/amber/cli/commands/routes/routes_spec.cr index 95d95f6c1..50cc44835 100644 --- a/spec/amber/cli/commands/routes/routes_spec.cr +++ b/spec/amber/cli/commands/routes/routes_spec.cr @@ -24,7 +24,7 @@ module Amber::CLI expected = "Amber::Controller::Static" output.should contain expected - line = output_lines.find("") { |this_line| this_line.includes? expected } + line = output_lines.find("", &.includes?(expected)) expectations = %w(get Amber::Controller::Static index static /*) expectations.each do |expectation| line.should contain expectation @@ -32,7 +32,7 @@ module Amber::CLI expected = "HomeController" output.should contain expected - line = output_lines.find("") { |this_line| this_line.includes? expected } + line = output_lines.find("", &.includes?(expected)) expectations = %w(get HomeController index web /) expectations.each do |expectation| line.should contain expectation @@ -77,7 +77,7 @@ module Amber::CLI expected = "websocket" output.should contain expected - line = output_lines.find("") { |this_line| this_line.includes? expected } + line = output_lines.find("", &.includes?(expected)) expectations = %w(websocket ElectricSocket web /electric) expectations.each do |expectation| line.should contain expectation diff --git a/spec/amber/router/session/redis_store_spec.cr b/spec/amber/router/session/redis_store_spec.cr index d951938e1..152d3d5a5 100644 --- a/spec/amber/router/session/redis_store_spec.cr +++ b/spec/amber/router/session/redis_store_spec.cr @@ -152,7 +152,7 @@ module Amber::Router::Session cookie_store.delete("ses") cookie_store.to_h.keys.should eq %w(a b c) - cookie_store.to_h["c"].should eq "x" + cookie_store.to_h.dig("c").should eq "x" end end diff --git a/src/amber/cli/commands/exec.cr b/src/amber/cli/commands/exec.cr index 6d87d7921..ad24a1323 100644 --- a/src/amber/cli/commands/exec.cr +++ b/src/amber/cli/commands/exec.cr @@ -32,7 +32,7 @@ module Amber::CLI _filename = if File.exists?(args.code) args.code elsif options.back.to_i(strict: false) > 0 - Dir.glob("./tmp/*_console.cr").sort.reverse[options.back.to_i(strict: false) - 1]? + Dir.glob("./tmp/*_console.cr").sort.reverse![options.back.to_i(strict: false) - 1]? end system("cp #{_filename} #{@filename}") if _filename diff --git a/src/amber/cli/commands/pipelines.cr b/src/amber/cli/commands/pipelines.cr index a15a07735..a4dc770b2 100644 --- a/src/amber/cli/commands/pipelines.cr +++ b/src/amber/cli/commands/pipelines.cr @@ -88,7 +88,7 @@ module Amber::CLI match = line.match(PIPELINE_REGEX) if match && (pipes = match[1]) - pipes = pipes.split(/,\s*/).map { |s| s.gsub(/[:\"]/, "") } + pipes = pipes.split(/,\s*/).map(&.gsub(/[:\"]/, "")) result << {pipes: pipes, plugs: [] of String} else raise BadRoutesException.new(FAILED_TO_PARSE_ERROR) @@ -113,7 +113,7 @@ module Amber::CLI table.border_color = :dark_gray unless options.no_color? if options.no_plugs? - result.map { |pipes_and_plugs| pipes_and_plugs[:pipes] }.flatten.uniq.each do |pipe| + result.flat_map { |pipes_and_plugs| pipes_and_plugs[:pipes] }.uniq!.each do |pipe| row = table.add_row row.add_column(pipe) end diff --git a/src/amber/cli/commands/plugin.cr b/src/amber/cli/commands/plugin.cr index 9101513cf..edb7fc9df 100644 --- a/src/amber/cli/commands/plugin.cr +++ b/src/amber/cli/commands/plugin.cr @@ -25,7 +25,7 @@ module Amber::CLI if Amber::Plugins::Plugin.can_generate?(args.name) template = Amber::Plugins::Plugin.new(args.name, "./src/plugins", options.args) - template.generate (options.uninstall? ? "uninstall" : "install") + template.generate(options.uninstall? ? "uninstall" : "install") end end diff --git a/src/amber/cli/commands/routes.cr b/src/amber/cli/commands/routes.cr index c1f4bb26a..d8244a3c0 100644 --- a/src/amber/cli/commands/routes.cr +++ b/src/amber/cli/commands/routes.cr @@ -126,7 +126,7 @@ module Amber::CLI private def print_routes_table_json puts routes.map { |route| - route.transform_keys { |key| key.to_s.downcase.gsub(' ', '_') } + route.transform_keys(&.to_s.downcase.gsub(' ', '_')) }.to_json end diff --git a/src/amber/cli/helpers/process_runner.cr b/src/amber/cli/helpers/process_runner.cr index 08ae839d9..1b753a516 100644 --- a/src/amber/cli/helpers/process_runner.cr +++ b/src/amber/cli/helpers/process_runner.cr @@ -79,7 +79,7 @@ module Sentry private def check_processes @processes.each do |task, procs| # clean up process list and restart if terminated - if procs.any? + if !procs.empty? procs.reject!(&.terminated?) if procs.empty? diff --git a/src/amber/cli/recipes/scaffold/controller.cr b/src/amber/cli/recipes/scaffold/controller.cr index 846761aa7..027c6fc87 100644 --- a/src/amber/cli/recipes/scaffold/controller.cr +++ b/src/amber/cli/recipes/scaffold/controller.cr @@ -24,7 +24,7 @@ module Amber::Recipes::Scaffold @fields += %w(created_at:time updated_at:time).map do |f| Amber::CLI::Field.new(f, hidden: true, database: @database) end - @visible_fields = @fields.reject { |f| f.hidden } + @visible_fields = @fields.reject(&.hidden) field_hash @template = RecipeFetcher.new("scaffold", @recipe).fetch diff --git a/src/amber/cli/recipes/scaffold/view.cr b/src/amber/cli/recipes/scaffold/view.cr index 2eab38424..d1aa4879c 100644 --- a/src/amber/cli/recipes/scaffold/view.cr +++ b/src/amber/cli/recipes/scaffold/view.cr @@ -21,7 +21,7 @@ module Amber::Recipes::Scaffold Amber::CLI::Field.new(f, hidden: true, database: @database) end - @visible_fields = @fields.reject { |f| f.hidden } + @visible_fields = @fields.reject(&.hidden) @template = RecipeFetcher.new("scaffold", @recipe).fetch unless @template.nil? diff --git a/src/amber/cli/templates/app/.travis.yml b/src/amber/cli/templates/app/.travis.yml deleted file mode 100644 index 846a2fbc7..000000000 --- a/src/amber/cli/templates/app/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: crystal - -script: - - crystal spec - - crystal tool format --check - - bin/ameba diff --git a/src/amber/cli/templates/app/config/environments/test.yml.ecr b/src/amber/cli/templates/app/config/environments/test.yml.ecr index acd13de38..f440f874f 100644 --- a/src/amber/cli/templates/app/config/environments/test.yml.ecr +++ b/src/amber/cli/templates/app/config/environments/test.yml.ecr @@ -19,7 +19,7 @@ redis_url: "redis://localhost:6379" when "mysql" -%> database_url: mysql://root@localhost:3306/<%= database_name %>_test <% when "pg" -%> -database_url: postgres://postgres:password@localhost:5432/<%= database_name %>_test +database_url: <%= "postgres://postgres:#{ENV["POSTGRES_PASSWORD"]? || "password"}@localhost:5432/#{database_name}_test" %> <% when "sqlite" -%> database_url: sqlite3:./db/<%= database_name %>_test.db <% else diff --git a/src/amber/cli/templates/app/package.json.ecr b/src/amber/cli/templates/app/package.json.ecr index b8282a501..a3b680617 100644 --- a/src/amber/cli/templates/app/package.json.ecr +++ b/src/amber/cli/templates/app/package.json.ecr @@ -20,8 +20,13 @@ "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.1", "mini-css-extract-plugin": "^1.6.0", + <% if `node -v`.matches?(/v14|v16/) -%> "node-sass": "^5.0.0", "sass-loader": "^11.0.1", + <% else -%> + "node-sass": "^7.0.0", + "sass-loader": "^13.0.2", + <% end -%> "webpack": "^5.36.2", "webpack-cli": "^4.6.0", "webpack-merge": "^5.7.3" diff --git a/src/amber/cli/templates/app/src/views/home/index.ecr.ecr b/src/amber/cli/templates/app/src/views/home/index.ecr.ecr index f63adc2ce..1525fc836 100644 --- a/src/amber/cli/templates/app/src/views/home/index.ecr.ecr +++ b/src/amber/cli/templates/app/src/views/home/index.ecr.ecr @@ -4,7 +4,8 @@

Thank you for trying out the Amber Framework. We are working hard to provide a super fast and reliable framework that provides all the productivity tools you are used to without sacrificing the speed.

Getting Started with Amber Framework - List of Awesome Crystal projects and shards + List of Awesome C + Join the Amber Discord!
diff --git a/src/amber/cli/templates/app/src/views/home/index.slang.ecr b/src/amber/cli/templates/app/src/views/home/index.slang.ecr index 562ff3a99..a7080da73 100644 --- a/src/amber/cli/templates/app/src/views/home/index.slang.ecr +++ b/src/amber/cli/templates/app/src/views/home/index.slang.ecr @@ -5,3 +5,4 @@ .list-group a.list-group-item.list-group-item-action target="_blank" href="https://docs.amberframework.org" Getting Started with Amber Framework a.list-group-item.list-group-item-action target="_blank" href="https://github.com/veelenga/awesome-crystal" List of Awesome Crystal projects and shards + a.list-grouo-item.list-group-item-action target="_blank" href="https://discord.gg/vwvP5zakSn" Join the Amber Discord! diff --git a/src/amber/controller/helpers/responders.cr b/src/amber/controller/helpers/responders.cr index 5abe63606..b5e919a0c 100644 --- a/src/amber/controller/helpers/responders.cr +++ b/src/amber/controller/helpers/responders.cr @@ -90,7 +90,7 @@ module Amber::Controller::Helpers accept = context.request.headers["Accept"]? if accept && !accept.empty? accepts = accept.split(";").first?.try(&.split(Content::ACCEPT_SEPARATOR_REGEX)) - return accepts if !accepts.nil? && accepts.any? + return accepts if !accepts.nil? && !accepts.empty? end end diff --git a/src/amber/pipes/cors.cr b/src/amber/pipes/cors.cr index 9dd3b571b..88b9b2499 100644 --- a/src/amber/pipes/cors.cr +++ b/src/amber/pipes/cors.cr @@ -63,7 +63,7 @@ module Amber private def put_response_headers(response) response.headers[Headers::ALLOW_CREDENTIALS] = @credentials.to_s if @credentials response.headers[Headers::ALLOW_ORIGIN] = @origin.request_origin.not_nil! - response.headers[Headers::VARY] = vary unless @origin.any? + response.headers[Headers::VARY] = vary unless @origin.any_origin? end private def vary @@ -122,7 +122,7 @@ module Amber def match?(request) return false if @origins.empty? return false unless origin_header?(request) - return true if any? + return true if any_origin? @origins.any? do |origin| case origin @@ -132,7 +132,7 @@ module Amber end end - def any? + def any_origin? @origins.includes? "*" end diff --git a/src/amber/router/cookies/store.cr b/src/amber/router/cookies/store.cr index 6016cfe69..1f0a455ae 100644 --- a/src/amber/router/cookies/store.cr +++ b/src/amber/router/cookies/store.cr @@ -90,6 +90,9 @@ module Amber::Router::Cookies expires : Time? = nil, domain : String? = nil, secure : Bool = false, http_only : Bool = false, extension : String? = nil) + name = URI.encode_www_form(name) + value = URI.encode_www_form(value) + if @cookies[name]? != value || expires @cookies[name] = value @set_cookies[name] = HTTP::Cookie.new(name, value, path, expires, domain, secure, http_only, extension) diff --git a/src/amber/router/session/abstract_store.cr b/src/amber/router/session/abstract_store.cr index 88f765198..1b79819cb 100644 --- a/src/amber/router/session/abstract_store.cr +++ b/src/amber/router/session/abstract_store.cr @@ -3,7 +3,7 @@ module Amber::Router::Session abstract class AbstractStore abstract def id abstract def destroy - abstract def update(other_hash : Hash(String | Symbol, String)) + abstract def update(hash : Hash(String | Symbol, String)) abstract def set_session abstract def current_session end diff --git a/src/amber/router/session/redis_store.cr b/src/amber/router/session/redis_store.cr index 0e85e8e2e..8815ec606 100644 --- a/src/amber/router/session/redis_store.cr +++ b/src/amber/router/session/redis_store.cr @@ -56,7 +56,7 @@ module Amber::Router::Session end def to_h - store.hgetall(session_id).each_slice(2).to_h + store.hgetall(session_id) end def update(hash : Hash(String | Symbol, String)) diff --git a/src/amber/version.cr b/src/amber/version.cr index be5790c98..bc3427afb 100644 --- a/src/amber/version.cr +++ b/src/amber/version.cr @@ -1,3 +1,3 @@ module Amber - VERSION = "1.2.2" + VERSION = "1.3.0" end diff --git a/src/amber/websockets/client_socket.cr b/src/amber/websockets/client_socket.cr index 17fb0d818..f7a77de47 100644 --- a/src/amber/websockets/client_socket.cr +++ b/src/amber/websockets/client_socket.cr @@ -45,7 +45,7 @@ module Amber def self.get_topic_channel(topic_path) topic_channels = @@channels.select { |ch| WebSockets.topic_path(ch[:path]) == topic_path } - return topic_channels[0][:channel] if topic_channels.any? + return topic_channels[0][:channel] if !topic_channels.empty? end # Broadcast a message to all subscribers of the topic