metaruby 1.0.0.rc2 → 1.0.0.rc3

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.
@@ -1,15 +1,16 @@
1
1
  <% prefix = if ressource_dir[0, 1] == "/" then "file://"
2
2
  end
3
3
  %>
4
- <html>
4
+ <head>
5
5
  <link rel="stylesheet" href="<%= prefix %><%= File.join(ressource_dir, 'page.css') %>" type="text/css" />
6
6
  <script type="text/javascript" src="<%= prefix %><%= File.join(ressource_dir, 'jquery.min.js') %>"></script>
7
7
  <script type="text/javascript" src="<%= prefix %><%= File.join(ressource_dir, 'jquery.selectfilter.js') %>"></script>
8
- <% javascript.each do |js| %>
9
- <script type="text/javascript" src="<%= prefix %><%= File.expand_path(js, ressource_dir) %>"></script>
10
- <% end %>
8
+ <%= head.join("\n").gsub('${RESOURCE_DIR}', "#{prefix}#{ressource_dir}") %>
9
+ <% if page_name %>
11
10
  <title><%= page_name %></title>
12
- </html>
11
+ <% end %>
12
+ </head>
13
+ <%= scripts.join("\n").gsub('${RESOURCE_DIR}', "#{prefix}#{ressource_dir}") %>
13
14
  <body>
14
15
  <%= html_body(ressource_dir: ressource_dir) %>
15
16
  </body>
@@ -54,9 +54,12 @@ module MetaRuby
54
54
  # the model view
55
55
  attr_reader :central_splitter
56
56
 
57
- # A Page object with a #link_to method that is suitable for the
58
- # model browser
57
+ # A Page object tunes to create URIs for objects that are suitable
58
+ # for {#model_selector}
59
59
  class Page < HTML::Page
60
+ # Overloaded from {HTML::Page} to resolve object paths (in the
61
+ # constant hierarchy, e.g. A::B::C) into the corresponding
62
+ # path expected by {#model_selector} (e.g. /A/B/C)
60
63
  def uri_for(object)
61
64
  if object.respond_to?(:name) && (obj_name = object.name) && (obj_name =~ /^[\w:]+$/)
62
65
  path = obj_name.split("::")
@@ -66,7 +69,6 @@ module MetaRuby
66
69
  end
67
70
  end
68
71
 
69
-
70
72
  def initialize(main = nil, exception_view: nil)
71
73
  super(main)
72
74
 
@@ -95,6 +97,10 @@ module MetaRuby
95
97
  update_exceptions
96
98
  end
97
99
 
100
+ # Restore the state of this widget from settings previously saved
101
+ # with {#save_to_settings}
102
+ #
103
+ # @param [Qt::Settings] settings
98
104
  def restore_from_settings(settings)
99
105
  %w{central_splitter vertical_splitter}.each do |object_name|
100
106
  sizes = settings.value(object_name)
@@ -107,6 +113,9 @@ module MetaRuby
107
113
  end
108
114
  end
109
115
 
116
+ # Save the current state of this widget in the given settings
117
+ #
118
+ # @param [Qt::Settings] settings
110
119
  def save_to_settings(settings)
111
120
  %w{central_splitter vertical_splitter}.each do |object_name|
112
121
  sizes = send(object_name).sizes
@@ -115,7 +124,7 @@ module MetaRuby
115
124
  end
116
125
  end
117
126
 
118
- # Update the model selector after {register_type} got called
127
+ # Update the model selector after {#register_type} got called
119
128
  def update_model_selector
120
129
  model_selector.update
121
130
  end
@@ -126,7 +135,7 @@ module MetaRuby
126
135
  # It registers the given type on the model browser so that it gets
127
136
  # displayed there.
128
137
  #
129
- # You must call {update_model_selector} after this call for the
138
+ # You must call {#update_model_selector} after this call for the
130
139
  # modification to have any effect (i.e. for the newly registered
131
140
  # models to appear on the selector)
132
141
  #
@@ -204,7 +213,7 @@ module MetaRuby
204
213
  # Call to render the given model
205
214
  #
206
215
  # @param [Model] mod the model that should be rendered
