Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Encapsulate applications #511

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/doc/
/bin/
/lib/
/.crystal/
/.shards/
*.log

Expand Down
9 changes: 9 additions & 0 deletions samples/app.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require "kemal/base"

class MyApp < Kemal::Application
get "/" do
"Hello Kemal!"
end
end

MyApp.run
17 changes: 17 additions & 0 deletions samples/app_squared.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "../src/kemal/base"

class MyApp < Kemal::Application
get "/" do |env|
"Hello Kemal!"
end
end

class OtherApp < Kemal::Application
get "/" do |env|
"Hello World!"
end
end

spawn { MyApp.run(3002) }

OtherApp.run(3001)
2 changes: 1 addition & 1 deletion spec/all_spec.cr
Original file line number Diff line number Diff line change
@@ -1 +1 @@
require "./*"
# require "./*"
50 changes: 50 additions & 0 deletions spec/application_mode_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require "./spec_helper"

private class MyApp < Kemal::Application
get "/route1" do |env|
"Route 1"
end

get "/route2" do |env|
"Route 2"
end

get "/file" do |env|
send_file env, "Serdar".to_slice
end
end

describe MyApp do
it "matches the correct route" do
request = HTTP::Request.new("GET", "/route2")
client_response = call_request_on_app(MyApp.new, 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
MyApp.new.get "route" do |env|
"Route 1"
end
end
end

it "sends file with binary stream" do
request = HTTP::Request.new("GET", "/file")
response = call_request_on_app(MyApp.new, 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 "responds to delayed route" do
app = MyApp.new
app.setup
app.get "/delayed" do |env|
"Happy addition!"
end
request = HTTP::Request.new("GET", "/delayed")
client_response = call_request_on_app(app, request)
client_response.body.should eq("Happy addition!")
end
end
45 changes: 31 additions & 14 deletions spec/config_spec.cr
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
require "./spec_helper"

private class CustomTestHandler < Kemal::Handler
def call(env)
env.response << "Kemal"
call_next env
end
end

describe "Config" do
it "sets default port to 3000" do
Kemal::Config.new.port.should eq 3000
config = Kemal::Config.new
config.port.should eq 3000
end

it "sets default environment to development" do
Kemal::Config.new.env.should eq "development"
config = Kemal::Config.new
config.env.should eq "development"
end

it "sets environment to production" do
config = Kemal.config
config = Kemal::Config.new
config.env = "production"
config.env.should eq "production"
end
Expand All @@ -20,28 +29,35 @@ describe "Config" do
end

it "sets host binding" do
config = Kemal.config
config = Kemal::Config.new
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)
it "adds a custom handler to Base" do
application = Kemal::Base.new
application.add_handler CustomTestHandler.new
application.setup
application.handlers.size.should eq 6
end

it "adds a custom handler to Application" do
application = Kemal::Application.new
application.add_handler CustomTestHandler.new
application.setup
application.handlers.size.should eq 8
end

it "toggles the shutdown message" do
config = Kemal.config
config = Kemal::Config.new
config.shutdown_message = false
config.shutdown_message.should eq false
config.shutdown_message?.should be_false
config.shutdown_message = true
config.shutdown_message.should eq true
config.shutdown_message?.should be_true
end

it "adds custom options" do
config = Kemal.config
config = Kemal::Config.new
ARGV.push("--test")
ARGV.push("FOOBAR")
test_option = nil
Expand All @@ -51,7 +67,8 @@ describe "Config" do
test_option = opt
end
end
Kemal::CLI.new ARGV

Kemal::CLI.new(ARGV, config)
test_option.should eq("FOOBAR")
end

Expand Down
150 changes: 59 additions & 91 deletions spec/context_spec.cr
Original file line number Diff line number Diff line change
@@ -1,107 +1,75 @@
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")
it "sets content type" do
app = Kemal::Base.new
app.get "/" do |env|
env.response.content_type = "application/json"
"Hello"
end
request = HTTP::Request.new("GET", "/")
client_response = call_request_on_app(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"
it "parses headers" do
app = Kemal::Base.new
app.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(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"
it "sets response headers" do
app = Kemal::Base.new
app.get "/" do |env|
env.response.headers.add "Accept-Language", "tr"
end
request = HTTP::Request.new("GET", "/")
client_response = call_request_on_app(app, request)
client_response.headers["Accept-Language"].should eq "tr"
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
it "can store variables" do
app = Kemal::Base.new
app.before_get "/" do |env|
t = TestContextStorageType.new
t.id = 32
a = AnotherContextStorageType.new
env.set "key", "value"
env.set "before_get", "Kemal"
env.set "before_get_int", 123
env.set "before_get_context_test", t
env.set "another_context_test", a
env.set "before_get_float", 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"
app.get "/" do |env|
env.set "key", "value"
{
key: env.get("key"),
before_get: env.get("before_get"),
before_get_int: env.get("before_get_int"),
before_get_float: env.get("before_get_float"),
before_get_context_test: env.get("before_get_context_test"),
}
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
request = HTTP::Request.new("GET", "/")
io = IO::Memory.new
response = HTTP::Server::Response.new(io)
context = HTTP::Server::Context.new(request, response)
app.filter_handler.call(context)
app.route_handler.call(context)
context.store["key"].should eq "value"
context.store["before_get"].should eq "Kemal"
context.store["before_get_int"].should eq 123
context.store["before_get_float"].should eq 3.5
context.store["before_get_context_test"].as(TestContextStorageType).id.should eq 32
end
end
40 changes: 40 additions & 0 deletions spec/dsl_helper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "./spec_helper"
require "../src/kemal/dsl"

include Kemal

class CustomLogHandler < Kemal::BaseLogHandler
def call(env)
call_next env
end

def write(message)
end
end

def create_request_and_return_io(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
end

def call_request_on_app(request)
call_request_on_app(Kemal.application, request)
end

def build_main_handler
build_main_handler(Kemal.application)
end

Spec.before_each do
config = Kemal.config
config.env = "development"
end

Spec.after_each do
Kemal.application.clear
end
Loading