ramaze 2010.06.18 → 2011.01

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 (95) hide show
  1. data/.gitignore +1 -0
  2. data/MANIFEST +9 -16
  3. data/README.md +37 -30
  4. data/Rakefile +5 -1
  5. data/TODO.md +19 -0
  6. data/doc/AUTHORS +5 -1
  7. data/doc/CHANGELOG +3553 -3272
  8. data/doc/tutorial/todolist.html +1512 -1512
  9. data/examples/app/blog/app.rb +2 -0
  10. data/examples/app/todolist/controller/init.rb +1 -2
  11. data/examples/app/wiktacular/mkd/main/2007-07-20_19-21-12.mkd +1 -1
  12. data/examples/app/wiktacular/mkd/main/2007-07-20_19-23-10.mkd +1 -1
  13. data/examples/app/wiktacular/mkd/main/2007-07-20_19-45-07.mkd +1 -1
  14. data/examples/app/wiktacular/mkd/main/current.mkd +1 -1
  15. data/examples/app/wiktacular/mkd/testing/2007-07-20_16-43-46.mkd +1 -1
  16. data/examples/app/wiktacular/mkd/testing/2007-07-20_19-43-50.mkd +2 -2
  17. data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-08.mkd +16 -16
  18. data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-54.mkd +16 -16
  19. data/examples/app/wiktacular/mkd/testing/current.mkd +16 -16
  20. data/lib/proto/model/init.rb +1 -1
  21. data/lib/proto/public/js/jquery.js +2034 -1095
  22. data/lib/proto/start.rb +2 -0
  23. data/lib/proto/view/index.xhtml +3 -3
  24. data/lib/ramaze.rb +1 -2
  25. data/lib/ramaze/cache.rb +1 -0
  26. data/lib/ramaze/cache/sequel.rb +131 -37
  27. data/lib/ramaze/controller.rb +1 -0
  28. data/lib/ramaze/gestalt.rb +75 -46
  29. data/lib/ramaze/helper.rb +1 -0
  30. data/lib/ramaze/helper/auth.rb +38 -4
  31. data/lib/ramaze/helper/blue_form.rb +498 -78
  32. data/lib/ramaze/helper/cache.rb +2 -2
  33. data/lib/ramaze/helper/csrf.rb +225 -0
  34. data/lib/ramaze/helper/erector.rb +67 -9
  35. data/lib/ramaze/helper/flash.rb +4 -2
  36. data/lib/ramaze/helper/gestalt.rb +2 -0
  37. data/lib/ramaze/helper/gravatar.rb +1 -1
  38. data/lib/ramaze/helper/localize.rb +4 -0
  39. data/lib/ramaze/helper/send_file.rb +30 -0
  40. data/lib/ramaze/helper/thread.rb +5 -0
  41. data/lib/ramaze/helper/user.rb +4 -3
  42. data/lib/ramaze/helper/xhtml.rb +87 -8
  43. data/lib/ramaze/log.rb +13 -0
  44. data/lib/ramaze/log/analogger.rb +15 -5
  45. data/lib/ramaze/log/growl.rb +28 -13
  46. data/lib/ramaze/log/hub.rb +12 -4
  47. data/lib/ramaze/log/informer.rb +28 -11
  48. data/lib/ramaze/log/knotify.rb +7 -2
  49. data/lib/ramaze/log/logger.rb +12 -4
  50. data/lib/ramaze/log/logging.rb +40 -14
  51. data/lib/ramaze/log/rotatinginformer.rb +47 -23
  52. data/lib/ramaze/log/syslog.rb +37 -31
  53. data/lib/ramaze/log/xosd.rb +7 -4
  54. data/lib/ramaze/middleware_compiler.rb +2 -2
  55. data/lib/ramaze/snippets/fiber.rb +63 -63
  56. data/lib/ramaze/snippets/ramaze/lru_hash.rb +1 -1
  57. data/lib/ramaze/tool/bin.rb +1 -1
  58. data/lib/ramaze/version.rb +1 -1
  59. data/lib/ramaze/view.rb +4 -4
  60. data/lib/ramaze/view/erector.rb +88 -13
  61. data/ramaze.gemspec +65 -65
  62. data/spec/ramaze/bin/ramaze.rb +1 -1
  63. data/spec/ramaze/cache/localmemcache.rb +20 -12
  64. data/spec/ramaze/cache/sequel.rb +19 -19
  65. data/spec/ramaze/helper/blue_form.rb +549 -257
  66. data/spec/ramaze/helper/csrf.rb +109 -0
  67. data/spec/ramaze/helper/httpdigest.rb +31 -29
  68. data/spec/ramaze/helper/user.rb +1 -1
  69. data/spec/ramaze/helper/xhtml.rb +17 -0
  70. data/spec/ramaze/log/growl.rb +34 -0
  71. data/spec/ramaze/log/informer.rb +1 -0
  72. data/spec/ramaze/view/erector.rb +49 -71
  73. data/spec/ramaze/view/erector/external_view.erector +5 -0
  74. data/spec/ramaze/view/erector/index.erector +5 -0
  75. data/spec/ramaze/view/erector/layout.erector +13 -3
  76. data/spec/ramaze/view/erector/tables.erector +23 -0
  77. data/spec/ramaze/view/erector/view.erector +6 -0
  78. data/tasks/git.rake +2 -2
  79. metadata +133 -176
  80. data/examples/helpers/form_with_sequel.rb +0 -24
  81. data/examples/helpers/nitro_form.rb +0 -23
  82. data/lib/ramaze/helper/form.rb +0 -133
  83. data/lib/ramaze/helper/nitroform.rb +0 -14
  84. data/lib/ramaze/helper/pager.rb +0 -367
  85. data/lib/ramaze/helper/partial.rb +0 -100
  86. data/lib/ramaze/helper/sequel.rb +0 -55
  87. data/lib/ramaze/helper/sequel_form.rb +0 -284
  88. data/lib/vendor/etag.rb +0 -22
  89. data/spec/ramaze/helper/form.rb +0 -360
  90. data/spec/ramaze/helper/pager.rb +0 -96
  91. data/spec/ramaze/helper/sequel_form.rb +0 -94
  92. data/spec/ramaze/view/erector/external.erector +0 -1
  93. data/spec/ramaze/view/erector/invoke_helper_method.erector +0 -1
  94. data/spec/ramaze/view/erector/strict_xhtml.erector +0 -3
  95. data/spec/ramaze/view/erector/sum.erector +0 -1
