ruber 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGES +42 -1
  2. data/lib/ruber/application/application.rb +25 -5
  3. data/lib/ruber/application/plugin.yaml +2 -10
  4. data/lib/ruber/component_manager.rb +2 -2
  5. data/lib/ruber/document_project.rb +5 -3
  6. data/lib/ruber/editor/document.rb +5 -4
  7. data/lib/ruber/editor/ktexteditor_wrapper.rb +1 -1
  8. data/lib/ruber/exception_widgets.rb +1 -1
  9. data/lib/ruber/main_window/main_window.rb +4 -3
  10. data/lib/ruber/main_window/main_window_actions.rb +2 -2
  11. data/lib/ruber/main_window/main_window_internal.rb +1 -1
  12. data/lib/ruber/output_widget.rb +17 -5
  13. data/lib/ruber/project.rb +34 -3
  14. data/lib/ruber/project_dir_scanner.rb +171 -0
  15. data/lib/ruber/settings_container.rb +7 -7
  16. data/lib/ruber/settings_dialog.rb +24 -24
  17. data/lib/ruber/version.rb +1 -1
  18. data/lib/ruber/world/environment.rb +1 -0
  19. data/lib/ruber/world/plugin.yaml +7 -2
  20. data/lib/ruber/{application → world}/project_files_widget.rb +0 -0
  21. data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.rb +2 -2
  22. data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.ui +0 -0
  23. data/lib/ruber/{application → world}/ui/project_files_widget.rb +2 -2
  24. data/lib/ruber/{application → world}/ui/project_files_widget.ui +0 -0
  25. data/plugins/auto_end/auto_end.rb +21 -18
  26. data/plugins/autosave/autosave.rb +1 -1
  27. data/plugins/find_in_files/find_in_files.rb +1 -1
  28. data/plugins/irb/irb.png +0 -0
  29. data/plugins/irb/irb.rb +142 -0
  30. data/plugins/irb/irb.svg +240 -0
  31. data/plugins/irb/irb_controller.rb +541 -0
  32. data/plugins/irb/irbrc.rb +21 -0
  33. data/plugins/irb/plugin.yaml +19 -0
  34. data/plugins/irb/ui/irb_config_widget.rb +151 -0
  35. data/plugins/irb/ui/irb_config_widget.ui +123 -0
  36. data/plugins/irb/ui/irb_tool_widget.rb +97 -0
  37. data/plugins/irb/ui/irb_tool_widget.ui +86 -0
  38. data/plugins/project_browser/project_browser.rb +1 -1
  39. data/plugins/rspec/plugin.yaml +6 -3
  40. data/plugins/rspec/rspec.rb +172 -473
  41. data/plugins/rspec/tool_widget.rb +462 -0
  42. data/plugins/rspec/ui/rspec_project_widget.rb +58 -38
  43. data/plugins/rspec/ui/rspec_project_widget.ui +68 -64
  44. data/plugins/ruberri/class_formatter.rb +126 -0
  45. data/plugins/ruberri/method_formatter.rb +90 -0
  46. data/plugins/ruberri/plugin.yaml +13 -0
  47. data/plugins/ruberri/ruberri.rb +226 -0
  48. data/plugins/ruberri/search.rb +111 -0
  49. data/plugins/ruberri/ui/tool_widget.rb +73 -0
  50. data/plugins/ruberri/ui/tool_widget.ui +49 -0
  51. data/plugins/ruby_runner/ruby_runner.rb +2 -2
  52. data/plugins/ruby_syntax_checker/plugin.yaml +11 -0
  53. data/plugins/ruby_syntax_checker/ruby_syntax_checker.rb +147 -0
  54. data/plugins/syntax_checker/plugin.yaml +10 -6
  55. data/plugins/syntax_checker/syntax_checker.rb +216 -520
  56. data/plugins/syntax_checker/ui/config_widget.rb +61 -0
  57. data/plugins/syntax_checker/ui/config_widget.ui +44 -0
  58. data/plugins/yaml_syntax_checker/plugin.yaml +11 -0
  59. data/plugins/yaml_syntax_checker/yaml_syntax_checker.rb +62 -0
  60. data/ruber.desktop +0 -0
  61. data/spec/auto_end_spec.rb +224 -186
  62. data/spec/document_project_spec.rb +9 -1
  63. data/spec/document_spec.rb +9 -0
  64. data/spec/environment_spec.rb +12 -0
  65. data/spec/output_widget_spec.rb +69 -2
  66. data/spec/project_dir_scanner_spec.rb +195 -0
  67. data/spec/project_spec.rb +43 -73
  68. data/spec/ruby_syntax_checker_spec.rb +361 -0
  69. data/spec/syntax_checker_spec.rb +1132 -0
  70. data/spec/yaml_syntax_checker_spec.rb +130 -0
  71. metadata +232 -225
  72. data/lib/ruber/application/project_files_list.rb +0 -320
  73. data/spec/project_files_list_spec.rb +0 -411
