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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.gems +4 -0
  3. data/.gitignore +2 -0
  4. data/CHANGELOG +47 -0
  5. data/CONTRIBUTING +19 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +23 -0
  8. data/README.md +67 -0
  9. data/app/.rspec +2 -0
  10. data/app/Gemfile +13 -0
  11. data/app/app/controllers/application_controller.rb +3 -0
  12. data/app/app/services/.keep +0 -0
  13. data/app/config.ru +4 -0
  14. data/app/config/environment.rb +9 -0
  15. data/app/config/initializers/.keep +0 -0
  16. data/app/config/routes.rb +7 -0
  17. data/app/config/settings.rb +0 -0
  18. data/app/spec/helpers.rb +2 -0
  19. data/app/spec/helpers/goofy.rb +11 -0
  20. data/app/spec/spec_helper.rb +107 -0
  21. data/benchmark/measure.rb +35 -0
  22. data/bin/check_its_goofy.rb +15 -0
  23. data/bin/goofy +61 -0
  24. data/bin/goofy_generator.rb +357 -0
  25. data/bin/goofy_instance_creator.rb +40 -0
  26. data/examples/config.ru +18 -0
  27. data/examples/measure.rb +17 -0
  28. data/examples/rack-response.ru +21 -0
  29. data/examples/views/home.mote +7 -0
  30. data/examples/views/layout.mote +11 -0
  31. data/goofy.gemspec +26 -0
  32. data/lib/goofy.rb +405 -0
  33. data/lib/goofy/capybara.rb +13 -0
  34. data/lib/goofy/controller.rb +14 -0
  35. data/lib/goofy/controller/base.rb +21 -0
  36. data/lib/goofy/controller/callbacks.rb +19 -0
  37. data/lib/goofy/render.rb +63 -0
  38. data/lib/goofy/router.rb +9 -0
  39. data/lib/goofy/safe.rb +23 -0
  40. data/lib/goofy/safe/csrf.rb +47 -0
  41. data/lib/goofy/safe/secure_headers.rb +40 -0
  42. data/lib/goofy/test.rb +11 -0
  43. data/makefile +4 -0
  44. data/test/accept.rb +32 -0
  45. data/test/captures.rb +162 -0
  46. data/test/composition.rb +69 -0
  47. data/test/controller.rb +29 -0
  48. data/test/cookie.rb +34 -0
  49. data/test/csrf.rb +139 -0
  50. data/test/extension.rb +21 -0
  51. data/test/helper.rb +11 -0
  52. data/test/host.rb +29 -0
  53. data/test/integration.rb +114 -0
  54. data/test/match.rb +86 -0
  55. data/test/middleware.rb +46 -0
  56. data/test/number.rb +36 -0
  57. data/test/on.rb +157 -0
  58. data/test/param.rb +66 -0
  59. data/test/path.rb +86 -0
  60. data/test/plugin.rb +68 -0
  61. data/test/rack.rb +22 -0
  62. data/test/redirect.rb +21 -0
  63. data/test/render.rb +128 -0
  64. data/test/root.rb +83 -0
  65. data/test/run.rb +23 -0
  66. data/test/safe.rb +74 -0
  67. data/test/segment.rb +45 -0
  68. data/test/session.rb +21 -0
  69. data/test/settings.rb +52 -0
  70. data/test/views/about.erb +1 -0
  71. data/test/views/about.str +1 -0
  72. data/test/views/content-yield.erb +1 -0
  73. data/test/views/custom/abs_path.mote +1 -0
  74. data/test/views/frag.mote +1 -0
  75. data/test/views/home.erb +2 -0
  76. data/test/views/home.mote +1 -0
  77. data/test/views/home.str +2 -0
  78. data/test/views/layout-alternative.erb +2 -0
  79. data/test/views/layout-yield.erb +3 -0
  80. data/test/views/layout.erb +2 -0
  81. data/test/views/layout.mote +2 -0
  82. data/test/views/layout.str +2 -0
  83. data/test/views/test.erb +1 -0
  84. data/test/with.rb +42 -0
  85. metadata +271 -0
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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