drx 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
File without changes
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
3
  require 'rubygems'
4
-
5
4
  require 'drx'
6
5
 
7
6
  class Instrument
@@ -11,23 +10,16 @@ end
11
10
  class Guitar < Instrument
12
11
  def initialize
13
12
  @strings = 5
14
- # puts class # .add_some
15
13
  end
16
14
 
17
15
  def self.add_some
18
16
  @max_strings = 10
19
- @@approved = 'by obama team'
17
+ @@approved = 'by team obama'
20
18
  end
21
19
  end
22
20
 
23
- require 'dm-core'
24
- class Post
25
- include DataMapper::Resource
26
- property :id, Serial
27
- end
28
-
21
+ Guitar.add_some
29
22
  o = Guitar.new
30
- o = Post.new
31
23
  def o.unq
32
24
  def g;end
33
25
  end
data/ext/drx_core.c CHANGED
@@ -1,14 +1,5 @@
1
1
  #include "ruby.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
2
+ #include "st.h"
12
3
 
13
4
  /**
14
5
  * Gets the Ruby's engine type of a variable.
@@ -19,9 +10,11 @@ static VALUE t_get_type(VALUE self, VALUE obj)
19
10
  }
20
11
 
21
12
  // Helper for t_get_iv_tbl().
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).
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...
25
18
  rb_hash_aset(hash, ID2SYM(key), Qtrue);
26
19
  return ST_CONTINUE;
27
20
  }
@@ -38,13 +31,9 @@ static VALUE t_get_iv_tbl(VALUE self, VALUE obj)
38
31
  rb_raise(rb_eTypeError, "Only T_OBJECT/T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
39
32
  }
40
33
 
41
- #ifdef RUBY_1_8
42
34
  if (ROBJECT(obj)->iv_tbl) {
43
35
  st_foreach(ROBJECT(obj)->iv_tbl, record_var, (st_data_t)hash);
44
36
  }
45
- #else
46
- rb_ivar_foreach(obj, record_var, hash);
47
- #endif
48
37
 
49
38
  return hash;
50
39
  }
@@ -69,10 +58,11 @@ static VALUE t_get_ivar(VALUE self, VALUE obj, VALUE var_name)
69
58
  */
70
59
  static VALUE t_get_super(VALUE self, VALUE obj)
71
60
  {
61
+ VALUE super;
72
62
  if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
73
63
  rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
74
64
  }
75
- return RCLASS_SUPER(obj) ? RCLASS_SUPER(obj) : Qnil;
65
+ return RCLASS(obj)->super ? RCLASS(obj)->super : Qnil;
76
66
  }
77
67
 
78
68
  /**
@@ -95,13 +85,9 @@ static VALUE t_get_flags(VALUE self, VALUE obj)
95
85
  }
96
86
 
97
87
  // Helper for t_get_m_tbl().
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);
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));
105
91
  return ST_CONTINUE;
106
92
  }
107
93
 
@@ -132,7 +118,6 @@ static VALUE t_get_address(VALUE self, VALUE obj)
132
118
  return INT2NUM(obj);
133
119
  }
134
120
 
135
- #ifdef RUBY_1_8
136
121
  // {{{ Locating methods
137
122
 
138
123
  #include "node.h"
@@ -141,7 +126,7 @@ static VALUE t_get_address(VALUE self, VALUE obj)
141
126
 
142
127
  static VALUE t_do_locate_method(NODE *ND_method) {
143
128
  NODE *ND_scope = NULL, *ND_block = NULL;
144
- VALUE location;
129
+ VALUE place;
145
130
  char line_s[20];
146
131
 
147
132
  //
@@ -210,24 +195,23 @@ static VALUE t_do_locate_method(NODE *ND_method) {
210
195
  return RSTR("<I'm expecting a NODE_BLOCK here...>");
211
196
  }
212
197
 
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)));
198
+ sprintf(line_s, "%d:", nd_line(ND_block));
199
+ place = RSTR(line_s);
200
+ rb_str_cat2(place, ND_block->nd_file);
216
201
 
217
- return location;
202
+ return place;
218
203
  }
219
204
 
220
205
  /*
221
206
  * call-seq:
222
- * Drx::Core::locate_method(Date, "to_s") => ...
207
+ * Drx::Core::locate_method(Date, "to_s") => str
223
208
  *
224
- * Locates the filename and line-number where a method was defined.
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>".
225
212
  *
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.
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>.
231
215
  */
