goofy 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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