@@ -74,7 +74,7 @@ otherwise
74
74
  return true if @project.nil? or !@do_filtering
75
75
  it = source_model.item_for_index source_model.index(row,0,parent)
76
76
  return true if it.dir?
77
- @project.project_files.file_in_project? it.local_path
77
+ @project.file_in_project? it.local_path
78
78
  end
79
79
 
80
80
  =begin rdoc
@@ -7,7 +7,7 @@ about:
7
7
  bug_address: http://github.com/stcrocco/ruber/issues
8
8
  icon: rspec.png
9
9
  class: Ruber::RSpec::Plugin
10
- require: rspec
10
+ require: [rspec, tool_widget]
11
11
  deps: [ruby_development, autosave]
12
12
  ui_file: rspecui.rc
13
13
  config_options:
@@ -20,16 +20,19 @@ project_widgets:
20
20
  - {caption: RSpec, class: 'Ruber::RSpec::ProjectWidget', pixmap: rspec.png}
21
21
  project_options:
22
22
  rspec:
23
+ code_directory: {default: "File.join('lib', self[:general, :project_name].downcase.sub(/\s+/, '_'))", relative_path: true}
23
24
  executable: {default: 'Ruber::RSpec::Plugin.find_default_executable'}
24
25
  options: {default: []}
25
26
  spec_directory: {relative_path: true, default: spec}
