ruber 0.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/INSTALL +137 -0
- data/LICENSE +8 -0
- data/bin/ruber +65 -0
- data/data/share/apps/ruber/core_components.yaml +31 -0
- data/data/share/apps/ruber/ruberui.rc +109 -0
- data/data/share/icons/ruber.png +0 -0
- data/data/share/pixmaps/ruby.png +0 -0
- data/icons/ruber-16.png +0 -0
- data/icons/ruber-32.png +0 -0
- data/icons/ruber-48.png +0 -0
- data/icons/ruber-8.png +0 -0
- data/lib/ruber/application/application.rb +288 -0
- data/lib/ruber/application/plugin.yaml +11 -0
- data/lib/ruber/component_manager.rb +899 -0
- data/lib/ruber/config/config.rb +82 -0
- data/lib/ruber/config/plugin.yaml +3 -0
- data/lib/ruber/document_project.rb +209 -0
- data/lib/ruber/documents/document_list.rb +416 -0
- data/lib/ruber/documents/plugin.yaml +4 -0
- data/lib/ruber/editor/document.rb +506 -0
- data/lib/ruber/editor/editor_view.rb +167 -0
- data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
- data/lib/ruber/exception_widgets.rb +245 -0
- data/lib/ruber/external_program_plugin.rb +397 -0
- data/lib/ruber/filtered_output_widget.rb +342 -0
- data/lib/ruber/gui_states_handler.rb +231 -0
- data/lib/ruber/kde_config_option_backend.rb +167 -0
- data/lib/ruber/kde_sugar.rb +249 -0
- data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
- data/lib/ruber/main_window/main_window.rb +524 -0
- data/lib/ruber/main_window/main_window_actions.rb +537 -0
- data/lib/ruber/main_window/main_window_internal.rb +239 -0
- data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
- data/lib/ruber/main_window/output_color_widget.rb +35 -0
- data/lib/ruber/main_window/plugin.yaml +58 -0
- data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
- data/lib/ruber/main_window/status_bar.rb +156 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
- data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
- data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
- data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
- data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
- data/lib/ruber/main_window/workspace.rb +442 -0
- data/lib/ruber/output_widget.rb +1093 -0
- data/lib/ruber/plugin.rb +264 -0
- data/lib/ruber/plugin_like.rb +589 -0
- data/lib/ruber/plugin_specification.rb +106 -0
- data/lib/ruber/plugin_specification_reader.rb +451 -0
- data/lib/ruber/project.rb +493 -0
- data/lib/ruber/project_backend.rb +105 -0
- data/lib/ruber/projects/plugin.yaml +11 -0
- data/lib/ruber/projects/project_files_list.rb +314 -0
- data/lib/ruber/projects/project_files_widget.rb +301 -0
- data/lib/ruber/projects/project_list.rb +314 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
- data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
- data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
- data/lib/ruber/qt_sugar.rb +673 -0
- data/lib/ruber/settings_container.rb +515 -0
- data/lib/ruber/settings_dialog.rb +244 -0
- data/lib/ruber/settings_dialog_manager.rb +503 -0
- data/lib/ruber/utils.rb +414 -0
- data/lib/ruber/yaml_option_backend.rb +159 -0
- data/outsider_files +15 -0
- data/plugins/autosave/autosave.rb +404 -0
- data/plugins/autosave/plugin.yaml +16 -0
- data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
- data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
- data/plugins/command/command.png +0 -0
- data/plugins/command/command.rb +74 -0
- data/plugins/command/plugin.yaml +11 -0
- data/plugins/find_in_files/find_in_files.rb +337 -0
- data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
- data/plugins/find_in_files/find_in_files_ui.rc +11 -0
- data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
- data/plugins/find_in_files/plugin.yaml +23 -0
- data/plugins/find_in_files/ui/config_widget.rb +58 -0
- data/plugins/find_in_files/ui/config_widget.ui +41 -0
- data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
- data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
- data/plugins/project_browser/plugin.yaml +10 -0
- data/plugins/project_browser/project_browser.rb +245 -0
- data/plugins/rake/plugin.yaml +39 -0
- data/plugins/rake/rake.png +0 -0
- data/plugins/rake/rake.rb +567 -0
- data/plugins/rake/rake_extension.rb +153 -0
- data/plugins/rake/rake_widgets.rb +615 -0
- data/plugins/rake/rakeui.rc +27 -0
- data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
- data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
- data/plugins/rake/ui/choose_task_widget.rb +77 -0
- data/plugins/rake/ui/choose_task_widget.ui +72 -0
- data/plugins/rake/ui/config_widget.rb +127 -0
- data/plugins/rake/ui/config_widget.ui +123 -0
- data/plugins/rake/ui/project_widget.rb +217 -0
- data/plugins/rake/ui/project_widget.ui +246 -0
- data/plugins/rspec/plugin.yaml +30 -0
- data/plugins/rspec/rspec.png +0 -0
- data/plugins/rspec/rspec.rb +945 -0
- data/plugins/rspec/rspec.svg +90 -0
- data/plugins/rspec/rspecui.rc +20 -0
- data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
- data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
- data/plugins/ruby_development/plugin.yaml +27 -0
- data/plugins/ruby_development/ruby_development.png +0 -0
- data/plugins/ruby_development/ruby_development.rb +453 -0
- data/plugins/ruby_development/ruby_developmentui.rc +19 -0
- data/plugins/ruby_development/ui/project_widget.rb +112 -0
- data/plugins/ruby_development/ui/project_widget.ui +108 -0
- data/plugins/ruby_runner/config_widget.rb +116 -0
- data/plugins/ruby_runner/plugin.yaml +26 -0
- data/plugins/ruby_runner/project_widget.rb +62 -0
- data/plugins/ruby_runner/ruby.png +0 -0
- data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
- data/plugins/ruby_runner/ruby_runner.rb +411 -0
- data/plugins/ruby_runner/ui/config_widget.rb +92 -0
- data/plugins/ruby_runner/ui/config_widget.ui +91 -0
- data/plugins/ruby_runner/ui/project_widget.rb +60 -0
- data/plugins/ruby_runner/ui/project_widget.ui +48 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
- data/plugins/state/plugin.yaml +28 -0
- data/plugins/state/state.rb +520 -0
- data/plugins/state/ui/config_widget.rb +92 -0
- data/plugins/state/ui/config_widget.ui +89 -0
- data/plugins/syntax_checker/plugin.yaml +18 -0
- data/plugins/syntax_checker/syntax_checker.rb +662 -0
- data/ruber.desktop +10 -0
- data/spec/annotation_model_spec.rb +174 -0
- data/spec/common.rb +119 -0
- data/spec/component_manager_spec.rb +1259 -0
- data/spec/document_list_spec.rb +626 -0
- data/spec/document_project_spec.rb +373 -0
- data/spec/document_spec.rb +779 -0
- data/spec/editor_view_spec.rb +167 -0
- data/spec/external_program_plugin_spec.rb +676 -0
- data/spec/filtered_output_widget_spec.rb +642 -0
- data/spec/gui_states_handler_spec.rb +304 -0
- data/spec/kde_config_option_backend_spec.rb +214 -0
- data/spec/kde_sugar_spec.rb +101 -0
- data/spec/ktexteditor_wrapper_spec.rb +305 -0
- data/spec/output_widget_spec.rb +1703 -0
- data/spec/plugin_spec.rb +1393 -0
- data/spec/plugin_specification_reader_spec.rb +1765 -0
- data/spec/plugin_specification_spec.rb +401 -0
- data/spec/project_backend_spec.rb +172 -0
- data/spec/project_files_list_spec.rb +401 -0
- data/spec/project_list_spec.rb +511 -0
- data/spec/project_spec.rb +990 -0
- data/spec/qt_sugar_spec.rb +328 -0
- data/spec/settings_container_spec.rb +617 -0
- data/spec/settings_dialog_manager_spec.rb +773 -0
- data/spec/settings_dialog_spec.rb +419 -0
- data/spec/state_spec.rb +991 -0
- data/spec/utils_spec.rb +406 -0
- data/spec/workspace_spec.rb +869 -0
- data/spec/yaml_option_backend_spec.rb +246 -0
- metadata +284 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
name: rspec
|
2
|
+
version: 0.0.1
|
3
|
+
about:
|
4
|
+
authors: [Stefano Crocco, stefano.crocco@alice.it]
|
5
|
+
license: :gpl
|
6
|
+
description: Frontend to RSpec
|
7
|
+
bug_address: http://github.com/stcrocco/ruber/issues
|
8
|
+
icon: rspec.png
|
9
|
+
class: Ruber::RSpec::Plugin
|
10
|
+
require: rspec
|
11
|
+
deps: [ruby_development, autosave]
|
12
|
+
ui_file: rspecui.rc
|
13
|
+
project_widgets:
|
14
|
+
- {caption: RSpec, class: 'Ruber::RSpec::ProjectWidget', pixmap: rspec.png}
|
15
|
+
project_options:
|
16
|
+
rspec:
|
17
|
+
executable: {default: 'Ruber::RSpec::Plugin.find_default_executable'}
|
18
|
+
options: {default: []}
|
19
|
+
spec_directory: {relative_path: true, default: spec}
|
20
|
+
spec_files: {default: '*_spec.rb'}
|
21
|
+
spec_pattern: {default: ['%f_spec.rb']}
|
22
|
+
full_backtraces: {default: true}
|
23
|
+
tool_widgets: {class: 'Ruber::RSpec::ToolWidget', pixmap: rspec.png, caption: RSpec}
|
24
|
+
actions:
|
25
|
+
rspec-go_to_spec: {text: Switch to &Spec, shortcut: 'Alt+Shift+R, S', slot: go_to_spec(), states: [active_project_exists, current_document]}
|
26
|
+
rspec-go_to_file: {text: Switch to &File, shortcut: 'Alt+Shift+R, F', slot: go_to_file(), states: [active_project_exists, current_document]}
|
27
|
+
rspec-run_all: {text: Run &Project Specs, shortcut: 'Alt+Shift+R, P', slot: run_all(), states: [active_project_exists, rspec_running]}
|
28
|
+
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]}
|
29
|
+
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]}
|
30
|
+
rspec-stop: {text: S&top, shortcut: Esc, icon: process-stop, slot: stop_process(), state: rspec_running}
|
Binary file
|
@@ -0,0 +1,945 @@
|
|
1
|
+
require 'ruby_runner/ruby_runner'
|
2
|
+
require_relative 'ui/rspec_project_widget'
|
3
|
+
|
4
|
+
require 'strscan'
|
5
|
+
require 'yaml'
|
6
|
+
require 'open3'
|
7
|
+
|
8
|
+
module Ruber
|
9
|
+
|
10
|
+
=begin rdoc
|
11
|
+
Frontend plugin to RSpec
|
12
|
+
|
13
|
+
This plugin provides commands to run examples for all the files in the current
|
14
|
+
project or on the current document (only if it's part of a project). In the latter
|
15
|
+
case, the command can be issued while viewing either the spec or the code file.
|
16
|
+
Besides, an action to switch from spec file to code file and one to do the reverse
|
17
|
+
switch are provided.
|
18
|
+
|
19
|
+
The pattern to extract the name of the spec file from that of the code file can
|
20
|
+
be customized in the project configuration dialog (by default the plugin assumes
|
21
|
+
that the spec file for for a file called @code.rb@ is @code_spec.rb@). The directory
|
22
|
+
where the spec files are can also be customized (by default it's the @spec@ subdirectory
|
23
|
+
under the project directory)
|
24
|
+
|
25
|
+
@api feature rspec
|
26
|
+
@plugin
|
27
|
+
@config_option rspec ruby_options [<String>] the options to pass to ruby when executing
|
28
|
+
the spec command
|
29
|
+
@project_option rspec executable [String] the path to the spec executable
|
30
|
+
@project_option rspec options [<String>] the options to pass to the _spec_ command
|
31
|
+
@project_option rspec spec_directory [String] the directory where the example files
|
32
|
+
are
|
33
|
+
@project_option rspec spec_pattern [<String>] patterns to generate the spec files
|
34
|
+
from the code files. The name of the code file is obtained by using the special
|
35
|
+
sequence @%f@ in the strings. Only the file name of the code file is taken into
|
36
|
+
account (not the directory). All the spec file names are relative to the directory
|
37
|
+
specified in the @rspec/spec_directory@ option. If more than one entry is specified,
|
38
|
+
all of them will be tried
|
39
|
+
=end
|
40
|
+
module RSpec
|
41
|
+
|
42
|
+
=begin rdoc
|
43
|
+
Plugin object for the RSpec plugin
|
44
|
+
@api_method #specs_for_file
|
45
|
+
@api_method #file_for_spec
|
46
|
+
@api_method #run_rspec
|
47
|
+
@api_method #run_rspec_for
|
48
|
+
=end
|
49
|
+
class Plugin < RubyRunner::RubyRunnerPlugin
|
50
|
+
|
51
|
+
=begin rdoc
|
52
|
+
The starting delimiter of the data written by the formatter
|
53
|
+
=end
|
54
|
+
STARTING_DELIMITER = /^####%%%%####KRUBY_BEGIN$/
|
55
|
+
|
56
|
+
=begin rdoc
|
57
|
+
The ending delimiter of the data written by the formatter
|
58
|
+
=end
|
59
|
+
ENDING_DELIMITER = /^####%%%%####KRUBY_END$/
|
60
|
+
|
61
|
+
=begin rdoc
|
62
|
+
Finds the rspec program to use by default
|
63
|
+
|
64
|
+
It looks for an executable called @rspec@ or @spec@ (this is to support both RSpec
|
65
|
+
1 and 2) in @PATH@ (using the @which@ command).
|
66
|
+
@return [String] a string with the path to the rspec program or an empty string
|
67
|
+
if no rspec program was found
|
68
|
+
=end
|
69
|
+
def self.find_default_executable
|
70
|
+
path = Open3.popen3('which rspec'){|stdin, stdout, stderr| stdout.read}.strip
|
71
|
+
if path.empty?
|
72
|
+
path = Open3.popen3('which spec'){|stdin, stdout, stderr| stdout.read}.strip
|
73
|
+
end
|
74
|
+
path
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
slots :go_to_spec, :go_to_file, :run_all, :run_current, :run_current_line
|
79
|
+
|
80
|
+
=begin rdoc
|
81
|
+
@param [Ruber::PluginSpecification] the plugin specification object associated with
|
82
|
+
the plugin
|
83
|
+
=end
|
84
|
+
def initialize psf
|
85
|
+
super psf, :rspec, {:scope => [:global]}, nil, false
|
86
|
+
Ruber[:autosave].register_plugin self, true
|
87
|
+
@formatter = File.join File.dirname(__FILE__), 'ruber_rspec_formatter'
|
88
|
+
self.connect(SIGNAL('process_finished(int, QString)')){Ruber[:main_window].set_state 'rspec_running', false}
|
89
|
+
Ruber[:main_window].set_state 'rspec_running', false
|
90
|
+
|
91
|
+
switch_prc = Proc.new{|states| states['active_project_exists'] and states['current_document']}
|
92
|
+
register_action_handler 'rspec-go_to_file', &switch_prc
|
93
|
+
register_action_handler 'rspec-go_to_spec', &switch_prc
|
94
|
+
register_action_handler 'rspec-run_all' do |states|
|
95
|
+
states['active_project_exists'] and !states['rspec_running']
|
96
|
+
end
|
97
|
+
current_prc = Proc.new do |states|
|
98
|
+
states['active_project_exists'] and states['current_document'] and
|
99
|
+
!states['rspec_running']
|
100
|
+
end
|
101
|
+
register_action_handler 'rspec-run_current', ¤t_prc
|
102
|
+
register_action_handler 'rspec-run_current_line', ¤t_prc
|
103
|
+
|
104
|
+
Ruber[:components].connect(SIGNAL('feature_loaded(QString, QObject*)')) do |f, o|
|
105
|
+
o.register_plugin self, true if f == 'autosave'
|
106
|
+
end
|
107
|
+
@output_widget = @widget
|
108
|
+
end
|
109
|
+
|
110
|
+
=begin rdoc
|
111
|
+
Runs rspec for the given files
|
112
|
+
|
113
|
+
The output of spec is displayed in the associated output widget.
|
114
|
+
|
115
|
+
Files are autosaved before running rspec.
|
116
|
+
|
117
|
+
If spec is already running, or if autosaving fails, noting is done.
|
118
|
+
|
119
|
+
@param [Array<String>] files the spec files to run. Nonexisting files are ignored
|
120
|
+
@param [Hash] opts options to fine-tune the behaviour of spec
|
121
|
+
@param [Hash] autosave_opts options telling whether and how autosave files
|
122
|
+
@param [Proc] blk a block to pass to autosave. If not given, no block will be
|
123
|
+
passed to autosave
|
124
|
+
|
125
|
+
@option opts [String] :ruby (Ruber[:config][:ruby, :ruby]) the ruby interpreter
|
126
|
+
to use
|
127
|
+
@option opts [Array<String>] :ruby_options (Ruber[:config][:ruby, :ruby_options])
|
128
|
+
the options to pass to the ruby interpreter
|
129
|
+
@option opts [String] :spec ('spec') the path of the spec command to use
|
130
|
+
@option opts [Array<String>] :spec_options ([]) the options to pass to the spec
|
131
|
+
program
|
132
|
+
@option opts [String] :dir ('.') the directory to run spec from
|
133
|
+
@option opts [Boolean] :full_backtraces (nil) whether or not to pass the @-b@ option
|
134
|
+
to the spec program
|
135
|
+
|
136
|
+
@option autosave_opts [Array<Document>,Symbol] :files (nil) the documents to autosave.
|
137
|
+
It has the same meaning as second parameter to {Autosave::AutosavePlugin#autosave}.
|
138
|
+
If it's *nil*, autosave won't be used
|
139
|
+
@option autosave_opts [Symbol, Plugin] :plugin (Ruber[:rspec]) the value to pass
|
140
|
+
as first argument to {Autosave::AutosavePlugin#autosave}
|
141
|
+
@option autosave_opts [Boolean] :stop_on_failure (nil) as in {Autosave::AutosavePlugin#autosave}
|
142
|
+
@option autosave_opts [Symbol] :on_failure (nil) as in {Autosave::AutosavePlugin#autosave}
|
143
|
+
@option autosave_opts [String] :message (nil) as in {Autosave::AutosavePlugin#autosave}
|
144
|
+
|
145
|
+
@return [Boolean] *true* if the process is started and *false* otherwise
|
146
|
+
(including the case when the process was already running or autosaving failed)
|
147
|
+
=end
|
148
|
+
def run_rspec files, opts, autosave_opts = {}, &blk
|
149
|
+
default_opts = {
|
150
|
+
:ruby => Ruber[:config][:ruby, :ruby],
|
151
|
+
:ruby_options => Ruber[:config][:ruby, :ruby_options],
|
152
|
+
:spec => 'spec',
|
153
|
+
:spec_options => [],
|
154
|
+
:dir => '.'
|
155
|
+
}
|
156
|
+
opts = default_opts.merge opts
|
157
|
+
return false if @process.state != Qt::Process::NotRunning
|
158
|
+
@widget.clear_output
|
159
|
+
files = files.select{|f| File.exist? f}
|
160
|
+
# if files.empty?
|
161
|
+
# KDE::MessageBox.sorry nil, 'No spec file has been found. RSpec won\'t be run'
|
162
|
+
# return false
|
163
|
+
# end
|
164
|
+
if autosave_opts[:files]
|
165
|
+
plug = autosave_opts[:plugin] || self
|
166
|
+
what = autosave_opts[:files]
|
167
|
+
return false unless Ruber[:autosave].autosave plug,
|
168
|
+
what, autosave_opts, &blk
|
169
|
+
end
|
170
|
+
full_backtraces = opts[:full_backtraces] ? %w[-b] : []
|
171
|
+
args = [opts[:spec]] + %W[-r #{@formatter} -f Ruber::RSpec::Formatter] +
|
172
|
+
opts[:spec_options] + full_backtraces + files
|
173
|
+
@widget.working_directory = opts[:dir]
|
174
|
+
@display_standard_error = opts[:stderr]
|
175
|
+
Ruber[:main_window].activate_tool(@widget)
|
176
|
+
Ruber[:main_window].change_state 'rspec_running', true
|
177
|
+
title = ([opts[:spec].split('/')[-1]] + opts[:spec_options]+ full_backtraces + files).join ' '
|
178
|
+
run_process opts[:ruby], opts[:dir], opts[:ruby_options] + args, title
|
179
|
+
@widget.model.item(0,0).tool_tip = ([opts[:ruby]] + opts[:ruby_options] + args).join " "
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
=begin rdoc
|
186
|
+
Override of {ExternalProgramPlugin#process_standard_output}
|
187
|
+
|
188
|
+
It parses the output from the _spec_ program (generated with the
|
189
|
+
{Ruber::RSpec::Formatter Ruber rspec formatter}) and displays the data it contains
|
190
|
+
appropriately.
|
191
|
+
@param [Array] lines the output, split in lines
|
192
|
+
@return [nil]
|
193
|
+
=end
|
194
|
+
def process_standard_output lines
|
195
|
+
items = parse_spec_output lines.join "\n"
|
196
|
+
items.each do |it|
|
197
|
+
if it.is_a? String then @widget.model.insert_lines it, :output, nil
|
198
|
+
else @widget.display_example it
|
199
|
+
end
|
200
|
+
end
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
|
204
|
+
=begin rdoc
|
205
|
+
Override of {ExternalProgramPlugin#process_standard_error}
|
206
|
+
@param [Array] lines the standard error output, split in lines
|
207
|
+
@return [nil]
|
208
|
+
=end
|
209
|
+
def process_standard_error lines
|
210
|
+
@widget.model.insert_lines lines, :output1, nil
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
|
214
|
+
=begin rdoc
|
215
|
+
Parses the output of the _spec_ command
|
216
|
+
|
217
|
+
This method only works if the {Ruber::RSpec::Formatter Ruber rspec formatter}
|
218
|
+
is used.
|
219
|
+
|
220
|
+
The output is split according in regions between {STARTING_DELIMITER} and
|
221
|
+
{ENDING_DELIMITER}. All text inside these regions is considered the YAML dump of
|
222
|
+
a hash and converted back to a hash. All text which is not between a pair of
|
223
|
+
delimiters, as well as the text which can't be converted by YAML is left as is
|
224
|
+
@param [String] str the output to parse
|
225
|
+
@return [<Hash, String>] the result of the parse. The text which was successfully
|
226
|
+
parsed by YAML is stored as a hash, while the rest is stored as strings. Order is
|
227
|
+
preserved.
|
228
|
+
=end
|
229
|
+
def parse_spec_output str
|
230
|
+
sc = StringScanner.new str
|
231
|
+
res = []
|
232
|
+
until sc.eos?
|
233
|
+
match = sc.scan_until(STARTING_DELIMITER) || sc.rest
|
234
|
+
found = match.sub!(STARTING_DELIMITER, '')
|
235
|
+
res << match
|
236
|
+
if found
|
237
|
+
yaml = sc.scan_until(ENDING_DELIMITER) || sc.rest
|
238
|
+
yaml.sub! ENDING_DELIMITER, ''
|
239
|
+
res << (YAML.load(yaml) rescue yaml)
|
240
|
+
else sc.terminate
|
241
|
+
end
|
242
|
+
end
|
243
|
+
res
|
244
|
+
end
|
245
|
+
|
246
|
+
=begin rdoc
|
247
|
+
Runs all the specs for the project.
|
248
|
+
@return [nil]
|
249
|
+
=end
|
250
|
+
def run_all
|
251
|
+
prj = Ruber[:projects].current_project
|
252
|
+
unless prj
|
253
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
254
|
+
return
|
255
|
+
end
|
256
|
+
opts = options prj
|
257
|
+
opts[:files] = Dir.glob File.join(opts[:specs_dir], opts[:filter])
|
258
|
+
run_rspec_for prj, opts, :files => :project_files, :on_failure => :ask, :message => 'Do you want to run the tests all the same?'
|
259
|
+
nil
|
260
|
+
end
|
261
|
+
|
262
|
+
=begin rdoc
|
263
|
+
Runs the specs corresponding to the current file
|
264
|
+
|
265
|
+
If the current file is a code file, the corresponding spec file will be run. If
|
266
|
+
it is a spec file, the file itself will be run.
|
267
|
+
|
268
|
+
To decide whether the current file is a spec or a code file, this method uses
|
269
|
+
the patters stored in the @rspec/spec_pattern@ project setting to build the names
|
270
|
+
of the spec files associated with the current file, which is assumed to be a code
|
271
|
+
file. If none of these files exist, then the current file is treated as a spec file,
|
272
|
+
otherwise one of the spec files thus generated is run.
|
273
|
+
|
274
|
+
*Note:* the way this method works implies that if it is called on a code
|
275
|
+
file which doesn't have an associated spec file, rspec will be run on that file,
|
276
|
+
which, most likely, will cause it to fail.
|
277
|
+
|
278
|
+
@return [Boolean] *true* if the spec program is started and *false* otherwise
|
279
|
+
(including the case when the process was already running or autosaving failed)
|
280
|
+
=end
|
281
|
+
def run_current
|
282
|
+
prj = Ruber[:projects].current_project
|
283
|
+
unless prj
|
284
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
285
|
+
return
|
286
|
+
end
|
287
|
+
opts = options prj
|
288
|
+
doc = Ruber[:main_window].current_document
|
289
|
+
unless doc
|
290
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
291
|
+
return
|
292
|
+
end
|
293
|
+
files = specs_for_file opts, doc.path
|
294
|
+
files.reject!{|f| !File.exist? f}
|
295
|
+
opts[:files] = files.empty? ? [doc.path] : files
|
296
|
+
run_rspec_for prj, opts, :files => :documents_with_file, :on_failure => :ask,
|
297
|
+
:message => 'Do you want to run the tests all the same?'
|
298
|
+
end
|
299
|
+
|
300
|
+
=begin rdoc
|
301
|
+
Runs the example(s) in the current line
|
302
|
+
|
303
|
+
Similar to {#run_current}, but tells spec to run only the example or example
|
304
|
+
group corresponding to the line where the cursor is (using spec's -l option).
|
305
|
+
Besides, unlike {#run_current}, this method can only be called when the
|
306
|
+
current file is the example file, not the source.
|
307
|
+
|
308
|
+
@return [Boolean] *true* if the spec program is started and *false* otherwise
|
309
|
+
(including the case when the process was already running or autosaving failed)
|
310
|
+
=end
|
311
|
+
def run_current_line
|
312
|
+
prj = Ruber[:projects].current_project
|
313
|
+
unless prj
|
314
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
315
|
+
return
|
316
|
+
end
|
317
|
+
opts = options prj
|
318
|
+
doc = Ruber[:main_window].current_document
|
319
|
+
unless doc
|
320
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
321
|
+
return
|
322
|
+
end
|
323
|
+
line = doc.view.cursor_position.line + 1
|
324
|
+
files = specs_for_file opts, doc.path
|
325
|
+
files.reject!{|f| !File.exist? f}
|
326
|
+
opts[:files] = files.empty? ? [doc.path] : files
|
327
|
+
opts[:spec_options] += ["-l", line.to_s]
|
328
|
+
run_rspec_for prj, opts, :files => :documents_with_file, :on_failure => :ask,
|
329
|
+
:message => 'Do you want to run the tests all the same?'
|
330
|
+
end
|
331
|
+
|
332
|
+
=begin rdoc
|
333
|
+
Runs the spec command for the given object
|
334
|
+
|
335
|
+
It works like {RubyRunner::RubyRunnerPluginInternal#ruby_command_for RubyRunner::RubyRunnerPlugin#ruby_command_for}
|
336
|
+
but already takes care of the user's settings regarding the path of the spec program.
|
337
|
+
|
338
|
+
@param [AbstractProject, Document, String, nil] origin see the _target_ argument
|
339
|
+
of {RubyRunner::RubyRunnerPluginInternal#option_for RubyRunner::RubyRunnerPlugin#option_for}
|
340
|
+
@param [Hash] opts the options to pass to the _spec_ program. It has the same
|
341
|
+
meaning as the _opts_ argument to {#run_rspec}, but can also contain an additional
|
342
|
+
entry (see below)
|
343
|
+
@param [Hash] autosave_opts see the _autosave_opts_ argument of {#run_rspec}
|
344
|
+
@param [Proc] blk see the _blk_ argument of {#run_rspec}
|
345
|
+
|
346
|
+
@option opts [Array<String>] :files the files to pass to the spec program
|
347
|
+
|
348
|
+
@return [Boolean] *true* if the process is started and *false* otherwise
|
349
|
+
(including the case when the process was already running or autosaving failed)
|
350
|
+
=end
|
351
|
+
def run_rspec_for origin, opts, autosave_opts = {}, &blk
|
352
|
+
process.kill
|
353
|
+
ruby, *cmd = ruby_command_for origin, opts[:dir]
|
354
|
+
opts = {:ruby => ruby, :ruby_options => cmd}.merge opts
|
355
|
+
run_rspec opts[:files], opts, autosave_opts, &blk
|
356
|
+
end
|
357
|
+
|
358
|
+
=begin rdoc
|
359
|
+
Collects all the options relative to this plugin from a project
|
360
|
+
|
361
|
+
*Note:* *never* use destructive methods on the values contained in this hash. Doing so will
|
362
|
+
change the options stored in the project, which most likely isn't what you want.
|
363
|
+
If you need to change the options, make duplicates of the values
|
364
|
+
|
365
|
+
@param [AbstractProject] prj the project to retrieve options from
|
366
|
+
@return [Hash] an hash containing the options stored in the project. The correspondence
|
367
|
+
between options and entries in this hash is the following:
|
368
|
+
* @:rspec/executable@ → @:spec@
|
369
|
+
* @:rspec/options@ → @:spec_options@
|
370
|
+
* @:rspec/spec_directory@ → @:specs_dir@
|
371
|
+
* @:rspec/spec_files@ → @:filter@
|
372
|
+
* @:rspec/spec_pattern@ → @:pattern@
|
373
|
+
* @:rspec/full_backtraces@ → @:full_backtraces@
|
374
|
+
|
375
|
+
Besides, the above entries, the hash also contains a @:dir@ entry which contains
|
376
|
+
the project directory.
|
377
|
+
=end
|
378
|
+
def options prj
|
379
|
+
res = {}
|
380
|
+
res[:spec] = prj[:rspec, :executable]
|
381
|
+
res[:spec_options] = prj[:rspec, :options]
|
382
|
+
res[:specs_dir] = prj[:rspec, :spec_directory, :absolute]
|
383
|
+
res[:filter] = prj[:rspec, :spec_files]
|
384
|
+
res[:pattern] = prj[:rspec, :spec_pattern]
|
385
|
+
res[:dir] = prj.project_directory
|
386
|
+
res[:full_backtraces] = prj[:rspec, :full_backtraces]
|
387
|
+
res
|
388
|
+
end
|
389
|
+
|
390
|
+
=begin rdoc
|
391
|
+
Opens the spec file(s) associated with the current document in the editor
|
392
|
+
|
393
|
+
@return [nil]
|
394
|
+
=end
|
395
|
+
def go_to_spec
|
396
|
+
prj = Ruber[:projects].current_project
|
397
|
+
unless prj
|
398
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
399
|
+
return
|
400
|
+
end
|
401
|
+
opts = options prj
|
402
|
+
doc = Ruber[:main_window].current_document
|
403
|
+
unless prj
|
404
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
405
|
+
return
|
406
|
+
end
|
407
|
+
files = specs_for_file opts, doc.path
|
408
|
+
files.each{|f| Ruber[:main_window].display_document f if File.exist? f}
|
409
|
+
nil
|
410
|
+
end
|
411
|
+
|
412
|
+
=begin rdoc
|
413
|
+
Opens the code file associated with the current spec file in the editor
|
414
|
+
|
415
|
+
This method assumes the current file is a spec file and attempts to find the
|
416
|
+
correspondent code file and open it in the editor. It does nothing if no correspondent
|
417
|
+
code file can be found.
|
418
|
+
@return [nil]
|
419
|
+
@see #file_for_spec
|
420
|
+
=end
|
421
|
+
def go_to_file
|
422
|
+
prj = Ruber[:projects].current_project
|
423
|
+
unless prj
|
424
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
425
|
+
return
|
426
|
+
end
|
427
|
+
opts = options prj
|
428
|
+
doc = Ruber[:main_window].current_document
|
429
|
+
unless prj
|
430
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
431
|
+
return
|
432
|
+
end
|
433
|
+
file = file_for_spec prj, doc.path
|
434
|
+
Ruber[:main_window].display_document file if file and File.exist? file
|
435
|
+
nil
|
436
|
+
end
|
437
|
+
|
438
|
+
=begin rdoc
|
439
|
+
Determines all possible specs files associated with a code file
|
440
|
+
|
441
|
+
The names of the possible spec files are obtained replacing the @%f@ tag in each
|
442
|
+
entry of the @rspec/pattern@ setting with the name of the file (without checking
|
443
|
+
whether the files actually exist).
|
444
|
+
|
445
|
+
@param [String] file the name of the code file
|
446
|
+
@return [<String>] the names of the possible spec file associated with _file_
|
447
|
+
=end
|
448
|
+
def specs_for_file opts, file
|
449
|
+
file = File.basename file, '.rb'
|
450
|
+
res = opts[:pattern].map{|i| File.join opts[:specs_dir], i.gsub('%f', file)}
|
451
|
+
res
|
452
|
+
end
|
453
|
+
|
454
|
+
=begin rdoc
|
455
|
+
The name of the code file associated with a given spec file
|
456
|
+
|
457
|
+
To find out which code file is associated with the given spec file, this method
|
458
|
+
takes all the project files and constructs the file names of all the specs associated
|
459
|
+
to them according to the @rspec/spec_pattern@ project option. As soon as one of
|
460
|
+
the generated file names matches the given spec file, the generating file is returned.
|
461
|
+
|
462
|
+
@param [Ruber::AbstractProject] prj the project containing the settings to use
|
463
|
+
@param [String] file the name of the spec file to find the code file for
|
464
|
+
@return [String] the absolute path of a file _file_ is a spec of, according
|
465
|
+
to the settings contained in _prj_.
|
466
|
+
=end
|
467
|
+
def file_for_spec prj, file
|
468
|
+
pattern = prj[:spec_pattern]
|
469
|
+
opts = options prj
|
470
|
+
prj.project_files.abs.find{|f| specs_for_file( opts, f).include? file}
|
471
|
+
end
|
472
|
+
|
473
|
+
=begin rdoc
|
474
|
+
Override of {ExternalProgramPlugin#display_exit_message}
|
475
|
+
|
476
|
+
It works as the base class's method except when the program exits successfully,
|
477
|
+
in which case it does nothing.
|
478
|
+
|
479
|
+
See {ExternalProgramPlugin#display_exit_message} for the meaning of the parameters
|
480
|
+
|
481
|
+
@return nil
|
482
|
+
=end
|
483
|
+
def display_exit_message code, reason
|
484
|
+
super unless reason.empty?
|
485
|
+
end
|
486
|
+
|
487
|
+
end
|
488
|
+
|
489
|
+
=begin rdoc
|
490
|
+
Filter model used by the RSpec output widget
|
491
|
+
|
492
|
+
It allows to choose whether to accept items corresponding to output to standard error or to reject
|
493
|
+
it. To find out if a given item corresponds to the output of standard error or
|
494
|
+
standard output, this model uses the data contained in a custom role in the output.
|
495
|
+
The index of this role is {RSpec::OutputWidget::OutputTypeRole}.
|
496
|
+
=end
|
497
|
+
class FilterModel < FilteredOutputWidget::FilterModel
|
498
|
+
|
499
|
+
slots 'toggle_display_stderr(bool)'
|
500
|
+
|
501
|
+
=begin rdoc
|
502
|
+
Whether output from standard error should be displayed or not
|
503
|
+
@return [Boolean]
|
504
|
+
=end
|
505
|
+
attr_reader :display_stderr
|
506
|
+
|
507
|
+
=begin rdoc
|
508
|
+
Create a new instance
|
509
|
+
|
510
|
+
The new instance is set not to show the output from standard error
|
511
|
+
|
512
|
+
@param [Qt::Object, nil] parent the parent object
|
513
|
+
=end
|
514
|
+
def initialize parent = nil
|
515
|
+
super
|
516
|
+
@display_stderr = false
|
517
|
+
end
|
518
|
+
|
519
|
+
=begin rdoc
|
520
|
+
Sets whether to display or ignore items corresponding to output to standard error
|
521
|
+
|
522
|
+
If this choice has changed, the model is invalidated.
|
523
|
+
|
524
|
+
@param [Boolean] val whether to display or ignore the output to standard error
|
525
|
+
@return [Boolean] _val_
|
526
|
+
=end
|
527
|
+
def display_stderr= val
|
528
|
+
old, @display_stderr = @display_stderr, val
|
529
|
+
invalidate if old != @display_stderr
|
530
|
+
@display_standard_error
|
531
|
+
end
|
532
|
+
alias_method :toggle_display_stderr, :display_stderr=
|
533
|
+
|
534
|
+
=begin rdoc
|
535
|
+
Override of {FilteredOutputWidget::FilterModel#filterAcceptsRow}
|
536
|
+
|
537
|
+
According to the value of {#display_stderr}, it can filter out items corresponding
|
538
|
+
to standard error. In all other respects, it behaves as the base class method.
|
539
|
+
@param [Integer] r the row number
|
540
|
+
@param [Qt::ModelIndex] parent the parent index
|
541
|
+
@return [Boolean] *true* if the row should be displayed and *false* otherwise
|
542
|
+
=end
|
543
|
+
def filterAcceptsRow r, parent
|
544
|
+
if !@display_stderr
|
545
|
+
idx = source_model.index(r,0,parent)
|
546
|
+
return false if idx.data(OutputWidget::OutputTypeRole).to_string == 'output1'
|
547
|
+
end
|
548
|
+
super
|
549
|
+
end
|
550
|
+
|
551
|
+
end
|
552
|
+
|
553
|
+
=begin rdoc
|
554
|
+
Tool widget used by the rspec plugin.
|
555
|
+
|
556
|
+
It displays the output from the spec program in a multi column tree. The name of
|
557
|
+
failing or pending examples are displayed in a full line; all other information,
|
558
|
+
such as the location of the example, the error message and so on are displayed
|
559
|
+
in child items.
|
560
|
+
|
561
|
+
While the examples are being run, a progress bar is shown.
|
562
|
+
=end
|
563
|
+
class ToolWidget < FilteredOutputWidget
|
564
|
+
|
565
|
+
slots :spec_started, 'spec_finished(int, QString)'
|
566
|
+
|
567
|
+
=begin rdoc
|
568
|
+
@param [Qt::Widget, nil] parent the parent widget
|
569
|
+
=end
|
570
|
+
def initialize parent = nil
|
571
|
+
super parent, :view => :tree, :filter => FilterModel.new
|
572
|
+
@ignore_word_wrap_option = true
|
573
|
+
view.word_wrap = true
|
574
|
+
view.horizontal_scroll_mode = Qt::AbstractItemView::ScrollPerItem
|
575
|
+
# view.text_elide_mode = Qt::ElideNone
|
576
|
+
model.append_column [] if model.column_count < 2
|
577
|
+
@progress_bar = Qt::ProgressBar.new(self){|w| w.hide}
|
578
|
+
layout.add_widget @progress_bar, 1,0
|
579
|
+
view.header_hidden = true
|
580
|
+
view.header.resize_mode = Qt::HeaderView::ResizeToContents
|
581
|
+
connect Ruber[:rspec], SIGNAL(:process_started), self, SLOT(:spec_started)
|
582
|
+
connect Ruber[:rspec], SIGNAL('process_finished(int, QString)'), self, SLOT('spec_finished(int, QString)')
|
583
|
+
filter.connect(SIGNAL('rowsInserted(QModelIndex, int, int)')) do |par, st, en|
|
584
|
+
if !par.valid?
|
585
|
+
st.upto(en) do |i|
|
586
|
+
view.set_first_column_spanned i, par, true
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
setup_actions
|
591
|
+
end
|
592
|
+
|
593
|
+
=begin rdoc
|
594
|
+
Displays the data relative to an example in the widget
|
595
|
+
|
596
|
+
Actually, this method simply passes its argument to a more specific method, depending
|
597
|
+
on the data it contains.
|
598
|
+
|
599
|
+
@param [Hash] data a hash containing the data describing the results of running
|
600
|
+
the example. This hash must contain the @:type@ key, which tells which kind of
|
601
|
+
event the hash describes. The other entries change depending on the method which
|
602
|
+
will be called, which is determined according to the @:type@ entry:
|
603
|
+
* @:success@: {#display_successful_example}
|
604
|
+
* @:failure@: {#display_failed_example}
|
605
|
+
* @:pending@: {#display_pending_example}
|
606
|
+
* @:new_example@: {#change_current_example}
|
607
|
+
* @:start@: {#set_example_count}
|
608
|
+
* @:summary@: {#display_summary}
|
609
|
+
If the @:type@ entry doesn't have one of the previous values, the hash will be
|
610
|
+
converted to a string and displayed in the widget
|
611
|
+
=end
|
612
|
+
def display_example data
|
613
|
+
unless data.is_a?(Hash)
|
614
|
+
model.insert_lines data.to_s, :output, nil
|
615
|
+
return
|
616
|
+
end
|
617
|
+
case data[:type]
|
618
|
+
when :success then display_successful_example data
|
619
|
+
when :failure then display_failed_example data
|
620
|
+
when :pending then display_pending_example data
|
621
|
+
when :new_example then change_current_example data
|
622
|
+
when :start then set_example_count data
|
623
|
+
when :summary then display_summary data
|
624
|
+
else model.insert_lines data.to_s, :output, nil
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
=begin rdoc
|
629
|
+
Changes the current example
|
630
|
+
|
631
|
+
Currently, this only affects the tool tip displayed by the progress bar.
|
632
|
+
|
633
|
+
@param [Hash] data the data to use. It must contain the @:description@ entry,
|
634
|
+
which contains the text of the tool tip to use.
|
635
|
+
@return [nil]
|
636
|
+
=end
|
637
|
+
def change_current_example data
|
638
|
+
@progress_bar.tool_tip = data[:description]
|
639
|
+
nil
|
640
|
+
end
|
641
|
+
|
642
|
+
=begin rdoc
|
643
|
+
Sets the number of examples found by the spec program.
|
644
|
+
|
645
|
+
This is used to set the maximum value of the progress bar.
|
646
|
+
|
647
|
+
@param [Hash] data the data to use. It must contain the @:count@ entry,
|
648
|
+
which contains the number of examples
|
649
|
+
@return [nil]
|
650
|
+
=end
|
651
|
+
def set_example_count data
|
652
|
+
@progress_bar.maximum = data[:count]
|
653
|
+
nil
|
654
|
+
end
|
655
|
+
|
656
|
+
|
657
|
+
=begin rdoc
|
658
|
+
Updates the progress bar by incrementing its value by one
|
659
|
+
|
660
|
+
@param [Hash] data the data to use. Currently it's unused
|
661
|
+
@return [nil]
|
662
|
+
=end
|
663
|
+
def display_successful_example data
|
664
|
+
@progress_bar.value += 1
|
665
|
+
nil
|
666
|
+
end
|
667
|
+
|
668
|
+
=begin rdoc
|
669
|
+
Displays information about a failed example in the tool widget.
|
670
|
+
|
671
|
+
@param [Hash] data the data about the example.
|
672
|
+
|
673
|
+
@option data [String] :location the line number where the error occurred
|
674
|
+
@option data [String] :description the name of the failed example
|
675
|
+
@option data [String] :message the explaination of why the example failed
|
676
|
+
@option data [String] :exception the content of the exception
|
677
|
+
@option data [String] :backtrace the backtrace of the exception (a single new-line separated string)
|
678
|
+
@return [nil]
|
679
|
+
=end
|
680
|
+
def display_failed_example data
|
681
|
+
@progress_bar.value += 1
|
682
|
+
top = model.insert("[FAILURE] #{data[:description]}", :error, nil).first
|
683
|
+
model.insert ['From:', data[:location]], :message, nil, :parent => top
|
684
|
+
ex_label = model.insert('Exception:', :message, nil, :parent => top).first
|
685
|
+
exception_body = "#{data[:message]} (#{data[:exception]})".split_lines.delete_if{|l| l.strip.empty?}
|
686
|
+
#exception_body may contain more than one line and some of them may be empty
|
687
|
+
model.set exception_body.shift, :message, ex_label.row, :col => 1, :parent => top
|
688
|
+
exception_body.each do |l|
|
689
|
+
unless l.strip.empty?
|
690
|
+
model.set l, :message, top.row_count, :col => 1, :parent => top
|
691
|
+
end
|
692
|
+
end
|
693
|
+
backtrace = data[:backtrace].split_lines
|
694
|
+
back_label, back = model.insert(['Backtrace:', backtrace.shift], :message, nil, :parent => top)
|
695
|
+
backtrace.each do |l|
|
696
|
+
model.insert [nil, l], :message, nil, :parent => back_label
|
697
|
+
end
|
698
|
+
top_index = filter.map_from_source(top.index)
|
699
|
+
view.collapse top_index
|
700
|
+
view.set_first_column_spanned top_index.row, Qt::ModelIndex.new, true
|
701
|
+
view.expand filter.map_from_source(back_label.index)
|
702
|
+
nil
|
703
|
+
end
|
704
|
+
|
705
|
+
=begin rdoc
|
706
|
+
Displays information about a pending example in the tool widget
|
707
|
+
|
708
|
+
@param [Hash] data
|
709
|
+
@option data [String] :location the line number where the error occurred
|
710
|
+
@option data [String] :description the name of the failed example
|
711
|
+
@option data [String] :message the explaination of why the example failed
|
712
|
+
@return [nil]
|
713
|
+
=end
|
714
|
+
def display_pending_example data
|
715
|
+
@progress_bar.value += 1
|
716
|
+
top = model.insert("[PENDING] #{data[:description]}", :warning, nil)[0]
|
717
|
+
model.insert ['From:', data[:location]], :message, nil, :parent => top
|
718
|
+
model.insert ['Message: ', "#{data[:message]} (#{data[:exception]})"], :message, nil, :parent => top
|
719
|
+
nil
|
720
|
+
end
|
721
|
+
|
722
|
+
=begin rdoc
|
723
|
+
Displays a summary of the spec run in the tool widget
|
724
|
+
|
725
|
+
The summary is a single title line which contains the number or successful, pending
|
726
|
+
and failed example.
|
727
|
+
|
728
|
+
@param [Hash] data
|
729
|
+
@option data [Integer] :total the number of run examples
|
730
|
+
@option data [Integer] :passed the number of passed examples
|
731
|
+
@option data [Integer] :failed the number of failed examples
|
732
|
+
@option data [Integer] :pending the number of pending examples
|
733
|
+
@return [nil]
|
734
|
+
=end
|
735
|
+
def display_summary data
|
736
|
+
@progress_bar.hide
|
737
|
+
if data[:passed] == data[:total]
|
738
|
+
self.title = "[SUMMARY] All #{data[:total]} examples passed"
|
739
|
+
set_output_type model.index(0,0), :message_good
|
740
|
+
else
|
741
|
+
text = "[SUMMARY] Examples: #{data[:total]}"
|
742
|
+
text << " Failed: #{data[:failure]}" if data[:failure] > 0
|
743
|
+
text << " Pending: #{data[:pending]}" if data[:pending] > 0
|
744
|
+
text << " Passed: #{data[:passed]}"
|
745
|
+
self.title = text
|
746
|
+
type = data[:failure] > 0 ? :message_bad : :message
|
747
|
+
set_output_type model.index(0,0), type
|
748
|
+
end
|
749
|
+
nil
|
750
|
+
end
|
751
|
+
|
752
|
+
=begin rdoc
|
753
|
+
Override of {OutputWidget#title=}
|
754
|
+
|
755
|
+
It's needed to have the title element span all columns
|
756
|
+
|
757
|
+
@param [String] val the new title
|
758
|
+
=end
|
759
|
+
def title= val
|
760
|
+
super
|
761
|
+
model.item(0,0).tool_tip = val
|
762
|
+
view.set_first_column_spanned 0, Qt::ModelIndex.new, true
|
763
|
+
end
|
764
|
+
|
765
|
+
private
|
766
|
+
|
767
|
+
=begin rdoc
|
768
|
+
Resets the tool widget and sets the cursor to busy
|
769
|
+
@return [nil]
|
770
|
+
=end
|
771
|
+
def spec_started
|
772
|
+
@progress_bar.maximum = 0
|
773
|
+
@progress_bar.value = 0
|
774
|
+
@progress_bar.show
|
775
|
+
@progress_bar.tool_tip = ''
|
776
|
+
actions['show_stderr'].checked = false
|
777
|
+
self.cursor = Qt::Cursor.new(Qt::BusyCursor)
|
778
|
+
nil
|
779
|
+
end
|
780
|
+
|
781
|
+
=begin rdoc
|
782
|
+
Does the necessary cleanup for when spec finishes running
|
783
|
+
|
784
|
+
It hides the progress widget and restores the default cursor.
|
785
|
+
|
786
|
+
@param [Integer] code the exit code
|
787
|
+
@param [String] reason why the program exited
|
788
|
+
@return [nil]
|
789
|
+
=end
|
790
|
+
def spec_finished code, reason
|
791
|
+
@progress_bar.hide
|
792
|
+
@progress_bar.value = 0
|
793
|
+
@progress_bar.maximum = 100
|
794
|
+
self.set_focus
|
795
|
+
unset_cursor
|
796
|
+
unless reason == 'killed'
|
797
|
+
non_stderr_types = %w[message message_good message_bad warning error]
|
798
|
+
only_stderr = !model.item(0,0).text.match(/^\[SUMMARY\]/)
|
799
|
+
if only_stderr
|
800
|
+
1.upto(model.row_count - 1) do |i|
|
801
|
+
if non_stderr_types.include? model.item(i,0).data(OutputWidget::OutputTypeRole).to_string
|
802
|
+
only_stderr = false
|
803
|
+
break
|
804
|
+
end
|
805
|
+
end
|
806
|
+
end
|
807
|
+
if only_stderr
|
808
|
+
actions['show_stderr'].checked = true
|
809
|
+
model.insert "spec wasn't able to run the examples", :message_bad, nil
|
810
|
+
end
|
811
|
+
end
|
812
|
+
nil
|
813
|
+
end
|
814
|
+
|
815
|
+
=begin rdoc
|
816
|
+
Creates the additional actions.
|
817
|
+
|
818
|
+
It adds a single action, which allows the user to chose whether messages from
|
819
|
+
standard error should be displayed or not.
|
820
|
+
|
821
|
+
@return [nil]
|
822
|
+
=end
|
823
|
+
def setup_actions
|
824
|
+
action_list << nil << 'show_stderr'
|
825
|
+
a = KDE::ToggleAction.new 'S&how Standard Error', self
|
826
|
+
actions['show_stderr'] = a
|
827
|
+
a.checked = false
|
828
|
+
connect a, SIGNAL('toggled(bool)'), filter, SLOT('toggle_display_stderr(bool)')
|
829
|
+
end
|
830
|
+
|
831
|
+
=begin rdoc
|
832
|
+
Override of {OutputWidget#find_filename_in_index}
|
833
|
+
|
834
|
+
It works as the base class method, but, if it doesn't find a result in _idx_,
|
835
|
+
it looks for it in the parent indexes
|
836
|
+
|
837
|
+
@param [Qt::ModelIndex] idx the index where to look for a file name
|
838
|
+
@return [Array<String,Integer>,String,nil] see {OutputWidget#find_filename_in_index}
|
839
|
+
=end
|
840
|
+
def find_filename_in_index idx
|
841
|
+
res = super
|
842
|
+
unless res
|
843
|
+
idx = idx.parent while idx.parent.valid?
|
844
|
+
idx = idx.child(0,1)
|
845
|
+
res = super idx if idx.valid?
|
846
|
+
end
|
847
|
+
res
|
848
|
+
end
|
849
|
+
|
850
|
+
=begin rdoc
|
851
|
+
Override of {OutputWidget#text_for_clipboard}
|
852
|
+
|
853
|
+
@param [<Qt::ModelIndex>] idxs the selected indexes
|
854
|
+
@return [QString] the text to copy to the clipboard
|
855
|
+
=end
|
856
|
+
def text_for_clipboard idxs
|
857
|
+
order = {}
|
858
|
+
idxs.each do |i|
|
859
|
+
val = []
|
860
|
+
parent = i
|
861
|
+
while parent.parent.valid?
|
862
|
+
parent = parent.parent
|
863
|
+
val.unshift parent.row
|
864
|
+
end
|
865
|
+
val << [i.row, i.column]
|
866
|
+
order[val] = i
|
867
|
+
end
|
868
|
+
order = order.sort do |a, b|
|
869
|
+
a, b = a[0], b[0]
|
870
|
+
res = a[0..-2] <=> b[0..-2]
|
871
|
+
if res == 0 then a[-1] <=> b[-1]
|
872
|
+
else res
|
873
|
+
end
|
874
|
+
end
|
875
|
+
prev = order.shift[1]
|
876
|
+
text = prev.data.valid? ? prev.data.to_string : ''
|
877
|
+
order.each do |_, v|
|
878
|
+
text << ( (prev.parent == v.parent and prev.row == v.row) ? "\t" : "\n")
|
879
|
+
text << (v.data.valid? ? v.data.to_string : '')
|
880
|
+
prev = v
|
881
|
+
end
|
882
|
+
text
|
883
|
+
end
|
884
|
+
|
885
|
+
end
|
886
|
+
|
887
|
+
=begin rdoc
|
888
|
+
Project widget for the RSpec frontend plugin
|
889
|
+
=end
|
890
|
+
class ProjectWidget < ProjectConfigWidget
|
891
|
+
|
892
|
+
=begin rdoc
|
893
|
+
@param [Qt::Widget,nil] prj the parent widget
|
894
|
+
=end
|
895
|
+
def initialize prj
|
896
|
+
super
|
897
|
+
@ui = Ui::RSpecProjectWidget.new
|
898
|
+
@ui.setupUi self
|
899
|
+
end
|
900
|
+
|
901
|
+
private
|
902
|
+
|
903
|
+
=begin rdoc
|
904
|
+
Sets the text of the pattern widget
|
905
|
+
@param [Array<String>] the pattern to use. They'll be joined with commas to create
|
906
|
+
the text to put in the widget
|
907
|
+
=end
|
908
|
+
def pattern= value
|
909
|
+
value.join ', '
|
910
|
+
end
|
911
|
+
|
912
|
+
=begin rdoc
|
913
|
+
Parses the content of the pattern widget
|
914
|
+
@return [Array<String>] an array containing the patterns
|
915
|
+
=end
|
916
|
+
def pattern
|
917
|
+
@ui._rspec__spec_pattern.text.split(/;\s*/)
|
918
|
+
end
|
919
|
+
|
920
|
+
=begin rdoc
|
921
|
+
Changes the text of the "RSpec options" widget
|
922
|
+
|
923
|
+
@param [Array<String>] value the options to pass to spec. They'll be joined with
|
924
|
+
spaces
|
925
|
+
=end
|
926
|
+
def spec_options= value
|
927
|
+
@ui._rspec__options.text = value.join ' '
|
928
|
+
end
|
929
|
+
|
930
|
+
=begin rdoc
|
931
|
+
Parses the text of the "RSpec options" widget
|
932
|
+
|
933
|
+
@return [Array<String>] an array with the options to pass to spec (options with
|
934
|
+
quotes around them keep them, as described in {Shellwords.split_with_quotes})
|
935
|
+
=end
|
936
|
+
def spec_options
|
937
|
+
Shellwords.split_with_quotes @ui._rspec__options.text
|
938
|
+
end
|
939
|
+
|
940
|
+
end
|
941
|
+
|
942
|
+
|
943
|
+
end
|
944
|
+
|
945
|
+
end
|