@@ -0,0 +1,109 @@
1
+ require File.expand_path('../../../../spec/helper', __FILE__)
2
+ require 'ramaze/helper/csrf'
3
+
4
+ ##
5
+ # A quick note on this controller.
6
+ # I decided to user global variables ($foo) instead of sending
7
+ # certain data to the "browser", this makes it much easier to compare
8
+ # certain values.
9
+ #
10
+ # - Yorick Peterse
11
+ #
12
+ class SpecHelperCSRF < Ramaze::Controller
13
+
14
+ engine :none
15
+ helper :csrf
16
+
17
+ before_all do
18
+ csrf_protection :check_post, :protect_me do
19
+ respond("The specified CSRF token is incorrect.", 401)
20
+ end
21
+ end
22
+
23
+ # Generate a new csrf token
24
+ def index
25
+ generate_csrf_token
26
+ end
27
+
28
+ # Retrieve the current value of the CSRF token
29
+ def get
30
+ return get_csrf_token
31
+ end
32
+
33
+ # Check if the token isn't regenerated
34
+ def dont_regenerate
35
+ $token_sess = session[:_csrf][:token]
36
+ $token_method = get_csrf_token
37
+ end
38
+
39
+ # Check the TTL
40
+ def check_ttl
41
+ generate_csrf_token :ttl => 3
42
+ $old_token = get_csrf_token
43
+ sleep 4
44
+ $new_token = get_csrf_token
45
+ end
46
+
47
+ # Check if the before_all block works
48
+ def check_post
49
+ "POST allowed."
50
+ end
51
+
52
+ end
53
+
54
+ describe Ramaze::Helper::CSRF do
55
+ behaves_like :rack_test
56
+
57
+ # ------------------------------------------------
58
+ # General validation
59
+
60
+ it 'Generate a new CSRF token' do
61
+ got = get '/'
62
+
63
+ got.status.should.equal 200
64
+ got.body.should.equal ''
65
+ end
66
+
67
+ it 'Retrieve the current CSRF token' do
68
+ got = get '/get'
69
+
70
+ got.status.should.equal 200
71
+ got.body.length.should.equal 128
72
+ end
73
+
74
+ # ------------------------------------------------
75
+ # Validate the token expiration
76
+
77
+ it 'Check if the token is regenerated (it shouldn\'t)' do
78
+ got = get '/dont_regenerate'
79
+
80
+ got.status.should.equal 200
81
+ $token_sess.should.equal $token_method
82
+ end
83
+
84
+ it 'Check if the token successfully expires after 3 seconds' do
85
+ got = get '/check_ttl'
86
+
87
+ got.status.should.equal 200
88
+ $old_token.should.not.equal $new_token
89
+ end
90
+
91
+ # ------------------------------------------------
92
+ # Validate all HTTP requests (GET, POST, etc)
93
+
94
+ it 'Validate all HTTP requests (GET, POST, etc)' do
95
+ methods = [:get, :post, :put, :delete]
96
+
97
+ methods.each do |method|
98
+ got_invalid = self.send(method, '/check_post', :name => "Yorick Peterse")
99
+ got_valid = self.send(method, '/check_post', :csrf_token => $new_token)
100
+
101
+ got_invalid.status.should.equal 401
102
+ got_invalid.body.should.equal "The specified CSRF token is incorrect."
103
+
104
+ got_valid.status.should.equal 200
105
+ got_valid.body.should.equal "POST allowed."
106
+ end
107
+ end
108
+
109
+ end
@@ -60,17 +60,19 @@ describe Ramaze::Helper::HttpDigest do
60
60
 
