ruber 0.0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. data/COPYING +339 -0
  2. data/INSTALL +137 -0
  3. data/LICENSE +8 -0
  4. data/bin/ruber +65 -0
  5. data/data/share/apps/ruber/core_components.yaml +31 -0
  6. data/data/share/apps/ruber/ruberui.rc +109 -0
  7. data/data/share/icons/ruber.png +0 -0
  8. data/data/share/pixmaps/ruby.png +0 -0
  9. data/icons/ruber-16.png +0 -0
  10. data/icons/ruber-32.png +0 -0
  11. data/icons/ruber-48.png +0 -0
  12. data/icons/ruber-8.png +0 -0
  13. data/lib/ruber/application/application.rb +288 -0
  14. data/lib/ruber/application/plugin.yaml +11 -0
  15. data/lib/ruber/component_manager.rb +899 -0
  16. data/lib/ruber/config/config.rb +82 -0
  17. data/lib/ruber/config/plugin.yaml +3 -0
  18. data/lib/ruber/document_project.rb +209 -0
  19. data/lib/ruber/documents/document_list.rb +416 -0
  20. data/lib/ruber/documents/plugin.yaml +4 -0
  21. data/lib/ruber/editor/document.rb +506 -0
  22. data/lib/ruber/editor/editor_view.rb +167 -0
  23. data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
  24. data/lib/ruber/exception_widgets.rb +245 -0
  25. data/lib/ruber/external_program_plugin.rb +397 -0
  26. data/lib/ruber/filtered_output_widget.rb +342 -0
  27. data/lib/ruber/gui_states_handler.rb +231 -0
  28. data/lib/ruber/kde_config_option_backend.rb +167 -0
  29. data/lib/ruber/kde_sugar.rb +249 -0
  30. data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
  31. data/lib/ruber/main_window/main_window.rb +524 -0
  32. data/lib/ruber/main_window/main_window_actions.rb +537 -0
  33. data/lib/ruber/main_window/main_window_internal.rb +239 -0
  34. data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
  35. data/lib/ruber/main_window/output_color_widget.rb +35 -0
  36. data/lib/ruber/main_window/plugin.yaml +58 -0
  37. data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
  38. data/lib/ruber/main_window/status_bar.rb +156 -0
  39. data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
  40. data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
  41. data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
  42. data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
  43. data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
  44. data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
  45. data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
  46. data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
  47. data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
  48. data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
  49. data/lib/ruber/main_window/workspace.rb +442 -0
  50. data/lib/ruber/output_widget.rb +1093 -0
  51. data/lib/ruber/plugin.rb +264 -0
  52. data/lib/ruber/plugin_like.rb +589 -0
  53. data/lib/ruber/plugin_specification.rb +106 -0
  54. data/lib/ruber/plugin_specification_reader.rb +451 -0
  55. data/lib/ruber/project.rb +493 -0
  56. data/lib/ruber/project_backend.rb +105 -0
  57. data/lib/ruber/projects/plugin.yaml +11 -0
  58. data/lib/ruber/projects/project_files_list.rb +314 -0
  59. data/lib/ruber/projects/project_files_widget.rb +301 -0
  60. data/lib/ruber/projects/project_list.rb +314 -0
  61. data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
  62. data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
  63. data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
  64. data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
  65. data/lib/ruber/qt_sugar.rb +673 -0
  66. data/lib/ruber/settings_container.rb +515 -0
  67. data/lib/ruber/settings_dialog.rb +244 -0
  68. data/lib/ruber/settings_dialog_manager.rb +503 -0
  69. data/lib/ruber/utils.rb +414 -0
  70. data/lib/ruber/yaml_option_backend.rb +159 -0
  71. data/outsider_files +15 -0
  72. data/plugins/autosave/autosave.rb +404 -0
  73. data/plugins/autosave/plugin.yaml +16 -0
  74. data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
  75. data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
  76. data/plugins/command/command.png +0 -0
  77. data/plugins/command/command.rb +74 -0
  78. data/plugins/command/plugin.yaml +11 -0
  79. data/plugins/find_in_files/find_in_files.rb +337 -0
  80. data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
  81. data/plugins/find_in_files/find_in_files_ui.rc +11 -0
  82. data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
  83. data/plugins/find_in_files/plugin.yaml +23 -0
  84. data/plugins/find_in_files/ui/config_widget.rb +58 -0
  85. data/plugins/find_in_files/ui/config_widget.ui +41 -0
  86. data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
  87. data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
  88. data/plugins/project_browser/plugin.yaml +10 -0
  89. data/plugins/project_browser/project_browser.rb +245 -0
  90. data/plugins/rake/plugin.yaml +39 -0
  91. data/plugins/rake/rake.png +0 -0
  92. data/plugins/rake/rake.rb +567 -0
  93. data/plugins/rake/rake_extension.rb +153 -0
  94. data/plugins/rake/rake_widgets.rb +615 -0
  95. data/plugins/rake/rakeui.rc +27 -0
  96. data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
  97. data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
  98. data/plugins/rake/ui/choose_task_widget.rb +77 -0
  99. data/plugins/rake/ui/choose_task_widget.ui +72 -0
  100. data/plugins/rake/ui/config_widget.rb +127 -0
  101. data/plugins/rake/ui/config_widget.ui +123 -0
  102. data/plugins/rake/ui/project_widget.rb +217 -0
  103. data/plugins/rake/ui/project_widget.ui +246 -0
  104. data/plugins/rspec/plugin.yaml +30 -0
  105. data/plugins/rspec/rspec.png +0 -0
  106. data/plugins/rspec/rspec.rb +945 -0
  107. data/plugins/rspec/rspec.svg +90 -0
  108. data/plugins/rspec/rspecui.rc +20 -0
  109. data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
  110. data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
  111. data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
  112. data/plugins/ruby_development/plugin.yaml +27 -0
  113. data/plugins/ruby_development/ruby_development.png +0 -0
  114. data/plugins/ruby_development/ruby_development.rb +453 -0
  115. data/plugins/ruby_development/ruby_developmentui.rc +19 -0
  116. data/plugins/ruby_development/ui/project_widget.rb +112 -0
  117. data/plugins/ruby_development/ui/project_widget.ui +108 -0
  118. data/plugins/ruby_runner/config_widget.rb +116 -0
  119. data/plugins/ruby_runner/plugin.yaml +26 -0
  120. data/plugins/ruby_runner/project_widget.rb +62 -0
  121. data/plugins/ruby_runner/ruby.png +0 -0
  122. data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
  123. data/plugins/ruby_runner/ruby_runner.rb +411 -0
  124. data/plugins/ruby_runner/ui/config_widget.rb +92 -0
  125. data/plugins/ruby_runner/ui/config_widget.ui +91 -0
  126. data/plugins/ruby_runner/ui/project_widget.rb +60 -0
  127. data/plugins/ruby_runner/ui/project_widget.ui +48 -0
  128. data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
  129. data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
  130. data/plugins/state/plugin.yaml +28 -0
  131. data/plugins/state/state.rb +520 -0
  132. data/plugins/state/ui/config_widget.rb +92 -0
  133. data/plugins/state/ui/config_widget.ui +89 -0
  134. data/plugins/syntax_checker/plugin.yaml +18 -0
  135. data/plugins/syntax_checker/syntax_checker.rb +662 -0
  136. data/ruber.desktop +10 -0
  137. data/spec/annotation_model_spec.rb +174 -0
  138. data/spec/common.rb +119 -0
  139. data/spec/component_manager_spec.rb +1259 -0
  140. data/spec/document_list_spec.rb +626 -0
  141. data/spec/document_project_spec.rb +373 -0
  142. data/spec/document_spec.rb +779 -0
  143. data/spec/editor_view_spec.rb +167 -0
  144. data/spec/external_program_plugin_spec.rb +676 -0
  145. data/spec/filtered_output_widget_spec.rb +642 -0
  146. data/spec/gui_states_handler_spec.rb +304 -0
  147. data/spec/kde_config_option_backend_spec.rb +214 -0
  148. data/spec/kde_sugar_spec.rb +101 -0
  149. data/spec/ktexteditor_wrapper_spec.rb +305 -0
  150. data/spec/output_widget_spec.rb +1703 -0
  151. data/spec/plugin_spec.rb +1393 -0
  152. data/spec/plugin_specification_reader_spec.rb +1765 -0
  153. data/spec/plugin_specification_spec.rb +401 -0
  154. data/spec/project_backend_spec.rb +172 -0
  155. data/spec/project_files_list_spec.rb +401 -0
  156. data/spec/project_list_spec.rb +511 -0
  157. data/spec/project_spec.rb +990 -0
  158. data/spec/qt_sugar_spec.rb +328 -0
  159. data/spec/settings_container_spec.rb +617 -0
  160. data/spec/settings_dialog_manager_spec.rb +773 -0
  161. data/spec/settings_dialog_spec.rb +419 -0
  162. data/spec/state_spec.rb +991 -0
  163. data/spec/utils_spec.rb +406 -0
  164. data/spec/workspace_spec.rb +869 -0
  165. data/spec/yaml_option_backend_spec.rb +246 -0
  166. 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', &current_prc
102
+ register_action_handler 'rspec-run_current_line', &current_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@ &rarr; @:spec@
369
+ * @:rspec/options@ &rarr; @:spec_options@
370
+ * @:rspec/spec_directory@ &rarr; @:specs_dir@
371
+ * @:rspec/spec_files@ &rarr; @:filter@
372
+ * @:rspec/spec_pattern@ &rarr; @:pattern@
373
+ * @:rspec/full_backtraces@ &rarr; @: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