haveapi 0.20.0 → 0.21.0

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Rakefile +6 -6
  4. data/haveapi.gemspec +13 -13
  5. data/lib/haveapi/action.rb +140 -158
  6. data/lib/haveapi/action_state.rb +2 -6
  7. data/lib/haveapi/actions/default.rb +8 -10
  8. data/lib/haveapi/api.rb +2 -1
  9. data/lib/haveapi/authentication/base.rb +5 -8
  10. data/lib/haveapi/authentication/basic/provider.rb +4 -5
  11. data/lib/haveapi/authentication/chain.rb +19 -17
  12. data/lib/haveapi/authentication/oauth2/config.rb +12 -32
  13. data/lib/haveapi/authentication/oauth2/provider.rb +20 -30
  14. data/lib/haveapi/authentication/oauth2/revoke_endpoint.rb +1 -2
  15. data/lib/haveapi/authentication/token/action_config.rb +5 -3
  16. data/lib/haveapi/authentication/token/config.rb +5 -5
  17. data/lib/haveapi/authentication/token/provider.rb +33 -37
  18. data/lib/haveapi/authorization.rb +5 -4
  19. data/lib/haveapi/client_example.rb +11 -14
  20. data/lib/haveapi/client_examples/curl.rb +37 -37
  21. data/lib/haveapi/client_examples/fs_client.rb +29 -31
  22. data/lib/haveapi/client_examples/http.rb +35 -36
  23. data/lib/haveapi/client_examples/js_client.rb +62 -63
  24. data/lib/haveapi/client_examples/php_client.rb +77 -76
  25. data/lib/haveapi/client_examples/ruby_cli.rb +30 -30
  26. data/lib/haveapi/client_examples/ruby_client.rb +26 -26
  27. data/lib/haveapi/common.rb +3 -4
  28. data/lib/haveapi/context.rb +11 -10
  29. data/lib/haveapi/example.rb +9 -4
  30. data/lib/haveapi/example_list.rb +2 -2
  31. data/lib/haveapi/exceptions.rb +1 -1
  32. data/lib/haveapi/extensions/action_exceptions.rb +2 -2
  33. data/lib/haveapi/extensions/base.rb +1 -3
  34. data/lib/haveapi/extensions/exception_mailer.rb +260 -257
  35. data/lib/haveapi/hooks.rb +40 -39
  36. data/lib/haveapi/metadata.rb +1 -1
  37. data/lib/haveapi/model_adapter.rb +16 -27
  38. data/lib/haveapi/model_adapters/active_record.rb +59 -69
  39. data/lib/haveapi/output_formatter.rb +7 -7
  40. data/lib/haveapi/output_formatters/base.rb +2 -4
  41. data/lib/haveapi/parameters/resource.rb +7 -7
  42. data/lib/haveapi/parameters/typed.rb +6 -9
  43. data/lib/haveapi/params.rb +38 -45
  44. data/lib/haveapi/resource.rb +8 -8
  45. data/lib/haveapi/resources/action_state.rb +11 -19
  46. data/lib/haveapi/server.rb +102 -107
  47. data/lib/haveapi/spec/api_response.rb +1 -1
  48. data/lib/haveapi/spec/helpers.rb +1 -1
  49. data/lib/haveapi/spec/mock_action.rb +11 -10
  50. data/lib/haveapi/spec/spec_methods.rb +9 -8
  51. data/lib/haveapi/tasks/yard.rb +2 -2
  52. data/lib/haveapi/types.rb +0 -3
  53. data/lib/haveapi/validator.rb +6 -3
  54. data/lib/haveapi/validator_chain.rb +9 -8
  55. data/lib/haveapi/validators/acceptance.rb +6 -6
  56. data/lib/haveapi/validators/confirmation.rb +2 -3
  57. data/lib/haveapi/validators/exclusion.rb +1 -1
  58. data/lib/haveapi/validators/format.rb +1 -1
  59. data/lib/haveapi/validators/inclusion.rb +1 -1
  60. data/lib/haveapi/validators/length.rb +12 -11
  61. data/lib/haveapi/validators/numericality.rb +14 -13
  62. data/lib/haveapi/validators/presence.rb +4 -3
  63. data/lib/haveapi/version.rb +2 -2
  64. data/lib/haveapi.rb +2 -3
  65. data/spec/.rubocop.yml +4 -0
  66. data/spec/action/dsl_spec.rb +18 -18
  67. data/spec/authorization_spec.rb +8 -8
  68. data/spec/common_spec.rb +2 -1
  69. data/spec/documentation_spec.rb +2 -9
  70. data/spec/envelope_spec.rb +2 -2
  71. data/spec/hooks_spec.rb +12 -12
  72. data/spec/parameters/typed_spec.rb +6 -6
  73. data/spec/params_spec.rb +22 -24
  74. data/spec/resource_spec.rb +5 -7
  75. data/spec/spec_helper.rb +0 -1
  76. data/spec/validators/acceptance_spec.rb +1 -1
  77. data/spec/validators/confirmation_spec.rb +5 -5
  78. data/spec/validators/exclusion_spec.rb +3 -3
  79. data/spec/validators/format_spec.rb +2 -2
  80. data/spec/validators/inclusion_spec.rb +4 -4
  81. data/spec/validators/length_spec.rb +23 -23
  82. data/spec/validators/numericality_spec.rb +13 -13
  83. data/spec/validators/presence_spec.rb +3 -3
  84. metadata +49 -48