232
216
  static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
233
217
  {
@@ -237,17 +221,19 @@ static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
237
221
  if (TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
238
222
  rb_raise(rb_eTypeError, "Only T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
239
223
  }
224
+ if (!RCLASS(obj)->m_tbl) {
225
+ return Qnil;
226
+ }
240
227
  c_name = StringValuePtr(method_name);
241
228
  ID id = rb_intern(c_name);
242
- if (RCLASS(obj)->m_tbl && st_lookup(RCLASS(obj)->m_tbl, id, (st_data_t *)&method_node)) {
229
+ if (st_lookup(RCLASS(obj)->m_tbl, id, (st_data_t *)&method_node)) {
243
230
  return t_do_locate_method(method_node);
244
231
  } else {
245
- rb_raise(rb_eNameError, "method not found");
232
+ return Qnil;
246
233
  }
247
234
  }
248
235
 
249
236
  // }}}
250
- #endif // RUBY_1_8
251
237
 
252
238
  VALUE mDrx;
253
239
  VALUE mCore;
@@ -263,19 +249,18 @@ void Init_drx_core() {
263
249
  rb_define_module_function(mCore, "get_address", t_get_address, 1);
264
250
  rb_define_module_function(mCore, "get_type", t_get_type, 1);
265
251
  rb_define_module_function(mCore, "get_ivar", t_get_ivar, 2);
266
- #ifdef RUBY_1_8
267
252
  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
+
268
259
  // For the following, see explanation in t_do_locate_method().
269
260
  rb_eval_string("\
270
261
  class ::Proc;\
271
262
  def _location;\
272
- if to_s =~ /@(.*?):(\\d+)>$/ then [$1, $2.to_i] end;\
263
+ if to_s =~ /@(.*?):(\\d+)>$/ then \"#$2:#$1\" end;\
273
264
  end;\
274
265
  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));
281
266
  }
data/lib/drx/graphviz.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Adds Graphviz diagramming capability to ObjInfo
1
+ # Adds Graphviz diagraming capability to ObjInfo
2
2
 
3
3
  module Drx
4
4
 
@@ -38,19 +38,16 @@ 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} */ "
45
41
  end
46
42
 
47
43
  # Creates a pseudo URL for the HTML imagemap.
48
44
  def dot_url
49
- "http://ruby/object/#{dot_id}"
45
+ "http://server/obj/#{dot_id}"
50
46
  end
51
47
 
52
48
  # Quotes a string to be used in DOT source.
53
49
  def dot_quote(s)
50
+ # @todo: find the documentation for tr()?
54
51
  '"' + s.gsub('\\') { '\\\\' }.gsub('"', '\\"').gsub("\n", '\\n') + '"'
55
52
  end
56
53
 
@@ -114,26 +111,18 @@ module Drx
114
111
  end
115
112
  end
116
113
 
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:
114
+ def dot_source(level = 0, opts = {}, &block) # :yield:
132
115
  out = ''
133
- # Note: since 'obj' may be a T_ICLASS, it doesn't respond to many methods,
116
+ # Note: since 'obj' may be a T_ICLASS, it doesn't repond to many methods,
134
117
  # including is_a?. So when we're querying things we're using Drx calls
135
118
  # instead.
136
119
 
120
+ if level.zero?
121
+ out << 'digraph {' "\n"
122
+ out << @@sizes[opts[:size] || '100%']
123
+ @@seen = {}
124
+ end
125
+
137
126
  seen = @@seen[address]
138
127
  @@seen[address] = true
139
128
 
@@ -148,8 +137,8 @@ module Drx
148
137
 
149
138
  if class_like?
150
139
  if spr = self.super and display_super?(spr)
151
- out << spr.dot_fragment(opts, &block)
152
- if insignificant_super_arrow?(opts)
140
+ out << spr.dot_source(level+1, opts, &block)
141
+ if [Module, ObjInfo.new(Module).klass.the_object].include? the_object
153
142
  # We don't want these relatively insignificant lines to clutter the display,
154
143
  # so we paint them lightly and tell DOT they aren't to affect the layout (width=0).
155
144
  out << "#{dot_id} -> #{spr.dot_id} [color=gray85, weight=0];" "\n"
@@ -161,38 +150,16 @@ module Drx
161
150
 
162
151
  kls = effective_klass
