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.
- 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
data/lib/ruber/utils.rb
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright (C) 2010 by Stefano Crocco
|
|
3
|
+
stefano.crocco@alice.it
|
|
4
|
+
|
|
5
|
+
This program is free software; you can redistribute it andor modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program; if not, write to the
|
|
17
|
+
Free Software Foundation, Inc.,
|
|
18
|
+
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
require 'delegate'
|
|
22
|
+
require 'enumerator'
|
|
23
|
+
require 'forwardable'
|
|
24
|
+
require 'pathname'
|
|
25
|
+
require 'shellwords'
|
|
26
|
+
require 'ostruct'
|
|
27
|
+
|
|
28
|
+
require 'dictionary'
|
|
29
|
+
require 'facets/boolean'
|
|
30
|
+
require 'facets/enumerable/mash'
|
|
31
|
+
|
|
32
|
+
module Ruber
|
|
33
|
+
|
|
34
|
+
=begin rdoc
|
|
35
|
+
The directory where the Ruber core files are
|
|
36
|
+
=end
|
|
37
|
+
RUBER_LIB_DIR = File.dirname(File.expand_path(__FILE__))
|
|
38
|
+
|
|
39
|
+
=begin rdoc
|
|
40
|
+
The Ruber @data@ directory
|
|
41
|
+
=end
|
|
42
|
+
RUBER_DATA_DIR = File.expand_path File.join(RUBER_LIB_DIR, '..', 'data')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module Kernel
|
|
46
|
+
|
|
47
|
+
=begin rdoc
|
|
48
|
+
Whether an object is _similar_ to a string
|
|
49
|
+
@return [Boolean] *true* if the object is similar to a string and *false* otherwise. In particular
|
|
50
|
+
currently it returns *true* for strings and symbol and *false* for all other objects.
|
|
51
|
+
=end
|
|
52
|
+
def string_like?
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
=begin rdoc
|
|
57
|
+
@return [Binding] the binding of the object
|
|
58
|
+
=end
|
|
59
|
+
def obj_binding
|
|
60
|
+
binding
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
alias :same? :equal?
|
|
64
|
+
|
|
65
|
+
=begin rdoc
|
|
66
|
+
Executes the body of the block without emitting warnings
|
|
67
|
+
|
|
68
|
+
This method sets the @$VERBOSE@ global variable to *nil* (which disables warnings)
|
|
69
|
+
before calling the block and restores it to the original value after executing the
|
|
70
|
+
block (even if the block raises an exception)
|
|
71
|
+
@yield
|
|
72
|
+
@return [Object] the value returned by the block
|
|
73
|
+
=end
|
|
74
|
+
def silently
|
|
75
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
|
76
|
+
begin yield
|
|
77
|
+
ensure
|
|
78
|
+
$VERBOSE = old_verbose
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class Object
|
|
85
|
+
|
|
86
|
+
=begin rdoc
|
|
87
|
+
Prints the given object and the position from which the method was called
|
|
88
|
+
@param [Object] the object to print on screen (its @inspect@ method will be used)
|
|
89
|
+
@return [nil]
|
|
90
|
+
=end
|
|
91
|
+
def debug obj
|
|
92
|
+
$stderr.puts "DEBUG(#{caller[0][/^[^:]+:\d+/]}): #{obj.inspect}\n"
|
|
93
|
+
end
|
|
94
|
+
alias_method :d, :debug
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class OpenStruct
|
|
99
|
+
#This is needed for compatibility with ruby 1.8, where there's an Object#type
|
|
100
|
+
#deprecated method which forbids OpenStruct to define its own, thus breaking
|
|
101
|
+
#the type entry of Options objects
|
|
102
|
+
undef_method :type rescue nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class Object
|
|
106
|
+
|
|
107
|
+
=begin rdoc
|
|
108
|
+
Encloses *self* in an array unless it's already an array
|
|
109
|
+
|
|
110
|
+
Unlike @Kernel#Array@, this doesn't use the @to_ary@ or @to_a@ methods
|
|
111
|
+
|
|
112
|
+
@return [Array] *self* enclosed in an array
|
|
113
|
+
=end
|
|
114
|
+
def to_array
|
|
115
|
+
[self]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class Array
|
|
121
|
+
|
|
122
|
+
=begin rdoc
|
|
123
|
+
Override of {Object#to_array}
|
|
124
|
+
|
|
125
|
+
@return [Array] *self*
|
|
126
|
+
=end
|
|
127
|
+
def to_array
|
|
128
|
+
self
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if RUBY_VERSION.match /8/
|
|
132
|
+
=begin rdoc
|
|
133
|
+
Choose a random element or n random elements
|
|
134
|
+
|
|
135
|
+
It's a version for ruby 1.8.7 of Array#sample from ruby 1.9. It uses Array#choice
|
|
136
|
+
which only exists in ruby 1.8.7 but this doesn't matter since this method is only
|
|
137
|
+
defined in ruby 1.8.7
|
|
138
|
+
|
|
139
|
+
*Note:* ruby 1.9 already defines this method, so the original version is kept
|
|
140
|
+
|
|
141
|
+
@param [Integer] n the number of elements to sample
|
|
142
|
+
@return [<Object>,Object] if _n_ is 1, then a single element is returned. If _n_
|
|
143
|
+
is greater than one, then an array of _n_ elements randomly chosen from those in
|
|
144
|
+
the array are returned (without duplicates). If _n_ is greater than the size
|
|
145
|
+
of the array, then the returned array will have the size of the array
|
|
146
|
+
=end
|
|
147
|
+
def sample n = 1
|
|
148
|
+
if n == 1 then choice
|
|
149
|
+
else
|
|
150
|
+
a = dup
|
|
151
|
+
n = [a.size, n].min
|
|
152
|
+
res = []
|
|
153
|
+
n.times do
|
|
154
|
+
res << a.delete(rand(a.size))
|
|
155
|
+
end
|
|
156
|
+
res
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
=begin rdoc
|
|
162
|
+
Converts an array to a hash
|
|
163
|
+
|
|
164
|
+
Depending on the value of the argument, this method can work as the reverse of
|
|
165
|
+
@Hash#to_a@ or as @Hash.[]@.
|
|
166
|
+
|
|
167
|
+
In the first case, each element of the array must be an array of size 2. The first
|
|
168
|
+
element of each inner array will be used as key, while the second will be used as
|
|
169
|
+
the corresponding value.
|
|
170
|
+
|
|
171
|
+
In the second case, this method works exactly as @Hash.[]@
|
|
172
|
+
@param [Boolean] pairs whether to work as the inverse of @Hash#to_a@ or as @Hash.[]@
|
|
173
|
+
@return [Hash] a hash built as described above
|
|
174
|
+
=end
|
|
175
|
+
def to_h pairs = true
|
|
176
|
+
if pairs then self.inject({}){|res, i| res[i[0]] = i[1]; res}
|
|
177
|
+
else Hash[*self]
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
=begin rdoc
|
|
182
|
+
Whether the array contains a single element and that element is equal to a given value
|
|
183
|
+
@param [Object] value the object to compare the only element of the array
|
|
184
|
+
@return [Boolean] *true* if the array has size 1 and its element is equal (according
|
|
185
|
+
to @==@) to _value_ and *false* otherwise.
|
|
186
|
+
=end
|
|
187
|
+
def only? value
|
|
188
|
+
self.size == 1 and self[0] == value
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
class String
|
|
194
|
+
|
|
195
|
+
=begin rdoc
|
|
196
|
+
Splits the string in lines.
|
|
197
|
+
|
|
198
|
+
It's a shortcut for str.split("\n")
|
|
199
|
+
|
|
200
|
+
@return [<String>] an array containing the lines which make up the string
|
|
201
|
+
=end
|
|
202
|
+
def split_lines
|
|
203
|
+
split "\n"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
=begin rdoc
|
|
207
|
+
Overryde of {Kernel#string_like?}
|
|
208
|
+
|
|
209
|
+
@return [Boolean] *true*
|
|
210
|
+
=end
|
|
211
|
+
def string_like?
|
|
212
|
+
true
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
class Symbol
|
|
218
|
+
|
|
219
|
+
=begin rdoc
|
|
220
|
+
Overryde of {Kernel#string_like?}
|
|
221
|
+
|
|
222
|
+
@return [Boolean] *true*
|
|
223
|
+
=end
|
|
224
|
+
def string_like?
|
|
225
|
+
true
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
class Pathname
|
|
231
|
+
|
|
232
|
+
=begin rdoc
|
|
233
|
+
Whether the pathname represents a file or directory under the directory represented
|
|
234
|
+
by another pathname
|
|
235
|
+
|
|
236
|
+
@param [Pathname,String] the string or pathname representing the directory which
|
|
237
|
+
can be parent of *self*
|
|
238
|
+
=end
|
|
239
|
+
def child_of? other
|
|
240
|
+
other = Pathname.new(other) if other.is_a? String
|
|
241
|
+
self.relative_path_from(other).to_s[0..2]!= '../'
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
class Dictionary
|
|
247
|
+
|
|
248
|
+
# In ruby 1.9, it seems that passing a lambda to one of the Array#each or similar
|
|
249
|
+
# methods doesn't unpack the elements of the array, so the call to the lambda
|
|
250
|
+
# fails because of wrong number of arguments
|
|
251
|
+
if RUBY_VERSION.match /9/
|
|
252
|
+
|
|
253
|
+
=begin rdoc
|
|
254
|
+
@private
|
|
255
|
+
Works as the method with the same name in facets but works around a bug with facets
|
|
256
|
+
and ruby 1.9
|
|
257
|
+
=end
|
|
258
|
+
def order_by_value
|
|
259
|
+
@order_by = lambda { |i| i[0] }
|
|
260
|
+
order
|
|
261
|
+
self
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
=begin rdoc
|
|
265
|
+
@private
|
|
266
|
+
Works as the method with the same name in facets but works around a bug with facets
|
|
267
|
+
and ruby 1.9
|
|
268
|
+
=end
|
|
269
|
+
def order_by_key
|
|
270
|
+
@order_by = lambda { |i| i[0] }
|
|
271
|
+
order
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
=begin rdoc
|
|
278
|
+
Calls the block passing it each key and the corresponding value starting from the
|
|
279
|
+
last
|
|
280
|
+
@yield key, value
|
|
281
|
+
@return [Dictionary] *self*
|
|
282
|
+
=end
|
|
283
|
+
def reverse_each
|
|
284
|
+
order.reverse_each{|k| yield k, @hash[k] }
|
|
285
|
+
self
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
module Enumerable
|
|
291
|
+
|
|
292
|
+
=begin rdoc
|
|
293
|
+
Finds the first element for which the block returns a true value and returns the
|
|
294
|
+
value of the block
|
|
295
|
+
|
|
296
|
+
It works like @Enumerable#find@, but, instead of returning the element for which
|
|
297
|
+
the block returned a true value, it returns the value returned by the block.
|
|
298
|
+
@yield obj
|
|
299
|
+
@return [Object, nil] the first non-false value returned by the block or *nil*
|
|
300
|
+
if the block returns *nil* or *false* for all elements
|
|
301
|
+
|
|
302
|
+
@example
|
|
303
|
+
[1, 2, 3, 4].find!{|i| 2*i if i > 2}
|
|
304
|
+
=> 6 #(3*2)
|
|
305
|
+
=end
|
|
306
|
+
def find!
|
|
307
|
+
each do |obj|
|
|
308
|
+
res = yield obj
|
|
309
|
+
return res if res
|
|
310
|
+
end
|
|
311
|
+
nil
|
|
312
|
+
end
|
|
313
|
+
alias_method :find_and_map, :find!
|
|
314
|
+
|
|
315
|
+
alias_method :map_hash, :mash
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
module Ruber
|
|
319
|
+
|
|
320
|
+
=begin rdoc
|
|
321
|
+
Module for objects which can be activated and disactivated. It provides methods
|
|
322
|
+
for inquiring the active state and to change it. If the object is a @Qt::Object@,
|
|
323
|
+
and has an @activated()@ and a @deactivated@ signals, they will be
|
|
324
|
+
emitted when the state change. If the object is not a @Qt::Object@, or doesn't have
|
|
325
|
+
those signals, everything else will still work (in the rest of the documentation,
|
|
326
|
+
every time emitting a signal is mentioned, it's understood that the signal won't
|
|
327
|
+
be emitted if it doesn't exist).
|
|
328
|
+
|
|
329
|
+
Classes mixing-in this module should initialize an instance variable called
|
|
330
|
+
<tt>@active</tt>. If they don't, one initialized to *nil* will be created the first
|
|
331
|
+
time it'll be needed (possibly with a warning).
|
|
332
|
+
=end
|
|
333
|
+
module Activable
|
|
334
|
+
|
|
335
|
+
=begin rdoc
|
|
336
|
+
@return [Boolean] whether the object is active or not
|
|
337
|
+
=end
|
|
338
|
+
def active?
|
|
339
|
+
@active
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
=begin rdoc
|
|
343
|
+
Makes the object inactive
|
|
344
|
+
|
|
345
|
+
If previously the object was active, emits the @deactivated@ signal
|
|
346
|
+
@return [nil]
|
|
347
|
+
=end
|
|
348
|
+
def deactivate
|
|
349
|
+
self.active = false
|
|
350
|
+
nil
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
=begin rdoc
|
|
354
|
+
Makes the object active
|
|
355
|
+
|
|
356
|
+
If previously the object was inactive, emits the @activated@ signal.
|
|
357
|
+
@return [nil]
|
|
358
|
+
=end
|
|
359
|
+
def activate
|
|
360
|
+
self.active = true
|
|
361
|
+
nil
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
=begin rdoc
|
|
365
|
+
Enables or disables the object
|
|
366
|
+
|
|
367
|
+
If the state of the object changes, the @activated@ or @deactivated@ signal is
|
|
368
|
+
emitted.
|
|
369
|
+
|
|
370
|
+
@param [Object] val whether the object should be activated or deactivated. If the
|
|
371
|
+
object is a true value, the object will be activated, otherwise it will be deactivated
|
|
372
|
+
@return [Object] _val_
|
|
373
|
+
=end
|
|
374
|
+
def active= val
|
|
375
|
+
old = @active
|
|
376
|
+
@active = val.to_bool
|
|
377
|
+
if old != @active
|
|
378
|
+
emit(@active ? activated : deactivated) rescue NameError
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
module Shellwords
|
|
387
|
+
|
|
388
|
+
=begin rdoc
|
|
389
|
+
Similar to @Shellwords.split@, but attempts to include quotes in the returned
|
|
390
|
+
array for strings containing spaces.
|
|
391
|
+
|
|
392
|
+
Since it's impossible to find out from the output of @Shellwords#split@ whether a string was
|
|
393
|
+
quoted with signle or double quotes, the following approach is taken: if the
|
|
394
|
+
string contains double quotes, it's sourrounded with single quotes; otherwise
|
|
395
|
+
double quotes are used.
|
|
396
|
+
|
|
397
|
+
TODO: improve on the above algorithm.
|
|
398
|
+
@param [String] str the string to split
|
|
399
|
+
@return [<String>] an array as with @Shellwords#split@ but with strings containing
|
|
400
|
+
whitespaces quoted according to the above algorithm
|
|
401
|
+
=end
|
|
402
|
+
def self.split_with_quotes str
|
|
403
|
+
res = split str
|
|
404
|
+
res.map! do |s|
|
|
405
|
+
unless s.include? ' ' then s
|
|
406
|
+
else
|
|
407
|
+
quote = s.include?('"') ? "'" : '"'
|
|
408
|
+
quote + s + quote
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
res
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Copyright (C) 2010 by Stefano Crocco
|
|
3
|
+
stefano.crocco@alice.it
|
|
4
|
+
|
|
5
|
+
This program is free software; you can redistribute it andor modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program; if not, write to the
|
|
17
|
+
Free Software Foundation, Inc.,
|
|
18
|
+
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
require 'facets/kernel/deep_copy'
|
|
22
|
+
|
|
23
|
+
module Ruber
|
|
24
|
+
|
|
25
|
+
=begin rdoc
|
|
26
|
+
Backend for <tt>SettingsContainer</tt> which writes the options to a YAML file,
|
|
27
|
+
as a nested hash, where the outer hash contains the groups and each inner hash,
|
|
28
|
+
which represents a group, contains the options. Here's an example of the YAML
|
|
29
|
+
file produced by this class.
|
|
30
|
+
|
|
31
|
+
:group1:
|
|
32
|
+
:opt1: value1
|
|
33
|
+
:opt2: value2
|
|
34
|
+
:opt3: value3
|
|
35
|
+
:group2:
|
|
36
|
+
:opt1: value4
|
|
37
|
+
:opt4: value5
|
|
38
|
+
:group3:
|
|
39
|
+
:opt5: value6
|
|
40
|
+
=end
|
|
41
|
+
class YamlSettingsBackend
|
|
42
|
+
|
|
43
|
+
=begin rdoc
|
|
44
|
+
Exception raised when attempted to parse an invalid YAML file
|
|
45
|
+
=end
|
|
46
|
+
class InvalidSettingsFile < StandardError
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
=begin rdoc
|
|
50
|
+
Creates a new YamlSettingsBackend corresponding to the YAML file with name _file_.
|
|
51
|
+
If the file exists, it's parsed and the content is stored internally in a hash.
|
|
52
|
+
If it doesn't exist, the object is initialized with a new hash.
|
|
53
|
+
|
|
54
|
+
If the file exists but isn't a valid YAML file, or if it doesn't contain a toplevel
|
|
55
|
+
hash, InvalidSettingsFile is raised. In this case, however, the object will be
|
|
56
|
+
fully initialized (with an empty hash) before the exception is raised. This means
|
|
57
|
+
that a class deriving from YamlSettingsBackend which wants to ignore errors due to
|
|
58
|
+
an invalid project file can simply do the following:
|
|
59
|
+
|
|
60
|
+
class CustomSettingsBackend < Ruber::Yaml::SettingsBackend
|
|
61
|
+
|
|
62
|
+
def initialize file
|
|
63
|
+
begin super
|
|
64
|
+
rescue Ruber::YamlSettingsBackend::InvalidSettingsFile
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
This way
|
|
71
|
+
|
|
72
|
+
CustomSettingsBackend.new('/path/to/invalid_file')
|
|
73
|
+
|
|
74
|
+
will return an option backend fully initialized. (This happens because of how
|
|
75
|
+
Class.new works: the object is first allocated, then its initialize method is called
|
|
76
|
+
and the object is returned. If an exception raised by initialize is rescued within
|
|
77
|
+
the initialize method, Class.new will never notice something went wrong and still
|
|
78
|
+
return the allocated object)
|
|
79
|
+
=end
|
|
80
|
+
def initialize file
|
|
81
|
+
@filename = file
|
|
82
|
+
if File.exist? file
|
|
83
|
+
@data = begin YAML.load(File.read(file))
|
|
84
|
+
rescue ArgumentError => e
|
|
85
|
+
@data = {}
|
|
86
|
+
raise InvalidSettingsFile, e.message
|
|
87
|
+
end
|
|
88
|
+
unless @data.is_a? Hash
|
|
89
|
+
@data = {}
|
|
90
|
+
raise InvalidSettingsFile, "The file #{file} isn\'t a valid option file"
|
|
91
|
+
end
|
|
92
|
+
else @data = {}
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
=begin rdoc
|
|
97
|
+
The name of the file associated with the backend. Note that there's no warranty
|
|
98
|
+
the file actually exists.
|
|
99
|
+
=end
|
|
100
|
+
def file
|
|
101
|
+
@filename
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
=begin rdoc
|
|
105
|
+
Returns the option corresponding to _opt_. _opt_ is an option object with
|
|
106
|
+
the characteristics specified in SettingsContainer#add_option. If an option with
|
|
107
|
+
the same name and value of _opt_ isn't included in the internal hash, the option
|
|
108
|
+
default value will be returned
|
|
109
|
+
=end
|
|
110
|
+
def [] opt
|
|
111
|
+
grp = @data.fetch(opt.group){return opt.default.deep_copy}
|
|
112
|
+
grp.fetch(opt.name, opt.default)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
=begin rdoc
|
|
116
|
+
Writes the options back to the YAML file (creating it if needed). _options_ is a
|
|
117
|
+
hash with option objects as keys and the corresponding values as entries. There
|
|
118
|
+
are two issues to be aware of:
|
|
119
|
+
* if one of the options in _options_ has a value which is equal to its default
|
|
120
|
+
value, it won't be written to file
|
|
121
|
+
* _options_ is interpreted as only containing the options which might have changed:
|
|
122
|
+
any option contained in the internal hash but not in _options_ is written back
|
|
123
|
+
to the file unchanged.
|
|
124
|
+
|
|
125
|
+
After having written the new options back to file, the internal hash is updated
|
|
126
|
+
=end
|
|
127
|
+
def write options
|
|
128
|
+
new_data = compute_data options
|
|
129
|
+
File.open(@filename, 'w'){|f| f.write YAML.dump(new_data)}
|
|
130
|
+
@data = new_data
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
=begin rdoc
|
|
136
|
+
Creates a hash with the options to be written to file. See +write+ for a more detailed
|
|
137
|
+
description of how this happens
|
|
138
|
+
=end
|
|
139
|
+
def compute_data options
|
|
140
|
+
new_data = Hash.new{|h, k| h[k] = {}}
|
|
141
|
+
removed = []
|
|
142
|
+
options.each_pair do |k, v|
|
|
143
|
+
if v != k.default then new_data[k.group][k.name] = v
|
|
144
|
+
else removed << [k.group, k.name]
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
@data.each_pair do |grp, data|
|
|
148
|
+
data.each_pair do |opt, val|
|
|
149
|
+
unless removed.include?([grp, opt])
|
|
150
|
+
new_data[grp][opt] ||= val
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
new_data
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|