bbserver 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02244715c71e9fb55237c70a09d89a145b9949365a5e5d379c4f1d40d2eeeb81
4
- data.tar.gz: 6975e80113ea1c6adfd57ea0145e5b6f8cfc9642a73713d3bc37640e24974389
3
+ metadata.gz: 87cee3b4f4f872001b320d5aeb494e3496e903240c17f4b7601f2a34f14ead8b
4
+ data.tar.gz: 72304d3ba819dcfcac6d8202c8dadc7ae9c03dc6840d1d965ca13b118282d066
5
5
  SHA512:
6
- metadata.gz: 9b21c97f518bb8759d2717db6f8e2bcd6cc703ddc11278eea249fc6bad8b429cf44ef9665d8dc66402772364d1f3fd4e673664649c2d7b68cb041d4653f147a2
7
- data.tar.gz: 8cb7330834c359e8934e3c63e8b28dc431ed1afc130cfca1ea7cc2c860c3a4f92c2deaaba6596dba3e0dab82981ecc2c4c4a007c74011eeb168af200433cf511
6
+ metadata.gz: 07b9b3986487dd38b9ae1dbd45dff2d5562a5f0e2c471d40841fc9a01e2853a7af55e0892402126961fefdcb2f819b4d651249fe8cbc90a89af7b4232aab1701
7
+ data.tar.gz: 48e22d0cbf53f2a30137cfac5757c6c957eff173ac8e93e8e1cff16301defc0b2adc7bcd91ab68f0adb83434063437e1dfe4edde1ac66182efe6f2dccfe62866
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class ContextLocals
5
+ def initialize
6
+ @storage = {}
7
+ end
8
+
9
+ def method_missing(name, *args, &block)
10
+ if name.to_s.end_with?('=')
11
+ key = name.to_s.chomp('=').to_sym
12
+ @storage[key] = args.first
13
+ elsif @storage.key?(name)
14
+ @storage[name]
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def respond_to_missing?(name, include_private = false)
21
+ @storage.key?(name.to_s.chomp('=').to_sym) || super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class ExecutionStack
5
+ def initialize(handler_class, middlewares)
6
+ @handler_class = handler_class
7
+ @middlewares = middlewares
8
+ end
9
+
10
+ def execute(context)
11
+ chain = @middlewares.dup
12
+
13
+ process = proc do |ctx|
14
+ if chain.empty?
15
+ handler = @handler_class.new(ctx)
16
+ handler.handle
17
+ else
18
+ middleware = chain.shift
19
+ middleware.call(ctx, process)
20
+ end
21
+ end
22
+
23
+ process.call(context)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class Handler
5
+ attr_reader :context
6
+
7
+ def initialize(context)
8
+ @context = context
9
+ end
10
+
11
+ def handle
12
+ raise NotImplementedError
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer::Handlers
4
+ class NotFoundHandler
5
+ def initialize(context)
6
+ @context = context
7
+ end
8
+
9
+ def handle
10
+ @context.response.write_status 404
11
+ @context.response.write_body "Not Found"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class HttpContext
5
+ attr_reader :request, :response, :server_context, :params
6
+
7
+ def initialize(server_context, request, response, params)
8
+ @server_context = server_context
9
+ @request = request
10
+ @response = response
11
+ @params = params
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class Params
5
+ def initialize(data = {})
6
+ @data = data
7
+ end
8
+
9
+ def [](key)
10
+ @data[key.to_sym]
11
+ end
12
+
13
+ def get(key)
14
+ self[key]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class Request
5
+ attr_reader :env
6
+
7
+ def initialize(env)
8
+ @env = env
9
+ end
10
+
11
+ def body
12
+ @body ||= env["rack.input"].read
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class Response
5
+ def initialize
6
+ @status = 200
7
+ @headers = {}
8
+ @body = []
9
+ end
10
+
11
+ def write_status(status)
12
+ @status = status
13
+ end
14
+
15
+ def write_header(key, value)
16
+ @headers[key] = value
17
+ end
18
+
19
+ def has_header?(key)
20
+ @headers.key?(key)
21
+ end
22
+
23
+ def write_body(body)
24
+ @body << body
25
+ end
26
+
27
+ def to_rack
28
+ [@status, @headers, @body]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class Route
5
+ attr_reader :middlewares, :handler_proc
6
+
7
+ def self.any(middlewares = [])
8
+ new(method: nil, path: nil, middlewares: middlewares, any: true)
9
+ end
10
+
11
+ def initialize(method: nil, path: nil, middlewares: [], any: false)
12
+ raise ArgumentError, "Method and Path are required for non-ANY routes" unless any || (method && path)
13
+
14
+ @method = method
15
+ @path = path
16
+ @middlewares = middlewares
17
+ @any = any
18
+ end
19
+
20
+ def method
21
+ raise RuntimeError, "Method is not defined for this route" if @method.nil?
22
+ @method
23
+ end
24
+
25
+ def path
26
+ raise RuntimeError, "Path is not defined for this route" if @path.nil?
27
+ @path
28
+ end
29
+
30
+ def any?
31
+ @any
32
+ end
33
+
34
+ def use(&block)
35
+ @middlewares << block
36
+ self
37
+ end
38
+
39
+ def call(&block)
40
+ @handler_proc = block
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class RouteConfigurationError < StandardError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class RouteNotFoundError < StandardError; end
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class RouteRegistry
5
+ def initialize
6
+ @routes = []
7
+ @middlewares = []
8
+ end
9
+
10
+ def use(&block)
11
+ @middlewares << block
12
+ end
13
+
14
+ def any
15
+ route = Route.new(method: nil, path: nil, middlewares: @middlewares.dup, any: true)
16
+ @routes << route
17
+ route
18
+ end
19
+
20
+ def has_any?
21
+ @routes.any?(&:any?)
22
+ end
23
+
24
+ def get_any
25
+ @routes.find(&:any?)
26
+ end
27
+
28
+ [:get, :post, :put, :delete, :patch, :head, :options].each do |http_verb|
29
+ define_method(http_verb) do |path|
30
+ raise RuntimeError, "Cannot register routes after ANY handler" if has_any?
31
+ route = Route.new(method: http_verb.to_s.upcase, path: path, middlewares: @middlewares.dup)
32
+ @routes << route
33
+ route
34
+ end
35
+ end
36
+
37
+ def find(path)
38
+ @routes.find { |r| r.path == path }
39
+ end
40
+
41
+ def length
42
+ @routes.length
43
+ end
44
+
45
+ def list
46
+ @routes
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module BBServer
5
+ class Router
6
+ def initialize(routes)
7
+ @routes = routes
8
+ end
9
+
10
+ def match(request)
11
+ path = request.instance_variable_get(:@env)["PATH_INFO"] || "/"
12
+ method = request.instance_variable_get(:@env)["REQUEST_METHOD"] || "GET"
13
+
14
+ route, params = find_route(path, method)
15
+
16
+ raise RouteNotFoundError, "No route found for #{method} #{path}" unless route
17
+
18
+ handler_class = route.handler_proc.call
19
+ stack = ExecutionStack.new(handler_class, route.middlewares)
20
+
21
+ [stack, Params.new(params || {})]
22
+ end
23
+
24
+ private
25
+
26
+ def find_route(path, method)
27
+ @routes.list.each do |route|
28
+ return [route, {}] if route.any?
29
+
30
+ next unless route.method == method
31
+
32
+ parts = route.path.split('/')
33
+ path_parts = path.split('/')
34
+
35
+ next unless parts.size == path_parts.size
36
+
37
+ params = {}
38
+ match = parts.each_with_index.all? do |part, i|
39
+ if part.start_with?(':')
40
+ params[part[1..-1].to_sym] = path_parts[i]
41
+ true
42
+ else
43
+ part == path_parts[i]
44
+ end
45
+ end
46
+
47
+ return [route, params] if match
48
+ end
49
+ nil
50
+ end
51
+ end
52
+ end
@@ -1,14 +1,22 @@
1
1
 
