rails 0.8.5 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

Files changed (66) hide show
  1. data/CHANGELOG +86 -0
  2. data/README +48 -8
  3. data/Rakefile +87 -108
  4. data/bin/breakpointer +3 -0
  5. data/bin/breakpointer_for_gem +4 -0
  6. data/bin/console +30 -0
  7. data/bin/generate +41 -0
  8. data/bin/rails +1 -1
  9. data/{dispatches/dispatch.servlet → bin/server} +15 -11
  10. data/configs/apache.conf +12 -27
  11. data/configs/database.yml +9 -2
  12. data/dispatches/dispatch.fcgi +2 -2
  13. data/dispatches/dispatch.rb +2 -2
  14. data/doc/index.html +12 -36
  15. data/environments/development.rb +5 -0
  16. data/environments/production.rb +3 -6
  17. data/environments/shared.rb +46 -20
  18. data/environments/shared_for_gem.rb +41 -8
  19. data/environments/test.rb +3 -6
  20. data/fresh_rakefile +25 -21
  21. data/generators/controller/USAGE +28 -0
  22. data/generators/controller/controller_generator.rb +22 -0
  23. data/generators/controller/templates/controller.rb +10 -0
  24. data/generators/{templates/controller_test.erb → controller/templates/functional_test.rb} +1 -1
  25. data/generators/{templates/helper.erb → controller/templates/helper.rb} +0 -0
  26. data/generators/controller/templates/view.rhtml +2 -0
  27. data/generators/mailer/USAGE +27 -0
  28. data/generators/mailer/mailer_generator.rb +22 -0
  29. data/generators/{templates/mailer_action.rhtml → mailer/templates/fixture.rhtml} +0 -0
  30. data/generators/{templates/mailer.erb → mailer/templates/mailer.rb} +4 -4
  31. data/generators/{templates/mailer_test.erb → mailer/templates/unit_test.rb} +2 -10
  32. data/generators/mailer/templates/view.rhtml +3 -0
  33. data/generators/model/USAGE +17 -0
  34. data/generators/model/model_generator.rb +10 -0
  35. data/generators/model/templates/fixtures.yml +1 -0
  36. data/generators/{templates/model.erb → model/templates/model.rb} +0 -2
  37. data/generators/{templates/model_test.erb → model/templates/unit_test.rb} +2 -3
  38. data/generators/scaffold/USAGE +25 -0
  39. data/generators/scaffold/scaffold_generator.rb +53 -0
  40. data/generators/scaffold/templates/controller.rb +57 -0
  41. data/generators/scaffold/templates/fixtures.yml +7 -0
  42. data/generators/scaffold/templates/functional_test.rb +79 -0
  43. data/generators/scaffold/templates/layout.rhtml +11 -0
  44. data/generators/scaffold/templates/style.css +53 -0
  45. data/generators/scaffold/templates/view_edit.rhtml +7 -0
  46. data/generators/scaffold/templates/view_list.rhtml +24 -0
  47. data/generators/scaffold/templates/view_new.rhtml +6 -0
  48. data/generators/scaffold/templates/view_show.rhtml +8 -0
  49. data/helpers/{abstract_application.rb → application.rb} +1 -4
  50. data/helpers/test_helper.rb +4 -5
  51. data/lib/binding_of_caller.rb +81 -0
  52. data/lib/breakpoint.rb +526 -0
  53. data/lib/breakpoint_client.rb +167 -0
  54. data/lib/dispatcher.rb +43 -12
  55. data/lib/rails_generator.rb +175 -0
  56. data/lib/webrick_server.rb +48 -52
  57. metadata +49 -21
  58. data/gem_snapshot +0 -14
  59. data/generators/new_controller.rb +0 -43
  60. data/generators/new_crud.rb +0 -34
  61. data/generators/new_mailer.rb +0 -43
  62. data/generators/new_model.rb +0 -31
  63. data/generators/templates/controller.erb +0 -24
  64. data/generators/templates/controller_view.rhtml +0 -10
  65. data/generators/templates/mailer_fixture.rhtml +0 -4
  66. data/lib/generator.rb +0 -112
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+
3
+ first_<%= singular_name %>:
4
+ id: 1
5
+
6
+ another_<%= singular_name %>:
7
+ id: 2
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require '<%= file_name %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= class_name %>ControllerTest < Test::Unit::TestCase
8
+ fixtures :<%= table_name %>
9
+
10
+ def setup
11
+ @controller = <%= class_name %>Controller.new
12
+ @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
13
+ end
14
+
15
+ <% for action in unscaffolded_actions -%>
16
+ def test_<%= action %>
17
+ process :<%= action %>
18
+ assert_rendered_file '<%= action %>'
19
+ end
20
+
21
+ <% end -%>
22
+ <% unless suffix -%>
23
+ def test_index
24
+ process :index
25
+ assert_rendered_file 'list'
26
+ end
27
+
28
+ <% end -%>
29
+ def test_list<%= suffix %>
30
+ process :list<%= suffix %>
31
+ assert_rendered_file 'list<%= suffix %>'
32
+ assert_template_has '<%= plural_name %>'
33
+ end
34
+
35
+ def test_show<%= suffix %>
36
+ process :show<%= suffix %>, 'id' => 1
37
+ assert_rendered_file 'show'
38
+ assert_template_has '<%= singular_name %>'
39
+ assert_valid_record '<%= singular_name %>'
40
+ end
41
+
42
+ def test_new<%= suffix %>
43
+ process :new<%= suffix %>
44
+ assert_rendered_file 'new<%= suffix %>'
45
+ assert_template_has '<%= singular_name %>'
46
+ end
47
+
48
+ def test_create
49
+ num_<%= plural_name %> = <%= class_name %>.find_all.size
50
+
51
+ process :create<%= suffix %>, '<%= singular_name %>' => { }
52
+ assert_redirected_to :action => 'list<%= suffix %>'
53
+
54
+ assert_equal num_<%= plural_name %> + 1, <%= class_name %>.find_all.size
55
+ end
56
+
57
+ def test_edit<%= suffix %>
58
+ process :edit<%= suffix %>, 'id' => 1
59
+ assert_rendered_file 'edit<%= suffix %>'
60
+ assert_template_has '<%= singular_name %>'
61
+ assert_valid_record '<%= singular_name %>'
62
+ end
63
+
64
+ def test_update<%= suffix %>
65
+ process :update<%= suffix %>, '<%= singular_name %>' => { 'id' => 1 }
66
+ assert_redirected_to :action => 'show<%= suffix %>', :id => 1
67
+ end
68
+
69
+ def test_destroy<%= suffix %>
70
+ assert_not_nil <%= class_name %>.find(1)
71
+
72
+ process :destroy, 'id' => 1
73
+ assert_redirected_to :action => 'list<%= suffix %>'
74
+
75
+ assert_raise(ActiveRecord::RecordNotFound) {
76
+ <%= singular_name %> = <%= class_name %>.find(1)
77
+ }
78
+ end
79
+ end
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <head>
3
+ <title>Scaffolding: <%%= controller.controller_name %>#<%%= controller.action_name %></title>
4
+ <link href="/stylesheets/scaffold.css" rel="stylesheet" type="text/css" />
5
+ </head>
6
+ <body>
7
+
8
+ <%%= @content_for_layout %>
9
+
10
+ </body>
11
+ </html>
@@ -0,0 +1,53 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ .fieldWithErrors {
20
+ padding: 2px;
21
+ background-color: red;
22
+ display: table;
23
+ }
24
+
25
+ #ErrorExplanation {
26
+ width: 400px;
27
+ border: 2px solid #red;
28
+ padding: 7px;
29
+ padding-bottom: 12px;
30
+ margin-bottom: 20px;
31
+ background-color: #f0f0f0;
32
+ }
33
+
34
+ #ErrorExplanation h2 {
35
+ text-align: left;
36
+ font-weight: bold;
37
+ padding: 5px 5px 5px 15px;
38
+ font-size: 12px;
39
+ margin: -7px;
40
+ background-color: #c00;
41
+ color: #fff;
42
+ }
43
+
44
+ #ErrorExplanation p {
45
+ color: #333;
46
+ margin-bottom: 0;
47
+ padding: 5px;
48
+ }
49
+
50
+ #ErrorExplanation ul li {
51
+ font-size: 12px;
52
+ list-style: square;
53
+ }
@@ -0,0 +1,7 @@
1
+ <h1>Editing <%= singular_name %></h1>
2
+
3
+ <%%= error_messages_for '<%= singular_name %>' %>
4
+ <%%= form '<%= singular_name %>', :action => 'update<%= suffix %>' %>
5
+
6
+ <%%= link_to 'Show', :action => 'show<%= suffix %>', :id => @<%= singular_name %>.id %> |
7
+ <%%= link_to 'Back', :action => 'list<%= suffix %>' %>
@@ -0,0 +1,24 @@
1
+ <h1>Listing <%= plural_name %></h1>
2
+
3
+ <table>
4
+ <tr>
5
+ <%% for column in <%= class_name %>.content_columns %>
6
+ <th><%%= column.human_name %></th>
7
+ <%% end %>
8
+ </tr>
9
+
10
+ <%% for <%= singular_name %> in @<%= plural_name %> %>
11
+ <tr>
12
+ <%% for column in <%= class_name %>.content_columns %>
13
+ <td><%%=h <%= singular_name %>[column.name] %></td>
14
+ <%% end %>
15
+ <td><%%= link_to 'Show', :action => 'show<%= suffix %>', :id => <%= singular_name %>.id %></td>
16
+ <td><%%= link_to 'Edit', :action => 'edit<%= suffix %>', :id => <%= singular_name %>.id %></td>
17
+ <td><%%= link_to 'Destroy', :action => 'destroy<%= suffix %>', :id => <%= singular_name %>.id %></td>
18
+ </tr>
19
+ <%% end %>
20
+ </table>
21
+
22
+ <br />
23
+
24
+ <%%= link_to 'New <%= singular_name %>', :action => 'new<%= suffix %>' %>
@@ -0,0 +1,6 @@
1
+ <h1>New <%= @singular_name %></h1>
2
+
3
+ <%%= error_messages_for '<%= singular_name %>' %>
4
+ <%%= form '<%= singular_name %>', :action => 'create<%= suffix %>' %>
5
+
6
+ <%%= link_to 'Back', :action => 'list<%= suffix %>' %>
@@ -0,0 +1,8 @@
1
+ <%% for column in <%= class_name %>.content_columns %>
2
+ <p>
3
+ <b><%%= column.human_name %>:</b> <%%= @<%= singular_name %>[column.name] %>
4
+ </p>
5
+ <%% end %>
6
+
7
+ <%%= link_to 'Edit', :action => 'edit<%= suffix %>', :id => @<%= singular_name %>.id %> |
8
+ <%%= link_to 'Back', :action => 'list<%= suffix %>' %>
@@ -1,7 +1,4 @@
1
- require 'action_controller'
2
-
3
1
  # The filters added to this controller will be run for all controllers in the application.
