tynn 1.4.0 → 2.0.0.alpha
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 +540 -25
- data/lib/tynn.rb +50 -103
- data/lib/tynn/base.rb +97 -0
- data/lib/tynn/default_headers.rb +50 -0
- data/lib/tynn/environment.rb +54 -28
- data/lib/tynn/json.rb +7 -18
- data/lib/tynn/render.rb +16 -12
- data/lib/tynn/request.rb +54 -38
- data/lib/tynn/response.rb +33 -173
- data/lib/tynn/secure_headers.rb +28 -31
- data/lib/tynn/session.rb +68 -34
- data/lib/tynn/settings.rb +56 -27
- data/lib/tynn/ssl.rb +78 -70
- data/lib/tynn/static.rb +12 -21
- data/lib/tynn/test.rb +51 -78
- data/lib/tynn/version.rb +4 -7
- data/test/default_headers_test.rb +21 -9
- data/test/environment_test.rb +57 -16
- data/test/helper.rb +4 -6
- data/test/json_test.rb +48 -10
- data/test/middleware_test.rb +63 -54
- data/test/plugin_test.rb +121 -0
- data/test/render_test.rb +56 -65
- data/test/request_headers_test.rb +33 -0
- data/test/routing_test.rb +111 -0
- data/test/secure_headers_test.rb +29 -17
- data/test/session_test.rb +44 -11
- data/test/settings_test.rb +53 -0
- data/test/ssl_test.rb +107 -35
- data/test/static_test.rb +25 -6
- metadata +33 -38
- data/lib/tynn/all_methods.rb +0 -50
- data/lib/tynn/hmote.rb +0 -34
- data/lib/tynn/not_found.rb +0 -20
- data/lib/tynn/protection.rb +0 -45
- data/test/all_methods_test.rb +0 -16
- data/test/core_test.rb +0 -65
- data/test/hmote_test.rb +0 -78
- data/test/not_found_test.rb +0 -19
- data/test/protection_test.rb +0 -36
data/lib/tynn/all_methods.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
class Tynn
|
2
|
-
# Public: Adds method for HTTP's +HEAD+ and +OPTIONS+.
|
3
|
-
#
|
4
|
-
# Examples
|
5
|
-
#
|
6
|
-
# require "tynn"
|
7
|
-
# require "tynn/all_methods"
|
8
|
-
#
|
9
|
-
# Tynn.plugin(Tynn::AllMethods)
|
10
|
-
#
|
11
|
-
module AllMethods
|
12
|
-
module InstanceMethods
|
13
|
-
# Public: Executes the given block if the request method is +HEAD+.
|
14
|
-
#
|
15
|
-
# Examples
|
16
|
-
#
|
17
|
-
# Tynn.define do
|
18
|
-
# head do
|
19
|
-
# res.status = 201
|
20
|
-
# end
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
def head
|
24
|
-
if root? && req.head?
|
25
|
-
yield
|
26
|
-
|
27
|
-
halt(res.finish)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Public: Executes the given block if the request method is +OPTIONS+.
|
32
|
-
#
|
33
|
-
# Examples
|
34
|
-
#
|
35
|
-
# Tynn.define do
|
36
|
-
# options do
|
37
|
-
# res.status = 405
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
def options
|
42
|
-
if root? && req.options?
|
43
|
-
yield
|
44
|
-
|
45
|
-
halt(res.finish)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
data/lib/tynn/hmote.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require "hmote"
|
2
|
-
|
3
|
-
class Tynn
|
4
|
-
module HMote
|
5
|
-
def self.setup(app, options = {}) # :nodoc:
|
6
|
-
app.set(:layout, options.fetch(:layout, "layout"))
|
7
|
-
app.set(:views, options.fetch(:views, File.expand_path("views", Dir.pwd)))
|
8
|
-
end
|
9
|
-
|
10
|
-
module InstanceMethods
|
11
|
-
include ::HMote::Helpers
|
12
|
-
|
13
|
-
def render(template, locals = {}, layout = self.class.settings[:layout])
|
14
|
-
res.headers[Rack::CONTENT_TYPE] ||= Syro::Response::DEFAULT
|
15
|
-
|
16
|
-
res.write(view(template, locals, layout))
|
17
|
-
end
|
18
|
-
|
19
|
-
def view(template, locals = {}, layout = self.class.settings[:layout])
|
20
|
-
return partial(layout, locals.merge(content: partial(template, locals)))
|
21
|
-
end
|
22
|
-
|
23
|
-
def partial(template, locals = {})
|
24
|
-
return hmote(template_path(template), locals.merge(app: self), TOPLEVEL_BINDING)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def template_path(template)
|
30
|
-
return File.join(self.class.settings[:views], "#{ template }.mote")
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/tynn/not_found.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
class Tynn
|
2
|
-
module NotFound
|
3
|
-
module InstanceMethods
|
4
|
-
def call(*) # :nodoc:
|
5
|
-
result = super
|
6
|
-
|
7
|
-
if result[0] == 404 && result[2].empty?
|
8
|
-
not_found
|
9
|
-
|
10
|
-
return res.finish
|
11
|
-
else
|
12
|
-
return result
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def not_found # :nodoc:
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/tynn/protection.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
require_relative "secure_headers"
|
2
|
-
|
3
|
-
class Tynn
|
4
|
-
# Public: Adds security measures against common attacks.
|
5
|
-
#
|
6
|
-
# Examples
|
7
|
-
#
|
8
|
-
# require "tynn"
|
9
|
-
# require "tynn/protection"
|
10
|
-
#
|
11
|
-
# Tynn.plugin(Tynn::Protection)
|
12
|
-
#
|
13
|
-
# If you are using SSL/TLS (HTTPS), it's recommended to set
|
14
|
-
# the +:ssl+ option:
|
15
|
-
#
|
16
|
-
# Examples
|
17
|
-
#
|
18
|
-
# require "tynn"
|
19
|
-
# require "tynn/protection"
|
20
|
-
#
|
21
|
-
# Tynn.plugin(Tynn::Protection, ssl: true)
|
22
|
-
#
|
23
|
-
# By default, it includes the following security plugins:
|
24
|
-
#
|
25
|
-
# - Tynn::SecureHeaders
|
26
|
-
#
|
27
|
-
# If the +:ssl+ option is +true+, includes:
|
28
|
-
#
|
29
|
-
# - Tynn::SSL
|
30
|
-
#
|
31
|
-
module Protection
|
32
|
-
# Internal: Configures security related plugins.
|
33
|
-
def self.setup(app, ssl: false, hsts: {})
|
34
|
-
app.plugin(Tynn::SecureHeaders)
|
35
|
-
|
36
|
-
if ssl
|
37
|
-
app.settings[:ssl] = true
|
38
|
-
|
39
|
-
require_relative "ssl"
|
40
|
-
|
41
|
-
app.plugin(Tynn::SSL, hsts: hsts)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
data/test/all_methods_test.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/all_methods"
|
2
|
-
|
3
|
-
Tynn.plugin(Tynn::AllMethods)
|
4
|
-
|
5
|
-
test "methods" do
|
6
|
-
[:head, :options].each do |method|
|
7
|
-
Tynn.define do
|
8
|
-
send(method) { res.write "" }
|
9
|
-
end
|
10
|
-
|
11
|
-
app = Tynn::Test.new
|
12
|
-
app.send(method, "/")
|
13
|
-
|
14
|
-
assert_equal 200, app.res.status
|
15
|
-
end
|
16
|
-
end
|
data/test/core_test.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
test "hello" do
|
2
|
-
Tynn.define do
|
3
|
-
get do
|
4
|
-
res.write("hello")
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
app = Tynn::Test.new
|
9
|
-
app.get("/")
|
10
|
-
|
11
|
-
assert_equal 200, app.res.status
|
12
|
-
assert_equal "hello", app.res.body
|
13
|
-
end
|
14
|
-
|
15
|
-
test "methods" do
|
16
|
-
[:get, :post, :put, :patch, :delete].each do |method|
|
17
|
-
Tynn.define do
|
18
|
-
send(method) { res.write "" }
|
19
|
-
end
|
20
|
-
|
21
|
-
app = Tynn::Test.new
|
22
|
-
app.send(method, "/")
|
23
|
-
|
24
|
-
assert_equal 200, app.res.status
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
test "captures" do
|
29
|
-
Tynn.define do
|
30
|
-
on :foo do |foo|
|
31
|
-
on :bar do |bar|
|
32
|
-
res.write(sprintf("%s:%s", foo, bar))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
app = Tynn::Test.new
|
38
|
-
app.get("/foo/bar")
|
39
|
-
|
40
|
-
assert_equal 200, app.res.status
|
41
|
-
assert_equal "foo:bar", app.res.body
|
42
|
-
end
|
43
|
-
|
44
|
-
test "composition" do
|
45
|
-
class Foo < Tynn
|
46
|
-
end
|
47
|
-
|
48
|
-
Foo.define do
|
49
|
-
get do
|
50
|
-
res.write(inbox[:foo])
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
Tynn.define do
|
55
|
-
on "foo" do
|
56
|
-
run(Foo, foo: 42)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
app = Tynn::Test.new
|
61
|
-
app.get("/foo")
|
62
|
-
|
63
|
-
assert_equal 200, app.res.status
|
64
|
-
assert_equal "42", app.res.body
|
65
|
-
end
|
data/test/hmote_test.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/hmote"
|
2
|
-
|
3
|
-
setup do
|
4
|
-
Tynn.plugin(Tynn::HMote, views: File.expand_path("./test/views"))
|
5
|
-
|
6
|
-
Tynn::Test.new
|
7
|
-
end
|
8
|
-
|
9
|
-
test "partial" do |app|
|
10
|
-
Tynn.define do
|
11
|
-
on "partial" do
|
12
|
-
res.write(partial("partial", name: "mote"))
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
app.get("/partial")
|
17
|
-
|
18
|
-
assert_equal "mote", app.res.body.strip
|
19
|
-
end
|
20
|
-
|
21
|
-
test "view" do |app|
|
22
|
-
Tynn.define do
|
23
|
-
on "view" do
|
24
|
-
res.write(view("view", title: "tynn", name: "mote"))
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
app.get("/view")
|
29
|
-
|
30
|
-
assert_equal "tynn / mote", app.res.body.strip
|
31
|
-
end
|
32
|
-
|
33
|
-
test "render" do |app|
|
34
|
-
Tynn.define do
|
35
|
-
on "render" do
|
36
|
-
render("view", title: "tynn", name: "mote")
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
app.get("/render")
|
41
|
-
|
42
|
-
assert_equal 200, app.res.status
|
43
|
-
assert_equal "text/html", app.res.headers["Content-Type"]
|
44
|
-
assert_equal "tynn / mote", app.res.body.strip
|
45
|
-
end
|
46
|
-
|
47
|
-
test "404" do |app|
|
48
|
-
Tynn.define do
|
49
|
-
on "404" do
|
50
|
-
res.status = 404
|
51
|
-
|
52
|
-
render("view", title: "tynn", name: "mote")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
app.get("/404")
|
57
|
-
|
58
|
-
assert_equal 404, app.res.status
|
59
|
-
assert_equal "text/html", app.res.headers["Content-Type"]
|
60
|
-
assert_equal "tynn / mote", app.res.body.strip
|
61
|
-
end
|
62
|
-
|
63
|
-
test "custom layout" do
|
64
|
-
class App < Tynn
|
65
|
-
set(:layout, "custom_layout")
|
66
|
-
end
|
67
|
-
|
68
|
-
App.define do
|
69
|
-
root do
|
70
|
-
render("view", title: "tynn", name: "mote")
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
app = Tynn::Test.new(App)
|
75
|
-
app.get("/")
|
76
|
-
|
77
|
-
assert_equal "custom / tynn / mote", app.res.body.strip
|
78
|
-
end
|
data/test/not_found_test.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/not_found"
|
2
|
-
|
3
|
-
test "not found" do
|
4
|
-
Tynn.plugin(Tynn::NotFound)
|
5
|
-
|
6
|
-
class Tynn
|
7
|
-
def not_found
|
8
|
-
res.write("not found")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
Tynn.define do
|
13
|
-
end
|
14
|
-
|
15
|
-
app = Tynn::Test.new
|
16
|
-
app.get("/notfound")
|
17
|
-
|
18
|
-
assert_equal "not found", app.res.body
|
19
|
-
end
|
data/test/protection_test.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/protection"
|
2
|
-
require_relative "../lib/tynn/session"
|
3
|
-
|
4
|
-
test "supports hsts options" do
|
5
|
-
hsts = { expires: 100, subdomains: false, preload: true }
|
6
|
-
|
7
|
-
Tynn.plugin(Tynn::Protection, ssl: true, hsts: hsts)
|
8
|
-
|
9
|
-
Tynn.define do
|
10
|
-
end
|
11
|
-
|
12
|
-
app = Tynn::Test.new
|
13
|
-
app.get("/", {}, "HTTPS" => "on")
|
14
|
-
|
15
|
-
hsts = app.res.headers["Strict-Transport-Security"]
|
16
|
-
|
17
|
-
assert_equal "max-age=100; preload", hsts
|
18
|
-
end
|
19
|
-
|
20
|
-
test "adds secure flag to session cookie" do
|
21
|
-
Tynn.plugin(Tynn::Protection, ssl: true)
|
22
|
-
Tynn.plugin(Tynn::Session, secret: "_this_must_be_random_")
|
23
|
-
|
24
|
-
Tynn.define do
|
25
|
-
root do
|
26
|
-
session[:foo] = "foo"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
app = Tynn::Test.new
|
31
|
-
app.get("/", {}, "HTTPS" => "on")
|
32
|
-
|
33
|
-
session, _ = app.res.headers["Set-Cookie"].split("\n")
|
34
|
-
|
35
|
-
assert(/;\s*secure\s*(;|$)/i === session)
|
36
|
-
end
|