2
- module BBServer
2
+ # frozen_string_literal: true
3
3
 
4
+ module BBServer
4
5
  class Server
6
+ def initialize(context)
7
+ @context = context
8
+ end
9
+
5
10
  def call(env)
6
- [
7
- 200,
8
- { "content-type" => "text/plain" },
9
- ["Hello BBServer"]
10
- ]
11
+ request = Request.new(env)
12
+ response = Response.new
13
+
14
+ router = Router.new(@context.routes)
15
+ stack, params = router.match(request)
16
+ http_context = HttpContext.new(@context, request, response, params)
17
+ stack.execute(http_context)
18
+
19
+ response.to_rack
11
20
  end
12
21
  end
13
-
14
22
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class ServerContext
5
+ attr_reader :routes
6
+
7
+ def initialize
8
+ @routes = BBServer::RouteRegistry.new
9
+ end
10
+
11
+ def set_routes(routes)
12
+ raise RouteConfigurationError, "Any handler is required" unless routes.has_any?
13
+ @routes = routes
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BBServer
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bbserver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juliano Soares da Silva
@@ -76,7 +76,21 @@ files:
76
76
  - LICENSE
77
77
  - README.md
78
78
  - lib/bbserver.rb
79
+ - lib/bbserver/context_locals.rb
80
+ - lib/bbserver/execution_stack.rb
81
+ - lib/bbserver/handler.rb
82
+ - lib/bbserver/handlers/not_found_handler.rb
83
+ - lib/bbserver/http_context.rb
84
+ - lib/bbserver/params.rb
85
+ - lib/bbserver/request.rb
86
+ - lib/bbserver/response.rb
87
+ - lib/bbserver/route.rb
88
+ - lib/bbserver/route_configuration_error.rb
89
+ - lib/bbserver/route_not_found_error.rb
90
+ - lib/bbserver/route_registry.rb
91
+ - lib/bbserver/router.rb
79
92
  - lib/bbserver/server.rb
93
+ - lib/bbserver/server_context.rb
80
94
  - lib/bbserver/version.rb
81
95
  homepage: https://github.com/bytebytebug/bbserver
82
96
  licenses: