drx 0.0.1
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.
- data/README +13 -0
- data/ext/drx_ext.c +138 -0
- data/ext/extconf.rb +2 -0
- data/lib/drx.rb +67 -0
- data/lib/drx_test.rb +25 -0
- data/lib/drx_test2.rb +25 -0
- data/lib/drxtk.rb +126 -0
- metadata +59 -0
data/README
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= Dr. X, the Good Doctor
|
2
|
+
|
3
|
+
Doctor X is a utility for programmers who wish to understand Ruby's
|
4
|
+
object model better. It's a small 'gem' that exposes, in a Tk GUI,
|
5
|
+
almost everything about a Ruby object: its 'klass', 'super', 'iv_tbl',
|
6
|
+
'm_tbl'. See your singletons with your very own eyes!
|
7
|
+
|
8
|
+
== Usage
|
9
|
+
|
10
|
+
This section is yet to be written. In the meanwhile, visit the homepage
|
11
|
+
to see usage example:
|
12
|
+
|
13
|
+
http://drx.rubyforge.org/
|
data/ext/drx_ext.c
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "st.h"
|
3
|
+
|
4
|
+
// Helper for t_get_iv_tbl().
|
5
|
+
int record_var(st_data_t key, st_data_t value, VALUE hash) {
|
6
|
+
// I originally did the following, but it breaks for object::Tk*. Perhaps these
|
7
|
+
// values are T_NODEs? (e.g. aliases) @todo: debug using INT2FIX(TYPE(value)).
|
8
|
+
rb_hash_aset(hash, ID2SYM(key), value);
|
9
|
+
// So...
|
10
|
+
rb_hash_aset(hash, ID2SYM(key), Qtrue);
|
11
|
+
return ST_CONTINUE;
|
12
|
+
}
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Gets the iv_tbl of the object, as a Ruby hash.
|
16
|
+
*/
|
17
|
+
static VALUE t_get_iv_tbl(VALUE self, VALUE obj)
|
18
|
+
{
|
19
|
+
VALUE hash;
|
20
|
+
hash = rb_hash_new();
|
21
|
+
|
22
|
+
if (TYPE(obj) != T_OBJECT && TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
23
|
+
rb_raise(rb_eTypeError, "Only T_OBJECT/T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
24
|
+
}
|
25
|
+
|
26
|
+
if (ROBJECT(obj)->iv_tbl) {
|
27
|
+
st_foreach(ROBJECT(obj)->iv_tbl, record_var, (st_data_t)hash);
|
28
|
+
}
|
29
|
+
|
30
|
+
return hash;
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Extracts one varibale from an object iv_tbl.
|
35
|
+
*/
|
36
|
+
static VALUE t_get_ivar(VALUE self, VALUE obj, VALUE var_name)
|
37
|
+
{
|
38
|
+
const char *c_name;
|
39
|
+
if (TYPE(obj) != T_OBJECT && TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
40
|
+
rb_raise(rb_eTypeError, "Only T_OBJECT/T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
41
|
+
}
|
42
|
+
c_name = StringValuePtr(var_name);
|
43
|
+
return rb_iv_get(obj, c_name);
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Returns a class's super.
|
48
|
+
*
|
49
|
+
* In contrast to Class#superclass, this function doesn't skip singletons and T_ICLASS.
|
50
|
+
*/
|
51
|
+
static VALUE t_get_super(VALUE self, VALUE obj)
|
52
|
+
{
|
53
|
+
VALUE super;
|
54
|
+
if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
55
|
+
rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
56
|
+
}
|
57
|
+
return RCLASS(obj)->super ? RCLASS(obj)->super : Qnil;
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Returns an object's klass.
|
62
|
+
*
|
63
|
+
* In contrast to Object#class, this function doesn't skip singletons and T_ICLASS.
|
64
|
+
*/
|
65
|
+
static VALUE t_get_klass(VALUE self, VALUE obj)
|
66
|
+
{
|
67
|
+
return CLASS_OF(obj);
|
68
|
+
// Note: we can't simply do 'RBASIC(obj)->klass', because obj may be an 'immediate'.
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Returns an object's flags.
|
73
|
+
*/
|
74
|
+
static VALUE t_get_flags(VALUE self, VALUE obj)
|
75
|
+
{
|
76
|
+
return INT2NUM(RBASIC(obj)->flags);
|
77
|
+
}
|
78
|
+
|
79
|
+
// Helper for t_get_m_tbl().
|
80
|
+
int record_method(st_data_t key, st_data_t value, VALUE hash) {
|
81
|
+
// @todo: Store something useful in the values.
|
82
|
+
rb_hash_aset(hash, key == ID_ALLOCATOR ? rb_str_new2("<Allocator>") : ID2SYM(key), INT2FIX(666));
|
83
|
+
return ST_CONTINUE;
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Gets the m_tbl of a class.
|
88
|
+
*/
|
89
|
+
static VALUE t_get_m_tbl(VALUE self, VALUE obj)
|
90
|
+
{
|
91
|
+
VALUE hash;
|
92
|
+
|
93
|
+
if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
94
|
+
rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
95
|
+
}
|
96
|
+
|
97
|
+
hash = rb_hash_new();
|
98
|
+
st_foreach(RCLASS(obj)->m_tbl, record_method, (st_data_t)hash);
|
99
|
+
return hash;
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Returns the object's "id".
|
104
|
+
*
|
105
|
+
* This is an alternative to Object#__id__ because the latter doesn't
|
106
|
+
* work for T_ICLASS.
|
107
|
+
*/
|
108
|
+
static VALUE t_get_address(VALUE self, VALUE obj)
|
109
|
+
{
|
110
|
+
return INT2NUM(obj);
|
111
|
+
}
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Gets the Ruby's engine type of a variable.
|
115
|
+
*/
|
116
|
+
static VALUE t_get_type(VALUE self, VALUE obj)
|
117
|
+
{
|
118
|
+
return INT2NUM(TYPE(obj));
|
119
|
+
}
|
120
|
+
|
121
|
+
VALUE mDrx;
|
122
|
+
|
123
|
+
void Init_drx_ext() {
|
124
|
+
mDrx = rb_define_module("Drx");
|
125
|
+
rb_define_module_function(mDrx, "get_iv_tbl", t_get_iv_tbl, 1);
|
126
|
+
rb_define_module_function(mDrx, "get_m_tbl", t_get_m_tbl, 1);
|
127
|
+
rb_define_module_function(mDrx, "get_super", t_get_super, 1);
|
128
|
+
rb_define_module_function(mDrx, "get_klass", t_get_klass, 1);
|
129
|
+
rb_define_module_function(mDrx, "get_flags", t_get_flags, 1);
|
130
|
+
rb_define_module_function(mDrx, "get_address", t_get_address, 1);
|
131
|
+
rb_define_module_function(mDrx, "get_type", t_get_type, 1);
|
132
|
+
rb_define_module_function(mDrx, "get_ivar", t_get_ivar, 2);
|
133
|
+
rb_define_const(mDrx, "FL_SINGLETON", INT2FIX(FL_SINGLETON));
|
134
|
+
rb_define_const(mDrx, "T_OBJECT", INT2FIX(T_OBJECT));
|
135
|
+
rb_define_const(mDrx, "T_CLASS", INT2FIX(T_CLASS));
|
136
|
+
rb_define_const(mDrx, "T_ICLASS", INT2FIX(T_ICLASS));
|
137
|
+
rb_define_const(mDrx, "T_MODULE", INT2FIX(T_MODULE));
|
138
|
+
}
|
data/ext/extconf.rb
ADDED
data/lib/drx.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'drx_ext' # Load the C extension
|
2
|
+
|
3
|
+
# Contains a simple utility function, Drx::examine.
|
4
|
+
|
5
|
+
module Drx
|
6
|
+
|
7
|
+
def self.obj_repr(obj)
|
8
|
+
is_singleton = Drx.is_class_like(obj) && (Drx.get_flags(obj) & Drx::FL_SINGLETON).nonzero?
|
9
|
+
is_iclass = Drx.get_type(obj) == Drx::T_ICLASS
|
10
|
+
if is_iclass
|
11
|
+
return "'ICLS { include " + obj_repr(Drx.get_klass(obj)) + " }"
|
12
|
+
else
|
13
|
+
return obj.inspect + (is_singleton ? " 'S" : "")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.examine(obj, level = 0, title = '', &block) # :yield:
|
18
|
+
# Note: since 'obj' may be a T_ICLASS, it doesn't repond to may methods,
|
19
|
+
# including is_a?. So when we're querying things we're using Drx calls
|
20
|
+
# instead.
|
21
|
+
|
22
|
+
$seen = {} if level.zero?
|
23
|
+
line = (' ' * level) + title + ' ' + obj_repr(obj)
|
24
|
+
|
25
|
+
address = Drx.get_address(obj)
|
26
|
+
seen = $seen[address]
|
27
|
+
$seen[address] = true
|
28
|
+
|
29
|
+
if seen
|
30
|
+
line += " [seen]" # #{address.to_s}"
|
31
|
+
end
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
yield line, obj
|
35
|
+
else
|
36
|
+
puts line
|
37
|
+
end
|
38
|
+
|
39
|
+
return if seen
|
40
|
+
|
41
|
+
if Drx.is_class_like(obj)
|
42
|
+
# Kernel has a NULL super.
|
43
|
+
# Modules too have NULL super, unless when 'include'ing.
|
44
|
+
if Drx.get_super(obj) # Warning: we can't do 'if !Drx.get_super(obj).#nil?' because
|
45
|
+
# T_ICLASS doesn't "have" #nil.
|
46
|
+
Drx.examine(Drx.get_super(obj), level+1, '[super]', &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Dipslaying a T_ICLASS's klass isn't very useful, because the data
|
51
|
+
# is already mirrored in the m_tbl and iv_tvl of the T_ICLASS itself.
|
52
|
+
if Drx.get_type(obj) != Drx::T_ICLASS
|
53
|
+
Drx.examine(Drx.get_klass(obj), level+1, '[klass]', &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.has_iv_tbl(obj)
|
58
|
+
Drx.get_type(obj) == T_OBJECT or Drx.is_class_like(obj)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns true if this object is either a class or a module.
|
62
|
+
# When true, you know it has 'm_tbl' and 'super'.
|
63
|
+
def self.is_class_like(obj)
|
64
|
+
[Drx::T_CLASS, Drx::T_ICLASS, Drx::T_MODULE].include? Drx.get_type(obj)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/drx_test.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'drxtk'
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
##############################
|
6
|
+
|
7
|
+
class Zeman < DateTime
|
8
|
+
def int
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
zmn = Zeman.new
|
13
|
+
|
14
|
+
def zmn.koko
|
15
|
+
9090
|
16
|
+
end
|
17
|
+
|
18
|
+
Drx.examine(zmn)
|
19
|
+
|
20
|
+
#############################
|
21
|
+
|
22
|
+
Drx.examinetk(zmn)
|
23
|
+
|
24
|
+
#Drx.examinetk(zmn)
|
25
|
+
#Drx.examinetk("some_string")
|
data/lib/drx_test2.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'dm-core'
|
3
|
+
|
4
|
+
class Post
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
property :post_id, Integer, :serial => true
|
8
|
+
property :title, String
|
9
|
+
property :body, Text
|
10
|
+
|
11
|
+
belongs_to :user
|
12
|
+
end
|
13
|
+
|
14
|
+
class User
|
15
|
+
include DataMapper::Resource
|
16
|
+
|
17
|
+
property :user_uid, Integer, :serial => true
|
18
|
+
property :name, String
|
19
|
+
property :mail, String
|
20
|
+
end
|
21
|
+
|
22
|
+
post = Post.new
|
23
|
+
|
24
|
+
require 'drxtk'
|
25
|
+
Drx.see(post)
|
data/lib/drxtk.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'drx'
|
2
|
+
require 'tk'
|
3
|
+
|
4
|
+
module Drx
|
5
|
+
def self.examinetk(obj)
|
6
|
+
app = Drx::TkGUI::DrxWindow.new
|
7
|
+
app.display_value(obj)
|
8
|
+
app.run
|
9
|
+
end
|
10
|
+
|
11
|
+
# easier to type...
|
12
|
+
def self.see(obj)
|
13
|
+
examinetk(obj)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Drx
|
18
|
+
module TkGUI
|
19
|
+
|
20
|
+
class ScrolledListbox < TkFrame
|
21
|
+
def initialize(*args, &block)
|
22
|
+
super(*args, &block)
|
23
|
+
@the_list = the_list = TkListbox.new(self) {
|
24
|
+
#pack :side => 'left'#, :expand => 'true', :fill => 'both'
|
25
|
+
}
|
26
|
+
TkScrollbar.new(self) { |s|
|
27
|
+
pack :side => 'right', :fill => 'y'
|
28
|
+
command { |*args| the_list.yview *args }
|
29
|
+
the_list.yscrollcommand { |first,last| s.set first,last }
|
30
|
+
}
|
31
|
+
TkScrollbar.new(self) { |s|
|
32
|
+
orient 'horizontal'
|
33
|
+
pack :side => 'bottom', :fill => 'x'
|
34
|
+
command { |*args| the_list.xview *args }
|
35
|
+
the_list.xscrollcommand { |first,last| s.set first,last }
|
36
|
+
}
|
37
|
+
@the_list.pack(:side => 'left', :expand => 'true', :fill => 'both')
|
38
|
+
end
|
39
|
+
def the_list
|
40
|
+
@the_list
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class ::TkListbox
|
45
|
+
def get_selection
|
46
|
+
idx = curselection[0]
|
47
|
+
return get(idx)
|
48
|
+
end
|
49
|
+
def get_index
|
50
|
+
curselection[0]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class DrxWindow
|
55
|
+
def initialize
|
56
|
+
root = TkRoot.new
|
57
|
+
@list = (ScrolledListbox.new(root) {
|
58
|
+
#pack :side => 'left', :fill => 'y'
|
59
|
+
pack :side => 'left', :fill => 'both', :expand => true
|
60
|
+
}).the_list
|
61
|
+
@list.width 52
|
62
|
+
@list.height 25
|
63
|
+
@list.focus
|
64
|
+
@varsbox = (ScrolledListbox.new(root) {
|
65
|
+
#pack :side => 'left', :fill => 'y'
|
66
|
+
pack :side => 'left', :fill => 'both', :expand => true
|
67
|
+
}).the_list
|
68
|
+
@methodsbox = (ScrolledListbox.new(root) {
|
69
|
+
#pack :side => 'left', :fill => 'y'
|
70
|
+
pack :side => 'left', :fill => 'both', :expand => true
|
71
|
+
}).the_list
|
72
|
+
|
73
|
+
@list.bind('<ListboxSelect>') {
|
74
|
+
@current_object = @objs[@list.get_index]
|
75
|
+
display_variables(current_object)
|
76
|
+
display_methods(current_object)
|
77
|
+
}
|
78
|
+
@varsbox.bind('<ListboxSelect>') {
|
79
|
+
inspect_variable(current_object, @varsbox.get_selection)
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
# def current_object=(obj)
|
84
|
+
# @current_object = obj
|
85
|
+
# end
|
86
|
+
|
87
|
+
def current_object
|
88
|
+
@current_object
|
89
|
+
end
|
90
|
+
|
91
|
+
def display_variables(obj)
|
92
|
+
@varsbox.delete('0', 'end')
|
93
|
+
if (Drx.has_iv_tbl(obj))
|
94
|
+
vars = Drx.get_iv_tbl(obj).keys.map do |v| v.to_s end.sort
|
95
|
+
@varsbox.insert('end', *vars)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def inspect_variable(obj, var_name)
|
100
|
+
print "\n== Variable #{var_name}\n\n"
|
101
|
+
p Drx.get_ivar(obj, var_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def display_methods(obj)
|
105
|
+
@methodsbox.delete('0', 'end')
|
106
|
+
if (Drx.is_class_like(obj))
|
107
|
+
methods = Drx.get_m_tbl(obj).keys.map do |v| v.to_s end.sort
|
108
|
+
@methodsbox.insert('end', *methods)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def display_value(value)
|
113
|
+
@objs = []
|
114
|
+
Drx.examine(value) do |line, obj|
|
115
|
+
@list.insert('end', line)
|
116
|
+
@objs << obj
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def run
|
121
|
+
Tk.mainloop
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end # module TkGUI
|
126
|
+
end # module Drx
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: drx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mooffie
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-03 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: mooffie@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/extconf.rb
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- lib/drxtk.rb
|
27
|
+
- lib/drx_test2.rb
|
28
|
+
- lib/drx_test.rb
|
29
|
+
- lib/drx.rb
|
30
|
+
- ext/extconf.rb
|
31
|
+
- ext/drx_ext.c
|
32
|
+
has_rdoc: false
|
33
|
+
homepage: http://drx.rubyforge.org/
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "1.8"
|
44
|
+
version:
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
rubyforge_project: drx
|
54
|
+
rubygems_version: 1.3.1
|
55
|
+
signing_key:
|
56
|
+
specification_version: 2
|
57
|
+
summary: Inspect Ruby objects.
|
58
|
+
test_files: []
|
59
|
+
|