adaptation 0.0.2 → 0.0.3

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