163
152
  if display_klass?(kls)
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"
153
+ out << kls.dot_source(level+1, opts, &block)
154
+ out << "#{dot_id} -> #{kls.dot_id} [style=dotted];" "\n"
176
155
  out << "{ rank=same; #{dot_id}; #{kls.dot_id}; }" "\n"
177
156
  end
178
157
 
179
- out
180
- end
181
-
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
158
+ if level.zero?
159
+ out << '}' "\n"
195
160
  end
161
+
162
+ return out
196
163
  end
197
164
 
198
165
  # Whether to display the klass.
@@ -200,12 +167,12 @@ module Drx
200
167
  if t_iclass?
201
168
  # We're interested in an ICLASS's klass only if it isn't Module.
202
169
  #
203
- # Usually this means that the ICLASS has a singleton (see "Singletons
170
+ # Usualy this means that the ICLASS has a singleton (see "Singletons
204
171
  # of included modules" in display_super?()). We want to see this
205
172
  # singleton.
206
173
  return Module != kls.the_object
207
174
  else
208
- # Displaying a singleton's klass is confusing and usually unneeded.
175
+ # Displaying a singletone's klass is confusing and usually unneeded.
209
176
  return !singleton?
210
177
  end
211
178
  end
@@ -248,7 +215,7 @@ module Drx
248
215
  # end
249
216
  #
250
217
  def generate_diagram(files, opts = {}, &block)
251
- source = self.dot_source(opts, &block)
218
+ source = self.dot_source(0, opts, &block)
252
219
  File.open(files['dot'], 'w') { |f| f.write(source) }
253
220
  command = GRAPHVIZ_COMMAND % [files['dot'], files['gif'], files['map']]
254
221
  message = Kernel.`(command) # `
