adaptation 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ begin
2
+ require 'simplecc'
3
+ rescue LoadError
4
+ # to satisfy rdoc
5
+ class Continuation #:nodoc:
6
+ end
7
+ def Continuation.create(*args, &block) # :nodoc:
8
+ cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
9
+ result ||= args
10
+ return *[cc, *result]
11
+ end
12
+ end
13
+
14
+ class Binding; end # for RDoc
15
+ # This method returns the binding of the method that called your
16
+ # method. It will raise an Exception when you're not inside a method.
17
+ #
18
+ # It's used like this:
19
+ # def inc_counter(amount = 1)
20
+ # Binding.of_caller do |binding|
21
+ # # Create a lambda that will increase the variable 'counter'
22
+ # # in the caller of this method when called.
23
+ # inc = eval("lambda { |arg| counter += arg }", binding)
24
+ # # We can refer to amount from inside this block safely.
25
+ # inc.call(amount)
26
+ # end
27
+ # # No other statements can go here. Put them inside the block.
28
+ # end
29
+ # counter = 0
30
+ # 2.times { inc_counter }
31
+ # counter # => 2
32
+ #
33
+ # Binding.of_caller must be the last statement in the method.
34
+ # This means that you will have to put everything you want to
35
+ # do after the call to Binding.of_caller into the block of it.
36
+ # This should be no problem however, because Ruby has closures.
37
+ # If you don't do this an Exception will be raised. Because of
38
+ # the way that Binding.of_caller is implemented it has to be
39
+ # done this way.
40
+ def Binding.of_caller(&block)
41
+ old_critical = Thread.critical
42
+ Thread.critical = true
43
+ count = 0
44
+ cc, result, error, extra_data = Continuation.create(nil, nil)
45
+ error.call if error
46
+
47
+ tracer = lambda do |*args|
48
+ type, context, extra_data = args[0], args[4], args
49
+ if type == "return"
50
+ count += 1
51
+ # First this method and then calling one will return --
52
+ # the trace event of the second event gets the context
53
+ # of the method which called the method that called this
54
+ # method.
55
+ if count == 2
56
+ # It would be nice if we could restore the trace_func
57
+ # that was set before we swapped in our own one, but
58
+ # this is impossible without overloading set_trace_func
59
+ # in current Ruby.
60
+ set_trace_func(nil)
61
+ cc.call(eval("binding", context), nil, extra_data)
62
+ end
63
+ elsif type == "line" then
64
+ nil
65
+ elsif type == "c-return" and extra_data[3] == :set_trace_func then
66
+ nil
67
+ else
68
+ set_trace_func(nil)
69
+ error_msg = "Binding.of_caller used in non-method context or " +
70
+ "trailing statements of method using it aren't in the block."
71
+ cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
72
+ end
73
+ end
74
+
75
+ unless result
76
+ set_trace_func(tracer)
77
+ return nil
78
+ else
79
+ Thread.critical = old_critical
80
+ case block.arity
81
+ when 1 then yield(result)
82
+ else yield(result, extra_data)
83
+ end
84
+ end
85
+ end
data/lib/breakpoint.rb ADDED
@@ -0,0 +1,554 @@
1
+ # The Breakpoint library provides the convenience of
2
+ # being able to inspect and modify state, diagnose
3
+ # bugs all via IRB by simply setting breakpoints in
4
+ # your applications by the call of a method.
5
+ #
6
+ # This library was written and is supported by me,
7
+ # Florian Gross. I can be reached at flgr@ccan.de
8
+ # and enjoy getting feedback about my libraries.
9
+ #
10
+ # The whole library (including breakpoint_client.rb
11
+ # and binding_of_caller.rb) is licensed under the
12
+ # same license that Ruby uses. (Which is currently
13
+ # either the GNU General Public License or a custom
14
+ # one that allows for commercial usage.) If you for
15
+ # some good reason need to use this under another
16
+ # license please contact me.
17
+
18
+ require 'irb'
19
+ if RUBY_VERSION == '1.8.5'
20
+ puts "HOLA!"
21
+ def Binding.of_caller(&block)
22
+ raise "Breakpoints are not currently working with Ruby 1.8.5"
23
+ end
24
+ else
25
+ require 'binding_of_caller'
26
+ end
27
+ require 'drb'
28
+ require 'drb/acl'
29
+
30
+ module Breakpoint
31
+ id = %q$Id: breakpoint.rb 92 2005-02-04 22:35:53Z flgr $
32
+ Version = id.split(" ")[2].to_i
33
+
34
+ extend self
35
+
36
+ # This will pop up an interactive ruby session at a
37
+ # pre-defined break point in a Ruby application. In
38
+ # this session you can examine the environment of
39
+ # the break point.
40
+ #
41
+ # You can get a list of variables in the context using
42
+ # local_variables via +local_variables+. You can then
43
+ # examine their values by typing their names.
44
+ #
45
+ # You can have a look at the call stack via +caller+.
46
+ #
47
+ # The source code around the location where the breakpoint
48
+ # was executed can be examined via +source_lines+. Its
49
+ # argument specifies how much lines of context to display.
50
+ # The default amount of context is 5 lines. Note that
51
+ # the call to +source_lines+ can raise an exception when
52
+ # it isn't able to read in the source code.
53
+ #
54
+ # breakpoints can also return a value. They will execute
55
+ # a supplied block for getting a default return value.
56
+ # A custom value can be returned from the session by doing
57
+ # +throw(:debug_return, value)+.
58
+ #
59
+ # You can also give names to break points which will be
60
+ # used in the message that is displayed upon execution
61
+ # of them.
62
+ #
63
+ # Here's a sample of how breakpoints should be placed:
64
+ #
65
+ # class Person
66
+ # def initialize(name, age)
67
+ # @name, @age = name, age
68
+ # breakpoint("Person#initialize")
69
+ # end
70
+ #
71
+ # attr_reader :age
72
+ # def name
73
+ # breakpoint("Person#name") { @name }
74
+ # end
75
+ # end
76
+ #
77
+ # person = Person.new("Random Person", 23)
78
+ # puts "Name: #{person.name}"
79
+ #
80
+ # And here is a sample debug session:
81
+ #
82
+ # Executing break point "Person#initialize" at file.rb:4 in `initialize'
83
+ # irb(#<Person:0x292fbe8>):001:0> local_variables
84
+ # => ["name", "age", "_", "__"]
85
+ # irb(#<Person:0x292fbe8>):002:0> [name, age]
86
+ # => ["Random Person", 23]
87
+ # irb(#<Person:0x292fbe8>):003:0> [@name, @age]
88
+ # => ["Random Person", 23]
89
+ # irb(#<Person:0x292fbe8>):004:0> self
90
+ # => #<Person:0x292fbe8 @age=23, @name="Random Person">
91
+ # irb(#<Person:0x292fbe8>):005:0> @age += 1; self
92
+ # => #<Person:0x292fbe8 @age=24, @name="Random Person">
93
+ # irb(#<Person:0x292fbe8>):006:0> exit
94
+ # Executing break point "Person#name" at file.rb:9 in `name'
95
+ # irb(#<Person:0x292fbe8>):001:0> throw(:debug_return, "Overriden name")
96
+ # Name: Overriden name
97
+ #
98
+ # Breakpoint sessions will automatically have a few
99
+ # convenience methods available. See Breakpoint::CommandBundle
100
+ # for a list of them.
101
+ #
102
+ # Breakpoints can also be used remotely over sockets.
103
+ # This is implemented by running part of the IRB session
104
+ # in the application and part of it in a special client.
105
+ # You have to call Breakpoint.activate_drb to enable
106
+ # support for remote breakpoints and then run
107
+ # breakpoint_client.rb which is distributed with this
108
+ # library. See the documentation of Breakpoint.activate_drb
109
+ # for details.
110
+ def breakpoint(id = nil, context = nil, &block)
111
+ callstack = caller
112
+ callstack.slice!(0, 3) if callstack.first["breakpoint"]
113
+ file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
114
+
115
+ message = "Executing break point " + (id ? "#{id.inspect} " : "") +
116
+ "at #{file}:#{line}" + (method ? " in `#{method}'" : "")
117
+
118
+ if context then
119
+ return handle_breakpoint(context, message, file, line, &block)
120
+ end
121
+
122
+ Binding.of_caller do |binding_context|
123
+ handle_breakpoint(binding_context, message, file, line, &block)
124
+ end
125
+ end
126
+
127
+ module CommandBundle
128
+ # Proxy to a Breakpoint client. Lets you directly execute code
129
+ # in the context of the client.
130
+ class Client
131
+ def initialize(eval_handler) # :nodoc:
132
+ eval_handler.untaint
133
+ @eval_handler = eval_handler
134
+ end
135
+
136
+ instance_methods.each do |method|
137
+ next if method[/^__.+__$/]
138
+ undef_method method
139
+ end
140
+
141
+ # Executes the specified code at the client.
142
+ def eval(code)
143
+ @eval_handler.call(code)
144
+ end
145
+
146
+ # Will execute the specified statement at the client.
147
+ def method_missing(method, *args, &block)
148
+ if args.empty? and not block
149
+ result = eval "#{method}"
150
+ else
151
+ # This is a bit ugly. The alternative would be using an
152
+ # eval context instead of an eval handler for executing
153
+ # the code at the client. The problem with that approach
154
+ # is that we would have to handle special expressions
155
+ # like "self", "nil" or constants ourself which is hard.
156
+ remote = eval %{
157
+ result = lambda { |block, *args| #{method}(*args, &block) }
158
+ def result.call_with_block(*args, &block)
159
+ call(block, *args)
160
+ end
161
+ result
162
+ }
163
+ remote.call_with_block(*args, &block)
164
+ end
165
+
166
+ return result
167
+ end
168
+ end
169
+
170
+ # Returns the source code surrounding the location where the
171
+ # breakpoint was issued.
172
+ def source_lines(context = 5, return_line_numbers = false)
173
+ lines = File.readlines(@__bp_file).map { |line| line.chomp }
174
+
175
+ break_line = @__bp_line
176
+ start_line = [break_line - context, 1].max
177
+ end_line = break_line + context
178
+
179
+ result = lines[(start_line - 1) .. (end_line - 1)]
180
+
181
+ if return_line_numbers then
182
+ return [start_line, break_line, result]
183
+ else
184
+ return result
185
+ end
186
+ end
187
+
188
+ # Prints the source code surrounding the location where the
189
+ # breakpoint was issued.
190
+ def show_source_list(context = 5)
191
+ start_line, break_line, result = source_lines(context, true)
192
+ offset = [(break_line + context).to_s.length, 4].max
193
+ result.each_with_index do |line, i|
194
+ mark = (start_line + i == break_line ? '->' : ' ')
195
+ client.puts("%0#{offset}d%s#{line}" % [start_line + i, mark])
196
+ end
197
+ Pathname.new(@__bp_file).cleanpath.to_s
198
+ end
199
+
200
+ # Prints the call stack.
201
+ def show_call_stack(depth = 10)
202
+ base = Pathname.new(RAILS_ROOT).cleanpath.to_s
203
+ caller[1..depth].each do |line|
204
+ line.sub!(/^[^:]*/) do |path|
205
+ Pathname.new(path).cleanpath.to_s
206
+ end
207
+ client.puts(line.index(base) == 0 ? line[(base.length + 1)..-1] : line)
208
+ end
209
+ "#{Pathname.new(@__bp_file).cleanpath.to_s}:#{@__bp_line}"
210
+ end
211
+
212
+ # Lets an object that will forward method calls to the breakpoint
213
+ # client. This is useful for outputting longer things at the client
214
+ # and so on. You can for example do these things:
215
+ #
216
+ # client.puts "Hello" # outputs "Hello" at client console
217
+ # # outputs "Hello" into the file temp.txt at the client
218
+ # client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
219
+ def client()
220
+ if Breakpoint.use_drb? then
221
+ sleep(0.5) until Breakpoint.drb_service.eval_handler
222
+ Client.new(Breakpoint.drb_service.eval_handler)
223
+ else
224
+ Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
225
+ end
226
+ end
227
+ end
228
+
229
+ def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
230
+ catch(:debug_return) do |value|
231
+ eval(%{
232
+ @__bp_file = #{file.inspect}
233
+ @__bp_line = #{line}
234
+ extend Breakpoint::CommandBundle
235
+ extend DRbUndumped if self
236
+ }, context) rescue nil
237
+
238
+ if not use_drb? then
239
+ puts message
240
+ IRB.start(nil, IRB::WorkSpace.new(context))
241
+ else
242
+ @drb_service.add_breakpoint(context, message)
243
+ end
244
+
245
+ block.call if block
246
+ end
247
+ end
248
+
249
+ # These exceptions will be raised on failed asserts
250
+ # if Breakpoint.asserts_cause_exceptions is set to
251
+ # true.
252
+ class FailedAssertError < RuntimeError
253
+ end
254
+
255
+ # This asserts that the block evaluates to true.
256
+ # If it doesn't evaluate to true a breakpoint will
257
+ # automatically be created at that execution point.
258
+ #
259
+ # You can disable assert checking in production
260
+ # code by setting Breakpoint.optimize_asserts to
261
+ # true. (It will still be enabled when Ruby is run
262
+ # via the -d argument.)
263
+ #
264
+ # Example:
265
+ # person_name = "Foobar"
266
+ # assert { not person_name.nil? }
267
+ #
268
+ # Note: If you want to use this method from an
269
+ # unit test, you will have to call it by its full
270
+ # name, Breakpoint.assert.
271
+ def assert(context = nil, &condition)
272
+ return if Breakpoint.optimize_asserts and not $DEBUG
273
+ return if yield
274
+
275
+ callstack = caller
276
+ callstack.slice!(0, 3) if callstack.first["assert"]
277
+ file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
278
+
279
+ message = "Assert failed at #{file}:#{line}#{" in `#{method}'" if method}."
280
+
281
+ if Breakpoint.asserts_cause_exceptions and not $DEBUG then
282
+ raise(Breakpoint::FailedAssertError, message)
283
+ end
284
+
285
+ message += " Executing implicit breakpoint."
286
+
287
+ if context then
288
+ return handle_breakpoint(context, message, file, line)
289
+ end
290
+
291
+ Binding.of_caller do |context|
292
+ handle_breakpoint(context, message, file, line)
293
+ end
294
+ end
295
+
296
+ # Whether asserts should be ignored if not in debug mode.
297
+ # Debug mode can be enabled by running ruby with the -d
298
+ # switch or by setting $DEBUG to true.
299
+ attr_accessor :optimize_asserts
300
+ self.optimize_asserts = false
301
+
302
+ # Whether an Exception should be raised on failed asserts
303
+ # in non-$DEBUG code or not. By default this is disabled.
304
+ attr_accessor :asserts_cause_exceptions
305
+ self.asserts_cause_exceptions = false
306
+ @use_drb = false
307
+
308
+ attr_reader :drb_service # :nodoc:
309
+
310
+ class DRbService # :nodoc:
311
+ include DRbUndumped
312
+
313
+ def initialize
314
+ @handler = @eval_handler = @collision_handler = nil
315
+
316
+ IRB.instance_eval { @CONF[:RC] = true }
317
+ IRB.run_config
318
+ end
319
+
320
+ def collision
321
+ sleep(0.5) until @collision_handler
322
+
323
+ @collision_handler.untaint
324
+
325
+ @collision_handler.call
326
+ end
327
+
328
+ def ping() end
329
+
330
+ def add_breakpoint(context, message)
331
+ workspace = IRB::WorkSpace.new(context)
332
+ workspace.extend(DRbUndumped)
333
+
334
+ sleep(0.5) until @handler
335
+
336
+ @handler.untaint
337
+ @handler.call(workspace, message)
338
+ end
339
+
340
+ attr_accessor :handler, :eval_handler, :collision_handler
341
+ end
342
+
343
+ # Will run Breakpoint in DRb mode. This will spawn a server
344
+ # that can be attached to via the breakpoint-client command
345
+ # whenever a breakpoint is executed. This is useful when you
346
+ # are debugging CGI applications or other applications where
347
+ # you can't access debug sessions via the standard input and
348
+ # output of your application.
349
+ #
350
+ # You can specify an URI where the DRb server will run at.
351
+ # This way you can specify the port the server runs on. The
352
+ # default URI is druby://localhost:42531.
353
+ #
354
+ # Please note that breakpoints will be skipped silently in
355
+ # case the DRb server can not spawned. (This can happen if
356
+ # the port is already used by another instance of your
357
+ # application on CGI or another application.)
358
+ #
359
+ # Also note that by default this will only allow access
360
+ # from localhost. You can however specify a list of
361
+ # allowed hosts or nil (to allow access from everywhere).
362
+ # But that will still not protect you from somebody
363
+ # reading the data as it goes through the net.
364
+ #
365
+ # A good approach for getting security and remote access
366
+ # is setting up an SSH tunnel between the DRb service
367
+ # and the client. This is usually done like this:
368
+ #
369
+ # $ ssh -L20000:127.0.0.1:20000 -R10000:127.0.0.1:10000 example.com
370
+ # (This will connect port 20000 at the client side to port
371
+ # 20000 at the server side, and port 10000 at the server
372
+ # side to port 10000 at the client side.)
373
+ #
374
+ # After that do this on the server side: (the code being debugged)
375
+ # Breakpoint.activate_drb("druby://127.0.0.1:20000", "localhost")
376
+ #
377
+ # And at the client side:
378
+ # ruby breakpoint_client.rb -c druby://127.0.0.1:10000 -s druby://127.0.0.1:20000
379
+ #
380
+ # Running through such a SSH proxy will also let you use
381
+ # breakpoint.rb in case you are behind a firewall.
382
+ #
383
+ # Detailed information about running DRb through firewalls is
384
+ # available at http://www.rubygarden.org/ruby?DrbTutorial
385
+ def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
386
+ ignore_collisions = false)
387
+
388
+ return false if @use_drb
389
+
390
+ uri ||= 'druby://localhost:42531'
391
+
392
+ if allowed_hosts then
393
+ acl = ["deny", "all"]
394
+
395
+ Array(allowed_hosts).each do |host|
396
+ acl += ["allow", host]
397
+ end
398
+
399
+ DRb.install_acl(ACL.new(acl))
400
+ end
401
+
402
+ @use_drb = true
403
+ @drb_service = DRbService.new
404
+ did_collision = false
405
+ begin
406
+ @service = DRb.start_service(uri, @drb_service)
407
+ rescue Errno::EADDRINUSE
408
+ if ignore_collisions then
409
+ nil
410
+ else
411
+ # The port is already occupied by another
412
+ # Breakpoint service. We will try to tell
413
+ # the old service that we want its port.
414
+ # It will then forward that request to the
415
+ # user and retry.
416
+ unless did_collision then
417
+ DRbObject.new(nil, uri).collision
418
+ did_collision = true
419
+ end
420
+ sleep(10)
421
+ retry
422
+ end
423
+ end
424
+
425
+ return true
426
+ end
427
+
428
+ # Deactivates a running Breakpoint service.
429
+ def deactivate_drb
430
+ @service.stop_service unless @service.nil?
431
+ @service = nil
432
+ @use_drb = false
433
+ @drb_service = nil
434
+ end
435
+
436
+ # Returns true when Breakpoints are used over DRb.
437
+ # Breakpoint.activate_drb causes this to be true.
438
+ def use_drb?
439
+ @use_drb == true
440
+ end
441
+ end
442
+
443
+ module IRB # :nodoc:
444
+ class << self; remove_method :start; end
445
+ def self.start(ap_path = nil, main_context = nil, workspace = nil)
446
+ $0 = File::basename(ap_path, ".rb") if ap_path
447
+
448
+ # suppress some warnings about redefined constants
449
+ old_verbose, $VERBOSE = $VERBOSE, nil
450
+ IRB.setup(ap_path)
451
+ $VERBOSE = old_verbose
452
+
453
+ if @CONF[:SCRIPT] then
454
+ irb = Irb.new(main_context, @CONF[:SCRIPT])
455
+ else
456
+ irb = Irb.new(main_context)
457
+ end
458
+
459
+ if workspace then
460
+ irb.context.workspace = workspace
461
+ end
462
+
463
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
464
+ @CONF[:MAIN_CONTEXT] = irb.context
465
+
466
+ old_sigint = trap("SIGINT") do
467
+ begin
468
+ irb.signal_handle
469
+ rescue RubyLex::TerminateLineInput
470
+ # ignored
471
+ end
472
+ end
473
+
474
+ catch(:IRB_EXIT) do
475
+ irb.eval_input
476
+ end
477
+ ensure
478
+ trap("SIGINT", old_sigint)
479
+ end
480
+
481
+ class << self
482
+ alias :old_CurrentContext :CurrentContext
483
+ remove_method :CurrentContext
484
+ end
485
+ def IRB.CurrentContext
486
+ if old_CurrentContext.nil? and Breakpoint.use_drb? then
487
+ result = Object.new
488
+ def result.last_value; end
489
+ return result
490
+ else
491
+ old_CurrentContext
492
+ end
493
+ end
494
+ def IRB.parse_opts() end
495
+
496
+ class Context #:nodoc:
497
+ alias :old_evaluate :evaluate
498
+ def evaluate(line, line_no)
499
+ if line.chomp == "exit" then
500
+ exit
501
+ else
502
+ old_evaluate(line, line_no)
503
+ end
504
+ end
505
+ end
506
+
507
+ class WorkSpace #:nodoc:
508
+ alias :old_evaluate :evaluate
509
+
510
+ def evaluate(*args)
511
+ if Breakpoint.use_drb? then
512
+ result = old_evaluate(*args)
513
+ if args[0] != :no_proxy and
514
+ not [true, false, nil].include?(result)
515
+ then
516
+ result.extend(DRbUndumped) rescue nil
517
+ end
518
+ return result
519
+ else
520
+ old_evaluate(*args)
521
+ end
522
+ end
523
+ end
524
+
525
+ module InputCompletor #:nodoc:
526
+ def self.eval(code, context, *more)
527
+ # Big hack, this assumes that InputCompletor
528
+ # will only call eval() when it wants code
529
+ # to be executed in the IRB context.
530
+ IRB.conf[:MAIN_CONTEXT].workspace.evaluate(:no_proxy, code, *more)
531
+ end
532
+ end
533
+ end
534
+
535
+ module DRb #:nodoc:
536
+ class DRbObject #:nodoc:
537
+ undef :inspect if method_defined?(:inspect)
538
+ undef :clone if method_defined?(:clone)
539
+ end
540
+ end
541
+
542
+ # See Breakpoint.breakpoint
543
+ def breakpoint(id = nil, &block)
544
+ Binding.of_caller do |context|
545
+ Breakpoint.breakpoint(id, context, &block)
546
+ end
547
+ end
548
+
549
+ # See Breakpoint.assert
550
+ def assert(&block)
551
+ Binding.of_caller do |context|
552
+ Breakpoint.assert(context, &block)
553
+ end
554
+ end