Class: MetaRuby::GUI::RubyConstantsItemModel

Inherits:
Qt::AbstractItemModel
  • Object
show all
Defined in:
lib/metaruby/gui/ruby_constants_item_model.rb

Overview

A Qt item model that enumerates models stored in the Ruby constant hierarchy

The model exposes all registered constants for which #predicate returns true in a hierarchy, allowing the user to interact with it.

Discovery starts at Object

Defined Under Namespace

Classes: ModuleInfo, TypeInfo

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type_info = Hash.new, &predicate) ⇒ RubyConstantsItemModel

Initialize this model for objects of the given type

Parameters:

  • type_info (Hash) (defaults to: Hash.new)

    value for #type_info

  • predicate (#call)

    the filter #predicate



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 67

def initialize(type_info = Hash.new, &predicate)
    super()
    @predicate = predicate || proc { true }
    @type_info = type_info
    @title = "Model Browser"
    @excludes = [Qt].to_set

    @id_to_module = []
    @filtered_out_modules = Set.new
    @object_paths = Hash.new
end

Instance Attribute Details

#excludesSet (readonly)

Explicitely excluded objects

Set of objects that should be excluded from discovery, regardless of what #predicate would return from them.

Note that such objects are not discovered at all, meaning that if they contain objects that should have been discovered, they won’t be.

Returns:

  • (Set)


35
36
37
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 35

def excludes
  @excludes
end

#filtered_out_modulesObject (readonly)

Set of objects that have been filtered out by #predicate



51
52
53
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 51

def filtered_out_modules
  @filtered_out_modules
end

#id_to_module{Integer=>ModuleInfo} (readonly)

Mapping from module ID to a module object

Returns:



48
49
50
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 48

def id_to_module
  @id_to_module
end

#object_paths{Object=>String} (readonly)

List of paths for each of the discovered objects

Returns:

  • ({Object=>String})


61
62
63
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 61

def object_paths
  @object_paths
end

#predicate#call (readonly)

Predicate that filters objects in addition to #excludes

Only objects for which #call returns true and the constants that contain them are exposed by this model

Returns:

  • (#call)


23
24
25
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 23

def predicate
  @predicate
end

#titleString

Name of the root item

Returns:

  • (String)


56
57
58
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 56

def title
  @title
end

#type_info{Class=>TypeInfo} (readonly)

A list of expected object types

This is used to decide where a given object should be “attached” in the hierarchy. Matching types are stored in MetaRuby::GUI::RubyConstantsItemModel::ModuleInfo#types.

Returns:



43
44
45
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 43

def type_info
  @type_info
end

Instance Method Details

#columnCount(parent) ⇒ Object

Reimplemented for Qt model interface



369
370
371
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 369

def columnCount(parent)
    return 1
end

#compute_full_name(info) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Lazily computes the full name of a discovered object. It updates MetaRuby::GUI::RubyConstantsItemModel::ModuleInfo#full_name

Parameters:

Returns:

  • (String)


224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 224

def compute_full_name(info)
    if name = info.full_name
        return name
    else
        full_name = []
        current = info
        while current.parent
            full_name << current.name
            current = current.parent
        end
        info.full_name = full_name.reverse
    end
end

#compute_keyword_string(info) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Lazily compute a comma-separated string that can be used to search for the given node. The result is stored in MetaRuby::GUI::RubyConstantsItemModel::ModuleInfo#keyword_string

The returned string is of the form

type0[,type1...]:name0[,name1...]

Parameters:

Returns:

  • (String)


305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 305

def compute_keyword_string(info)
    if keywords = info.keyword_string
        return keywords
    else
        types = info.types.map do |type|
            type_info[type].name
        end.sort.join(",")
        paths = [compute_path(info)]
        paths.concat info.children.map { |child| compute_keyword_string(child) }
        info.keyword_string = "#{types};#{paths.join(",")}"
    end
end

#compute_path(info) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Lazily compute the path of a discovered object. The result is stored in MetaRuby::GUI::RubyConstantsItemModel::ModuleInfo#path

Parameters:

Returns:

  • (String)


245
246
247
248
249
250
251
252
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 245

def compute_path(info)
    if path = info.path
        return path
    else
        full_name = compute_full_name(info)
        info.path = ("/" + full_name.map(&:downcase).join("/"))
    end
end

#data(index, role) ⇒ Object

Reimplemented for Qt model interface



327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 327

def data(index, role)
    if info = info_from_index(index)
        if role == Qt::DisplayRole
            return Qt::Variant.new(info.name)
        elsif role == Qt::EditRole
            return Qt::Variant.new(compute_full_name(info).join("/"))
        elsif role == Qt::UserRole
            return Qt::Variant.new(compute_keyword_string(info))
        end
    end
    return Qt::Variant.new
end

#discover_module(mod, stack = Array.new) ⇒ ModuleInfo

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Discovers an object and its children

Parameters:

  • mod (Object)

    an object that should be discovered

  • stack (Array) (defaults to: Array.new)

    the current stack (to avoid infinite recursions)

Returns:



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 142

def discover_module(mod, stack = Array.new)
    return if excludes.include?(mod)
    stack.push mod

    children = []
    mod_info = ModuleInfo.new(nil, nil, mod, nil, children, nil, Set.new)

    is_needed = (mod.kind_of?(Class) && mod == Object) || predicate.call(mod)

    if mod.respond_to?(:constants)
        children_modules = begin mod.constants
                           rescue TypeError
                               puts "cannot discover module #{mod}"
                               []
                           end

        children_modules = children_modules.map do |child_name|
            next if !mod.const_defined?(child_name, false)
            # Ruby issues a warning when one tries to access Config
            # (it has been deprecated in favor of RbConfig). Ignore
            # it explicitly
            next if mod == Object && child_name == :Config
            next if mod.autoload?(child_name)
            child_mod = begin mod.const_get(child_name)
                        rescue LoadError
                            # Handle autoload errors
                        end
            next if !child_mod
            next if filtered_out_modules.include?(child_mod)
            next if stack.include?(child_mod)
            [child_name.to_s, child_mod]
        end.compact.sort_by(&:first)

        children_modules.each do |child_name, child_mod|
            if info = discover_module(child_mod, stack)
                info.id = id_to_module.size
                info.name = child_name.to_s
                info.parent = mod_info
                info.row = children.size
                children << info
                id_to_module << info
            else
                filtered_out_modules << child_mod
            end
        end
    end

    if is_needed
        klass = if mod.respond_to?(:ancestors) then mod
                else mod.class
                end

        current_priority = nil
        klass.ancestors.each do |ancestor|
            if info = type_info[ancestor]
                current_priority ||= info.priority
                if current_priority < info.priority
                    mod_info.types.clear
                    mod_info.types << ancestor
                    current_priority = info.priority
                elsif current_priority == info.priority
                    mod_info.types << ancestor
                end
            end
        end
    end

    update_module_type_info(mod_info)

    if !children.empty? || is_needed
        mod_info
    end
ensure stack.pop
end

#find_index_by_model(model) ⇒ Qt::ModelIndex?

Return the Qt::ModelIndex that represents a given object

Returns:

  • (Qt::ModelIndex, nil)

    the index, or nil if the object is not included in this model



268
269
270
271
272
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 268

def find_index_by_model(model)
    if info = id_to_module.find { |info| info.this == model }
        return create_index(info.row, 0, info.id)
    end
end

#find_index_by_path(*path) ⇒ Qt::ModelIndex?

Returns the Qt::ModelIndex that matches a given path

Parameters:

  • path (Array<String>)

    path to the desired object

Returns:

  • (Qt::ModelIndex, nil)

    the index, or nil if the path does not resolve to an object included in this model



279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 279

def find_index_by_path(*path)
    current = id_to_module.last
    if path.first == current.name
        path.shift
    end

    path.each do |name|
        current = id_to_module.find do |info|
            info.name == name && info.parent == current
        end
        return if !current
    end
    create_index(current.row, 0, current.id)
end

#generate_paths(paths, info, current) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generate the path information, i.e. per-object path string

Parameters:

  • paths (Hash)

    the generated paths (matches #object_paths)

  • info (ModuleInfo)

    the object information for the object to be discovered

  • current (String)

    the path of ‘info’



111
112
113
114
115
116
117
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 111

def generate_paths(paths, info, current)
    info.children.each do |child|
        child_uri = current + '/' + child.name
        paths[child.this] = child_uri
        generate_paths(paths, child, child_uri)
    end
end

#headerData(section, orientation, role) ⇒ Object

Reimplemented for Qt model interface



319
320
321
322
323
324
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 319

def headerData(section, orientation, role)
    if role == Qt::DisplayRole && section == 0
        Qt::Variant.new(title)
    else Qt::Variant.new
    end
end

#index(row, column, parent) ⇒ Object

Reimplemented for Qt model interface



341
342
343
344
345
346
347
348
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 341

def index(row, column, parent)
    if info = info_from_index(parent)
        if child_info = info.children[row]
            return create_index(row, column, child_info.id)
        end
    end
    Qt::ModelIndex.new
end

#info_from_index(index) ⇒ Object

Resolves a ModuleInfo from a Qt::ModelIndex



256
257
258
259
260
261
262
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 256

def info_from_index(index)
    if !index.valid?
        return id_to_module.last
    else
        id_to_module[index.internal_id >> 1]
    end
end

#parent(child) ⇒ Object

Reimplemented for Qt model interface



351
352
353
354
355
356
357
358
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 351

def parent(child)
    if info = info_from_index(child)
        if info.parent && info.parent != root_info
            return create_index(info.parent.row, 0, info.parent.id)
        end
    end
    Qt::ModelIndex.new
end

#reloadObject

Discovers or rediscovers the objects



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 80

def reload
    begin_reset_model
    @id_to_module = []
    @filtered_out_modules = Set.new
    
    info = discover_module(Object)
    info.id = id_to_module.size
    info.name = title
    update_module_type_info(info)
    info.row = 0
    id_to_module << info

    @object_paths = Hash.new
    generate_paths(object_paths, info, "")
ensure
    end_reset_model
end

#root_infoObject

ModuleInfo for the root



99
100
101
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 99

def root_info
    id_to_module.last
end

#rowCount(parent) ⇒ Object

Reimplemented for Qt model interface



361
362
363
364
365
366
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 361

def rowCount(parent)
    if info = info_from_index(parent)
        info.children.size
    else 0
    end
end

#update_module_type_info(info) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Updates MetaRuby::GUI::RubyConstantsItemModel::ModuleInfo#types so that it includes the type of its

children

Parameters:

  • info (ModuleInfo)

    the object info that should be updated



125
126
127
128
129
130
131
132
133
# File 'lib/metaruby/gui/ruby_constants_item_model.rb', line 125

def update_module_type_info(info)
    types = info.types.to_set
    info.children.each do |child_info|
        types |= child_info.types.to_set
    end
    info.types = types.to_a.sort_by do |type|
        type_info[type].priority
    end.reverse
end