61
61
  it 'sends out all the required header information' do
62
62
  get '/authenticate'
63
- www_authenticate = last_response.headers['WWW-Authenticate']
64
- authorization = Rack::Auth::Digest::Params.parse(www_authenticate)
63
+ www_authenticate = last_response.headers['WWW-Authenticate']
64
+ authorization = Rack::Auth::Digest::Params.parse(www_authenticate)
65
+
65
66
  authorization["opaque"].should.not.be.empty
66
67
  authorization["nonce"].should.not.be.empty
67
- authorization["realm"].should == REALM
68
- authorization["qop"].should == "auth,auth-int"
68
+ authorization["realm"].should.equal REALM
69
+ authorization["qop"].should.equal "auth,auth-int"
69
70
 
70
71
  digest_authorize 'foo', 'oof'
71
72
  get '/authenticate'
73
+
72
74
  last_response.headers.should.satisfy do |headers|
73
- !headers.has_key?( "WWW-Authenticate" )
75
+ !headers.has_key? "WWW-Authenticate"
74
76
  end
75
77
  end
76
78
  end
@@ -83,13 +85,13 @@ describe Ramaze::Helper::HttpDigest do
83
85
  digest_authorize nil, nil
84
86
 
85
87
  get '/authenticate'
86
- last_response.status.should == 401
87
- last_response.body.should == "Unauthorized"
88
+ last_response.status.should.equal 401
89
+ last_response.body.should.equal "Unauthorized"
88
90
 
89
91
  digest_authorize 'foo', 'oof'
90
92
  get '/authenticate'
91
- last_response.status.should == 200
92
- last_response.body.should == "Hello foo"
93
+ last_response.status.should.equal 200
94
+ last_response.body.should.equal "Hello foo"
93
95
  end
94
96
 
95
97
  it 'fails to authenticate an incorrect password with a block' do
@@ -97,13 +99,13 @@ describe Ramaze::Helper::HttpDigest do
97
99
  digest_authorize nil, nil
98
100
 
99
101
  get '/authenticate'
100
- last_response.status.should == 401
101
- last_response.body.should == "Unauthorized"
102
+ last_response.status.should.equal 401
103
+ last_response.body.should.equal "Unauthorized"
102
104
 
103
105
  digest_authorize 'foo', 'bar'
