Browse Source

Add Lucky framework (#6069)

Matthew McGarvey 4 years ago
parent
commit
3cfcda124f
43 changed files with 468 additions and 0 deletions
  1. 6 0
      frameworks/Crystal/lucky/README.md
  2. 30 0
      frameworks/Crystal/lucky/benchmark_config.json
  3. 4 0
      frameworks/Crystal/lucky/config/colors.cr
  4. 19 0
      frameworks/Crystal/lucky/config/cookies.cr
  5. 11 0
      frameworks/Crystal/lucky/config/database.cr
  6. 13 0
      frameworks/Crystal/lucky/config/env.cr
  7. 3 0
      frameworks/Crystal/lucky/config/error_handler.cr
  8. 3 0
      frameworks/Crystal/lucky/config/html_page.cr
  9. 16 0
      frameworks/Crystal/lucky/config/log.cr
  10. 4 0
      frameworks/Crystal/lucky/config/route_helper.cr
  11. 15 0
      frameworks/Crystal/lucky/config/server.cr
  12. 17 0
      frameworks/Crystal/lucky/lucky.dockerfile
  13. 5 0
      frameworks/Crystal/lucky/run.sh
  14. 66 0
      frameworks/Crystal/lucky/shard.lock
  15. 16 0
      frameworks/Crystal/lucky/shard.yml
  16. 8 0
      frameworks/Crystal/lucky/src/actions/base_action.cr
  17. 6 0
      frameworks/Crystal/lucky/src/actions/db/index.cr
  18. 8 0
      frameworks/Crystal/lucky/src/actions/fortunes/index.cr
  19. 5 0
      frameworks/Crystal/lucky/src/actions/home/index.cr
  20. 5 0
      frameworks/Crystal/lucky/src/actions/json/index.cr
  21. 11 0
      frameworks/Crystal/lucky/src/actions/mixins/add_required_headers.cr
  22. 7 0
      frameworks/Crystal/lucky/src/actions/mixins/queries_parser.cr
  23. 5 0
      frameworks/Crystal/lucky/src/actions/plaintext/index.cr
  24. 12 0
      frameworks/Crystal/lucky/src/actions/queries/index.cr
  25. 13 0
      frameworks/Crystal/lucky/src/actions/updates/index.cr
  26. 16 0
      frameworks/Crystal/lucky/src/app.cr
  27. 2 0
      frameworks/Crystal/lucky/src/app_database.cr
  28. 20 0
      frameworks/Crystal/lucky/src/app_server.cr
  29. 6 0
      frameworks/Crystal/lucky/src/bench.cr
  30. 16 0
      frameworks/Crystal/lucky/src/handlers/charset_handler.cr
  31. 5 0
      frameworks/Crystal/lucky/src/models/base_model.cr
  32. 8 0
      frameworks/Crystal/lucky/src/models/fortune.cr
  33. 8 0
      frameworks/Crystal/lucky/src/models/world.cr
  34. 2 0
      frameworks/Crystal/lucky/src/operations/save_fortune.cr
  35. 2 0
      frameworks/Crystal/lucky/src/operations/save_world.cr
  36. 20 0
      frameworks/Crystal/lucky/src/pages/fortunes/index_page.cr
  37. 19 0
      frameworks/Crystal/lucky/src/pages/main_layout.cr
  38. 2 0
      frameworks/Crystal/lucky/src/queries/fortune_query.cr
  39. 2 0
      frameworks/Crystal/lucky/src/queries/world_query.cr
  40. 7 0
      frameworks/Crystal/lucky/src/serializers/base_serializer.cr
  41. 11 0
      frameworks/Crystal/lucky/src/serializers/world_serializer.cr
  42. 3 0
      frameworks/Crystal/lucky/src/shards.cr
  43. 11 0
      frameworks/Crystal/lucky/src/start_server.cr

+ 6 - 0
frameworks/Crystal/lucky/README.md

@@ -0,0 +1,6 @@
+[![github banner-short](https://user-images.githubusercontent.com/22394/26989908-dd99cc2c-4d22-11e7-9576-c6aeada2bd63.png)](http://luckyframework.org)
+
+This is the [Lucky](https://luckyframework.org/) test of the Framework Benchmarks.
+
+Lucky is a web framework written in Crystal. It helps you work quickly, catch bugs at compile time, and deliver blazing fast responses.
+

+ 30 - 0
frameworks/Crystal/lucky/benchmark_config.json

@@ -0,0 +1,30 @@
+{
+  "framework": "lucky",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "postgres",
+        "framework": "Lucky",
+        "language": "Crystal",
+        "flavor": "None",
+        "orm": "Full",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Lucky",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 4 - 0
frameworks/Crystal/lucky/config/colors.cr

@@ -0,0 +1,4 @@
+# This enables the color output when in development or test
+# Check out the Colorize docs for more information
+# https://crystal-lang.org/api/Colorize.html
+Colorize.enabled = false

+ 19 - 0
frameworks/Crystal/lucky/config/cookies.cr

@@ -0,0 +1,19 @@
+require "./server"
+
+Lucky::Session.configure do |settings|
+  settings.key = "_bench_session"
+end
+
+Lucky::CookieJar.configure do |settings|
+  settings.on_set = ->(cookie : HTTP::Cookie) {
+    # If ForceSSLHandler is enabled, only send cookies over HTTPS
+    cookie.secure(Lucky::ForceSSLHandler.settings.enabled)
+
+    # By default, don't allow reading cookies with JavaScript
+    cookie.http_only(true)
+
+    # You can set other defaults for cookies here. For example:
+    #
+    #    cookie.expires(1.year.from_now).domain("mydomain.com")
+  }
+end

+ 11 - 0
frameworks/Crystal/lucky/config/database.cr

@@ -0,0 +1,11 @@
+AppDatabase.configure do |settings|
+  settings.credentials = Avram::Credentials.parse(ENV["DATABASE_URL"])
+end
+
+Avram.configure do |settings|
+  settings.database_to_migrate = AppDatabase
+
+  # In production, allow lazy loading (N+1).
+  # In development and test, raise an error if you forget to preload associations
+  settings.lazy_load_enabled = Lucky::Env.production?
+end

+ 13 - 0
frameworks/Crystal/lucky/config/env.cr

@@ -0,0 +1,13 @@
+module Lucky::Env
+  extend self
+
+  {% for env in [:development, :test, :production] %}
+    def {{ env.id }}?
+      name == {{ env.id.stringify }}
+    end
+  {% end %}
+
+  def name
+    ENV["LUCKY_ENV"]? || "development"
+  end
+end

+ 3 - 0
frameworks/Crystal/lucky/config/error_handler.cr

@@ -0,0 +1,3 @@
+Lucky::ErrorHandler.configure do |settings|
+  settings.show_debug_output = false
+end

+ 3 - 0
frameworks/Crystal/lucky/config/html_page.cr

@@ -0,0 +1,3 @@
+Lucky::HTMLPage.configure do |settings|
+  settings.render_component_comments = false
+end

+ 16 - 0
frameworks/Crystal/lucky/config/log.cr

@@ -0,0 +1,16 @@
+require "file_utils"
+
+backend = Log::IOBackend.new
+backend.formatter = Dexter::JSONLogFormatter.proc
+Log.dexter.configure(:none, backend)
+
+# Lucky only logs when before/after pipes halt by redirecting, or rendering a
+# response. Pipes that run without halting are not logged.
+#
+# If you want to log every pipe that runs, set the log level to ':info'
+Lucky::ContinuedPipeLog.dexter.configure(:none)
+
+# Lucky only logs failed queries by default.
+#
+# Set the log to ':info' to log all queries
+Avram::QueryLog.dexter.configure(:none)

+ 4 - 0
frameworks/Crystal/lucky/config/route_helper.cr

@@ -0,0 +1,4 @@
+# This is used when generating URLs for your application
+Lucky::RouteHelper.configure do |settings|
+  settings.base_uri = "http://localhost:8080"
+end

+ 15 - 0
frameworks/Crystal/lucky/config/server.cr

@@ -0,0 +1,15 @@
+# Here is where you configure the Lucky server
+#
+# Look at config/route_helper.cr if you want to change the domain used when
+# generating links with `Action.url`.
+Lucky::Server.configure do |settings|
+  settings.secret_key_base = "u4PWnhZfOFXdTOtoiSBF+6jn0zHbYS6/yumo3WXYNSw"
+  settings.host = "0.0.0.0"
+  settings.port = 8080
+  settings.gzip_enabled = true
+  settings.asset_host = ""
+end
+
+Lucky::ForceSSLHandler.configure do |settings|
+  settings.enabled = false
+end

+ 17 - 0
frameworks/Crystal/lucky/lucky.dockerfile

@@ -0,0 +1,17 @@
+FROM crystallang/crystal:0.35.1
+
+WORKDIR /lucky
+COPY config config
+COPY src src
+COPY run.sh run.sh
+COPY shard.lock shard.lock
+COPY shard.yml shard.yml
+
+ENV GC_MARKERS 1
+ENV LUCKY_ENV production
+ENV DATABASE_URL postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world?initial_pool_size=56&max_idle_pool_size=56
+
+RUN apt-get install -yqq libyaml-dev
+RUN shards build bench --release --no-debug
+
+CMD bash run.sh

+ 5 - 0
frameworks/Crystal/lucky/run.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./bin/bench &
+
+wait

+ 66 - 0
frameworks/Crystal/lucky/shard.lock

@@ -0,0 +1,66 @@
+version: 2.0
+shards:
+  avram:
+    git: https://github.com/luckyframework/avram.git
+    version: 0.17.0
+
+  blank:
+    git: https://github.com/kostya/blank.git
+    version: 0.1.0
+
+  cry:
+    git: https://github.com/luckyframework/cry.git
+    version: 0.4.2
+
+  db:
+    git: https://github.com/crystal-lang/crystal-db.git
+    version: 0.9.0
+
+  dexter:
+    git: https://github.com/luckyframework/dexter.git
+    version: 0.3.1
+
+  exception_page:
+    git: https://github.com/crystal-loot/exception_page.git
+    version: 0.1.4
+
+  future:
+    git: https://github.com/crystal-community/future.cr.git
+    version: 0.1.0
+
+  habitat:
+    git: https://github.com/luckyframework/habitat.git
+    version: 0.4.4
+
+  lucky:
+    git: https://github.com/luckyframework/lucky.git
+    version: 0.24.0
+
+  lucky_cli:
+    git: https://github.com/luckyframework/lucky_cli.git
+    version: 0.24.0
+
+  lucky_router:
+    git: https://github.com/luckyframework/lucky_router.git
+    version: 0.3.1
+
+  pg:
+    git: https://github.com/will/crystal-pg.git
+    version: 0.21.1
+
+  pulsar:
+    git: https://github.com/luckyframework/pulsar.git
+    version: 0.2.1
+
+  shell-table:
+    git: https://github.com/luckyframework/shell-table.cr.git
+    version: 0.9.2+git.commit.078a04ea58ead5203bb435a3b5fff448ddabaeea
+
+  teeplate:
+    git: https://github.com/luckyframework/teeplate.git
+    version: 0.8.2
+
+  wordsmith:
+    git: https://github.com/luckyframework/wordsmith.git
+    version: 0.2.1
+

+ 16 - 0
frameworks/Crystal/lucky/shard.yml

@@ -0,0 +1,16 @@
+name: bench
+version: 0.1.0
+
+authors:
+  - your-name-here <your-email-here>
+
+targets:
+  bench:
+    main: src/bench.cr
+
+crystal: 0.35.1
+
+dependencies:
+  lucky:
+    github: luckyframework/lucky
+    version: ~> 0.24.0

+ 8 - 0
frameworks/Crystal/lucky/src/actions/base_action.cr

@@ -0,0 +1,8 @@
+abstract class BaseAction < Lucky::Action
+  ID_MAXIMUM = 10_000
+
+  include AddRequiredHeaders
+
+  disable_cookies
+  accepted_formats [:json, :html, :plain_text], default: :json
+end

+ 6 - 0
frameworks/Crystal/lucky/src/actions/db/index.cr

@@ -0,0 +1,6 @@
+class Db::Index < BaseAction
+  get "/db" do
+    world = WorldQuery.find(rand(1..ID_MAXIMUM))
+    json WorldSerializer.new(world)
+  end
+end

+ 8 - 0
frameworks/Crystal/lucky/src/actions/fortunes/index.cr

@@ -0,0 +1,8 @@
+class Fortunes::Index < BaseAction
+  get "/fortunes" do
+    fortunes = FortuneQuery.all.results
+    fortunes << Fortune.new(id: 0, message: "Additional fortune added at request time.")
+    fortunes.sort_by!(&.message)
+    html Fortunes::IndexPage, fortunes: fortunes
+  end
+end

+ 5 - 0
frameworks/Crystal/lucky/src/actions/home/index.cr

@@ -0,0 +1,5 @@
+class Home::Index < BaseAction
+  get "/" do
+    html Lucky::WelcomePage
+  end
+end

+ 5 - 0
frameworks/Crystal/lucky/src/actions/json/index.cr

@@ -0,0 +1,5 @@
+class Json::Index < BaseAction
+  get "/json" do
+    json({message: "Hello, World!"})
+  end
+end

+ 11 - 0
frameworks/Crystal/lucky/src/actions/mixins/add_required_headers.cr

@@ -0,0 +1,11 @@
+module AddRequiredHeaders
+  macro included
+    after add_required_headers
+  end
+
+  def add_required_headers
+    response.headers["Server"] = "Lucky"
+    response.headers["Date"] = HTTP.format_time(Time.local)
+    continue
+  end
+end

+ 7 - 0
frameworks/Crystal/lucky/src/actions/mixins/queries_parser.cr

@@ -0,0 +1,7 @@
+module QueriesParser
+  def queries_param : Int32
+    queries = params.get?("queries") || "1"
+    queries = queries.to_i? || 1
+    queries.clamp(1..500)
+  end
+end

+ 5 - 0
frameworks/Crystal/lucky/src/actions/plaintext/index.cr

@@ -0,0 +1,5 @@
+class Plaintext::Index < BaseAction
+  get "/plaintext" do
+    plain_text "Hello, World!"
+  end
+end

+ 12 - 0
frameworks/Crystal/lucky/src/actions/queries/index.cr

@@ -0,0 +1,12 @@
+class Queries::Index < BaseAction
+  include QueriesParser
+
+  get "/queries" do
+    results = (1..queries_param).map do
+      world = WorldQuery.find(rand(1..ID_MAXIMUM))
+      WorldSerializer.new(world)
+    end
+
+    json results
+  end
+end

+ 13 - 0
frameworks/Crystal/lucky/src/actions/updates/index.cr

@@ -0,0 +1,13 @@
+class Updates::Index < BaseAction
+  include QueriesParser
+
+  get "/updates" do
+    results = (1..queries_param).map do
+      world = WorldQuery.find(rand(1..ID_MAXIMUM))
+      world = SaveWorld.update!(world, randomnumber: rand(1..ID_MAXIMUM))
+      WorldSerializer.new(world)
+    end
+
+    json results
+  end
+end

+ 16 - 0
frameworks/Crystal/lucky/src/app.cr

@@ -0,0 +1,16 @@
+require "./shards"
+
+require "./app_database"
+require "./models/base_model"
+require "./models/**"
+require "./queries/**"
+require "./operations/**"
+require "./serializers/base_serializer"
+require "./serializers/**"
+require "./actions/mixins/**"
+require "./actions/**"
+require "./handlers/**"
+require "./pages/**"
+require "../config/env"
+require "../config/**"
+require "./app_server"

+ 2 - 0
frameworks/Crystal/lucky/src/app_database.cr

@@ -0,0 +1,2 @@
+class AppDatabase < Avram::Database
+end

+ 20 - 0
frameworks/Crystal/lucky/src/app_server.cr

@@ -0,0 +1,20 @@
+class AppServer < Lucky::BaseAppServer
+  # Learn about middleware with HTTP::Handlers:
+  # https://luckyframework.org/guides/http-and-routing/http-handlers
+  def middleware : Array(HTTP::Handler)
+    [
+      CharsetHandler.new,
+      Lucky::RouteHandler.new,
+      Lucky::RouteNotFoundHandler.new,
+    ] of HTTP::Handler
+  end
+
+  def protocol
+    "http"
+  end
+
+  def listen
+    server.bind_tcp(host, port, reuse_port: true)
+    server.listen
+  end
+end

+ 6 - 0
frameworks/Crystal/lucky/src/bench.cr

@@ -0,0 +1,6 @@
+# Typically you will not use or modify this file. 'shards build' and some
+# other crystal tools will sometimes use this.
+#
+# When this file is compiled/run it will require and run 'start_server',
+# which as its name implies will start the server for you app.
+require "./start_server"

+ 16 - 0
frameworks/Crystal/lucky/src/handlers/charset_handler.cr

@@ -0,0 +1,16 @@
+class CharsetHandler
+  include HTTP::Handler
+
+  def call(context : HTTP::Server::Context)
+    # Go to the next handler in the stack
+    call_next(context)
+
+    if html_content_type?(context.response)
+      context.response.content_type = "text/html; charset=utf-8"
+    end
+  end
+
+  private def html_content_type?(response)
+    response.headers["Content-Type"]? == "text/html"
+  end
+end

+ 5 - 0
frameworks/Crystal/lucky/src/models/base_model.cr

@@ -0,0 +1,5 @@
+abstract class BaseModel < Avram::Model
+  def self.database : Avram::Database.class
+    AppDatabase
+  end
+end

+ 8 - 0
frameworks/Crystal/lucky/src/models/fortune.cr

@@ -0,0 +1,8 @@
+class Fortune < BaseModel
+  skip_default_columns
+
+  table :fortune do
+    primary_key id : Int32
+    column message : String
+  end
+end

+ 8 - 0
frameworks/Crystal/lucky/src/models/world.cr

@@ -0,0 +1,8 @@
+class World < BaseModel
+  skip_default_columns
+
+  table :world do
+    primary_key id : Int32
+    column randomnumber : Int32
+  end
+end

+ 2 - 0
frameworks/Crystal/lucky/src/operations/save_fortune.cr

@@ -0,0 +1,2 @@
+class SaveFortune < Fortune::SaveOperation
+end

+ 2 - 0
frameworks/Crystal/lucky/src/operations/save_world.cr

@@ -0,0 +1,2 @@
+class SaveWorld < World::SaveOperation
+end

+ 20 - 0
frameworks/Crystal/lucky/src/pages/fortunes/index_page.cr

@@ -0,0 +1,20 @@
+class Fortunes::IndexPage < MainLayout
+  needs fortunes : Array(Fortune)
+
+  def content
+    table do
+      tr do
+        th "id"
+        th "message"
+      end
+
+      fortunes.each do |fortune|
+        tr do
+          td fortune.id
+          # html text escaped by default in Lucky
+          td fortune.message
+        end
+      end
+    end
+  end
+end

+ 19 - 0
frameworks/Crystal/lucky/src/pages/main_layout.cr

@@ -0,0 +1,19 @@
+abstract class MainLayout
+  include Lucky::HTMLPage
+
+  abstract def content
+
+  def render
+    html_doctype
+
+    html lang: "en" do
+      head do
+        title "Fortunes"
+      end
+
+      body do
+        content
+      end
+    end
+  end
+end

+ 2 - 0
frameworks/Crystal/lucky/src/queries/fortune_query.cr

@@ -0,0 +1,2 @@
+class FortuneQuery < Fortune::BaseQuery
+end

+ 2 - 0
frameworks/Crystal/lucky/src/queries/world_query.cr

@@ -0,0 +1,2 @@
+class WorldQuery < World::BaseQuery
+end

+ 7 - 0
frameworks/Crystal/lucky/src/serializers/base_serializer.cr

@@ -0,0 +1,7 @@
+abstract class BaseSerializer < Lucky::Serializer
+  def self.for_collection(collection : Enumerable, *args, **named_args)
+    collection.map do |object|
+      new(object, *args, **named_args)
+    end
+  end
+end

+ 11 - 0
frameworks/Crystal/lucky/src/serializers/world_serializer.cr

@@ -0,0 +1,11 @@
+class WorldSerializer < BaseSerializer
+  def initialize(@world : World)
+  end
+
+  def render
+    {
+      id:           @world.id,
+      randomNumber: @world.randomnumber,
+    }
+  end
+end

+ 3 - 0
frameworks/Crystal/lucky/src/shards.cr

@@ -0,0 +1,3 @@
+# Require your shards here
+require "avram"
+require "lucky"

+ 11 - 0
frameworks/Crystal/lucky/src/start_server.cr

@@ -0,0 +1,11 @@
+require "./app"
+
+Habitat.raise_if_missing_settings!
+
+app_server = AppServer.new
+
+Signal::INT.trap do
+  app_server.close
+end
+
+app_server.listen