From: Jamis Buck Date: 2004-11-22T11:38:36+09:00 Subject: Re: [SOLUTION] Object Browser (#8) R. Mark Volkmann wrote: > I'm new to using gems. Can you tell me the command I need to run to get > ruby-gtk2 so I can run your code? Unfortunately, ruby-gtk2 is not a gem, nor is it in rpa. So you have to install it the "hard" way--from source. If you're on Windows, it's even harder: you have to install GTK2 first. GTK2: http://www.gtk.org Ruby-GTK2 (part of Ruby-Gnome2): http://ruby-gnome2.sourceforge.jp/ Having never used GTK in Windows, I have no idea how easy/hard it is to get ruby-gtk2 running under Windows. YMMV. YHBW. - Jamis P.S.: A plea to the ruby-gnome2 folks: a gemmable version would be a real boon. A similar plea to the RPA folks... ;) > > ----- Original Message ----- From: "Jamis Buck" > To: "ruby-talk ML" > Sent: Sunday, November 21, 2004 6:48 PM > Subject: [SOLUTION] Object Browser (#8) > > >> 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 >> > > > -------------------------------------------------------------------------------- > > > >> require 'gtk2' >> >> DEFAULT_OBJECTBROWSER_ROOT = self >> >> class Object >> alias :pre_objbrowser_inspect :inspect >> def inspect >> result = pre_objbrowser_inspect >> result = $1 + " ...>" if result =~ /^(#<.*?:0x\w+) / >> result >> end >> end >> >> module ObjectBrowser >> >> def browse( root = DEFAULT_OBJECTBROWSER_ROOT ) >> Interface.new( root ).display_and_wait >> end >> module_function :browse >> >> class Interface >> def initialize( root = DEFAULT_OBJECTBROWSER_ROOT ) >> @root = root >> Gtk.init >> end >> >> def display >> window = Window.new( @root ) >> window.show_all >> end >> >> def display_and_wait >> display >> wait >> end >> >> def wait >> Gtk.main >> end >> end >> >> class Window < Gtk::Window >> OBJECT = 1 >> CLASS = 2 >> INSTANCE_VARS = 3 >> PUBLIC_METHODS = 4 >> PROTECTED_METHODS = 5 >> PRIVATE_METHODS = 6 >> CLASS_VARS = 7 >> CONSTANTS = 8 >> SUPERCLASS = 9 >> STRING = 10 >> INSTANCE_METHODS = 11 >> >> LABEL = 0 >> TYPE = 1 >> REF = 2 >> >> def initialize( root ) >> super( Gtk::Window::TOPLEVEL ) >> >> signal_connect "delete_event", &method( :on_delete ) >> signal_connect "destroy", &method( :on_destroy ) >> >> vbox = Gtk::VBox.new >> add(vbox) >> >> pane = Gtk::VPaned.new >> vbox.add pane >> >> sw = Gtk::ScrolledWindow.new >> sw.set_policy *[Gtk::POLICY_AUTOMATIC]*2 >> sw.shadow_type = Gtk::SHADOW_IN >> pane.add sw >> >> @model = Gtk::TreeStore.new( String, Integer, Integer ) >> add_node( nil, root ) >> >> @tree = Gtk::TreeView.new( @model ) >> @tree.set_size_request -1, 400 >> >> renderer = Gtk::CellRendererText.new >> >> col = Gtk::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 = Gtk::ScrolledWindow.new >> sw.set_policy *[Gtk::POLICY_AUTOMATIC]*2 >> sw.shadow_type = Gtk::SHADOW_IN >> pane.add sw >> >> @text = Gtk::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 = ObjectSpace._id2ref( i[REF].to_i ) >> r.text = "#{i[LABEL]}#{obj.inspect}" >> when CLASS, SUPERCLASS >> obj = ObjectSpace._id2ref( i[REF].to_i ) >> r.text = "#{i[LABEL]} #{obj.name}" >> else >> r.text = i[LABEL] >> end >> end >> >> def on_row_expanded( widget, iter, path ) >> unless iter.first_child[LABEL] >> case iter[1] >> when OBJECT, CLASS, SUPERCLASS then >> obj = ObjectSpace._id2ref( iter[REF].to_i ) >> add_node iter, obj, iter.first_child >> when INSTANCE_VARS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> initialize_vars_list( obj, iter, obj.instance_variables.sort, >> :instance_variable_get ) >> when PUBLIC_METHODS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> initialize_methods_list( obj, iter, >> obj.public_methods(false).sort ) >> when PROTECTED_METHODS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> initialize_methods_list( obj, iter, >> obj.protected_methods(false).sort ) >> when PRIVATE_METHODS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> initialize_methods_list( obj, iter, >> obj.private_methods(false).sort ) >> when INSTANCE_METHODS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> initialize_methods_list( obj, iter, >> obj.instance_methods(false).sort, true ) >> when CLASS_VARS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> initialize_vars_list( obj, iter, >> obj.class_variables.sort, :class_eval ) >> when CONSTANTS then >> obj = ObjectSpace._id2ref( iter.parent[REF].to_i ) >> constants = obj.constants >> if obj.respond_to?(:superclass) && obj.superclass >> constants = constants - 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 = iter.path.to_s + ":" + ( iter.n_children - 1 ).to_s >> path = Gtk::TreePath.new( path_str ) >> >> @tree.scroll_to_cell( path, nil, true, 1.0, 0 ) >> end >> >> def add_node( parent, object, node=nil ) >> unless node >> node = add_row( parent, "", object, OBJECT, false ) >> add_row( node, "class", object.class, CLASS ) >> else >> add_row( parent, "class", object.class, CLASS, true, node ) >> node = parent >> 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 = object.constants >> if object.respond_to?(:superclass) && object.superclass >> constants = constants - 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=true ) >> unless list.empty? >> summary = list.sort.join( "," ) >> summary = summary[0,60] + "..." if summary.length > 63 >> add_row( node, "#{name} (#{summary})", nil, type ) >> end >> end >> >> def add_row( parent, label, value, type, add_empty=true, node=nil ) >> node = @model.append( parent ) unless node >> >> node[ LABEL ] = label >> node[ TYPE ] = type >> node[ REF ] = value.object_id >> >> @model.append( node ) if add_empty >> >> node >> end >> >> def initialize_methods_list( obj, iter, list, instance=false ) >> node = iter.first_child >> list.each do |item| >> if instance >> method = obj.instance_method( item.to_sym ) >> else >> method = obj.method( item.to_sym ) >> end >> add_row iter, item + "(#{method.arity})", obj, STRING, false, node >> node = nil >> end >> end >> >> def initialize_vars_list( obj, iter, list, message ) >> node = iter.first_child >> list.each do |item| >> value = obj.__send__( message, item ) >> add_row iter, "#{item}=", value, OBJECT, true, node >> node = nil >> end >> end >> end >> >> end >> >> if __FILE__ == $0 >> @obj = ObjectBrowser::Interface.new >> @obj.display_and_wait >> end >> > > > > . > -- Jamis Buck jgb3@email.byu.edu http://www.jamisbuck.org/jamis