104
106
  get '/authenticate'
105
- last_response.status.should == 401
106
- last_response.body.should == "Unauthorized"
107
+ last_response.status.should.equal 401
108
+ last_response.body.should.equal "Unauthorized"
107
109
  end
108
110
  end
109
111
 
@@ -115,13 +117,13 @@ describe Ramaze::Helper::HttpDigest do
115
117
  digest_authorize nil, nil
116
118
 
117
119
  get '/plaintext/authenticate'
118
- last_response.status.should == 401
119
- last_response.body.should == 'Unauthorized'
120
+ last_response.status.should.equal 401
121
+ last_response.body.should.equal 'Unauthorized'
120
122
 
121
123
  digest_authorize 'foo', 'oof'
122
124
  get '/plaintext/authenticate'
123
- last_response.status.should == 200
124
- last_response.body.should == "Hello foo"
125
+ last_response.status.should.equal 200
126
+ last_response.body.should.equal "Hello foo"
125
127
  end
126
128
 
127
129
  it 'fails to authenticate an incorrect password with the plaintext method' do
@@ -129,13 +131,13 @@ describe Ramaze::Helper::HttpDigest do
129
131
  digest_authorize nil, nil
130
132
 
131
133
  get '/plaintext/authenticate'
132
- last_response.status.should == 401
133
- last_response.body.should == "Unauthorized"
134
+ last_response.status.should.equal 401
135
+ last_response.body.should.equal "Unauthorized"
134
136
 
135
137
  digest_authorize 'foo', 'bar'
136
138
  get '/plaintext/authenticate'
137
- last_response.status.should == 401
138
- last_response.body.should == "Unauthorized"
139
+ last_response.status.should.equal 401
140
+ last_response.body.should.equal "Unauthorized"
139
141
  end
140
142
  end
141
143
 
@@ -147,13 +149,13 @@ describe Ramaze::Helper::HttpDigest do
147
149
  digest_authorize nil, nil
148
150
 
149
151
  get '/lookup/authenticate'
150
- last_response.status.should == 401
151
- last_response.body.should == "Unauthorized"
152
+ last_response.status.should.equal 401
153
+ last_response.body.should.equal "Unauthorized"
152
154
 
153
155
  digest_authorize 'foo', 'oof'
154
156
  get '/lookup/authenticate'
155
- last_response.status.should == 200
156
- last_response.body.should == "Hello foo"
157
+ last_response.status.should.equal 200
158
+ last_response.body.should.equal "Hello foo"
157
159
  end
158
160
 
159
161
  it 'fails to authenticate an incorrect password with the password lookup method' do
@@ -161,13 +163,13 @@ describe Ramaze::Helper::HttpDigest do
161
163
  digest_authorize nil, nil
162
164
 
163
165
  get '/lookup/authenticate'
164
- last_response.status.should == 401
165
- last_response.body.should == "Unauthorized"
166
+ last_response.status.should.equal 401
167
+ last_response.body.should.equal "Unauthorized"
166
168
 
167
169
  digest_authorize 'foo', 'bar'
168
170
  get '/lookup/authenticate'
169
- last_response.status.should == 401
170
- last_response.body.should == "Unauthorized"
171
+ last_response.status.should.equal 401
172
+ last_response.body.should.equal "Unauthorized"
171
173
  end
172
174
  end
173
175
 
@@ -54,7 +54,7 @@ class SpecUserHelperCallback < SpecUserHelper
54
54
  end
55
55
  end
56
56
 
57
- describe Ramaze::Helper::User do
57
+ describe Ramaze::Helper::UserHelper do
58
58
  behaves_like :rack_test
59
59
 
60
60
  should 'login' do
@@ -18,6 +18,16 @@ describe Ramaze::Helper::XHTML do
18
18
  should == '<link href="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" media="screen" rel="stylesheet" type="text/css" />'
19
19
  end
20
20
 
