--------------080608050401040503050008
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Well, I was kind of waiting to see what other people came up with, but
since the list seems quiet on this topic, I guess I'll go ahead and post
first.
This is a VERY rough implementation. It uses ruby-gtk2, and is one of my
first projects using that interface, so I've doubtless done all kinds of
things wrong. :) But it works.
By default, it displays the "main" object. You can see the class,
superclass, instance/class variables, public/private/protected methods,
and constants (where any of them apply and are non-empty).
I wanted to add the ability to modify values, but didn't quite have time
to get that far.
This was a great quiz, though. I'd love to see a more sophisticated
version of this. I can use mine, for instance, to do a kind of
breakpoint in my code:
ObjectBrowser.browse( @foo )
And the program will stop, display the window, and wait for the window
to close before proceeding.
Anyway. Comments?
- Jamis
--
Jamis Buck
jgb3 / email.byu.edu
http://www.jamisbuck.org/jamis
--------------080608050401040503050008
Content-Type: text/plain;
name bject-browser.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename bject-browser.rb"
require 'gtk2'
DEFAULT_OBJECTBROWSER_ROOT elf
class Object
alias :pre_objbrowser_inspect :inspect
def inspect
result re_objbrowser_inspect
result 1 + " ...>" if result /^(#<.*?:0x\w+) /
result
end
end
module ObjectBrowser
def browse( root EFAULT_OBJECTBROWSER_ROOT )
Interface.new( root ).display_and_wait
end
module_function :browse
class Interface
def initialize( root EFAULT_OBJECTBROWSER_ROOT )
@root oot
Gtk.init
end
def display
window indow.new( @root )
window.show_all
end
def display_and_wait
display
wait
end
def wait
Gtk.main
end
end
class Window < Gtk::Window
OBJECT
CLASS
INSTANCE_VARS
PUBLIC_METHODS
PROTECTED_METHODS
PRIVATE_METHODS
CLASS_VARS
CONSTANTS
SUPERCLASS
STRING 0
INSTANCE_METHODS 1
LABEL
TYPE
REF
def initialize( root )
super( Gtk::Window::TOPLEVEL )
signal_connect "delete_event", &method( :on_delete )
signal_connect "destroy", &method( :on_destroy )
vbox tk::VBox.new
add(vbox)
pane tk::VPaned.new
vbox.add pane
sw tk::ScrolledWindow.new
sw.set_policy *[Gtk::POLICY_AUTOMATIC]*2
sw.shadow_type tk::SHADOW_IN
pane.add sw
@model tk::TreeStore.new( String, Integer, Integer )
add_node( nil, root )
@tree tk::TreeView.new( @model )
@tree.set_size_request -1, 400
renderer tk::CellRendererText.new
col tk::TreeViewColumn.new( "Data", renderer )
col.set_cell_data_func renderer, &method( :on_cell_render )
@tree.append_column col
@tree.expand_row Gtk::TreePath.new( "0" ), false
@tree.signal_connect "row_expanded", &method( :on_row_expanded )
sw.add @tree
sw tk::ScrolledWindow.new
sw.set_policy *[Gtk::POLICY_AUTOMATIC]*2
sw.shadow_type tk::SHADOW_IN
pane.add sw
@text tk::TextView.new
sw.add @text
set_default_size 650, 500
end
def on_delete( widget, event )
false
end
def on_destroy( widget )
Gtk.main_quit
end
def on_cell_render( c, r, m, i )
case i[TYPE]
when OBJECT
obj bjectSpace._id2ref( i[REF].to_i )
r.text #{i[LABEL]}#{obj.inspect}"
when CLASS, SUPERCLASS
obj bjectSpace._id2ref( i[REF].to_i )
r.text #{i[LABEL]} #{obj.name}"
else
r.text [LABEL]
end
end
def on_row_expanded( widget, iter, path )
unless iter.first_child[LABEL]
case iter[1]
when OBJECT, CLASS, SUPERCLASS then
obj bjectSpace._id2ref( iter[REF].to_i )
add_node iter, obj, iter.first_child
when INSTANCE_VARS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
initialize_vars_list( obj, iter, obj.instance_variables.sort,
:instance_variable_get )
when PUBLIC_METHODS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
initialize_methods_list( obj, iter, obj.public_methods(false).sort )
when PROTECTED_METHODS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
initialize_methods_list( obj, iter,
obj.protected_methods(false).sort )
when PRIVATE_METHODS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
initialize_methods_list( obj, iter,
obj.private_methods(false).sort )
when INSTANCE_METHODS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
initialize_methods_list( obj, iter,
obj.instance_methods(false).sort, true )
when CLASS_VARS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
initialize_vars_list( obj, iter,
obj.class_variables.sort, :class_eval )
when CONSTANTS then
obj bjectSpace._id2ref( iter.parent[REF].to_i )
constants bj.constants
if obj.respond_to?(:superclass) && obj.superclass
constants onstants - obj.superclass.constants
end
initialize_vars_list( obj, iter, constants.sort, :const_get )
else
raise "don't know what to do with row of type #{iter[TYPE]}"
end
end
path_str ter.path.to_s + ":" + ( iter.n_children - 1 ).to_s
path tk::TreePath.new( path_str )
@tree.scroll_to_cell( path, nil, true, 1.0, 0 )
end
def add_node( parent, object, node l )
unless node
node dd_row( parent, "", object, OBJECT, false )
add_row( node, "class", object.class, CLASS )
else
add_row( parent, "class", object.class, CLASS, true, node )
node arent
end
if object.is_a?( Module )
if object.respond_to?(:superclass) && object.superclass
add_row( node, "extends", object.superclass, SUPERCLASS )
end
add_row_unless_empty(
object.class_variables, node, "Class Variables", CLASS_VARS )
constants bject.constants
if object.respond_to?(:superclass) && object.superclass
constants onstants - object.superclass.constants
end
add_row_unless_empty( constants, node, "Constants", CONSTANTS )
add_row_unless_empty( object.instance_methods(false), node,
"Instance Methods", INSTANCE_METHODS )
end
add_row_unless_empty( object.instance_variables, node,
"Instance Variables", INSTANCE_VARS )
add_row_unless_empty( object.public_methods(false), node,
"Public Methods", PUBLIC_METHODS )
add_row_unless_empty( object.protected_methods(false), node,
"Protected Methods", PROTECTED_METHODS )
add_row_unless_empty( object.private_methods(false), node,
"Private Methods", PRIVATE_METHODS )
node
end
def add_row_unless_empty( list, node, name, type, add_empty ue )
unless list.empty?
summary ist.sort.join( "," )
summary ummary[0,60] + "..." if summary.length > 63
add_row( node, "#{name} (#{summary})", nil, type )
end
end
def add_row( parent, label, value, type, add_empty ue, node l )
node model.append( parent ) unless node
node[ LABEL ] abel
node[ TYPE ] ype
node[ REF ] alue.object_id
@model.append( node ) if add_empty
node
end
def initialize_methods_list( obj, iter, list, instanceł±se )
node ter.first_child
list.each do |item|
if instance
method bj.instance_method( item.to_sym )
else
method bj.method( item.to_sym )
end
add_row iter, item + "(#{method.arity})", obj, STRING, false, node
node il
end
end
def initialize_vars_list( obj, iter, list, message )
node ter.first_child
list.each do |item|
value bj.__send__( message, item )
add_row iter, "#{item} value, OBJECT, true, node
node il
end
end
end
end
if __FILE__ $0
@obj bjectBrowser::Interface.new
@obj.display_and_wait
end
--------------080608050401040503050008--