🧚 A practical web framework for Gleam

Compare changes

Choose any two refs to compare.

Changed files
+128 -119
.github
workflows
examples
00-hello-world
src
01-routing
src
02-working-with-form-data
src
05-using-a-database
src
app
06-serving-static-assets
src
08-working-with-cookies
src
09-configuring-default-responses
10-working-with-files
src
src
test
+1 -1
.github/workflows/ci.yml
··· 13 13 - uses: actions/checkout@v4 14 14 - uses: erlef/setup-beam@v1 15 15 with: 16 - otp-version: "26.0" 16 + otp-version: "27.0" 17 17 gleam-version: "1.4.0" 18 18 rebar3-version: "3" 19 19 # elixir-version: "1.14.2"
+14 -2
CHANGELOG.md
··· 1 1 # Changelog 2 2 3 - ## v1.1.0 - Unreleased 3 + ## v1.3.0 - 2024-11-21 4 4 5 - - Rather than using `/tmp`, the platform-specific temporary directory is detected used 5 + - Updated for `gleam_stdlib` v0.43.0. 6 + 7 + ## v1.2.0 - 2024-10-09 8 + 9 + - The requirement for `gleam_json` has been relaxed to < 3.0.0. 10 + - The requirement for `mist` has been relaxed to < 4.0.0. 11 + - The Gleam version requirement has been corrected to `>= 1.1.0` from the 12 + previously inaccurate `">= 0.32.0`. 13 + 14 + ## v1.1.0 - 2024-08-23 15 + 16 + - Rather than using `/tmp`, the platform-specific temporary directory is 17 + detected used. 6 18 7 19 ## v1.0.0 - 2024-08-21 8 20
+3 -3
examples/00-hello-world/src/app/router.gleam
··· 1 - import wisp.{type Request, type Response} 2 - import gleam/string_builder 3 1 import app/web 2 + import gleam/string_tree 3 + import wisp.{type Request, type Response} 4 4 5 5 /// The HTTP request handler- your application! 6 6 /// ··· 9 9 use _req <- web.middleware(req) 10 10 11 11 // Later we'll use templates, but for now a string will do. 12 - let body = string_builder.from_string("<h1>Hello, Joe!</h1>") 12 + let body = string_tree.from_string("<h1>Hello, Joe!</h1>") 13 13 14 14 // Return a 200 OK response with the body and a HTML content type. 15 15 wisp.html_response(body, 200)
+7 -7
examples/01-routing/src/app/router.gleam
··· 1 - import wisp.{type Request, type Response} 2 - import gleam/string_builder 3 - import gleam/http.{Get, Post} 4 1 import app/web 2 + import gleam/http.{Get, Post} 3 + import gleam/string_tree 4 + import wisp.{type Request, type Response} 5 5 6 6 pub fn handle_request(req: Request) -> Response { 7 7 use req <- web.middleware(req) ··· 31 31 // used to return a 405: Method Not Allowed response for all other methods. 32 32 use <- wisp.require_method(req, Get) 33 33 34 - let html = string_builder.from_string("Hello, Joe!") 34 + let html = string_tree.from_string("Hello, Joe!") 35 35 wisp.ok() 36 36 |> wisp.html_body(html) 37 37 } ··· 48 48 49 49 fn list_comments() -> Response { 50 50 // In a later example we'll show how to read from a database. 51 - let html = string_builder.from_string("Comments!") 51 + let html = string_tree.from_string("Comments!") 52 52 wisp.ok() 53 53 |> wisp.html_body(html) 54 54 } 55 55 56 56 fn create_comment(_req: Request) -> Response { 57 57 // In a later example we'll show how to parse data from the request body. 58 - let html = string_builder.from_string("Created") 58 + let html = string_tree.from_string("Created") 59 59 wisp.created() 60 60 |> wisp.html_body(html) 61 61 } ··· 66 66 // The `id` path parameter has been passed to this function, so we could use 67 67 // it to look up a comment in a database. 68 68 // For now we'll just include in the response body. 69 - let html = string_builder.from_string("Comment with id " <> id) 69 + let html = string_tree.from_string("Comment with id " <> id) 70 70 wisp.ok() 71 71 |> wisp.html_body(html) 72 72 }
+3 -3
examples/02-working-with-form-data/src/app/router.gleam
··· 2 2 import gleam/http.{Get, Post} 3 3 import gleam/list 4 4 import gleam/result 5 - import gleam/string_builder 5 + import gleam/string_tree 6 6 import wisp.{type Request, type Response} 7 7 8 8 pub fn handle_request(req: Request) -> Response { ··· 21 21 // In a larger application a template library or HTML form library might 22 22 // be used here instead of a string literal. 23 23 let html = 24 - string_builder.from_string( 24 + string_tree.from_string( 25 25 "<form method='post'> 26 26 <label>Title: 27 27 <input type='text' name='title'> ··· 60 60 case result { 61 61 Ok(content) -> { 62 62 wisp.ok() 63 - |> wisp.html_body(string_builder.from_string(content)) 63 + |> wisp.html_body(string_tree.from_string(content)) 64 64 } 65 65 Error(_) -> { 66 66 wisp.bad_request()
+2 -2
examples/05-using-a-database/src/app/web/people.gleam
··· 1 1 import app/web.{type Context} 2 + import gleam/dict 2 3 import gleam/dynamic.{type Dynamic} 3 4 import gleam/http.{Get, Post} 4 5 import gleam/json 5 - import gleam/dict 6 6 import gleam/result.{try} 7 7 import tiny_database 8 8 import wisp.{type Request, type Response} ··· 122 122 // In this example we are not going to be reporting specific errors to the 123 123 // user, so we can discard the error and replace it with Nil. 124 124 result 125 - |> result.nil_error 125 + |> result.replace_error(Nil) 126 126 } 127 127 128 128 /// Save a person to the database and return the id of the newly created record.
+3 -3
examples/06-serving-static-assets/src/app/router.gleam
··· 1 - import wisp.{type Request, type Response} 2 - import gleam/string_builder 3 1 import app/web.{type Context} 2 + import gleam/string_tree 3 + import wisp.{type Request, type Response} 4 4 5 5 const html = "<!DOCTYPE html> 6 6 <html lang=\"en\"> ··· 18 18 19 19 pub fn handle_request(req: Request, ctx: Context) -> Response { 20 20 use _req <- web.middleware(req, ctx) 21 - wisp.html_response(string_builder.from_string(html), 200) 21 + wisp.html_response(string_tree.from_string(html), 200) 22 22 }
+3 -3
examples/08-working-with-cookies/src/app/router.gleam
··· 1 1 import app/web 2 2 import gleam/http.{Delete, Get, Post} 3 3 import gleam/list 4 - import gleam/string_builder 4 + import gleam/string_tree 5 5 import wisp.{type Request, type Response} 6 6 7 7 const cookie_name = "id" ··· 25 25 " <button type='submit'>Log out</button>", 26 26 "</form>", 27 27 ] 28 - |> string_builder.from_strings 28 + |> string_tree.from_strings 29 29 |> wisp.html_response(200) 30 30 } 31 31 Error(_) -> { ··· 52 52 <button type='submit'>Log in</button> 53 53 </form> 54 54 " 55 - |> string_builder.from_string 55 + |> string_tree.from_string 56 56 |> wisp.html_response(200) 57 57 } 58 58
+1 -1
examples/09-configuring-default-responses/README.md
··· 1 - # Wisp Example: Working with form data 1 + # Wisp Example: Configuring default responses 2 2 3 3 ```sh 4 4 gleam run # Run the server
+2 -2
examples/09-configuring-default-responses/src/app/router.gleam
··· 1 1 import app/web 2 - import gleam/string_builder 2 + import gleam/string_tree 3 3 import wisp.{type Request, type Response} 4 4 5 5 pub fn handle_request(req: Request) -> Response { ··· 9 9 // This request returns a non-empty body. 10 10 [] -> { 11 11 "<h1>Hello, Joe!</h1>" 12 - |> string_builder.from_string 12 + |> string_tree.from_string 13 13 |> wisp.html_response(200) 14 14 } 15 15
+6 -6
examples/09-configuring-default-responses/src/app/web.gleam
··· 1 - import wisp 2 1 import gleam/bool 3 - import gleam/string_builder 2 + import gleam/string_tree 3 + import wisp 4 4 5 5 pub fn middleware( 6 6 req: wisp.Request, ··· 32 32 case response.status { 33 33 404 | 405 -> 34 34 "<h1>There's nothing here</h1>" 35 - |> string_builder.from_string 35 + |> string_tree.from_string 36 36 |> wisp.html_body(response, _) 37 37 38 38 400 | 422 -> 39 39 "<h1>Bad request</h1>" 40 - |> string_builder.from_string 40 + |> string_tree.from_string 41 41 |> wisp.html_body(response, _) 42 42 43 43 413 -> 44 44 "<h1>Request entity too large</h1>" 45 - |> string_builder.from_string 45 + |> string_tree.from_string 46 46 |> wisp.html_body(response, _) 47 47 48 48 500 -> 49 49 "<h1>Internal server error</h1>" 50 - |> string_builder.from_string 50 + |> string_tree.from_string 51 51 |> wisp.html_body(response, _) 52 52 53 53 // For other status codes redirect to the home page
+5 -5
examples/10-working-with-files/src/app/router.gleam
··· 1 1 import app/web 2 + import gleam/bytes_tree 2 3 import gleam/http.{Get, Post} 3 4 import gleam/list 4 5 import gleam/result 5 - import gleam/string_builder 6 - import gleam/bytes_builder 6 + import gleam/string_tree 7 7 import wisp.{type Request, type Response} 8 8 9 9 pub fn handle_request(req: Request) -> Response { ··· 35 35 fn show_home(req: Request) -> Response { 36 36 use <- wisp.require_method(req, Get) 37 37 html 38 - |> string_builder.from_string 38 + |> string_tree.from_string 39 39 |> wisp.html_response(200) 40 40 } 41 41 ··· 45 45 // In this case we have the file contents in memory as a string. 46 46 // This is good if we have just made the file, but if the file already exists 47 47 // on the disc then the approach in the next function is more efficient. 48 - let file_contents = bytes_builder.from_string("Hello, Joe!") 48 + let file_contents = bytes_tree.from_string("Hello, Joe!") 49 49 50 50 wisp.ok() 51 51 |> wisp.set_header("content-type", "text/plain") ··· 107 107 case result { 108 108 Ok(name) -> { 109 109 { "<p>Thank you for your file!" <> name <> "</p>" <> html } 110 - |> string_builder.from_string 110 + |> string_tree.from_string 111 111 |> wisp.html_response(200) 112 112 } 113 113 Error(_) -> {
+6 -6
gleam.toml
··· 1 1 name = "wisp" 2 - version = "1.0.0" 3 - gleam = ">= 0.32.0" 2 + version = "1.3.0" 3 + gleam = ">= 1.4.0" 4 4 description = "A practical web framework for Gleam" 5 5 licences = ["Apache-2.0"] 6 6 ··· 12 12 gleam_crypto = ">= 1.0.0 and < 2.0.0" 13 13 gleam_erlang = ">= 0.21.0 and < 2.0.0" 14 14 gleam_http = ">= 3.5.0 and < 4.0.0" 15 - gleam_json = ">= 0.6.0 and < 2.0.0" 16 - gleam_stdlib = ">= 0.29.0 and < 2.0.0" 17 - mist = ">= 1.2.0 and < 3.0.0" 15 + gleam_json = ">= 0.6.0 and < 3.0.0" 16 + gleam_stdlib = ">= 0.43.0 and < 2.0.0" 17 + mist = ">= 1.2.0 and < 4.0.0" 18 18 simplifile = ">= 2.0.0 and < 3.0.0" 19 19 marceau = ">= 1.1.0 and < 2.0.0" 20 20 logging = ">= 1.2.0 and < 2.0.0" 21 21 directories = ">= 1.0.0 and < 2.0.0" 22 22 23 23 [dev-dependencies] 24 - gleeunit = "~> 1.0" 24 + gleeunit = ">= 1.0.0 and < 2.0.0"
+19 -19
manifest.toml
··· 3 3 4 4 packages = [ 5 5 { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, 6 - { name = "directories", version = "1.0.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_erlang", "gleam_stdlib", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "8691C1581C6D8FB7EF390127B096B308D2ED05BCA90468B1D1218DE5AB5C4B04" }, 7 - { name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, 6 + { name = "directories", version = "1.1.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "BDA521A4EB9EE3A7894F0DC863797878E91FF5C7826F7084B2E731E208BDB076" }, 7 + { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" }, 8 8 { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, 9 - { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, 10 - { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, 11 - { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, 12 - { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, 13 - { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, 14 - { name = "gleam_otp", version = "0.11.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "54C04621B3B5C8E5AE5DC7EF250EEA3C9109FB4F729723A865E41211E77EBEE0" }, 15 - { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, 9 + { name = "filepath", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "67A6D15FB39EEB69DD31F8C145BB5A421790581BD6AA14B33D64D5A55DBD6587" }, 10 + { name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" }, 11 + { name = "gleam_erlang", version = "0.30.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "760618870AE4A497B10C73548E6E44F43B76292A54F0207B3771CBB599C675B4" }, 12 + { name = "gleam_http", version = "3.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "A9EE0722106FCCAB8AD3BF9D0A3EFF92BFE8561D59B83BAE96EB0BE1938D4E0F" }, 13 + { name = "gleam_json", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "0A57FB5666E695FD2BEE74C0428A98B0FC11A395D2C7B4CDF5E22C5DD32C74C6" }, 14 + { name = "gleam_otp", version = "0.14.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5A8CE8DBD01C29403390A7BD5C0A63D26F865C83173CF9708E6E827E53159C65" }, 15 + { name = "gleam_stdlib", version = "0.43.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "69EF22E78FDCA9097CBE7DF91C05B2A8B5436826D9F66680D879182C0860A747" }, 16 16 { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, 17 - { name = "glisten", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "48EF7F6D1DCA877C2F49AF35CC33946C7129EEB05A114758A2CC569C708BFAF8" }, 17 + { name = "glisten", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "912132751031473CB38F454120124FFC96AF6B0EA33D92C9C90DB16327A2A972" }, 18 18 { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, 19 19 { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 20 20 { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, 21 - { name = "marceau", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "5188D643C181EE350D8A20A3BDBD63AF7B6C505DE333CFBE05EF642ADD88A59B" }, 22 - { name = "mist", version = "2.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "981F12FC8BA0656B40099EC876D6F2BEE7B95593610F342E9AB0DC4E663A932F" }, 21 + { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" }, 22 + { name = "mist", version = "3.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "CDA1A74E768419235E16886463EC4722EFF4AB3F8D820A76EAD45D7C167D7282" }, 23 + { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, 23 24 { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, 24 - { name = "simplifile", version = "2.0.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "5FFEBD0CAB39BDD343C3E1CCA6438B2848847DC170BA2386DF9D7064F34DF000" }, 25 - { name = "telemetry", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "DAD9CE9D8EFFC621708F99EAC538EF1CBE05D6A874DD741DE2E689C47FEAFED5" }, 26 - { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, 25 + { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, 26 + { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" }, 27 27 ] 28 28 29 29 [requirements] ··· 32 32 gleam_crypto = { version = ">= 1.0.0 and < 2.0.0" } 33 33 gleam_erlang = { version = ">= 0.21.0 and < 2.0.0" } 34 34 gleam_http = { version = ">= 3.5.0 and < 4.0.0" } 35 - gleam_json = { version = ">= 0.6.0 and < 2.0.0" } 36 - gleam_stdlib = { version = ">= 0.29.0 and < 2.0.0" } 37 - gleeunit = { version = "~> 1.0" } 35 + gleam_json = { version = ">= 0.6.0 and < 3.0.0" } 36 + gleam_stdlib = { version = ">= 0.43.0 and < 2.0.0" } 37 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 38 38 logging = { version = ">= 1.2.0 and < 2.0.0" } 39 39 marceau = { version = ">= 1.1.0 and < 2.0.0" } 40 - mist = { version = ">= 1.2.0 and < 3.0.0" } 40 + mist = { version = ">= 1.2.0 and < 4.0.0" } 41 41 simplifile = { version = ">= 2.0.0 and < 3.0.0" }
+6 -7
src/wisp/testing.gleam
··· 1 1 import gleam/bit_array 2 - import gleam/bytes_builder 2 + import gleam/bytes_tree 3 3 import gleam/crypto 4 4 import gleam/http 5 5 import gleam/http/request 6 6 import gleam/json.{type Json} 7 7 import gleam/option.{None, Some} 8 8 import gleam/string 9 - import gleam/string_builder 9 + import gleam/string_tree 10 10 import gleam/uri 11 11 import simplifile 12 12 import wisp.{type Request, type Response, Bytes, Empty, File, Text} ··· 227 227 pub fn string_body(response: Response) -> String { 228 228 case response.body { 229 229 Empty -> "" 230 - Text(builder) -> string_builder.to_string(builder) 230 + Text(tree) -> string_tree.to_string(tree) 231 231 Bytes(bytes) -> { 232 - let data = bytes_builder.to_bit_array(bytes) 232 + let data = bytes_tree.to_bit_array(bytes) 233 233 let assert Ok(string) = bit_array.to_string(data) 234 234 string 235 235 } ··· 250 250 pub fn bit_array_body(response: Response) -> BitArray { 251 251 case response.body { 252 252 Empty -> <<>> 253 - Bytes(builder) -> bytes_builder.to_bit_array(builder) 254 - Text(builder) -> 255 - bytes_builder.to_bit_array(bytes_builder.from_string_builder(builder)) 253 + Bytes(tree) -> bytes_tree.to_bit_array(tree) 254 + Text(tree) -> bytes_tree.to_bit_array(bytes_tree.from_string_tree(tree)) 256 255 File(path) -> { 257 256 let assert Ok(contents) = simplifile.read_bits(path) 258 257 contents
+5 -5
src/wisp/wisp_mist.gleam
··· 1 1 import exception 2 - import gleam/bytes_builder 2 + import gleam/bytes_tree 3 3 import gleam/http/request.{type Request as HttpRequest} 4 4 import gleam/http/response.{type Response as HttpResponse} 5 5 import gleam/option ··· 63 63 chunk: Result(mist.Chunk, mist.ReadError), 64 64 ) -> Result(internal.Read, Nil) { 65 65 chunk 66 - |> result.nil_error 66 + |> result.replace_error(Nil) 67 67 |> result.map(fn(chunk) { 68 68 case chunk { 69 69 mist.Done -> internal.ReadingFinished ··· 75 75 76 76 fn mist_response(response: wisp.Response) -> HttpResponse(mist.ResponseData) { 77 77 let body = case response.body { 78 - wisp.Empty -> mist.Bytes(bytes_builder.new()) 79 - wisp.Text(text) -> mist.Bytes(bytes_builder.from_string_builder(text)) 78 + wisp.Empty -> mist.Bytes(bytes_tree.new()) 79 + wisp.Text(text) -> mist.Bytes(bytes_tree.from_string_tree(text)) 80 80 wisp.Bytes(bytes) -> mist.Bytes(bytes) 81 81 wisp.File(path) -> mist_send_file(path) 82 82 } ··· 90 90 Error(error) -> { 91 91 wisp.log_error(string.inspect(error)) 92 92 // TODO: return 500 93 - mist.Bytes(bytes_builder.new()) 93 + mist.Bytes(bytes_tree.new()) 94 94 } 95 95 } 96 96 }
+27 -29
src/wisp.gleam
··· 1 1 import exception 2 2 import gleam/bit_array 3 3 import gleam/bool 4 - import gleam/bytes_builder.{type BytesBuilder} 4 + import gleam/bytes_tree.{type BytesTree} 5 5 import gleam/crypto 6 6 import gleam/dict.{type Dict} 7 7 import gleam/dynamic.{type Dynamic} ··· 19 19 import gleam/option.{type Option} 20 20 import gleam/result 21 21 import gleam/string 22 - import gleam/string_builder.{type StringBuilder} 22 + import gleam/string_tree.{type StringTree} 23 23 import gleam/uri 24 24 import logging 25 25 import marceau ··· 35 35 pub type Body { 36 36 /// A body of unicode text. 37 37 /// 38 - /// The body is represented using a `StringBuilder`. If you have a `String` 39 - /// you can use the `string_builder.from_string` function to convert it. 38 + /// The body is represented using a `StringTree`. If you have a `String` 39 + /// you can use the `string_tree.from_string` function to convert it. 40 40 /// 41 - Text(StringBuilder) 41 + Text(StringTree) 42 42 /// A body of binary data. 43 43 /// 44 - /// The body is represented using a `BytesBuilder`. If you have a `BitArray` 45 - /// you can use the `bytes_builder.from_bit_array` function to convert it. 44 + /// The body is represented using a `BytesTree`. If you have a `BitArray` 45 + /// you can use the `bytes_tree.from_bit_array` function to convert it. 46 46 /// 47 - Bytes(BytesBuilder) 47 + Bytes(BytesTree) 48 48 /// A body of the contents of a file. 49 49 /// 50 50 /// This will be sent efficiently using the `send_file` function of the ··· 146 146 /// # Examples 147 147 /// 148 148 /// ```gleam 149 + /// let content = bytes_tree.from_string("Hello, Joe!") 149 150 /// response(200) 150 - /// |> file_download_from_memory(named: "myfile.txt", containing: "Hello, Joe!") 151 + /// |> file_download_from_memory(named: "myfile.txt", containing: content) 151 152 /// // -> Response( 152 153 /// // 200, 153 154 /// // [#("content-disposition", "attachment; filename=\"myfile.txt\"")], ··· 158 159 pub fn file_download_from_memory( 159 160 response: Response, 160 161 named name: String, 161 - containing data: BytesBuilder, 162 + containing data: BytesTree, 162 163 ) -> Response { 163 164 let name = uri.percent_encode(name) 164 165 response ··· 177 178 /// # Examples 178 179 /// 179 180 /// ```gleam 180 - /// let body = string_builder.from_string("<h1>Hello, Joe!</h1>") 181 + /// let body = string_tree.from_string("<h1>Hello, Joe!</h1>") 181 182 /// html_response(body, 200) 182 183 /// // -> Response(200, [#("content-type", "text/html")], Text(body)) 183 184 /// ``` 184 185 /// 185 - pub fn html_response(html: StringBuilder, status: Int) -> Response { 186 + pub fn html_response(html: StringTree, status: Int) -> Response { 186 187 HttpResponse( 187 188 status, 188 189 [#("content-type", "text/html; charset=utf-8")], ··· 198 199 /// # Examples 199 200 /// 200 201 /// ```gleam 201 - /// let body = string_builder.from_string("{\"name\": \"Joe\"}") 202 + /// let body = string_tree.from_string("{\"name\": \"Joe\"}") 202 203 /// json_response(body, 200) 203 204 /// // -> Response(200, [#("content-type", "application/json")], Text(body)) 204 205 /// ``` 205 206 /// 206 - pub fn json_response(json: StringBuilder, status: Int) -> Response { 207 + pub fn json_response(json: StringTree, status: Int) -> Response { 207 208 HttpResponse( 208 209 status, 209 210 [#("content-type", "application/json; charset=utf-8")], ··· 219 220 /// # Examples 220 221 /// 221 222 /// ```gleam 222 - /// let body = string_builder.from_string("<h1>Hello, Joe!</h1>") 223 + /// let body = string_tree.from_string("<h1>Hello, Joe!</h1>") 223 224 /// response(201) 224 225 /// |> html_body(body) 225 226 /// // -> Response(201, [#("content-type", "text/html; charset=utf-8")], Text(body)) 226 227 /// ``` 227 228 /// 228 - pub fn html_body(response: Response, html: StringBuilder) -> Response { 229 + pub fn html_body(response: Response, html: StringTree) -> Response { 229 230 response 230 231 |> response.set_body(Text(html)) 231 232 |> response.set_header("content-type", "text/html; charset=utf-8") ··· 239 240 /// # Examples 240 241 /// 241 242 /// ```gleam 242 - /// let body = string_builder.from_string("{\"name\": \"Joe\"}") 243 + /// let body = string_tree.from_string("{\"name\": \"Joe\"}") 243 244 /// response(201) 244 245 /// |> json_body(body) 245 246 /// // -> Response(201, [#("content-type", "application/json; charset=utf-8")], Text(body)) 246 247 /// ``` 247 248 /// 248 - pub fn json_body(response: Response, json: StringBuilder) -> Response { 249 + pub fn json_body(response: Response, json: StringTree) -> Response { 249 250 response 250 251 |> response.set_body(Text(json)) 251 252 |> response.set_header("content-type", "application/json; charset=utf-8") 252 253 } 253 254 254 - /// Set the body of a response to a given string builder. 255 + /// Set the body of a response to a given string tree. 255 256 /// 256 257 /// You likely want to also set the request `content-type` header to an 257 258 /// appropriate value for the format of the content. ··· 259 260 /// # Examples 260 261 /// 261 262 /// ```gleam 262 - /// let body = string_builder.from_string("Hello, Joe!") 263 + /// let body = string_tree.from_string("Hello, Joe!") 263 264 /// response(201) 264 - /// |> string_builder_body(body) 265 + /// |> string_tree_body(body) 265 266 /// // -> Response(201, [], Text(body)) 266 267 /// ``` 267 268 /// 268 - pub fn string_builder_body( 269 - response: Response, 270 - content: StringBuilder, 271 - ) -> Response { 269 + pub fn string_tree_body(response: Response, content: StringTree) -> Response { 272 270 response 273 271 |> response.set_body(Text(content)) 274 272 } 275 273 276 - /// Set the body of a response to a given string builder. 274 + /// Set the body of a response to a given string. 277 275 /// 278 276 /// You likely want to also set the request `content-type` header to an 279 277 /// appropriate value for the format of the content. ··· 287 285 /// // -> Response( 288 286 /// // 201, 289 287 /// // [], 290 - /// // Text(string_builder.from_string("Hello, Joe")) 288 + /// // Text(string_tree.from_string("Hello, Joe")) 291 289 /// // ) 292 290 /// ``` 293 291 /// 294 292 pub fn string_body(response: Response, content: String) -> Response { 295 293 response 296 - |> response.set_body(Text(string_builder.from_string(content))) 294 + |> response.set_body(Text(string_tree.from_string(content))) 297 295 } 298 296 299 297 /// Escape a string so that it can be safely included in a HTML document. ··· 1475 1473 http.Get, True -> { 1476 1474 let path = 1477 1475 path 1478 - |> string.drop_left(string.length(prefix)) 1476 + |> string.drop_start(string.length(prefix)) 1479 1477 |> string.replace(each: "..", with: "") 1480 1478 |> internal.join_path(directory, _) 1481 1479
+3 -3
test/wisp/testing_test.gleam
··· 2 2 import gleam/http/response 3 3 import gleam/json 4 4 import gleam/option.{None, Some} 5 - import gleam/string_builder 5 + import gleam/string_tree 6 6 import gleeunit/should 7 7 import wisp 8 8 import wisp/testing ··· 502 502 503 503 pub fn string_body_text_test() { 504 504 wisp.ok() 505 - |> response.set_body(wisp.Text(string_builder.from_string("Hello, Joe!"))) 505 + |> response.set_body(wisp.Text(string_tree.from_string("Hello, Joe!"))) 506 506 |> testing.string_body 507 507 |> should.equal("Hello, Joe!") 508 508 } ··· 523 523 524 524 pub fn bit_array_body_text_test() { 525 525 wisp.ok() 526 - |> response.set_body(wisp.Text(string_builder.from_string("Hello, Joe!"))) 526 + |> response.set_body(wisp.Text(string_tree.from_string("Hello, Joe!"))) 527 527 |> testing.bit_array_body 528 528 |> should.equal(<<"Hello, Joe!":utf8>>) 529 529 }
+12 -12
test/wisp_test.gleam
··· 11 11 import gleam/list 12 12 import gleam/set 13 13 import gleam/string 14 - import gleam/string_builder 14 + import gleam/string_tree 15 15 import gleeunit 16 16 import gleeunit/should 17 17 import simplifile ··· 119 119 } 120 120 121 121 pub fn json_response_test() { 122 - let body = string_builder.from_string("{\"one\":1,\"two\":2}") 122 + let body = string_tree.from_string("{\"one\":1,\"two\":2}") 123 123 let response = wisp.json_response(body, 201) 124 124 response.status 125 125 |> should.equal(201) ··· 131 131 } 132 132 133 133 pub fn html_response_test() { 134 - let body = string_builder.from_string("Hello, world!") 134 + let body = string_tree.from_string("Hello, world!") 135 135 let response = wisp.html_response(body, 200) 136 136 response.status 137 137 |> should.equal(200) ··· 143 143 } 144 144 145 145 pub fn html_body_test() { 146 - let body = string_builder.from_string("Hello, world!") 146 + let body = string_tree.from_string("Hello, world!") 147 147 let response = 148 148 wisp.method_not_allowed([http.Get]) 149 149 |> wisp.html_body(body) ··· 751 751 list.key_find(request.headers, "x-original-method") 752 752 |> should.equal(header) 753 753 754 - string_builder.from_string("Hello!") 754 + string_tree.from_string("Hello!") 755 755 |> wisp.html_response(201) 756 756 } 757 757 ··· 761 761 |> should.equal(Response( 762 762 201, 763 763 [#("content-type", "text/html; charset=utf-8")], 764 - wisp.Text(string_builder.from_string("Hello!")), 764 + wisp.Text(string_tree.from_string("Hello!")), 765 765 )) 766 766 767 767 testing.get("/", []) ··· 897 897 |> should.equal(Response( 898 898 200, 899 899 [], 900 - wisp.Text(string_builder.from_string("Hello, world!")), 900 + wisp.Text(string_tree.from_string("Hello, world!")), 901 901 )) 902 902 } 903 903 904 - pub fn string_builder_body_test() { 904 + pub fn string_tree_body_test() { 905 905 wisp.ok() 906 - |> wisp.string_builder_body(string_builder.from_string("Hello, world!")) 906 + |> wisp.string_tree_body(string_tree.from_string("Hello, world!")) 907 907 |> should.equal(Response( 908 908 200, 909 909 [], 910 - wisp.Text(string_builder.from_string("Hello, world!")), 910 + wisp.Text(string_tree.from_string("Hello, world!")), 911 911 )) 912 912 } 913 913 914 914 pub fn json_body_test() { 915 915 wisp.ok() 916 - |> wisp.json_body(string_builder.from_string("{\"one\":1,\"two\":2}")) 916 + |> wisp.json_body(string_tree.from_string("{\"one\":1,\"two\":2}")) 917 917 |> should.equal(Response( 918 918 200, 919 919 [#("content-type", "application/json; charset=utf-8")], 920 - wisp.Text(string_builder.from_string("{\"one\":1,\"two\":2}")), 920 + wisp.Text(string_tree.from_string("{\"one\":1,\"two\":2}")), 921 921 )) 922 922 } 923 923