drx 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/drx_datamapper19.rb +35 -0
- data/ext/drx_core.c +49 -34
- data/lib/drx/graphviz.rb +55 -22
- data/lib/drx/objinfo.rb +22 -1
- data/lib/drx/tk/app.rb +7 -7
- data/lib/drx/tk/imagemap.rb +3 -1
- metadata +6 -5
@@ -0,0 +1,35 @@
|
|
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
|
data/ext/drx_core.c
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
#include "ruby.h"
|
2
|
-
|
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
|
3
12
|
|
4
13
|
/**
|
5
14
|
* Gets the Ruby's engine type of a variable.
|
@@ -10,11 +19,9 @@ static VALUE t_get_type(VALUE self, VALUE obj)
|
|
10
19
|
}
|
11
20
|
|
12
21
|
// Helper for t_get_iv_tbl().
|
13
|
-
int record_var(st_data_t key, st_data_t value, VALUE hash) {
|
14
|
-
//
|
15
|
-
//
|
16
|
-
rb_hash_aset(hash, ID2SYM(key), value);
|
17
|
-
// So...
|
22
|
+
static int record_var(st_data_t key, st_data_t value, VALUE hash) {
|
23
|
+
// We don't put the 'value' in the hash because it may not be a Ruby
|
24
|
+
// conventional value but a NODE (and acidentally printing it may crash ruby).
|
18
25
|
rb_hash_aset(hash, ID2SYM(key), Qtrue);
|
19
26
|
return ST_CONTINUE;
|
20
27
|
}
|
@@ -31,9 +38,13 @@ static VALUE t_get_iv_tbl(VALUE self, VALUE obj)
|
|
31
38
|
rb_raise(rb_eTypeError, "Only T_OBJECT/T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
32
39
|
}
|
33
40
|
|
41
|
+
#ifdef RUBY_1_8
|
34
42
|
if (ROBJECT(obj)->iv_tbl) {
|
35
43
|
st_foreach(ROBJECT(obj)->iv_tbl, record_var, (st_data_t)hash);
|
36
44
|
}
|
45
|
+
#else
|
46
|
+
rb_ivar_foreach(obj, record_var, hash);
|
47
|
+
#endif
|
37
48
|
|
38
49
|
return hash;
|
39
50
|
}
|
@@ -58,11 +69,10 @@ static VALUE t_get_ivar(VALUE self, VALUE obj, VALUE var_name)
|
|
58
69
|
*/
|
59
70
|
static VALUE t_get_super(VALUE self, VALUE obj)
|
60
71
|
{
|
61
|
-
VALUE super;
|
62
72
|
if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
63
73
|
rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
64
74
|
}
|
65
|
-
return
|
75
|
+
return RCLASS_SUPER(obj) ? RCLASS_SUPER(obj) : Qnil;
|
66
76
|
}
|
67
77
|
|
68
78
|
/**
|
@@ -85,9 +95,13 @@ static VALUE t_get_flags(VALUE self, VALUE obj)
|
|
85
95
|
}
|
86
96
|
|
87
97
|
// Helper for t_get_m_tbl().
|
88
|
-
|
89
|
-
|
90
|
-
|
98
|
+
// @todo: Store something useful in the values?
|
99
|
+
static int record_method(st_data_t key, st_data_t value, VALUE hash) {
|
100
|
+
static ID id_allocator_symb = 0;
|
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);
|
91
105
|
return ST_CONTINUE;
|
92
106
|
}
|
93
107
|
|
@@ -118,6 +132,7 @@ static VALUE t_get_address(VALUE self, VALUE obj)
|
|
118
132
|
return INT2NUM(obj);
|
119
133
|
}
|
120
134
|
|
135
|
+
#ifdef RUBY_1_8
|
121
136
|
// {{{ Locating methods
|
122
137
|
|
123
138
|
#include "node.h"
|
@@ -126,7 +141,7 @@ static VALUE t_get_address(VALUE self, VALUE obj)
|
|
126
141
|
|
127
142
|
static VALUE t_do_locate_method(NODE *ND_method) {
|
128
143
|
NODE *ND_scope = NULL, *ND_block = NULL;
|
129
|
-
VALUE
|
144
|
+
VALUE location;
|
130
145
|
char line_s[20];
|
131
146
|
|
132
147
|
//
|
@@ -195,23 +210,24 @@ static VALUE t_do_locate_method(NODE *ND_method) {
|
|
195
210
|
return RSTR("<I'm expecting a NODE_BLOCK here...>");
|
196
211
|
}
|
197
212
|
|
198
|
-
|
199
|
-
|
200
|
-
|
213
|
+
location = rb_ary_new();
|
214
|
+
rb_ary_push(location, RSTR(ND_block->nd_file));
|
215
|
+
rb_ary_push(location, INT2FIX(nd_line(ND_block)));
|
201
216
|
|
202
|
-
return
|
217
|
+
return location;
|
203
218
|
}
|
204
219
|
|
205
220
|
/*
|
206
221
|
* call-seq:
|
207
|
-
* Drx::Core::locate_method(Date, "to_s") =>
|
222
|
+
* Drx::Core::locate_method(Date, "to_s") => ...
|
208
223
|
*
|
209
|
-
* Locates the filename and line-number where a method was defined.
|
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>".
|
224
|
+
* Locates the filename and line-number where a method was defined.
|
212
225
|
*
|
213
|
-
*
|
214
|
-
*
|
226
|
+
* Returns one of:
|
227
|
+
* - [ "/path/to/file.rb", 89 ]
|
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.
|
215
231
|
*/
|
216
232
|
static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
|
217
233
|
{
|
@@ -221,19 +237,17 @@ static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
|
|
221
237
|
if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
|
222
238
|
rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
|
223
239
|
}
|
224
|
-
if (!RCLASS(obj)->m_tbl) {
|
225
|
-
return Qnil;
|
226
|
-
}
|
227
240
|
c_name = StringValuePtr(method_name);
|
228
241
|
ID id = rb_intern(c_name);
|
229
|
-
if (st_lookup(RCLASS(obj)->m_tbl, id, (st_data_t *)&method_node)) {
|
242
|
+
if (RCLASS(obj)->m_tbl && st_lookup(RCLASS(obj)->m_tbl, id, (st_data_t *)&method_node)) {
|
230
243
|
return t_do_locate_method(method_node);
|
231
244
|
} else {
|
232
|
-
|
245
|
+
rb_raise(rb_eNameError, "method not found");
|
233
246
|
}
|
234
247
|
}
|
235
248
|
|
236
249
|
// }}}
|
250
|
+
#endif // RUBY_1_8
|
237
251
|
|
238
252
|
VALUE mDrx;
|
239
253
|
VALUE mCore;
|
@@ -249,18 +263,19 @@ void Init_drx_core() {
|
|
249
263
|
rb_define_module_function(mCore, "get_address", t_get_address, 1);
|
250
264
|
rb_define_module_function(mCore, "get_type", t_get_type, 1);
|
251
265
|
rb_define_module_function(mCore, "get_ivar", t_get_ivar, 2);
|
266
|
+
#ifdef RUBY_1_8
|
252
267
|
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
|
-
|
259
268
|
// For the following, see explanation in t_do_locate_method().
|
260
269
|
rb_eval_string("\
|
261
270
|
class ::Proc;\
|
262
271
|
def _location;\
|
263
|
-
if to_s =~ /@(.*?):(\\d+)>$/ then
|
272
|
+
if to_s =~ /@(.*?):(\\d+)>$/ then [$1, $2.to_i] end;\
|
264
273
|
end;\
|
265
274
|
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));
|
266
281
|
}
|
data/lib/drx/graphviz.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Adds Graphviz
|
1
|
+
# Adds Graphviz diagramming capability to ObjInfo
|
2
2
|
|
3
3
|
module Drx
|
4
4
|
|
@@ -38,16 +38,19 @@ 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} */ "
|
41
45
|
end
|
42
46
|
|
43
47
|
# Creates a pseudo URL for the HTML imagemap.
|
44
48
|
def dot_url
|
45
|
-
"http://
|
49
|
+
"http://ruby/object/#{dot_id}"
|
46
50
|
end
|
47
51
|
|
48
52
|
# Quotes a string to be used in DOT source.
|
49
53
|
def dot_quote(s)
|
50
|
-
# @todo: find the documentation for tr()?
|
51
54
|
'"' + s.gsub('\\') { '\\\\' }.gsub('"', '\\"').gsub("\n", '\\n') + '"'
|
52
55
|
end
|
53
56
|
|
@@ -111,18 +114,26 @@ module Drx
|
|
111
114
|
end
|
112
115
|
end
|
113
116
|
|
114
|
-
|
117
|
+
# Builds the DOT source for the diagram. if you're only interested
|
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:
|
115
132
|
out = ''
|
116
|
-
# Note: since 'obj' may be a T_ICLASS, it doesn't
|
133
|
+
# Note: since 'obj' may be a T_ICLASS, it doesn't respond to many methods,
|
117
134
|
# including is_a?. So when we're querying things we're using Drx calls
|
118
135
|
# instead.
|
119
136
|
|
120
|
-
if level.zero?
|
121
|
-
out << 'digraph {' "\n"
|
122
|
-
out << @@sizes[opts[:size] || '100%']
|
123
|
-
@@seen = {}
|
124
|
-
end
|
125
|
-
|
126
137
|
seen = @@seen[address]
|
127
138
|
@@seen[address] = true
|
128
139
|
|
@@ -137,8 +148,8 @@ module Drx
|
|
137
148
|
|
138
149
|
if class_like?
|
139
150
|
if spr = self.super and display_super?(spr)
|
140
|
-
out << spr.
|
141
|
-
if
|
151
|
+
out << spr.dot_fragment(opts, &block)
|
152
|
+
if insignificant_super_arrow?(opts)
|
142
153
|
# We don't want these relatively insignificant lines to clutter the display,
|
143
154
|
# so we paint them lightly and tell DOT they aren't to affect the layout (width=0).
|
144
155
|
out << "#{dot_id} -> #{spr.dot_id} [color=gray85, weight=0];" "\n"
|
@@ -150,16 +161,38 @@ module Drx
|
|
150
161
|
|
151
162
|
kls = effective_klass
|
152
163
|
if display_klass?(kls)
|
153
|
-
out << kls.
|
154
|
-
|
164
|
+
out << kls.dot_fragment(opts, &block)
|
165
|
+
# Recall that in Ruby there are two main inheritance groups: the class
|
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"
|
155
176
|
out << "{ rank=same; #{dot_id}; #{kls.dot_id}; }" "\n"
|
156
177
|
end
|
157
178
|
|
158
|
-
|
159
|
-
|
160
|
-
end
|
179
|
+
out
|
180
|
+
end
|
161
181
|
|
162
|
-
|
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
|
195
|
+
end
|
163
196
|
end
|
164
197
|
|
165
198
|
# Whether to display the klass.
|
@@ -167,12 +200,12 @@ module Drx
|
|
167
200
|
if t_iclass?
|
168
201
|
# We're interested in an ICLASS's klass only if it isn't Module.
|
169
202
|
#
|
170
|
-
#
|
203
|
+
# Usually this means that the ICLASS has a singleton (see "Singletons
|
171
204
|
# of included modules" in display_super?()). We want to see this
|
172
205
|
# singleton.
|
173
206
|
return Module != kls.the_object
|
174
207
|
else
|
175
|
-
# Displaying a
|
208
|
+
# Displaying a singleton's klass is confusing and usually unneeded.
|
176
209
|
return !singleton?
|
177
210
|
end
|
178
211
|
end
|
@@ -215,7 +248,7 @@ module Drx
|
|
215
248
|
# end
|
216
249
|
#
|
217
250
|
def generate_diagram(files, opts = {}, &block)
|
218
|
-
source = self.dot_source(
|
251
|
+
source = self.dot_source(opts, &block)
|
219
252
|
File.open(files['dot'], 'w') { |f| f.write(source) }
|
220
253
|
command = GRAPHVIZ_COMMAND % [files['dot'], files['gif'], files['map']]
|
221
254
|
message = Kernel.`(command) # `
|
data/lib/drx/objinfo.rb
CHANGED
@@ -35,8 +35,29 @@ 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.
|
38
45
|
def locate_method(method_name)
|
39
|
-
Core
|
46
|
+
if Core.respond_to? :locate_method
|
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
|
40
61
|
end
|
41
62
|
|
42
63
|
def has_iv_tbl?
|
data/lib/drx/tk/app.rb
CHANGED
@@ -74,7 +74,7 @@ module Drx
|
|
74
74
|
}
|
75
75
|
@methodsbox.bind('Double-Button-1') {
|
76
76
|
if @methodsbox.has_selection?
|
77
|
-
|
77
|
+
edit(current_object, @methodsbox.get_selection)
|
78
78
|
end
|
79
79
|
}
|
80
80
|
@eval_entry.bind('Key-Return') {
|
@@ -160,15 +160,15 @@ module Drx
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
-
def
|
164
|
-
|
165
|
-
if !
|
163
|
+
def edit(obj, method_name)
|
164
|
+
location = ObjInfo.new(obj).locate_method(method_name) rescue nil
|
165
|
+
if !location
|
166
166
|
output "Method #{method_name} doesn't exist\n", 'info'
|
167
167
|
else
|
168
|
-
if
|
169
|
-
|
168
|
+
if location.is_a? String
|
169
|
+
output "Can't locate method, because it's a: #{location}\n", 'info'
|
170
170
|
else
|
171
|
-
|
171
|
+
open_up_editor(location[0], location[1])
|
172
172
|
end
|
173
173
|
end
|
174
174
|
end
|
data/lib/drx/tk/imagemap.rb
CHANGED
@@ -187,7 +187,9 @@ module TkImageMap
|
|
187
187
|
# DOT outputs oval elements as polygons with many many sides. Since Tk doesn't
|
188
188
|
# draw them well, we convert such polygons to ovals.
|
189
189
|
def self.to_oval(coords)
|
190
|
-
|
190
|
+
require 'enumerator'
|
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
|
191
193
|
if points.size < 10
|
192
194
|
# If there are few sides, we assume it's a real polygon.
|
193
195
|
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.3.
|
4
|
+
version: 0.3.2
|
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-04-02 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- examples/drx_test.rb
|
35
35
|
- examples/drx_test3.rb
|
36
36
|
- examples/drx_test2.rb
|
37
|
+
- examples/drx_datamapper19.rb
|
37
38
|
has_rdoc: true
|
38
39
|
homepage: http://drx.rubyforge.org/
|
39
40
|
licenses: []
|
@@ -45,9 +46,9 @@ require_paths:
|
|
45
46
|
- lib
|
46
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
47
48
|
requirements:
|
48
|
-
- -
|
49
|
+
- - ">="
|
49
50
|
- !ruby/object:Gem::Version
|
50
|
-
version: 1.8.
|
51
|
+
version: 1.8.2
|
51
52
|
version:
|
52
53
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
54
|
requirements:
|
@@ -61,6 +62,6 @@ rubyforge_project: drx
|
|
61
62
|
rubygems_version: 1.3.5
|
62
63
|
signing_key:
|
63
64
|
specification_version: 3
|
64
|
-
summary:
|
65
|
+
summary: Object inspector for Ruby.
|
65
66
|
test_files: []
|
66
67
|
|