207
- # @raises [ArgumentError] if there is no view available for the
216
+ # @raise [ArgumentError] if there is no view available for the
208
217
  # given model
209
218
  def render_model(mod, options = Hash.new)
210
219
  page.clear
@@ -1,20 +1,42 @@
1
1
  module MetaRuby
2
2
  module GUI
3
- # A Qt widget that allows to browse the models registered in the Ruby
4
- # constanat hierarchy
3
+ # A Qt widget based on {RubyConstantsItemModel} to browse a set of
4
+ # models, and display them when the user selects one
5
5
  class ModelSelector < Qt::Widget
6
- attr_reader :btn_type_filter_menu
6
+ # A per-type matching of the type to the actio that allows to
7
+ # filter/unfilter on this type
8
+ #
9
+ # @return [Hash<Module,Qt::Action>]
7
10
  attr_reader :type_filters
11
+
12
+ # The view that shows the object hierarchy
13
+ #
14
+ # @return [Qt::TreeView]
8
15
  attr_reader :model_list
16
+
17
+ # Qt model filter
18
+ # @return [Qt::SortFilterProxyModel]
9
19
  attr_reader :model_filter
20
+
21
+ # (see {RubyConstantsItemModel#type_info)
22
+ attr_reader :type_info
23
+
24
+ # The Qt item model that represents the object hierarchy
25
+ # @return [RubyConstantsItemModel]
26
+ attr_reader :browser_model
27
+
28
+ # @return [Qt::PushButton] a button allowing to filter models by
29
+ # type
30
+ attr_reader :btn_type_filter_menu
10
31
  # @return [Qt::LineEdit] the line edit widget that allows to modify
11
32
  # the tree view filter
12
33
  attr_reader :filter_box
13
- # @return [Qt::Completer] auto-completion for {filter_box}
34
+ # @return [Qt::Completer] auto-completion for {#filter_box}
14
35
  attr_reader :filter_completer
15
- attr_reader :type_info
16
- attr_reader :browser_model
17
36
 
37
+ # Create a new widget with an optional parent
38
+ #
39
+ # @param [Qt::Widget,nil] parent
18
40
  def initialize(parent = nil)
19
41
  super
20
42
 
@@ -35,6 +57,14 @@ module MetaRuby
35
57
  setTabOrder(filter_button, model_list)
36
58
  end
37
59
 
60
+ # Register a new object type
61
+ #
62
+ # @param [Module] model_base a module or class whose all objects of
63
+ # this type have as superclass
64
+ # @param [String] name the string that should be used to represent
65
+ # objects of this type
66
+ # @param [Integer] priority if an object's ancestry matches multiple
67
+ # types, only the ones of the highest priority will be retained
38
68
  def register_type(model_base, name, priority = 0)
39
69
  type_info[model_base] = RubyConstantsItemModel::TypeInfo.new(name, priority)
40
70
  action = Qt::Action.new(name, self)
@@ -47,11 +77,15 @@ module MetaRuby
47
77
  end
48
78
  end
49
79
 
80
+ # Update the view, reloading the underlying model
50
81
  def update
51
82
  update_model_filter
52
83
  reload
53
84
  end
54
85
 
86
+ # @api private
87
+ #
88
+ # Update {#model_filter} to match the current filter setup
55
89
  def update_model_filter
56
90
  type_rx = type_filters.map do |model_base, act|
57
91
  if act.checked?
@@ -74,6 +108,10 @@ module MetaRuby
74
108
  auto_open
75
109
  end
76
110
 
111
+ # Auto-open in the current state
112
+ #
113
+ # @param [Integer] threshold the method opens items whose number of
114
+ # children is lower than this threshold
77
115
  def auto_open(threshold = 5)
78
116
  current_level = [Qt::ModelIndex.new]
79
117
  while !current_level.empty?
@@ -97,6 +135,7 @@ module MetaRuby
97
135
  end
98
136
  end
99
137
 
138
+ # Tests if an object if a model
100
139
  def model?(obj)
101
140
  type_info.any? do |model_base, _|
102
141
  obj.kind_of?(model_base) ||
@@ -113,6 +152,9 @@ module MetaRuby
113
152
  end
114
153
  end
115
154
 
155
+ # @api private
156
+ #
157
+ # Helper method for {#select_first_item}
116
158
  def all_leaves(model, limit = nil, item = Qt::ModelIndex.new, result = [])
117
159
  if !model.hasChildren(item)
118
160
  result << item
@@ -131,12 +173,16 @@ module MetaRuby
131
173
  return result
132
174
  end
133
175
 
176
+ # Select the first displayed item
134
177
  def select_first_item
135
178
  if item = all_leaves(model_filter, 1).first
136
179
  model_list.setCurrentIndex(item)
137
180
  end
138
181
  end
139
182
 
183
+ # @api private
184
+ #
185
+ # Create and setup {#model_list}
140
186
  def setup_tree_view(layout)
141
187
  @model_list = Qt::TreeView.new(self)
142
188
  @model_filter = Qt::SortFilterProxyModel.new
@@ -169,6 +215,7 @@ module MetaRuby
169
215
  end
170
216
  signals 'model_selected(QVariant)'
171
217
 
218
+ # Reload the object model, keeping the current selection if possible
172
219
  def reload
173
220
  if current = current_selection
174
221
  current_module = current.this
@@ -202,12 +249,11 @@ module MetaRuby
202
249
  # control whether the filter should be reset if the index given as
203
250
  # parameter is currently filtered out
204
251
  #
205
- # @param [Qt::ModelIndex] an index valid in {browser_model}
206
- # @option options [Boolean] :reset_filter (true) if true, the filter
252
+ # @param [Qt::ModelIndex] source_index an index valid in {#browser_model}
253
+ # @param [Boolean] reset_filter if true, the filter
207
254
  # is reset if the requested index is currently filtered out
208
- # @return [Qt::ModelIndex] an index filtered by {model_filter}
209
- def map_index_from_source(source_index, options = Hash.new)
210
- options = Kernel.validate_options options, :reset_filter => true
255
+ # @return [Qt::ModelIndex] an index filtered by {#model_filter}
256
+ def map_index_from_source(source_index, reset_filter: true)
211
257
  index = model_filter.map_from_source(source_index)
212
258
  if !index
213
259
  return
@@ -1,5 +1,10 @@
1
1
  module MetaRuby
2
2
  module GUI
3
+ # Management of HTML rendering of objects of different types
4
+ #
5
+ # Objects of this class allow to register a set of renderers, dedicated
6
+ # to rendering objects of a certain type (defined as a superclass) and
7
+ # automatically switch between the rendering objects.
3
8
  class RenderingManager < Qt::Object
4
9
  # @return [#push] the page object on which we render
5
10
  attr_reader :page
@@ -10,15 +15,19 @@ module MetaRuby
10
15
  # matches will be used.
11
16
  # Do not modify directly, use {#register_type} instead
12
17
  attr_reader :available_renderers
13
- # The last used rendering objects
18
+ # The rendering object used last in {#render}
14
19
  attr_reader :current_renderer
15
20
 
21
+ # Create a rendering manager that acts on a given page
16
22
  def initialize(page = nil)
17
23
  super()
18
24
  @page = page
19
25
  @available_renderers = Hash.new
20
26
  end
21
27
 
28
+ # A list of exceptions that happened during rendering
29
+ #
30
+ # @return [Array<Exception>]
22
31
  def registered_exceptions
23
32
  if current_renderer.respond_to?(:registered_exceptions)
24
33
  current_renderer.registered_exceptions
@@ -32,12 +41,15 @@ module MetaRuby
32
41
  # It registers the given type on the model browser so that it gets
33
42
  # displayed there.
34
43
  #
35
- # @param [Class] type the base model class for the models that are
36
- # considered here
44
+ # @param [Class] type objects whose class or ancestry include 'type'
45
+ # will be rendered using the provided rendering class. If more tha
46
+ # none matches, the first one is used.
37
47
  # @param [Class] rendering_class a class from which a relevant
38
48
  # rendering object can be created. The generated instances must
39
49
  # follow the rules described in the documentation of
40
50
  # {ModelBrowser}
51
+ # @param [Hash] render_options a set of options that must be passed
52
+ # to the renderer's #render method
41
53
  def register_type(type, rendering_class, render_options = Hash.new)