21
+ should 'answer with <link> on #css with optional prefix' do
22
+ css(:foo, 'screen', :prefix => 'bar').
23
+ should == '<link href="/bar/foo.css" media="screen" rel="stylesheet" type="text/css" />'
24
+ end
25
+
26
+ should 'answer with <link> on #css with options hash as second argument' do
27
+ css(:foo, :prefix => 'bar/baz').
28
+ should == '<link href="/bar/baz/foo.css" media="screen" rel="stylesheet" type="text/css" />'
29
+ end
30
+
21
31
  should 'answer with <script> on #js' do
22
32
  js(:foo).
23
33
  should == '<script src="/js/foo.js" type="text/javascript"></script>'
@@ -25,6 +35,13 @@ describe Ramaze::Helper::XHTML do
25
35
  should == '<script src="http://example.com/foo.js" type="text/javascript"></script>'
26
36
  end
27
37
 
38
+ should 'answer with <script> on #js with optional prefix' do
39
+ js(:foo, :prefix => 'bar').
40
+ should == '<script src="/bar/foo.js" type="text/javascript"></script>'
41
+ js(:foo, :prefix => 'javascripts/deeply/nested').
42
+ should == '<script src="/javascripts/deeply/nested/foo.js" type="text/javascript"></script>'
43
+ end
44
+
28
45
  should 'answer with multiple <link> on #css_for' do
29
46
  css_for(:foo, :bar).
30
47
  should == "<link href=\"/css/foo.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"/css/bar.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
@@ -0,0 +1,34 @@
1
+ require File.expand_path('../../../../spec/helper', __FILE__)
2
+
3
+ spec_precondition 'ruby-growl is installed' do
4
+ require 'ruby-growl'
5
+ end
6
+
7
+ require 'ramaze/log/growl'
8
+
9
+ # Configure Growl, make sure your growl server matches
10
+ # these settings.
11
+ Ramaze::Logger::Growl.trait[:defaults][:name] = 'ramaze'
12
+ Ramaze::Logger::Growl.trait[:defaults][:password] = 'ramaze'
13
+
14
+ describe Ramaze::Logger::Growl do
15
+ it 'Create a new instance of the Growl logger' do
16
+ growl = Ramaze::Logger::Growl.new
17
+
18
+ growl.should.respond_to? :log
19
+ end
20
+
21
+ it 'Send a growl notification' do
22
+ growl = Ramaze::Logger::Growl.new
23
+ growl.log(:info, 'Hello, Ramaze!')
24
+
25
+ # The Growl library doesn't seem to return anything,
26
+ # let's ask the user if the notification was sent
27
+ print "Did you see the the notification? (y/n) "
28
+ response = gets.strip
29
+
30
+ response.should.satisfy do |object|
31
+ object === 'y' or object === 'yes'
32
+ end
33
+ end
34
+ end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require File.expand_path('../../../../spec/helper', __FILE__)
5
5
  require 'ramaze/log/informer'
6
+ require 'stringio'
6
7
 
7
8
  describe 'Informer' do
8
9
  @out = []
@@ -1,90 +1,68 @@
1
- # Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
2
- # All files in this distribution are subject to the terms of the Ruby license.
3
-
4
1
  require File.expand_path('../../../../spec/helper', __FILE__)
5
2
  spec_require 'erector'
6
3
 
7
- Ramaze::App.options.views = 'erector'
4
+ # Define what view and layout we'll need to load
5
+ Ramaze::App.options.views = 'erector'
8
6
  Ramaze::App.options.layouts = 'erector'
9
7
 
8
+ ##
9
+ # Core spec class for the test.
10
+ # This class is nothing more than a regular controller but is called using Bacon instead of a browser.
11
+ #
10
12
  class SpecErector < Ramaze::Controller
13
+ # Map the controller to the root of the server.
11
14
  map '/'
12
- engine :Erector
13
- helper :erector
15
+
16
+ # Set the engine to Erector. We can't test Erector if we're not using it can we?
17
+ engine :erector
18
+ helper :erector, :thread
14
19
  layout :layout
15
-
16
- def invoke_helper_method
17
- end
18
-
20
+
21
+ # The index method loads a very basic view in a layout.
19
22
  def index
20
- erector { h1 "Erector Index" }
21
23
  end
