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/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
|