tynn 2.0.0.beta3 → 2.0.0.beta4
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/LICENSE +1 -1
- data/README.md +83 -660
- data/lib/tynn.rb +186 -50
- data/lib/tynn/environment.rb +1 -84
- data/lib/tynn/json.rb +24 -25
- data/lib/tynn/render.rb +72 -33
- data/lib/tynn/request.rb +2 -82
- data/lib/tynn/response.rb +4 -118
- data/lib/tynn/secure_headers.rb +2 -24
- data/lib/tynn/session.rb +4 -87
- data/lib/tynn/ssl.rb +12 -71
- data/lib/tynn/static.rb +1 -19
- data/lib/tynn/test.rb +2 -125
- data/lib/tynn/utils.rb +51 -8
- data/lib/tynn/version.rb +1 -1
- data/lib/tynn/x/versioning.rb +29 -0
- data/test/json_test.rb +197 -7
- data/test/render_test.rb +56 -8
- data/test/request_test.rb +2 -0
- data/test/response_test.rb +7 -3
- data/test/routing_test.rb +56 -4
- data/test/settings_test.rb +31 -0
- data/test/versioning_test.rb +201 -0
- metadata +10 -35
- data/lib/tynn/base.rb +0 -434
data/lib/tynn/ssl.rb
CHANGED
@@ -3,74 +3,12 @@
|
|
3
3
|
require_relative "request"
|
4
4
|
|
5
5
|
class Tynn
|
6
|
-
# Enforces secure HTTP requests by:
|
7
|
-
#
|
8
|
-
# 1. Redirecting HTTP requests to their HTTPS counterparts.
|
9
|
-
#
|
10
|
-
# 2. Setting the HTTP <tt>Strict-Transport-Security</tt> header (HSTS).
|
11
|
-
# This ensures the browser never visits the http version of a website.
|
12
|
-
# This reduces the impact of leaking session data through cookies
|
13
|
-
# and external links, and defends against Man-in-the-middle attacks.
|
14
|
-
#
|
15
|
-
# 3. Setting the <tt>secure</tt> flag on cookies. This tells the browser to
|
16
|
-
# only transmit them over HTTPS.
|
17
|
-
#
|
18
|
-
# You can configure HSTS passing through the <tt>:hsts</tt> option.
|
19
|
-
# The following options are supported:
|
20
|
-
#
|
21
|
-
# [expires]
|
22
|
-
# The time, in seconds, that the browser access the site only by HTTPS.
|
23
|
-
# Defaults to 180 days.
|
24
|
-
#
|
25
|
-
# [subdomains]
|
26
|
-
# If this is <tt>true</tt>, the rule applies to all the site's subdomains as
|
27
|
-
# well. Defaults to <tt>true</tt>.
|
28
|
-
#
|
29
|
-
# [preload]
|
30
|
-
# A limitation of HSTS is that the initial request remains unprotected if it
|
31
|
-
# uses HTTP. The same applies to the first request after the activity period
|
32
|
-
# specified by <tt>max-age</tt>. Modern browsers implements a "HSTS preload
|
33
|
-
# list", which contains known sites supporting HSTS. If you would like to
|
34
|
-
# include your website into the list, set this option to <tt>true</tt> and
|
35
|
-
# submit your domain to this form[https://hstspreload.appspot.com/].
|
36
|
-
# Supported by Chrome, Firefox, IE11+ and IE Edge.
|
37
|
-
#
|
38
|
-
# To disable HSTS, you will need to tell the browser to expire it immediately.
|
39
|
-
# Setting <tt>hsts: false</tt> is a shortcut for <tt>hsts: { expires: 0 }</tt>.
|
40
|
-
#
|
41
|
-
# require "tynn"
|
42
|
-
# require "tynn/ssl"
|
43
|
-
# require "tynn/test"
|
44
|
-
#
|
45
|
-
# Tynn.plugin(Tynn::SSL)
|
46
|
-
#
|
47
|
-
# Tynn.define { }
|
48
|
-
#
|
49
|
-
# app = Tynn::Test.new
|
50
|
-
# app.get("/", {}, "HTTP_HOST" => "tynn.xyz")
|
51
|
-
#
|
52
|
-
# app.res.status # => 301
|
53
|
-
# app.res.location # => "https://tynn.xyz/"
|
54
|
-
#
|
55
|
-
# # Using different HSTS options
|
56
|
-
# Tynn.plugin(
|
57
|
-
# Tynn::SSL,
|
58
|
-
# hsts: {
|
59
|
-
# expires: 31_536_000,
|
60
|
-
# includeSubdomains: false,
|
61
|
-
# preload: true
|
62
|
-
# }
|
63
|
-
# )
|
64
|
-
#
|
65
|
-
# # Disabling HSTS
|
66
|
-
# Tynn.plugin(Tynn::SSL, hsts: false)
|
67
|
-
#
|
68
6
|
module SSL
|
69
|
-
def self.setup(app, hsts: {})
|
7
|
+
def self.setup(app, hsts: {})
|
70
8
|
app.use(Tynn::SSL::Middleware, hsts: hsts)
|
71
9
|
end
|
72
10
|
|
73
|
-
class Middleware
|
11
|
+
class Middleware
|
74
12
|
HSTS_MAX_AGE = 15_552_000 # 180 days
|
75
13
|
|
76
14
|
def initialize(app, hsts: {})
|
@@ -86,7 +24,7 @@ class Tynn
|
|
86
24
|
@app.call(env).tap do |_, headers, _|
|
87
25
|
set_hsts_header!(headers)
|
88
26
|
|
89
|
-
|
27
|
+
set_secure_flag!(headers)
|
90
28
|
end
|
91
29
|
end
|
92
30
|
|
@@ -115,13 +53,16 @@ class Tynn
|
|
115
53
|
headers["Strict-Transport-Security"] ||= @hsts_header
|
116
54
|
end
|
117
55
|
|
118
|
-
def
|
119
|
-
return unless
|
56
|
+
def set_secure_flag!(headers)
|
57
|
+
return unless header = headers["Set-Cookie"]
|
120
58
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
59
|
+
cookies = header.split("\n")
|
60
|
+
|
61
|
+
cookies.each do |c|
|
62
|
+
c << "; secure" if c !~ /;\s*secure\s*(;|$)/i
|
63
|
+
end
|
64
|
+
|
65
|
+
headers["Set-Cookie"] = cookies.join("\n")
|
125
66
|
end
|
126
67
|
end
|
127
68
|
end
|
data/lib/tynn/static.rb
CHANGED
@@ -3,26 +3,8 @@
|
|
3
3
|
require "rack/static"
|
4
4
|
|
5
5
|
class Tynn
|
6
|
-
# Serves static files (javascript files, images, stylesheets, etc).
|
7
|
-
#
|
8
|
-
# By default, these files are served from the <tt>./public</tt> folder.
|
9
|
-
# A different location can be specified through the <tt>:root</tt> option.
|
10
|
-
#
|
11
|
-
# Under the hood, it uses the Rack::Static middleware.
|
12
|
-
# Thus, supports all the options available by the middleware.
|
13
|
-
#
|
14
|
-
# require "tynn"
|
15
|
-
# require "tynn/static"
|
16
|
-
#
|
17
|
-
# Tynn.plugin(Tynn::Static, ["/js", "/css"])
|
18
|
-
# Tynn.plugin(Tynn::Static, ["/js", "/css"], root: "assets")
|
19
|
-
# Tynn.plugin(Tynn::Static, ["/js", "/css"], index: "index.html")
|
20
|
-
#
|
21
|
-
# For more information on the supported options, please see
|
22
|
-
# Rack::Static[http://www.rubydoc.info/gems/rack/Rack/Static].
|
23
|
-
#
|
24
6
|
module Static
|
25
|
-
def self.setup(app, urls, opts = {})
|
7
|
+
def self.setup(app, urls, opts = {})
|
26
8
|
options = opts.dup
|
27
9
|
|
28
10
|
options[:urls] ||= urls
|
data/lib/tynn/test.rb
CHANGED
@@ -1,160 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Tynn
|
4
|
-
# A simple helper class to simulate requests to the application.
|
5
|
-
#
|
6
|
-
# require "tynn"
|
7
|
-
# require "tynn/test"
|
8
|
-
#
|
9
|
-
# Tynn.define do
|
10
|
-
# on get do
|
11
|
-
# res.write("Hei!")
|
12
|
-
# end
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# ts = Tynn::Test.new
|
16
|
-
# ts.get("/")
|
17
|
-
#
|
18
|
-
# ts.res.status # => 200
|
19
|
-
# ts.res.body.join # => "Hei!"
|
20
|
-
#
|
21
4
|
class Test
|
22
|
-
attr_reader :app
|
5
|
+
attr_reader :app
|
23
6
|
|
24
|
-
# Initializes a new Tynn::Test object.
|
25
|
-
#
|
26
|
-
# class API < Tynn
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# ts = Tynn::Test.new(API)
|
30
|
-
# ts.get("/user.json")
|
31
|
-
#
|
32
7
|
def initialize(app = Tynn)
|
33
8
|
@app = app
|
34
9
|
end
|
35
10
|
|
36
|
-
# This module provides the Tynn::Test API methods. If the stand-alone
|
37
|
-
# version is not preferred, this module can be integrated into the
|
38
|
-
# testing environment. The following example uses Minitest:
|
39
|
-
#
|
40
|
-
# class HomeTest < Minitest::Test
|
41
|
-
# include Tynn::Test::Methods
|
42
|
-
#
|
43
|
-
# def app
|
44
|
-
# return Tynn
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# def test_home
|
48
|
-
# get("/")
|
49
|
-
#
|
50
|
-
# assert_equal 200, res.status
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
#
|
54
11
|
module Methods
|
55
|
-
# If a request has been issued, returns an instance of Tynn::Request.
|
56
|
-
# Otherwise, returns <tt>nil</tt>.
|
57
|
-
#
|
58
|
-
# ts = Tynn::Test.new
|
59
|
-
# ts.get("/", { foo: "foo" }, { "HTTP_USER_AGENT" => "Tynn::Test" })
|
60
|
-
#
|
61
|
-
# ts.req.get?
|
62
|
-
# # => true
|
63
|
-
#
|
64
|
-
# ts.req.params["foo"]
|
65
|
-
# # => "foo"
|
66
|
-
#
|
67
|
-
# ts.req.env["HTTP_USER_AGENT"]
|
68
|
-
# # => "Tynn::Test"
|
69
|
-
#
|
70
12
|
def req
|
71
13
|
@__req
|
72
14
|
end
|
73
15
|
|
74
|
-
# If a request has been issued, returns an instance of Tynn::Response
|
75
|
-
# Otherwise, returns <tt>nil</tt>.
|
76
|
-
#
|
77
|
-
# ts = Tynn::Test.new
|
78
|
-
# ts.get("/", name: "Jane")
|
79
|
-
#
|
80
|
-
# ts.res.status
|
81
|
-
# # => 200
|
82
|
-
#
|
83
|
-
# ts.res.body.join
|
84
|
-
# # => "Hello Jane!"
|
85
|
-
#
|
86
|
-
# ts.res.headers["Content-Type"]
|
87
|
-
# # => "text/html"
|
88
|
-
#
|
89
16
|
def res
|
90
17
|
@__res
|
91
18
|
end
|
92
19
|
|
93
|
-
# Issues a <tt>GET</tt> request.
|
94
|
-
#
|
95
|
-
# [path] A request path.
|
96
|
-
# [params] A Hash of query/post parameters, a String request body,
|
97
|
-
# or <tt>nil</tt>.
|
98
|
-
# [env] A Hash of Rack environment values.
|
99
|
-
#
|
100
|
-
# ts = Tynn::Test.new
|
101
|
-
# ts.get("/search", name: "jane")
|
102
|
-
# ts.get("/cart", {}, { "HTTPS" => "on" })
|
103
|
-
#
|
104
20
|
def get(path, params = {}, env = {})
|
105
21
|
request(path, env.merge(method: "GET", params: params))
|
106
22
|
end
|
107
23
|
|
108
|
-
# Issues a <tt>POST</tt> request. See #get for more information.
|
109
|
-
#
|
110
|
-
# ts = Tynn::Test.new
|
111
|
-
# ts.post("/signup", username: "janedoe", password: "secret")
|
112
|
-
#
|
113
24
|
def post(path, params = {}, env = {})
|
114
25
|
request(path, env.merge(method: "POST", params: params))
|
115
26
|
end
|
116
27
|
|
117
|
-
# Issues a <tt>PUT</tt> request. See #get for more information.
|
118
|
-
#
|
119
|
-
# ts = Tynn::Test.new
|
120
|
-
# ts.put("/users/1", username: "johndoe", name: "John")
|
121
|
-
#
|
122
28
|
def put(path, params = {}, env = {})
|
123
29
|
request(path, env.merge(method: "PUT", params: params))
|
124
30
|
end
|
125
31
|
|
126
|
-
# Issues a <tt>PATCH</tt> request. See #get for more information.
|
127
|
-
#
|
128
|
-
# ts = Tynn::Test.new
|
129
|
-
# ts.patch("/users/1", username: "janedoe")
|
130
|
-
#
|
131
32
|
def patch(path, params = {}, env = {})
|
132
33
|
request(path, env.merge(method: "PATCH", params: params))
|
133
34
|
end
|
134
35
|
|
135
|
-
# Issues a <tt>DELETE</tt> request. See #get for more information.
|
136
|
-
#
|
137
|
-
# ts = Tynn::Test.new
|
138
|
-
# ts.delete("/users/1")
|
139
|
-
#
|
140
36
|
def delete(path, params = {}, env = {})
|
141
37
|
request(path, env.merge(method: "DELETE", params: params))
|
142
38
|
end
|
143
39
|
|
144
|
-
# Issues a <tt>HEAD</tt> request. See #get for more information.
|
145
|
-
#
|
146
|
-
# ts = Tynn::Test.new
|
147
|
-
# ts.head("/users/1")
|
148
|
-
#
|
149
40
|
def head(path, params = {}, env = {})
|
150
41
|
request(path, env.merge(method: Rack::HEAD, params: params))
|
151
42
|
end
|
152
43
|
|
153
|
-
# Issues a <tt>OPTIONS</tt> request. See #get for more information.
|
154
|
-
#
|
155
|
-
# ts = Tynn::Test.new
|
156
|
-
# ts.options("/users")
|
157
|
-
#
|
158
44
|
def options(path, params = {}, env = {})
|
159
45
|
request(path, env.merge(method: "OPTIONS", params: params))
|
160
46
|
end
|
@@ -163,16 +49,7 @@ class Tynn
|
|
163
49
|
|
164
50
|
def request(path, opts = {})
|
165
51
|
@__req = Tynn::Request.new(Rack::MockRequest.env_for(path, opts))
|
166
|
-
@__res =
|
167
|
-
end
|
168
|
-
|
169
|
-
def make_response(status, headers, body)
|
170
|
-
res = Tynn::Response.new(headers)
|
171
|
-
res.status = status
|
172
|
-
|
173
|
-
body.each { |b| res.write(b) }
|
174
|
-
|
175
|
-
res
|
52
|
+
@__res = Tynn::Response.new(*app.call(@__req.env))
|
176
53
|
end
|
177
54
|
end
|
178
55
|
|
data/lib/tynn/utils.rb
CHANGED
@@ -1,17 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Tynn
|
2
|
-
module Utils
|
4
|
+
module Utils
|
3
5
|
module_function
|
4
6
|
|
5
|
-
def
|
6
|
-
|
7
|
+
def deep_dup(obj)
|
8
|
+
case obj
|
9
|
+
when Array then deep_dup_array(obj)
|
10
|
+
when Hash then deep_dup_hash(obj)
|
11
|
+
else obj
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def deep_dup_hash(hash)
|
16
|
+
hash.each_with_object(hash.dup) do |(k, v), h|
|
17
|
+
h[k] = deep_dup(v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def deep_dup_array(array)
|
22
|
+
array.map { |v| deep_dup(v) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def deep_freeze!(obj)
|
26
|
+
case obj
|
27
|
+
when Array then deep_freeze_array!(obj)
|
28
|
+
when Hash then deep_freeze_hash!(obj)
|
29
|
+
end
|
30
|
+
end
|
7
31
|
|
8
|
-
|
9
|
-
|
10
|
-
hash.default_proc = default_proc
|
32
|
+
def deep_freeze_array!(array)
|
33
|
+
array.freeze.each { |v| deep_freeze!(v) }
|
11
34
|
end
|
12
35
|
|
13
|
-
def
|
14
|
-
|
36
|
+
def deep_freeze_hash!(hash)
|
37
|
+
hash.freeze.each { |_, v| deep_freeze!(v) }
|
38
|
+
end
|
39
|
+
|
40
|
+
HTML_ESCAPE = {
|
41
|
+
"&" => "&",
|
42
|
+
">" => ">",
|
43
|
+
"<" => "<",
|
44
|
+
'"' => "'",
|
45
|
+
"'" => """
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
UNSAFE = /[&"'><]/
|
49
|
+
|
50
|
+
def h(str)
|
51
|
+
str.gsub(UNSAFE, HTML_ESCAPE)
|
52
|
+
end
|
53
|
+
|
54
|
+
ERROR_URL = "%s. For more information, see http://tynn.rtfd.io/en/latest/troubleshooting#%s"
|
55
|
+
|
56
|
+
def raise_error(message, error = RuntimeError)
|
57
|
+
raise error, sprintf(ERROR_URL, message, message.downcase.gsub(" ", "-"))
|
15
58
|
end
|
16
59
|
end
|
17
60
|
end
|
data/lib/tynn/version.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Tynn
|
4
|
+
module Versioning
|
5
|
+
module InstanceMethods
|
6
|
+
def version(default: nil, vendor: "api")
|
7
|
+
lambda do
|
8
|
+
version, format = nil
|
9
|
+
accepts = req.headers["Accept"]
|
10
|
+
|
11
|
+
if accepts && !accepts.empty?
|
12
|
+
accepts.split(",").each do |accept|
|
13
|
+
break if version
|
14
|
+
|
15
|
+
matcher = /application\/vnd\.#{ vendor }\+(.+);\s*version\s*=\s*(\d+)/
|
16
|
+
matcher.match(accept) do |match|
|
17
|
+
version, format = match[2], match[1]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
version ||= default
|
23
|
+
|
24
|
+
captures.push(version.to_i, format) if version
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/test/json_test.rb
CHANGED
@@ -8,24 +8,144 @@ class JSONTest < Minitest::Test
|
|
8
8
|
@app = new_app
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def test_read_hash
|
12
|
+
@app.plugin(Tynn::JSON)
|
13
|
+
|
14
|
+
@app.define do
|
15
|
+
on post do
|
16
|
+
json(json_params)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
json = JSON.generate(ok: true)
|
21
|
+
|
22
|
+
ts = Tynn::Test.new(@app)
|
23
|
+
ts.post("/", json)
|
24
|
+
|
25
|
+
assert_equal %({"ok":true}), ts.res.body.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_read_array
|
29
|
+
@app.plugin(Tynn::JSON)
|
30
|
+
|
31
|
+
@app.define do
|
32
|
+
on post do
|
33
|
+
json(json_params)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
json = JSON.generate(%w(foo bar baz))
|
38
|
+
|
39
|
+
ts = Tynn::Test.new(@app)
|
40
|
+
ts.post("/", json)
|
41
|
+
|
42
|
+
assert_equal %(["foo","bar","baz"]), ts.res.body.join
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_read_rewinds_body
|
46
|
+
@app.plugin(Tynn::JSON)
|
47
|
+
|
48
|
+
@app.define do
|
49
|
+
on post do
|
50
|
+
_ = json_params
|
51
|
+
|
52
|
+
res.write(req.body.read)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
json = JSON.generate(%w(foo bar baz))
|
57
|
+
|
58
|
+
ts = Tynn::Test.new(@app)
|
59
|
+
ts.post("/", json)
|
60
|
+
|
61
|
+
assert_equal json, ts.res.body.join
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_read_on_parse_error
|
65
|
+
@app.plugin(Tynn::JSON)
|
66
|
+
|
67
|
+
@app.define do
|
68
|
+
on post do
|
69
|
+
json_params
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
ts = Tynn::Test.new(@app)
|
74
|
+
ts.post("/", "")
|
75
|
+
|
76
|
+
assert_equal 400, ts.res.status
|
77
|
+
assert_empty ts.res.headers
|
78
|
+
assert_empty ts.res.body
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_read_on_parse_error_proc
|
82
|
+
@app.plugin(Tynn::JSON, on_parse_error: proc {
|
83
|
+
res.status = 400
|
84
|
+
|
85
|
+
json(ok: false)
|
86
|
+
|
87
|
+
halt(res.finish)
|
88
|
+
})
|
89
|
+
|
90
|
+
@app.define do
|
91
|
+
on post do
|
92
|
+
json_params
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
ts = Tynn::Test.new(@app)
|
97
|
+
ts.post("/", "")
|
98
|
+
|
99
|
+
assert_equal 400, ts.res.status
|
100
|
+
assert_equal "application/json", ts.res.content_type
|
101
|
+
assert_equal %({"ok":false}), ts.res.body.join
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_read_on_parse_error_method
|
105
|
+
@app.plugin(Tynn::JSON, on_parse_error: :custom_bad_request)
|
106
|
+
|
107
|
+
@app.class_eval do
|
108
|
+
def custom_bad_request
|
109
|
+
res.status = 400
|
110
|
+
|
111
|
+
json(ok: false)
|
112
|
+
|
113
|
+
halt(res.finish)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
@app.define do
|
118
|
+
on post do
|
119
|
+
json_params
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
ts = Tynn::Test.new(@app)
|
124
|
+
ts.post("/", "")
|
125
|
+
|
126
|
+
assert_equal 400, ts.res.status
|
127
|
+
assert_equal "application/json", ts.res.content_type
|
128
|
+
assert_equal %({"ok":false}), ts.res.body.join
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_write_hash
|
12
132
|
@app.plugin(Tynn::JSON)
|
13
133
|
|
14
134
|
@app.define do
|
15
135
|
on get do
|
16
|
-
json(
|
136
|
+
json(ok: true)
|
17
137
|
end
|
18
138
|
end
|
19
139
|
|
20
140
|
ts = Tynn::Test.new(@app)
|
21
141
|
ts.get("/")
|
22
142
|
|
23
|
-
|
143
|
+
hash = JSON.parse(ts.res.body.join)
|
24
144
|
|
25
|
-
|
145
|
+
assert hash["ok"]
|
26
146
|
end
|
27
147
|
|
28
|
-
def
|
148
|
+
def test_write_array
|
29
149
|
@app.plugin(Tynn::JSON)
|
30
150
|
|
31
151
|
@app.define do
|
@@ -40,7 +160,60 @@ class JSONTest < Minitest::Test
|
|
40
160
|
assert_equal %w(foo bar baz), JSON.parse(ts.res.body.join)
|
41
161
|
end
|
42
162
|
|
43
|
-
def
|
163
|
+
def test_write_object
|
164
|
+
@app.plugin(Tynn::JSON)
|
165
|
+
|
166
|
+
@app.define do
|
167
|
+
on get do
|
168
|
+
object = Object.new
|
169
|
+
|
170
|
+
def object.to_json(_)
|
171
|
+
JSON.generate(ok: true)
|
172
|
+
end
|
173
|
+
|
174
|
+
json(object)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
ts = Tynn::Test.new(@app)
|
179
|
+
ts.get("/")
|
180
|
+
|
181
|
+
hash = JSON.parse(ts.res.body.join)
|
182
|
+
|
183
|
+
assert hash["ok"]
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_write_custom_options
|
187
|
+
@app.plugin(Tynn::JSON, write_opts: JSON::PRETTY_STATE_PROTOTYPE)
|
188
|
+
|
189
|
+
@app.define do
|
190
|
+
on get do
|
191
|
+
json(ok: true)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
ts = Tynn::Test.new(@app)
|
196
|
+
ts.get("/")
|
197
|
+
|
198
|
+
assert_equal JSON.pretty_generate(ok: true), ts.res.body.join
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_write_options
|
202
|
+
@app.plugin(Tynn::JSON)
|
203
|
+
|
204
|
+
@app.define do
|
205
|
+
on get do
|
206
|
+
json({ ok: true }, JSON::PRETTY_STATE_PROTOTYPE)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
ts = Tynn::Test.new(@app)
|
211
|
+
ts.get("/")
|
212
|
+
|
213
|
+
assert_equal JSON.pretty_generate(ok: true), ts.res.body.join
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_write_content_type
|
44
217
|
@app.plugin(Tynn::JSON)
|
45
218
|
|
46
219
|
@app.define do
|
@@ -55,7 +228,7 @@ class JSONTest < Minitest::Test
|
|
55
228
|
assert_equal "application/json", ts.res.content_type
|
56
229
|
end
|
57
230
|
|
58
|
-
def
|
231
|
+
def test_write_custom_content_type
|
59
232
|
@app.plugin(Tynn::JSON, content_type: "application/js")
|
60
233
|
|
61
234
|
@app.define do
|
@@ -69,4 +242,21 @@ class JSONTest < Minitest::Test
|
|
69
242
|
|
70
243
|
assert_equal "application/js", ts.res.content_type
|
71
244
|
end
|
245
|
+
|
246
|
+
def test_write_alias
|
247
|
+
@app.plugin(Tynn::JSON)
|
248
|
+
|
249
|
+
@app.define do
|
250
|
+
on get do
|
251
|
+
json(ok: true)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
ts = Tynn::Test.new(@app)
|
256
|
+
ts.get("/")
|
257
|
+
|
258
|
+
hash = JSON.parse(ts.res.body.join)
|
259
|
+
|
260
|
+
assert hash["ok"]
|
261
|
+
end
|
72
262
|
end
|