activesupport 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- data/CHANGELOG +70 -0
- data/lib/active_support.rb +31 -0
- data/lib/active_support/binding_of_caller.rb +84 -0
- data/lib/active_support/breakpoint.rb +523 -0
- data/lib/active_support/class_attribute_accessors.rb +57 -0
- data/lib/active_support/class_inheritable_attributes.rb +117 -0
- data/lib/active_support/clean_logger.rb +10 -0
- data/lib/active_support/core_ext.rb +1 -0
- data/lib/active_support/core_ext/date.rb +6 -0
- data/lib/active_support/core_ext/date/conversions.rb +31 -0
- data/lib/active_support/core_ext/hash.rb +7 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +38 -0
- data/lib/active_support/core_ext/hash/keys.rb +53 -0
- data/lib/active_support/core_ext/numeric.rb +7 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +33 -0
- data/lib/active_support/core_ext/numeric/time.rb +59 -0
- data/lib/active_support/core_ext/object_and_class.rb +24 -0
- data/lib/active_support/core_ext/string.rb +5 -0
- data/lib/active_support/core_ext/string/inflections.rb +49 -0
- data/lib/active_support/core_ext/time.rb +7 -0
- data/lib/active_support/core_ext/time/calculations.rb +133 -0
- data/lib/active_support/core_ext/time/conversions.rb +34 -0
- data/lib/active_support/dependencies.rb +212 -0
- data/lib/active_support/inflector.rb +93 -0
- data/lib/active_support/misc.rb +8 -0
- data/lib/active_support/module_attribute_accessors.rb +57 -0
- data/lib/active_support/values/time_zone.rb +180 -0
- metadata +71 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
*1.0.0* (24th February, 2005)
|
2
|
+
|
3
|
+
* Added TimeZone as the first of a number of value objects that among others Active Record can use rich value objects using composed_of #688 [Jamis Buck]
|
4
|
+
|
5
|
+
* Added Date::Conversions for getting dates in different convenient string representations and other objects
|
6
|
+
|
7
|
+
* Added Time::Conversions for getting times in different convenient string representations and other objects
|
8
|
+
|
9
|
+
* Added Time::Calculations to ask for things like Time.now.tomorrow, Time.now.yesterday, Time.now.months_ago(4) #580 [DP|Flurin]. Examples:
|
10
|
+
|
11
|
+
"Later today" => now.in(3.hours),
|
12
|
+
"Tomorrow morning" => now.tomorrow.change(:hour => 9),
|
13
|
+
"Tomorrow afternoon" => now.tomorrow.change(:hour => 14),
|
14
|
+
"In a couple of days" => now.tomorrow.tomorrow.change(:hour => 9),
|
15
|
+
"Next monday" => now.next_week.change(:hour => 9),
|
16
|
+
"In a month" => now.next_month.change(:hour => 9),
|
17
|
+
"In 6 months" => now.months_since(6).change(:hour => 9),
|
18
|
+
"In a year" => now.in(1.year).change(:hour => 9)
|
19
|
+
|
20
|
+
* Upgraded to breakpoint 92 which fixes:
|
21
|
+
|
22
|
+
* overload IRB.parse_opts(), fixes #443
|
23
|
+
=> breakpoints in tests work even when running them via rake
|
24
|
+
* untaint handlers, might fix an issue discussed on the Rails ML
|
25
|
+
* added verbose mode to breakpoint_client
|
26
|
+
* less noise caused by breakpoint_client by default
|
27
|
+
* ignored TerminateLineInput exception in signal handler
|
28
|
+
=> quiet exit on Ctrl-C
|
29
|
+
|
30
|
+
* Fixed Inflector for words like "news" and "series" that are the same in plural and singular #603 [echion], #615 [marcenuc]
|
31
|
+
|
32
|
+
* Added Hash#stringify_keys and Hash#stringify_keys!
|
33
|
+
|
34
|
+
* Added IndifferentAccess as a way to wrap a hash by a symbol-based store that also can be accessed by string keys
|
35
|
+
|
36
|
+
* Added Inflector.constantize to turn "Admin::User" into a reference for the constant Admin::User
|
37
|
+
|
38
|
+
* Added that Inflector.camelize and Inflector.underscore can deal with modules like turning "Admin::User" into "admin/user" and back
|
39
|
+
|
40
|
+
* Added Inflector.humanize to turn attribute names like employee_salary into "Employee salary". Used by automated error reporting in AR.
|
41
|
+
|
42
|
+
* Added availability of class inheritable attributes to the masses #477 [bitsweat]
|
43
|
+
|
44
|
+
class Foo
|
45
|
+
class_inheritable_reader :read_me
|
46
|
+
class_inheritable_writer :write_me
|
47
|
+
class_inheritable_accessor :read_and_write_me
|
48
|
+
class_inheritable_array :read_and_concat_me
|
49
|
+
class_inheritable_hash :read_and_update_me
|
50
|
+
end
|
51
|
+
|
52
|
+
# Bar gets a clone of (not a reference to) Foo's attributes.
|
53
|
+
class Bar < Foo
|
54
|
+
end
|
55
|
+
|
56
|
+
Bar.read_and_write_me == Foo.read_and_write_me
|
57
|
+
Bar.read_and_write_me = 'bar'
|
58
|
+
Bar.read_and_write_me != Foo.read_and_write_me
|
59
|
+
|
60
|
+
* Added Inflections as an extension on String, so Inflector.pluralize(Inflector.classify(name)) becomes name.classify.pluralize #476 [bitsweat]
|
61
|
+
|
62
|
+
* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina]
|
63
|
+
|
64
|
+
* Fixed that Dependencies.reload can't load the same file twice #420 [Kent Sibilev]
|
65
|
+
|
66
|
+
* Added Fixnum#ago/until, Fixnum#since/from_now #450 [bitsweat]
|
67
|
+
|
68
|
+
* Added that Inflector now accepts Symbols and Classes by calling .to_s on the word supplied
|
69
|
+
|
70
|
+
* Added time unit extensions to Fixnum that'll return the period in seconds, like 2.days + 4.hours.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005 David Heinemeier Hansson
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
$:.unshift(File.dirname(__FILE__))
|
25
|
+
|
26
|
+
require 'active_support/core_ext'
|
27
|
+
require 'active_support/clean_logger'
|
28
|
+
require 'active_support/misc'
|
29
|
+
require 'active_support/dependencies'
|
30
|
+
|
31
|
+
require 'active_support/values/time_zone'
|
@@ -0,0 +1,84 @@
|
|
1
|
+
begin
|
2
|
+
require 'simplecc'
|
3
|
+
rescue LoadError
|
4
|
+
class Continuation # :nodoc: # for RDoc
|
5
|
+
end
|
6
|
+
def Continuation.create(*args, &block) # :nodoc:
|
7
|
+
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
8
|
+
result ||= args
|
9
|
+
return *[cc, *result]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Binding; end # for RDoc
|
14
|
+
# This method returns the binding of the method that called your
|
15
|
+
# method. It will raise an Exception when you're not inside a method.
|
16
|
+
#
|
17
|
+
# It's used like this:
|
18
|
+
# def inc_counter(amount = 1)
|
19
|
+
# Binding.of_caller do |binding|
|
20
|
+
# # Create a lambda that will increase the variable 'counter'
|
21
|
+
# # in the caller of this method when called.
|
22
|
+
# inc = eval("lambda { |arg| counter += arg }", binding)
|
23
|
+
# # We can refer to amount from inside this block safely.
|
24
|
+
# inc.call(amount)
|
25
|
+
# end
|
26
|
+
# # No other statements can go here. Put them inside the block.
|
27
|
+
# end
|
28
|
+
# counter = 0
|
29
|
+
# 2.times { inc_counter }
|
30
|
+
# counter # => 2
|
31
|
+
#
|
32
|
+
# Binding.of_caller must be the last statement in the method.
|
33
|
+
# This means that you will have to put everything you want to
|
34
|
+
# do after the call to Binding.of_caller into the block of it.
|
35
|
+
# This should be no problem however, because Ruby has closures.
|
36
|
+
# If you don't do this an Exception will be raised. Because of
|
37
|
+
# the way that Binding.of_caller is implemented it has to be
|
38
|
+
# done this way.
|
39
|
+
def Binding.of_caller(&block)
|
40
|
+
old_critical = Thread.critical
|
41
|
+
Thread.critical = true
|
42
|
+
count = 0
|
43
|
+
cc, result, error, extra_data = Continuation.create(nil, nil)
|
44
|
+
error.call if error
|
45
|
+
|
46
|
+
tracer = lambda do |*args|
|
47
|
+
type, context, extra_data = args[0], args[4], args
|
48
|
+
if type == "return"
|
49
|
+
count += 1
|
50
|
+
# First this method and then calling one will return --
|
51
|
+
# the trace event of the second event gets the context
|
52
|
+
# of the method which called the method that called this
|
53
|
+
# method.
|
54
|
+
if count == 2
|
55
|
+
# It would be nice if we could restore the trace_func
|
56
|
+
# that was set before we swapped in our own one, but
|
57
|
+
# this is impossible without overloading set_trace_func
|
58
|
+
# in current Ruby.
|
59
|
+
set_trace_func(nil)
|
60
|
+
cc.call(eval("binding", context), nil, extra_data)
|
61
|
+
end
|
62
|
+
elsif type == "line" then
|
63
|
+
nil
|
64
|
+
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
65
|
+
nil
|
66
|
+
else
|
67
|
+
set_trace_func(nil)
|
68
|
+
error_msg = "Binding.of_caller used in non-method context or " +
|
69
|
+
"trailing statements of method using it aren't in the block."
|
70
|
+
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
unless result
|
75
|
+
set_trace_func(tracer)
|
76
|
+
return nil
|
77
|
+
else
|
78
|
+
Thread.critical = old_critical
|
79
|
+
case block.arity
|
80
|
+
when 1 then yield(result)
|
81
|
+
else yield(result, extra_data)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,523 @@
|
|
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
|
+
require File.dirname(__FILE__) + '/binding_of_caller'
|
20
|
+
require 'drb'
|
21
|
+
require 'drb/acl'
|
22
|
+
|
23
|
+
module Breakpoint
|
24
|
+
id = %q$Id: breakpoint.rb 92 2005-02-04 22:35:53Z flgr $
|
25
|
+
Version = id.split(" ")[2].to_i
|
26
|
+
|
27
|
+
extend self
|
28
|
+
|
29
|
+
# This will pop up an interactive ruby session at a
|
30
|
+
# pre-defined break point in a Ruby application. In
|
31
|
+
# this session you can examine the environment of
|
32
|
+
# the break point.
|
33
|
+
#
|
34
|
+
# You can get a list of variables in the context using
|
35
|
+
# local_variables via +local_variables+. You can then
|
36
|
+
# examine their values by typing their names.
|
37
|
+
#
|
38
|
+
# You can have a look at the call stack via +caller+.
|
39
|
+
#
|
40
|
+
# The source code around the location where the breakpoint
|
41
|
+
# was executed can be examined via +source_lines+. Its
|
42
|
+
# argument specifies how much lines of context to display.
|
43
|
+
# The default amount of context is 5 lines. Note that
|
44
|
+
# the call to +source_lines+ can raise an exception when
|
45
|
+
# it isn't able to read in the source code.
|
46
|
+
#
|
47
|
+
# breakpoints can also return a value. They will execute
|
48
|
+
# a supplied block for getting a default return value.
|
49
|
+
# A custom value can be returned from the session by doing
|
50
|
+
# +throw(:debug_return, value)+.
|
51
|
+
#
|
52
|
+
# You can also give names to break points which will be
|
53
|
+
# used in the message that is displayed upon execution
|
54
|
+
# of them.
|
55
|
+
#
|
56
|
+
# Here's a sample of how breakpoints should be placed:
|
57
|
+
#
|
58
|
+
# class Person
|
59
|
+
# def initialize(name, age)
|
60
|
+
# @name, @age = name, age
|
61
|
+
# breakpoint("Person#initialize")
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# attr_reader :age
|
65
|
+
# def name
|
66
|
+
# breakpoint("Person#name") { @name }
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# person = Person.new("Random Person", 23)
|
71
|
+
# puts "Name: #{person.name}"
|
72
|
+
#
|
73
|
+
# And here is a sample debug session:
|
74
|
+
#
|
75
|
+
# Executing break point "Person#initialize" at file.rb:4 in `initialize'
|
76
|
+
# irb(#<Person:0x292fbe8>):001:0> local_variables
|
77
|
+
# => ["name", "age", "_", "__"]
|
78
|
+
# irb(#<Person:0x292fbe8>):002:0> [name, age]
|
79
|
+
# => ["Random Person", 23]
|
80
|
+
# irb(#<Person:0x292fbe8>):003:0> [@name, @age]
|
81
|
+
# => ["Random Person", 23]
|
82
|
+
# irb(#<Person:0x292fbe8>):004:0> self
|
83
|
+
# => #<Person:0x292fbe8 @age=23, @name="Random Person">
|
84
|
+
# irb(#<Person:0x292fbe8>):005:0> @age += 1; self
|
85
|
+
# => #<Person:0x292fbe8 @age=24, @name="Random Person">
|
86
|
+
# irb(#<Person:0x292fbe8>):006:0> exit
|
87
|
+
# Executing break point "Person#name" at file.rb:9 in `name'
|
88
|
+
# irb(#<Person:0x292fbe8>):001:0> throw(:debug_return, "Overriden name")
|
89
|
+
# Name: Overriden name
|
90
|
+
#
|
91
|
+
# Breakpoint sessions will automatically have a few
|
92
|
+
# convenience methods available. See Breakpoint::CommandBundle
|
93
|
+
# for a list of them.
|
94
|
+
#
|
95
|
+
# Breakpoints can also be used remotely over sockets.
|
96
|
+
# This is implemented by running part of the IRB session
|
97
|
+
# in the application and part of it in a special client.
|
98
|
+
# You have to call Breakpoint.activate_drb to enable
|
99
|
+
# support for remote breakpoints and then run
|
100
|
+
# breakpoint_client.rb which is distributed with this
|
101
|
+
# library. See the documentation of Breakpoint.activate_drb
|
102
|
+
# for details.
|
103
|
+
def breakpoint(id = nil, context = nil, &block)
|
104
|
+
callstack = caller
|
105
|
+
callstack.slice!(0, 3) if callstack.first["breakpoint"]
|
106
|
+
file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
107
|
+
|
108
|
+
message = "Executing break point " + (id ? "#{id.inspect} " : "") +
|
109
|
+
"at #{file}:#{line}" + (method ? " in `#{method}'" : "")
|
110
|
+
|
111
|
+
if context then
|
112
|
+
return handle_breakpoint(context, message, file, line, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
Binding.of_caller do |binding_context|
|
116
|
+
handle_breakpoint(binding_context, message, file, line, &block)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
module CommandBundle #:nodoc:
|
121
|
+
# Proxy to a Breakpoint client. Lets you directly execute code
|
122
|
+
# in the context of the client.
|
123
|
+
class Client #:nodoc:
|
124
|
+
def initialize(eval_handler) # :nodoc:
|
125
|
+
eval_handler.untaint
|
126
|
+
@eval_handler = eval_handler
|
127
|
+
end
|
128
|
+
|
129
|
+
instance_methods.each do |method|
|
130
|
+
next if method[/^__.+__$/]
|
131
|
+
undef_method method
|
132
|
+
end
|
133
|
+
|
134
|
+
# Executes the specified code at the client.
|
135
|
+
def eval(code)
|
136
|
+
@eval_handler.call(code)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Will execute the specified statement at the client.
|
140
|
+
def method_missing(method, *args, &block)
|
141
|
+
if args.empty? and not block
|
142
|
+
result = eval "#{method}"
|
143
|
+
else
|
144
|
+
# This is a bit ugly. The alternative would be using an
|
145
|
+
# eval context instead of an eval handler for executing
|
146
|
+
# the code at the client. The problem with that approach
|
147
|
+
# is that we would have to handle special expressions
|
148
|
+
# like "self", "nil" or constants ourself which is hard.
|
149
|
+
remote = eval %{
|
150
|
+
result = lambda { |block, *args| #{method}(*args, &block) }
|
151
|
+
def result.call_with_block(*args, &block)
|
152
|
+
call(block, *args)
|
153
|
+
end
|
154
|
+
result
|
155
|
+
}
|
156
|
+
remote.call_with_block(*args, &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
return result
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Returns the source code surrounding the location where the
|
164
|
+
# breakpoint was issued.
|
165
|
+
def source_lines(context = 5, return_line_numbers = false)
|
166
|
+
lines = File.readlines(@__bp_file).map { |line| line.chomp }
|
167
|
+
|
168
|
+
break_line = @__bp_line
|
169
|
+
start_line = [break_line - context, 1].max
|
170
|
+
end_line = break_line + context
|
171
|
+
|
172
|
+
result = lines[(start_line - 1) .. (end_line - 1)]
|
173
|
+
|
174
|
+
if return_line_numbers then
|
175
|
+
return [start_line, break_line, result]
|
176
|
+
else
|
177
|
+
return result
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Lets an object that will forward method calls to the breakpoint
|
182
|
+
# client. This is useful for outputting longer things at the client
|
183
|
+
# and so on. You can for example do these things:
|
184
|
+
#
|
185
|
+
# client.puts "Hello" # outputs "Hello" at client console
|
186
|
+
# # outputs "Hello" into the file temp.txt at the client
|
187
|
+
# client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
|
188
|
+
def client()
|
189
|
+
if Breakpoint.use_drb? then
|
190
|
+
sleep(0.5) until Breakpoint.drb_service.eval_handler
|
191
|
+
Client.new(Breakpoint.drb_service.eval_handler)
|
192
|
+
else
|
193
|
+
Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
|
199
|
+
catch(:debug_return) do |value|
|
200
|
+
eval(%{
|
201
|
+
@__bp_file = #{file.inspect}
|
202
|
+
@__bp_line = #{line}
|
203
|
+
extend Breakpoint::CommandBundle
|
204
|
+
extend DRbUndumped if self
|
205
|
+
}, context) rescue nil
|
206
|
+
|
207
|
+
if not use_drb? then
|
208
|
+
puts message
|
209
|
+
IRB.start(nil, IRB::WorkSpace.new(context))
|
210
|
+
else
|
211
|
+
@drb_service.add_breakpoint(context, message)
|
212
|
+
end
|
213
|
+
|
214
|
+
block.call if block
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# These exceptions will be raised on failed asserts
|
219
|
+
# if Breakpoint.asserts_cause_exceptions is set to
|
220
|
+
# true.
|
221
|
+
class FailedAssertError < RuntimeError #:nodoc:
|
222
|
+
end
|
223
|
+
|
224
|
+
# This asserts that the block evaluates to true.
|
225
|
+
# If it doesn't evaluate to true a breakpoint will
|
226
|
+
# automatically be created at that execution point.
|
227
|
+
#
|
228
|
+
# You can disable assert checking in production
|
229
|
+
# code by setting Breakpoint.optimize_asserts to
|
230
|
+
# true. (It will still be enabled when Ruby is run
|
231
|
+
# via the -d argument.)
|
232
|
+
#
|
233
|
+
# Example:
|
234
|
+
# person_name = "Foobar"
|
235
|
+
# assert { not person_name.nil? }
|
236
|
+
#
|
237
|
+
# Note: If you want to use this method from an
|
238
|
+
# unit test, you will have to call it by its full
|
239
|
+
# name, Breakpoint.assert.
|
240
|
+
def assert(context = nil, &condition)
|
241
|
+
return if Breakpoint.optimize_asserts and not $DEBUG
|
242
|
+
return if yield
|
243
|
+
|
244
|
+
callstack = caller
|
245
|
+
callstack.slice!(0, 3) if callstack.first["assert"]
|
246
|
+
file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
247
|
+
|
248
|
+
message = "Assert failed at #{file}:#{line}#{" in `#{method}'" if method}."
|
249
|
+
|
250
|
+
if Breakpoint.asserts_cause_exceptions and not $DEBUG then
|
251
|
+
raise(Breakpoint::FailedAssertError, message)
|
252
|
+
end
|
253
|
+
|
254
|
+
message += " Executing implicit breakpoint."
|
255
|
+
|
256
|
+
if context then
|
257
|
+
return handle_breakpoint(context, message, file, line)
|
258
|
+
end
|
259
|
+
|
260
|
+
Binding.of_caller do |context|
|
261
|
+
handle_breakpoint(context, message, file, line)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Whether asserts should be ignored if not in debug mode.
|
266
|
+
# Debug mode can be enabled by running ruby with the -d
|
267
|
+
# switch or by setting $DEBUG to true.
|
268
|
+
attr_accessor :optimize_asserts
|
269
|
+
self.optimize_asserts = false
|
270
|
+
|
271
|
+
# Whether an Exception should be raised on failed asserts
|
272
|
+
# in non-$DEBUG code or not. By default this is disabled.
|
273
|
+
attr_accessor :asserts_cause_exceptions
|
274
|
+
self.asserts_cause_exceptions = false
|
275
|
+
@use_drb = false
|
276
|
+
|
277
|
+
attr_reader :drb_service # :nodoc:
|
278
|
+
|
279
|
+
class DRbService # :nodoc:
|
280
|
+
include DRbUndumped
|
281
|
+
|
282
|
+
def initialize
|
283
|
+
@handler = @eval_handler = @collision_handler = nil
|
284
|
+
|
285
|
+
IRB.instance_eval { @CONF[:RC] = true }
|
286
|
+
IRB.run_config
|
287
|
+
end
|
288
|
+
|
289
|
+
def collision
|
290
|
+
sleep(0.5) until @collision_handler
|
291
|
+
|
292
|
+
@collision_handler.untaint
|
293
|
+
|
294
|
+
@collision_handler.call
|
295
|
+
end
|
296
|
+
|
297
|
+
def ping() end
|
298
|
+
|
299
|
+
def add_breakpoint(context, message)
|
300
|
+
workspace = IRB::WorkSpace.new(context)
|
301
|
+
workspace.extend(DRbUndumped)
|
302
|
+
|
303
|
+
sleep(0.5) until @handler
|
304
|
+
|
305
|
+
@handler.untaint
|
306
|
+
@handler.call(workspace, message)
|
307
|
+
end
|
308
|
+
|
309
|
+
attr_accessor :handler, :eval_handler, :collision_handler
|
310
|
+
end
|
311
|
+
|
312
|
+
# Will run Breakpoint in DRb mode. This will spawn a server
|
313
|
+
# that can be attached to via the breakpoint-client command
|
314
|
+
# whenever a breakpoint is executed. This is useful when you
|
315
|
+
# are debugging CGI applications or other applications where
|
316
|
+
# you can't access debug sessions via the standard input and
|
317
|
+
# output of your application.
|
318
|
+
#
|
319
|
+
# You can specify an URI where the DRb server will run at.
|
320
|
+
# This way you can specify the port the server runs on. The
|
321
|
+
# default URI is druby://localhost:42531.
|
322
|
+
#
|
323
|
+
# Please note that breakpoints will be skipped silently in
|
324
|
+
# case the DRb server can not spawned. (This can happen if
|
325
|
+
# the port is already used by another instance of your
|
326
|
+
# application on CGI or another application.)
|
327
|
+
#
|
328
|
+
# Also note that by default this will only allow access
|
329
|
+
# from localhost. You can however specify a list of
|
330
|
+
# allowed hosts or nil (to allow access from everywhere).
|
331
|
+
# But that will still not protect you from somebody
|
332
|
+
# reading the data as it goes through the net.
|
333
|
+
#
|
334
|
+
# A good approach for getting security and remote access
|
335
|
+
# is setting up an SSH tunnel between the DRb service
|
336
|
+
# and the client. This is usually done like this:
|
337
|
+
#
|
338
|
+
# $ ssh -L20000:127.0.0.1:20000 -R10000:127.0.0.1:10000 example.com
|
339
|
+
# (This will connect port 20000 at the client side to port
|
340
|
+
# 20000 at the server side, and port 10000 at the server
|
341
|
+
# side to port 10000 at the client side.)
|
342
|
+
#
|
343
|
+
# After that do this on the server side: (the code being debugged)
|
344
|
+
# Breakpoint.activate_drb("druby://127.0.0.1:20000", "localhost")
|
345
|
+
#
|
346
|
+
# And at the client side:
|
347
|
+
# ruby breakpoint_client.rb -c druby://127.0.0.1:10000 -s druby://127.0.0.1:20000
|
348
|
+
#
|
349
|
+
# Running through such a SSH proxy will also let you use
|
350
|
+
# breakpoint.rb in case you are behind a firewall.
|
351
|
+
#
|
352
|
+
# Detailed information about running DRb through firewalls is
|
353
|
+
# available at http://www.rubygarden.org/ruby?DrbTutorial
|
354
|
+
def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
|
355
|
+
ignore_collisions = false)
|
356
|
+
|
357
|
+
return false if @use_drb
|
358
|
+
|
359
|
+
uri ||= 'druby://localhost:42531'
|
360
|
+
|
361
|
+
if allowed_hosts then
|
362
|
+
acl = ["deny", "all"]
|
363
|
+
|
364
|
+
Array(allowed_hosts).each do |host|
|
365
|
+
acl += ["allow", host]
|
366
|
+
end
|
367
|
+
|
368
|
+
DRb.install_acl(ACL.new(acl))
|
369
|
+
end
|
370
|
+
|
371
|
+
@use_drb = true
|
372
|
+
@drb_service = DRbService.new
|
373
|
+
did_collision = false
|
374
|
+
begin
|
375
|
+
@service = DRb.start_service(uri, @drb_service)
|
376
|
+
rescue Errno::EADDRINUSE
|
377
|
+
if ignore_collisions then
|
378
|
+
nil
|
379
|
+
else
|
380
|
+
# The port is already occupied by another
|
381
|
+
# Breakpoint service. We will try to tell
|
382
|
+
# the old service that we want its port.
|
383
|
+
# It will then forward that request to the
|
384
|
+
# user and retry.
|
385
|
+
unless did_collision then
|
386
|
+
DRbObject.new(nil, uri).collision
|
387
|
+
did_collision = true
|
388
|
+
end
|
389
|
+
sleep(10)
|
390
|
+
retry
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
return true
|
395
|
+
end
|
396
|
+
|
397
|
+
# Deactivates a running Breakpoint service.
|
398
|
+
def deactivate_drb
|
399
|
+
@service.stop_service unless @service.nil?
|
400
|
+
@service = nil
|
401
|
+
@use_drb = false
|
402
|
+
@drb_service = nil
|
403
|
+
end
|
404
|
+
|
405
|
+
# Returns true when Breakpoints are used over DRb.
|
406
|
+
# Breakpoint.activate_drb causes this to be true.
|
407
|
+
def use_drb?
|
408
|
+
@use_drb == true
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
module IRB #:nodoc:
|
413
|
+
class << self; remove_method :start; end
|
414
|
+
def self.start(ap_path = nil, main_context = nil, workspace = nil)
|
415
|
+
$0 = File::basename(ap_path, ".rb") if ap_path
|
416
|
+
|
417
|
+
# suppress some warnings about redefined constants
|
418
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
419
|
+
IRB.setup(ap_path)
|
420
|
+
$VERBOSE = old_verbose
|
421
|
+
|
422
|
+
if @CONF[:SCRIPT] then
|
423
|
+
irb = Irb.new(main_context, @CONF[:SCRIPT])
|
424
|
+
else
|
425
|
+
irb = Irb.new(main_context)
|
426
|
+
end
|
427
|
+
|
428
|
+
if workspace then
|
429
|
+
irb.context.workspace = workspace
|
430
|
+
end
|
431
|
+
|
432
|
+
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
433
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
434
|
+
|
435
|
+
old_sigint = trap("SIGINT") do
|
436
|
+
begin
|
437
|
+
irb.signal_handle
|
438
|
+
rescue RubyLex::TerminateLineInput
|
439
|
+
# ignored
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
catch(:IRB_EXIT) do
|
444
|
+
irb.eval_input
|
445
|
+
end
|
446
|
+
ensure
|
447
|
+
trap("SIGINT", old_sigint)
|
448
|
+
end
|
449
|
+
|
450
|
+
class << self
|
451
|
+
alias :old_CurrentContext :CurrentContext
|
452
|
+
remove_method :CurrentContext
|
453
|
+
end
|
454
|
+
def IRB.CurrentContext
|
455
|
+
if old_CurrentContext.nil? and Breakpoint.use_drb? then
|
456
|
+
result = Object.new
|
457
|
+
def result.last_value; end
|
458
|
+
return result
|
459
|
+
else
|
460
|
+
old_CurrentContext
|
461
|
+
end
|
462
|
+
end
|
463
|
+
def IRB.parse_opts() end
|
464
|
+
|
465
|
+
class Context #:nodoc:
|
466
|
+
alias :old_evaluate :evaluate
|
467
|
+
def evaluate(line, line_no)
|
468
|
+
if line.chomp == "exit" then
|
469
|
+
exit
|
470
|
+
else
|
471
|
+
old_evaluate(line, line_no)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
class WorkSpace #:nodoc:
|
477
|
+
alias :old_evaluate :evaluate
|
478
|
+
|
479
|
+
def evaluate(*args)
|
480
|
+
if Breakpoint.use_drb? then
|
481
|
+
result = old_evaluate(*args)
|
482
|
+
if args[0] != :no_proxy and
|
483
|
+
not [true, false, nil].include?(result)
|
484
|
+
then
|
485
|
+
result.extend(DRbUndumped) rescue nil
|
486
|
+
end
|
487
|
+
return result
|
488
|
+
else
|
489
|
+
old_evaluate(*args)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
module InputCompletor #:nodoc:
|
495
|
+
def self.eval(code, context, *more)
|
496
|
+
# Big hack, this assumes that InputCompletor
|
497
|
+
# will only call eval() when it wants code
|
498
|
+
# to be executed in the IRB context.
|
499
|
+
IRB.conf[:MAIN_CONTEXT].workspace.evaluate(:no_proxy, code, *more)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
module DRb # :nodoc:
|
505
|
+
class DRbObject #:nodoc:
|
506
|
+
undef :inspect if method_defined?(:inspect)
|
507
|
+
undef :clone if method_defined?(:clone)
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
# See Breakpoint.breakpoint
|
512
|
+
def breakpoint(id = nil, &block)
|
513
|
+
Binding.of_caller do |context|
|
514
|
+
Breakpoint.breakpoint(id, context, &block)
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
# See Breakpoint.assert
|
519
|
+
def assert(&block)
|
520
|
+
Binding.of_caller do |context|
|
521
|
+
Breakpoint.assert(context, &block)
|
522
|
+
end
|
523
|
+
end
|