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