42
54
  render = if rendering_class.kind_of?(Class)
43
55
  rendering_class.new(page)
@@ -60,37 +72,54 @@ module MetaRuby
60
72
  end
61
73
  end
62
74
 
75
+ # @api private
76
+ #
77
+ # Find a rendering object for the given object
78
+ #
79
+ # @param [Object] mod the object we need to render
80
+ # @return [(Class,(#render,Hash)),nil] either the base class,
81
+ # rendering object and rendering options that should be used for
82
+ # the given object, or nil if there are no matching rendering
83
+ # objects
63
84
  def find_renderer(mod)
64
85
  available_renderers.find do |model, _|
65
86
  mod.kind_of?(model) || (mod.kind_of?(Module) && model.kind_of?(Module) && mod <= model)
66
87
  end
67
88
  end
68
89
 
90
+ # Disable the current renderer
69
91
  def disable
70
92
  if current_renderer
71
93
  current_renderer.disable
72
94
  end
73
95
  end
74
96
 
97
+ # Enable the current renderer
75
98
  def enable
76
99
  if current_renderer
77
100
  current_renderer.enable
78
101
  end
79
102
  end
80
103
 
104
+ # Clear the current renderer
81
105
  def clear
82
106
  if current_renderer
83
107
  current_renderer.clear
84
108
  end
85
109
  end
86
110
 
87
-
88
111
  # Call to render the given model
89
112
  #
90
- # @param [Model] mod the model that should be rendered
91
- # @raises [ArgumentError] if there is no view available for the
113
+ # The renderer that has been used is made active (enabled) and
114
+ # stored in {#current_renderer}. The previous one is disabled and
115
+ # cleared.
116
+ #
117
+ # @param [Model] object the model that should be rendered
118
+ # @param [Hash] push_options options that should be passed to the
119
+ # object's renderer {#render} method
120
+ # @raise [ArgumentError] if there is no view available for the
92
121
  # given model
93
- def render(object, push_options = Hash.new)
122
+ def render(object, **push_options)
94
123
  _, (renderer, render_options) = find_renderer(object)
95
124
  if renderer
96
125
  if current_renderer
@@ -1,21 +1,69 @@
1
1
  module MetaRuby
2
2
  module GUI
3
- # A Qt item model that lists Ruby modules that match a given predicate
4
- # (given at construction time)
3
+ # A Qt item model that enumerates models stored in the Ruby constant
4
+ # hierarchy
5
+ #
6
+ # The model exposes all registered constants for which {#predicate}
7
+ # returns true in a hierarchy, allowing the user to interact with it.
8
+ #
9
+ # Discovery starts at Object
5
10
  class RubyConstantsItemModel < Qt::AbstractItemModel
11
+ # Stored per-module information test
6
12
  ModuleInfo = Struct.new :id, :name, :this, :parent, :children, :row, :types, :full_name, :keyword_string, :path
13
+
14
+ # Information about different model types
7
15
  TypeInfo = Struct.new :name, :priority, :color
8
16
 
17
+ # Predicate that filters objects in addition to {#excludes}
18
+ #
19
+ # Only objects for which #call returns true and the constants that
20
+ # contain them are exposed by this model
21
+ #
22
+ # @return [#call]
9
23
  attr_reader :predicate
24
+
25
+ # Explicitely excluded objects
26
+ #
27
+ # Set of objects that should be excluded from discovery, regardless
28
+ # of what {#predicate} would return from them.
29
+ #
30
+ # Note that such objects are not discovered at all, meaning that if
31
+ # they contain objects that should have been discovered, they won't
32
+ # be.
33
+ #
34
+ # @return [Set]
35
+ attr_reader :excludes
36
+
37
+ # A list of expected object types
38
+ #
39
+ # This is used to decide where a given object should be "attached"
40
+ # in the hierarchy. Matching types are stored in {ModuleInfo#types}.
41
+ #
42
+ # @return [{Class=>TypeInfo}]
43
+ attr_reader :type_info
44
+
45
+ # Mapping from module ID to a module object
46
+ #
47
+ # @return [{Integer=>ModuleInfo}]
10
48
  attr_reader :id_to_module
