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.
@@ -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
@@ -74,7 +74,7 @@ module Drx
74
74
  }
75
75
  @methodsbox.bind('Double-Button-1') {
76
76
  if @methodsbox.has_selection?
77
- locate_method(current_object, @methodsbox.get_selection)
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 locate_method(obj, method_name)
164
- place = ObjInfo.new(obj).locate_method(method_name)
165
- if !place
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 place =~ /\A(\d+):(.*)/
169
- open_up_editor($2, $1)
168
+ if location.is_a? String
169
+ output "Can't locate method, because it's a: #{location}\n", 'info'
170
170
  else
171
- output "Can't locate method, because: #{place}\n", 'info'
171
+ open_up_editor(location[0], location[1])
172
172
  end
173
173
  end
174
174
  end
@@ -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
- points = coords.enum_slice(2).to_a
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.1
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-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_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.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