goofy 1.0.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 +7 -0
- data/.gems +4 -0
- data/.gitignore +2 -0
- data/CHANGELOG +47 -0
- data/CONTRIBUTING +19 -0
- data/Gemfile +4 -0
- data/LICENSE +23 -0
- data/README.md +67 -0
- data/app/.rspec +2 -0
- data/app/Gemfile +13 -0
- data/app/app/controllers/application_controller.rb +3 -0
- data/app/app/services/.keep +0 -0
- data/app/config.ru +4 -0
- data/app/config/environment.rb +9 -0
- data/app/config/initializers/.keep +0 -0
- data/app/config/routes.rb +7 -0
- data/app/config/settings.rb +0 -0
- data/app/spec/helpers.rb +2 -0
- data/app/spec/helpers/goofy.rb +11 -0
- data/app/spec/spec_helper.rb +107 -0
- data/benchmark/measure.rb +35 -0
- data/bin/check_its_goofy.rb +15 -0
- data/bin/goofy +61 -0
- data/bin/goofy_generator.rb +357 -0
- data/bin/goofy_instance_creator.rb +40 -0
- data/examples/config.ru +18 -0
- data/examples/measure.rb +17 -0
- data/examples/rack-response.ru +21 -0
- data/examples/views/home.mote +7 -0
- data/examples/views/layout.mote +11 -0
- data/goofy.gemspec +26 -0
- data/lib/goofy.rb +405 -0
- data/lib/goofy/capybara.rb +13 -0
- data/lib/goofy/controller.rb +14 -0
- data/lib/goofy/controller/base.rb +21 -0
- data/lib/goofy/controller/callbacks.rb +19 -0
- data/lib/goofy/render.rb +63 -0
- data/lib/goofy/router.rb +9 -0
- data/lib/goofy/safe.rb +23 -0
- data/lib/goofy/safe/csrf.rb +47 -0
- data/lib/goofy/safe/secure_headers.rb +40 -0
- data/lib/goofy/test.rb +11 -0
- data/makefile +4 -0
- data/test/accept.rb +32 -0
- data/test/captures.rb +162 -0
- data/test/composition.rb +69 -0
- data/test/controller.rb +29 -0
- data/test/cookie.rb +34 -0
- data/test/csrf.rb +139 -0
- data/test/extension.rb +21 -0
- data/test/helper.rb +11 -0
- data/test/host.rb +29 -0
- data/test/integration.rb +114 -0
- data/test/match.rb +86 -0
- data/test/middleware.rb +46 -0
- data/test/number.rb +36 -0
- data/test/on.rb +157 -0
- data/test/param.rb +66 -0
- data/test/path.rb +86 -0
- data/test/plugin.rb +68 -0
- data/test/rack.rb +22 -0
- data/test/redirect.rb +21 -0
- data/test/render.rb +128 -0
- data/test/root.rb +83 -0
- data/test/run.rb +23 -0
- data/test/safe.rb +74 -0
- data/test/segment.rb +45 -0
- data/test/session.rb +21 -0
- data/test/settings.rb +52 -0
- data/test/views/about.erb +1 -0
- data/test/views/about.str +1 -0
- data/test/views/content-yield.erb +1 -0
- data/test/views/custom/abs_path.mote +1 -0
- data/test/views/frag.mote +1 -0
- data/test/views/home.erb +2 -0
- data/test/views/home.mote +1 -0
- data/test/views/home.str +2 -0
- data/test/views/layout-alternative.erb +2 -0
- data/test/views/layout-yield.erb +3 -0
- data/test/views/layout.erb +2 -0
- data/test/views/layout.mote +2 -0
- data/test/views/layout.str +2 -0
- data/test/views/test.erb +1 -0
- data/test/with.rb +42 -0
- metadata +271 -0
data/test/plugin.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
scope do
|
4
|
+
module Helper
|
5
|
+
def clean(str)
|
6
|
+
str.strip
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
test do
|
11
|
+
Goofy.plugin Helper
|
12
|
+
|
13
|
+
Goofy.define do
|
14
|
+
on default do
|
15
|
+
res.write clean " foo "
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
_, _, body = Goofy.call({})
|
20
|
+
|
21
|
+
assert_response body, ["foo"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
scope do
|
26
|
+
module Number
|
27
|
+
def num
|
28
|
+
1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module Plugin
|
33
|
+
def self.setup(app)
|
34
|
+
app.plugin Number
|
35
|
+
end
|
36
|
+
|
37
|
+
def bar
|
38
|
+
"baz"
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
def foo
|
43
|
+
"bar"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
setup do
|
49
|
+
Goofy.plugin Plugin
|
50
|
+
|
51
|
+
Goofy.define do
|
52
|
+
on default do
|
53
|
+
res.write bar
|
54
|
+
res.write num
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
test do
|
60
|
+
assert_equal "bar", Goofy.foo
|
61
|
+
end
|
62
|
+
|
63
|
+
test do
|
64
|
+
_, _, body = Goofy.call({})
|
65
|
+
|
66
|
+
assert_response body, ["baz", "1"]
|
67
|
+
end
|
68
|
+
end
|
data/test/rack.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
require "goofy/test"
|
3
|
+
|
4
|
+
scope do
|
5
|
+
test do
|
6
|
+
Goofy.define do
|
7
|
+
on root do
|
8
|
+
res.write "home"
|
9
|
+
end
|
10
|
+
|
11
|
+
on "about" do
|
12
|
+
res.write "about"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
get "/"
|
17
|
+
assert_equal "home", last_response.body
|
18
|
+
|
19
|
+
get "/about"
|
20
|
+
assert_equal "about", last_response.body
|
21
|
+
end
|
22
|
+
end
|
data/test/redirect.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "redirect" do
|
4
|
+
Goofy.define do
|
5
|
+
on "hello" do
|
6
|
+
res.write "hello, world"
|
7
|
+
end
|
8
|
+
|
9
|
+
on "" do
|
10
|
+
res.redirect "/hello"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/" }
|
15
|
+
|
16
|
+
status, headers, body = Goofy.call(env)
|
17
|
+
|
18
|
+
assert_equal status, 302
|
19
|
+
assert_equal headers, { "Location" => "/hello" }
|
20
|
+
assert_response body, []
|
21
|
+
end
|
data/test/render.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
require "goofy/render"
|
4
|
+
|
5
|
+
test "doesn't override the settings if they already exist" do
|
6
|
+
Goofy.settings[:render] = {
|
7
|
+
:views => "./test/views",
|
8
|
+
:template_engine => "haml"
|
9
|
+
}
|
10
|
+
|
11
|
+
Goofy.plugin Goofy::Render
|
12
|
+
|
13
|
+
assert_equal "./test/views", Goofy.settings[:render][:views]
|
14
|
+
assert_equal "haml", Goofy.settings[:render][:template_engine]
|
15
|
+
end
|
16
|
+
|
17
|
+
scope do
|
18
|
+
setup do
|
19
|
+
Goofy.plugin Goofy::Render
|
20
|
+
Goofy.settings[:render][:views] = "./test/views"
|
21
|
+
Goofy.settings[:render][:template_engine] = "erb"
|
22
|
+
|
23
|
+
Goofy.define do
|
24
|
+
on "home" do
|
25
|
+
res.write view("home", name: "Agent Smith", title: "Home")
|
26
|
+
end
|
27
|
+
|
28
|
+
on "about" do
|
29
|
+
res.write partial("about", title: "About Goofy")
|
30
|
+
end
|
31
|
+
|
32
|
+
on "render" do
|
33
|
+
render("about", title: "About Goofy")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
test "partial" do
|
39
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/about", "SCRIPT_NAME" => "/" })
|
40
|
+
|
41
|
+
assert_response body, ["<h1>About Goofy</h1>"]
|
42
|
+
end
|
43
|
+
|
44
|
+
test "view" do
|
45
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/home", "SCRIPT_NAME" => "/" })
|
46
|
+
|
47
|
+
assert_response body, ["<title>Goofy: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"]
|
48
|
+
end
|
49
|
+
|
50
|
+
test "render" do
|
51
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/render", "SCRIPT_NAME" => "/" })
|
52
|
+
|
53
|
+
assert_response body, ["<title>Goofy: About Goofy</title>\n<h1>About Goofy</h1>"]
|
54
|
+
end
|
55
|
+
|
56
|
+
test "partial with str as engine" do
|
57
|
+
Goofy.settings[:render][:template_engine] = "str"
|
58
|
+
|
59
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/about", "SCRIPT_NAME" => "/" })
|
60
|
+
|
61
|
+
assert_response body, ["<h1>About Goofy</h1>"]
|
62
|
+
end
|
63
|
+
|
64
|
+
test "view with str as engine" do
|
65
|
+
Goofy.settings[:render][:template_engine] = "str"
|
66
|
+
|
67
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/home", "SCRIPT_NAME" => "/" })
|
68
|
+
|
69
|
+
assert_response body, ["<title>Goofy: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"]
|
70
|
+
end
|
71
|
+
|
72
|
+
test "custom default layout support" do
|
73
|
+
Goofy.settings[:render][:layout] = "layout-alternative"
|
74
|
+
|
75
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/home", "SCRIPT_NAME" => "/" })
|
76
|
+
|
77
|
+
assert_response body, ["<title>Alternative Layout: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
test "caching behavior" do
|
82
|
+
Thread.current[:_cache] = nil
|
83
|
+
|
84
|
+
Goofy.plugin Goofy::Render
|
85
|
+
Goofy.settings[:render][:views] = "./test/views"
|
86
|
+
|
87
|
+
Goofy.define do
|
88
|
+
on "foo/:i" do |i|
|
89
|
+
res.write partial("test", title: i)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
10.times do |i|
|
94
|
+
_, _, resp = Goofy.call({ "PATH_INFO" => "/foo/#{i}", "SCRIPT_NAME" => "" })
|
95
|
+
end
|
96
|
+
|
97
|
+
assert_equal 1, Thread.current[:_cache].instance_variable_get(:@cache).size
|
98
|
+
end
|
99
|
+
|
100
|
+
test "overrides layout" do
|
101
|
+
Goofy.plugin Goofy::Render
|
102
|
+
Goofy.settings[:render][:views] = "./test/views"
|
103
|
+
|
104
|
+
Goofy.define do
|
105
|
+
on true do
|
106
|
+
res.write view("home", { name: "Agent Smith", title: "Home" }, "layout-alternative")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
_, _, body = Goofy.call({})
|
111
|
+
|
112
|
+
assert_response body, ["<title>Alternative Layout: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"]
|
113
|
+
end
|
114
|
+
|
115
|
+
test "ensures content-type header is set" do
|
116
|
+
Goofy.plugin(Goofy::Render)
|
117
|
+
|
118
|
+
Goofy.define do
|
119
|
+
on default do
|
120
|
+
res.status = 403
|
121
|
+
render("about", title: "Hello Goofy")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
_, headers, _ = Goofy.call({})
|
126
|
+
|
127
|
+
assert_equal("text/html; charset=utf-8", headers["Content-Type"])
|
128
|
+
end
|
data/test/root.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "matching an empty segment" do
|
4
|
+
Goofy.define do
|
5
|
+
on "" do
|
6
|
+
res.write req.path
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
env = {
|
11
|
+
"SCRIPT_NAME" => "",
|
12
|
+
"PATH_INFO" => "/"
|
13
|
+
}
|
14
|
+
|
15
|
+
_, _, resp = Goofy.call(env)
|
16
|
+
|
17
|
+
assert_response resp, ["/"]
|
18
|
+
end
|
19
|
+
|
20
|
+
test "nested empty segments" do
|
21
|
+
Goofy.define do
|
22
|
+
on "" do
|
23
|
+
on "" do
|
24
|
+
on "1" do
|
25
|
+
res.write "IT WORKS!"
|
26
|
+
res.write req.path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
env = {
|
33
|
+
"SCRIPT_NAME" => "",
|
34
|
+
"PATH_INFO" => "///1"
|
35
|
+
}
|
36
|
+
|
37
|
+
_, _, resp = Goofy.call(env)
|
38
|
+
|
39
|
+
assert_response resp, ["IT WORKS!", "///1"]
|
40
|
+
end
|
41
|
+
|
42
|
+
test "/events/? scenario" do
|
43
|
+
class Events < Goofy
|
44
|
+
define do
|
45
|
+
on root do
|
46
|
+
res.write "Hooray"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Goofy.define do
|
52
|
+
on "events" do
|
53
|
+
run Events
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
env = {
|
58
|
+
"SCRIPT_NAME" => "",
|
59
|
+
"PATH_INFO" => "/events"
|
60
|
+
}
|
61
|
+
|
62
|
+
_, _, resp = Goofy.call(env)
|
63
|
+
|
64
|
+
assert_response resp, ["Hooray"]
|
65
|
+
|
66
|
+
env = {
|
67
|
+
"SCRIPT_NAME" => "",
|
68
|
+
"PATH_INFO" => "/events/"
|
69
|
+
}
|
70
|
+
|
71
|
+
_, _, resp = Goofy.call(env)
|
72
|
+
|
73
|
+
assert_response resp, ["Hooray"]
|
74
|
+
|
75
|
+
env = {
|
76
|
+
"SCRIPT_NAME" => "",
|
77
|
+
"PATH_INFO" => "/events/a"
|
78
|
+
}
|
79
|
+
|
80
|
+
_, _, resp = Goofy.call(env)
|
81
|
+
|
82
|
+
assert_response resp, []
|
83
|
+
end
|
data/test/run.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "redirect canonical example" do
|
4
|
+
Goofy.define do
|
5
|
+
def redirect(*args)
|
6
|
+
run Goofy.new { on(true) { res.redirect(*args) }}
|
7
|
+
end
|
8
|
+
|
9
|
+
on "account" do
|
10
|
+
redirect "/login", 307
|
11
|
+
|
12
|
+
res.write "Super secure content"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/account" }
|
17
|
+
|
18
|
+
status, headers, resp = Goofy.call(env)
|
19
|
+
|
20
|
+
assert_equal "/login", headers["Location"]
|
21
|
+
assert_equal 307, status
|
22
|
+
assert_response resp, []
|
23
|
+
end
|
data/test/safe.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
require "goofy/safe"
|
3
|
+
|
4
|
+
scope do
|
5
|
+
test "secure headers" do
|
6
|
+
Goofy.plugin(Goofy::Safe)
|
7
|
+
|
8
|
+
class Hello < Goofy
|
9
|
+
define do
|
10
|
+
on root do
|
11
|
+
res.write("hello")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Goofy.define do
|
17
|
+
on root do
|
18
|
+
res.write("home")
|
19
|
+
end
|
20
|
+
|
21
|
+
on "hello" do
|
22
|
+
run(Hello)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
secure_headers = Goofy::Safe::SecureHeaders::HEADERS
|
27
|
+
|
28
|
+
_, headers, _ = Goofy.call("PATH_INFO" => "/", "SCRIPT_NAME" => "/")
|
29
|
+
secure_headers.each do |header, value|
|
30
|
+
assert_equal(value, headers[header])
|
31
|
+
end
|
32
|
+
|
33
|
+
_, headers, _ = Goofy.call("PATH_INFO" => "/hello", "SCRIPT_NAME" => "/")
|
34
|
+
secure_headers.each do |header, value|
|
35
|
+
assert_equal(value, headers[header])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
test "secure headers only in sub app" do
|
40
|
+
Goofy.settings[:default_headers] = {}
|
41
|
+
|
42
|
+
class About < Goofy
|
43
|
+
plugin(Goofy::Safe)
|
44
|
+
|
45
|
+
define do
|
46
|
+
on root do
|
47
|
+
res.write("about")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Goofy.define do
|
53
|
+
on root do
|
54
|
+
res.write("home")
|
55
|
+
end
|
56
|
+
|
57
|
+
on "about" do
|
58
|
+
run(About)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
secure_headers = Goofy::Safe::SecureHeaders::HEADERS
|
63
|
+
|
64
|
+
_, headers, _ = Goofy.call("PATH_INFO" => "/", "SCRIPT_NAME" => "/")
|
65
|
+
secure_headers.each do |header, _|
|
66
|
+
assert(!headers.key?(header))
|
67
|
+
end
|
68
|
+
|
69
|
+
_, headers, _ = Goofy.call("PATH_INFO" => "/about", "SCRIPT_NAME" => "/")
|
70
|
+
secure_headers.each do |header, value|
|
71
|
+
assert_equal(value, headers[header])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/test/segment.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Goofy.define do
|
5
|
+
on "post" do
|
6
|
+
on :id do |id|
|
7
|
+
res.write id
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
{ "SCRIPT_NAME" => "/", "PATH_INFO" => "/post" }
|
13
|
+
end
|
14
|
+
|
15
|
+
test "matches numeric ids" do |env|
|
16
|
+
env["PATH_INFO"] += "/1"
|
17
|
+
|
18
|
+
_, _, resp = Goofy.call(env)
|
19
|
+
|
20
|
+
assert_response resp, ["1"]
|
21
|
+
end
|
22
|
+
|
23
|
+
test "matches decimal numbers" do |env|
|
24
|
+
env["PATH_INFO"] += "/1.1"
|
25
|
+
|
26
|
+
_, _, resp = Goofy.call(env)
|
27
|
+
|
28
|
+
assert_response resp, ["1.1"]
|
29
|
+
end
|
30
|
+
|
31
|
+
test "matches slugs" do |env|
|
32
|
+
env["PATH_INFO"] += "/my-blog-post-about-Goofy"
|
33
|
+
|
34
|
+
_, _, resp = Goofy.call(env)
|
35
|
+
|
36
|
+
assert_response resp, ["my-blog-post-about-Goofy"]
|
37
|
+
end
|
38
|
+
|
39
|
+
test "matches only the first segment available" do |env|
|
40
|
+
env["PATH_INFO"] += "/one/two/three"
|
41
|
+
|
42
|
+
_, _, resp = Goofy.call(env)
|
43
|
+
|
44
|
+
assert_response resp, ["one"]
|
45
|
+
end
|