goofy 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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/composition.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "composing on top of a PATH" do
|
4
|
+
Services = Goofy.new {
|
5
|
+
on "services/:id" do |id|
|
6
|
+
res.write "View #{id}"
|
7
|
+
end
|
8
|
+
}
|
9
|
+
|
10
|
+
Goofy.define do
|
11
|
+
on "provider" do
|
12
|
+
run Services
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/provider/services/101" }
|
17
|
+
|
18
|
+
_, _, resp = Goofy.call(env)
|
19
|
+
|
20
|
+
assert_response resp, ["View 101"]
|
21
|
+
end
|
22
|
+
|
23
|
+
test "redefining not_found" do
|
24
|
+
class Users < Goofy
|
25
|
+
def not_found
|
26
|
+
res.status = 404
|
27
|
+
res.write "Not found!"
|
28
|
+
end
|
29
|
+
|
30
|
+
define do
|
31
|
+
on root do
|
32
|
+
res.write "Users"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Goofy
|
38
|
+
def not_found
|
39
|
+
res.status = 404
|
40
|
+
res.write "Error 404"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Goofy.define do
|
45
|
+
on "users" do
|
46
|
+
run Users
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/users" }
|
51
|
+
|
52
|
+
_, _, resp = Goofy.call(env)
|
53
|
+
|
54
|
+
assert_response resp, ["Users"]
|
55
|
+
|
56
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/users/42" }
|
57
|
+
|
58
|
+
status, _, resp = Goofy.call(env)
|
59
|
+
|
60
|
+
assert_response resp, ["Not found!"]
|
61
|
+
assert_equal status, 404
|
62
|
+
|
63
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/guests" }
|
64
|
+
|
65
|
+
status, _, resp = Goofy.call(env)
|
66
|
+
|
67
|
+
assert_response resp, ["Error 404"]
|
68
|
+
assert_equal status, 404
|
69
|
+
end
|
data/test/controller.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
require "goofy/controller"
|
4
|
+
|
5
|
+
scope do
|
6
|
+
setup do
|
7
|
+
@@___stack_ = []
|
8
|
+
class HomeControlelr < Goofy::Controller
|
9
|
+
before_response -> { @message = "before_response_from_controller"; @@___stack_ << @message }
|
10
|
+
around_response -> { @around = "around_from_controller"; @@___stack_ << @around }
|
11
|
+
after_response -> { @after = "after_from_controller"; @@___stack_ << @after }
|
12
|
+
def response
|
13
|
+
res.write(@message)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
Goofy.define do
|
17
|
+
on "home" do
|
18
|
+
controller HomeControlelr
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
test "response" do
|
24
|
+
_, _, body = Goofy.call({ "PATH_INFO" => "/home", "SCRIPT_NAME" => "/" })
|
25
|
+
|
26
|
+
assert_response body, ["before_response_from_controller"]
|
27
|
+
assert_equal ["before_response_from_controller", "around_from_controller", "around_from_controller", "after_from_controller"], @@___stack_
|
28
|
+
end
|
29
|
+
end
|
data/test/cookie.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "set cookie" do
|
4
|
+
Goofy.define do
|
5
|
+
on default do
|
6
|
+
res.set_cookie("foo", "bar")
|
7
|
+
res.set_cookie("bar", "baz")
|
8
|
+
res.write "Hello"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/" }
|
13
|
+
|
14
|
+
_, headers, body = Goofy.call(env)
|
15
|
+
|
16
|
+
assert_equal "foo=bar\nbar=baz", headers["Set-Cookie"]
|
17
|
+
end
|
18
|
+
|
19
|
+
test "delete cookie" do
|
20
|
+
Goofy.define do
|
21
|
+
on default do
|
22
|
+
res.set_cookie("foo", "bar")
|
23
|
+
res.delete_cookie("foo")
|
24
|
+
res.write "Hello"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/" }
|
29
|
+
|
30
|
+
_, headers, body = Goofy.call(env)
|
31
|
+
|
32
|
+
assert_equal "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000",
|
33
|
+
headers["Set-Cookie"]
|
34
|
+
end
|
data/test/csrf.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
require "goofy/safe/csrf"
|
3
|
+
require "goofy/test"
|
4
|
+
|
5
|
+
def assert_no_raise
|
6
|
+
yield
|
7
|
+
success
|
8
|
+
end
|
9
|
+
|
10
|
+
class UnsafeRequest < RuntimeError; end
|
11
|
+
|
12
|
+
scope do
|
13
|
+
setup do
|
14
|
+
Goofy.reset!
|
15
|
+
|
16
|
+
Goofy.use(Rack::Session::Cookie, secret: "_this_must_be_secret")
|
17
|
+
Goofy.plugin(Goofy::Safe::CSRF)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "safe http methods" do
|
21
|
+
Goofy.define do
|
22
|
+
raise UnsafeRequest if csrf.unsafe?
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_no_raise do
|
26
|
+
get "/"
|
27
|
+
head "/"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "invalid csrf param" do
|
32
|
+
Goofy.define do
|
33
|
+
if csrf.unsafe?
|
34
|
+
csrf.reset!
|
35
|
+
end
|
36
|
+
|
37
|
+
res.write(csrf.token)
|
38
|
+
end
|
39
|
+
|
40
|
+
get "/"
|
41
|
+
|
42
|
+
old_token = last_response.body
|
43
|
+
|
44
|
+
post "/", "csrf_token" => "nonsense"
|
45
|
+
|
46
|
+
new_token = last_response.body
|
47
|
+
|
48
|
+
assert(old_token != new_token)
|
49
|
+
end
|
50
|
+
|
51
|
+
test "valid csrf param" do
|
52
|
+
Goofy.define do
|
53
|
+
raise unless csrf.safe?
|
54
|
+
|
55
|
+
on get do
|
56
|
+
res.write(csrf.token)
|
57
|
+
end
|
58
|
+
|
59
|
+
on post do
|
60
|
+
res.write("safe")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
get "/"
|
65
|
+
|
66
|
+
csrf_token = last_response.body
|
67
|
+
|
68
|
+
assert(!csrf_token.empty?)
|
69
|
+
|
70
|
+
assert_no_raise do
|
71
|
+
post "/", "csrf_token" => csrf_token
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
test "http header" do
|
76
|
+
csrf_token = SecureRandom.hex(32)
|
77
|
+
|
78
|
+
Goofy.define do
|
79
|
+
session[:csrf_token] = csrf_token
|
80
|
+
raise if csrf.unsafe?
|
81
|
+
end
|
82
|
+
|
83
|
+
assert_no_raise do
|
84
|
+
post "/", {}, { "HTTP_X_CSRF_TOKEN" => csrf_token }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
test "sub app raises too" do
|
89
|
+
class App < Goofy
|
90
|
+
define do
|
91
|
+
on post do
|
92
|
+
res.write("unsafe")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Goofy.define do
|
98
|
+
raise UnsafeRequest unless csrf.safe?
|
99
|
+
|
100
|
+
on "app" do
|
101
|
+
run(App)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
assert_raise(UnsafeRequest) do
|
106
|
+
post "/app"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
test "only sub app" do
|
111
|
+
class App < Goofy
|
112
|
+
define do
|
113
|
+
raise UnsafeRequest unless csrf.safe?
|
114
|
+
|
115
|
+
on post do
|
116
|
+
res.write("unsafe")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
Goofy.define do
|
122
|
+
on "app" do
|
123
|
+
run(App)
|
124
|
+
end
|
125
|
+
|
126
|
+
on default do
|
127
|
+
res.write("safe")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
assert_no_raise do
|
132
|
+
post "/"
|
133
|
+
end
|
134
|
+
|
135
|
+
assert_raise(UnsafeRequest) do
|
136
|
+
post "/app"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/test/extension.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Goofy.define do
|
5
|
+
on "styles" do
|
6
|
+
on extension("css") do |file|
|
7
|
+
res.write file
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
{ "SCRIPT_NAME" => "/", "PATH_INFO" => "/styles" }
|
13
|
+
end
|
14
|
+
|
15
|
+
test "/styles/reset.css" do |env|
|
16
|
+
env["PATH_INFO"] += "/reset.css"
|
17
|
+
|
18
|
+
_, _, resp = Goofy.call(env)
|
19
|
+
|
20
|
+
assert_response resp, ["reset"]
|
21
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$:.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
|
2
|
+
require "goofy"
|
3
|
+
|
4
|
+
prepare { Goofy.reset! }
|
5
|
+
|
6
|
+
def assert_response(body, expected)
|
7
|
+
arr = body.map { |line| line.strip }
|
8
|
+
|
9
|
+
flunk "#{arr.inspect} != #{expected.inspect}" unless arr == expected
|
10
|
+
print "."
|
11
|
+
end
|
data/test/host.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "matches a host" do
|
4
|
+
Goofy.define do
|
5
|
+
on host("example.com") do
|
6
|
+
res.write "worked"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
env = { "HTTP_HOST" => "example.com" }
|
11
|
+
|
12
|
+
_, _, resp = Goofy.call(env)
|
13
|
+
|
14
|
+
assert_response resp, ["worked"]
|
15
|
+
end
|
16
|
+
|
17
|
+
test "matches a host with a regexp" do
|
18
|
+
Goofy.define do
|
19
|
+
on host(/example/) do
|
20
|
+
res.write "worked"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
env = { "HTTP_HOST" => "example.com" }
|
25
|
+
|
26
|
+
_, _, resp = Goofy.call(env)
|
27
|
+
|
28
|
+
assert_response resp, ["worked"]
|
29
|
+
end
|
data/test/integration.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "resetting" do
|
4
|
+
old = Goofy.app
|
5
|
+
assert old.object_id == Goofy.app.object_id
|
6
|
+
|
7
|
+
Goofy.reset!
|
8
|
+
assert old.object_id != Goofy.app.object_id
|
9
|
+
end
|
10
|
+
|
11
|
+
class Middle
|
12
|
+
def initialize(app, first, second, &block)
|
13
|
+
@app, @first, @second, @block = app, first, second, block
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
env["m.first"] = @first
|
18
|
+
env["m.second"] = @second
|
19
|
+
env["m.block"] = @block.call
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
test "use passes in the arguments and block" do
|
26
|
+
Goofy.use Middle, "First", "Second" do
|
27
|
+
"this is the block"
|
28
|
+
end
|
29
|
+
|
30
|
+
Goofy.define do
|
31
|
+
on get do
|
32
|
+
on "hello" do
|
33
|
+
"Default"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/hello",
|
39
|
+
"SCRIPT_NAME" => "/" }
|
40
|
+
|
41
|
+
Goofy.call(env)
|
42
|
+
|
43
|
+
assert "First" == env["m.first"]
|
44
|
+
assert "Second" == env["m.second"]
|
45
|
+
assert "this is the block" == env["m.block"]
|
46
|
+
end
|
47
|
+
|
48
|
+
test "reset and use" do
|
49
|
+
Goofy.use Middle, "First", "Second" do
|
50
|
+
"this is the block"
|
51
|
+
end
|
52
|
+
|
53
|
+
Goofy.define do
|
54
|
+
on get do
|
55
|
+
on "hello" do
|
56
|
+
res.write "Default"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Goofy.reset!
|
62
|
+
|
63
|
+
Goofy.use Middle, "1", "2" do
|
64
|
+
"3"
|
65
|
+
end
|
66
|
+
|
67
|
+
Goofy.define do
|
68
|
+
on get do
|
69
|
+
on "hello" do
|
70
|
+
res.write "2nd Default"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/hello",
|
76
|
+
"SCRIPT_NAME" => "/" }
|
77
|
+
|
78
|
+
status, headers, resp = Goofy.call(env)
|
79
|
+
|
80
|
+
assert_equal 200, status
|
81
|
+
assert "text/html; charset=utf-8" == headers["Content-Type"]
|
82
|
+
assert_response resp, ["2nd Default"]
|
83
|
+
|
84
|
+
assert "1" == env["m.first"]
|
85
|
+
assert "2" == env["m.second"]
|
86
|
+
assert "3" == env["m.block"]
|
87
|
+
end
|
88
|
+
|
89
|
+
test "custom response" do
|
90
|
+
class MyResponse < Goofy::Response
|
91
|
+
def foobar
|
92
|
+
write "Default"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Goofy.settings[:res] = MyResponse
|
97
|
+
|
98
|
+
Goofy.define do
|
99
|
+
on get do
|
100
|
+
on "hello" do
|
101
|
+
res.foobar
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/hello",
|
107
|
+
"SCRIPT_NAME" => "/" }
|
108
|
+
|
109
|
+
status, headers, resp = Goofy.call(env)
|
110
|
+
|
111
|
+
assert 200 == status
|
112
|
+
assert "text/html; charset=utf-8" == headers["Content-Type"]
|
113
|
+
assert_response resp, ["Default"]
|
114
|
+
end
|