adaptation 0.1.0 → 0.1.1
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.
- data/CHANGELOG +4 -0
- data/lib/adaptation/adaptor.rb +4 -0
- data/lib/adaptation/base.rb +11 -26
- data/lib/adaptation/message.rb +53 -14
- data/lib/adaptation/test/test_help.rb +7 -15
- metadata +2 -4
- data/lib/breakpoint.rb +0 -554
- data/lib/breakpoint_client.rb +0 -196
data/CHANGELOG
CHANGED
@@ -27,3 +27,7 @@
|
|
27
27
|
placed in ApplicationAdaptor (app/adaptors/application.rb)
|
28
28
|
- if a message is not implemented in the adaptor, ApplicationAdaptor process method will be called
|
29
29
|
with that message as a parameter (wich will be an instance of Adaptation::Message)
|
30
|
+
* 0.1.1
|
31
|
+
- added method 'maps_xml' to Adaptation::Message (and 'mapped_xml')
|
32
|
+
- removed 'check' method from Adaptation::Message (using valid? instead)
|
33
|
+
- removed breakpoint.rb library
|
data/lib/adaptation/adaptor.rb
CHANGED
data/lib/adaptation/base.rb
CHANGED
@@ -62,28 +62,17 @@ module Adaptation
|
|
62
62
|
message_type = xml_message[1..(xml_message.index(/(>| |\/)/) - 1)]
|
63
63
|
adaptor = message = nil
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
begin
|
78
|
-
adaptor_class = get_class_object("#{message_type.capitalize}Adaptor")
|
79
|
-
adaptor = adaptor_class.new
|
80
|
-
rescue
|
81
|
-
# adaptor class not implemented in this adaptor
|
82
|
-
adaptor = ApplicationAdaptor.new
|
83
|
-
end
|
84
|
-
|
85
|
-
unless message.is_a?(String) # TODO: remove when feature explained in line 70 implemented
|
86
|
-
unless message.check
|
65
|
+
message_class = Adaptation::Message.get_class_object(message_type.capitalize)
|
66
|
+
message = message_class.nil? ? xml_message : message_class.to_object(xml_message)
|
67
|
+
# TODO: the xml is returned as a String if a class to map it is not found;
|
68
|
+
# in future versions Adaptation may build a valid Adaptation::Message even
|
69
|
+
# without implementation for this type of message
|
70
|
+
|
71
|
+
adaptor_class = Adaptation::Adaptor.get_class_object("#{message_type.capitalize}Adaptor")
|
72
|
+
adaptor = adaptor_class.nil? ? ApplicationAdaptor.new : adaptor_class.new
|
73
|
+
|
74
|
+
unless message.is_a?(String) # TODO: remove when feature explained in line 67 implemented
|
75
|
+
unless message.valid?
|
87
76
|
@@logger.info "WARNING:Message doesn't validate!"
|
88
77
|
return
|
89
78
|
end
|
@@ -92,10 +81,6 @@ module Adaptation
|
|
92
81
|
adaptor.process message
|
93
82
|
end
|
94
83
|
|
95
|
-
def get_class_object(searched_class)
|
96
|
-
Object.const_get searched_class
|
97
|
-
end
|
98
|
-
|
99
84
|
end
|
100
85
|
|
101
86
|
end
|
data/lib/adaptation/message.rb
CHANGED
@@ -153,12 +153,13 @@ module Adaptation
|
|
153
153
|
@objects = [] if @objects.nil?
|
154
154
|
unless @objects.include?(symbols[1])
|
155
155
|
@objects << symbols[1]
|
156
|
-
|
157
|
-
|
158
|
-
|
156
|
+
|
157
|
+
klass = get_class_object(symbols[1])
|
158
|
+
if klass.nil?
|
159
159
|
require "#{ADAPTOR_ROOT}/app/messages/_#{symbols[1].to_s}.rb"
|
160
|
-
klass = get_class_object(symbols[1]
|
160
|
+
klass = get_class_object(symbols[1])
|
161
161
|
end
|
162
|
+
|
162
163
|
xml_object symbols[1], klass
|
163
164
|
validates_associated symbols[1]
|
164
165
|
end
|
@@ -271,10 +272,55 @@ module Adaptation
|
|
271
272
|
end
|
272
273
|
end
|
273
274
|
|
274
|
-
|
275
|
-
|
275
|
+
# Defines the xml element that this class is mapping. This is useful to avoid class
|
276
|
+
# name collisions:
|
277
|
+
#
|
278
|
+
# Example:
|
279
|
+
#
|
280
|
+
# We already have a database table called 'things' and we
|
281
|
+
# interoperate with it with an ActiveRecord subclass called
|
282
|
+
# 'Thing':
|
283
|
+
#
|
284
|
+
# class Thing < ActiveRecord::Base
|
285
|
+
# ...
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# But in the same Adaptation application we want to parse the
|
289
|
+
# following xml:
|
290
|
+
#
|
291
|
+
# <thing>...</thing>
|
292
|
+
#
|
293
|
+
# Defining another class Thing would produce a class name
|
294
|
+
# collision, but we can do:
|
295
|
+
#
|
296
|
+
# class XmlThing < Adaptation::Message
|
297
|
+
# maps_xml :thing
|
298
|
+
# ...
|
299
|
+
# end
|
300
|
+
#
|
301
|
+
# and store it in a file called app/messages/thing.rb
|
302
|
+
#
|
303
|
+
def self.maps_xml element
|
304
|
+
@mapped_xml = element
|
305
|
+
xml_name element.to_s
|
276
306
|
end
|
277
|
-
|
307
|
+
|
308
|
+
# Returns the xml element this class is mapping
|
309
|
+
def self.mapped_xml
|
310
|
+
@mapped_xml || self.to_s.downcase.gsub("::","_").to_sym
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.get_class_object(mapped_xml) #:nodoc:
|
314
|
+
# TODO: reimplement this as read in ruby-talk (using 'inherited' method)
|
315
|
+
mapped_xml = mapped_xml.downcase.to_sym if mapped_xml.is_a?(String)
|
316
|
+
klass = nil
|
317
|
+
ObjectSpace.each_object(Class) do |c|
|
318
|
+
next unless c.ancestors.include?(Adaptation::Message) and (c != self) and (c != Adaptation::Message)
|
319
|
+
(klass = c and break) if c.mapped_xml == mapped_xml rescue next
|
320
|
+
end
|
321
|
+
klass
|
322
|
+
end
|
323
|
+
|
278
324
|
# This is the constructor. Instead of <em>Adaptation::Message#new</em>, an <em>Adaptation::Message</em>
|
279
325
|
# instance is created like this:
|
280
326
|
#
|
@@ -284,13 +330,6 @@ module Adaptation
|
|
284
330
|
parse xml_message
|
285
331
|
end
|
286
332
|
|
287
|
-
# Checks if an Adaptation::Message object passes all validations.
|
288
|
-
#
|
289
|
-
# It is an alias for <em>Adaptation::Message#valid?</em>
|
290
|
-
def check
|
291
|
-
valid?
|
292
|
-
end
|
293
|
-
|
294
333
|
end
|
295
334
|
|
296
335
|
end
|
@@ -3,11 +3,6 @@ $environment = "test"
|
|
3
3
|
|
4
4
|
Adaptation::Initializer.run
|
5
5
|
|
6
|
-
# for adaptations that require models from a rails application, sometimes
|
7
|
-
# rails needs to load the environment.rb from the rails app. When this happens
|
8
|
-
# the following variable must be set.
|
9
|
-
ENV['RAILS_ENV'] = "test"
|
10
|
-
|
11
6
|
if File.exists?("config/database.yml")
|
12
7
|
begin
|
13
8
|
require 'active_record/fixtures'
|
@@ -71,7 +66,7 @@ class Test::Unit::TestCase
|
|
71
66
|
message_symbol.to_s
|
72
67
|
message_object.clear_errors
|
73
68
|
assert_block error do
|
74
|
-
message_object.
|
69
|
+
message_object.valid?
|
75
70
|
end
|
76
71
|
end
|
77
72
|
|
@@ -84,7 +79,7 @@ class Test::Unit::TestCase
|
|
84
79
|
"? message shouldn't validate",
|
85
80
|
message_symbol.to_s
|
86
81
|
assert_block error do
|
87
|
-
!message_object.
|
82
|
+
!message_object.valid?
|
88
83
|
end
|
89
84
|
end
|
90
85
|
|
@@ -100,7 +95,7 @@ class Test::Unit::TestCase
|
|
100
95
|
if message_object.is_a?(String)
|
101
96
|
# build Message object with xml_data
|
102
97
|
message_type = xml_message[1..(xml_message.index(/(>| )/) - 1)]
|
103
|
-
message_class = get_class_object(message_type.capitalize)
|
98
|
+
message_class = Adaptation::Message.get_class_object(message_type.capitalize)
|
104
99
|
message_object = message_class.to_object(xml_message)
|
105
100
|
end
|
106
101
|
|
@@ -199,6 +194,7 @@ class Test::Unit::TestCase
|
|
199
194
|
|
200
195
|
end
|
201
196
|
|
197
|
+
# Asserts a file exists
|
202
198
|
def assert_file_present file
|
203
199
|
error = build_message error,
|
204
200
|
"? not found",
|
@@ -208,6 +204,7 @@ class Test::Unit::TestCase
|
|
208
204
|
end
|
209
205
|
end
|
210
206
|
|
207
|
+
# Asserts a file doesn't exist
|
211
208
|
def assert_file_not_present file
|
212
209
|
error = build_message error,
|
213
210
|
"? shouldn't exist",
|
@@ -262,16 +259,11 @@ class Test::Unit::TestCase
|
|
262
259
|
def load_message_fixture fixture_symbol #:nodoc:
|
263
260
|
data = get_message_fixture(fixture_symbol.to_s)
|
264
261
|
class_name = data[1..(data.index(/(>| )/) - 1)].capitalize
|
265
|
-
message_class = get_class_object(class_name)
|
266
|
-
message_object = message_class.to_object(data)
|
262
|
+
message_class = Adaptation::Message.get_class_object(class_name)
|
263
|
+
message_object = message_class.nil? ? data : message_class.to_object(data)
|
267
264
|
[data, message_object]
|
268
265
|
end
|
269
266
|
|
270
|
-
# TODO: this method is repeated in different parts of the code... module?
|
271
|
-
def get_class_object(searched_class) #:nodoc:
|
272
|
-
Object.const_get searched_class
|
273
|
-
end
|
274
|
-
|
275
267
|
def compare_xml_elements element1, element2 #:nodoc:
|
276
268
|
if element1.has_attributes?
|
277
269
|
if !element2.has_attributes?
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adaptation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavi Vila Morell
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-08 00:00:00 +01:00
|
13
13
|
default_executable: adaptation
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -50,7 +50,6 @@ files:
|
|
50
50
|
- bin/generate
|
51
51
|
- bin/console
|
52
52
|
- bin/mom
|
53
|
-
- lib/breakpoint.rb
|
54
53
|
- lib/adaptation
|
55
54
|
- lib/adaptation/base.rb
|
56
55
|
- lib/adaptation/console
|
@@ -118,7 +117,6 @@ files:
|
|
118
117
|
- lib/commands/subscribe.rb
|
119
118
|
- lib/commands/destroy.rb
|
120
119
|
- lib/commands/generate.rb
|
121
|
-
- lib/breakpoint_client.rb
|
122
120
|
- helpers/application.rb
|
123
121
|
- helpers/test.rb
|
124
122
|
- helpers/publish.rb
|
data/lib/breakpoint.rb
DELETED
@@ -1,554 +0,0 @@
|
|
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
|
data/lib/breakpoint_client.rb
DELETED
@@ -1,196 +0,0 @@
|
|
1
|
-
require 'breakpoint'
|
2
|
-
require 'optparse'
|
3
|
-
require 'timeout'
|
4
|
-
|
5
|
-
Options = {
|
6
|
-
:ClientURI => nil,
|
7
|
-
:ServerURI => "druby://localhost:42531",
|
8
|
-
:RetryDelay => 2,
|
9
|
-
:Permanent => true,
|
10
|
-
:Verbose => false
|
11
|
-
}
|
12
|
-
|
13
|
-
ARGV.options do |opts|
|
14
|
-
script_name = File.basename($0)
|
15
|
-
opts.banner = [
|
16
|
-
"Usage: ruby #{script_name} [Options] [server uri]",
|
17
|
-
"",
|
18
|
-
"This tool lets you connect to a breakpoint service ",
|
19
|
-
"which was started via Breakpoint.activate_drb.",
|
20
|
-
"",
|
21
|
-
"The server uri defaults to druby://localhost:42531"
|
22
|
-
].join("\n")
|
23
|
-
|
24
|
-
opts.separator ""
|
25
|
-
|
26
|
-
opts.on("-c", "--client-uri=uri",
|
27
|
-
"Run the client on the specified uri.",
|
28
|
-
"This can be used to specify the port",
|
29
|
-
"that the client uses to allow for back",
|
30
|
-
"connections from the server.",
|
31
|
-
"Default: Find a good URI automatically.",
|
32
|
-
"Example: -c druby://localhost:12345"
|
33
|
-
) { |v| Options[:ClientURI] = v }
|
34
|
-
|
35
|
-
opts.on("-s", "--server-uri=uri",
|
36
|
-
"Connect to the server specified at the",
|
37
|
-
"specified uri.",
|
38
|
-
"Default: druby://localhost:42531"
|
39
|
-
) { |v| Options[:ServerURI] = v }
|
40
|
-
|
41
|
-
opts.on("-R", "--retry-delay=delay", Integer,
|
42
|
-
"Automatically try to reconnect to the",
|
43
|
-
"server after delay seconds when the",
|
44
|
-
"connection failed or timed out.",
|
45
|
-
"A value of 0 disables automatical",
|
46
|
-
"reconnecting completely.",
|
47
|
-
"Default: 10"
|
48
|
-
) { |v| Options[:RetryDelay] = v }
|
49
|
-
|
50
|
-
opts.on("-P", "--[no-]permanent",
|
51
|
-
"Run the breakpoint client in permanent mode.",
|
52
|
-
"This means that the client will keep continue",
|
53
|
-
"running even after the server has closed the",
|
54
|
-
"connection. Useful for example in Rails."
|
55
|
-
) { |v| Options[:Permanent] = v }
|
56
|
-
|
57
|
-
opts.on("-V", "--[no-]verbose",
|
58
|
-
"Run the breakpoint client in verbose mode.",
|
59
|
-
"Will produce more messages, for example between",
|
60
|
-
"individual breakpoints. This might help in seeing",
|
61
|
-
"that the breakpoint client is still alive, but adds",
|
62
|
-
"quite a bit of clutter."
|
63
|
-
) { |v| Options[:Verbose] = v }
|
64
|
-
|
65
|
-
opts.separator ""
|
66
|
-
|
67
|
-
opts.on("-h", "--help",
|
68
|
-
"Show this help message."
|
69
|
-
) { puts opts; exit }
|
70
|
-
opts.on("-v", "--version",
|
71
|
-
"Display the version information."
|
72
|
-
) do
|
73
|
-
id = %q$Id: breakpoint_client.rb 91 2005-02-04 22:34:08Z flgr $
|
74
|
-
puts id.sub("Id: ", "")
|
75
|
-
puts "(Breakpoint::Version = #{Breakpoint::Version})"
|
76
|
-
exit
|
77
|
-
end
|
78
|
-
|
79
|
-
opts.parse!
|
80
|
-
end
|
81
|
-
|
82
|
-
Options[:ServerURI] = ARGV[0] if ARGV[0]
|
83
|
-
|
84
|
-
module Handlers #:nodoc:
|
85
|
-
extend self
|
86
|
-
|
87
|
-
def breakpoint_handler(workspace, message)
|
88
|
-
puts message
|
89
|
-
IRB.start(nil, nil, workspace)
|
90
|
-
|
91
|
-
puts ""
|
92
|
-
if Options[:Verbose] then
|
93
|
-
puts "Resumed execution. Waiting for next breakpoint...", ""
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def eval_handler(code)
|
98
|
-
result = eval(code, TOPLEVEL_BINDING)
|
99
|
-
if result then
|
100
|
-
DRbObject.new(result)
|
101
|
-
else
|
102
|
-
result
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def collision_handler()
|
107
|
-
msg = [
|
108
|
-
" *** Breakpoint service collision ***",
|
109
|
-
" Another Breakpoint service tried to use the",
|
110
|
-
" port already occupied by this one. It will",
|
111
|
-
" keep waiting until this Breakpoint service",
|
112
|
-
" is shut down.",
|
113
|
-
" ",
|
114
|
-
" If you are using the Breakpoint library for",
|
115
|
-
" debugging a Rails or other CGI application",
|
116
|
-
" this likely means that this Breakpoint",
|
117
|
-
" session belongs to an earlier, outdated",
|
118
|
-
" request and should be shut down via 'exit'."
|
119
|
-
].join("\n")
|
120
|
-
|
121
|
-
if RUBY_PLATFORM["win"] then
|
122
|
-
# This sucks. Sorry, I'm not doing this because
|
123
|
-
# I like funky message boxes -- I need to do this
|
124
|
-
# because on Windows I have no way of displaying
|
125
|
-
# my notification via puts() when gets() is still
|
126
|
-
# being performed on STDIN. I have not found a
|
127
|
-
# better solution.
|
128
|
-
begin
|
129
|
-
require 'tk'
|
130
|
-
root = TkRoot.new { withdraw }
|
131
|
-
Tk.messageBox('message' => msg, 'type' => 'ok')
|
132
|
-
root.destroy
|
133
|
-
rescue Exception
|
134
|
-
puts "", msg, ""
|
135
|
-
end
|
136
|
-
else
|
137
|
-
puts "", msg, ""
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# Used for checking whether we are currently in the reconnecting loop.
|
143
|
-
reconnecting = false
|
144
|
-
|
145
|
-
loop do
|
146
|
-
DRb.start_service(Options[:ClientURI])
|
147
|
-
|
148
|
-
begin
|
149
|
-
service = DRbObject.new(nil, Options[:ServerURI])
|
150
|
-
|
151
|
-
begin
|
152
|
-
ehandler = Handlers.method(:eval_handler)
|
153
|
-
chandler = Handlers.method(:collision_handler)
|
154
|
-
handler = Handlers.method(:breakpoint_handler)
|
155
|
-
service.eval_handler = ehandler
|
156
|
-
service.collision_handler = chandler
|
157
|
-
service.handler = handler
|
158
|
-
|
159
|
-
reconnecting = false
|
160
|
-
if Options[:Verbose] then
|
161
|
-
puts "Connection established. Waiting for breakpoint...", ""
|
162
|
-
end
|
163
|
-
|
164
|
-
loop do
|
165
|
-
begin
|
166
|
-
service.ping
|
167
|
-
rescue DRb::DRbConnError => error
|
168
|
-
puts "Server exited. Closing connection...", ""
|
169
|
-
exit! unless Options[:Permanent]
|
170
|
-
break
|
171
|
-
end
|
172
|
-
|
173
|
-
sleep(0.5)
|
174
|
-
end
|
175
|
-
ensure
|
176
|
-
service.eval_handler = nil
|
177
|
-
service.collision_handler = nil
|
178
|
-
service.handler = nil
|
179
|
-
end
|
180
|
-
rescue Exception => error
|
181
|
-
if Options[:RetryDelay] > 0 then
|
182
|
-
if not reconnecting then
|
183
|
-
reconnecting = true
|
184
|
-
puts "No connection to breakpoint service at #{Options[:ServerURI]} " +
|
185
|
-
"(#{error.class})"
|
186
|
-
puts error.backtrace if $DEBUG
|
187
|
-
puts "Tries to connect will be made every #{Options[:RetryDelay]} seconds..."
|
188
|
-
end
|
189
|
-
|
190
|
-
sleep Options[:RetryDelay]
|
191
|
-
retry
|
192
|
-
else
|
193
|
-
raise
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|