lennarb 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3b1fe759bb5de90ef757201a4023c441a9c40146f31a318aecc5c5c08cd04bb
4
- data.tar.gz: 8137ee838ca5636d7013cbfa5aa5f7e83d66e3b28d3387255b702d82becd884a
3
+ metadata.gz: 3a9951f0d799cadd7b4c3025eeaa9b050dd3920524b0a256530b4c53bb924a74
4
+ data.tar.gz: 39e78cd19783b98bb9f88d064821207dbc506c94f6742b840bfc3e31ed6568e9
5
5
  SHA512:
6
- metadata.gz: 3216bc56c89485d3292b621dcdfb26082957bea1ecfc622107b2f631f86508d7eb5318ce5bc43889ccf0db59775dcefd34e7e4ac00f5b023f34c4f371bab83b9
7
- data.tar.gz: 944b432173eeeee78c2fc8d546ee74f6768ab3581a61babfdad27f8e54969afd96581259d8866126a3e6e26c2418f92355af8cc07d3aa215b0f9fb9a6bceccc0
6
+ metadata.gz: 9ec6a17703b76f22f9f24f1d356c8bf190ed351d1629deeffaf5b871d3ae5907130d90d9329cc9b51f859659c75732fdb034dc029d81c7faed324979e536b090
7
+ data.tar.gz: 5efe46152e8796c8f07d19d0117da18c7868f07bf472be060de06ae140ff6cccbf69daf9d0b0883584875e600ecf0855ec52cffbef253c277325cbdbd2363d72
data/changelog.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2024-08-01
9
+
10
+ ### Removed
11
+
12
+ - Remove `zeitwerk` gem to load the files in the project.
13
+ - Remove `console` gem to print the logs in the console.
14
+ - Remove `Lenna` module. Now, the `Lennarb` class is the main class of the project.
15
+ - Remove `Middleware` module.
16
+ - Remove `CLI` module.
17
+ - Remove `Cache` module
18
+
19
+ ### Changed
20
+
21
+ - Change `Lennarb::Application` class to `Lennarb` class.
22
+ - Request class and Response class now are in `Lennarb` class
23
+ - Change `Lennarb::Router` class to `Lennarb` class
24
+
25
+ ### Fixed
26
+
27
+ - Improve performance of the RPS (Requests per second), memory and CPU usage. Now the performance is similar to the [Roda](https://github.com/jeremyevans/roda/tree/master).
28
+
8
29
  ## [0.1.7] - 2023-23-12
9
30
 
10
31
  ### Added
data/exe/lenna CHANGED
@@ -10,8 +10,6 @@
10
10
  #
11
11
  require 'lennarb'
12
12
 
13
- # Call the CLI to start the server
13
+ # Show the version
14
14
  #
15
- # @private `Since v0.1`
16
- #
17
- Lenna::Cli::App.run!(ARGV)
15
+ puts Lennarb::VERSION
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023-2024, by Aristóteles Coutinho.
5
+
6
+ class Lennarb
7
+ class Request < Rack::Request
8
+ # Initialize the request object
9
+ #
10
+ # @parameter [Hash] env
11
+ # @parameter [Hash] route_params
12
+ #
13
+ # @return [Request]
14
+ #
15
+ def initialize(env, route_params = {})
16
+ super(env)
17
+ @route_params = route_params
18
+ end
19
+
20
+ # Get the request body
21
+ #
22
+ # @return [String]
23
+ def params
24
+ @params ||= super.merge(@route_params)
25
+ end
26
+
27
+ private
28
+
29
+ # Get the query string
30
+ #
31
+ # @return [String]
32
+ #
33
+ def query_params
34
+ @query_params ||= Rack::Utils.parse_nested_query(query_string)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023-2024, by Aristóteles Coutinho.
5
+
6
+ class Lennarb
7
+ class Response
8
+ # @!attribute [rw] status
9
+ # @return [Integer]
10
+ #
11
+ attr_accessor :status
12
+
13
+ # @!attribute [r] body
14
+ # @return [Array]
15
+ #
16
+ attr_reader :body
17
+
18
+ # @!attribute [r] headers
19
+ # @return [Hash]
20
+ #
21
+ attr_reader :headers
22
+
23
+ # @!attribute [r] length
24
+ # @return [Integer]
25
+ #
26
+ attr_reader :length
27
+
28
+ # Constants
29
+ #
30
+ LOCATION = 'location'
31
+ private_constant :LOCATION
32
+
33
+ CONTENT_TYPE = 'content-type'
34
+ private_constant :CONTENT_TYPE
35
+
36
+ CONTENT_LENGTH = 'content-length'
37
+ private_constant :CONTENT_LENGTH
38
+
39
+ ContentType = { HTML: 'text/html', TEXT: 'text/plain', JSON: 'application/json' }.freeze
40
+ private_constant :ContentType
41
+
42
+ # Initialize the response object
43
+ #
44
+ # @return [Response]
45
+ #
46
+ def initialize
47
+ @status = 404
48
+ @headers = {}
49
+ @body = []
50
+ @length = 0
51
+ end
52
+
53
+ # Set the response header
54
+ #
55
+ # @parameter [String] key
56
+ #
57
+ # @return [String] value
58
+ #
59
+ def [](key)
60
+ @headers[key]
61
+ end
62
+
63
+ # Get the response header
64
+ #
65
+ # @parameter [String] key
66
+ # @parameter [String] value
67
+ #
68
+ # @return [String] value
69
+ #
70
+ def []=(key, value)
71
+ @headers[key] = value
72
+ end
73
+
74
+ # Write to the response body
75
+ #
76
+ # @parameter [String] str
77
+ #
78
+ # @return [String] str
79
+ #
80
+ def write(str)
81
+ str = str.to_s
82
+ @length += str.bytesize
83
+ @headers[CONTENT_LENGTH] ||= @length.to_s
84
+ @body << str
85
+ end
86
+
87
+ # Set the response type to text
88
+ #
89
+ # @parameter [String] str
90
+ #
91
+ # @return [String] str
92
+ #
93
+ def text(str)
94
+ @headers[CONTENT_TYPE] = ContentType[:TEXT]
95
+ write(str)
96
+ end
97
+
98
+ # Set the response type to html
99
+ #
100
+ # @parameter [String] str
101
+ #
102
+ # @return [String] str
103
+ #
104
+ def html(str)
105
+ @headers[CONTENT_TYPE] = ContentType[:HTML]
106
+ write(str)
107
+ end
108
+
109
+ # Set the response type to json
110
+ #
111
+ # @parameter [String] str
112
+ #
113
+ # @return [String] str
114
+ #
115
+ def json(str)
116
+ @headers[CONTENT_TYPE] = ContentType[:JSON]
117
+ write(str)
118
+ end
119
+
120
+ # Redirect the response
121
+ #
122
+ # @parameter [String] path
123
+ # @parameter [Integer] status, default: 302
124
+ #
125
+ def redirect(path, status = 302)
126
+ @headers[LOCATION] = path
127
+ @status = status
128
+ end
129
+
130
+ # Finish the response
131
+ #
132
+ # @return [Array] response
133
+ #
134
+ def finish
135
+ [@status, @headers, @body]
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023-2024, by Aristóteles Coutinho.
5
+
6
+ class Lennarb
7
+ class RouteNode
8
+ attr_accessor :children, :blocks, :param_key
9
+
10
+ # Initialize the route node
11
+ #
12
+ # @return [RouteNode]
13
+ #
14
+ def initialize
15
+ @children = {}
16
+ @blocks = {}
17
+ @param_key = nil
18
+ end
19
+
20
+ # Add a route to the route node
21
+ #
22
+ # @parameter [Array] parts
23
+ # @parameter [String] http_method
24
+ # @parameter [Proc] block
25
+ #
26
+ # @return [void]
27
+ #
28
+ def add_route(parts, http_method, block)
29
+ current_node = self
30
+
31
+ parts.each do |part|
32
+ if part.start_with?(':')
33
+ key = :param
34
+ current_node.children[key] ||= RouteNode.new
35
+ current_node = current_node.children[key]
36
+ current_node.param_key = part[1..].to_sym
37
+ else
38
+ key = part
39
+ current_node.children[key] ||= RouteNode.new
40
+ current_node = current_node.children[key]
41
+ end
42
+ end
43
+
44
+ current_node.blocks[http_method] = block
45
+ end
46
+
47
+ # Match a route to the route node
48
+ #
49
+ # @parameter [Array] parts
50
+ # @parameter [String] http_method
51
+ #
52
+ # @return [Array]
53
+ #
54
+ def match_route(parts, http_method)
55
+ current_node = self
56
+ params = {}
57
+
58
+ parts.each do |part|
59
+ return [nil, nil] unless current_node.children.key?(part) || current_node.children[:param]
60
+
61
+ if current_node.children.key?(part)
62
+ current_node = current_node.children[part]
63
+ else
64
+ param_node = current_node.children[:param]
65
+ params[param_node.param_key] = part
66
+ current_node = param_node
67
+ end
68
+ end
69
+
70
+ [current_node.blocks[http_method], params]
71
+ end
72
+ end
73
+ end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
4
+ # Copyright, 2023-2024, by Aristóteles Coutinho.
5
5
 
6
- module Lennarb
7
- VERSION = '0.1.7'
6
+ class Lennarb
7
+ VERSION = '0.2.0'
8
8
 
9
9
  public_constant :VERSION
10
10
  end
data/lib/lennarb.rb CHANGED
@@ -1,46 +1,97 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
4
+ # Copyright, 2023-2024, by Aristóteles Coutinho.
5
5
 
6
6
  ENV['RACK_ENV'] ||= 'development'
7
7
 
8
- # Extension for Array class
8
+ # Core extensions
9
9
  #
10
- require 'lennarb/array_extensions'
10
+ require 'pathname'
11
+ require 'rack'
11
12
 
12
13
  # Base class for Lennarb
13
14
  #
14
- require 'lenna/application'
15
- require 'lennarb/version'
15
+ require_relative 'lennarb/request'
16
+ require_relative 'lennarb/response'
17
+ require_relative 'lennarb/route_node'
18
+ require_relative 'lennarb/version'
16
19
 
17
- # Core extensions
18
- #
19
- require 'pathname'
20
+ class Lennarb
21
+ # Error class
22
+ #
23
+ class LennarbError < StandardError; end
20
24
 
21
- # Zeitwerk
22
- #
23
- require 'zeitwerk'
25
+ # @attribute [r] root
26
+ # @return [RouteNode]
27
+ #
28
+ attr_reader :root
24
29
 
25
- # Zeitwerk loader
26
- #
27
- Zeitwerk::Loader.new.tap do |loader|
28
- loader.inflector.inflect('Version' => 'VERSION')
29
- loader.push_dir(__dir__)
30
- loader.setup
31
- end
30
+ # Initialize the application
31
+ #
32
+ # @yield { ... } The application
33
+ #
34
+ # @return [Lennarb]
35
+ #
36
+ def initialize
37
+ @root = RouteNode.new
38
+ yield self if block_given?
39
+ end
32
40
 
33
- # Lennarb module
34
- #
35
- module Lennarb
36
- module_function
41
+ # Split a path into parts
42
+ #
43
+ # @parameter [String] path
44
+ #
45
+ # @return [Array] parts. Ex. ['users', ':id']
46
+ #
47
+ SplitPath = ->(path) { path.split('/').reject(&:empty?) }
48
+ private_constant :SplitPath
49
+
50
+ # Call the application
51
+ #
52
+ # @parameter [Hash] env
53
+ #
54
+ # @return [Array] response
55
+ #
56
+ def call(env)
57
+ res = Response.new
58
+ http_method = env.fetch('REQUEST_METHOD').to_sym
59
+ parts = SplitPath[env.fetch('PATH_INFO')]
60
+
61
+ block, params = @root.match_route(parts, http_method)
62
+ return [404, { 'content-type' => 'text/plain' }, ['Not Found']] unless block
63
+
64
+ req = Request.new(env, params)
65
+ instance_exec(req, res, &block)
37
66
 
38
- # Lennarb root path
67
+ res.finish
68
+ end
69
+
70
+ # Add a routes
71
+ #
72
+ # @parameter [String] path
73
+ # @parameter [Proc] block
74
+ #
75
+ # @return [void]
76
+ #
77
+ def get(path, &block) = add_route(path, :GET, block)
78
+ def post(path, &block) = add_route(path, :POST, block)
79
+ def put(path, &block) = add_route(path, :PUT, block)
80
+ def patch(path, &block) = add_route(path, :PATCH, block)
81
+ def delete(path, &block) = add_route(path, :DELETE, block)
82
+
83
+ private
84
+
85
+ # Add a route
86
+ #
87
+ # @parameter [String] path
88
+ # @parameter [String] http_method
89
+ # @parameter [Proc] block
39
90
  #
40
- # @return [Pathname] the root path
91
+ # @return [void]
41
92
  #
42
- def root
43
- File.expand_path('..', __dir__)
44
- Pathname.new(File.expand_path('..', __dir__))
93
+ def add_route(path, http_method, block)
94
+ parts = SplitPath[path]
95
+ @root.add_route(parts, http_method, block)
45
96
  end
46
97
  end
data/license.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2023, by Aristóteles Coutinho.
3
+ Copyright, 2023-2024, by Aristóteles Coutinho.
4
+ Copyright, 2023, by aristotelesbr.
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -1,26 +1,43 @@
1
1
  # Lennarb
2
2
 
3
- Lennarb is a experimental lightweight, fast, and modular web framework for Ruby based on Rack.
3
+ Lennarb is a lightweight, fast, and modular web framework for Ruby based on Rack. The **Lennarb** supports Ruby (MRI) 3.0+
4
4
 
5
- ## Usage
5
+ **Basic Usage**
6
+
7
+ ```ruby
8
+ require "lennarb"
9
+
10
+ Lennarb.new do |router|
11
+ router.get("/hello/:name") do |req, res|
12
+ name = req.params[:name]
13
+ res.html("Hello, #{name}!")
14
+ end
15
+ end
16
+ ```
6
17
 
7
- Please see the [project documentation](https://aristotelesbr.github.io/lennarb) for more details.
18
+ ## Performance
8
19
 
9
- - [Getting Started](https://aristotelesbr.github.io/lennarbguides/getting-started/index) - This guide show you how to use the `lennarb`
20
+ ### 1. Requests per Second (RPS)
10
21
 
11
- - [Middlewares](https://aristotelesbr.github.io/lennarbguides/middlewares/index) - This guide shows how to use middlewares in Lennarb.
22
+ | Position | Application | 10 RPS | 100 RPS | 1.000 RPS | 10.000 RPS |
23
+ | -------- | ----------- | ---------- | ---------- | --------- | ---------- |
24
+ | 1 | Lenna | 126.252,36 | 108.086,55 | 87.111,91 | 68.460,64 |
25
+ | 2 | Roda | 123.360,37 | 88.380,56 | 66.990,77 | 48.108,29 |
26
+ | 3 | Syro | 114.105,38 | 80.909,39 | 61.415,86 | 46.639,81 |
27
+ | 4 | Hanami-API | 68.089,18 | 52.851,88 | 40.801,78 | 27.996,00 |
12
28
 
13
- - [Namespace routes](https://aristotelesbr.github.io/lennarbguides/namespace-routes/index) - This guide show you how to use namespace routes.
29
+ This table ranks the routers by the number of requests they can process per second. Higher numbers indicate better performance.
30
+
31
+ Plese see [Performance](https://aristotelesbr.github.io/lennarb/guides/performance/index.html) for more information.
32
+
33
+ ## Usage
14
34
 
15
- ## Contributing
35
+ - [Getting Started](https://aristotelesbr.github.io/lennarb/guides/getting-started/index) - This guide covers getting up and running with **Lennarb**.
16
36
 
17
- We welcome contributions to this project.
37
+ - [Performance](https://aristotelesbr.github.io/lennarb/guides/performance/index.html) - The **Lennarb** is very fast. The following benchmarks were performed on a MacBook Pro (Retina, 13-inch, Early 2013) with 2,7 GHz Intel Core i7 and 8 GB 1867 MHz DDR3. Based on [jeremyevans/r10k](https://github.com/jeremyevans/r10k) using the following [template build](static/r10k/build/lennarb.rb).
18
38
 
19
- 1. Fork it.
20
- 2. Create your feature branch (`git checkout -b my-new-feature`).
21
- 3. Commit your changes (`git commit -am 'Add some feature'`).
22
- 4. Push to the branch (`git push origin my-new-feature`).
23
- 5. Create new Pull Request.
39
+ - [Response](https://aristotelesbr.github.io/lennarb/guides/response/index.html) - This is the response guide.
40
+ The `res` object is used to send a response to the client. The Lennarb use a custom response object to send responses to the client. The `res` object is an instance of `Lennarb::Response`.
24
41
 
25
42
  ### Developer Certificate of Origin
26
43
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lennarb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aristóteles Coutinho
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-31 00:00:00.000000000 Z
11
+ date: 2024-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.1'
27
- - !ruby/object:Gem::Dependency
28
- name: console
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.23'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.23'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rack
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -59,45 +45,33 @@ dependencies:
59
45
  - !ruby/object:Gem::Version
60
46
  version: 3.0.8
61
47
  - !ruby/object:Gem::Dependency
62
- name: zeitwerk
48
+ name: bake
63
49
  requirement: !ruby/object:Gem::Requirement
64
50
  requirements:
65
51
  - - "~>"
66
52
  - !ruby/object:Gem::Version
67
- version: '2.6'
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: 2.6.12
71
- type: :runtime
53
+ version: 0.18.2
54
+ type: :development
72
55
  prerelease: false
73
56
  version_requirements: !ruby/object:Gem::Requirement
74
57
  requirements:
75
58
  - - "~>"
76
59
  - !ruby/object:Gem::Version
77
- version: '2.6'
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 2.6.12
60
+ version: 0.18.2
81
61
  - !ruby/object:Gem::Dependency
82
- name: bake
62
+ name: bundler
83
63
  requirement: !ruby/object:Gem::Requirement
84
64
  requirements:
85
65
  - - "~>"
86
66
  - !ruby/object:Gem::Version
87
- version: '0.18'
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- version: 0.18.2
67
+ version: '2.2'
91
68
  type: :development
92
69
  prerelease: false
93
70
  version_requirements: !ruby/object:Gem::Requirement
94
71
  requirements:
95
72
  - - "~>"
96
73
  - !ruby/object:Gem::Version
97
- version: '0.18'
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- version: 0.18.2
74
+ version: '2.2'
101
75
  - !ruby/object:Gem::Dependency
102
76
  name: covered
103
77
  requirement: !ruby/object:Gem::Requirement
@@ -112,6 +86,20 @@ dependencies:
112
86
  - - "~>"
113
87
  - !ruby/object:Gem::Version
114
88
  version: 0.25.1
89
+ - !ruby/object:Gem::Dependency
90
+ name: minitest
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '5.20'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '5.20'
115
103
  - !ruby/object:Gem::Dependency
116
104
  name: puma
117
105
  requirement: !ruby/object:Gem::Requirement
@@ -126,6 +114,20 @@ dependencies:
126
114
  - - "~>"
127
115
  - !ruby/object:Gem::Version
128
116
  version: '6.4'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rake
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '13.0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '13.0'
129
131
  - !ruby/object:Gem::Dependency
130
132
  name: rack-test
131
133
  requirement: !ruby/object:Gem::Requirement
@@ -177,27 +179,10 @@ extra_rdoc_files: []
177
179
  files:
178
180
  - changelog.md
179
181
  - exe/lenna
180
- - lib/lenna/application.rb
181
- - lib/lenna/cli/app.rb
182
- - lib/lenna/cli/commands/create_project.rb
183
- - lib/lenna/cli/commands/interface.rb
184
- - lib/lenna/cli/commands/start_server.rb
185
- - lib/lenna/cli/templates/application.erb
186
- - lib/lenna/cli/templates/config.ru.erb
187
- - lib/lenna/cli/templates/gemfile.erb
188
- - lib/lenna/middleware/app.rb
189
- - lib/lenna/middleware/default/error_handler.rb
190
- - lib/lenna/middleware/default/logging.rb
191
- - lib/lenna/middleware/default/reload.rb
192
- - lib/lenna/router.rb
193
- - lib/lenna/router/builder.rb
194
- - lib/lenna/router/cache.rb
195
- - lib/lenna/router/namespace_stack.rb
196
- - lib/lenna/router/request.rb
197
- - lib/lenna/router/response.rb
198
- - lib/lenna/router/route_matcher.rb
199
182
  - lib/lennarb.rb
200
- - lib/lennarb/array_extensions.rb
183
+ - lib/lennarb/request.rb
184
+ - lib/lennarb/response.rb
185
+ - lib/lennarb/route_node.rb
201
186
  - lib/lennarb/version.rb
202
187
  - license.md
203
188
  - readme.md
@@ -218,14 +203,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
218
203
  requirements:
219
204
  - - ">="
220
205
  - !ruby/object:Gem::Version
221
- version: '3.0'
206
+ version: '3.1'
222
207
  required_rubygems_version: !ruby/object:Gem::Requirement
223
208
  requirements:
224
209
  - - ">="
225
210
  - !ruby/object:Gem::Version
226
211
  version: '0'
227
212
  requirements: []
228
- rubygems_version: 3.4.10
213
+ rubygems_version: 3.5.3
229
214
  signing_key:
230
215
  specification_version: 4
231
216
  summary: A lightweight and experimental web framework for Ruby.