drx 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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