27
+ patterns: {default: [{:code: **/*.rb, :spec: %f_spec.rb}]}
26
28
  spec_files: {default: '*_spec.rb'}
27
- spec_pattern: {default: ['%f_spec.rb']}
28
29
  full_backtraces: {default: true}
30
+ extensions:
31
+ rspec: {class: Ruber::RSpec::ProjectExtension}
29
32
  tool_widgets: {class: 'Ruber::RSpec::ToolWidget', pixmap: rspec.png, caption: RSpec}
30
33
  actions:
31
34
  rspec-switch: {text: 'Switch to &Spec', shortcut: 'Shift+F12', slot: switch(), states: [active_project_exists, current_document]}
32
35
  rspec-run_all: {text: Run &Project Specs, shortcut: 'Alt+Shift+R, P', slot: run_all(), states: [active_project_exists, rspec_running]}
33
- rspec-run_current: {text: Run Specs for &Current File, shortcut: 'Alt+Shift+R, C', slot: run_current(), states: [active_project_exists, current_document, rspec_running]}
36
+ rspec-run_current: {text: Run Specs for &Current File, shortcut: 'Alt+Shift+R, C', slot: run_current_document(), states: [active_project_exists, current_document, rspec_running]}
34
37
  rspec-run_current_line: {text: Run Current Spec, shortcut: 'Alt+Shift+R, L', slot: run_current_line(), states: [active_project_exists, current_document, rspec_running]}
35
38
  rspec-stop: {text: S&top, shortcut: Esc, icon: process-stop, slot: stop_process(), state: rspec_running}
@@ -84,6 +84,8 @@ Symbolic values associated with the @rspec/switch_behaviour@ settings
84
84
  =end
85
85
  SWITCH_BEHAVIOUR = [:new_tab, :horizontal, :vertical]
86
86
 
87
+ signals :settings_changed
88
+
87
89
  =begin rdoc
88
90
  Finds the rspec program to use by default
89
91
 
@@ -133,7 +135,7 @@ the plugin
133
135
  end
134
136
 
135
137
  =begin rdoc
136
- Whether a file is a spec file or a code file for a given project
138
+ Whether or not a file is a spec file for a given project
137
139
 
138
140
  It uses the @rspec/spec_directory@ and @rspec/spec_pattern@ options from the project
139
141
  to find out whether the file is a spec file or not.
@@ -146,9 +148,24 @@ to find out whether the file is a spec file or not.
146
148
  =end
147
149
  def spec_file? file, prj = Ruber[:world].active_project
148
150
  return nil unless prj
149
- dir = prj[:rspec, :spec_directory, :absolute]
150
- return false unless file.start_with? dir
151
- File.fnmatch File.join(dir, prj[:rspec, :spec_files]), file
151
+ prj.extension(:rspec).spec_file? file
152
+ end
153
+
154
+ =begin rdoc
155
+ Whether or not file is a code file for a given project
156
+
157
+ It uses the @rspec/spec_directory@ and @rspec/spec_pattern@ options from the project
158
+ to find out whether the file is a spec file or not.
159
+
160
+ @param [String] file the file to test
161
+ @param [Project,nil] prj the project _file_ could be a spec for. If *nil*, the
162
+ current project, if any, will be used
163
+ @return [Boolean,nil] wheter or not _file_ is a spec file for the given project
164
+ or *nil* if no project was specified and there's no open project
165
+ =end
166
+ def code_file? file, prj = Ruber[:world].active_project
167
+ return nil unless prj
168
+ prj.extension(:rspec).code_file? file
152
169
  end
153
170
 
154
171
  =begin rdoc
@@ -220,6 +237,22 @@ as first argument to {Autosave::AutosavePlugin#autosave}
220
237
  true
221
238
  end
222
239
 
240
+ def load_settings
241
+ super
242
+ emit settings_changed
243
+ end
244
+
245
+ def spec_for_pattern pattern, file
246
+ spec = pattern[:spec].gsub(/%f/, File.basename(file, '.rb'))
247
+ dir = File.dirname(file)
248
+ dir_parts = dir.split '/'
249
+ spec.gsub! %r{%d\d+} do |str|
250
+ dir_parts[str[2..-1].to_i-1] || ''
251
+ end
252
+ spec.gsub! %r{%d}, dir
253
+ spec
254
+ end
255
+
223
256
  private
224
257
 
225
258
  =begin rdoc
@@ -312,7 +345,7 @@ Runs all the specs for the project.
312
345
  return
313
346
  end
314
347
  opts = options prj
315
- opts[:files] = Dir.glob File.join(opts[:specs_dir], opts[:filter])
348
+ opts[:files] = Dir.glob File.join(opts[:specs_dir], '**', opts[:filter])
316
349
  run_rspec_for prj, opts, :files => :project_files, :on_failure => :ask, :message => 'Do you want to run the tests all the same?'
317
350
  nil
318
351
  end
@@ -335,34 +368,30 @@ which, most likely, will cause it to fail.
335
368
 
336
369
  @return [Boolean] *true* if the spec program is started and *false* otherwise
337
370
  (including the case when the process was already running or autosaving failed)
338
- =end
339
- def run_current what = :all
371
+ =end
372
+ def run_current_document
373
+ doc = Ruber[:world].active_document
374
+ unless doc
375
+ raise "It shouldn't be possible to call #{self.class}#run_current_document when there's no active document"
376
+ end
340
377
  prj = Ruber[:world].active_project
341
- unless prj
342
- KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
343
- return
378
+ unless doc
379
+ raise "It shouldn't be possible to call #{self.class}#run_current_document when there's no active project"
344
380
  end
345
381
  opts = options prj
346
- view = Ruber[:main_window].active_editor
347
- doc = view.document
348
- unless doc.url.local_file?
349
- KDE::MessageBox.sorry nil, 'You can\'t run rspec for remote files'
350
- return
351
- end
352
- unless doc
353
- KDE::MessageBox.error nil, "You must have an open editor to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
382
+ ext = prj.extension(:rspec)
383
+ if doc.path.empty?
384
+ KDE::MessageBox.sorry nil, KDE.i18n("You must save the document to a file before running rspec on it")
354
385
  return
355
- end
356
- files = specs_for_file opts, doc.path
357
- files.reject!{|f| !File.exist? f}
358
- opts[:files] = files.empty? ? [doc.path] : files
359
- if what == :current_line
360
- line = view.cursor_position.line + 1
361
- opts[:spec_options] += ["-l", line.to_s]
386
+ elsif ext.spec_file? doc.path
387
+ opts[:files] = [doc.path]
388
+ elsif ext.code_file?(doc.path)
389
+ opts[:files] = ext.specs_for_code doc.path
362
390
  end
363
391
  run_rspec_for prj, opts, :files => :documents_with_file, :on_failure => :ask,
364
392
  :message => 'Do you want to run the tests all the same?'
365
393
  end
394
+ slots :run_current_document
366
395
 
367
396
  =begin rdoc
368
397
  Runs the example(s) in the current line
@@ -376,7 +405,37 @@ current file is the example file, not the source.
376
405
  (including the case when the process was already running or autosaving failed)
377
406
  =end
378
407
  def run_current_line
379
- run_current :current_line
408
+ doc = Ruber[:world].active_document
409
+ unless doc
410
+ raise "It shouldn't be possible to call #{self.class}#run_current_document when there's no active document"
411
+ end
412
+ prj = Ruber[:world].active_project
413
+ unless doc
414
+ raise "It shouldn't be possible to call #{self.class}#run_current_document when there's no active project"
415
+ end
416
+ opts = options prj
417
+ ext = prj.extension(:rspec)
418
+ if doc.path.empty?
419
+ KDE::MessageBox.sorry nil, KDE.i18n("You must save the document to a file before running rspec on it")
420
+ return
421
+ elsif ext.spec_file? doc.path
422
+ view = Ruber[:main_window].active_editor
423
+ elsif ext.code_file?(doc.path)
424
+ specs = ext.specs_for_code doc.path
425
+ view = Ruber[:world].active_environment.views.find do |v|
426
+ specs.include? v.document.path
427
+ end
428
+ unless view
429
+ KDE::MessageBox.sorry nil, KDE.i18n('You don\'t have any spec file for %s opened. Without it it\'s impossible to find out what the current line is', doc.path)
430
+ return
431
+ end
432
+ doc = view.document
433
+ end
434
+ opts[:files] = [view.document.path]
435
+ line = view.cursor_position.line + 1
436
+ opts[:spec_options] += ["-l", line.to_s]
437
+ run_rspec_for prj, opts, :files => :documents_with_file, :on_failure => :ask,
438
+ :message => 'Do you want to run the tests all the same?'
380
439
  end
381
440
 
382
441
  =begin rdoc
@@ -431,7 +490,6 @@ the project directory.
431
490
  res[:spec_options] = prj[:rspec, :options]
432
491
  res[:specs_dir] = prj[:rspec, :spec_directory, :absolute]
433
492
  res[:filter] = prj[:rspec, :spec_files]
434
- res[:pattern] = prj[:rspec, :spec_pattern]
435
493
  res[:dir] = prj.project_directory
436
494
  res[:full_backtraces] = prj[:rspec, :full_backtraces]
437
495
  res
@@ -452,8 +510,9 @@ It does nothing if the file corresponding to the current document isn't found
452
510
  def switch
453
511
  file = Ruber[:main_window].current_document.path
454
512
  prj = Ruber[:world].active_project
455
- if spec_file? file, prj then switch_to = file_for_spec prj, file
456
- else switch_to = specs_for_file(options(prj), file)[0]
513
+ ext = prj.extension(:rspec)
514
+ if ext.spec_file? file then ;switch_to = ext.code_for_spec file
515
+ else switch_to = ext.specs_for_code(file)[0]
457
516
  end
458
517
  if switch_to and File.exist? switch_to
459
518
  behaviour = Ruber[:config][:rspec, :switch_behaviour]
@@ -466,41 +525,6 @@ It does nothing if the file corresponding to the current document isn't found
466
525
  end
467
526
  slots :switch
468
527
 
469
- =begin rdoc
470
- Determines all possible specs files associated with a code file
471
-
472
- The names of the possible spec files are obtained replacing the @%f@ tag in each
473
- entry of the @rspec/pattern@ setting with the name of the file (without checking
474
- whether the files actually exist).
475
-
476
- @param [String] file the name of the code file
477
- @return [<String>] the names of the possible spec file associated with _file_
478
- =end
479
- def specs_for_file opts, file
480
- file = File.basename file, '.rb'
481
- res = opts[:pattern].map{|i| File.join opts[:specs_dir], i.gsub('%f', file)}
482
- res
483
- end
484
-
485
- =begin rdoc
486
- The name of the code file associated with a given spec file
487
-
488
- To find out which code file is associated with the given spec file, this method
489
- takes all the project files and constructs the file names of all the specs associated
490
- to them according to the @rspec/spec_pattern@ project option. As soon as one of
491
- the generated file names matches the given spec file, the generating file is returned.
492
-
493
- @param [Ruber::AbstractProject] prj the project containing the settings to use
494
- @param [String] file the name of the spec file to find the code file for
495
- @return [String] the absolute path of a file _file_ is a spec of, according
496
- to the settings contained in _prj_.
497
- =end
498
- def file_for_spec prj, file
499
- pattern = prj[:spec_pattern]
500
- opts = options prj
501
- prj.project_files.abs.find{|f| specs_for_file( opts, f).include? file}
502
- end
503
-
504
528
  =begin rdoc
505
529
  Override of {ExternalProgramPlugin#display_exit_message}
506
530
 
@@ -527,8 +551,9 @@ signal.
527
551
  @return [nil]
528
552
  =end
529
553
  def change_switch_name doc
530
- return unless doc
531
- if spec_file? doc.path then text = 'Switch to &Code'
554
+ prj = Ruber[:world].active_project
555
+ return unless doc and prj
556
+ if prj.extension(:rspec).spec_file? doc.path then text = 'Switch to &Code'
532
557
  else text = 'Switch to &Spec'
533
558
  end
534
559
  action_collection.action('rspec-switch').text = i18n(text)
@@ -538,431 +563,73 @@ signal.
538
563
 
539
564
  end
540
565
 
541
- =begin rdoc
542
- Filter model used by the RSpec output widget
543
-
544
- It allows to choose whether to accept items corresponding to output to standard error or to reject
545
- it. To find out if a given item corresponds to the output of standard error or
546
- standard output, this model uses the data contained in a custom role in the output.
547
- The index of this role is {RSpec::OutputWidget::OutputTypeRole}.
548
- =end
549
- class FilterModel < FilteredOutputWidget::FilterModel
550
-
551
- slots 'toggle_display_stderr(bool)'
552
-
553
- =begin rdoc
554
- Whether output from standard error should be displayed or not
555
- @return [Boolean]
556
- =end
557
- attr_reader :display_stderr
558
-
559
- =begin rdoc
560
- Create a new instance
561
-
562
- The new instance is set not to show the output from standard error
563
-
564
- @param [Qt::Object, nil] parent the parent object
565
- =end
566
- def initialize parent = nil
567
- super
568
- @display_stderr = false
569
- end
570
-
571
- =begin rdoc
572
- Sets whether to display or ignore items corresponding to output to standard error
573
-
574
- If this choice has changed, the model is invalidated.
575
-
576
- @param [Boolean] val whether to display or ignore the output to standard error
577
- @return [Boolean] _val_
578
- =end
579
- def display_stderr= val
580
- old, @display_stderr = @display_stderr, val
581
- invalidate if old != @display_stderr
582
- @display_standard_error
583
- end
584
- alias_method :toggle_display_stderr, :display_stderr=
585
-
586
- =begin rdoc
587
- Override of {FilteredOutputWidget::FilterModel#filterAcceptsRow}
588
-
589
- According to the value of {#display_stderr}, it can filter out items corresponding
590
- to standard error. In all other respects, it behaves as the base class method.
591
- @param [Integer] r the row number
592
- @param [Qt::ModelIndex] parent the parent index
593
- @return [Boolean] *true* if the row should be displayed and *false* otherwise
594
- =end
595
- def filterAcceptsRow r, parent
596
- if !@display_stderr
597
- idx = source_model.index(r,0,parent)
598
- return false if idx.data(OutputWidget::OutputTypeRole).to_string == 'output1'
599
- end
600
- super
601
- end
602
-
603
- end
604
566
 
605
- =begin rdoc
606
- Tool widget used by the rspec plugin.
607
-
608
- It displays the output from the spec program in a multi column tree. The name of
609
- failing or pending examples are displayed in a full line; all other information,
610
- such as the location of the example, the error message and so on are displayed
611
- in child items.
612
-
613
- While the examples are being run, a progress bar is shown.
614
- =end
615
- class ToolWidget < FilteredOutputWidget
616
-
617
- slots :spec_started, 'spec_finished(int, QString)'
618
-
619
- =begin rdoc
620
- @param [Qt::Widget, nil] parent the parent widget
621
- =end
622
- def initialize parent = nil
623
- super parent, :view => :tree, :filter => FilterModel.new
624
- @ignore_word_wrap_option = true
625
- view.text_elide_mode = Qt::ElideNone
626
- model.append_column [] if model.column_count < 2
627
- @progress_bar = Qt::ProgressBar.new(self){|w| w.hide}
628
- layout.add_widget @progress_bar, 2,0
629
- view.header_hidden = true
630
- view.header.resize_mode = Qt::HeaderView::ResizeToContents
631
- connect Ruber[:rspec], SIGNAL(:process_started), self, SLOT(:spec_started)
632
- connect Ruber[:rspec], SIGNAL('process_finished(int, QString)'), self, SLOT('spec_finished(int, QString)')
633
- filter.connect(SIGNAL('rowsInserted(QModelIndex, int, int)')) do |par, st, en|
634
- if !par.valid?
635
- st.upto(en) do |i|
636
- view.set_first_column_spanned i, par, true
637
- end
638
- end
639
- end
640
- #without this, the horizontal scrollbars won't be shown
641
- view.connect(SIGNAL('expanded(QModelIndex)')) do |_|
642
- view.resize_column_to_contents 1
643
- end
644
- view.connect(SIGNAL('collapsed(QModelIndex)')) do |_|
645
- view.resize_column_to_contents 1
646
- end
647
- setup_actions
648
- end
649
-
650
- =begin rdoc
651
- Displays the data relative to an example in the widget
652
-
653
- Actually, this method simply passes its argument to a more specific method, depending
654
- on the data it contains.
655
-
656
- @param [Hash] data a hash containing the data describing the results of running
657
- the example. This hash must contain the @:type@ key, which tells which kind of
658
- event the hash describes. The other entries change depending on the method which
659
- will be called, which is determined according to the @:type@ entry:
660
- * @:success@: {#display_successful_example}
661
- * @:failure@: {#display_failed_example}
662
- * @:pending@: {#display_pending_example}
663
- * @:new_example@: {#change_current_example}
664
- * @:start@: {#set_example_count}
665
- * @:summary@: {#display_summary}
666
- If the @:type@ entry doesn't have one of the previous values, the hash will be
667
- converted to a string and displayed in the widget
668
- =end
669
- def display_example data
670
- unless data.is_a?(Hash)
671
- model.insert_lines data.to_s, :output, nil
672
- return
673
- end
674
- case data[:type]
675
- when :success then display_successful_example data
676
- when :failure then display_failed_example data
677
- when :pending then display_pending_example data
678
- when :new_example then change_current_example data
679
- when :start then set_example_count data
680
- when :summary then display_summary data
681
- else model.insert_lines data.to_s, :output, nil
682
- end
683
- end
567
+ class ProjectExtension < Qt::Object
684
568
 
685
- =begin rdoc
686
- Changes the current example
687
-
688
- Currently, this only affects the tool tip displayed by the progress bar.
689
-
690
- @param [Hash] data the data to use. It must contain the @:description@ entry,
691
- which contains the text of the tool tip to use.
692
- @return [nil]
693
- =end
694
- def change_current_example data
695
- @progress_bar.tool_tip = data[:description]
696
- nil
697
- end
569
+ include Ruber::Extension
698
570
 
699
- =begin rdoc
700
- Sets the number of examples found by the spec program.
701
-
702
- This is used to set the maximum value of the progress bar.
703
-
704
- @param [Hash] data the data to use. It must contain the @:count@ entry,
705
- which contains the number of examples
706
- @return [nil]
707
- =end
708
- def set_example_count data
709
- @progress_bar.maximum = data[:count]
710
- nil
711
- end
712
-
713
-
714
- =begin rdoc
715
- Updates the progress bar by incrementing its value by one
716
-
717
- @param [Hash] data the data to use. Currently it's unused
718
- @return [nil]
719
- =end
720
- def display_successful_example data
721
- @progress_bar.value += 1
722
- nil
571
+ def initialize prj
572
+ super
573
+ @project = prj
574
+ @categories = {}
575
+ connect Ruber[:rspec], SIGNAL(:settings_changed), self, SLOT(:clear)
723
576
  end
724
577
 
725
- =begin rdoc
726
- Displays information about a failed example in the tool widget.
727
-
728
- @param [Hash] data the data about the example.
729
-
730
- @option data [String] :location the line number where the error occurred
731
- @option data [String] :description the name of the failed example
732
- @option data [String] :message the explaination of why the example failed
733
- @option data [String] :exception the content of the exception
734
- @option data [String] :backtrace the backtrace of the exception (a single new-line separated string)
735
- @return [nil]
736
- =end
737
- def display_failed_example data
738
- @progress_bar.value += 1
739
- top = model.insert("[FAILURE] #{data[:description]}", :error, nil).first
740
- model.insert ['From:', data[:location]], :message, nil, :parent => top
741
- ex_label = model.insert('Exception:', :message, nil, :parent => top).first
742
- exception_body = "#{data[:message]} (#{data[:exception]})".split_lines.delete_if{|l| l.strip.empty?}
743
- #exception_body may contain more than one line and some of them may be empty
744
- model.set exception_body.shift, :message, ex_label.row, :col => 1, :parent => top
745
- exception_body.each do |l|
746
- unless l.strip.empty?
747
- model.set l, :message, top.row_count, :col => 1, :parent => top
578
+ def specs_for_code file
579
+ return [] unless @project.file_in_project? file
580
+ return [] unless code_file? file
581
+ file.sub(@project.project_directory + '/', '')
582
+ res = []
583
+ @project[:rspec, :patterns].each do |pn|
584
+ if File.fnmatch pn[:code], file
585
+ basename = Ruber[:rspec].spec_for_pattern pn, file
586
+ spec = File.join(@project.project_directory, @project[:rspec, :spec_directory], basename)
587
+ res << spec
748
588
  end
749
589
  end
750
- backtrace = data[:backtrace].split_lines
751
- back_label, back = model.insert(['Backtrace:', backtrace.shift], :message, nil, :parent => top)
752
- backtrace.each do |l|
753
- model.insert [nil, l], :message, nil, :parent => back_label
754
- end
755
- top_index = filter.map_from_source(top.index)
756
- view.collapse top_index
757
- view.set_first_column_spanned top_index.row, Qt::ModelIndex.new, true
758
- view.expand filter.map_from_source(back_label.index)
759
- nil
590
+ res.select{|f| File.exist? f}
760
591
  end
761
592
 
762
- =begin rdoc
763
- Displays information about a pending example in the tool widget
764
-
765
- @param [Hash] data
766
- @option data [String] :location the line number where the error occurred
767
- @option data [String] :description the name of the failed example
768
- @option data [String] :message the explaination of why the example failed
769
- @return [nil]
770
- =end
771
- def display_pending_example data
772
- @progress_bar.value += 1
773
- top = model.insert("[PENDING] #{data[:description]}", :warning, nil)[0]
774
- model.insert ['From:', data[:location]], :message, nil, :parent => top
775
- model.insert ['Message: ', "#{data[:message]} (#{data[:exception]})"], :message, nil, :parent => top
776
- nil
777
- end
778
-
779
- =begin rdoc
780
- Displays a summary of the spec run in the tool widget
781
-
782
- The summary is a single title line which contains the number or successful, pending
783
- and failed example.
784
-
785
- @param [Hash] data
786
- @option data [Integer] :total the number of run examples
787
- @option data [Integer] :passed the number of passed examples
788
- @option data [Integer] :failed the number of failed examples
789
- @option data [Integer] :pending the number of pending examples
790
- @return [nil]
791
- =end
792
- def display_summary data
793
- @progress_bar.hide
794
- if data[:passed] == data[:total]
795
- self.title = "[SUMMARY] All #{data[:total]} examples passed"
796
- set_output_type model.index(0,0), :message_good
797
- else
798
- text = "[SUMMARY] Examples: #{data[:total]}"
799
- text << " Failed: #{data[:failure]}" if data[:failure] > 0
800
- text << " Pending: #{data[:pending]}" if data[:pending] > 0
801
- text << " Passed: #{data[:passed]}"
802
- self.title = text
803
- type = data[:failure] > 0 ? :message_bad : :message
804
- set_output_type model.index(0,0), type
593
+ def code_for_spec file
594
+ return nil unless @project.file_in_project? file
595
+ return nil unless spec_file? file
596
+ @project.project_files.find do |f|
597
+ specs_for_code(f).include? file
805
598
  end
806
- nil
807
- end
808
-
809
- =begin rdoc
810
- Override of {OutputWidget#title=}
811
-
812
- It's needed to have the title element span all columns
813
-
814
- @param [String] val the new title
815
- =end
816
- def title= val
817
- super
818
- model.item(0,0).tool_tip = val
819
- view.set_first_column_spanned 0, Qt::ModelIndex.new, true
820
599
  end
821
600
 
822
- private
823
-
824
- =begin rdoc
825
- Resets the tool widget and sets the cursor to busy
826
- @return [nil]
827
- =end
828
- def spec_started
829
- @progress_bar.maximum = 0
830
- @progress_bar.value = 0
831
- @progress_bar.show
832
- @progress_bar.tool_tip = ''
833
- actions['show_stderr'].checked = false
834
- self.cursor = Qt::Cursor.new(Qt::BusyCursor)
835
- nil
601
+ def code_file? file
602
+ category(file) == :code
836
603
  end
837
604
 
838
- =begin rdoc
839
- Does the necessary cleanup for when spec finishes running
840
-
841
- It hides the progress widget and restores the default cursor.
842
-
843
- @param [Integer] code the exit code
844
- @param [String] reason why the program exited
845
- @return [nil]
846
- =end
847
- def spec_finished code, reason
848
- @progress_bar.hide
849
- @progress_bar.value = 0
850
- @progress_bar.maximum = 100
851
- self.set_focus
852
- unset_cursor
853
- unless reason == 'killed'
854
- non_stderr_types = %w[message message_good message_bad warning error]
855
- only_stderr = !model.item(0,0).text.match(/^\[SUMMARY\]/)
856
- if only_stderr
857
- 1.upto(model.row_count - 1) do |i|
858
- if non_stderr_types.include? model.item(i,0).data(OutputWidget::OutputTypeRole).to_string
859
- only_stderr = false
860
- break
861
- end
862
- end
863
- end
864
- if only_stderr
865
- actions['show_stderr'].checked = true
866
- model.insert "spec wasn't able to run the examples", :message_bad, nil
867
- end
868
- end
869
- auto_expand_items
870
- nil
605
+ def spec_file? file
606
+ category(file) == :spec
871
607
  end
872
-
873
- =begin rdoc
874
- Expands items according to the @rspec/auto_expand@ option
875
608
 
876
- If the option is @:expand_first@, the first failed example is expanded; if the
877
- option is @:expand_all@, all failed or pending examples are expanded. If the option
878
- is @:expand_none@, nothing is done
879
- @return [nil]
880
- =end
881
- def auto_expand_items
882
- if model.row_count > 1
883
- case Ruber[:config][:rspec, :auto_expand]
884
- when :expand_first
885
- item = model.each_row.find{|items| items[0].has_children}
886
- view.expand filter_model.map_from_source(item[0].index) if item
887
- when :expand_all
888
- model.each_row do |items|
889
- view.expand filter_model.map_from_source(items[0].index)
609
+ def category file
610
+ cat = @categories[file]
611
+ return cat if cat
612
+ spec_dir = @project[:rspec, :spec_directory, :abs]
613
+ code_dir = @project[:rspec, :code_directory, :abs]
614
+ if @project.file_in_project? file
615
+ if file.start_with? spec_dir
616
+ if File.fnmatch @project[:rspec, :spec_files], file.sub(spec_dir + '/', '')
617
+ @categories[file] = :spec
618
+ else @categories[file] = :unknown
890
619
  end
620
+ elsif file.start_with?(code_dir) and @project.file_in_project?(file)
621
+ @categories[file] = :code
622
+ else @categories[file] = :unknown
891
623
  end
624
+ else @categories[file] = :unknown
892
625
  end
893
- nil
894
- end
895
-
896
- =begin rdoc
897
- Creates the additional actions.
898
-
899
- It adds a single action, which allows the user to chose whether messages from
900
- standard error should be displayed or not.
901
-
902
- @return [nil]
903
- =end
904
- def setup_actions
905
- action_list << nil << 'show_stderr'
906
- a = KDE::ToggleAction.new 'S&how Standard Error', self
907
- actions['show_stderr'] = a
908
- a.checked = false
909
- connect a, SIGNAL('toggled(bool)'), filter, SLOT('toggle_display_stderr(bool)')
910
626
  end
911
627
 
912
- =begin rdoc
913
- Override of {OutputWidget#find_filename_in_index}
914
-
915
- It works as the base class method, but, if it doesn't find a result in _idx_,
916
- it looks for it in the parent indexes
917
-
918
- @param [Qt::ModelIndex] idx the index where to look for a file name
919
- @return [Array<String,Integer>,String,nil] see {OutputWidget#find_filename_in_index}
920
- =end
921
- def find_filename_in_index idx
922
- res = super
923
- unless res
924
- idx = idx.parent while idx.parent.valid?
925
- idx = idx.child(0,1)
926
- res = super idx if idx.valid?
927
- end
928
- res
628
+ def clear
629
+ @categories.clear
929
630
  end
631
+ slots :clear
930
632
 
931
- =begin rdoc
932
- Override of {OutputWidget#text_for_clipboard}
933
-
934
- @param [<Qt::ModelIndex>] idxs the selected indexes
935
- @return [QString] the text to copy to the clipboard
936
- =end
937
- def text_for_clipboard idxs
938
- order = {}
939
- idxs.each do |i|
940
- val = []
941
- parent = i
942
- while parent.parent.valid?
943
- parent = parent.parent
944
- val.unshift parent.row
945
- end
946
- val << [i.row, i.column]
947
- order[val] = i
948
- end
949
- order = order.sort do |a, b|
950
- a, b = a[0], b[0]
951
- res = a[0..-2] <=> b[0..-2]
952
- if res == 0 then a[-1] <=> b[-1]
953
- else res
954
- end
955
- end
956
- prev = order.shift[1]
957
- text = prev.data.valid? ? prev.data.to_string : ''
958
- order.each do |_, v|
959
- text << ( (prev.parent == v.parent and prev.row == v.row) ? "\t" : "\n")
960
- text << (v.data.valid? ? v.data.to_string : '')
961
- prev = v
962
- end
963
- text
964
- end
965
-
966
633
  end
967
634
 
968
635
  =begin rdoc
@@ -977,8 +644,26 @@ Project widget for the RSpec frontend plugin
977
644
  super
978
645
  @ui = Ui::RSpecProjectWidget.new
979
646
  @ui.setupUi self
647
+ view = @ui._rspec__patterns
648
+ mod = Qt::StandardItemModel.new
649
+ view.model = mod
650
+ mod.horizontal_header_labels = ['Code file', 'Spec file']
651
+ @ui.add_pattern.connect(SIGNAL(:clicked)) do
652
+ row = [Qt::StandardItem.new, Qt::StandardItem.new]
653
+ mod.append_row row
654
+ view.current_index = row[0].index
655
+ view.edit row[0].index
656
+ end
657
+ @ui.remove_pattern.connect(SIGNAL(:clicked)) do
658
+ sel = view.selection_model.selected_indexes
659
+ mod.remove_row sel[0].row
660
+ end
661
+ view.selection_model.connect(SIGNAL('selectionChanged(QItemSelection,QItemSelection)')) do
662
+ @ui.remove_pattern.enabled = view.selection_model.has_selection
663
+ end
980
664
  end
981
665
 
666
+
982
667
  private
983
668
 
984
669
  =begin rdoc
@@ -986,16 +671,30 @@ Sets the text of the pattern widget
986
671
  @param [Array<String>] the pattern to use. They'll be joined with commas to create
987
672
  the text to put in the widget
988
673
  =end
989
- def pattern= value
990
- value.join ', '
674
+ def patterns= value
675
+ view = @ui._rspec__patterns
676
+ value.each do |h|
677
+ row = [Qt::StandardItem.new(h[:code]),
678
+ Qt::StandardItem.new(h[:spec])]
679
+ view.model.append_row row
680
+ end
681
+ 2.times{|i| view.resize_column_to_contents i}
991
682
  end
992
683
 
993
684
  =begin rdoc
994
685
  Parses the content of the pattern widget
995
- @return [Array<String>] an array containing the patterns
686
+ @return [Array<Hash>] an array containing the patterns
996
687
  =end
997
- def pattern
998
- @ui._rspec__spec_pattern.text.split(/;\s*/)
688
+ def patterns
689
+ mod = @ui._rspec__patterns.model
690
+ mod.each_row.map do |cols|
691
+ code = cols[0].text
692
+ {:code => code, :spec => cols[1].text, :glob => text_glob?(code)}
693
+ end
694
+ end
695
+
696
+ def text_glob? text
697
+ text=~ /[*?{}\[\]]/
999
698
  end
1000
699
 
1001
700
  =begin rdoc