drx 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/drx_activerecord.rb +44 -0
- data/examples/drx_sequel.rb +35 -0
- data/lib/drx/arguments.rb +103 -0
- data/lib/drx/graphviz.rb +55 -21
- data/lib/drx/objinfo.rb +8 -3
- data/lib/drx/tk/app.rb +117 -27
- data/lib/drx.rb +1 -0
- metadata +5 -3
- data/examples/drx_datamapper19.rb +0 -35
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'activerecord'
|
3
|
+
|
4
|
+
#
|
5
|
+
# This is part of a blogging website. Users write posts. A post
|
6
|
+
# belongs to a user.
|
7
|
+
#
|
8
|
+
|
9
|
+
ActiveRecord::Base.logger = Logger.new(STDERR)
|
10
|
+
ActiveRecord::Base.colorize_logging = false
|
11
|
+
|
12
|
+
ActiveRecord::Base.establish_connection(
|
13
|
+
:adapter => 'sqlite3',
|
14
|
+
:database => ':memory:'
|
15
|
+
)
|
16
|
+
|
17
|
+
ActiveRecord::Schema.define do
|
18
|
+
create_table :users do |table|
|
19
|
+
table.column :name, :string
|
20
|
+
table.column :mail, :string
|
21
|
+
end
|
22
|
+
create_table :posts do |table|
|
23
|
+
table.column :user_id, :integer
|
24
|
+
table.column :title, :string
|
25
|
+
table.column :body, :string
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class User < ActiveRecord::Base
|
30
|
+
has_many :posts
|
31
|
+
end
|
32
|
+
|
33
|
+
class Post < ActiveRecord::Base
|
34
|
+
belongs_to :users
|
35
|
+
end
|
36
|
+
|
37
|
+
david = User.create(:name => 'David')
|
38
|
+
david.posts.create(:title => 'Lies')
|
39
|
+
david.posts.create(:title => 'Truths')
|
40
|
+
|
41
|
+
post = User.find(1).posts.first
|
42
|
+
|
43
|
+
require 'drx'
|
44
|
+
post.see
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sequel'
|
3
|
+
|
4
|
+
#
|
5
|
+
# This is part of a blogging website. Users write posts. A post
|
6
|
+
# belongs to a user.
|
7
|
+
#
|
8
|
+
|
9
|
+
DB = Sequel.sqlite(':memory:')
|
10
|
+
|
11
|
+
DB.create_table :posts do
|
12
|
+
primary_key :id
|
13
|
+
Integer :user_id
|
14
|
+
String :title
|
15
|
+
String :body
|
16
|
+
end
|
17
|
+
|
18
|
+
DB.create_table :users do
|
19
|
+
primary_key :id
|
20
|
+
String :name
|
21
|
+
String :email
|
22
|
+
end
|
23
|
+
|
24
|
+
class Post < Sequel::Model
|
25
|
+
many_to_one :user
|
26
|
+
end
|
27
|
+
|
28
|
+
class User < Sequel::Model
|
29
|
+
one_to_many :posts
|
30
|
+
end
|
31
|
+
|
32
|
+
post = Post.create(:title => 'Lies', :user => User.create(:name => 'David'))
|
33
|
+
|
34
|
+
require 'drx'
|
35
|
+
post.see
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Adds method arguments detection to ObjInfo
|
2
|
+
|
3
|
+
module Drx
|
4
|
+
|
5
|
+
class ObjInfo
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Whether to use the 'arguments' gem, if present.
|
9
|
+
# This isn't on by default because it's slow.
|
10
|
+
attr_accessor :use_arguments_gem
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a Ruby 1.9.2-compatible array describing the arguments a method expects.
|
14
|
+
def method_arguments(method_name)
|
15
|
+
if ObjInfo.use_arguments_gem
|
16
|
+
_method_arguments__by_arguments_gem(method_name) || _method_arguments__by_arity(method_name)
|
17
|
+
else
|
18
|
+
_method_arguments__by_methopara(method_name) || _method_arguments__by_arity(method_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Strategy: use the 'arguments' gem, which, in turn, uses either ParseTree (Ruby 1.8)
|
23
|
+
# or RubyParser (Ruby 1.9)
|
24
|
+
#
|
25
|
+
# pros: shows default values; works for 1.8 too.
|
26
|
+
# cons: very slow.
|
27
|
+
def _method_arguments__by_arguments_gem(method_name)
|
28
|
+
@@once__arguments ||= begin
|
29
|
+
begin
|
30
|
+
require 'arguments'
|
31
|
+
rescue LoadError
|
32
|
+
# Not installed.
|
33
|
+
end
|
34
|
+
1
|
35
|
+
end
|
36
|
+
return nil if not defined? Arguments
|
37
|
+
args = Arguments.names(the_object, method_name, false)
|
38
|
+
# Convert this to a Ruby 1.9.2 format:
|
39
|
+
return args.map do |arg|
|
40
|
+
if arg.size == 2
|
41
|
+
[:opt, arg[0], arg[1]]
|
42
|
+
else
|
43
|
+
name = arg[0].to_s
|
44
|
+
case name[0,1]
|
45
|
+
when '*' then [:rest, name[1..-1]]
|
46
|
+
when '&' then [:block, name[1..-1]]
|
47
|
+
else [:req, name]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue SyntaxError => e
|
52
|
+
# We could just return nil here, to continue to the next strategy,
|
53
|
+
# but we want to inform the user of the suckiness of RubyParser.
|
54
|
+
raise
|
55
|
+
rescue Exception
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Strategy: use Method#parameters (for ruby 1.9 only).
|
60
|
+
#
|
61
|
+
# pros: fast.
|
62
|
+
# cons: doesn't show default values.
|
63
|
+
def _method_arguments__by_methopara(method_name)
|
64
|
+
@@once__methopara ||= begin
|
65
|
+
# For ruby 1.9.0 and 1.9.1, we need to use a gem.
|
66
|
+
begin
|
67
|
+
require 'methopara'
|
68
|
+
rescue LoadError
|
69
|
+
# Not installed.
|
70
|
+
end
|
71
|
+
1
|
72
|
+
end
|
73
|
+
method = the_object.instance_method(method_name)
|
74
|
+
if method.respond_to? :parameters
|
75
|
+
return method.parameters
|
76
|
+
end
|
77
|
+
rescue NotImplementedError
|
78
|
+
# For some methods #parameters raises an exception. We return nil
|
79
|
+
# to move on to the next strategy.
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# Strategy: simulation via Method#arity.
|
84
|
+
#
|
85
|
+
# This is used as a fallback if any of the other strategies fail.
|
86
|
+
#
|
87
|
+
# pros: fast; work without any gems.
|
88
|
+
# cons: doesn't show argument names.
|
89
|
+
def _method_arguments__by_arity(method_name)
|
90
|
+
method = the_object.instance_method(method_name)
|
91
|
+
ary, rest = method.arity, false
|
92
|
+
if ary < 0
|
93
|
+
ary = -ary - 1
|
94
|
+
rest = true
|
95
|
+
end
|
96
|
+
args = [[:req]] * ary
|
97
|
+
args << [:rest] if rest
|
98
|
+
return args
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
data/lib/drx/graphviz.rb
CHANGED
@@ -8,7 +8,8 @@ module Drx
|
|
8
8
|
# - Windows' CMD.EXE too supports "2>&1"
|
9
9
|
# - We're generating GIF, not PNG, because that's a format Tk
|
10
10
|
# supports out of the box.
|
11
|
-
|
11
|
+
# - Each {extension} token is replaced by the filepath with that extension.
|
12
|
+
GRAPHVIZ_COMMAND = 'dot "{dot}" -Tgif -o "{gif}" -Tcmapx -o "{map}" 2>&1'
|
12
13
|
|
13
14
|
@@sizes = {
|
14
15
|
'100%' => "
|
@@ -128,7 +129,7 @@ module Drx
|
|
128
129
|
out
|
129
130
|
end
|
130
131
|
|
131
|
-
def dot_fragment(opts = {}, &block) # :yield:
|
132
|
+
def dot_fragment(opts = {}, ancestors = [], &block) # :yield:
|
132
133
|
out = ''
|
133
134
|
# Note: since 'obj' may be a T_ICLASS, it doesn't respond to many methods,
|
134
135
|
# including is_a?. So when we're querying things we're using Drx calls
|
@@ -147,9 +148,9 @@ module Drx
|
|
147
148
|
return '' if seen
|
148
149
|
|
149
150
|
if class_like?
|
150
|
-
if spr = self.super and display_super?(spr)
|
151
|
-
out << spr.dot_fragment(opts, &block)
|
152
|
-
if insignificant_super_arrow?(opts)
|
151
|
+
if spr = self.super and display_super?(spr, ancestors)
|
152
|
+
out << spr.dot_fragment(opts, ancestors + [self], &block)
|
153
|
+
if insignificant_super_arrow?(opts, ancestors)
|
153
154
|
# We don't want these relatively insignificant lines to clutter the display,
|
154
155
|
# so we paint them lightly and tell DOT they aren't to affect the layout (width=0).
|
155
156
|
out << "#{dot_id} -> #{spr.dot_id} [color=gray85, weight=0];" "\n"
|
@@ -172,6 +173,12 @@ module Drx
|
|
172
173
|
# (To see the effect of this, set the weight unconditionally to '0' and
|
173
174
|
# see the graph for DataMapper.)
|
174
175
|
weight = t_iclass? ? 1 : 0
|
176
|
+
|
177
|
+
# However, here's a special case. DOT seems to have a bug: it sometimes
|
178
|
+
# doesn't draw the arrows going out of Class and Module (to their
|
179
|
+
# singletons). Making their weight 1 makes DOT draw them.
|
180
|
+
weight = 1 if self == _Class or self == _Module
|
181
|
+
|
175
182
|
out << "#{dot_id} -> #{kls.dot_id} [style=dotted, weight=#{weight}];" "\n"
|
176
183
|
out << "{ rank=same; #{dot_id}; #{kls.dot_id}; }" "\n"
|
177
184
|
end
|
@@ -187,11 +194,13 @@ module Drx
|
|
187
194
|
# following method is to break the cycle. Normally we break the cycle at
|
188
195
|
# Module (and its singleton). When the user is examining a module, we
|
189
196
|
# instead break the cycle at Class (and its singleton).
|
190
|
-
def insignificant_super_arrow?(opts)
|
197
|
+
def insignificant_super_arrow?(opts, my_ancestors)
|
191
198
|
if opts[:base].t_module?
|
192
|
-
|
199
|
+
self == _ClassS or
|
200
|
+
(self.super == _Module and (my_ancestors + [self]).include? _Class)
|
193
201
|
else
|
194
|
-
|
202
|
+
self == _ModuleS or
|
203
|
+
(self.super == _Object and (my_ancestors + [self]).include? _Module)
|
195
204
|
end
|
196
205
|
end
|
197
206
|
|
@@ -203,7 +212,17 @@ module Drx
|
|
203
212
|
# Usually this means that the ICLASS has a singleton (see "Singletons
|
204
213
|
# of included modules" in display_super?()). We want to see this
|
205
214
|
# singleton.
|
206
|
-
|
215
|
+
|
216
|
+
# Unfortunately, here is a special treatment for the 'arguments' gem,
|
217
|
+
# which our GUI uses. That gem includes the 'Arguments' module in both
|
218
|
+
# Class and Module (this is redundant!) and having the singleton twice
|
219
|
+
# in our graph may break its nice rectangular structure. So we don't
|
220
|
+
# show its singleton.
|
221
|
+
if defined? ::Arguments
|
222
|
+
return false if ::Arguments == klass.the_object
|
223
|
+
end
|
224
|
+
|
225
|
+
return kls != _Module
|
207
226
|
else
|
208
227
|
# Displaying a singleton's klass is confusing and usually unneeded.
|
209
228
|
return !singleton?
|
@@ -211,16 +230,19 @@ module Drx
|
|
211
230
|
end
|
212
231
|
|
213
232
|
# Whether to display the super.
|
214
|
-
def display_super?(spr)
|
215
|
-
if
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
233
|
+
def display_super?(spr, my_ancestors)
|
234
|
+
if spr == _Module
|
235
|
+
# Many objects have Module as their super (e.g., singletones
|
236
|
+
# of included modules, or modules included in them). To prevent
|
237
|
+
# clutter we print the arrow to Module only if it comes from
|
238
|
+
# Class (or a module included in it).
|
239
|
+
return (my_ancestors + [self]).include? _Class
|
240
|
+
#
|
241
|
+
# A somewhat irrelevant note (I don't have a better place to put it):
|
242
|
+
#
|
243
|
+
# "Singletons of included modules" often exist solely for their
|
244
|
+
# #included method. For example, DataMapper#Resource has
|
245
|
+
# such a singleton.
|
224
246
|
end
|
225
247
|
return true
|
226
248
|
end
|
@@ -237,8 +259,19 @@ module Drx
|
|
237
259
|
end
|
238
260
|
end
|
239
261
|
|
262
|
+
# Shortcuts for some specific objects. An "S" suffix
|
263
|
+
# denotes a singleton.
|
264
|
+
protected
|
265
|
+
def _Module; ObjInfo.new(Module); end
|
266
|
+
def _ModuleS; ObjInfo.new(Module).klass; end
|
267
|
+
def _Object; ObjInfo.new(Object); end
|
268
|
+
def _ObjectS; ObjInfo.new(Object).klass; end
|
269
|
+
def _Class; ObjInfo.new(Class); end
|
270
|
+
def _ClassS; ObjInfo.new(Class).klass; end
|
271
|
+
public
|
272
|
+
|
240
273
|
# Generates a diagram of the inheritance hierarchy. It accepts a hash
|
241
|
-
# pointing to
|
274
|
+
# pointing to filepaths to write the result to. A Tempfiles hash
|
242
275
|
# can be used instead.
|
243
276
|
#
|
244
277
|
# the_object = "some object"
|
@@ -250,7 +283,8 @@ module Drx
|
|
250
283
|
def generate_diagram(files, opts = {}, &block)
|
251
284
|
source = self.dot_source(opts, &block)
|
252
285
|
File.open(files['dot'], 'w') { |f| f.write(source) }
|
253
|
-
|
286
|
+
# Replace {extension} tokens with their corresponding filepaths:
|
287
|
+
command = GRAPHVIZ_COMMAND.gsub(/\{([a-z]+)\}/) { files[$1] }
|
254
288
|
message = Kernel.`(command) # `
|
255
289
|
if $? != 0
|
256
290
|
error = <<-EOS % [command, message]
|
data/lib/drx/objinfo.rb
CHANGED
@@ -19,6 +19,10 @@ module Drx
|
|
19
19
|
Core::get_address(@obj)
|
20
20
|
end
|
21
21
|
|
22
|
+
def ==(other)
|
23
|
+
other.is_a?(ObjInfo) and address == other.address
|
24
|
+
end
|
25
|
+
|
22
26
|
def the_object
|
23
27
|
@obj
|
24
28
|
end
|
@@ -70,8 +74,9 @@ module Drx
|
|
70
74
|
Core::get_iv_tbl(@obj)
|
71
75
|
end
|
72
76
|
|
73
|
-
#
|
74
|
-
|
77
|
+
# Returns the value of an instance variable. Actually, of any sort
|
78
|
+
# of variable that's recorded in the variable-table.
|
79
|
+
def get_ivar(name)
|
75
80
|
if class_like? and name.to_s =~ /^[A-Z]/
|
76
81
|
# If it's a constant, it may be 'autoloaded'. We
|
77
82
|
# trigger the loading by calling const_get().
|
@@ -120,7 +125,7 @@ module Drx
|
|
120
125
|
if t_iclass?
|
121
126
|
'include ' + klass.repr
|
122
127
|
elsif singleton?
|
123
|
-
attached =
|
128
|
+
attached = get_ivar('__attached__') || self
|
124
129
|
attached.inspect + " 'S"
|
125
130
|
else
|
126
131
|
@obj.inspect
|
data/lib/drx/tk/app.rb
CHANGED
@@ -11,8 +11,23 @@ module Drx
|
|
11
11
|
|
12
12
|
class Application
|
13
13
|
|
14
|
+
# Loads ~/.drxrc.
|
15
|
+
#
|
16
|
+
# @see Application#user_customizations
|
17
|
+
def self.load_rc
|
18
|
+
@rc_loaded ||= begin
|
19
|
+
rc = File.join(ENV['HOME'] || Dir.pwd, '.drxrc')
|
20
|
+
load rc if File.exist? rc
|
21
|
+
1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the top-level frame in which to show ourselves.
|
14
26
|
def toplevel
|
15
|
-
@toplevel ||=
|
27
|
+
@toplevel ||= begin
|
28
|
+
# We're showing ourselves inside a TkRoot, unless one already exists.
|
29
|
+
Application.first_window? ? TkRoot.new : TkToplevel.new
|
30
|
+
end
|
16
31
|
end
|
17
32
|
|
18
33
|
def initialize
|
@@ -56,12 +71,16 @@ module Drx
|
|
56
71
|
show 'headings'
|
57
72
|
}
|
58
73
|
@methodsbox = Tk::Tile::Treeview.new(toplevel) {
|
59
|
-
columns 'name location'
|
74
|
+
columns 'name arguments location'
|
60
75
|
heading_configure('name', :text => 'Name')
|
76
|
+
heading_configure('arguments', :text => 'Arguments')
|
61
77
|
heading_configure('location', :text => 'Location')
|
62
78
|
column_configure('name', :stretch => false )
|
79
|
+
column_configure('arguments', :stretch => false )
|
63
80
|
column_configure('location', :stretch => false )
|
64
81
|
show 'headings'
|
82
|
+
# We want the layout manager to allocate space for two columns only:
|
83
|
+
displaycolumns 'name location'
|
65
84
|
}
|
66
85
|
|
67
86
|
@graph_size_menu = Tk::Tile::Combobox.new(toplevel) {
|
@@ -83,6 +102,15 @@ module Drx
|
|
83
102
|
save_graph tip
|
84
103
|
}
|
85
104
|
|
105
|
+
@show_arguments_chk = TkCheckbutton.new(toplevel) {
|
106
|
+
text 'Show arguments'
|
107
|
+
variable TkVariable.new(0)
|
108
|
+
}
|
109
|
+
@use_arguments_gem_chk = TkCheckbutton.new(toplevel) {
|
110
|
+
text "Use the 'arguments' gem (slower)"
|
111
|
+
variable TkVariable.new(0)
|
112
|
+
}
|
113
|
+
|
86
114
|
layout
|
87
115
|
|
88
116
|
@varsbox.bind('<TreeviewSelect>') {
|
@@ -141,30 +169,45 @@ module Drx
|
|
141
169
|
@graph_opts[:style] = @graph_style_menu.get
|
142
170
|
refresh
|
143
171
|
}
|
172
|
+
@show_arguments_chk.variable.trace('w') do |value,|
|
173
|
+
if value == 1
|
174
|
+
@use_arguments_gem_chk.raise
|
175
|
+
@methodsbox.displaycolumns 'name arguments location'
|
176
|
+
display_methods(current_object)
|
177
|
+
else
|
178
|
+
@use_arguments_gem_chk.lower
|
179
|
+
@methodsbox.displaycolumns 'name location'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
@use_arguments_gem_chk.variable.trace('w') do |value,|
|
183
|
+
ObjInfo.use_arguments_gem = (value == 1)
|
184
|
+
display_methods(current_object)
|
185
|
+
end
|
186
|
+
@show_arguments_chk.variable.value = @show_arguments_chk.variable.value # Trigger the trace handler.
|
144
187
|
|
145
188
|
output "Please visit the homepage, http://drx.rubyforge.org/, for usage instructions.\n", 'info'
|
146
189
|
|
147
|
-
|
190
|
+
Application.load_rc
|
191
|
+
system_customizations
|
148
192
|
user_customizations
|
149
193
|
end
|
150
194
|
|
151
|
-
|
152
|
-
|
195
|
+
# Users may redefine this method in their ~/.drxrc
|
196
|
+
# to fine-tune the app.
|
197
|
+
def user_customizations
|
198
|
+
end
|
199
|
+
|
200
|
+
# The following are default customizations. They are subjective in
|
201
|
+
# nature and users may knock them out in their ~/.drxrc.
|
202
|
+
def system_customizations
|
203
|
+
if Application.first_window?
|
153
204
|
# Try to make the Unixy GUI less ugly.
|
154
205
|
if Tk.windowingsystem == 'x11' and Ttk.themes.include? 'clam'
|
155
206
|
Ttk.set_theme 'clam'
|
156
207
|
end
|
157
|
-
rc = File.join(ENV['HOME'] || Dir.pwd, '.drxrc')
|
158
|
-
load rc if File.exist? rc
|
159
|
-
1
|
160
208
|
end
|
161
209
|
end
|
162
210
|
|
163
|
-
# Users may redefine this method in their ~/.drxrc
|
164
|
-
# to fine-tune the app.
|
165
|
-
def user_customizations
|
166
|
-
end
|
167
|
-
|
168
211
|
def vbox(*args); VBox.new(toplevel, args); end
|
169
212
|
def hbox(*args); HBox.new(toplevel, args); end
|
170
213
|
def separator; TkLabel.new(toplevel, :text => ' '); end
|
@@ -204,7 +247,8 @@ module Drx
|
|
204
247
|
), :weight => 50
|
205
248
|
panes.add vbox(
|
206
249
|
TkLabel.new(toplevel, :text => 'Methods (m_tbl):', :anchor => 'w'),
|
207
|
-
[Scrolled.new(toplevel, @methodsbox), { :expand => true, :fill => 'both' } ]
|
250
|
+
[Scrolled.new(toplevel, @methodsbox), { :expand => true, :fill => 'both' } ],
|
251
|
+
hbox(@show_arguments_chk, separator, @use_arguments_gem_chk)
|
208
252
|
), :weight => 10
|
209
253
|
|
210
254
|
main_frame.add(panes)
|
@@ -221,11 +265,10 @@ module Drx
|
|
221
265
|
def open_up_editor(filename, lineno)
|
222
266
|
command = sprintf(ENV['DRX_EDITOR_COMMAND'] || EDITOR_COMMAND, lineno, filename)
|
223
267
|
output "Executing: #{command}...\n", 'info'
|
224
|
-
|
268
|
+
Thread.new do
|
225
269
|
if !Kernel.system(command)
|
226
270
|
output "Could not execute the command '#{command}'\n", 'error'
|
227
271
|
end
|
228
|
-
exit!
|
229
272
|
end
|
230
273
|
end
|
231
274
|
|
@@ -250,7 +293,7 @@ module Drx
|
|
250
293
|
end
|
251
294
|
|
252
295
|
def selected_var
|
253
|
-
ObjInfo.new(current_object).
|
296
|
+
ObjInfo.new(current_object).get_ivar(@varsbox.get_selection)
|
254
297
|
end
|
255
298
|
|
256
299
|
def eval_code(code)
|
@@ -289,14 +332,20 @@ module Drx
|
|
289
332
|
# Get rid of gazillions of Tk classes:
|
290
333
|
vars = vars.reject { |v| v =~ /Tk|Ttk/ }
|
291
334
|
vars.each do |name|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
335
|
+
begin
|
336
|
+
value = if allowed_names.any? { |p| p === name }
|
337
|
+
info.get_ivar(name).inspect
|
338
|
+
else
|
339
|
+
# We don't want to inspect ruby's internal variables (because
|
340
|
+
# they may not be Ruby values at all).
|
341
|
+
''
|
342
|
+
end
|
343
|
+
@varsbox.insert('', 'end', :text => name, :values => [ name, value ] )
|
344
|
+
rescue NameError
|
345
|
+
# Referencing an autoloaded constant (in ObjInfo#get_ivar()) may
|
346
|
+
# raise a NameError. This happens when the source file autoloaded
|
347
|
+
# defines the moudle/class in the top-level. Example is Camping::Mab.
|
348
|
+
end
|
300
349
|
end
|
301
350
|
end
|
302
351
|
end
|
@@ -321,6 +370,27 @@ module Drx
|
|
321
370
|
end
|
322
371
|
end
|
323
372
|
|
373
|
+
# Returns a string describing a method's arguments, for use in GUIs.
|
374
|
+
def pretty_arguments(info, name)
|
375
|
+
args = info.method_arguments(name)
|
376
|
+
return args.map do |arg|
|
377
|
+
case arg[0]
|
378
|
+
when :req; (arg[1] || 'arg').to_s
|
379
|
+
when :opt; (arg[1] || 'arg').to_s + '=' + (arg[2] || '?')
|
380
|
+
when :rest; '*' + (arg[1] || 'args').to_s
|
381
|
+
when :block; '&' + (arg[1] || 'arg').to_s
|
382
|
+
end
|
383
|
+
end.join ', '
|
384
|
+
rescue NameError
|
385
|
+
return '---'
|
386
|
+
rescue SyntaxError => e
|
387
|
+
'SYNTAX ERROR: ' + e.to_s
|
388
|
+
end
|
389
|
+
|
390
|
+
def show_arguments?
|
391
|
+
@show_arguments_chk.variable == 1
|
392
|
+
end
|
393
|
+
|
324
394
|
# Fills the methods listbox with a list of the object's methods.
|
325
395
|
def display_methods(obj)
|
326
396
|
@methodsbox.clear
|
@@ -328,7 +398,11 @@ module Drx
|
|
328
398
|
if obj and info.class_like?
|
329
399
|
methods = info.m_tbl.keys.map do |v| v.to_s end.sort
|
330
400
|
methods.each do |name|
|
331
|
-
@methodsbox.insert('', 'end', :text => name, :values => [
|
401
|
+
@methodsbox.insert('', 'end', :text => name, :values => [
|
402
|
+
name,
|
403
|
+
show_arguments? ? pretty_arguments(info, name) : '-',
|
404
|
+
pretty_location(info, name)
|
405
|
+
])
|
332
406
|
end
|
333
407
|
end
|
334
408
|
end
|
@@ -357,11 +431,19 @@ module Drx
|
|
357
431
|
end
|
358
432
|
end
|
359
433
|
|
434
|
+
# Updates the window title (usually shown in the taskbar).
|
435
|
+
def update_title(obj)
|
436
|
+
toplevel.title = 'Drx: ' + begin
|
437
|
+
obj.is_a?(Module) ? obj.name : obj.class.name
|
438
|
+
end.to_s # In case of singletons, #name returns nil, so to_s enforces a string.
|
439
|
+
end
|
440
|
+
|
360
441
|
# Makes `obj` the primary object seen (the one which is the tip of the diagram).
|
361
442
|
def navigate_to(obj)
|
362
443
|
@current_object = obj
|
363
444
|
@navigation_history << obj
|
364
445
|
display_graph(obj)
|
446
|
+
update_title(obj)
|
365
447
|
# Trigger the update of the variables and methods tables by selecting this object
|
366
448
|
# in the imagemap.
|
367
449
|
@im.active_url = @im.urls.first
|
@@ -391,9 +473,17 @@ module Drx
|
|
391
473
|
navigate_to(current_object)
|
392
474
|
end
|
393
475
|
|
476
|
+
class << self
|
477
|
+
attr_accessor :in_loop
|
478
|
+
def first_window?; !in_loop; end
|
479
|
+
end
|
480
|
+
|
394
481
|
def run
|
395
|
-
|
482
|
+
return if Application.in_loop
|
483
|
+
# @todo Any other way to detect that Tk's mainloop is already running?
|
484
|
+
Application.in_loop = true
|
396
485
|
Tk.mainloop
|
486
|
+
Application.in_loop = false
|
397
487
|
Tk.restart # So that Tk doesn't complain 'can't invoke "frame" command: application has been destroyed' next time.
|
398
488
|
end
|
399
489
|
end
|
data/lib/drx.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: drx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
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-
|
12
|
+
date: 2010-04-16 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- lib/drx/tk/imagemap.rb
|
28
28
|
- lib/drx/tk/app.rb
|
29
29
|
- lib/drx/objinfo.rb
|
30
|
+
- lib/drx/arguments.rb
|
30
31
|
- lib/drx/tempfiles.rb
|
31
32
|
- lib/drx.rb
|
32
33
|
- ext/extconf.rb
|
@@ -34,7 +35,8 @@ files:
|
|
34
35
|
- examples/drx_datamapper.rb
|
35
36
|
- examples/drx_date.rb
|
36
37
|
- examples/drx_guitar.rb
|
37
|
-
- examples/
|
38
|
+
- examples/drx_activerecord.rb
|
39
|
+
- examples/drx_sequel.rb
|
38
40
|
has_rdoc: true
|
39
41
|
homepage: http://drx.rubyforge.org/
|
40
42
|
licenses: []
|
@@ -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
|