data/lib/drx/objinfo.rb CHANGED
@@ -35,29 +35,8 @@ 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.
45
38
  def locate_method(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
39
+ Core::locate_method(@obj, method_name)
61
40
  end
62
41
 
63
42
  def has_iv_tbl?
data/lib/drx/tk/app.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require 'tk'
2
- #Tk.default_widget_set = :Ttk
3
2
  require 'drx/tk/imagemap'
4
3
 
4
+ Tk.default_widget_set = :Ttk
5
+
5
6
  module Drx
6
7
  module TkGUI
7
8
 
@@ -17,6 +18,7 @@ module Drx
17
18
  def initialize
18
19
  @navigation_history = []
19
20
  @eval_history = LineHistory.new
21
+ @graph_opts = { :size => '100%', :style => 'default' }
20
22
 
21
23
  @eval_entry = TkEntry.new(toplevel) {
22
24
  font 'Courier'
@@ -45,16 +47,45 @@ module Drx
45
47
  navigate_back
46
48
  }
47
49
 
48
- @varsbox = TkListbox.new(toplevel) {
49
- width 25
50
+ @varsbox = Tk::Tile::Treeview.new(toplevel) {
51
+ columns 'name value'
52
+ heading_configure('name', :text => 'Name')
53
+ heading_configure('value', :text => 'Value')
54
+ column_configure('name', :stretch => false )
55
+ column_configure('value', :stretch => false )
56
+ show 'headings'
50
57
  }
51
- @methodsbox = TkListbox.new(toplevel) {
52
- width 35
58
+ @methodsbox = Tk::Tile::Treeview.new(toplevel) {
59
+ columns 'name location'
60
+ heading_configure('name', :text => 'Name')
61
+ heading_configure('location', :text => 'Location')
62
+ column_configure('name', :stretch => false )
63
+ column_configure('location', :stretch => false )
64
+ show 'headings'
65
+ }
66
+
67
+ @graph_size_menu = Tk::Tile::Combobox.new(toplevel) {
68
+ set '100%'
69
+ values ['100%', '90%', '80%', '60%']
70
+ state :readonly
71
+ width 6
72
+ }
73
+ @graph_style_menu = Tk::Tile::Combobox.new(toplevel) {
74
+ set 'default'
75
+ values ['default', 'crazy']
76
+ state :readonly
77
+ width 10
78
+ }
79
+ @save_btn = TkButton.new(toplevel) {
80
+ text 'Save...'
81
+ }
82
+ @save_btn.command {
83
+ save_graph tip
53
84
  }
54
85
 
55
86
  layout
56
87
 
57
- @varsbox.bind('<ListboxSelect>') {
88
+ @varsbox.bind('<TreeviewSelect>') {
58
89
  if @varsbox.has_selection?
59
90
  require 'pp'
60
91
  output "\n== Variable #{@varsbox.get_selection}\n\n", 'info'
@@ -74,7 +105,7 @@ module Drx
74
105
  }
75
106
  @methodsbox.bind('Double-Button-1') {
76
107
  if @methodsbox.has_selection?
77
- edit(current_object, @methodsbox.get_selection)
108
+ locate_method(current_object, @methodsbox.get_selection)
78
109
  end
79
110
  }
80
111
  @eval_entry.bind('Key-Return') {
@@ -97,15 +128,47 @@ module Drx
97
128
  toplevel.bind('Control-r') {
98
129
  # Refresh the display. Useful if you eval'ed some code that changes the
99
130
  # object inspected.
100
- navigate_to tip
131
+ refresh
101
132
  # Note: it seems that #instance_eval creates a singleton for the object.
102
133
  # So after eval'ing something and pressing C-r, you're going to see this
103
134
  # extra class.
104
135
  }
136
+ @graph_size_menu.bind('<ComboboxSelected>') {
137
+ @graph_opts[:size] = @graph_size_menu.get
138
+ refresh
139
+ }
140
+ @graph_style_menu.bind('<ComboboxSelected>') {
141
+ @graph_opts[:style] = @graph_style_menu.get
142
+ refresh
143
+ }
105
144
 
106
145
  output "Please visit the homepage, http://drx.rubyforge.org/, for usage instructions.\n", 'info'
146
+
147
+ one_time_initialization
148
+ user_customizations
107
149
  end
108
150
 
151
+ def one_time_initialization
152
+ @@one_time_initialization ||= begin
153
+ # Try to make the Unixy GUI less ugly.
154
+ if Tk.windowingsystem == 'x11' and Ttk.themes.include? 'clam'
155
+ Ttk.set_theme 'clam'
156
+ end
157
+ rc = File.join(ENV['HOME'] || Dir.pwd, '.drxrc')
158
+ load rc if File.exist? rc
159
+ 1
160
+ end
161
+ end
162
+
163
+ # Users may redefine this method in their ~/.drxrc
164
+ # to fine-tune the app.
165
+ def user_customizations
166
+ end
167
+
168
+ def vbox(*args); VBox.new(toplevel, args); end
169
+ def hbox(*args); HBox.new(toplevel, args); end
170
+ def separator; TkLabel.new(toplevel, :text => ' '); end
171
+
109
172
  # Arrange the main widgets inside layout widgets.
110
173
  def layout
111
174
  main_frame = TkPanedwindow.new(toplevel, :orient => :vertical) {
@@ -114,29 +177,35 @@ module Drx
114
177
  # We don't want them to obscure the main ones.
115
178
  lower
116
179
  }
117
- main_frame.add VBox.new toplevel, [
180
+ main_frame.add vbox(
118
181
  [Scrolled.new(toplevel, @eval_result, :vertical => true), { :expand => true, :fill => 'both' } ],
119
182
  TkLabel.new(toplevel, :anchor => 'w') {
120
- text 'Type some code to eval; \'self\' is the object at tip of diagram; prepend with "see" to examine result.'
183
+ text 'Type some code to eval; \'self\' is the object at the base of the graph; prepend with "see" to inspect result.'
121
184
  },
122
- @eval_entry,
123
- ]
185
+ @eval_entry
186
+ )
124
187
 
125
188
  panes = TkPanedwindow.new(main_frame, :orient => :horizontal) {
126
189
  lower
127
190
  }
128
- panes.add VBox.new toplevel, [
191
+ # Note the :weight's on the followings.
192
+ panes.add vbox(
129
193
  TkLabel.new(toplevel, :text => 'Object graph (klass and super):', :anchor => 'w'),
130
- [@im, { :expand => true, :fill => 'both' } ],
131
- ]
132
- panes.add VBox.new toplevel, [
194
+ [Scrolled.new(toplevel, @im), { :expand => true, :fill => 'both' } ],
195
+ hbox(TkLabel.new(toplevel, :text => 'Size: '), @graph_size_menu,
196
+ separator,
197
+ TkLabel.new(toplevel, :text => 'Style: '), @graph_style_menu,
198
+ separator,
199
+ [@save_btn, { :pady => 5 }])
200
+ ), :weight => 10
201
+ panes.add vbox(
133
202
  TkLabel.new(toplevel, :text => 'Variables (iv_tbl):', :anchor => 'w'),
134
203
  [Scrolled.new(toplevel, @varsbox), { :expand => true, :fill => 'both' } ]
135
- ]
136
- panes.add VBox.new toplevel, [
204
+ ), :weight => 50
205
+ panes.add vbox(
137
206
  TkLabel.new(toplevel, :text => 'Methods (m_tbl):', :anchor => 'w'),
138
207
  [Scrolled.new(toplevel, @methodsbox), { :expand => true, :fill => 'both' } ]
139
- ]
208
+ ), :weight => 10
140
209
 
141
210
  main_frame.add(panes)
142
211
  end
@@ -151,7 +220,7 @@ module Drx
151
220
 
152
221
  def open_up_editor(filename, lineno)
153
222
  command = sprintf(ENV['DRX_EDITOR_COMMAND'] || EDITOR_COMMAND, lineno, filename)
154
- output "Execting: #{command}...\n", 'info'
223
+ output "Executing: #{command}...\n", 'info'
155
224
  if !fork
156
225
  if !Kernel.system(command)
157
226
  output "Could not execute the command '#{command}'\n", 'error'
@@ -160,15 +229,15 @@ module Drx
160
229
  end
161
230
  end
162
231
 
163
- def edit(obj, method_name)
164
- location = ObjInfo.new(obj).locate_method(method_name) rescue nil
165
- if !location
232
+ def locate_method(obj, method_name)
233
+ place = ObjInfo.new(obj).locate_method(method_name)
234
+ if !place
166
235
  output "Method #{method_name} doesn't exist\n", 'info'
167
236
  else
168
- if location.is_a? String
169
- output "Can't locate method, because it's a: #{location}\n", 'info'
237
+ if place =~ /\A(\d+):(.*)/
238
+ open_up_editor($2, $1)
170
239
  else
171
- open_up_editor(location[0], location[1])
240
+ output "Can't locate method, because it's a: #{place}\n", 'info'
172
241
  end
173
242
  end
174
243
  end
@@ -212,23 +281,35 @@ module Drx
212
281
 
213
282
  # Fills the variables listbox with a list of the object's instance variables.
214
283
  def display_variables(obj)
215
- @varsbox.delete('0', 'end')
284
+ allowed_names = [/^@/, /^[A-Z]/, '__classpath__', '__tmp_classpath__', '__classid__', '__attached__']
285
+ @varsbox.clear
216
286
  info = ObjInfo.new(obj)
217
287
  if obj and info.has_iv_tbl?
218
288
  vars = info.iv_tbl.keys.map do |v| v.to_s end.sort
219
289
  # Get rid of gazillions of Tk classes:
220
290
  vars = vars.reject { |v| v =~ /Tk|Ttk/ }
221
- @varsbox.insert('end', *vars)
291
+ vars.each do |name|
292
+ value = if allowed_names.any? { |p| p === name }
293
+ info.__get_ivar(name).inspect
294
+ else
295
+ # We don't want to inspect ruby's internal variables (because
296
+ # they may not be Ruby values at all).
297
+ ''
298
+ end
299
+ @varsbox.insert('', 'end', :text => name, :values => [ name, value ] )
300
+ end
222
301
  end
223
302
  end
224
303
 
225
304
  # Fills the methods listbox with a list of the object's methods.
226
305
  def display_methods(obj)
227
- @methodsbox.delete('0', 'end')
306
+ @methodsbox.clear
228
307
  info = ObjInfo.new(obj)
229
308
  if obj and info.class_like?
230
309
  methods = info.m_tbl.keys.map do |v| v.to_s end.sort
231
- @methodsbox.insert('end', *methods)
310
+ methods.each do |name|
311
+ @methodsbox.insert('', 'end', :text => name, :values => [ name, File.basename(String(info.locate_method(name))) ] )
312
+ end
232
313
  end
233
314
  end
234
315
 
@@ -237,7 +318,7 @@ module Drx
237
318
  require 'drx/tempfiles'
238
319
  @objs = {}
239
320
  Tempfiles.new do |files|
240
- ObjInfo.new(obj).generate_diagram(files) do |info|
321
+ ObjInfo.new(obj).generate_diagram(files, @graph_opts) do |info|
241
322
  @objs[info.dot_url] = info
242
323
  end
243
324
  @im.image = files['gif']
@@ -245,6 +326,17 @@ module Drx
245
326
  end
246
327
  end
247
328
 
329
+ # Saves the graph to a file.
330
+ def save_graph(obj)
331
+ require 'drx/tempfiles'
332
+ Tempfiles.new do |files|
333
+ ObjInfo.new(obj).generate_diagram(files, @graph_opts)
334
+ if (output = Tk.getSaveFile(:parent => toplevel, :defaultextension => '.gif')) != ''
335
+ FileUtils.cp(files['gif'], output)
336
+ end
337
+ end
338
+ end
339
+
248
340
  # Makes `obj` the primary object seen (the one which is the tip of the diagram).
249
341
  def navigate_to(obj)
250
342
  @current_object = obj
@@ -261,6 +353,11 @@ module Drx
261
353
  @navigation_history.last
262
354
  end
263
355
 
356
+ # Refreshes the display.
357
+ def refresh
358
+ navigate_to tip
359
+ end
360
+
264
361
  # Make `obj` the selected object. That is, the one the variable and method boxes reflect.
265
362
  def select_object(obj)
266
363
  @current_object = obj
@@ -315,7 +412,7 @@ module Drx
315
412
  @the_widget = the_widget
316
413
  if opts[:vertical]
317
414
  TkScrollbar.new(self) { |s|
318
- pack :side => 'right', :fill => 'y'
415
+ grid(:row => 0, :column => 1, :rowspan => 1, :columnspan => 1, :sticky => 'news')
319
416
  command { |*args| the_widget.yview *args }
320
417
  the_widget.yscrollcommand { |first,last| s.set first,last }
321
418
  }
@@ -323,13 +420,15 @@ module Drx
323
420
  if opts[:horizontal]
324
421
  TkScrollbar.new(self) { |s|
325
422
  orient 'horizontal'
326
- pack :side => 'bottom', :fill => 'x'
423
+ grid(:row => 1, :column => 0, :rowspan => 1, :columnspan => 1, :sticky => 'news')
327
424
  command { |*args| the_widget.xview *args }
328
425
  the_widget.xscrollcommand { |first,last| s.set first,last }
329
426
  }
330
427
  end
331
428
  the_widget.raise # Since the frame is created after the widget, it obscures it by default.
332
- the_widget.pack(:in => self, :side => 'left', :expand => 'true', :fill => 'both')
429
+ the_widget.grid(:in => self, :row => 0, :column => 0, :rowspan => 1, :columnspan => 1, :sticky => 'news')
430
+ TkGrid.rowconfigure(self, 0, :weight => 1, :minsize => 0)
431
+ TkGrid.columnconfigure(self, 0, :weight => 1, :minsize => 0)
333
432
  end
334
433
  def raise
335
434
  super
@@ -342,20 +441,35 @@ module Drx
342
441
  def initialize(parent, widgets)
343
442
  super(parent)
344
443
  widgets.each { |w, layout|
345
- layout = {} if layout.nil?
346
- layout = { :in => self, :side => 'top', :fill => 'x' }.merge layout
444
+ layout = default_layout.merge(layout || {})
347
445
  w.raise
348
446
  w.pack(layout)
349
447
  }
350
448
  end
449
+ def default_layout
450
+ { :in => self, :side => 'top', :fill => 'x' }
451
+ end
452
+ def raise
453
+ pack_slaves.each {|w| w.raise }
454
+ end
351
455
  end
352
456
 
353
- class ::TkListbox
457
+ # Arranges widgets one beside the other.
458
+ class HBox < VBox
459
+ def default_layout
460
+ { :in => self, :side => 'left', :fill => 'none' }
461
+ end
462
+ end
463
+
464
+ class ::Tk::Tile::Treeview
354
465
  def get_selection
355
- return get(curselection[0])
466
+ selection[0].text
356
467
  end
357
468
  def has_selection?
358
- not curselection.empty?
469
+ not selection.empty?
470
+ end
471
+ def clear
472
+ children('').each { |i| delete i }
359
473
  end
360
474
  end
361
475
 
@@ -2,12 +2,20 @@ class TkCanvas
2
2
  def coords(evt)
3
3
  return [canvasx(evt.x), canvasy(evt.y)]
4
4
  end
5
+
6
+ # TkCanvas redefines raise() (and lower()) to operate on tags.
7
+ # But our layout classes (VBox and Scrolled) require a proper raise(),
8
+ # so we restore its behavior.
9
+ # @todo: find out the "correct" way to do it.
10
+ def raise
11
+ super
12
+ end
5
13
  end
6
14
 
7
15
  module TkImageMap
8
16
 
9
17
  # A widget to show an image together with an HTML imagemap.
10
- class ImageMap < TkFrame
18
+ class ImageMap < TkCanvas
11
19
 
12
20
  def select_command &block
13
21
  @select_command = block
@@ -22,10 +30,12 @@ module TkImageMap
22
30
  @hotspots.map { |h| h[:url] }
23
31
  end
24
32
 
25
- # Delegates all bindings to the canvas. That's probably what the
26
- # programmer expects.
27
- def bind(*args, &block)
28
- @canvas.bind *args, &block
33
+ # I'm not yet sure I want ImageMap to be a TkCanvas directly (an
34
+ # alternative would be for it to control an external canvas). So,
35
+ # for future compatibility, I access the canvas through canvas()
36
+ # instead of assuming it's 'self'.
37
+ def canvas
38
+ self
29
39
  end
30
40
 
31
41
  def initialize(parent, *rest)
@@ -37,22 +47,16 @@ module TkImageMap
37
47
  @select_command = proc {}
38
48
  @double_select_command = proc {}
39
49
 
40
- @canvas = TkCanvas.new self, :scrollregion => [0, 0, 2000, 2000]
41
-
42
- v_scr = TkScrollbar.new(self, :orient => 'vertical').pack :side=> 'right', :fill => 'y'
43
- h_scr = TkScrollbar.new(self, :orient => 'horizontal').pack :side=> 'bottom', :fill => 'x'
44
- @canvas.xscrollbar(h_scr)
45
- @canvas.yscrollbar(v_scr)
46
50
  # Attach scrolling to middle button.
47
- @canvas.bind('2', proc { |x,y| @canvas.scan_mark(x,y) }, '%x %y')
48
- @canvas.bind('B2-Motion', proc { |x,y| @canvas.scan_dragto x, y }, '%x %y')
51
+ canvas.bind('2', proc { |x,y| canvas.scan_mark(x,y) }, '%x %y')
52
+ canvas.bind('B2-Motion', proc { |x,y| canvas.scan_dragto x, y }, '%x %y')
49
53
 
50
- @canvas.pack :expand => true, :fill => 'both'
54
+ canvas.pack :expand => true, :fill => 'both'
51
55
 
52
56
  ['Motion','Control-Motion'].each { |sequence|
53
- @canvas.bind sequence do |evt|
54
- x, y = @canvas.coords(evt)
55
- spot = @canvas.find('overlapping', x, y, x + 1, y + 1).first
57
+ canvas.bind sequence do |evt|
58
+ x, y = canvas.coords(evt)
59
+ spot = canvas.find('overlapping', x, y, x + 1, y + 1).first
56
60
  if spot and spot.cget('tags').include? 'hotspot'
57
61
  new_hover_region = spot.hotspot_region
58
62
  else
@@ -68,31 +72,31 @@ module TkImageMap
68
72
  end
69
73
  end
70
74
  }
71
- @canvas.bind 'Button-1' do |evt|
72
- x, y = @canvas.coords(evt)
73
- spot = @canvas.find('overlapping', x, y, x+1, y+1).first
75
+ canvas.bind 'Button-1' do |evt|
76
+ x, y = canvas.coords(evt)
77
+ spot = canvas.find('overlapping', x, y, x+1, y+1).first
74
78
  if spot and spot.cget('tags').include? 'hotspot'
75
79
  self.active_region = spot.hotspot_region
76
80
  else
77
81
  self.active_region = nil
78
82
  end
79
83
  end
80
- @canvas.bind 'Double-Button-1' do
84
+ canvas.bind 'Double-Button-1' do
81
85
  @double_select_command.call(active_url) if @active_region
82
86
  end
83
87
  # Middle button: vertical scrolling.
84
- @canvas.bind 'Button-4' do
85
- @canvas.yview_scroll(-1, 'units')
88
+ canvas.bind 'Button-4' do
89
+ canvas.yview_scroll(-1, 'units')
86
90
  end
87
- @canvas.bind 'Button-5' do
88
- @canvas.yview_scroll(1, 'units')
91
+ canvas.bind 'Button-5' do
92
+ canvas.yview_scroll(1, 'units')
89
93
  end
90
94
  # Middle button: horizontal scrolling.
91
- @canvas.bind 'Shift-Button-4' do
92
- @canvas.xview_scroll(-1, 'units')
95
+ canvas.bind 'Shift-Button-4' do
96
+ canvas.xview_scroll(-1, 'units')
93
97
  end
94
- @canvas.bind 'Shift-Button-5' do
95
- @canvas.xview_scroll(1, 'units')
98
+ canvas.bind 'Shift-Button-5' do
99
+ canvas.xview_scroll(1, 'units')
96
100
  end
97
101
  end
98
102
 
@@ -124,15 +128,15 @@ module TkImageMap
124
128
 
125
129
  def image=(pathname)
126
130
  @image = TkPhotoImage.new :file => pathname
127
- @canvas.configure :scrollregion => [0, 0, @image.width, @image.height]
131
+ canvas.configure :scrollregion => [0, 0, @image.width, @image.height]
128
132
  end
129
133
 
130
134
  # You must call this after image=()
131
135
  def image_map=(pathname)
132
136
  # Delete the previous spot widgets and the image widget.
133
- @canvas.find('all').each {|w| w.destroy }
134
- @canvas.xview_moveto(0)
135
- @canvas.yview_moveto(0)
137
+ canvas.find('all').each {|w| w.destroy }
138
+ canvas.xview_moveto(0)
139
+ canvas.yview_moveto(0)
136
140
 
137
141
  @hotspots = Utils.parse_imap(pathname)
138
142
 
@@ -140,7 +144,7 @@ module TkImageMap
140
144
  # Create the back spots.
141
145
  #
142
146
  @hotspots.each do |region|
143
- args = [@canvas, *(region[:args])]
147
+ args = [canvas, *(region[:args])]
144
148
  opts = {
145
149
  :fill => 'red',
146
150
  :tags => ['hotspot']
@@ -156,13 +160,13 @@ module TkImageMap
156
160
  end
157
161
  end
158
162
 
159
- @image_widget = TkcImage.new(@canvas,0,0, :image => @image, :anchor=> 'nw')
163
+ @image_widget = TkcImage.new(canvas,0,0, :image => @image, :anchor=> 'nw')
160
164
 
161
165
  #
162
166
  # Create the hover spots.
163
167
  #
164
168
  @hotspots.each do |region|
165
- args = [@canvas, *(region[:args])]
169
+ args = [canvas, *(region[:args])]
166
170
  opts = {
167
171
  :dash => '. ',
168
172
  :width => 5,
@@ -187,9 +191,7 @@ module TkImageMap
187
191
  # DOT outputs oval elements as polygons with many many sides. Since Tk doesn't
188
192
  # draw them well, we convert such polygons to ovals.
189
193
  def self.to_oval(coords)
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
194
+ points = coords.enum_slice(2).to_a
193
195
  if points.size < 10
194
196
  # If there are few sides, we assume it's a real polygon.
195
197
  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.2
4
+ version: 0.4.0
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-04-02 00:00:00 +03:00
12
+ date: 2010-03-28 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,10 +31,9 @@ files:
31
31
  - lib/drx.rb
32
32
  - ext/extconf.rb
33
33
  - ext/drx_core.c
34
- - examples/drx_test.rb
35
- - examples/drx_test3.rb
36
- - examples/drx_test2.rb
37
- - examples/drx_datamapper19.rb
34
+ - examples/drx_datamapper.rb
35
+ - examples/drx_date.rb
36
+ - examples/drx_guitar.rb
38
37
  has_rdoc: true
39
38
  homepage: http://drx.rubyforge.org/
40
39
  licenses: []
@@ -46,9 +45,9 @@ require_paths:
46
45
  - lib
47
46
  required_ruby_version: !ruby/object:Gem::Requirement
48
47
  requirements:
49
- - - ">="
48
+ - - ~>
50
49
  - !ruby/object:Gem::Version
51
- version: 1.8.2
50
+ version: "1.8"
52
51
  version:
53
52
  required_rubygems_version: !ruby/object:Gem::Requirement
54
53
  requirements:
@@ -62,6 +61,6 @@ rubyforge_project: drx
62
61
  rubygems_version: 1.3.5
63
62
  signing_key:
64
63
  specification_version: 3
65
- summary: Object inspector for Ruby.
64
+ summary: Inspect Ruby objects.
66
65
  test_files: []
67
66
 
@@ -1,35 +0,0 @@
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