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,86 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+
3
+ setup do
4
+ { "SCRIPT_NAME" => "/", "PATH_INFO" => "/posts/123" }
5
+ end
6
+
7
+ test "text-book example" do |env|
8
+ Goofy.define do
9
+ on "posts/:id" do |id|
10
+ res.write id
11
+ end
12
+ end
13
+
14
+ _, _, resp = Goofy.call(env)
15
+
16
+ assert_response resp, ["123"]
17
+ end
18
+
19
+ test "multi-param" do |env|
20
+ Goofy.define do
21
+ on "u/:uid/posts/:id" do |uid, id|
22
+ res.write uid
23
+ res.write id
24
+ end
25
+ end
26
+
27
+ env["PATH_INFO"] = "/u/jdoe/posts/123"
28
+
29
+ _, _, resp = Goofy.call(env)
30
+
31
+ assert_response resp, ["jdoe", "123"]
32
+ end
33
+
34
+ test "regex nesting" do |env|
35
+ Goofy.define do
36
+ on(/u\/(\w+)/) do |uid|
37
+ res.write uid
38
+
39
+ on(/posts\/(\d+)/) do |id|
40
+ res.write id
41
+ end
42
+ end
43
+ end
44
+
45
+ env["PATH_INFO"] = "/u/jdoe/posts/123"
46
+
47
+ _, _, resp = Goofy.call(env)
48
+
49
+ assert_response resp, ["jdoe", "123"]
50
+ end
51
+
52
+ test "regex nesting colon param style" do |env|
53
+ Goofy.define do
54
+ on(/u:(\w+)/) do |uid|
55
+ res.write uid
56
+
57
+ on(/posts:(\d+)/) do |id|
58
+ res.write id
59
+ end
60
+ end
61
+ end
62
+
63
+ env["PATH_INFO"] = "/u:jdoe/posts:123"
64
+
65
+ _, _, resp = Goofy.call(env)
66
+
67
+ assert_response resp, ["jdoe", "123"]
68
+ end
69
+
70
+ test "symbol matching" do |env|
71
+ Goofy.define do
72
+ on "user", :id do |uid|
73
+ res.write uid
74
+
75
+ on "posts", :pid do |id|
76
+ res.write id
77
+ end
78
+ end
79
+ end
80
+
81
+ env["PATH_INFO"] = "/user/jdoe/posts/123"
82
+
83
+ _, _, resp = Goofy.call(env)
84
+
85
+ assert_response resp, ["jdoe", "123"]
86
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+
3
+ class Shrimp
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ status, headers, resp = @app.call(env)
10
+
11
+ arr = []
12
+ resp.each { |e| arr << e }
13
+
14
+ [status, headers, arr.reverse]
15
+ end
16
+ end
17
+
18
+ test do
19
+ class API < Goofy
20
+ use Shrimp
21
+
22
+ define do
23
+ on "v1/test" do
24
+ res.write "OK"
25
+ res.write "1"
26
+ res.write "2"
27
+ end
28
+ end
29
+ end
30
+
31
+ Goofy.define do
32
+ on "api" do
33
+ run API
34
+ end
35
+ end
36
+
37
+ _, _, body = Goofy.call({ "PATH_INFO" => "/api/v1/test", "SCRIPT_NAME" => "/" })
38
+
39
+ arr = []
40
+
41
+ body.each do |line|
42
+ arr << line
43
+ end
44
+
45
+ assert_equal ["2", "1", "OK"], arr
46
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+
3
+ setup do
4
+ { "SCRIPT_NAME" => "/", "PATH_INFO" => "/about/1/2" }
5
+ end
6
+
7
+ test "paths and numbers" do |env|
8
+ Goofy.define do
9
+ on "about" do
10
+ on :one, :two do |one, two|
11
+ res.write one
12
+ res.write two
13
+ end
14
+ end
15
+ end
16
+
17
+ _, _, resp = Goofy.call(env)
18
+
19
+ assert_response resp, ["1", "2"]
20
+ end
21
+
22
+ test "paths and decimals" do |env|
23
+ Goofy.define do
24
+ on "about" do
25
+ on(/(\d+)/) do |one|
26
+ res.write one
27
+ end
28
+ end
29
+ end
30
+
31
+ env["PATH_INFO"] = "/about/1.2"
32
+
33
+ _, _, resp = Goofy.call(env)
34
+
35
+ assert_response resp, []
36
+ end
@@ -0,0 +1,157 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+
3
+ test "executes on true" do
4
+ Goofy.define do
5
+ on true do
6
+ res.write "+1"
7
+ end
8
+ end
9
+
10
+ _, _, resp = Goofy.call({})
11
+
12
+ assert_response resp, ["+1"]
13
+ end
14
+
15
+ test "executes on non-false" do
16
+ Goofy.define do
17
+ on "123" do
18
+ res.write "+1"
19
+ end
20
+ end
21
+
22
+ _, _, resp = Goofy.call({ "PATH_INFO" => "/123", "SCRIPT_NAME" => "/" })
23
+
24
+ assert_response resp, ["+1"]
25
+ end
26
+
27
+ test "ensures SCRIPT_NAME and PATH_INFO are reverted" do
28
+ Goofy.define do
29
+ on lambda { env["SCRIPT_NAME"] = "/hello"; false } do
30
+ res.write "Unreachable"
31
+ end
32
+ end
33
+
34
+ env = { "SCRIPT_NAME" => "/", "PATH_INFO" => "/hello" }
35
+
36
+ _, _, resp = Goofy.call(env)
37
+
38
+ assert_equal "/", env["SCRIPT_NAME"]
39
+ assert_equal "/hello", env["PATH_INFO"]
40
+ assert_response resp, []
41
+ end
42
+
43
+ test "skips consecutive matches" do
44
+ Goofy.define do
45
+ on true do
46
+ env["foo"] = "foo"
47
+
48
+ res.write "foo"
49
+ end
50
+
51
+ on true do
52
+ env["bar"] = "bar"
53
+
54
+ res.write "bar"
55
+ end
56
+ end
57
+
58
+ env = {}
59
+
60
+ _, _, resp = Goofy.call(env)
61
+
62
+ assert_equal "foo", env["foo"]
63
+ assert_response resp, ["foo"]
64
+
65
+ assert ! env["bar"]
66
+ end
67
+
68
+ test "finds first match available" do
69
+ Goofy.define do
70
+ on false do
71
+ res.write "foo"
72
+ end
73
+
74
+ on true do
75
+ res.write "bar"
76
+ end
77
+ end
78
+
79
+ _, _, resp = Goofy.call({})
80
+
81
+ assert_response resp, ["bar"]
82
+ end
83
+
84
+ test "reverts a half-met matcher" do
85
+ Goofy.define do
86
+ on "post", false do
87
+ res.write "Should be unmet"
88
+ end
89
+ end
90
+
91
+ env = { "PATH_INFO" => "/post", "SCRIPT_NAME" => "/" }
92
+ _, _, resp = Goofy.call(env)
93
+
94
+ assert_response resp, []
95
+ assert_equal "/post", env["PATH_INFO"]
96
+ assert_equal "/", env["SCRIPT_NAME"]
97
+ end
98
+
99
+ test "responds 404 if conditions are not met" do
100
+ Goofy.define do
101
+ on root do
102
+ res.write("Should be unmet")
103
+ end
104
+ end
105
+
106
+ env = { "PATH_INFO" => "/notexists", "SCRIPT_NAME" => "/" }
107
+ status, _, body = Goofy.call(env)
108
+
109
+ assert_equal 404, status
110
+ assert body.empty?
111
+ end
112
+
113
+ test "responds 404 if nested conditions are not met" do
114
+ Goofy.define do
115
+ on get do
116
+ on root do
117
+ res.write("Should be unmet")
118
+ end
119
+ end
120
+
121
+ on default do
122
+ res.write("Should be unmet")
123
+ end
124
+ end
125
+
126
+ env = {
127
+ "REQUEST_METHOD" => "GET",
128
+ "PATH_INFO" => "/notexists",
129
+ "SCRIPT_NAME" => "/"
130
+ }
131
+
132
+ status, _, body = Goofy.call(env)
133
+
134
+ assert_equal 404, status
135
+ assert body.empty?
136
+ end
137
+
138
+ test "responds 200 even with an empty body if status is set" do
139
+ Goofy.define do
140
+ on get do
141
+ on root do
142
+ res.status = 200
143
+ end
144
+ end
145
+ end
146
+
147
+ env = {
148
+ "REQUEST_METHOD" => "GET",
149
+ "PATH_INFO" => "/",
150
+ "SCRIPT_NAME" => "/"
151
+ }
152
+
153
+ status, _, body = Goofy.call(env)
154
+
155
+ assert_equal 200, status
156
+ assert body.empty?
157
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+ require "stringio"
3
+
4
+ prepare do
5
+ Goofy.define do
6
+ on get, "signup", param("email") do |email|
7
+ res.write email
8
+ end
9
+
10
+ on get, "login", param("username", "guest") do |username|
11
+ res.write username
12
+ end
13
+
14
+ on default do
15
+ res.write "No email"
16
+ end
17
+ end
18
+ end
19
+
20
+ test "yields a param" do
21
+ env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/signup",
22
+ "SCRIPT_NAME" => "/", "rack.input" => StringIO.new,
23
+ "QUERY_STRING" => "email=john@doe.com" }
24
+
25
+ _, _, resp = Goofy.call(env)
26
+
27
+ assert_response resp, ["john@doe.com"]
28
+ end
29
+
30
+ test "doesn't yield a missing param" do
31
+ env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/signup",
32
+ "SCRIPT_NAME" => "/", "rack.input" => StringIO.new,
33
+ "QUERY_STRING" => "" }
34
+
35
+ _, _, resp = Goofy.call(env)
36
+
37
+ assert_response resp, ["No email"]
38
+ end
39
+
40
+ test "doesn't yield an empty param" do
41
+ env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/signup",
42
+ "SCRIPT_NAME" => "/", "rack.input" => StringIO.new,
43
+ "QUERY_STRING" => "email=" }
44
+
45
+ _, _, resp = Goofy.call(env)
46
+
47
+ assert_response resp, ["No email"]
48
+ end
49
+
50
+ test "yields a default param" do
51
+ env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/login",
52
+ "SCRIPT_NAME" => "/", "rack.input" => StringIO.new,
53
+ "QUERY_STRING" => "username=john" }
54
+
55
+ _, _, resp = Goofy.call(env)
56
+
57
+ assert_response resp, ["john"]
58
+
59
+ env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/login",
60
+ "SCRIPT_NAME" => "/", "rack.input" => StringIO.new,
61
+ "QUERY_STRING" => "" }
62
+
63
+ _, _, resp = Goofy.call(env)
64
+
65
+ assert_response resp, ["guest"]
66
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+
3
+ setup do
4
+ { "SCRIPT_NAME" => "/", "PATH_INFO" => "/about" }
5
+ end
6
+
7
+ test "one level path" do |env|
8
+ Goofy.define do
9
+ on "about" do
10
+ res.write "About"
11
+ end
12
+ end
13
+
14
+ _, _, resp = Goofy.call(env)
15
+
16
+ assert_response resp, ["About"]
17
+ end
18
+
19
+ test "two level nested paths" do |env|
20
+ Goofy.define do
21
+ on "about" do
22
+ on "1" do
23
+ res.write "+1"
24
+ end
25
+
26
+ on "2" do
27
+ res.write "+2"
28
+ end
29
+ end
30
+ end
31
+
32
+ env["PATH_INFO"] = "/about/1"
33
+
34
+ _, _, resp = Goofy.call(env)
35
+
36
+ assert_response resp, ["+1"]
37
+
38
+ env["PATH_INFO"] = "/about/2"
39
+
40
+ _, _, resp = Goofy.call(env)
41
+
42
+ assert_response resp, ["+2"]
43
+ end
44
+
45
+ test "two level inlined paths" do |env|
46
+ Goofy.define do
47
+ on "a/b" do
48
+ res.write "a"
49
+ res.write "b"
50
+ end
51
+ end
52
+
53
+ env["PATH_INFO"] = "/a/b"
54
+
55
+ _, _, resp = Goofy.call(env)
56
+
57
+ assert_response resp, ["a", "b"]
58
+ end
59
+
60
+ test "a path with some regex captures" do |env|
61
+ Goofy.define do
62
+ on "user(\\d+)" do |uid|
63
+ res.write uid
64
+ end
65
+ end
66
+
67
+ env["PATH_INFO"] = "/user123"
68
+
69
+ _, _, resp = Goofy.call(env)
70
+
71
+ assert_response resp, ["123"]
72
+ end
73
+
74
+ test "matching the root" do |env|
75
+ Goofy.define do
76
+ on "" do
77
+ res.write "Home"
78
+ end
79
+ end
80
+
81
+ env["PATH_INFO"] = "/"
82
+
83
+ _, _, resp = Goofy.call(env)
84
+
85
+ assert_response resp, ["Home"]
86
+ end