aris 1.3.0 → 1.4.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 +4 -4
- data/lib/aris/adapters/base.rb +1 -1
- data/lib/aris/adapters/mock/adapter.rb +20 -2
- data/lib/aris/adapters/rack/adapter.rb +121 -80
- data/lib/aris/config.rb +53 -0
- data/lib/aris/core.rb +4 -744
- data/lib/aris/router.rb +722 -0
- data/lib/aris/version.rb +1 -1
- data/lib/aris.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf323ca0ed960b60c26e09f0b575212544c8c5b15b95be5e299b6296f03d97df
|
|
4
|
+
data.tar.gz: a68dfcec5a685a815f069f960f9bdcf6462115d6f5758e4adbedaf3ddbe3b3fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d42298b4ba003ec4d10494b9545e23fc381e037087720293e176e46a49446d9897c665a8dc3d4e684551193df7b2c07850f8743542b75718b7242b0bda816246
|
|
7
|
+
data.tar.gz: b04ef54738e7f457ee594d83407f27a58b94b05c8d8c00c03106f9cfdaf950d3c58d0e153fdfe613d2e98f41a9216d10c93b84d9b9b5768e49474e9e40d283c2
|
data/lib/aris/adapters/base.rb
CHANGED
|
@@ -39,7 +39,7 @@ module Aris
|
|
|
39
39
|
if domain_config && domain_config[:locales] && domain_config[:locales].any? && domain_config[:root_locale_redirect] != false
|
|
40
40
|
if path == '/' || path.empty?
|
|
41
41
|
default_locale = domain_config[:default_locale] || domain_config[:locales].first
|
|
42
|
-
return { status: 302, headers: {'
|
|
42
|
+
return { status: 302, headers: {'location' => "/#{default_locale}/"}, body: [] }
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -52,7 +52,7 @@ module Aris
|
|
|
52
52
|
if defined?(Aris::Utils::Redirects)
|
|
53
53
|
redirect = Aris::Utils::Redirects.find(path)
|
|
54
54
|
if redirect
|
|
55
|
-
return { status: redirect[:status], headers: {'
|
|
55
|
+
return { status: redirect[:status], headers: {'location' => redirect[:to]}, body: [] }
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -116,6 +116,24 @@ module Aris
|
|
|
116
116
|
cookies
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
+
def handle_static(request)
|
|
120
|
+
return nil unless request.method == 'GET'
|
|
121
|
+
return nil unless Aris::Config.serve_static
|
|
122
|
+
|
|
123
|
+
path = File.join('public', request.path)
|
|
124
|
+
return nil unless File.file?(path)
|
|
125
|
+
|
|
126
|
+
[200,
|
|
127
|
+
{'content-type' => mime_type(path)},
|
|
128
|
+
[File.binread(path)]
|
|
129
|
+
]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def mime_type(path)
|
|
133
|
+
ext = File.extname(path).downcase
|
|
134
|
+
Aris::Config.mime_types[ext] || 'application/octet-stream'
|
|
135
|
+
end
|
|
136
|
+
|
|
119
137
|
def format_response(result, response = nil)
|
|
120
138
|
case result
|
|
121
139
|
when Response
|
|
@@ -13,57 +13,87 @@ module Aris
|
|
|
13
13
|
@app = app
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
def call(env)
|
|
17
|
+
request = Rack::Request.new(env)
|
|
18
|
+
static_response = handle_static(request)
|
|
19
|
+
return static_response if static_response
|
|
20
|
+
|
|
21
|
+
sitemap_response = handle_sitemap(request)
|
|
22
|
+
return sitemap_response if sitemap_response
|
|
23
|
+
|
|
24
|
+
redirect_response = handle_redirect(request)
|
|
25
|
+
return redirect_response if redirect_response
|
|
26
|
+
|
|
27
|
+
redirect_res, normalized_path = Aris.handle_trailing_slash(request.path_info)
|
|
28
|
+
return redirect_res if redirect_res
|
|
29
|
+
|
|
30
|
+
# return res if res = handle_static(request)
|
|
31
|
+
# return res if res = handle_sitemap(request)
|
|
32
|
+
# return res if res = handle_redirect(request)
|
|
33
|
+
# redirect_res, normalized_path = Aris.handle_trailing_slash(request.path_info)
|
|
34
|
+
# return redirect_res if redirect_res
|
|
35
|
+
|
|
36
|
+
path_for_matching = normalized_path || request.path_info
|
|
37
|
+
|
|
38
|
+
request_domain = request.host
|
|
39
|
+
Thread.current[:aris_current_domain] = request_domain
|
|
40
|
+
|
|
41
|
+
response = Rack::Response.new
|
|
42
|
+
request.instance_variable_set(:@response, response)
|
|
43
|
+
request.define_singleton_method(:response) { @response }
|
|
19
44
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
45
|
+
begin
|
|
46
|
+
domain_config = Aris::Router.domain_config(request_domain)
|
|
47
|
+
route = Aris::Router.match(
|
|
48
|
+
domain: request_domain,
|
|
49
|
+
method: request.request_method.downcase.to_sym,
|
|
50
|
+
path: path_for_matching
|
|
51
|
+
)
|
|
52
|
+
if route
|
|
53
|
+
inject_locale_methods(request, route, domain_config)
|
|
54
|
+
result = PipelineRunner.call(request: request, route: route, response: response)
|
|
55
|
+
format_response(result, response)
|
|
56
|
+
else
|
|
57
|
+
format_response(Aris.not_found(request, response), response)
|
|
58
|
+
end
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
rescue Aris::Router::RouteNotFoundError
|
|
61
|
+
format_response(Aris.not_found(request, response), response)
|
|
62
|
+
rescue Exception => e
|
|
63
|
+
# Error Path: 500
|
|
64
|
+
format_response(Aris.error(request, e), response)
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
66
|
+
ensure
|
|
67
|
+
Thread.current[:aris_current_domain] = nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
61
70
|
|
|
62
71
|
def subdomain
|
|
63
72
|
@subdomain || extract_subdomain_from_domain
|
|
64
73
|
end
|
|
65
74
|
|
|
66
75
|
private
|
|
76
|
+
def handle_static(request)
|
|
77
|
+
return nil unless request.request_method == 'GET'
|
|
78
|
+
return nil unless Aris::Config.serve_static
|
|
79
|
+
|
|
80
|
+
path = File.join('public', request.path_info)
|
|
81
|
+
return nil unless File.file?(path)
|
|
82
|
+
|
|
83
|
+
[200,
|
|
84
|
+
{
|
|
85
|
+
'content-type' => mime_type(path),
|
|
86
|
+
'cache-control' => 'public, max-age=31536000'
|
|
87
|
+
},
|
|
88
|
+
[File.binread(path)]
|
|
89
|
+
]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def mime_type(path)
|
|
93
|
+
ext = File.extname(path).downcase
|
|
94
|
+
Aris::Config.mime_types[ext] || 'application/octet-stream'
|
|
95
|
+
end
|
|
96
|
+
|
|
67
97
|
|
|
68
98
|
def extract_subdomain_from_domain
|
|
69
99
|
return nil unless @env[:subdomain] || @env['SUBDOMAIN']
|
|
@@ -72,46 +102,57 @@ end
|
|
|
72
102
|
end
|
|
73
103
|
|
|
74
104
|
|
|
75
|
-
def inject_locale_methods(request, route, domain_config)
|
|
76
|
-
|
|
105
|
+
def inject_locale_methods(request, route, domain_config)
|
|
106
|
+
return unless route[:locale]
|
|
107
|
+
|
|
108
|
+
# Inject the locale helper
|
|
109
|
+
request.define_singleton_method(:locale) { route[:locale] }
|
|
110
|
+
|
|
111
|
+
# Inject domain-specific locale configuration if it exists
|
|
112
|
+
if domain_config
|
|
113
|
+
request.define_singleton_method(:available_locales) { domain_config[:locales] }
|
|
114
|
+
request.define_singleton_method(:default_locale) { domain_config[:default_locale] }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
def handle_sitemap(request)
|
|
118
|
+
# Overridden by sitemap utils if loaded
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def handle_redirect(request)
|
|
123
|
+
# Overridden by redirects utils if loaded
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def format_response(result, response = nil)
|
|
128
|
+
# 1. Identify the 'active' response source
|
|
129
|
+
# We check the passed object (response) and the returned object (result)
|
|
130
|
+
active_res = if result.respond_to?(:body) && !Array(result.body).empty?
|
|
131
|
+
result
|
|
132
|
+
elsif response.respond_to?(:body) && !Array(response.body).empty?
|
|
133
|
+
response
|
|
134
|
+
else
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# 2. If we found a response with content, send it to the Rack server
|
|
139
|
+
if active_res
|
|
140
|
+
# Standardize the body as an Array of Strings for Rack 3 compatibility
|
|
141
|
+
body = active_res.body
|
|
142
|
+
body = [body] if body.is_a?(String)
|
|
143
|
+
|
|
144
|
+
return [active_res.status, active_res.headers, body]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# 3. Final Fallback (If Joys rendered nothing)
|
|
148
|
+
case result
|
|
149
|
+
when Array then result
|
|
150
|
+
when Hash then [200, {'content-type' => 'application/json'}, [result.to_json]]
|
|
151
|
+
else [200, {'content-type' => 'text/plain'}, [result.to_s]]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
77
154
|
|
|
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
155
|
|
|
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
156
|
end
|
|
116
157
|
end
|
|
117
158
|
end
|
data/lib/aris/config.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Aris
|
|
2
|
+
module Config
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :trailing_slash, :trailing_slash_status,:secret_key_base, :cookie_options, :serve_static
|
|
5
|
+
DEFAULT_MIME_TYPES = {
|
|
6
|
+
'.html' => 'text/html',
|
|
7
|
+
'.css' => 'text/css',
|
|
8
|
+
'.js' => 'application/javascript',
|
|
9
|
+
'.json' => 'application/json',
|
|
10
|
+
'.jpg' => 'image/jpeg',
|
|
11
|
+
'.jpeg' => 'image/jpeg',
|
|
12
|
+
'.png' => 'image/png',
|
|
13
|
+
'.gif' => 'image/gif',
|
|
14
|
+
'.svg' => 'image/svg+xml',
|
|
15
|
+
'.webp' => 'image/webp',
|
|
16
|
+
'.ico' => 'image/x-icon',
|
|
17
|
+
'.woff' => 'font/woff',
|
|
18
|
+
'.woff2' => 'font/woff2',
|
|
19
|
+
'.ttf' => 'font/ttf',
|
|
20
|
+
'.pdf' => 'application/pdf',
|
|
21
|
+
'.xml' => 'application/xml',
|
|
22
|
+
'.txt' => 'text/plain',
|
|
23
|
+
'.mp3' => 'audio/mpeg',
|
|
24
|
+
'.wav' => 'audio/wav',
|
|
25
|
+
'.ogg' => 'audio/ogg',
|
|
26
|
+
'.flac' => 'audio/flac',
|
|
27
|
+
'.m4a' => 'audio/mp4'
|
|
28
|
+
}.freeze
|
|
29
|
+
def mime_types
|
|
30
|
+
@mime_types ||= DEFAULT_MIME_TYPES.dup
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def mime_types=(custom_types)
|
|
34
|
+
@mime_types = DEFAULT_MIME_TYPES.merge(custom_types)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reset!
|
|
38
|
+
@trailing_slash = :strict
|
|
39
|
+
@serve_static = false
|
|
40
|
+
@trailing_slash_status = 301
|
|
41
|
+
@secret_key_base = nil
|
|
42
|
+
@cookie_options = {
|
|
43
|
+
httponly: true,
|
|
44
|
+
secure: false, # Default to false for development
|
|
45
|
+
same_site: :lax,
|
|
46
|
+
path: '/'
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
reset!
|
|
52
|
+
end
|
|
53
|
+
end
|