drx 0.3.1 → 0.3.2
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_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
|
|