drx 0.4.1 → 0.4.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.
@@ -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
- #include "st.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
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
- // 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...
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 RCLASS(obj)->super ? RCLASS(obj)->super : Qnil;
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
- 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));
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 place;
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
- sprintf(line_s, "%d:", nd_line(ND_block));
199
- place = RSTR(line_s);
200
- rb_str_cat2(place, ND_block->nd_file);
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 place;
217
+ return location;
203
218
  }
204
219
 
205
220
  /*
206
221
  * call-seq:
207
- * Drx::Core::locate_method(Date, "to_s") => str
222
+ * Drx::Core::locate_method(Date, "to_s") => ...
208
223
  *
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>".
224
+ * Locates the filename and line-number where a method was defined.
212
225
  *
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>.
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
- return Qnil;
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 \"#$2:#$1\" end;\
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 diagraming capability to ObjInfo
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://server/obj/#{dot_id}"
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
- def dot_source(level = 0, opts = {}, &block) # :yield:
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 repond to many methods,
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.dot_source(level+1, opts, &block)
141
- if [Module, ObjInfo.new(Module).klass.the_object].include? the_object
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.dot_source(level+1, opts, &block)
154
- out << "#{dot_id} -> #{kls.dot_id} [style=dotted];" "\n"
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
- if level.zero?
159
- out << '}' "\n"
160
- end
179
+ out
180
+ end
161
181
 
162
- return out
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
- # Usualy this means that the ICLASS has a singleton (see "Singletons
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 singletone's klass is confusing and usually unneeded.
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(0, opts, &block)
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::locate_method(@obj, method_name)
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
@@ -105,7 +105,7 @@ module Drx
105
105
  }
106
106
  @methodsbox.bind('Double-Button-1') {
107
107
  if @methodsbox.has_selection?
108
- locate_method(current_object, @methodsbox.get_selection)
108
+ edit(current_object, @methodsbox.get_selection)
109
109
  end
110
110
  }
111
111
  @eval_entry.bind('Key-Return') {
@@ -229,15 +229,15 @@ module Drx
229
229
  end
230
230
  end
231
231
 
232
- def locate_method(obj, method_name)
233
- place = ObjInfo.new(obj).locate_method(method_name)
234
- if !place
232
+ def edit(obj, method_name)
233
+ location = ObjInfo.new(obj).locate_method(method_name) rescue nil
234
+ if !location
235
235
  output "Method #{method_name} doesn't exist\n", 'info'
236
236
  else
237
- if place =~ /\A(\d+):(.*)/
238
- open_up_editor($2, $1)
237
+ if location.is_a? String
238
+ output "Can't locate method, because it's a: #{location}\n", 'info'
239
239
  else
240
- output "Can't locate method, because it's a: #{place}\n", 'info'
240
+ open_up_editor(location[0], location[1])
241
241
  end
242
242
  end
243
243
  end
@@ -301,6 +301,26 @@ module Drx
301
301
  end
302
302
  end
303
303
 
304
+ # Returns a terse method location, for use in GUIs.
305
+ def pretty_location(info, method)
306
+ location = begin
307
+ info.locate_method(method)
308
+ rescue NameError
309
+ if method != '<Allocator>'
310
+ # Since we're using the GUI, the method has to exist. The
311
+ # only possibility here is that it's an undef'ed method entry.
312
+ '<undef>'
313
+ end
314
+ end
315
+ if location.nil?
316
+ ''
317
+ elsif location.is_a? String
318
+ location
319
+ else
320
+ File.basename location[0]
321
+ end
322
+ end
323
+
304
324
  # Fills the methods listbox with a list of the object's methods.
305
325
  def display_methods(obj)
306
326
  @methodsbox.clear
@@ -308,7 +328,7 @@ module Drx
308
328
  if obj and info.class_like?
309
329
  methods = info.m_tbl.keys.map do |v| v.to_s end.sort
310
330
  methods.each do |name|
311
- @methodsbox.insert('', 'end', :text => name, :values => [ name, File.basename(String(info.locate_method(name))) ] )
331
+ @methodsbox.insert('', 'end', :text => name, :values => [ name, pretty_location(info, name) ] )
312
332
  end
313
333
  end
314
334
  end
@@ -191,7 +191,9 @@ module TkImageMap
191
191
  # DOT outputs oval elements as polygons with many many sides. Since Tk doesn't
192
192
  # draw them well, we convert such polygons to ovals.
193
193
  def self.to_oval(coords)
194
- points = coords.enum_slice(2).to_a
194
+ require 'enumerator'
195
+ #points = coords.each_slice(2).to_a # Doesn't work for older 1.8's.
196
+ points = coords.enum_for(:each_slice, 2).to_a
195
197
  if points.size < 10
196
198
  # If there are few sides, we assume it's a real polygon.
197
199
  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.1
4
+ version: 0.4.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-03-29 00:00:00 +03:00
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_datamapper.rb
35
35
  - examples/drx_date.rb
36
36
  - examples/drx_guitar.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.0
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: Inspect Ruby objects.
65
+ summary: Object inspector for Ruby.
65
66
  test_files: []
66
67