22
-
23
- def links
24
- erector {
25
- ul {
26
- li { a(:href => r(:index)) { text "Index page" } }
27
- li { a(:href => r(:internal)){ text "Internal template" } }
28
- li { a(:href => r(:external)){ text "External template" } }
29
- }
30
- }
24
+
25
+ # The tables method loads a view that contains a html table.
26
+ def tables
27
+ @users = [{:name => 'Yorick Peterse', :age => 18}, {:name => 'Chuck Norris', :age => 9000}, {:name => 'Bob Ross', :age => 53}]
31
28
  end
32
-
33
- def strict_xhtml
34
- end
35
-
36
- def ie_if
37
- erector {
38
- ie_if("lt IE 8") {
39
- css "ie.css"
40
- css "test2.css", :media => 'screen'
41
- }
42
- }
43
- end
44
-
45
- def css
46
- erector {
47
- css "test.css", :media => 'screen'
48
- }
49
- end
50
-
51
- def sum(num1, num2)
52
- @num1, @num2 = num1.to_i, num2.to_i
29
+
30
+ # Render a view inside a view
31
+ def view
53
32
  end
33
+
54
34
  end
55
35
 
36
+ # Testing time!
56
37
  describe Ramaze::View::Erector do
38
+ # The test type is a basic Rack based test.
57
39
  behaves_like :rack_test
58
-
59
- should 'use erector methods' do
60
- get('/').body.should == '<div><h1>Erector Index</h1></div>'
40
+
41
+ # Render the index view. This is a basic view wrapped in a layout
42
+ it 'Render a basic layout and view' do
43
+ got = get '/'
44
+
45
+ got.status.should.equal 200
46
+ got['Content-Type'].should.equal 'text/html'
47
+ got.body.strip.should.equal '<html><head><title>erector</title></head><body><p>paragraph text</p></body></html>'
61
48
  end
62
-
63
- should 'use other helper methods' do
64
- get('/links').body.should == '<div><ul><li><a href="/index">Index page</a></li><li><a href="/internal">Internal template</a></li><li><a href="/external">External template</a></li></ul></div>'
65
- end
66
-
67
- should 'render external template' do
68
- get('/external').body.should == "<div><h1>External Erector View Template</h1></div>"
49
+
50
+ # Render the tables view
51
+ it 'Render a view with an HTML table' do
52
+ got = get '/tables'
53
+
54
+ got.status.should.equal 200
55
+ got['Content-Type'].should.equal 'text/html'
56
+ got.body.strip.should.equal '<html><head><title>erector</title></head><body><table><thead><tr><th>Name</th><th>Age</th></tr></thead><tbody><tr><td>Yorick Peterse</td><td>18</td></tr><tr><td>Chuck Norris</td><td>9000</td></tr><tr><td>Bob Ross</td><td>53</td></tr></tbody></table></body></html>'
69
57
  end
70
-
71
- should 'render external template with instance variables' do
72
- get('/sum/1/2').body.should == '<div><p>3</p></div>'
58
+
59
+ # Render a view inside a view
60
+ it 'Render a view inside a view' do
61
+ got = get '/view'
62
+
63
+ got.status.should.equal 200
64
+ got['Content-Type'].should.equal 'text/html'
65
+ got.body.strip.should.equal '<html><head><title>erector</title></head><body><h2>Hello, view!</h2><p>view body.</p></body></html>'
73
66
  end
74
-
75
- should 'render external strict xhtml template' do
76
- get('/strict_xhtml').body.should == "<div><?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\"><html lang=\"en\" xml:lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><p>STRICT!</p></html></div>"
77
- end
78
-
79
- should 'render css link with merged options' do
80
- get('/css').body.should == "<div><link href=\"test.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" /></div>"
81
- end
82
-
83
- should 'render ie conditional' do
84
- get('/ie_if').body.should == "<div><!--[if lt IE 8]><link href=\"ie.css\" rel=\"stylesheet\" type=\"text/css\" /><link href=\"test2.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" /><![endif]--></div>"
85
- end
86
-
87
- should 'invoke helper methods from external template' do
88
- get('/invoke_helper_method').body.should == "<div><a href=\"/index\">Index page</a></div>"
89
- end
90
- end
67
+
68
+ end