4
2
  # Likewise will all the methods added be available for all controllers.
5
- class AbstractApplicationController < ActionController::Base
6
- helper :application
3
+ class ApplicationController < ActionController::Base
7
4
  end
@@ -1,12 +1,11 @@
1
- require File.dirname(__FILE__) + "/../config/environments/test"
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require File.dirname(__FILE__) + "/../config/environment"
3
+ require 'application'
2
4
 
3
5
  require 'test/unit'
4
6
  require 'active_record/fixtures'
5
7
  require 'action_controller/test_process'
6
-
7
- # Make rubygems available for testing if possible
8
- begin require('rubygems'); rescue LoadError; end
9
- begin require('dev-utils/debug'); rescue LoadError; end
8
+ require 'breakpoint'
10
9
 
11
10
  def create_fixtures(*table_names)
12
11
  Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures", table_names)
@@ -0,0 +1,81 @@
1
+ begin
2
+ require 'simplecc'
3
+ rescue LoadError
4
+ def Continuation.create(*args, &block)
5
+ cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
6
+ result ||= args
7
+ return *[cc, *result]
8
+ end
9
+ end
10
+
11
+ # This method returns the binding of the method that called your
12
+ # method. It will raise an Exception when you're not inside a method.
13
+ #
14
+ # It's used like this:
15
+ # def inc_counter(amount = 1)
16
+ # Binding.of_caller do |binding|
17
+ # # Create a lambda that will increase the variable 'counter'
18
+ # # in the caller of this method when called.
19
+ # inc = eval("lambda { |arg| counter += arg }", binding)
20
+ # # We can refer to amount from inside this block safely.
21
+ # inc.call(amount)
22
+ # end
23
+ # # No other statements can go here. Put them inside the block.
24
+ # end
25
+ # counter = 0
26
+ # 2.times { inc_counter }
27
+ # counter # => 2
28
+ #
29
+ # Binding.of_caller must be the last statement in the method.
30
+ # This means that you will have to put everything you want to
31
+ # do after the call to Binding.of_caller into the block of it.
32
+ # This should be no problem however, because Ruby has closures.
33
+ # If you don't do this an Exception will be raised. Because of
34
+ # the way that Binding.of_caller is implemented it has to be
35
+ # done this way.
36
+ def Binding.of_caller(&block)
37
+ old_critical = Thread.critical
38
+ Thread.critical = true
39
+ count = 0
40
+ cc, result, error, extra_data = Continuation.create(nil, nil)
41
+ error.call if error
42
+
43
+ tracer = lambda do |*args|
44
+ type, context, extra_data = args[0], args[4], args
45
+ if type == "return"
46
+ count += 1
47
+ # First this method and then calling one will return --
48
+ # the trace event of the second event gets the context
49
+ # of the method which called the method that called this
50
+ # method.
51
+ if count == 2
52
+ # It would be nice if we could restore the trace_func
53
+ # that was set before we swapped in our own one, but
54
+ # this is impossible without overloading set_trace_func
55
+ # in current Ruby.
56
+ set_trace_func(nil)
57
+ cc.call(eval("binding", context), nil, extra_data)
58
+ end
59
+ elsif type == "line" then
60
+ nil
61
+ elsif type == "c-return" and extra_data[3] == :set_trace_func then
62
+ nil
63
+ else
64
+ set_trace_func(nil)
65
+ error_msg = "Binding.of_caller used in non-method context or " +
66
+ "trailing statements of method using it aren't in the block."
67
+ cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
68
+ end
69
+ end
70
+
71
+ unless result
72
+ set_trace_func(tracer)
73
+ return nil
74
+ else
75
+ Thread.critical = old_critical
76
+ case block.arity
77
+ when 1 then yield(result)
78
+ else yield(result, extra_data)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,526 @@
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 'binding_of_caller'
20
+ require 'drb'
21
+ require 'drb/acl'
22
+
23
+ module Breakpoint
24
+ extend self
25
+
26
+ # This will pop up an interactive ruby session at a
27
+ # pre-defined break point in a Ruby application. In
28
+ # this session you can examine the environment of
29
+ # the break point.
30
+ #
31
+ # You can get a list of variables in the context using
32
+ # local_variables via +local_variables+. You can then
33
+ # examine their values by typing their names.
34
+ #
35
+ # You can have a look at the call stack via +caller+.
36
+ #
37
+ # The source code around the location where the breakpoint
38
+ # was executed can be examined via +source_lines+. Its
39
+ # argument specifies how much lines of context to display.
40
+ # The default amount of context is 5 lines. Note that
41
+ # the call to +source_lines+ can raise an exception when
42
+ # it isn't able to read in the source code.
43
+ #
44
+ # breakpoints can also return a value. They will execute
45
+ # a supplied block for getting a default return value.
46
+ # A custom value can be returned from the session by doing
47
+ # +throw(:debug_return, value)+.
48
+ #
49
+ # You can also give names to break points which will be
50
+ # used in the message that is displayed upon execution
51
+ # of them.
52
+ #
53
+ # Here's a sample of how breakpoints should be placed:
54
+ #
55
+ # class Person
56
+ # def initialize(name, age)
57
+ # @name, @age = name, age
58
+ # breakpoint("Person#initialize")
59
+ # end
60
+ #
61
+ # attr_reader :age
62
+ # def name
63
+ # breakpoint("Person#name") { @name }
64
+ # end
65
+ # end
66
+ #
67
+ # person = Person.new("Random Person", 23)
68
+ # puts "Name: #{person.name}"
69
+ #
70
+ # And here is a sample debug session:
71
+ #
72
+ # Executing break point "Person#initialize" at file.rb:4 in `initialize'
73
+ # irb(#<Person:0x292fbe8>):001:0> local_variables
74
+ # => ["name", "age", "_", "__"]
75
+ # irb(#<Person:0x292fbe8>):002:0> [name, age]
76
+ # => ["Random Person", 23]
77
+ # irb(#<Person:0x292fbe8>):003:0> [@name, @age]
78
+ # => ["Random Person", 23]
79
+ # irb(#<Person:0x292fbe8>):004:0> self
80
+ # => #<Person:0x292fbe8 @age=23, @name="Random Person">
81
+ # irb(#<Person:0x292fbe8>):005:0> @age += 1; self
82
+ # => #<Person:0x292fbe8 @age=24, @name="Random Person">
83
+ # irb(#<Person:0x292fbe8>):006:0> exit
84
+ # Executing break point "Person#name" at file.rb:9 in `name'
85
+ # irb(#<Person:0x292fbe8>):001:0> throw(:debug_return, "Overriden name")
86
+ # Name: Overriden name
87
+ #
88
+ # Breakpoint sessions will automatically have a few
89
+ # convenience methods available. See Breakpoint::CommandBundle
90
+ # for a list of them.
91
+ #
92
+ # Breakpoints can also be used remotely over sockets.
93
+ # This is implemented by running part of the IRB session
94
+ # in the application and part of it in a special client.
95
+ # You have to call Breakpoint.activate_drb to enable
96
+ # support for remote breakpoints and then run
97
+ # breakpoint_client.rb which is distributed with this
98
+ # library. See the documentation of Breakpoint.activate_drb
99
+ # for details.
100
+ def breakpoint(id = nil, context = nil, &block)
101
+ callstack = caller
102
+ callstack.slice!(0, 3) if callstack.first["breakpoint"]
103
+ file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
104
+
105
+ message = "Executing break point " + (id ? "#{id.inspect} " : "") +
106
+ "at #{file}:#{line}" + (method ? " in `#{method}'" : "")
107
+
108
+ if context then
109
+ return handle_breakpoint(context, message, file, line, &block)
110
+ end
111
+
112
+ Binding.of_caller do |binding_context|
113
+ handle_breakpoint(binding_context, message, file, line, &block)
114
+ end
115
+ end
116
+
117
+ module CommandBundle
118
+ # Proxy to a Breakpoint client. Lets you directly execute code
119
+ # in the context of the client.
120
+ class Client
121
+ def initialize(eval_handler) # :nodoc:
122
+ @eval_handler = eval_handler
123
+ end
124
+
125
+ instance_methods.each do |method|
126
+ next if method[/^__.+__$/]
127
+ undef_method method
128
+ end
129
+
130
+ # Executes the specified code at the client.
131
+ def eval(code)
132
+ @eval_handler.call(code)
133
+ end
134
+
135
+ # Will execute the specified statement at the client.
136
+ def method_missing(method, *args)
137
+ if args.empty?
138
+ result = eval("#{method}")
139
+ else
140
+ result = eval("#{method}(*Marshal.load(#{Marshal.dump(args).inspect}))")
141
+ end
142
+
143
+ unless [true, false, nil].include?(result)
144
+ result.extend(DRbUndumped) if result
145
+ end
146
+
147
+ return result
148
+ end
149
+ end
150
+
151
+ # Returns the source code surrounding the location where the
152
+ # breakpoint was issued.
153
+ def source_lines(context = 5, return_line_numbers = false)
154
+ lines = File.readlines(@__bp_file).map { |line| line.chomp }
155
+
156
+ break_line = @__bp_line
157
+ start_line = [break_line - context, 1].max
158
+ end_line = break_line + context
159
+
160
+ result = lines[(start_line - 1) .. (end_line - 1)]
161
+
162
+ if return_line_numbers then
163
+ return [start_line, break_line, result]
164
+ else
165
+ return result
166
+ end
167
+ end
168
+
169
+ # Lets an object that will forward method calls to the breakpoint
170
+ # client. This is useful for outputting longer things at the client
171
+ # and so on. You can for example do these things:
172
+ #
173
+ # client.puts "Hello" # outputs "Hello" at client console
174
+ # # outputs "Hello" into the file temp.txt at the client
175
+ # client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
176
+ def client()
177
+ if Breakpoint.use_drb? then
178
+ Client.new(Breakpoint.drb_service.eval_handler)
179
+ else
180
+ Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
181
+ end
182
+ end
183
+ end
184
+
185
+ def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
186
+ catch(:debug_return) do |value|
187
+ eval(%{
188
+ @__bp_file = #{file.inspect}
189
+ @__bp_line = #{line}
190
+ extend Breakpoint::CommandBundle
191
+ extend DRbUndumped if self
192
+ }, context) rescue nil
193
+
194
+ if not use_drb? then
195
+ puts message
196
+ IRB.start(nil, IRB::WorkSpace.new(context))
197
+ else
198
+ @drb_service.add_breakpoint(context, message)
199
+ end
200
+
201
+ block.call if block
202
+ end
203
+ end
204
+
205
+ # These exceptions will be raised on failed asserts
206
+ # if Breakpoint.asserts_cause_exceptions is set to
207
+ # true.
208
+ class FailedAssertError < RuntimeError
209
+ end
210
+
211
+ # This asserts that the block evaluates to true.
212
+ # If it doesn't evaluate to true a breakpoint will
213
+ # automatically be created at that execution point.
214
+ #
215
+ # You can disable assert checking in production
216
+ # code by setting Breakpoint.optimize_asserts to
217
+ # true. (It will still be enabled when Ruby is run
218
+ # via the -d argument.)
219
+ #
220
+ # Example:
221
+ # person_name = "Foobar"
222
+ # assert { not person_name.nil? }
223
+ #
224
+ # Note: If you want to use this method from an
225
+ # unit test, you will have to call it by its full
226
+ # name, Breakpoint.assert.
227
+ def assert(context = nil, &condition)
228
+ return if Breakpoint.optimize_asserts and not $DEBUG
229
+ return if yield
230
+
231
+ callstack = caller
232
+ callstack.slice!(0, 3) if callstack.first["assert"]
233
+ file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
234
+
235
+ message = "Assert failed at #{file}:#{line}#{" in `#{method}'" if method}."
236
+
237
+ if Breakpoint.asserts_cause_exceptions and not $DEBUG then
238
+ raise(Breakpoint::FailedAssertError, message)
239
+ end
240
+
241
+ message += " Executing implicit breakpoint."
242
+
243
+ if context then
244
+ return handle_breakpoint(context, message, file, line)
245
+ end
246
+
247
+ Binding.of_caller do |context|
248
+ handle_breakpoint(context, message, file, line)
249
+ end
250
+ end
251
+
252
+ # Whether asserts should be ignored if not in debug mode.
253
+ # Debug mode can be enabled by running ruby with the -d
254
+ # switch or by setting $DEBUG to true.
255
+ attr_accessor :optimize_asserts
256
+ self.optimize_asserts = false
257
+
258
+ # Whether an Exception should be raised on failed asserts
259
+ # in non-$DEBUG code or not. By default this is disabled.
260
+ attr_accessor :asserts_cause_exceptions
261
+ self.asserts_cause_exceptions = false
262
+ @use_drb = false
263
+
264
+ attr_reader :drb_service # :nodoc:
265
+
266
+ class DRbService # :nodoc:
267
+ include DRbUndumped
268
+
269
+ def initialize
270
+ @handler = @eval_handler = @collision_handler = nil
271
+
272
+ IRB.instance_eval { @CONF[:RC] = true }
273
+ IRB.run_config
274
+ end
275
+
276
+ def collision
277
+ sleep(0.5) until @collision_handler
278
+
279
+ @collision_handler.call
280
+ end
281
+
282
+ def ping; end
283
+
284
+ def add_breakpoint(context, message)
285
+ workspace = IRB::WorkSpace.new(context)
286
+ workspace.extend(DRbUndumped)
287
+
288
+ sleep(0.5) until @handler
289
+
290
+ @handler.call(workspace, message)
291
+ end
292
+
293
+ def register_handler(&block)
294
+ @handler = block
295
+ end
296
+
297
+ def unregister_handler
298
+ @handler = nil
299
+ end
300
+
301
+ attr_reader :eval_handler
302
+
303
+ def register_eval_handler(&block)
304
+ @eval_handler = block
305
+ end
306
+
307
+ def unregister_eval_handler
308
+ @eval_handler = lambda { }
309
+ end
310
+
311
+ def register_collision_handler(&block)
312
+ @collision_handler = block
313
+ end
314
+
315
+ def unregister_collision_handler
316
+ @collision_handler = lambda { }
317
+ end
318
+ end
319
+
320
+ # Will run Breakpoint in DRb mode. This will spawn a server
321
+ # that can be attached to via the breakpoint-client command
322
+ # whenever a breakpoint is executed. This is useful when you
323
+ # are debugging CGI applications or other applications where
324
+ # you can't access debug sessions via the standard input and
325
+ # output of your application.
326
+ #
327
+ # You can specify an URI where the DRb server will run at.
328
+ # This way you can specify the port the server runs on. The
329
+ # default URI is druby://localhost:42531.
330
+ #
331
+ # Please note that breakpoints will be skipped silently in
332
+ # case the DRb server can not spawned. (This can happen if
333
+ # the port is already used by another instance of your
334
+ # application on CGI or another application.)
335
+ #
336
+ # Also note that by default this will only allow access
337
+ # from localhost. You can however specify a list of
338
+ # allowed hosts or nil (to allow access from everywhere).
339
+ # But that will still not protect you from somebody
340
+ # reading the data as it goes through the net.
341
+ #
342
+ # A good approach for getting security and remote access
343
+ # is setting up an SSH tunnel between the DRb service
344
+ # and the client. This is usually done like this:
345
+ #
346
+ # $ ssh -L20000:127.0.0.1:20000 -R10000:127.0.0.1:10000 example.com
347
+ # (This will connect port 20000 at the client side to port
348
+ # 20000 at the server side, and port 10000 at the server
349
+ # side to port 10000 at the client side.)
350
+ #
351
+ # After that do this on the server side: (the code being debugged)
352
+ # Breakpoint.activate_drb("druby://127.0.0.1:20000", "localhost")
353
+ #
354
+ # And at the client side:
355
+ # ruby breakpoint_client.rb -c druby://127.0.0.1:10000 -s druby://127.0.0.1:20000
356
+ #
357
+ # Running through such a SSH proxy will also let you use
358
+ # breakpoint.rb in case you are behind a firewall.
359
+ #
360
+ # Detailed information about running DRb through firewalls is
361
+ # available at http://www.rubygarden.org/ruby?DrbTutorial
362
+ def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
363
+ ignore_collisions = false)
364
+
365
+ return false if @use_drb
366
+
367
+ uri ||= 'druby://localhost:42531'
368
+
369
+ if allowed_hosts then
370
+ acl = ["deny", "all"]
371
+
372
+ Array(allowed_hosts).each do |host|
373
+ acl += ["allow", host]
374
+ end
375
+
376
+ DRb.install_acl(ACL.new(acl))
377
+ end
378
+
379
+ @use_drb = true
380
+ @drb_service = DRbService.new
381
+ did_collision = false
382
+ begin
383
+ @service = DRb.start_service(uri, @drb_service)
384
+ rescue Errno::EADDRINUSE
385
+ if ignore_collisions then
386
+ nil
387
+ else
388
+ # The port is already occupied by another
389
+ # Breakpoint service. We will try to tell
390
+ # the old service that we want its port.
391
+ # It will then forward that request to the
392
+ # user and retry.
393
+ unless did_collision then
394
+ DRbObject.new(nil, uri).collision
395
+ did_collision = true
396
+ end
397
+ sleep(10)
398
+ retry
399
+ end
400
+ end
401
+
402
+ return true
403
+ end
404
+
405
+ # Deactivates a running Breakpoint service.
406
+ def deactivate_drb
407
+ @service.stop_service unless @service.nil?
408
+ @service = nil
409
+ @use_drb = false
410
+ @drb_service = nil
411
+ end
412
+
413
+ # Returns true when Breakpoints are used over DRb.
414
+ # Breakpoint.activate_drb causes this to be true.
415
+ def use_drb?
416
+ @use_drb == true
417
+ end
418
+ end
419
+
420
+ module IRB # :nodoc:
421
+ class << self; remove_method :start; end
422
+ def self.start(ap_path = nil, main_context = nil, workspace = nil)
423
+ $0 = File::basename(ap_path, ".rb") if ap_path
424
+
425
+ # suppress some warnings about redefined constants
426
+ old_verbose, $VERBOSE = $VERBOSE, nil
427
+ IRB.setup(ap_path)
428
+ $VERBOSE = old_verbose
429
+
430
+ if @CONF[:SCRIPT] then
431
+ irb = Irb.new(main_context, @CONF[:SCRIPT])
432
+ else
433
+ irb = Irb.new(main_context)
434
+ end
435
+
436
+ if workspace then
437
+ irb.context.workspace = workspace
438
+ end
439
+
440
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
441
+ @CONF[:MAIN_CONTEXT] = irb.context
442
+
443
+ old_sigint = trap("SIGINT") do
444
+ irb.signal_handle
445
+ end
446
+
447
+ catch(:IRB_EXIT) do
448
+ irb.eval_input
449
+ end
450
+ ensure
451
+ trap("SIGINT", old_sigint)
452
+ end
453
+
454
+ class << self
455
+ alias :old_CurrentContext :CurrentContext
456
+ remove_method :CurrentContext
457
+ end
458
+ def IRB.CurrentContext
459
+ if old_CurrentContext.nil? and Breakpoint.use_drb? then
460
+ result = Object.new
461
+ def result.last_value; end
462
+ return result
463
+ else
464
+ old_CurrentContext
465
+ end
466
+ end
467
+
468
+ class Context
469
+ alias :old_evaluate :evaluate
470
+ def evaluate(line, line_no)
471
+ if line.chomp == "exit" then
472
+ exit
473
+ else
474
+ old_evaluate(line, line_no)
475
+ end
476
+ end
477
+ end
478
+
479
+ class WorkSpace
480
+ alias :old_evaluate :evaluate
481
+
482
+ def evaluate(*args)
483
+ if Breakpoint.use_drb? then
484
+ result = old_evaluate(*args)
485
+ if args[0] != :no_proxy and
486
+ not [true, false, nil].include?(result)
487
+ then
488
+ result.extend(DRbUndumped) rescue nil
489
+ end
490
+ return result
491
+ else
492
+ old_evaluate(*args)
493
+ end
494
+ end
495
+ end
496
+
497
+ module InputCompletor
498
+ def self.eval(code, context, *more)
499
+ # Big hack, this assumes that InputCompletor
500
+ # will only call eval() when it wants code
501
+ # to be executed in the IRB context.
502
+ IRB.conf[:MAIN_CONTEXT].workspace.evaluate(:no_proxy, code, *more)
503
+ end
504
+ end
505
+ end
506
+
507
+ module DRb # :nodoc:
508
+ class DRbObject
509
+ undef :inspect
510
+ undef :clone
511
+ end
512
+ end
513
+
514
+ # See Breakpoint.breakpoint
515
+ def breakpoint(id = nil, &block)
516
+ Binding.of_caller do |context|
517
+ Breakpoint.breakpoint(id, context, &block)
518
+ end
519
+ end
520
+
521
+ # See Breakpoint.assert
522
+ def assert(&block)
523
+ Binding.of_caller do |context|
524
+ Breakpoint.assert(context, &block)
525
+ end
526
+ end