ramaze 2010.06.18 → 2011.01

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