noodall-ui 0.0.11 → 0.0.12
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.
- data/Gemfile.lock +13 -13
- data/config/cucumber.yml +1 -0
- data/features/group_access_control.feature +27 -0
- data/features/preview.feature +2 -2
- data/features/sitemap.feature +1 -1
- data/features/step_definitions/groups_access_steps.rb +40 -19
- data/features/step_definitions/node_steps.rb +1 -2
- data/features/step_definitions/pubish_content_steps.rb +2 -1
- data/features/support/cucumber.css +251 -0
- data/features/support/sevenfeatures.rb +565 -0
- data/lib/noodall/ui/version.rb +1 -1
- data/public/404.html +27 -0
- data/public/features.html +252 -0
- data/public/javascripts/tiny_mce/plugins/media/media.htm +1 -2
- metadata +8 -4
- data/features/gallery_component.feature +0 -22
@@ -0,0 +1,565 @@
|
|
1
|
+
begin
|
2
|
+
require 'cucumber/formatter/ordered_xml_markup'
|
3
|
+
require 'cucumber/formatter/duration'
|
4
|
+
require 'cucumber/formatter/io'
|
5
|
+
rescue LoadError
|
6
|
+
puts "Can't load SevenFeatures"
|
7
|
+
end
|
8
|
+
|
9
|
+
module Cucumber
|
10
|
+
module Formatter
|
11
|
+
class SevenFeatures
|
12
|
+
begin
|
13
|
+
include ERB::Util # for the #h method
|
14
|
+
include Duration
|
15
|
+
include Io
|
16
|
+
rescue NameError
|
17
|
+
puts "Can't load SevenFeatures"
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(step_mother, path_or_io, options)
|
21
|
+
@io = ensure_io(path_or_io, "html")
|
22
|
+
@step_mother = step_mother
|
23
|
+
@options = options
|
24
|
+
@buffer = {}
|
25
|
+
@builder = create_builder(@io)
|
26
|
+
@feature_number = 0
|
27
|
+
@scenario_number = 0
|
28
|
+
@step_number = 0
|
29
|
+
@header_red = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def embed(file, mime_type)
|
33
|
+
case(mime_type)
|
34
|
+
when /^image\/(png|gif|jpg|jpeg)/
|
35
|
+
embed_image(file)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def embed_image(file)
|
40
|
+
id = file.hash
|
41
|
+
@builder.span(:class => 'embed') do |pre|
|
42
|
+
pre << %{<a href="" onclick="img=document.getElementById('#{id}'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false">Screenshot</a><br>
|
43
|
+
<img id="#{id}" style="display: none" src="#{file}"/>}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def before_features(features)
|
49
|
+
@step_count = get_step_count(features)
|
50
|
+
|
51
|
+
# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
52
|
+
@builder.declare!(
|
53
|
+
:DOCTYPE,
|
54
|
+
:html,
|
55
|
+
:PUBLIC,
|
56
|
+
'-//W3C//DTD XHTML 1.0 Strict//EN',
|
57
|
+
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
|
58
|
+
)
|
59
|
+
|
60
|
+
@builder << '<html xmlns ="http://www.w3.org/1999/xhtml">'
|
61
|
+
@builder.head do
|
62
|
+
@builder.meta(:content => 'text/html;charset=utf-8')
|
63
|
+
@builder.title 'Cucumber: ' + File.basename(Dir.getwd)
|
64
|
+
inline_css
|
65
|
+
google_js
|
66
|
+
#inline_js
|
67
|
+
end
|
68
|
+
@builder << '<body>'
|
69
|
+
@builder << "<!-- Step count #{@step_count}-->"
|
70
|
+
@builder << '<div class="cucumber">'
|
71
|
+
@builder.div(:id => 'cucumber-header') do
|
72
|
+
@builder.div(:id => 'label') do
|
73
|
+
@builder.h1('Cucumber: ' + File.basename(Dir.getwd))
|
74
|
+
end
|
75
|
+
@builder.div(:id => 'summary') do
|
76
|
+
@builder.p('',:id => 'totals')
|
77
|
+
@builder.p('',:id => 'duration')
|
78
|
+
@builder.div(:id => 'expand-collapse') do
|
79
|
+
@builder.p('Collapse\Expand All', :id => 'collapser')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def after_features(features)
|
86
|
+
print_stats(features)
|
87
|
+
@builder << '</div>'
|
88
|
+
@builder << '</body>'
|
89
|
+
@builder << '</html>'
|
90
|
+
end
|
91
|
+
|
92
|
+
def before_feature(feature)
|
93
|
+
@exceptions = []
|
94
|
+
@builder << '<div class="feature collapsed">'
|
95
|
+
end
|
96
|
+
|
97
|
+
def after_feature(feature)
|
98
|
+
@builder << '</div>'
|
99
|
+
end
|
100
|
+
|
101
|
+
def before_comment(comment)
|
102
|
+
@builder << '<pre class="comment">'
|
103
|
+
end
|
104
|
+
|
105
|
+
def after_comment(comment)
|
106
|
+
@builder << '</pre>'
|
107
|
+
end
|
108
|
+
|
109
|
+
def comment_line(comment_line)
|
110
|
+
@builder.text!(comment_line)
|
111
|
+
@builder.br
|
112
|
+
end
|
113
|
+
|
114
|
+
def after_tags(tags)
|
115
|
+
@tag_spacer = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def tag_name(tag_name)
|
119
|
+
@current_tag = tag_name
|
120
|
+
end
|
121
|
+
|
122
|
+
def feature_name(keyword, name)
|
123
|
+
lines = name.split(/\r?\n/)
|
124
|
+
return if lines.empty?
|
125
|
+
@builder.h2 do |h2|
|
126
|
+
@builder.span(lines[0], :class => 'val')
|
127
|
+
end
|
128
|
+
@builder.p(:class => 'narrative') do
|
129
|
+
lines[1..-1].each do |line|
|
130
|
+
@builder.text!(line.strip)
|
131
|
+
@builder.br
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def before_background(background)
|
137
|
+
@in_background = true
|
138
|
+
@builder << '<div class="background">'
|
139
|
+
end
|
140
|
+
|
141
|
+
def after_background(background)
|
142
|
+
@in_background = nil
|
143
|
+
@builder << '</div>'
|
144
|
+
end
|
145
|
+
|
146
|
+
def background_name(keyword, name, file_colon_line, source_indent)
|
147
|
+
@listing_background = true
|
148
|
+
@builder.h3 do |h3|
|
149
|
+
@builder.span(keyword, :class => 'keyword')
|
150
|
+
@builder.text!(' ')
|
151
|
+
@builder.span(name, :class => 'val')
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def before_feature_element(feature_element)
|
156
|
+
@scenario_number+=1
|
157
|
+
@scenario_red = false
|
158
|
+
css_class = {
|
159
|
+
Ast::Scenario => 'scenario collapsed',
|
160
|
+
Ast::ScenarioOutline => 'scenario outline'
|
161
|
+
}[feature_element.class]
|
162
|
+
@builder << "<div class='#{css_class}'>"
|
163
|
+
end
|
164
|
+
|
165
|
+
def after_feature_element(feature_element)
|
166
|
+
@builder << '</div>'
|
167
|
+
@open_step_list = true
|
168
|
+
end
|
169
|
+
|
170
|
+
def scenario_name(keyword, name, file_colon_line, source_indent)
|
171
|
+
@listing_background = false
|
172
|
+
@builder.h3(:id => "scenario_#{@scenario_number}") do
|
173
|
+
unless @current_tag.nil?
|
174
|
+
@builder.span(@current_tag, :class => 'tag')
|
175
|
+
@builder.text!(' ')
|
176
|
+
@current_tag = nil
|
177
|
+
end
|
178
|
+
#@builder.span(keyword, :class => 'keyword')
|
179
|
+
@builder.text!(' ')
|
180
|
+
@builder.span(name, :class => 'val')
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def before_outline_table(outline_table)
|
185
|
+
@outline_row = 0
|
186
|
+
@builder << '<table>'
|
187
|
+
end
|
188
|
+
|
189
|
+
def after_outline_table(outline_table)
|
190
|
+
@builder << '</table>'
|
191
|
+
@outline_row = nil
|
192
|
+
end
|
193
|
+
|
194
|
+
def before_examples(examples)
|
195
|
+
@builder << '<div class="examples">'
|
196
|
+
end
|
197
|
+
|
198
|
+
def after_examples(examples)
|
199
|
+
@builder << '</div>'
|
200
|
+
end
|
201
|
+
|
202
|
+
def examples_name(keyword, name)
|
203
|
+
@builder.h4 do
|
204
|
+
@builder.span(keyword, :class => 'keyword')
|
205
|
+
@builder.text!(' ')
|
206
|
+
@builder.span(name, :class => 'val')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def before_steps(steps)
|
211
|
+
@builder << '<ol>'
|
212
|
+
end
|
213
|
+
|
214
|
+
def after_steps(steps)
|
215
|
+
@builder << '</ol>'
|
216
|
+
end
|
217
|
+
|
218
|
+
def before_step(step)
|
219
|
+
@step_id = step.dom_id
|
220
|
+
@step_number += 1
|
221
|
+
@step = step
|
222
|
+
end
|
223
|
+
|
224
|
+
def after_step(step)
|
225
|
+
move_progress
|
226
|
+
end
|
227
|
+
|
228
|
+
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
229
|
+
@step_match = step_match
|
230
|
+
@hide_this_step = false
|
231
|
+
if exception
|
232
|
+
if @exceptions.include?(exception)
|
233
|
+
@hide_this_step = true
|
234
|
+
return
|
235
|
+
end
|
236
|
+
@exceptions << exception
|
237
|
+
end
|
238
|
+
if status != :failed && @in_background ^ background
|
239
|
+
@hide_this_step = true
|
240
|
+
return
|
241
|
+
end
|
242
|
+
@status = status
|
243
|
+
return if @hide_this_step
|
244
|
+
set_scenario_color(status)
|
245
|
+
@builder << "<li id='#{@step_id}' class='step #{status}'>"
|
246
|
+
end
|
247
|
+
|
248
|
+
def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
249
|
+
return if @hide_this_step
|
250
|
+
@builder << '</li>'
|
251
|
+
end
|
252
|
+
|
253
|
+
def step_name(keyword, step_match, status, source_indent, background)
|
254
|
+
@step_matches ||= []
|
255
|
+
background_in_scenario = background && !@listing_background
|
256
|
+
@skip_step = @step_matches.index(step_match) || background_in_scenario
|
257
|
+
@step_matches << step_match
|
258
|
+
|
259
|
+
unless @skip_step
|
260
|
+
build_step(keyword, step_match, status)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def exception(exception, status)
|
265
|
+
# build_exception_detail(exception)
|
266
|
+
end
|
267
|
+
|
268
|
+
def extra_failure_content(file_colon_line)
|
269
|
+
@snippet_extractor ||= SnippetExtractor.new
|
270
|
+
"<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
|
271
|
+
end
|
272
|
+
|
273
|
+
def before_multiline_arg(multiline_arg)
|
274
|
+
return if @hide_this_step || @skip_step
|
275
|
+
if Ast::Table === multiline_arg
|
276
|
+
@builder << '<table>'
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def after_multiline_arg(multiline_arg)
|
281
|
+
return if @hide_this_step || @skip_step
|
282
|
+
if Ast::Table === multiline_arg
|
283
|
+
@builder << '</table>'
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def py_string(string)
|
288
|
+
return if @hide_this_step
|
289
|
+
@builder.pre(:class => 'val') do |pre|
|
290
|
+
@builder << string.gsub("\n", '
')
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
def before_table_row(table_row)
|
296
|
+
@row_id = table_row.dom_id
|
297
|
+
@col_index = 0
|
298
|
+
return if @hide_this_step
|
299
|
+
@builder << "<tr class='step' id='#{@row_id}'>"
|
300
|
+
end
|
301
|
+
|
302
|
+
def after_table_row(table_row)
|
303
|
+
return if @hide_this_step
|
304
|
+
@builder << '</tr>'
|
305
|
+
if table_row.exception
|
306
|
+
@builder.tr do
|
307
|
+
@builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
if @outline_row
|
312
|
+
@outline_row += 1
|
313
|
+
end
|
314
|
+
@step_number += 1
|
315
|
+
move_progress
|
316
|
+
end
|
317
|
+
|
318
|
+
def table_cell_value(value, status)
|
319
|
+
return if @hide_this_step
|
320
|
+
|
321
|
+
@cell_type = @outline_row == 0 ? :th : :td
|
322
|
+
attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
|
323
|
+
attributes[:class] += " #{status}" if status
|
324
|
+
build_cell(@cell_type, value, attributes)
|
325
|
+
set_scenario_color(status)
|
326
|
+
@col_index += 1
|
327
|
+
end
|
328
|
+
|
329
|
+
def announce(announcement)
|
330
|
+
@builder.pre(announcement, :class => 'announcement')
|
331
|
+
end
|
332
|
+
|
333
|
+
protected
|
334
|
+
|
335
|
+
def set_scenario_color(status)
|
336
|
+
if status == :undefined || status == :pending || status == :skipped
|
337
|
+
@builder.script do
|
338
|
+
@builder.text!("makeBlack('cucumber-header');") unless @header_red
|
339
|
+
@builder.text!("makeBlack('scenario_#{@scenario_number}');") unless @scenario_red
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
if status == :failed
|
344
|
+
@builder.script do
|
345
|
+
@builder.text!("makeRed('cucumber-header');") unless @header_red
|
346
|
+
@header_red = true
|
347
|
+
@builder.text!("makeRed('scenario_#{@scenario_number}');") unless @scenario_red
|
348
|
+
@scenario_red = true
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def get_step_count(features)
|
354
|
+
count = 0
|
355
|
+
features = features.instance_variable_get("@features")
|
356
|
+
features.each do |feature|
|
357
|
+
#get background steps
|
358
|
+
if feature.instance_variable_get("@background")
|
359
|
+
background = feature.instance_variable_get("@background").instance_variable_get("@steps").instance_variable_get("@steps")
|
360
|
+
count += background.size unless background.nil?
|
361
|
+
end
|
362
|
+
#get scenarios
|
363
|
+
feature.instance_variable_get("@feature_elements").each do |scenario|
|
364
|
+
#get steps
|
365
|
+
steps = scenario.instance_variable_get("@steps").instance_variable_get("@steps")
|
366
|
+
unless steps.nil?
|
367
|
+
count += steps.size unless steps.nil?
|
368
|
+
|
369
|
+
#get example table
|
370
|
+
examples = scenario.instance_variable_get("@examples_array")
|
371
|
+
unless examples.nil?
|
372
|
+
examples.each do |example|
|
373
|
+
example_matrix = example.instance_variable_get("@outline_table").instance_variable_get("@cell_matrix")
|
374
|
+
count += example_matrix.size unless example_matrix.nil?
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
#get multiline step tables
|
379
|
+
steps.each do |step|
|
380
|
+
multi_arg = step.instance_variable_get("@multiline_arg")
|
381
|
+
next if multi_arg.nil?
|
382
|
+
matrix = multi_arg.instance_variable_get("@cell_matrix")
|
383
|
+
count += matrix.size unless matrix.nil?
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
return count
|
389
|
+
end
|
390
|
+
|
391
|
+
def build_step(keyword, step_match, status)
|
392
|
+
step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
|
393
|
+
@builder.div do |div|
|
394
|
+
@builder.span(keyword, :class => 'keyword')
|
395
|
+
@builder.text!(' ')
|
396
|
+
@builder.span(:class => 'step val') do |name|
|
397
|
+
name << h(step_name).gsub(/<span class="(.*?)">/, '<span class="\1">').gsub(/<\/span>/, '</span>')
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def build_cell(cell_type, value, attributes)
|
403
|
+
@builder.__send__(cell_type, attributes) do
|
404
|
+
@builder.div do
|
405
|
+
@builder.span(value,:class => 'step param')
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def inline_css
|
411
|
+
@builder.style(:type => 'text/css') do
|
412
|
+
@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def google_js
|
417
|
+
@builder.script(:type => 'text/javascript', :src => 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js') do
|
418
|
+
@builder << ' '
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def inline_js
|
423
|
+
@builder.script(:type => 'text/javascript') do
|
424
|
+
@builder << inline_js_content
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
def inline_js_content
|
429
|
+
<<-EOF
|
430
|
+
function moveProgressBar(percentDone) {
|
431
|
+
document.getElementById("cucumber-header").style.width = percentDone +"%";
|
432
|
+
}
|
433
|
+
function makeRed(element_id) {
|
434
|
+
document.getElementById(element_id).style.color = '#C40D0D';
|
435
|
+
}
|
436
|
+
|
437
|
+
function makeBlack(element_id) {
|
438
|
+
document.getElementById(element_id).style.background = '#FFFFFF';
|
439
|
+
document.getElementById(element_id).style.color = '#000000';
|
440
|
+
}
|
441
|
+
|
442
|
+
$(document).ready(function() {
|
443
|
+
$('.feature h2').click(function() {
|
444
|
+
$(this).parent('.feature').toggleClass('collapsed');
|
445
|
+
});
|
446
|
+
|
447
|
+
|
448
|
+
$("#collapser").css('cursor', 'pointer');
|
449
|
+
$("#collapser").click(function() {
|
450
|
+
$('.feature').toggleClass('collapsed');
|
451
|
+
$('.scenario').toggleClass('collapsed');
|
452
|
+
});
|
453
|
+
|
454
|
+
$('.scenario h3').click(function() {
|
455
|
+
$(this).parent('.scenario').toggleClass('collapsed');
|
456
|
+
});
|
457
|
+
});
|
458
|
+
|
459
|
+
EOF
|
460
|
+
end
|
461
|
+
|
462
|
+
def move_progress
|
463
|
+
@builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
|
464
|
+
end
|
465
|
+
|
466
|
+
def percent_done
|
467
|
+
result = 100.0
|
468
|
+
if @step_count != 0
|
469
|
+
result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
|
470
|
+
end
|
471
|
+
result
|
472
|
+
end
|
473
|
+
|
474
|
+
def backtrace_line(line)
|
475
|
+
line.gsub(/^([^:]*\.(?:rb|feature|haml)):(\d*)/) do
|
476
|
+
if ENV['TM_PROJECT_DIRECTORY']
|
477
|
+
"<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
|
478
|
+
else
|
479
|
+
line
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
def print_stats(features)
|
485
|
+
@builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in #{format_duration(features.duration)} seconds\";</script>"
|
486
|
+
@builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
|
487
|
+
end
|
488
|
+
|
489
|
+
def print_stat_string(features)
|
490
|
+
string = String.new
|
491
|
+
string << dump_count(@step_mother.scenarios.length, "scenario")
|
492
|
+
scenario_count = print_status_counts{|status| @step_mother.scenarios(status)}
|
493
|
+
string << scenario_count if scenario_count
|
494
|
+
string << "<br />"
|
495
|
+
string << dump_count(@step_mother.steps.length, "step")
|
496
|
+
step_count = print_status_counts{|status| @step_mother.steps(status)}
|
497
|
+
string << step_count if step_count
|
498
|
+
end
|
499
|
+
|
500
|
+
def print_status_counts
|
501
|
+
counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
|
502
|
+
elements = yield status
|
503
|
+
elements.any? ? "#{elements.length} #{status.to_s}" : nil
|
504
|
+
end.compact
|
505
|
+
return " (#{counts.join(', ')})" if counts.any?
|
506
|
+
end
|
507
|
+
|
508
|
+
def dump_count(count, what, state=nil)
|
509
|
+
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
|
510
|
+
end
|
511
|
+
|
512
|
+
def create_builder(io)
|
513
|
+
OrderedXmlMarkup.new(:target => io, :indent => 0)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
|
520
|
+
class SnippetExtractor #:nodoc:
|
521
|
+
class NullConverter; def convert(code, pre); code; end; end #:nodoc:
|
522
|
+
begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
|
523
|
+
|
524
|
+
def snippet(error)
|
525
|
+
raw_code, line = snippet_for(error[0])
|
526
|
+
highlighted = @@converter.convert(raw_code, false)
|
527
|
+
highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
|
528
|
+
post_process(highlighted, line)
|
529
|
+
end
|
530
|
+
|
531
|
+
def snippet_for(error_line)
|
532
|
+
if error_line =~ /(.*):(\d+)/
|
533
|
+
file = $1
|
534
|
+
line = $2.to_i
|
535
|
+
[lines_around(file, line), line]
|
536
|
+
else
|
537
|
+
return snippet_for()
|
538
|
+
["# Couldn't get snippet for #{error_line}", 1]
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
def lines_around(file, line)
|
543
|
+
if File.file?(file)
|
544
|
+
lines = File.open(file).read.split("\n")
|
545
|
+
min = [0, line-3].max
|
546
|
+
max = [line+1, lines.length-1].min
|
547
|
+
selected_lines = []
|
548
|
+
selected_lines.join("\n")
|
549
|
+
lines[min..max].join("\n")
|
550
|
+
else
|
551
|
+
"# Couldn't get snippet for #{file}"
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
def post_process(highlighted, offending_line)
|
556
|
+
new_lines = []
|
557
|
+
highlighted.split("\n").each_with_index do |line, i|
|
558
|
+
new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
|
559
|
+
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
|
560
|
+
new_lines << new_line
|
561
|
+
end
|
562
|
+
new_lines.join("\n")
|
563
|
+
end
|
564
|
+
|
565
|
+
end
|
data/lib/noodall/ui/version.rb
CHANGED
data/public/404.html
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/404.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
23
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
27
|
+
|