bbserver 0.1.0 → 0.3.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: 99665aa54be848e3c4fc6429586022214d4a2f2e12c743dd23ca4cac97582cce
4
+ data.tar.gz: 258ab27cb2a11c4379aa648dfff5e8dbec3e9a4e97062499fa293cd4ee66d1b3
5
5
  SHA512:
6
- metadata.gz: 9b21c97f518bb8759d2717db6f8e2bcd6cc703ddc11278eea249fc6bad8b429cf44ef9665d8dc66402772364d1f3fd4e673664649c2d7b68cb041d4653f147a2
7
- data.tar.gz: 8cb7330834c359e8934e3c63e8b28dc431ed1afc130cfca1ea7cc2c860c3a4f92c2deaaba6596dba3e0dab82981ecc2c4c4a007c74011eeb168af200433cf511
6
+ metadata.gz: 3e8ccc09aa06b5b86a773636f898f85280bee3e718d6f7241b1ccbdf70c5d635cc43b9c1db81217ac8c347bd067a7a3a7f2b19c30869d88573de4d20a074cd04
7
+ data.tar.gz: 5123fa56f4d9b285748c7d36ccc92c60565e783d4c3df44814f663de365efad5d5020cc631f13ceab8be2e56f2b32d5f03559d7df91e4efd888bc47aa3b0ee35
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBServer
4
+ class ContextLocals
5
+ def initialize
6
+ @storage: Hash[Symbol, untyped] = {}
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,87 @@
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
+ def get(path)
29
+ raise_if_any_registered
30
+ register_route(:GET, path)
31
+ end
32
+
33
+ def post(path)
34
+ raise_if_any_registered
35
+ register_route(:POST, path)
36
+ end
37
+
38
+ def put(path)
39
+ raise_if_any_registered
40
+ register_route(:PUT, path)
41
+ end
42
+
43
+ def delete(path)
44
+ raise_if_any_registered
45
+ register_route(:DELETE, path)
46
+ end
47
+
48
+ def patch(path)
49
+ raise_if_any_registered
50
+ register_route(:PATCH, path)
51
+ end
52
+
53
+ def head(path)
54
+ raise_if_any_registered
55
+ register_route(:HEAD, path)
56
+ end
57
+
58
+ def options(path)
59
+ raise_if_any_registered
60
+ register_route(:OPTIONS, path)
61
+ end
62
+
63
+ def list
64
+ @routes
65
+ end
66
+
67
+ def length
68
+ @routes.length
69
+ end
70
+
71
+ private
72
+
73
+ def raise_if_any_registered
74
+ raise RuntimeError, "Cannot register routes after ANY handler" if has_any?
75
+ end
76
+
77
+ def register_route(method, path)
78
+ route = Route.new(method: method.to_s.upcase, path: path, middlewares: @middlewares.dup)
79
+ @routes << route
80
+ route
81
+ end
82
+
83
+ def find(path)
84
+ @routes.find { |r| r.path == path }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,56 @@
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
+ route_path = route.path
33
+ next unless route_path.is_a?(String)
34
+ parts = route_path.split('/')
35
+ path_parts = path.split('/')
36
+
37
+ next unless parts.size == path_parts.size
38
+
39
+ # @type var params: Hash[Symbol, String]
40
+ params = {}
41
+ match = parts.each_with_index.all? do |part, i|
42
+ if part.start_with?(':')
43
+ part_name = part[1..-1]
44
+ params[part_name.to_sym] = path_parts[i] if part_name
45
+ true
46
+ else
47
+ part == path_parts[i]
48
+ end
49
+ end
50
+
51
+ return [route, params] if match
52
+ end
53
+ nil
54
+ end
55
+ end
56
+ 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.3.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.3.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: