aris 1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +342 -0
- data/lib/aris/adapters/base.rb +25 -0
- data/lib/aris/adapters/joys_integration.rb +94 -0
- data/lib/aris/adapters/mock/adapter.rb +141 -0
- data/lib/aris/adapters/mock/request.rb +81 -0
- data/lib/aris/adapters/mock/response.rb +17 -0
- data/lib/aris/adapters/rack/adapter.rb +117 -0
- data/lib/aris/adapters/rack/request.rb +66 -0
- data/lib/aris/adapters/rack/response.rb +16 -0
- data/lib/aris/core.rb +931 -0
- data/lib/aris/discovery.rb +312 -0
- data/lib/aris/locale_injector.rb +39 -0
- data/lib/aris/pipeline_runner.rb +100 -0
- data/lib/aris/plugins/api_key_auth.rb +61 -0
- data/lib/aris/plugins/basic_auth.rb +68 -0
- data/lib/aris/plugins/bearer_auth.rb +64 -0
- data/lib/aris/plugins/cache.rb +120 -0
- data/lib/aris/plugins/compression.rb +96 -0
- data/lib/aris/plugins/cookies.rb +46 -0
- data/lib/aris/plugins/cors.rb +81 -0
- data/lib/aris/plugins/csrf.rb +48 -0
- data/lib/aris/plugins/etag.rb +90 -0
- data/lib/aris/plugins/flash.rb +124 -0
- data/lib/aris/plugins/form_parser.rb +46 -0
- data/lib/aris/plugins/health_check.rb +62 -0
- data/lib/aris/plugins/json.rb +32 -0
- data/lib/aris/plugins/multipart.rb +160 -0
- data/lib/aris/plugins/rate_limiter.rb +60 -0
- data/lib/aris/plugins/request_id.rb +38 -0
- data/lib/aris/plugins/request_logger.rb +43 -0
- data/lib/aris/plugins/security_headers.rb +99 -0
- data/lib/aris/plugins/session.rb +175 -0
- data/lib/aris/plugins.rb +23 -0
- data/lib/aris/response_helpers.rb +156 -0
- data/lib/aris/route_helpers.rb +141 -0
- data/lib/aris/utils/redirects.rb +44 -0
- data/lib/aris/utils/sitemap.rb +84 -0
- data/lib/aris/version.rb +3 -0
- data/lib/aris.rb +35 -0
- metadata +151 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# lib/aris/adapters/mock/request.rb
|
|
2
|
+
module Aris
|
|
3
|
+
module Adapters
|
|
4
|
+
module Mock
|
|
5
|
+
class Request
|
|
6
|
+
attr_reader :raw_request, :cookies # Add cookies
|
|
7
|
+
attr_accessor :json_body
|
|
8
|
+
|
|
9
|
+
def initialize(method:, path:, domain:, headers: {}, body: '', query: '', cookies: {}) # Add cookies parameter
|
|
10
|
+
@raw_request = {
|
|
11
|
+
method: method.to_s.upcase,
|
|
12
|
+
path: path,
|
|
13
|
+
domain: domain,
|
|
14
|
+
headers: headers,
|
|
15
|
+
body: body,
|
|
16
|
+
query: query
|
|
17
|
+
}
|
|
18
|
+
@cookies = cookies || {} # Initialize cookies
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Add env method for plugin compatibility
|
|
22
|
+
def env
|
|
23
|
+
@raw_request[:headers]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def host
|
|
27
|
+
@raw_request[:domain]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
alias_method :domain, :host
|
|
31
|
+
|
|
32
|
+
def request_method
|
|
33
|
+
@raw_request[:method]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def method
|
|
37
|
+
@raw_request[:method]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def path_info
|
|
41
|
+
@raw_request[:path]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
alias_method :path, :path_info
|
|
45
|
+
|
|
46
|
+
def query
|
|
47
|
+
@raw_request[:query]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def headers
|
|
51
|
+
@raw_request[:headers]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def body
|
|
55
|
+
@raw_request[:body]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def params
|
|
59
|
+
# Simple query string parser (no external dependencies)
|
|
60
|
+
return @params if @params
|
|
61
|
+
@params = {}
|
|
62
|
+
query.split('&').each do |pair|
|
|
63
|
+
key, value = pair.split('=')
|
|
64
|
+
@params[key] = value if key
|
|
65
|
+
end
|
|
66
|
+
@params
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def [](key)
|
|
70
|
+
case key
|
|
71
|
+
when :method then method
|
|
72
|
+
when :domain then domain
|
|
73
|
+
when :path then path
|
|
74
|
+
when :host then host
|
|
75
|
+
else @raw_request[key]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# lib/aris/adapters/mock/response.rb
|
|
2
|
+
module Aris
|
|
3
|
+
module Adapters
|
|
4
|
+
module Mock
|
|
5
|
+
class Response
|
|
6
|
+
include Aris::ResponseHelpers
|
|
7
|
+
attr_accessor :status, :headers, :body
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@status = 200
|
|
11
|
+
@headers = {'content-type' => 'text/html'}
|
|
12
|
+
@body = []
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# lib/aris/adapters/rack/adapter.rb
|
|
2
|
+
require_relative '../../core'
|
|
3
|
+
require_relative '../../pipeline_runner'
|
|
4
|
+
require_relative '../base'
|
|
5
|
+
require_relative 'request'
|
|
6
|
+
require_relative 'response'
|
|
7
|
+
require 'json'
|
|
8
|
+
|
|
9
|
+
module Aris
|
|
10
|
+
module Adapters
|
|
11
|
+
class RackApp < Base
|
|
12
|
+
def initialize(app = nil)
|
|
13
|
+
@app = app
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# lib/aris/adapters/rack/adapter.rb
|
|
17
|
+
|
|
18
|
+
# lib/aris/adapters/rack/adapter.rb
|
|
19
|
+
|
|
20
|
+
def call(env)
|
|
21
|
+
request = Rack::Request.new(env)
|
|
22
|
+
return res if res = handle_sitemap(request)
|
|
23
|
+
return res if res = handle_redirect(request)
|
|
24
|
+
redirect_res, normalized_path = Aris.handle_trailing_slash(request.path_info)
|
|
25
|
+
return redirect_res if redirect_res
|
|
26
|
+
|
|
27
|
+
path_for_matching = normalized_path || request.path_info
|
|
28
|
+
|
|
29
|
+
request_domain = request.host
|
|
30
|
+
Thread.current[:aris_current_domain] = request_domain
|
|
31
|
+
|
|
32
|
+
response = Rack::Response.new
|
|
33
|
+
request.instance_variable_set(:@response, response)
|
|
34
|
+
request.define_singleton_method(:response) { @response }
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
domain_config = Aris::Router.domain_config(request_domain)
|
|
38
|
+
route = Aris::Router.match(
|
|
39
|
+
domain: request_domain,
|
|
40
|
+
method: request.request_method.downcase.to_sym,
|
|
41
|
+
path: path_for_matching
|
|
42
|
+
)
|
|
43
|
+
if route
|
|
44
|
+
inject_locale_methods(request, route, domain_config)
|
|
45
|
+
result = PipelineRunner.call(request: request, route: route, response: response)
|
|
46
|
+
format_response(result, response)
|
|
47
|
+
else
|
|
48
|
+
format_response(Aris.not_found(request, response), response)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
rescue Aris::Router::RouteNotFoundError
|
|
52
|
+
format_response(Aris.not_found(request, response), response)
|
|
53
|
+
rescue Exception => e
|
|
54
|
+
# Error Path: 500
|
|
55
|
+
format_response(Aris.error(request, e), response)
|
|
56
|
+
|
|
57
|
+
ensure
|
|
58
|
+
Thread.current[:aris_current_domain] = nil
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def subdomain
|
|
63
|
+
@subdomain || extract_subdomain_from_domain
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def extract_subdomain_from_domain
|
|
69
|
+
return nil unless @env[:subdomain] || @env['SUBDOMAIN']
|
|
70
|
+
|
|
71
|
+
@env[:subdomain] || @env['SUBDOMAIN']
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def inject_locale_methods(request, route, domain_config)
|
|
76
|
+
return unless route[:locale]
|
|
77
|
+
|
|
78
|
+
# Inject the locale helper
|
|
79
|
+
request.define_singleton_method(:locale) { route[:locale] }
|
|
80
|
+
|
|
81
|
+
# Inject domain-specific locale configuration if it exists
|
|
82
|
+
if domain_config
|
|
83
|
+
request.define_singleton_method(:available_locales) { domain_config[:locales] }
|
|
84
|
+
request.define_singleton_method(:default_locale) { domain_config[:default_locale] }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def format_response(result, response = nil)
|
|
89
|
+
# 1. Identify the 'active' response source
|
|
90
|
+
# We check the passed object (response) and the returned object (result)
|
|
91
|
+
active_res = if result.respond_to?(:body) && !Array(result.body).empty?
|
|
92
|
+
result
|
|
93
|
+
elsif response.respond_to?(:body) && !Array(response.body).empty?
|
|
94
|
+
response
|
|
95
|
+
else
|
|
96
|
+
nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# 2. If we found a response with content, send it to the Rack server
|
|
100
|
+
if active_res
|
|
101
|
+
# Standardize the body as an Array of Strings for Rack 3 compatibility
|
|
102
|
+
body = active_res.body
|
|
103
|
+
body = [body] if body.is_a?(String)
|
|
104
|
+
|
|
105
|
+
return [active_res.status, active_res.headers, body]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# 3. Final Fallback (If Joys rendered nothing)
|
|
109
|
+
case result
|
|
110
|
+
when Array then result
|
|
111
|
+
when Hash then [200, {'content-type' => 'application/json'}, [result.to_json]]
|
|
112
|
+
else [200, {'content-type' => 'text/plain'}, [result.to_s]]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# lib/aris/adapters/rack/request.rb
|
|
2
|
+
module Aris
|
|
3
|
+
module Adapters
|
|
4
|
+
module Rack
|
|
5
|
+
class Request
|
|
6
|
+
attr_reader :env
|
|
7
|
+
attr_accessor :json_body
|
|
8
|
+
|
|
9
|
+
def initialize(env)
|
|
10
|
+
@env = env
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Add cookies method to match Mock adapter interface
|
|
14
|
+
def cookies
|
|
15
|
+
@env['rack.request.cookie_hash'] || {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def host
|
|
19
|
+
@env['HTTP_HOST'] || @env['SERVER_NAME']
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
alias_method :domain, :host
|
|
23
|
+
|
|
24
|
+
def request_method
|
|
25
|
+
@env['REQUEST_METHOD']
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method
|
|
29
|
+
@env['REQUEST_METHOD']
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def path_info
|
|
33
|
+
@env['PATH_INFO']
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
alias_method :path, :path_info
|
|
37
|
+
|
|
38
|
+
def query
|
|
39
|
+
@env['QUERY_STRING']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def headers
|
|
43
|
+
@env.select { |k, v| k.start_with?('HTTP_') }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def body
|
|
47
|
+
@env['rack.input']&.read
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def params
|
|
51
|
+
@params ||= ::Rack::Utils.parse_nested_query(@env['QUERY_STRING'] || '')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def [](key)
|
|
55
|
+
case key
|
|
56
|
+
when :method then method
|
|
57
|
+
when :domain then domain
|
|
58
|
+
when :path then path
|
|
59
|
+
when :host then host
|
|
60
|
+
else @env[key.to_s]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# lib/aris/adapters/rack/response.rb
|
|
2
|
+
module Aris
|
|
3
|
+
module Adapters
|
|
4
|
+
module Rack
|
|
5
|
+
class Response
|
|
6
|
+
include Aris::ResponseHelpers
|
|
7
|
+
attr_accessor :status, :headers, :body
|
|
8
|
+
def initialize
|
|
9
|
+
@status = 200
|
|
10
|
+
@headers = {'content-type' => 'text/html'}
|
|
11
|
+
@body = []
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|