macaw_framework 0.1.0 → 0.1.2
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/CHANGELOG.md +11 -0
- data/README.md +2 -2
- data/lib/macaw_framework/http_status_code.rb +61 -64
- data/lib/macaw_framework/request_data_filtering.rb +60 -0
- data/lib/macaw_framework/version.rb +1 -1
- data/lib/macaw_framework.rb +35 -24
- data/sig/http_status_code.rbs +3 -0
- data/sig/macaw_framework/macaw.rbs +4 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 512b14576a1be6337300fc758b7be6acf48603fa605c0d60c21d494b2d411a10
|
4
|
+
data.tar.gz: 29f0115f9e93539c740e56834515d7d66dd768a2b5a8c5ef2668beb82d403fa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa4dd23ceaa6579ab7f0b77caa42b8745fb48f830aa8f5db9eb713bff5330d1879653209781e888c873b071c45b11afd5744282401d77c9c7609c0ed9f721e4b
|
7
|
+
data.tar.gz: 2cc30aff12830763c3e9a19c8597fa4cd5f8e8ec7bfc1201f5c367883e93c480cef243d2590725084b62922538e6e27798f53840df0660b8872e09be01336f8a
|
data/CHANGELOG.md
CHANGED
@@ -3,3 +3,14 @@
|
|
3
3
|
## [0.1.0] - 2022-12-05
|
4
4
|
|
5
5
|
- Initial release
|
6
|
+
|
7
|
+
## [0.1.1] - 2022-12-06
|
8
|
+
|
9
|
+
- Adding support for headers and body
|
10
|
+
|
11
|
+
## [0.1.2] - 2022-12-10
|
12
|
+
|
13
|
+
- Adding support to URL parameters
|
14
|
+
- Adding logs to the framework activity
|
15
|
+
- Removing undefined Status Codes from http_status_code hash
|
16
|
+
- Moving methods from Macaw class to RequestDataFiltering module, respecting SOLID
|
data/README.md
CHANGED
@@ -35,9 +35,9 @@ Example of usage:
|
|
35
35
|
require 'macaw_framework'
|
36
36
|
require 'json'
|
37
37
|
|
38
|
-
m = Macaw.new
|
38
|
+
m = MacawFramework::Macaw.new
|
39
39
|
|
40
|
-
m.get('/hello_world') do
|
40
|
+
m.get('/hello_world') do |headers, body, parameters|
|
41
41
|
return JSON.pretty_generate({ hello_message: 'Hello World!' }), 200
|
42
42
|
end
|
43
43
|
|
@@ -7,69 +7,66 @@ module HttpStatusCode
|
|
7
7
|
##
|
8
8
|
# Http Status Code Map
|
9
9
|
HTTP_STATUS_CODE_MAP = {
|
10
|
-
100 =>
|
11
|
-
101 =>
|
12
|
-
102 =>
|
13
|
-
103 =>
|
14
|
-
200 =>
|
15
|
-
201 =>
|
16
|
-
202 =>
|
17
|
-
203 =>
|
18
|
-
204 =>
|
19
|
-
205 =>
|
20
|
-
206 =>
|
21
|
-
207 =>
|
22
|
-
208 =>
|
23
|
-
226 =>
|
24
|
-
300 =>
|
25
|
-
301 =>
|
26
|
-
302 =>
|
27
|
-
303 =>
|
28
|
-
304 =>
|
29
|
-
305 =>
|
30
|
-
307 =>
|
31
|
-
308 =>
|
32
|
-
400 =>
|
33
|
-
401 =>
|
34
|
-
402 =>
|
35
|
-
403 =>
|
36
|
-
404 =>
|
37
|
-
405 =>
|
38
|
-
406 =>
|
39
|
-
407 =>
|
40
|
-
408 =>
|
41
|
-
409 =>
|
42
|
-
410 =>
|
43
|
-
411 =>
|
44
|
-
412 =>
|
45
|
-
413 =>
|
46
|
-
414 =>
|
47
|
-
415 =>
|
48
|
-
416 =>
|
49
|
-
417 =>
|
50
|
-
421 =>
|
51
|
-
422 =>
|
52
|
-
423 =>
|
53
|
-
424 =>
|
54
|
-
425 =>
|
55
|
-
426 =>
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
509 => 'Unassigned',
|
72
|
-
510 => 'Not Extended (OBSOLETED)',
|
73
|
-
511 => 'Network Authentication Required'
|
10
|
+
100 => "Continue",
|
11
|
+
101 => "Switching Protocols",
|
12
|
+
102 => "Processing",
|
13
|
+
103 => "Early Hints",
|
14
|
+
200 => "OK",
|
15
|
+
201 => "Created",
|
16
|
+
202 => "Accepted",
|
17
|
+
203 => "Non-Authoritative Information",
|
18
|
+
204 => "No Content",
|
19
|
+
205 => "Reset Content",
|
20
|
+
206 => "Partial Content",
|
21
|
+
207 => "Multi-Status",
|
22
|
+
208 => "Already Reported",
|
23
|
+
226 => "IM Used",
|
24
|
+
300 => "Multiple Choices",
|
25
|
+
301 => "Moved Permanently",
|
26
|
+
302 => "Found",
|
27
|
+
303 => "See Other",
|
28
|
+
304 => "Not Modified",
|
29
|
+
305 => "Use Proxy",
|
30
|
+
307 => "Temporary Redirect",
|
31
|
+
308 => "Permanent Redirect",
|
32
|
+
400 => "Bad Request",
|
33
|
+
401 => "Unauthorized",
|
34
|
+
402 => "Payment Required",
|
35
|
+
403 => "Forbidden",
|
36
|
+
404 => "Not Found",
|
37
|
+
405 => "Method Not Allowed",
|
38
|
+
406 => "Not Acceptable",
|
39
|
+
407 => "Proxy Authentication Required",
|
40
|
+
408 => "Request Timeout",
|
41
|
+
409 => "Conflict",
|
42
|
+
410 => "Gone",
|
43
|
+
411 => "Length Required",
|
44
|
+
412 => "Precondition Failed",
|
45
|
+
413 => "Content Too Large",
|
46
|
+
414 => "URI Too Long",
|
47
|
+
415 => "Unsupported Media Type",
|
48
|
+
416 => "Range Not Satisfiable",
|
49
|
+
417 => "Expectation Failed",
|
50
|
+
421 => "Misdirected Request",
|
51
|
+
422 => "Unprocessable Content",
|
52
|
+
423 => "Locked",
|
53
|
+
424 => "Failed Dependency",
|
54
|
+
425 => "Too Early",
|
55
|
+
426 => "Upgrade Required",
|
56
|
+
428 => "Precondition Required",
|
57
|
+
429 => "Too Many Requests",
|
58
|
+
431 => "Request Header Fields Too Large",
|
59
|
+
451 => "Unavailable For Legal Reasons",
|
60
|
+
500 => "Internal Server Error",
|
61
|
+
501 => "Not Implemented",
|
62
|
+
502 => "Bad Gateway",
|
63
|
+
503 => "Service Unavailable",
|
64
|
+
504 => "Gateway Timeout",
|
65
|
+
505 => "HTTP Version Not Supported",
|
66
|
+
506 => "Variant Also Negotiates",
|
67
|
+
507 => "Insufficient Storage",
|
68
|
+
508 => "Loop Detected",
|
69
|
+
510 => "Not Extended (OBSOLETED)",
|
70
|
+
511 => "Network Authentication Required"
|
74
71
|
}.freeze
|
75
72
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# Module containing methods to filter Strings
|
5
|
+
module RequestDataFiltering
|
6
|
+
##
|
7
|
+
# Method responsible for extracting information
|
8
|
+
# provided by the client like Headers and Body
|
9
|
+
def self.extract_client_info(client)
|
10
|
+
path, parameters = extract_url_parameters(client.gets.gsub('HTTP/1.1', ''))
|
11
|
+
method_name = path.gsub('/', '_').strip!.downcase
|
12
|
+
method_name.gsub!(' ', '')
|
13
|
+
body_first_line, headers = extract_headers(client)
|
14
|
+
body = extract_body(client, body_first_line, headers['Content-Length'].to_i)
|
15
|
+
[path, method_name, headers, body, parameters]
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Method responsible for extracting the path from URI
|
20
|
+
def self.extract_path(path)
|
21
|
+
path[0] == '/' ? path[1..].gsub('/', '_') : path.gsub('/', '_')
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Method responsible for extracting the headers from request
|
26
|
+
def self.extract_headers(client)
|
27
|
+
header = client.gets.delete("\n").delete("\r")
|
28
|
+
headers = {}
|
29
|
+
while header.match(%r{[a-zA-Z0-9\-/*]*: [a-zA-Z0-9\-/*]})
|
30
|
+
split_header = header.split(':')
|
31
|
+
headers[split_header[0]] = split_header[1][1..]
|
32
|
+
header = client.gets.delete("\n").delete("\r")
|
33
|
+
end
|
34
|
+
[header, headers]
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Method responsible for extracting the body from request
|
39
|
+
def self.extract_body(client, body_first_line, content_length)
|
40
|
+
body = client.read(content_length)
|
41
|
+
body_first_line << body.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Method responsible for extracting the parameters from URI
|
46
|
+
def self.extract_url_parameters(http_first_line)
|
47
|
+
return http_first_line, nil unless http_first_line =~ /\?/
|
48
|
+
|
49
|
+
path_and_parameters = http_first_line.split('?', 2)
|
50
|
+
path = "#{path_and_parameters[0]} "
|
51
|
+
parameters_array = path_and_parameters[1].split('&')
|
52
|
+
parameters_array.map! do |item|
|
53
|
+
split_item = item.split('=')
|
54
|
+
{ split_item[0] => split_item[1].gsub("\n", '').gsub("\r", '').gsub("\s", '') }
|
55
|
+
end
|
56
|
+
parameters = {}
|
57
|
+
parameters_array.each { |item| parameters.merge!(item) }
|
58
|
+
[path, parameters]
|
59
|
+
end
|
60
|
+
end
|
data/lib/macaw_framework.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
|
7
|
-
require
|
3
|
+
require_relative "macaw_framework/endpoint_not_mapped_error"
|
4
|
+
require_relative "macaw_framework/request_data_filtering"
|
5
|
+
require_relative "macaw_framework/http_status_code"
|
6
|
+
require_relative "macaw_framework/version"
|
7
|
+
require "socket"
|
8
|
+
require "json"
|
8
9
|
|
9
10
|
module MacawFramework
|
10
11
|
##
|
@@ -12,7 +13,9 @@ module MacawFramework
|
|
12
13
|
# starting the web server.
|
13
14
|
class Macaw
|
14
15
|
include(HttpStatusCode)
|
15
|
-
|
16
|
+
##
|
17
|
+
# @param {Logger} custom_log
|
18
|
+
def initialize(custom_log = nil)
|
16
19
|
begin
|
17
20
|
config = JSON.parse(File.read('application.json'))
|
18
21
|
@port = config['macaw']['port']
|
@@ -20,6 +23,7 @@ module MacawFramework
|
|
20
23
|
@port ||= 8080
|
21
24
|
end
|
22
25
|
@port ||= 8080
|
26
|
+
@macaw_log ||= custom_log.nil? ? Logger.new($stdout) : custom_log
|
23
27
|
end
|
24
28
|
|
25
29
|
##
|
@@ -29,8 +33,9 @@ module MacawFramework
|
|
29
33
|
# @param {Proc} block
|
30
34
|
# @return {Integer, String}
|
31
35
|
def get(path, &block)
|
32
|
-
path_clean =
|
33
|
-
|
36
|
+
path_clean = RequestDataFiltering.extract_path(path)
|
37
|
+
@macaw_log.info("Defining GET endpoint at #{path_clean}")
|
38
|
+
map_new_endpoint('get', path_clean, &block)
|
34
39
|
end
|
35
40
|
|
36
41
|
##
|
@@ -41,7 +46,8 @@ module MacawFramework
|
|
41
46
|
# @return {String, Integer}
|
42
47
|
def post(path, &block)
|
43
48
|
path_clean = path[0] == '/' ? path[1..].gsub('/', '_') : path.gsub('/', '_')
|
44
|
-
|
49
|
+
@macaw_log.info("Defining POST endpoint at #{path_clean}")
|
50
|
+
map_new_endpoint('post', path_clean, &block)
|
45
51
|
end
|
46
52
|
|
47
53
|
##
|
@@ -52,7 +58,8 @@ module MacawFramework
|
|
52
58
|
# @return {String, Integer}
|
53
59
|
def put(path, &block)
|
54
60
|
path_clean = path[0] == '/' ? path[1..].gsub('/', '_') : path.gsub('/', '_')
|
55
|
-
|
61
|
+
@macaw_log.info("Defining PUT endpoint at #{path_clean}")
|
62
|
+
map_new_endpoint('put', path_clean, &block)
|
56
63
|
end
|
57
64
|
|
58
65
|
##
|
@@ -63,7 +70,8 @@ module MacawFramework
|
|
63
70
|
# @return {String, Integer}
|
64
71
|
def patch(path, &block)
|
65
72
|
path_clean = path[0] == '/' ? path[1..].gsub('/', '_') : path.gsub('/', '_')
|
66
|
-
|
73
|
+
@macaw_log.info("Defining PATCH endpoint at #{path_clean}")
|
74
|
+
map_new_endpoint('patch', path_clean, &block)
|
67
75
|
end
|
68
76
|
|
69
77
|
##
|
@@ -74,20 +82,24 @@ module MacawFramework
|
|
74
82
|
# @return {String, Integer}
|
75
83
|
def delete(path, &block)
|
76
84
|
path_clean = path[0] == '/' ? path[1..].gsub('/', '_') : path.gsub('/', '_')
|
77
|
-
|
85
|
+
@macaw_log.info("Defining DELETE endpoint at #{path_clean}")
|
86
|
+
map_new_endpoint('delete', path_clean, &block)
|
78
87
|
end
|
79
88
|
|
80
89
|
##
|
81
90
|
# Starts the web server
|
82
91
|
def start!
|
92
|
+
@macaw_log.info("Starting server at port #{@port}")
|
93
|
+
time = Time.now
|
83
94
|
server = TCPServer.open(@port)
|
84
|
-
|
95
|
+
@macaw_log.info("Server started in #{Time.now - time} seconds.")
|
85
96
|
loop do
|
86
97
|
Thread.start(server.accept) do |client|
|
87
|
-
method_name = extract_client_info(client)
|
98
|
+
path, method_name, headers, body, parameters = RequestDataFiltering.extract_client_info(client)
|
88
99
|
raise EndpointNotMappedError unless respond_to?(method_name)
|
89
100
|
|
90
|
-
|
101
|
+
@macaw_log.info("Running #{path.gsub("\n", '').gsub("\r", '')}")
|
102
|
+
message, status = send(method_name, headers, body, parameters)
|
91
103
|
status ||= 200
|
92
104
|
message ||= 'Ok'
|
93
105
|
client.puts "HTTP/1.1 #{status} #{HTTP_STATUS_CODE_MAP[status]} \r\n\r\n#{message}"
|
@@ -95,23 +107,22 @@ module MacawFramework
|
|
95
107
|
rescue EndpointNotMappedError
|
96
108
|
client.print "HTTP/1.1 404 Not Found\r\n\r\n"
|
97
109
|
client.close
|
98
|
-
rescue StandardError
|
110
|
+
rescue StandardError => e
|
99
111
|
client.print "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
112
|
+
@macaw_log.info("Error: #{e}")
|
100
113
|
client.close
|
101
114
|
end
|
102
115
|
end
|
116
|
+
rescue Interrupt
|
117
|
+
@macaw_log.info('Stopping server')
|
118
|
+
server.close
|
119
|
+
@macaw_log.info('Macaw stop flying for some seeds...')
|
103
120
|
end
|
104
121
|
|
105
122
|
private
|
106
123
|
|
107
|
-
|
108
|
-
|
109
|
-
# STILL IN DEVELOPMENT
|
110
|
-
# @todo finish implementation
|
111
|
-
def extract_client_info(client)
|
112
|
-
method_name = client.gets.gsub('HTTP/1.1', '').gsub('/', '_').strip!.downcase
|
113
|
-
method_name.gsub!(' ', '')
|
114
|
-
method_name
|
124
|
+
def map_new_endpoint(prefix, path, &block)
|
125
|
+
define_singleton_method("#{prefix}_#{path}", block)
|
115
126
|
end
|
116
127
|
end
|
117
128
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: macaw_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aria Diniz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-12-
|
11
|
+
date: 2022-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A project started for study purpose that I intend to keep working on.
|
14
14
|
email:
|
@@ -27,13 +27,16 @@ files:
|
|
27
27
|
- lib/macaw_framework.rb
|
28
28
|
- lib/macaw_framework/endpoint_not_mapped_error.rb
|
29
29
|
- lib/macaw_framework/http_status_code.rb
|
30
|
+
- lib/macaw_framework/request_data_filtering.rb
|
30
31
|
- lib/macaw_framework/version.rb
|
32
|
+
- sig/http_status_code.rbs
|
31
33
|
- sig/macaw_framework.rbs
|
32
34
|
- sig/macaw_framework/macaw.rbs
|
33
35
|
homepage: https://github.com/ariasdiniz/macaw_framework
|
34
36
|
licenses:
|
35
37
|
- MIT
|
36
38
|
metadata:
|
39
|
+
documentation_uri: https://rubydoc.info/gems/macaw_framework
|
37
40
|
homepage_uri: https://github.com/ariasdiniz/macaw_framework
|
38
41
|
source_code_uri: https://github.com/ariasdiniz/macaw_framework
|
39
42
|
post_install_message:
|
@@ -44,7 +47,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
47
|
requirements:
|
45
48
|
- - ">="
|
46
49
|
- !ruby/object:Gem::Version
|
47
|
-
version: 2.
|
50
|
+
version: 2.7.0
|
48
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
52
|
requirements:
|
50
53
|
- - ">="
|