@@ -10,12 +10,15 @@ module HaveAPI::Extensions
10
10
  # functions are removed, since e-mail doesn't support it. HaveAPI-specific content
11
11
  # is added. Some helper methods are taken either from Sinatra or Rack.
12
12
  class ExceptionMailer < Base
13
+ Frame = Struct.new(:filename, :lineno, :function, :context_line)
14
+
13
15
  # @param opts [Hash] options
14
16
  # @option opts to [String] recipient address
15
17
  # @option opts from [String] sender address
16
18
  # @option opts subject [String] '%s' is replaced by the error message
17
19
  # @option opts smtp [Hash, falsy] smtp options, sendmail is used if not provided
18
20
  def initialize(opts)
21
+ super()
19
22
  @opts = opts
20
23
  end
21
24
 
@@ -33,28 +36,27 @@ module HaveAPI::Extensions
33
36
 
34
37
  def log(context, exception)
35
38
  req = context.request.request
36
- path = (req.script_name + req.path_info).squeeze("/")
39
+ path = (req.script_name + req.path_info).squeeze('/')
37
40
 
38
- frames = exception.backtrace.map { |line|
39
- frame = OpenStruct.new
41
+ frames = exception.backtrace.map do |line|
42
+ frame = Frame.new
40
43
 
41
- if line =~ /(.*?):(\d+)(:in `(.*)')?/
42
- frame.filename = $1
43
- frame.lineno = $2.to_i
44
- frame.function = $4
44
+ next unless line =~ /(.*?):(\d+)(:in `(.*)')?/
45
45
 
46
- begin
47
- lineno = frame.lineno-1
48
- lines = ::File.readlines(frame.filename)
49
- frame.context_line = lines[lineno].chomp
50
- rescue
51
- end
46
+ frame.filename = ::Regexp.last_match(1)
47
+ frame.lineno = ::Regexp.last_match(2).to_i
48
+ frame.function = ::Regexp.last_match(4)
52
49
 
53
- frame
54
- else
55
- nil
50
+ begin
51
+ lineno = frame.lineno - 1
52
+ lines = ::File.readlines(frame.filename)
53
+ frame.context_line = lines[lineno].chomp
54
+ rescue StandardError
55
+ # ignore
56
56
  end
57
- }.compact
57
+
58
+ frame
59
+ end.compact
58
60
 
59
61
  env = context.request.env
60
62
 
@@ -72,9 +74,9 @@ module HaveAPI::Extensions
72
74
  mail = ::Mail.new({
73
75
  from: @opts[:from],
74
76
  to: @opts[:to],
75
- subject: @opts[:subject] % [exception.to_s],
76
- body: body,
77
- content_type: 'text/html; charset=UTF-8',
77
+ subject: format(@opts[:subject], exception.to_s),
78
+ body:,
79
+ content_type: 'text/html; charset=UTF-8'
78
80
  })
79
81
 
80
82
  if @opts[:smtp]
@@ -89,15 +91,16 @@ module HaveAPI::Extensions
89
91
  end
90
92
 
91
93
  protected
94
+
92
95
  # From {Sinatra::ShowExceptions}
93
96
  def frame_class(frame)
94
- if frame.filename =~ /lib\/sinatra.*\.rb/
95
- "framework"
97
+ if frame.filename =~ %r{lib/sinatra.*\.rb}
98
+ 'framework'
96
99
  elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
97
- frame.filename =~ /\/bin\/(\w+)$/
98
- "system"
100
+ frame.filename =~ %r{/bin/(\w+)$}
101
+ 'system'
99
102
  else
100
- "app"
103
+ 'app'
101
104
  end
102
105
  end
103
106
 
@@ -111,237 +114,237 @@ module HaveAPI::Extensions
111
114
  end
112
115
  end
113
116
 
114
- TEMPLATE = ERB.new(<<END
115
- <!DOCTYPE html>
116
- <html>
117
- <head>
118
- <meta charset="utf-8">
119
- <title><%=h exception.class %> at <%=h path %></title>
120
- <style type="text/css">
121
- * {margin: 0; padding: 0; border: 0; outline: 0;}
122
- div.clear {clear: both;}
123
- body {background: #EEEEEE; margin: 0; padding: 0;
124
- font-family: 'Lucida Grande', 'Lucida Sans Unicode',
125
- 'Garuda';}
126
- code {font-family: 'Lucida Console', monospace;
127
- font-size: 12px;}
128
- li {height: 18px;}
129
- ul {list-style: none; margin: 0; padding: 0;}
130
- ol:hover {cursor: pointer;}
131
- ol li {white-space: pre;}
132
- #explanation {font-size: 12px; color: #666666;
133
- margin: 20px 0 0 100px;}
134
- /* WRAP */
135
- #wrap {width: 1000px; background: #FFFFFF; margin: 0 auto;
136
- padding: 30px 5px 20px 5px;
137
- border-left: 1px solid #DDDDDD;
138
- border-right: 1px solid #DDDDDD;}
139
- /* HEADER */
140
- #header {margin: 0 auto 25px auto;}
141
- #header #summary {margin: 12px 0 0 20px;
142
- font-family: 'Lucida Grande', 'Lucida Sans Unicode';}
143
- h1 {margin: 0; font-size: 36px; color: #981919;}
144
- h2 {margin: 0; font-size: 22px; color: #333333;}
145
- #header ul {margin: 0; font-size: 12px; color: #666666;}
146
- #header ul li strong{color: #444444;}
147
- #header ul li {display: inline; padding: 0 10px;}
148
- #header ul li.first {padding-left: 0;}
149
- #header ul li.last {border: 0; padding-right: 0;}
150
- /* BODY */
151
- #backtrace,
152
- #get,
153
- #post,
154
- #cookies,
155
- #rack, #context {width: 980px; margin: 0 auto 10px auto;}
156
- p#nav {float: right; font-size: 14px;}
157
- /* BACKTRACE */
158
- h3 {float: left; width: 100px; margin-bottom: 10px;
159
- color: #981919; font-size: 14px; font-weight: bold;}
160
- #nav a {color: #666666; text-decoration: none; padding: 0 5px;}
161
- #backtrace li.frame-info {background: #f7f7f7; padding-left: 10px;
162
- font-size: 12px; color: #333333;}
163
- #backtrace ul {list-style-position: outside; border: 1px solid #E9E9E9;
164
- border-bottom: 0;}
165
- #backtrace ol {width: 920px; margin-left: 50px;
166
- font: 10px 'Lucida Console', monospace; color: #666666;}
167
- #backtrace ol li {border: 0; border-left: 1px solid #E9E9E9;
168
- padding: 2px 0;}
169
- #backtrace ol code {font-size: 10px; color: #555555; padding-left: 5px;}
170
- #backtrace-ul li {border-bottom: 1px solid #E9E9E9; height: auto;
171
- padding: 3px 0;}
172
- #backtrace-ul .code {padding: 6px 0 4px 0;}
173
- /* REQUEST DATA */
174
- p.no-data {padding-top: 2px; font-size: 12px; color: #666666;}
175
- table.req {width: 980px; text-align: left; font-size: 12px;
176
- color: #666666; padding: 0; border-spacing: 0;
177
- border: 1px solid #EEEEEE; border-bottom: 0;
178
- border-left: 0;
179
- clear:both}
180
- table.req tr th {padding: 2px 10px; font-weight: bold;
181
- background: #F7F7F7; border-bottom: 1px solid #EEEEEE;
182
- border-left: 1px solid #EEEEEE;}
183
- table.req tr td {padding: 2px 20px 2px 10px;
184
- border-bottom: 1px solid #EEEEEE;
185
- border-left: 1px solid #EEEEEE;}
186
- /* HIDE PRE/POST CODE AT START */
187
- .pre-context,
188
- .post-context {display: none;}
189
- table td.code {width:750px}
190
- table td.code div {width:750px;overflow:hidden}
191
- </style>
192
- </head>
193
- <body>
194
- <div id="wrap">
195
- <div id="header">
196
- <div id="summary">
197
- <h1><strong><%=h exception.class %></strong> at <strong><%=h path %>
198
- </strong></h1>
199
- <h2><%=h exception.message %></h2>
200
- <ul>
201
- <li class="first"><strong>file:</strong> <code>
202
- <%=h frames.first.filename.split("/").last %></code></li>
203
- <li><strong>location:</strong> <code><%=h frames.first.function %>
204
- </code></li>
205
- <li class="last"><strong>line:
206
- </strong> <%=h frames.first.lineno %></li>
207
- </ul>
208
- </div>
209
- <div class="clear"></div>
210
- </div>
211
- <div id="context">
212
- <h3>Context</h3>
213
- <table class="req">
214
- <tr>
215
- <th>API version</th>
216
- <td><%=h context.version %></td>
217
- </tr>
218
- <tr>
219
- <th>Action</th>
220
- <td><%= h(context.action && context.action.to_s) %></td>
221
- </tr>
222
- <tr>
223
- <th>Arguments</th>
224
- <td><%=h context.args %></td>
225
- </tr>
226
- <tr>
227
- <th>Parameters</th>
228
- <td><%=h context.params %></td>
229
- </tr>
230
- <tr>
231
- <th>User</th>
232
- <td><%=h user %></td>
233
- </tr>
234
- </table>
235
- <div class="clear"></div>
236
- </div>
237
- <div id="backtrace">
238
- <h3>BACKTRACE</h3>
239
- <p id="nav"><strong>JUMP TO:</strong>
240
- <a href="#get-info">GET</a>
241
- <a href="#post-info">POST</a>
242
- <a href="#cookie-info">COOKIES</a>
243
- <a href="#env-info">ENV</a>
244
- </p>
245
- <div class="clear"></div>
246
- <ul id="backtrace-ul">
247
- <% frames.each do |frame| %>
248
- <li class="frame-info <%= frame_class(frame) %>">
249
- <code><%=h frame.filename %></code> in
250
- <code><strong><%=h frame.function %></strong></code>
251
- </li>
252
- <li class="code <%= frame_class(frame) %>">
253
- <ol start="<%= frame.lineno %>" class="context">
254
- <li class="context-line">
255
- <code><%=h frame.context_line %></code>
256
- </li>
257
- </ol>
258
- <div class="clear"></div>
259
- </li>
260
- <% end %>
261
- </ul>
262
- </div> <!-- /BACKTRACE -->
263
- <div id="get">
264
- <h3 id="get-info">GET</h3>
265
- <% if req.GET and not req.GET.empty? %>
266
- <table class="req">
267
- <tr>
268
- <th>Variable</th>
269
- <th>Value</th>
270
- </tr>
271
- <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
272
- <tr>
273
- <td><%=h key %></td>
274
- <td class="code"><div><%=h val.inspect %></div></td>
275
- </tr>
276
- <% } %>
277
- </table>
278
- <% else %>
279
- <p class="no-data">No GET data.</p>
280
- <% end %>
281
- <div class="clear"></div>
282
- </div> <!-- /GET -->
283
- <div id="post">
284
- <h3 id="post-info">POST</h3>
285
- <% if req.POST and not req.POST.empty? %>
286
- <table class="req">
287
- <tr>
288
- <th>Variable</th>
289
- <th>Value</th>
290
- </tr>
291
- <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
292
- <tr>
293
- <td><%=h key %></td>
294
- <td class="code"><div><%=h val.inspect %></div></td>
295
- </tr>
296
- <% } %>
297
- </table>
298
- <% else %>
299
- <p class="no-data">No POST data.</p>
300
- <% end %>
301
- <div class="clear"></div>
302
- </div> <!-- /POST -->
303
- <div id="cookies">
304
- <h3 id="cookie-info">COOKIES</h3>
305
- <% unless req.cookies.empty? %>
306
- <table class="req">
307
- <tr>
308
- <th>Variable</th>
309
- <th>Value</th>
310
- </tr>
311
- <% req.cookies.each { |key, val| %>
312
- <tr>
313
- <td><%=h key %></td>
314
- <td class="code"><div><%=h val.inspect %></div></td>
315
- </tr>
316
- <% } %>
317
- </table>
318
- <% else %>
319
- <p class="no-data">No cookie data.</p>
320
- <% end %>
321
- <div class="clear"></div>
322
- </div> <!-- /COOKIES -->
323
- <div id="rack">
324
- <h3 id="env-info">Rack ENV</h3>
325
- <table class="req">
326
- <tr>
327
- <th>Variable</th>
328
- <th>Value</th>
329
- </tr>
330
- <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
331
- <tr>
332
- <td><%=h key %></td>
333
- <td class="code"><div><%=h val %></div></td>
334
- </tr>
335
- <% } %>
336
- </table>
337
- <div class="clear"></div>
338
- </div> <!-- /RACK ENV -->
339
- <p id="explanation">You're seeing this error because you have
340
- enabled HaveAPI Extension <code>HaveAPI::Extensions::ExceptionMailer</code>.</p>
341
- </div> <!-- /WRAP -->
342
- </body>
343
- </html>
344
- END
345
- )
117
+ TEMPLATE = ERB.new(<<~END
118
+ <!DOCTYPE html>
119
+ <html>
120
+ <head>
121
+ <meta charset="utf-8">
122
+ <title><%=h exception.class %> at <%=h path %></title>
123
+ <style type="text/css">
124
+ * {margin: 0; padding: 0; border: 0; outline: 0;}
125
+ div.clear {clear: both;}
126
+ body {background: #EEEEEE; margin: 0; padding: 0;
127
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode',
128
+ 'Garuda';}
129
+ code {font-family: 'Lucida Console', monospace;
130
+ font-size: 12px;}
131
+ li {height: 18px;}
132
+ ul {list-style: none; margin: 0; padding: 0;}
133
+ ol:hover {cursor: pointer;}
134
+ ol li {white-space: pre;}
135
+ #explanation {font-size: 12px; color: #666666;
136
+ margin: 20px 0 0 100px;}
137
+ /* WRAP */
138
+ #wrap {width: 1000px; background: #FFFFFF; margin: 0 auto;
139
+ padding: 30px 5px 20px 5px;
140
+ border-left: 1px solid #DDDDDD;
141
+ border-right: 1px solid #DDDDDD;}
142
+ /* HEADER */
143
+ #header {margin: 0 auto 25px auto;}
144
+ #header #summary {margin: 12px 0 0 20px;
145
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode';}
146
+ h1 {margin: 0; font-size: 36px; color: #981919;}
147
+ h2 {margin: 0; font-size: 22px; color: #333333;}
148
+ #header ul {margin: 0; font-size: 12px; color: #666666;}
149
+ #header ul li strong{color: #444444;}
150
+ #header ul li {display: inline; padding: 0 10px;}
151
+ #header ul li.first {padding-left: 0;}
152
+ #header ul li.last {border: 0; padding-right: 0;}
153
+ /* BODY */
154
+ #backtrace,
155
+ #get,
156
+ #post,
157
+ #cookies,
158
+ #rack, #context {width: 980px; margin: 0 auto 10px auto;}
159
+ p#nav {float: right; font-size: 14px;}
160
+ /* BACKTRACE */
161
+ h3 {float: left; width: 100px; margin-bottom: 10px;
162
+ color: #981919; font-size: 14px; font-weight: bold;}
163
+ #nav a {color: #666666; text-decoration: none; padding: 0 5px;}
164
+ #backtrace li.frame-info {background: #f7f7f7; padding-left: 10px;
165
+ font-size: 12px; color: #333333;}
166
+ #backtrace ul {list-style-position: outside; border: 1px solid #E9E9E9;
167
+ border-bottom: 0;}
168
+ #backtrace ol {width: 920px; margin-left: 50px;
169
+ font: 10px 'Lucida Console', monospace; color: #666666;}
170
+ #backtrace ol li {border: 0; border-left: 1px solid #E9E9E9;
171
+ padding: 2px 0;}
172
+ #backtrace ol code {font-size: 10px; color: #555555; padding-left: 5px;}
173
+ #backtrace-ul li {border-bottom: 1px solid #E9E9E9; height: auto;
174
+ padding: 3px 0;}
175
+ #backtrace-ul .code {padding: 6px 0 4px 0;}
176
+ /* REQUEST DATA */
177
+ p.no-data {padding-top: 2px; font-size: 12px; color: #666666;}
178
+ table.req {width: 980px; text-align: left; font-size: 12px;
179
+ color: #666666; padding: 0; border-spacing: 0;
180
+ border: 1px solid #EEEEEE; border-bottom: 0;
181
+ border-left: 0;
182
+ clear:both}
183
+ table.req tr th {padding: 2px 10px; font-weight: bold;
184
+ background: #F7F7F7; border-bottom: 1px solid #EEEEEE;
185
+ border-left: 1px solid #EEEEEE;}
186
+ table.req tr td {padding: 2px 20px 2px 10px;
187
+ border-bottom: 1px solid #EEEEEE;
188
+ border-left: 1px solid #EEEEEE;}
189
+ /* HIDE PRE/POST CODE AT START */
190
+ .pre-context,
191
+ .post-context {display: none;}
192
+ table td.code {width:750px}
193
+ table td.code div {width:750px;overflow:hidden}
194
+ </style>
195
+ </head>
196
+ <body>
197
+ <div id="wrap">
198
+ <div id="header">
199
+ <div id="summary">
200
+ <h1><strong><%=h exception.class %></strong> at <strong><%=h path %>
201
+ </strong></h1>
202
+ <h2><%=h exception.message %></h2>
203
+ <ul>
204
+ <li class="first"><strong>file:</strong> <code>
205
+ <%=h frames.first.filename.split("/").last %></code></li>
206
+ <li><strong>location:</strong> <code><%=h frames.first.function %>
207
+ </code></li>
208
+ <li class="last"><strong>line:
209
+ </strong> <%=h frames.first.lineno %></li>
210
+ </ul>
211
+ </div>
212
+ <div class="clear"></div>
213
+ </div>
214
+ <div id="context">
215
+ <h3>Context</h3>
216
+ <table class="req">
217
+ <tr>
218
+ <th>API version</th>
219
+ <td><%=h context.version %></td>
220
+ </tr>
221
+ <tr>
222
+ <th>Action</th>
223
+ <td><%= h(context.action && context.action.to_s) %></td>
224
+ </tr>
225
+ <tr>
226
+ <th>Arguments</th>
227
+ <td><%=h context.args %></td>
228
+ </tr>
229
+ <tr>
230
+ <th>Parameters</th>
231
+ <td><%=h context.params %></td>
232
+ </tr>
233
+ <tr>
234
+ <th>User</th>
235
+ <td><%=h user %></td>
236
+ </tr>
237
+ </table>
238
+ <div class="clear"></div>
239
+ </div>
240
+ <div id="backtrace">
241
+ <h3>BACKTRACE</h3>
242
+ <p id="nav"><strong>JUMP TO:</strong>
243
+ <a href="#get-info">GET</a>
244
+ <a href="#post-info">POST</a>
245
+ <a href="#cookie-info">COOKIES</a>
246
+ <a href="#env-info">ENV</a>
247
+ </p>
248
+ <div class="clear"></div>
249
+ <ul id="backtrace-ul">
250
+ <% frames.each do |frame| %>
251
+ <li class="frame-info <%= frame_class(frame) %>">
252
+ <code><%=h frame.filename %></code> in
253
+ <code><strong><%=h frame.function %></strong></code>
254
+ </li>
255
+ <li class="code <%= frame_class(frame) %>">
256
+ <ol start="<%= frame.lineno %>" class="context">
257
+ <li class="context-line">
258
+ <code><%=h frame.context_line %></code>
259
+ </li>
260
+ </ol>
261
+ <div class="clear"></div>
262
+ </li>
263
+ <% end %>
264
+ </ul>
265
+ </div> <!-- /BACKTRACE -->
266
+ <div id="get">
267
+ <h3 id="get-info">GET</h3>
268
+ <% if req.GET and not req.GET.empty? %>
269
+ <table class="req">
270
+ <tr>
271
+ <th>Variable</th>
272
+ <th>Value</th>
273
+ </tr>
274
+ <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
275
+ <tr>
276
+ <td><%=h key %></td>
277
+ <td class="code"><div><%=h val.inspect %></div></td>
278
+ </tr>
279
+ <% } %>
280
+ </table>
281
+ <% else %>
282
+ <p class="no-data">No GET data.</p>
283
+ <% end %>
284
+ <div class="clear"></div>
285
+ </div> <!-- /GET -->
286
+ <div id="post">
287
+ <h3 id="post-info">POST</h3>
288
+ <% if req.POST and not req.POST.empty? %>
289
+ <table class="req">
290
+ <tr>
291
+ <th>Variable</th>
292
+ <th>Value</th>
293
+ </tr>
294
+ <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
295
+ <tr>
296
+ <td><%=h key %></td>
297
+ <td class="code"><div><%=h val.inspect %></div></td>
298
+ </tr>
299
+ <% } %>
300
+ </table>
301
+ <% else %>
302
+ <p class="no-data">No POST data.</p>
303
+ <% end %>
304
+ <div class="clear"></div>
305
+ </div> <!-- /POST -->
306
+ <div id="cookies">
307
+ <h3 id="cookie-info">COOKIES</h3>
308
+ <% unless req.cookies.empty? %>
309
+ <table class="req">
310
+ <tr>
311
+ <th>Variable</th>
312
+ <th>Value</th>
313
+ </tr>
314
+ <% req.cookies.each { |key, val| %>
315
+ <tr>
316
+ <td><%=h key %></td>
317
+ <td class="code"><div><%=h val.inspect %></div></td>
318
+ </tr>
319
+ <% } %>
320
+ </table>
321
+ <% else %>
322
+ <p class="no-data">No cookie data.</p>
323
+ <% end %>
324
+ <div class="clear"></div>
325
+ </div> <!-- /COOKIES -->
326
+ <div id="rack">
327
+ <h3 id="env-info">Rack ENV</h3>
328
+ <table class="req">
329
+ <tr>
330
+ <th>Variable</th>
331
+ <th>Value</th>
332
+ </tr>
333
+ <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
334
+ <tr>
335
+ <td><%=h key %></td>
336
+ <td class="code"><div><%=h val %></div></td>
337
+ </tr>
338
+ <% } %>
339
+ </table>
340
+ <div class="clear"></div>
341
+ </div> <!-- /RACK ENV -->
342
+ <p id="explanation">You're seeing this error because you have
343
+ enabled HaveAPI Extension <code>HaveAPI::Extensions::ExceptionMailer</code>.</p>
344
+ </div> <!-- /WRAP -->
345
+ </body>
346
+ </html>
347
+ END
348
+ )
346
349
  end
347
350
  end