drx 0.3.2 → 0.4.0

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.
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