49
+
50
+ # Set of objects that have been filtered out by {#predicate}
11
51
  attr_reader :filtered_out_modules
12
- attr_reader :type_info
52
+
53
+ # Name of the root item
54
+ #
55
+ # @return [String]
13
56
  attr_accessor :title
57
+
58
+ # List of paths for each of the discovered objects
59
+ #
60
+ # @return [{Object=>String}]
14
61
  attr_reader :object_paths
15
- # @return [Set<Object>] a set of objects that should not be
16
- # discovered
17
- attr_reader :excludes
18
62
 
63
+ # Initialize this model for objects of the given type
64
+ #
65
+ # @param [Hash] type_info value for {#type_info}
66
+ # @param [#call] predicate the filter {#predicate}
19
67
  def initialize(type_info = Hash.new, &predicate)
20
68
  super()
21
69
  @predicate = predicate || proc { true }
@@ -28,6 +76,7 @@ module MetaRuby
28
76
  @object_paths = Hash.new
29
77
  end
30
78
 
79
+ # Discovers or rediscovers the objects
31
80
  def reload
32
81
  begin_reset_model
33
82
  @id_to_module = []
@@ -46,10 +95,19 @@ module MetaRuby
46
95
  end_reset_model
47
96
  end
48
97
 
98
+ # {ModuleInfo} for the root
49
99
  def root_info
50
100
  id_to_module.last
51
101
  end
52
102
 
103
+ # @api private
104
+ #
105
+ # Generate the path information, i.e. per-object path string
106
+ #
107
+ # @param [Hash] paths the generated paths (matches {#object_paths})
108
+ # @param [ModuleInfo] info the object information for the object to
109
+ # be discovered
110
+ # @param [String] current the path of 'info'
53
111
  def generate_paths(paths, info, current)
54
112
  info.children.each do |child|
55
113
  child_uri = current + '/' + child.name
@@ -58,6 +116,12 @@ module MetaRuby
58
116
  end
59
117
  end
60
118
 
119
+ # @api private
120
+ #
121
+ # Updates {ModuleInfo#types} so that it includes the type of its
122
+ # children
123
+ #
124
+ # @param [ModuleInfo] info the object info that should be updated
61
125
  def update_module_type_info(info)
62
126
  types = info.types.to_set
63
127
  info.children.each do |child_info|
@@ -68,6 +132,13 @@ module MetaRuby
68
132
  end.reverse
69
133
  end
70
134
 
135
+ # @api private
136
+ #
137
+ # Discovers an object and its children
138
+ #
139
+ # @param [Object] mod an object that should be discovered
140
+ # @param [Array] stack the current stack (to avoid infinite recursions)
141
+ # @return [ModuleInfo]
71
142
  def discover_module(mod, stack = Array.new)
72
143
  return if excludes.include?(mod)
73
144
  stack.push mod
@@ -143,13 +214,13 @@ module MetaRuby
143
214
  ensure stack.pop
144
215
  end
145
216
 
146
- def headerData(section, orientation, role)
147
- if role == Qt::DisplayRole && section == 0
148
- Qt::Variant.new(title)
149
- else Qt::Variant.new
150
- end
151
- end
152
-
217
+ # @api private
218
+ #
219
+ # Lazily computes the full name of a discovered object. It updates
220
+ # {ModuleInfo#full_name}
221
+ #
222
+ # @param [ModuleInfo] info
223
+ # @return [String]
153
224
  def compute_full_name(info)
154
225
  if name = info.full_name
155
226
  return name
@@ -164,6 +235,13 @@ module MetaRuby
164
235
  end
165
236
  end
166
237
 
238
+ # @api private
239
+ #
240
+ # Lazily compute the path of a discovered object. The result is
241
+ # stored in {ModuleInfo#path}
242
+ #
243
+ # @param [ModuleInfo] info
244
+ # @return [String]
167
245
  def compute_path(info)
168
246
  if path = info.path
169
247
  return path
@@ -173,6 +251,57 @@ module MetaRuby
173
251
  end
174
252
  end
175
253
 
