lennarb 1.3.0 → 1.4.1
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 +4 -4
- data/.devcontainer/Dockerfile +8 -0
- data/.devcontainer/devcontainer.json +20 -0
- data/.editorconfig +9 -0
- data/.github/workflows/coverage.yaml +58 -0
- data/.github/workflows/documentation.yaml +47 -0
- data/.github/workflows/main.yaml +27 -0
- data/.github/workflows/test.yaml +50 -0
- data/.gitignore +12 -0
- data/.standard.yml +23 -0
- data/.tool-versions +1 -0
- data/LICENCE +24 -0
- data/Rakefile +12 -0
- data/benchmark/memory.png +0 -0
- data/benchmark/rps.png +0 -0
- data/benchmark/runtime_with_startup.png +0 -0
- data/bin/console +8 -0
- data/bin/release +15 -0
- data/bin/setup +8 -0
- data/changelog.md +78 -7
- data/exe/lenna +2 -3
- data/gems.rb +29 -0
- data/guides/getting-started/readme.md +215 -0
- data/guides/links.yaml +6 -0
- data/guides/performance/readme.md +120 -0
- data/guides/response/readme.md +83 -0
- data/lennarb.gemspec +47 -0
- data/lib/lennarb/app.rb +172 -0
- data/lib/lennarb/config.rb +34 -0
- data/lib/lennarb/constansts.rb +6 -0
- data/lib/lennarb/environment.rb +76 -0
- data/lib/lennarb/request.rb +202 -31
- data/lib/lennarb/request_handler.rb +31 -0
- data/lib/lennarb/response.rb +16 -19
- data/lib/lennarb/route_node.rb +50 -13
- data/lib/lennarb/routes.rb +71 -0
- data/lib/lennarb/version.rb +2 -9
- data/lib/lennarb.rb +17 -155
- data/logo/lennarb.png +0 -0
- data/readme.md +32 -13
- metadata +147 -60
- data/lib/lennarb/plugin.rb +0 -49
- data/lib/lennarb/plugins/hooks.rb +0 -117
- data/lib/lennarb/plugins/mount.rb +0 -66
@@ -1,117 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Lennarb
|
4
|
-
module Plugins
|
5
|
-
module Hooks
|
6
|
-
def self.configure(app)
|
7
|
-
app.instance_variable_set(:@_before_hooks, {})
|
8
|
-
app.instance_variable_set(:@_after_hooks, {})
|
9
|
-
app.extend(ClassMethods)
|
10
|
-
app.include(InstanceMethods)
|
11
|
-
end
|
12
|
-
|
13
|
-
module ClassMethods
|
14
|
-
def before(paths = '*', &block)
|
15
|
-
paths = Array(paths)
|
16
|
-
@_before_hooks ||= {}
|
17
|
-
|
18
|
-
paths.each do |path|
|
19
|
-
@_before_hooks[path] ||= []
|
20
|
-
@_before_hooks[path] << block
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def after(paths = '*', &block)
|
25
|
-
paths = Array(paths)
|
26
|
-
@_after_hooks ||= {}
|
27
|
-
|
28
|
-
paths.each do |path|
|
29
|
-
@_after_hooks[path] ||= []
|
30
|
-
@_after_hooks[path] << block
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def inherited(subclass)
|
35
|
-
super
|
36
|
-
subclass.instance_variable_set(:@_before_hooks, @_before_hooks&.dup || {})
|
37
|
-
subclass.instance_variable_set(:@_after_hooks, @_after_hooks&.dup || {})
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
module InstanceMethods
|
42
|
-
def call(env)
|
43
|
-
catch(:halt) do
|
44
|
-
req = Lennarb::Request.new(env)
|
45
|
-
res = Lennarb::Response.new
|
46
|
-
|
47
|
-
path = env[Rack::PATH_INFO]
|
48
|
-
|
49
|
-
execute_hooks(self.class.instance_variable_get(:@_before_hooks), '*', req, res)
|
50
|
-
|
51
|
-
execute_matching_hooks(self.class.instance_variable_get(:@_before_hooks), path, req, res)
|
52
|
-
|
53
|
-
status, headers, body = super
|
54
|
-
res.status = status
|
55
|
-
headers.each { |k, v| res[k] = v }
|
56
|
-
body.each { |chunk| res.write(chunk) }
|
57
|
-
|
58
|
-
execute_matching_hooks(self.class.instance_variable_get(:@_after_hooks), path, req, res)
|
59
|
-
|
60
|
-
execute_hooks(self.class.instance_variable_get(:@_after_hooks), '*', req, res)
|
61
|
-
|
62
|
-
res.finish
|
63
|
-
end
|
64
|
-
rescue StandardError => e
|
65
|
-
handle_error(e)
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def execute_hooks(hooks, path, req, res)
|
71
|
-
return unless hooks&.key?(path)
|
72
|
-
|
73
|
-
hooks[path].each do |hook|
|
74
|
-
instance_exec(req, res, &hook)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def execute_matching_hooks(hooks, current_path, req, res)
|
79
|
-
return unless hooks
|
80
|
-
|
81
|
-
hooks.each do |pattern, pattern_hooks|
|
82
|
-
next if pattern == '*'
|
83
|
-
|
84
|
-
next unless matches_pattern?(pattern, current_path)
|
85
|
-
|
86
|
-
pattern_hooks.each do |hook|
|
87
|
-
instance_exec(req, res, &hook)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def matches_pattern?(pattern, path)
|
93
|
-
return true if pattern == path
|
94
|
-
|
95
|
-
pattern_parts = pattern.split('/')
|
96
|
-
path_parts = path.split('/')
|
97
|
-
|
98
|
-
return false if pattern_parts.length != path_parts.length
|
99
|
-
|
100
|
-
pattern_parts.zip(path_parts).all? do |pattern_part, path_part|
|
101
|
-
pattern_part.start_with?(':') || pattern_part == path_part
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def handle_error(error)
|
106
|
-
case error
|
107
|
-
when ArgumentError
|
108
|
-
[400, { 'Content-Type' => 'text/plain' }, ["Bad Request: #{error.message}"]]
|
109
|
-
else
|
110
|
-
[500, { 'Content-Type' => 'text/plain' }, ['Internal Server Error']]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
Lennarb::Plugin.register(:hooks, Hooks)
|
116
|
-
end
|
117
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Lennarb
|
4
|
-
module Plugins
|
5
|
-
module Mount
|
6
|
-
def self.configure(app)
|
7
|
-
app.instance_variable_set(:@_mounted_apps, {})
|
8
|
-
app.extend(ClassMethods)
|
9
|
-
app.include(InstanceMethods)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
def mount(app_class, at:)
|
14
|
-
raise ArgumentError, 'Expected a Lennarb class' unless app_class.is_a?(Class) && app_class <= Lennarb
|
15
|
-
raise ArgumentError, 'Mount path must start with /' unless at.start_with?('/')
|
16
|
-
|
17
|
-
@_mounted_apps ||= {}
|
18
|
-
normalized_path = normalize_mount_path(at)
|
19
|
-
|
20
|
-
mounted_app = app_class.new
|
21
|
-
mounted_app.freeze!
|
22
|
-
|
23
|
-
@_mounted_apps[normalized_path] = mounted_app
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def normalize_mount_path(path)
|
29
|
-
path.chomp('/')
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
module InstanceMethods
|
34
|
-
def call(env)
|
35
|
-
path_info = env[Rack::PATH_INFO]
|
36
|
-
|
37
|
-
self.class.instance_variable_get(:@_mounted_apps)&.each do |mount_path, app|
|
38
|
-
next unless path_info.start_with?("#{mount_path}/") || path_info == mount_path
|
39
|
-
|
40
|
-
env[Rack::PATH_INFO] = path_info[mount_path.length..]
|
41
|
-
env[Rack::PATH_INFO] = '/' if env[Rack::PATH_INFO].empty?
|
42
|
-
env[Rack::SCRIPT_NAME] = "#{env[Rack::SCRIPT_NAME]}#{mount_path}"
|
43
|
-
|
44
|
-
return app.call(env)
|
45
|
-
end
|
46
|
-
|
47
|
-
super
|
48
|
-
rescue StandardError => e
|
49
|
-
handle_error(e)
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def handle_error(error)
|
55
|
-
case error
|
56
|
-
when ArgumentError
|
57
|
-
[400, { 'content-type' => 'text/plain' }, ["Bad Request: #{error.message}"]]
|
58
|
-
else
|
59
|
-
[500, { 'content-type' => 'text/plain' }, ['Internal Server Error']]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
Lennarb::Plugin.register(:mount, Mount)
|
65
|
-
end
|
66
|
-
end
|