drx 0.3.2 → 0.4.0
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/examples/{drx_test2.rb → drx_datamapper.rb} +0 -0
- data/examples/{drx_test.rb → drx_date.rb} +0 -0
- data/examples/{drx_test3.rb → drx_guitar.rb} +2 -10
- data/ext/drx_core.c +34 -49
- data/lib/drx/graphviz.rb +22 -55
- data/lib/drx/objinfo.rb +1 -22
- data/lib/drx/tk/app.rb +153 -39
- data/lib/drx/tk/imagemap.rb +41 -39
- metadata +8 -9
- data/examples/drx_datamapper19.rb +0 -35
File without changes
|
File without changes
|
@@ -1,7 +1,6 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
|
5
4
|
require 'drx'
|
6
5
|
|
7
6
|
class Instrument
|
@@ -11,23 +10,16 @@ end
|
|
11
10
|
class Guitar < Instrument
|
12
11
|
def initialize
|
13
12
|
@strings = 5
|
14
|
-
# puts class # .add_some
|
15
13
|
end
|
16
14
|
|
17
15
|
def self.add_some
|
18
16
|
@max_strings = 10
|
19
|
-
@@approved = 'by obama
|
17
|
+
@@approved = 'by team obama'
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
class Post
|
25
|
-
include DataMapper::Resource
|
26
|
-
property :id, Serial
|
27
|
-
end
|
28
|
-
|
21
|
+
Guitar.add_some
|
29
22
|
o = Guitar.new
|
30
|
-
o = Post.new
|
31
23
|
def o.unq
|
32
24
|
def g;end
|
33
25
|
end
|
data/ext/drx_core.c
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
#include "ruby.h"
|
2
|
-
|
3
|
-
#ifndef RUBY_VM
|
4
|
-
# define RUBY_1_8
|
5
|
-
#endif
|
6
|
-
|
7
|
-
#ifdef RUBY_1_8
|
8
|
-
# include "st.h"
|
9
|
-
#else
|
10
|
-
# include "ruby/st.h"
|
11
|
-
#endif
|
2
|
+
#include "st.h"
|
12
3
|
|
13
4
|
/**
|
14
5
|
* Gets the Ruby's engine type of a variable.
|
@@ -19,9 +10,11 @@ static VALUE t_get_type(VALUE self, VALUE obj)
|
|
19
10
|
}
|
20
11
|
|
21
12
|
// Helper for t_get_iv_tbl().
|
22
|
-
|
23
|
-
//
|
24
|
-
//
|
13
|
+
int record_var(st_data_t key, st_data_t value, VALUE hash) {
|
14
|
+
// I originally did the following, but it breaks for object::Tk*. Perhaps these
|
15
|
+
// values are T_NODEs? (e.g. aliases) @todo: debug using INT2FIX(TYPE(value)).
|
16
|
+
rb_hash_aset(hash, ID2SYM(key), value);
|
17
|
+
// So...
|
25
18
|
rb_hash_aset(hash, ID2SYM(key), Qtrue);
|
26
19
|
return ST_CONTINUE;
|
27
20
|
}
|
@@ -38,13 +31,9 @@ static VALUE t_get_iv_tbl(VALUE self, VALUE obj)
|
|
38
31
|
rb_raise(rb_eTypeError, "Only T_OBJECT/T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
39
32
|
}
|
40
33
|
|
41
|
-
#ifdef RUBY_1_8
|
42
34
|
if (ROBJECT(obj)->iv_tbl) {
|
43
35
|
st_foreach(ROBJECT(obj)->iv_tbl, record_var, (st_data_t)hash);
|
44
36
|
}
|
45
|
-
#else
|
46
|
-
rb_ivar_foreach(obj, record_var, hash);
|
47
|
-
#endif
|
48
37
|
|
49
38
|
return hash;
|
50
39
|
}
|
@@ -69,10 +58,11 @@ static VALUE t_get_ivar(VALUE self, VALUE obj, VALUE var_name)
|
|
69
58
|
*/
|
70
59
|
static VALUE t_get_super(VALUE self, VALUE obj)
|
71
60
|
{
|
61
|
+
VALUE super;
|
72
62
|
if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
73
63
|
rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
74
64
|
}
|
75
|
-
return
|
65
|
+
return RCLASS(obj)->super ? RCLASS(obj)->super : Qnil;
|
76
66
|
}
|
77
67
|
|
78
68
|
/**
|
@@ -95,13 +85,9 @@ static VALUE t_get_flags(VALUE self, VALUE obj)
|
|
95
85
|
}
|
96
86
|
|
97
87
|
// Helper for t_get_m_tbl().
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
if (!id_allocator_symb) {
|
102
|
-
id_allocator_symb = rb_intern("<Allocator>");
|
103
|
-
}
|
104
|
-
rb_hash_aset(hash, ID2SYM(key == ID_ALLOCATOR ? id_allocator_symb : key), Qtrue);
|
88
|
+
int record_method(st_data_t key, st_data_t value, VALUE hash) {
|
89
|
+
// @todo: Store something useful in the values?
|
90
|
+
rb_hash_aset(hash, key == ID_ALLOCATOR ? rb_str_new2("<Allocator>") : ID2SYM(key), INT2FIX(666));
|
105
91
|
return ST_CONTINUE;
|
106
92
|
}
|
107
93
|
|
@@ -132,7 +118,6 @@ static VALUE t_get_address(VALUE self, VALUE obj)
|
|
132
118
|
return INT2NUM(obj);
|
133
119
|
}
|
134
120
|
|
135
|
-
#ifdef RUBY_1_8
|
136
121
|
// {{{ Locating methods
|
137
122
|
|
138
123
|
#include "node.h"
|
@@ -141,7 +126,7 @@ static VALUE t_get_address(VALUE self, VALUE obj)
|
|
141
126
|
|
142
127
|
static VALUE t_do_locate_method(NODE *ND_method) {
|
143
128
|
NODE *ND_scope = NULL, *ND_block = NULL;
|
144
|
-
VALUE
|
129
|
+
VALUE place;
|
145
130
|
char line_s[20];
|
146
131
|
|
147
132
|
//
|
@@ -210,24 +195,23 @@ static VALUE t_do_locate_method(NODE *ND_method) {
|
|
210
195
|
return RSTR("<I'm expecting a NODE_BLOCK here...>");
|
211
196
|
}
|
212
197
|
|
213
|
-
|
214
|
-
|
215
|
-
|
198
|
+
sprintf(line_s, "%d:", nd_line(ND_block));
|
199
|
+
place = RSTR(line_s);
|
200
|
+
rb_str_cat2(place, ND_block->nd_file);
|
216
201
|
|
217
|
-
return
|
202
|
+
return place;
|
218
203
|
}
|
219
204
|
|
220
205
|
/*
|
221
206
|
* call-seq:
|
222
|
-
* Drx::Core::locate_method(Date, "to_s") =>
|
207
|
+
* Drx::Core::locate_method(Date, "to_s") => str
|
223
208
|
*
|
224
|
-
* Locates the filename and line-number where a method was defined.
|
209
|
+
* Locates the filename and line-number where a method was defined. Returns a
|
210
|
+
* string of the form "89:/path/to/file.rb", or nil if the method doesn't exist,
|
211
|
+
* or a string of the form "<identifier>".
|
225
212
|
*
|
226
|
-
*
|
227
|
-
*
|
228
|
-
* - A string of the form "<identifier>" if the method isn't written
|
229
|
-
* in Ruby. Possibilities are <c>, <alias>, <attr reader>, and more.
|
230
|
-
* - raises NameError if the method doesn't exist.
|
213
|
+
* If the method exists but isn't a Ruby method (e.g., if it's written in C),
|
214
|
+
* an <identifier> string is returned. E.g., <c>, <alias>, <attr reader>.
|
231
215
|
*/
|
232
216
|
static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
|
233
217
|
{
|
@@ -237,17 +221,19 @@ static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
|
|
237
221
|
if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
238
222
|
rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
239
223
|
}
|
224
|
+
if (!RCLASS(obj)->m_tbl) {
|
225
|
+
return Qnil;
|
226
|
+
}
|
240
227
|
c_name = StringValuePtr(method_name);
|
241
228
|
ID id = rb_intern(c_name);
|
242
|
-
if (
|
229
|
+
if (st_lookup(RCLASS(obj)->m_tbl, id, (st_data_t *)&method_node)) {
|
243
230
|
return t_do_locate_method(method_node);
|
244
231
|
} else {
|
245
|
-
|
232
|
+
return Qnil;
|
246
233
|
}
|
247
234
|
}
|
248
235
|
|
249
236
|
// }}}
|
250
|
-
#endif // RUBY_1_8
|
251
237
|
|
252
238
|
VALUE mDrx;
|
253
239
|
VALUE mCore;
|
@@ -263,19 +249,18 @@ void Init_drx_core() {
|
|
263
249
|
rb_define_module_function(mCore, "get_address", t_get_address, 1);
|
264
250
|
rb_define_module_function(mCore, "get_type", t_get_type, 1);
|
265
251
|
rb_define_module_function(mCore, "get_ivar", t_get_ivar, 2);
|
266
|
-
#ifdef RUBY_1_8
|
267
252
|
rb_define_module_function(mCore, "locate_method", t_locate_method, 2);
|
253
|
+
rb_define_const(mCore, "FL_SINGLETON", INT2FIX(FL_SINGLETON));
|
254
|
+
rb_define_const(mCore, "T_OBJECT", INT2FIX(T_OBJECT));
|
255
|
+
rb_define_const(mCore, "T_CLASS", INT2FIX(T_CLASS));
|
256
|
+
rb_define_const(mCore, "T_ICLASS", INT2FIX(T_ICLASS));
|
257
|
+
rb_define_const(mCore, "T_MODULE", INT2FIX(T_MODULE));
|
258
|
+
|
268
259
|
// For the following, see explanation in t_do_locate_method().
|
269
260
|
rb_eval_string("\
|
270
261
|
class ::Proc;\
|
271
262
|
def _location;\
|
272
|
-
if to_s =~ /@(.*?):(\\d+)>$/ then
|
263
|
+
if to_s =~ /@(.*?):(\\d+)>$/ then \"#$2:#$1\" end;\
|
273
264
|
end;\
|
274
265
|
end");
|
275
|
-
#endif
|
276
|
-
rb_define_const(mCore, "FL_SINGLETON", INT2FIX(FL_SINGLETON));
|
277
|
-
rb_define_const(mCore, "T_OBJECT", INT2FIX(T_OBJECT));
|
278
|
-
rb_define_const(mCore, "T_CLASS", INT2FIX(T_CLASS));
|
279
|
-
rb_define_const(mCore, "T_ICLASS", INT2FIX(T_ICLASS));
|
280
|
-
rb_define_const(mCore, "T_MODULE", INT2FIX(T_MODULE));
|
281
266
|
}
|
data/lib/drx/graphviz.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Adds Graphviz
|
1
|
+
# Adds Graphviz diagraming capability to ObjInfo
|
2
2
|
|
3
3
|
module Drx
|
4
4
|
|
@@ -38,19 +38,16 @@ module Drx
|
|
38
38
|
# Create an ID for the DOT node representing this object.
|
39
39
|
def dot_id
|
40
40
|
('o' + address.to_s).sub('-', '_')
|
41
|
-
# Tip: when examining the DOT output you may wish to
|
42
|
-
# uncomment the following line. It will show you which
|
43
|
-
# ruby object the DOT node represents.
|
44
|
-
#('o' + address.to_s).sub('-', '_') + " /* #{repr} */ "
|
45
41
|
end
|
46
42
|
|
47
43
|
# Creates a pseudo URL for the HTML imagemap.
|
48
44
|
def dot_url
|
49
|
-
"http://
|
45
|
+
"http://server/obj/#{dot_id}"
|
50
46
|
end
|
51
47
|
|
52
48
|
# Quotes a string to be used in DOT source.
|
53
49
|
def dot_quote(s)
|
50
|
+
# @todo: find the documentation for tr()?
|
54
51
|
'"' + s.gsub('\\') { '\\\\' }.gsub('"', '\\"').gsub("\n", '\\n') + '"'
|
55
52
|
end
|
56
53
|
|
@@ -114,26 +111,18 @@ module Drx
|
|
114
111
|
end
|
115
112
|
end
|
116
113
|
|
117
|
-
|
118
|
-
# in the output image, use generate_diagram() instead.
|
119
|
-
def dot_source(opts = {}, &block) # :yield:
|
120
|
-
opts = opts.dup
|
121
|
-
opts[:base] = self
|
122
|
-
@@seen = {}
|
123
|
-
|
124
|
-
out = 'digraph {' "\n"
|
125
|
-
out << @@sizes[opts[:size] || '100%']
|
126
|
-
out << dot_fragment(opts, &block)
|
127
|
-
out << '}' "\n"
|
128
|
-
out
|
129
|
-
end
|
130
|
-
|
131
|
-
def dot_fragment(opts = {}, &block) # :yield:
|
114
|
+
def dot_source(level = 0, opts = {}, &block) # :yield:
|
132
115
|
out = ''
|
133
|
-
# Note: since 'obj' may be a T_ICLASS, it doesn't
|
116
|
+
# Note: since 'obj' may be a T_ICLASS, it doesn't repond to many methods,
|
134
117
|
# including is_a?. So when we're querying things we're using Drx calls
|
135
118
|
# instead.
|
136
119
|
|
120
|
+
if level.zero?
|
121
|
+
out << 'digraph {' "\n"
|
122
|
+
out << @@sizes[opts[:size] || '100%']
|
123
|
+
@@seen = {}
|
124
|
+
end
|
125
|
+
|
137
126
|
seen = @@seen[address]
|
138
127
|
@@seen[address] = true
|
139
128
|
|
@@ -148,8 +137,8 @@ module Drx
|
|
148
137
|
|
149
138
|
if class_like?
|
150
139
|
if spr = self.super and display_super?(spr)
|
151
|
-
out << spr.
|
152
|
-
if
|
140
|
+
out << spr.dot_source(level+1, opts, &block)
|
141
|
+
if [Module, ObjInfo.new(Module).klass.the_object].include? the_object
|
153
142
|
# We don't want these relatively insignificant lines to clutter the display,
|
154
143
|
# so we paint them lightly and tell DOT they aren't to affect the layout (width=0).
|
155
144
|
out << "#{dot_id} -> #{spr.dot_id} [color=gray85, weight=0];" "\n"
|
@@ -161,38 +150,16 @@ module Drx
|
|
161
150
|
|
162
151
|
kls = effective_klass
|
163
152
|
if display_klass?(kls)
|
164
|
-
out << kls.
|
165
|
-
|
166
|
-
# inheritance and the singleton inheritance.
|
167
|
-
#
|
168
|
-
# When an ICLASS has a singleton, we want this singleton to appear close
|
169
|
-
# to the ICLASS, because we want to keep the two groups visually distinct.
|
170
|
-
# We do this by setting the arrow's weight to 1.0.
|
171
|
-
#
|
172
|
-
# (To see the effect of this, set the weight unconditionally to '0' and
|
173
|
-
# see the graph for DataMapper.)
|
174
|
-
weight = t_iclass? ? 1 : 0
|
175
|
-
out << "#{dot_id} -> #{kls.dot_id} [style=dotted, weight=#{weight}];" "\n"
|
153
|
+
out << kls.dot_source(level+1, opts, &block)
|
154
|
+
out << "#{dot_id} -> #{kls.dot_id} [style=dotted];" "\n"
|
176
155
|
out << "{ rank=same; #{dot_id}; #{kls.dot_id}; }" "\n"
|
177
156
|
end
|
178
157
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
# Whether the 'super' arrow is infignificant and must not affect the DOT
|
183
|
-
# layout
|
184
|
-
#
|
185
|
-
# A Ruby object graph is cyclic. We don't want to feed DOT a cyclic graph
|
186
|
-
# because it will ruin our nice "rectangular" layout. The purpose of the
|
187
|
-
# following method is to break the cycle. Normally we break the cycle at
|
188
|
-
# Module (and its singleton). When the user is examining a module, we
|
189
|
-
# instead break the cycle at Class (and its singleton).
|
190
|
-
def insignificant_super_arrow?(opts)
|
191
|
-
if opts[:base].t_module?
|
192
|
-
[Class, ObjInfo.new(Class).klass.the_object].include? the_object
|
193
|
-
else
|
194
|
-
[Module, ObjInfo.new(Module).klass.the_object].include? the_object
|
158
|
+
if level.zero?
|
159
|
+
out << '}' "\n"
|
195
160
|
end
|
161
|
+
|
162
|
+
return out
|
196
163
|
end
|
197
164
|
|
198
165
|
# Whether to display the klass.
|
@@ -200,12 +167,12 @@ module Drx
|
|
200
167
|
if t_iclass?
|
201
168
|
# We're interested in an ICLASS's klass only if it isn't Module.
|
202
169
|
#
|
203
|
-
#
|
170
|
+
# Usualy this means that the ICLASS has a singleton (see "Singletons
|
204
171
|
# of included modules" in display_super?()). We want to see this
|
205
172
|
# singleton.
|
206
173
|
return Module != kls.the_object
|
207
174
|
else
|
208
|
-
# Displaying a
|
175
|
+
# Displaying a singletone's klass is confusing and usually unneeded.
|
209
176
|
return !singleton?
|
210
177
|
end
|
211
178
|
end
|
@@ -248,7 +215,7 @@ module Drx
|
|
248
215
|
# end
|
249
216
|
#
|
250
217
|
def generate_diagram(files, opts = {}, &block)
|
251
|
-
source = self.dot_source(opts, &block)
|
218
|
+
source = self.dot_source(0, opts, &block)
|
252
219
|
File.open(files['dot'], 'w') { |f| f.write(source) }
|
253
220
|
command = GRAPHVIZ_COMMAND % [files['dot'], files['gif'], files['map']]
|
254
221
|
message = Kernel.`(command) # `
|
data/lib/drx/objinfo.rb
CHANGED
@@ -35,29 +35,8 @@ module Drx
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# Returns the source-code position where a method is defined.
|
38
|
-
#
|
39
|
-
# Returns one of:
|
40
|
-
# - [ 'file.rb', 12 ]
|
41
|
-
# - "<c>", "<undef>", "<alias>", etc., if not a ruby code (for Ruby 1.9,
|
42
|
-
# returns only "<c>").
|
43
|
-
# - nil, if functionality not implemented.
|
44
|
-
# - raises NameError if method not found.
|
45
38
|
def locate_method(method_name)
|
46
|
-
|
47
|
-
# Ruby 1.8
|
48
|
-
Core::locate_method(@obj, method_name)
|
49
|
-
elsif Method.method_defined? :source_location
|
50
|
-
# Ruby 1.9
|
51
|
-
location = @obj.instance_method(method_name).source_location
|
52
|
-
if location
|
53
|
-
location
|
54
|
-
else
|
55
|
-
'<c>'
|
56
|
-
end
|
57
|
-
else
|
58
|
-
# Some version of ruby that doesn't have Method#source_loction
|
59
|
-
nil
|
60
|
-
end
|
39
|
+
Core::locate_method(@obj, method_name)
|
61
40
|
end
|
62
41
|
|
63
42
|
def has_iv_tbl?
|
data/lib/drx/tk/app.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'tk'
|
2
|
-
#Tk.default_widget_set = :Ttk
|
3
2
|
require 'drx/tk/imagemap'
|
4
3
|
|
4
|
+
Tk.default_widget_set = :Ttk
|
5
|
+
|
5
6
|
module Drx
|
6
7
|
module TkGUI
|
7
8
|
|
@@ -17,6 +18,7 @@ module Drx
|
|
17
18
|
def initialize
|
18
19
|
@navigation_history = []
|
19
20
|
@eval_history = LineHistory.new
|
21
|
+
@graph_opts = { :size => '100%', :style => 'default' }
|
20
22
|
|
21
23
|
@eval_entry = TkEntry.new(toplevel) {
|
22
24
|
font 'Courier'
|
@@ -45,16 +47,45 @@ module Drx
|
|
45
47
|
navigate_back
|
46
48
|
}
|
47
49
|
|
48
|
-
@varsbox =
|
49
|
-
|
50
|
+
@varsbox = Tk::Tile::Treeview.new(toplevel) {
|
51
|
+
columns 'name value'
|
52
|
+
heading_configure('name', :text => 'Name')
|
53
|
+
heading_configure('value', :text => 'Value')
|
54
|
+
column_configure('name', :stretch => false )
|
55
|
+
column_configure('value', :stretch => false )
|
56
|
+
show 'headings'
|
50
57
|
}
|
51
|
-
@methodsbox =
|
52
|
-
|
58
|
+
@methodsbox = Tk::Tile::Treeview.new(toplevel) {
|
59
|
+
columns 'name location'
|
60
|
+
heading_configure('name', :text => 'Name')
|
61
|
+
heading_configure('location', :text => 'Location')
|
62
|
+
column_configure('name', :stretch => false )
|
63
|
+
column_configure('location', :stretch => false )
|
64
|
+
show 'headings'
|
65
|
+
}
|
66
|
+
|
67
|
+
@graph_size_menu = Tk::Tile::Combobox.new(toplevel) {
|
68
|
+
set '100%'
|
69
|
+
values ['100%', '90%', '80%', '60%']
|
70
|
+
state :readonly
|
71
|
+
width 6
|
72
|
+
}
|
73
|
+
@graph_style_menu = Tk::Tile::Combobox.new(toplevel) {
|
74
|
+
set 'default'
|
75
|
+
values ['default', 'crazy']
|
76
|
+
state :readonly
|
77
|
+
width 10
|
78
|
+
}
|
79
|
+
@save_btn = TkButton.new(toplevel) {
|
80
|
+
text 'Save...'
|
81
|
+
}
|
82
|
+
@save_btn.command {
|
83
|
+
save_graph tip
|
53
84
|
}
|
54
85
|
|
55
86
|
layout
|
56
87
|
|
57
|
-
@varsbox.bind('<
|
88
|
+
@varsbox.bind('<TreeviewSelect>') {
|
58
89
|
if @varsbox.has_selection?
|
59
90
|
require 'pp'
|
60
91
|
output "\n== Variable #{@varsbox.get_selection}\n\n", 'info'
|
@@ -74,7 +105,7 @@ module Drx
|
|
74
105
|
}
|
75
106
|
@methodsbox.bind('Double-Button-1') {
|
76
107
|
if @methodsbox.has_selection?
|
77
|
-
|
108
|
+
locate_method(current_object, @methodsbox.get_selection)
|
78
109
|
end
|
79
110
|
}
|
80
111
|
@eval_entry.bind('Key-Return') {
|
@@ -97,15 +128,47 @@ module Drx
|
|
97
128
|
toplevel.bind('Control-r') {
|
98
129
|
# Refresh the display. Useful if you eval'ed some code that changes the
|
99
130
|
# object inspected.
|
100
|
-
|
131
|
+
refresh
|
101
132
|
# Note: it seems that #instance_eval creates a singleton for the object.
|
102
133
|
# So after eval'ing something and pressing C-r, you're going to see this
|
103
134
|
# extra class.
|
104
135
|
}
|
136
|
+
@graph_size_menu.bind('<ComboboxSelected>') {
|
137
|
+
@graph_opts[:size] = @graph_size_menu.get
|
138
|
+
refresh
|
139
|
+
}
|
140
|
+
@graph_style_menu.bind('<ComboboxSelected>') {
|
141
|
+
@graph_opts[:style] = @graph_style_menu.get
|
142
|
+
refresh
|
143
|
+
}
|
105
144
|
|
106
145
|
output "Please visit the homepage, http://drx.rubyforge.org/, for usage instructions.\n", 'info'
|
146
|
+
|
147
|
+
one_time_initialization
|
148
|
+
user_customizations
|
107
149
|
end
|
108
150
|
|
151
|
+
def one_time_initialization
|
152
|
+
@@one_time_initialization ||= begin
|
153
|
+
# Try to make the Unixy GUI less ugly.
|
154
|
+
if Tk.windowingsystem == 'x11' and Ttk.themes.include? 'clam'
|
155
|
+
Ttk.set_theme 'clam'
|
156
|
+
end
|
157
|
+
rc = File.join(ENV['HOME'] || Dir.pwd, '.drxrc')
|
158
|
+
load rc if File.exist? rc
|
159
|
+
1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Users may redefine this method in their ~/.drxrc
|
164
|
+
# to fine-tune the app.
|
165
|
+
def user_customizations
|
166
|
+
end
|
167
|
+
|
168
|
+
def vbox(*args); VBox.new(toplevel, args); end
|
169
|
+
def hbox(*args); HBox.new(toplevel, args); end
|
170
|
+
def separator; TkLabel.new(toplevel, :text => ' '); end
|
171
|
+
|
109
172
|
# Arrange the main widgets inside layout widgets.
|
110
173
|
def layout
|
111
174
|
main_frame = TkPanedwindow.new(toplevel, :orient => :vertical) {
|
@@ -114,29 +177,35 @@ module Drx
|
|
114
177
|
# We don't want them to obscure the main ones.
|
115
178
|
lower
|
116
179
|
}
|
117
|
-
main_frame.add
|
180
|
+
main_frame.add vbox(
|
118
181
|
[Scrolled.new(toplevel, @eval_result, :vertical => true), { :expand => true, :fill => 'both' } ],
|
119
182
|
TkLabel.new(toplevel, :anchor => 'w') {
|
120
|
-
text 'Type some code to eval; \'self\' is the object at
|
183
|
+
text 'Type some code to eval; \'self\' is the object at the base of the graph; prepend with "see" to inspect result.'
|
121
184
|
},
|
122
|
-
@eval_entry
|
123
|
-
|
185
|
+
@eval_entry
|
186
|
+
)
|
124
187
|
|
125
188
|
panes = TkPanedwindow.new(main_frame, :orient => :horizontal) {
|
126
189
|
lower
|
127
190
|
}
|
128
|
-
|
191
|
+
# Note the :weight's on the followings.
|
192
|
+
panes.add vbox(
|
129
193
|
TkLabel.new(toplevel, :text => 'Object graph (klass and super):', :anchor => 'w'),
|
130
|
-
[@im, { :expand => true, :fill => 'both' } ],
|
131
|
-
|
132
|
-
|
194
|
+
[Scrolled.new(toplevel, @im), { :expand => true, :fill => 'both' } ],
|
195
|
+
hbox(TkLabel.new(toplevel, :text => 'Size: '), @graph_size_menu,
|
196
|
+
separator,
|
197
|
+
TkLabel.new(toplevel, :text => 'Style: '), @graph_style_menu,
|
198
|
+
separator,
|
199
|
+
[@save_btn, { :pady => 5 }])
|
200
|
+
), :weight => 10
|
201
|
+
panes.add vbox(
|
133
202
|
TkLabel.new(toplevel, :text => 'Variables (iv_tbl):', :anchor => 'w'),
|
134
203
|
[Scrolled.new(toplevel, @varsbox), { :expand => true, :fill => 'both' } ]
|
135
|
-
|
136
|
-
panes.add
|
204
|
+
), :weight => 50
|
205
|
+
panes.add vbox(
|
137
206
|
TkLabel.new(toplevel, :text => 'Methods (m_tbl):', :anchor => 'w'),
|
138
207
|
[Scrolled.new(toplevel, @methodsbox), { :expand => true, :fill => 'both' } ]
|
139
|
-
|
208
|
+
), :weight => 10
|
140
209
|
|
141
210
|
main_frame.add(panes)
|
142
211
|
end
|
@@ -151,7 +220,7 @@ module Drx
|
|
151
220
|
|
152
221
|
def open_up_editor(filename, lineno)
|
153
222
|
command = sprintf(ENV['DRX_EDITOR_COMMAND'] || EDITOR_COMMAND, lineno, filename)
|
154
|
-
output "
|
223
|
+
output "Executing: #{command}...\n", 'info'
|
155
224
|
if !fork
|
156
225
|
if !Kernel.system(command)
|
157
226
|
output "Could not execute the command '#{command}'\n", 'error'
|
@@ -160,15 +229,15 @@ module Drx
|
|
160
229
|
end
|
161
230
|
end
|
162
231
|
|
163
|
-
def
|
164
|
-
|
165
|
-
if !
|
232
|
+
def locate_method(obj, method_name)
|
233
|
+
place = ObjInfo.new(obj).locate_method(method_name)
|
234
|
+
if !place
|
166
235
|
output "Method #{method_name} doesn't exist\n", 'info'
|
167
236
|
else
|
168
|
-
if
|
169
|
-
|
237
|
+
if place =~ /\A(\d+):(.*)/
|
238
|
+
open_up_editor($2, $1)
|
170
239
|
else
|
171
|
-
|
240
|
+
output "Can't locate method, because it's a: #{place}\n", 'info'
|
172
241
|
end
|
173
242
|
end
|
174
243
|
end
|
@@ -212,23 +281,35 @@ module Drx
|
|
212
281
|
|
213
282
|
# Fills the variables listbox with a list of the object's instance variables.
|
214
283
|
def display_variables(obj)
|
215
|
-
|
284
|
+
allowed_names = [/^@/, /^[A-Z]/, '__classpath__', '__tmp_classpath__', '__classid__', '__attached__']
|
285
|
+
@varsbox.clear
|
216
286
|
info = ObjInfo.new(obj)
|
217
287
|
if obj and info.has_iv_tbl?
|
218
288
|
vars = info.iv_tbl.keys.map do |v| v.to_s end.sort
|
219
289
|
# Get rid of gazillions of Tk classes:
|
220
290
|
vars = vars.reject { |v| v =~ /Tk|Ttk/ }
|
221
|
-
|
291
|
+
vars.each do |name|
|
292
|
+
value = if allowed_names.any? { |p| p === name }
|
293
|
+
info.__get_ivar(name).inspect
|
294
|
+
else
|
295
|
+
# We don't want to inspect ruby's internal variables (because
|
296
|
+
# they may not be Ruby values at all).
|
297
|
+
''
|
298
|
+
end
|
299
|
+
@varsbox.insert('', 'end', :text => name, :values => [ name, value ] )
|
300
|
+
end
|
222
301
|
end
|
223
302
|
end
|
224
303
|
|
225
304
|
# Fills the methods listbox with a list of the object's methods.
|
226
305
|
def display_methods(obj)
|
227
|
-
@methodsbox.
|
306
|
+
@methodsbox.clear
|
228
307
|
info = ObjInfo.new(obj)
|
229
308
|
if obj and info.class_like?
|
230
309
|
methods = info.m_tbl.keys.map do |v| v.to_s end.sort
|
231
|
-
|
310
|
+
methods.each do |name|
|
311
|
+
@methodsbox.insert('', 'end', :text => name, :values => [ name, File.basename(String(info.locate_method(name))) ] )
|
312
|
+
end
|
232
313
|
end
|
233
314
|
end
|
234
315
|
|
@@ -237,7 +318,7 @@ module Drx
|
|
237
318
|
require 'drx/tempfiles'
|
238
319
|
@objs = {}
|
239
320
|
Tempfiles.new do |files|
|
240
|
-
ObjInfo.new(obj).generate_diagram(files) do |info|
|
321
|
+
ObjInfo.new(obj).generate_diagram(files, @graph_opts) do |info|
|
241
322
|
@objs[info.dot_url] = info
|
242
323
|
end
|
243
324
|
@im.image = files['gif']
|
@@ -245,6 +326,17 @@ module Drx
|
|
245
326
|
end
|
246
327
|
end
|
247
328
|
|
329
|
+
# Saves the graph to a file.
|
330
|
+
def save_graph(obj)
|
331
|
+
require 'drx/tempfiles'
|
332
|
+
Tempfiles.new do |files|
|
333
|
+
ObjInfo.new(obj).generate_diagram(files, @graph_opts)
|
334
|
+
if (output = Tk.getSaveFile(:parent => toplevel, :defaultextension => '.gif')) != ''
|
335
|
+
FileUtils.cp(files['gif'], output)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
248
340
|
# Makes `obj` the primary object seen (the one which is the tip of the diagram).
|
249
341
|
def navigate_to(obj)
|
250
342
|
@current_object = obj
|
@@ -261,6 +353,11 @@ module Drx
|
|
261
353
|
@navigation_history.last
|
262
354
|
end
|
263
355
|
|
356
|
+
# Refreshes the display.
|
357
|
+
def refresh
|
358
|
+
navigate_to tip
|
359
|
+
end
|
360
|
+
|
264
361
|
# Make `obj` the selected object. That is, the one the variable and method boxes reflect.
|
265
362
|
def select_object(obj)
|
266
363
|
@current_object = obj
|
@@ -315,7 +412,7 @@ module Drx
|
|
315
412
|
@the_widget = the_widget
|
316
413
|
if opts[:vertical]
|
317
414
|
TkScrollbar.new(self) { |s|
|
318
|
-
|
415
|
+
grid(:row => 0, :column => 1, :rowspan => 1, :columnspan => 1, :sticky => 'news')
|
319
416
|
command { |*args| the_widget.yview *args }
|
320
417
|
the_widget.yscrollcommand { |first,last| s.set first,last }
|
321
418
|
}
|
@@ -323,13 +420,15 @@ module Drx
|
|
323
420
|
if opts[:horizontal]
|
324
421
|
TkScrollbar.new(self) { |s|
|
325
422
|
orient 'horizontal'
|
326
|
-
|
423
|
+
grid(:row => 1, :column => 0, :rowspan => 1, :columnspan => 1, :sticky => 'news')
|
327
424
|
command { |*args| the_widget.xview *args }
|
328
425
|
the_widget.xscrollcommand { |first,last| s.set first,last }
|
329
426
|
}
|
330
427
|
end
|
331
428
|
the_widget.raise # Since the frame is created after the widget, it obscures it by default.
|
332
|
-
the_widget.
|
429
|
+
the_widget.grid(:in => self, :row => 0, :column => 0, :rowspan => 1, :columnspan => 1, :sticky => 'news')
|
430
|
+
TkGrid.rowconfigure(self, 0, :weight => 1, :minsize => 0)
|
431
|
+
TkGrid.columnconfigure(self, 0, :weight => 1, :minsize => 0)
|
333
432
|
end
|
334
433
|
def raise
|
335
434
|
super
|
@@ -342,20 +441,35 @@ module Drx
|
|
342
441
|
def initialize(parent, widgets)
|
343
442
|
super(parent)
|
344
443
|
widgets.each { |w, layout|
|
345
|
-
layout = {}
|
346
|
-
layout = { :in => self, :side => 'top', :fill => 'x' }.merge layout
|
444
|
+
layout = default_layout.merge(layout || {})
|
347
445
|
w.raise
|
348
446
|
w.pack(layout)
|
349
447
|
}
|
350
448
|
end
|
449
|
+
def default_layout
|
450
|
+
{ :in => self, :side => 'top', :fill => 'x' }
|
451
|
+
end
|
452
|
+
def raise
|
453
|
+
pack_slaves.each {|w| w.raise }
|
454
|
+
end
|
351
455
|
end
|
352
456
|
|
353
|
-
|
457
|
+
# Arranges widgets one beside the other.
|
458
|
+
class HBox < VBox
|
459
|
+
def default_layout
|
460
|
+
{ :in => self, :side => 'left', :fill => 'none' }
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
class ::Tk::Tile::Treeview
|
354
465
|
def get_selection
|
355
|
-
|
466
|
+
selection[0].text
|
356
467
|
end
|
357
468
|
def has_selection?
|
358
|
-
not
|
469
|
+
not selection.empty?
|
470
|
+
end
|
471
|
+
def clear
|
472
|
+
children('').each { |i| delete i }
|
359
473
|
end
|
360
474
|
end
|
361
475
|
|
data/lib/drx/tk/imagemap.rb
CHANGED
@@ -2,12 +2,20 @@ class TkCanvas
|
|
2
2
|
def coords(evt)
|
3
3
|
return [canvasx(evt.x), canvasy(evt.y)]
|
4
4
|
end
|
5
|
+
|
6
|
+
# TkCanvas redefines raise() (and lower()) to operate on tags.
|
7
|
+
# But our layout classes (VBox and Scrolled) require a proper raise(),
|
8
|
+
# so we restore its behavior.
|
9
|
+
# @todo: find out the "correct" way to do it.
|
10
|
+
def raise
|
11
|
+
super
|
12
|
+
end
|
5
13
|
end
|
6
14
|
|
7
15
|
module TkImageMap
|
8
16
|
|
9
17
|
# A widget to show an image together with an HTML imagemap.
|
10
|
-
class ImageMap <
|
18
|
+
class ImageMap < TkCanvas
|
11
19
|
|
12
20
|
def select_command &block
|
13
21
|
@select_command = block
|
@@ -22,10 +30,12 @@ module TkImageMap
|
|
22
30
|
@hotspots.map { |h| h[:url] }
|
23
31
|
end
|
24
32
|
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
28
|
-
|
33
|
+
# I'm not yet sure I want ImageMap to be a TkCanvas directly (an
|
34
|
+
# alternative would be for it to control an external canvas). So,
|
35
|
+
# for future compatibility, I access the canvas through canvas()
|
36
|
+
# instead of assuming it's 'self'.
|
37
|
+
def canvas
|
38
|
+
self
|
29
39
|
end
|
30
40
|
|
31
41
|
def initialize(parent, *rest)
|
@@ -37,22 +47,16 @@ module TkImageMap
|
|
37
47
|
@select_command = proc {}
|
38
48
|
@double_select_command = proc {}
|
39
49
|
|
40
|
-
@canvas = TkCanvas.new self, :scrollregion => [0, 0, 2000, 2000]
|
41
|
-
|
42
|
-
v_scr = TkScrollbar.new(self, :orient => 'vertical').pack :side=> 'right', :fill => 'y'
|
43
|
-
h_scr = TkScrollbar.new(self, :orient => 'horizontal').pack :side=> 'bottom', :fill => 'x'
|
44
|
-
@canvas.xscrollbar(h_scr)
|
45
|
-
@canvas.yscrollbar(v_scr)
|
46
50
|
# Attach scrolling to middle button.
|
47
|
-
|
48
|
-
|
51
|
+
canvas.bind('2', proc { |x,y| canvas.scan_mark(x,y) }, '%x %y')
|
52
|
+
canvas.bind('B2-Motion', proc { |x,y| canvas.scan_dragto x, y }, '%x %y')
|
49
53
|
|
50
|
-
|
54
|
+
canvas.pack :expand => true, :fill => 'both'
|
51
55
|
|
52
56
|
['Motion','Control-Motion'].each { |sequence|
|
53
|
-
|
54
|
-
x, y =
|
55
|
-
spot =
|
57
|
+
canvas.bind sequence do |evt|
|
58
|
+
x, y = canvas.coords(evt)
|
59
|
+
spot = canvas.find('overlapping', x, y, x + 1, y + 1).first
|
56
60
|
if spot and spot.cget('tags').include? 'hotspot'
|
57
61
|
new_hover_region = spot.hotspot_region
|
58
62
|
else
|
@@ -68,31 +72,31 @@ module TkImageMap
|
|
68
72
|
end
|
69
73
|
end
|
70
74
|
}
|
71
|
-
|
72
|
-
x, y =
|
73
|
-
spot =
|
75
|
+
canvas.bind 'Button-1' do |evt|
|
76
|
+
x, y = canvas.coords(evt)
|
77
|
+
spot = canvas.find('overlapping', x, y, x+1, y+1).first
|
74
78
|
if spot and spot.cget('tags').include? 'hotspot'
|
75
79
|
self.active_region = spot.hotspot_region
|
76
80
|
else
|
77
81
|
self.active_region = nil
|
78
82
|
end
|
79
83
|
end
|
80
|
-
|
84
|
+
canvas.bind 'Double-Button-1' do
|
81
85
|
@double_select_command.call(active_url) if @active_region
|
82
86
|
end
|
83
87
|
# Middle button: vertical scrolling.
|
84
|
-
|
85
|
-
|
88
|
+
canvas.bind 'Button-4' do
|
89
|
+
canvas.yview_scroll(-1, 'units')
|
86
90
|
end
|
87
|
-
|
88
|
-
|
91
|
+
canvas.bind 'Button-5' do
|
92
|
+
canvas.yview_scroll(1, 'units')
|
89
93
|
end
|
90
94
|
# Middle button: horizontal scrolling.
|
91
|
-
|
92
|
-
|
95
|
+
canvas.bind 'Shift-Button-4' do
|
96
|
+
canvas.xview_scroll(-1, 'units')
|
93
97
|
end
|
94
|
-
|
95
|
-
|
98
|
+
canvas.bind 'Shift-Button-5' do
|
99
|
+
canvas.xview_scroll(1, 'units')
|
96
100
|
end
|
97
101
|
end
|
98
102
|
|
@@ -124,15 +128,15 @@ module TkImageMap
|
|
124
128
|
|
125
129
|
def image=(pathname)
|
126
130
|
@image = TkPhotoImage.new :file => pathname
|
127
|
-
|
131
|
+
canvas.configure :scrollregion => [0, 0, @image.width, @image.height]
|
128
132
|
end
|
129
133
|
|
130
134
|
# You must call this after image=()
|
131
135
|
def image_map=(pathname)
|
132
136
|
# Delete the previous spot widgets and the image widget.
|
133
|
-
|
134
|
-
|
135
|
-
|
137
|
+
canvas.find('all').each {|w| w.destroy }
|
138
|
+
canvas.xview_moveto(0)
|
139
|
+
canvas.yview_moveto(0)
|
136
140
|
|
137
141
|
@hotspots = Utils.parse_imap(pathname)
|
138
142
|
|
@@ -140,7 +144,7 @@ module TkImageMap
|
|
140
144
|
# Create the back spots.
|
141
145
|
#
|
142
146
|
@hotspots.each do |region|
|
143
|
-
args = [
|
147
|
+
args = [canvas, *(region[:args])]
|
144
148
|
opts = {
|
145
149
|
:fill => 'red',
|
146
150
|
:tags => ['hotspot']
|
@@ -156,13 +160,13 @@ module TkImageMap
|
|
156
160
|
end
|
157
161
|
end
|
158
162
|
|
159
|
-
@image_widget = TkcImage.new(
|
163
|
+
@image_widget = TkcImage.new(canvas,0,0, :image => @image, :anchor=> 'nw')
|
160
164
|
|
161
165
|
#
|
162
166
|
# Create the hover spots.
|
163
167
|
#
|
164
168
|
@hotspots.each do |region|
|
165
|
-
args = [
|
169
|
+
args = [canvas, *(region[:args])]
|
166
170
|
opts = {
|
167
171
|
:dash => '. ',
|
168
172
|
:width => 5,
|
@@ -187,9 +191,7 @@ module TkImageMap
|
|
187
191
|
# DOT outputs oval elements as polygons with many many sides. Since Tk doesn't
|
188
192
|
# draw them well, we convert such polygons to ovals.
|
189
193
|
def self.to_oval(coords)
|
190
|
-
|
191
|
-
#points = coords.each_slice(2).to_a # Doesn't work for older 1.8's.
|
192
|
-
points = coords.enum_for(:each_slice, 2).to_a
|
194
|
+
points = coords.enum_slice(2).to_a
|
193
195
|
if points.size < 10
|
194
196
|
# If there are few sides, we assume it's a real polygon.
|
195
197
|
return nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: drx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mooffie
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-03-28 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -31,10 +31,9 @@ files:
|
|
31
31
|
- lib/drx.rb
|
32
32
|
- ext/extconf.rb
|
33
33
|
- ext/drx_core.c
|
34
|
-
- examples/
|
35
|
-
- examples/
|
36
|
-
- examples/
|
37
|
-
- examples/drx_datamapper19.rb
|
34
|
+
- examples/drx_datamapper.rb
|
35
|
+
- examples/drx_date.rb
|
36
|
+
- examples/drx_guitar.rb
|
38
37
|
has_rdoc: true
|
39
38
|
homepage: http://drx.rubyforge.org/
|
40
39
|
licenses: []
|
@@ -46,9 +45,9 @@ require_paths:
|
|
46
45
|
- lib
|
47
46
|
required_ruby_version: !ruby/object:Gem::Requirement
|
48
47
|
requirements:
|
49
|
-
- -
|
48
|
+
- - ~>
|
50
49
|
- !ruby/object:Gem::Version
|
51
|
-
version: 1.8
|
50
|
+
version: "1.8"
|
52
51
|
version:
|
53
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
53
|
requirements:
|
@@ -62,6 +61,6 @@ rubyforge_project: drx
|
|
62
61
|
rubygems_version: 1.3.5
|
63
62
|
signing_key:
|
64
63
|
specification_version: 3
|
65
|
-
summary:
|
64
|
+
summary: Inspect Ruby objects.
|
66
65
|
test_files: []
|
67
66
|
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'dm-core'
|
3
|
-
$: << File.join('/home/mooffie/_drx.git/lib')
|
4
|
-
require 'drx'
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
# This is part of a blogging website. Users write posts. A post
|
10
|
-
# belongs to a user.
|
11
|
-
#
|
12
|
-
|
13
|
-
class Post
|
14
|
-
include DataMapper::Resource
|
15
|
-
|
16
|
-
property :post_id, Serial
|
17
|
-
property :title, String
|
18
|
-
property :body, Text
|
19
|
-
|
20
|
-
belongs_to :user
|
21
|
-
end
|
22
|
-
|
23
|
-
class User
|
24
|
-
include DataMapper::Resource
|
25
|
-
|
26
|
-
property :user_uid, Serial
|
27
|
-
property :name, String
|
28
|
-
property :mail, String
|
29
|
-
end
|
30
|
-
|
31
|
-
post = Post.new
|
32
|
-
|
33
|
-
p post
|
34
|
-
require 'drx'
|
35
|
-
post.see
|