[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[SOLUTION] Object Browser (#8

Jamis Buck

11/22/2004 12:49:00 AM

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...
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
3 Answers

R. Mark Volkmann

11/22/2004 1:04:00 AM

0

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?

----- Original Message -----
From: "Jamis Buck" <jgb3@email.byu.edu>
To: "ruby-talk ML" <ruby-talk@ruby-lang.org>
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...
>


--------------------------------------------------------------------------------


> 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
>




James Gray

11/22/2004 2:04:00 PM

0

On Nov 21, 2004, at 6:48 PM, Jamis Buck wrote:

> 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.

[snip description]

> Anyway. Comments?

Yes. Would you mind posting a few screenshots, for those of us having
trouble getting past the interface requirements?

James Edward Gray II



Brian Schröder

11/25/2004 11:56:00 PM

0

On Mon, 22 Nov 2004 09:48:37 +0900
Jamis Buck <jgb3@email.byu.edu> wrote:

> Anyway. Comments?

Hello Jamis,

I'm polishing my solution, and I'm wondering why you are storing object id's instead of object references in the tree? I'd imagine that storing the object reference takes the same amount of space as storing the id. Is it maybe because objects can't be GCed while they are stored in the tree? On the other hand, wouldn't GCing break your program?

Regards,

Brian

PS: I uploaded a new, usable version of the object browser to the quiz homepage. If anybody gives it a try i'd be happy to hear of suggestions.

--
Brian Schröder
http://ruby.brian-schroede...