254
+
255
+ # Resolves a {ModuleInfo} from a Qt::ModelIndex
256
+ def info_from_index(index)
257
+ if !index.valid?
258
+ return id_to_module.last
259
+ else
260
+ id_to_module[index.internal_id >> 1]
261
+ end
262
+ end
263
+
264
+ # Return the Qt::ModelIndex that represents a given object
265
+ #
266
+ # @return [Qt::ModelIndex,nil] the index, or nil if the object is
267
+ # not included in this model
268
+ def find_index_by_model(model)
269
+ if info = id_to_module.find { |info| info.this == model }
270
+ return create_index(info.row, 0, info.id)
271
+ end
272
+ end
273
+
274
+ # Returns the Qt::ModelIndex that matches a given path
275
+ #
276
+ # @param [Array<String>] path path to the desired object
277
+ # @return [Qt::ModelIndex,nil] the index, or nil if the path does
278
+ # not resolve to an object included in this model
279
+ def find_index_by_path(*path)
280
+ current = id_to_module.last
281
+ if path.first == current.name
282
+ path.shift
283
+ end
284
+
285
+ path.each do |name|
286
+ current = id_to_module.find do |info|
287
+ info.name == name && info.parent == current
288
+ end
289
+ return if !current
290
+ end
291
+ create_index(current.row, 0, current.id)
292
+ end
293
+
294
+ # @api private
295
+ #
296
+ # Lazily compute a comma-separated string that can be used to search
297
+ # for the given node. The result is stored in
298
+ # {ModuleInfo#keyword_string}
299
+ #
300
+ # The returned string is of the form
301
+ # type0[,type1...]:name0[,name1...]
302
+ #
303
+ # @param [ModuleInfo] info
304
+ # @return [String]
176
305
  def compute_keyword_string(info)
177
306
  if keywords = info.keyword_string
178
307
  return keywords
@@ -186,6 +315,15 @@ module MetaRuby
186
315
  end
187
316
  end
188
317
 
318
+ # Reimplemented for Qt model interface
319
+ def headerData(section, orientation, role)
320
+ if role == Qt::DisplayRole && section == 0
321
+ Qt::Variant.new(title)
322
+ else Qt::Variant.new
323
+ end
324
+ end
325
+
326
+ # Reimplemented for Qt model interface
189
327
  def data(index, role)
190
328
  if info = info_from_index(index)
191
329
  if role == Qt::DisplayRole
@@ -199,6 +337,7 @@ module MetaRuby
199
337
  return Qt::Variant.new
200
338
  end
201
339
 
340
+ # Reimplemented for Qt model interface
202
341
  def index(row, column, parent)
203
342
  if info = info_from_index(parent)
204
343
  if child_info = info.children[row]
@@ -208,6 +347,7 @@ module MetaRuby
208
347
  Qt::ModelIndex.new
209
348
  end
210
349
 
350
+ # Reimplemented for Qt model interface
211
351
  def parent(child)
212
352
  if info = info_from_index(child)
213
353
  if info.parent && info.parent != root_info
@@ -217,6 +357,7 @@ module MetaRuby
217
357
  Qt::ModelIndex.new
218
358
  end
219
359
 
360
+ # Reimplemented for Qt model interface
220
361
  def rowCount(parent)
221
362
  if info = info_from_index(parent)
222
363
  info.children.size
@@ -224,38 +365,10 @@ module MetaRuby
224
365
  end
225
366
  end
226
367
 
368
+ # Reimplemented for Qt model interface
227
369
  def columnCount(parent)
228
370
  return 1
229
371
  end
230
-
231
- def info_from_index(index)
232
- if !index.valid?
233
- return id_to_module.last
234
- else
235
- id_to_module[index.internal_id >> 1]
236
- end
237
- end
238
-
239
- def find_index_by_model(model)
240
- if info = id_to_module.find { |info| info.this == model }
241
- return create_index(info.row, 0, info.id)
242
- end
243
- end
244
-
245
- def find_index_by_path(*path)
246
- current = id_to_module.last
247
- if path.first == current.name
248
- path.shift
249
- end
250
-
251
- path.each do |name|
252
- current = id_to_module.find do |info|
253
- info.name == name && info.parent == current
254
- end
255
- return if !current
256
- end
257
- create_index(current.row, 0, current.id)
258
- end
259
372
  end
260
373
  end
261
374
  end