nitro 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/ChangeLog +175 -0
  2. data/README +41 -7
  3. data/RELEASES +24 -0
  4. data/Rakefile +5 -7
  5. data/bin/new_app.rb +26 -4
  6. data/bin/new_form.rb +54 -0
  7. data/bin/proto/config.rb +3 -3
  8. data/bin/proto/root/index.xhtml +2 -34
  9. data/bin/proto/root/style.css +4 -70
  10. data/bin/proto/root/style.xsl +8 -39
  11. data/doc/tutorial.txt +5 -0
  12. data/examples/blog/app.rb +2 -1
  13. data/examples/blog/config.rb +7 -2
  14. data/examples/blog/root/style.xsl +1 -2
  15. data/examples/flash/README +34 -0
  16. data/examples/flash/app.rb +20 -0
  17. data/examples/flash/config.rb +38 -0
  18. data/examples/flash/lib/flash.rb +40 -0
  19. data/examples/flash/root/index.xhtml +25 -0
  20. data/examples/flash/root/show_inline_text.xhtml +12 -0
  21. data/examples/flash/tmp.swf +0 -0
  22. data/examples/og/README +7 -0
  23. data/examples/og/mock_example.rb +58 -0
  24. data/examples/og/run.rb +9 -5
  25. data/examples/tiny/root/include.xhtml +3 -0
  26. data/examples/tiny/root/index.xhtml +2 -1
  27. data/lib/glue/property.rb +166 -107
  28. data/lib/glue/property.rb.old +307 -0
  29. data/lib/nitro/builders/form.rb +26 -17
  30. data/lib/nitro/events.rb +1 -1
  31. data/lib/nitro/markup.rb +120 -0
  32. data/lib/nitro/server/cookie.rb +1 -1
  33. data/lib/nitro/server/dispatcher.rb +5 -6
  34. data/lib/nitro/server/filters.rb +1 -1
  35. data/lib/nitro/server/render.rb +33 -29
  36. data/lib/nitro/server/shaders.rb +32 -3
  37. data/lib/nitro/server/user.rb +1 -1
  38. data/lib/nitro/server/webrick.rb +9 -4
  39. data/lib/nitro/ui/popup.rb +1 -1
  40. data/lib/nitro/ui/select.rb +1 -1
  41. data/lib/nitro/ui/tabs.rb +1 -1
  42. data/lib/nitro/version.rb +2 -2
  43. data/lib/og.rb +17 -6
  44. data/lib/og/backend.rb +34 -4
  45. data/lib/og/backends/mysql.rb +3 -17
  46. data/lib/og/backends/psql.rb +5 -17
  47. data/lib/og/meta.rb +41 -26
  48. data/lib/og/mock.rb +223 -0
  49. data/lib/og/version.rb +2 -2
  50. data/lib/parts/content.rb +61 -0
  51. data/test/glue/{tc_properties.rb → tc_property.rb} +0 -1
  52. data/test/glue/tc_property_mixins.rb +62 -0
  53. data/test/og/tc_lifecycle.rb +107 -0
  54. data/test/tc_og.rb +31 -4
  55. data/vendor/README +6 -0
  56. data/vendor/binding_of_caller.rb +81 -0
  57. data/vendor/breakpoint.rb +526 -0
  58. data/vendor/breakpoint_client.rb +157 -0
  59. metadata +135 -95
data/lib/og/version.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # * George Moschovitis <gm@navel.gr>
3
3
  #
4
4
  # (c) 2004 Navel, all rights reserved.
5
- # $Id: version.rb 175 2004-11-26 16:11:27Z gmosx $
5
+ # $Id: version.rb 198 2004-12-22 11:26:59Z gmosx $
6
6
 
7
7
  # The version of Og.
8
- $og_version = '0.5.0'
8
+ $og_version = '0.7.0'
@@ -0,0 +1,61 @@
1
+ # = Content
2
+ #
3
+ # Content Management Foundation
4
+ #
5
+ # code:
6
+ # * George Moschovitis <gm@navel.gr>
7
+ #
8
+ # (c) 2004 Navel, all rights reserved.
9
+ # $Id$
10
+
11
+ require 'nitro/markup'
12
+
13
+ module N
14
+
15
+ # = BaseContent
16
+ #
17
+ # The foundamental Content Unit.
18
+ #
19
+ module BaseContent
20
+ include N::Markup
21
+
22
+ prop_accessor :title, String
23
+ prop_accessor :body, String, :markup => true, :ui => :textarea
24
+ end
25
+
26
+ # = Content
27
+ #
28
+ # The foundamental Content Unit.
29
+ #
30
+ #--
31
+ # THINK: rename this to Entity?
32
+ #++
33
+ module Content
34
+ include N::BaseContent
35
+ prop_accessor :ctime, Time
36
+ prop_accessor :mtime, Time
37
+
38
+ def initialize(*args)
39
+ @ctime = @mtime = Time.now
40
+ end
41
+ end
42
+
43
+ # = Category
44
+ #
45
+ class Category
46
+ include N::BaseContent
47
+ end
48
+
49
+ # = ACL
50
+ #
51
+ # Unix style permissions.
52
+ #
53
+ module ACL
54
+ # bits:
55
+ # 0, 1, 2: owner read, write, execute
56
+ # 3, 4, 5: group read, write, execute
57
+ # 6, 7, 8: other read, write, execute
58
+ prop_accessor :permissions
59
+ end
60
+
61
+ end # module
@@ -30,7 +30,6 @@ class SubMsg < Msg
30
30
  # to avoid conflicts with tc_og.rb
31
31
  include Og::Unmanageable
32
32
 
33
- inherit_meta(superclass)
34
33
  # duplicate definition with different type!
35
34
  prop_accessor :count, Float
36
35
  end
@@ -0,0 +1,62 @@
1
+ #! /usr/bin/env ruby
2
+ # vim:sw=2:ai
3
+
4
+ # code:
5
+ # * Thomas Quas <tquas@yahoo.com>
6
+ # * George Moschovitis <gm@navel.gr>
7
+ #
8
+ # $Id$
9
+
10
+ $LOAD_PATH.unshift 'lib'
11
+
12
+ require "test/unit"
13
+ require 'glue/property'
14
+
15
+ module Mixin
16
+ prop_accessor :date
17
+ end
18
+
19
+ class MixedOnly
20
+ include Mixin
21
+ end
22
+
23
+ class MixedWithProp
24
+ include Mixin
25
+
26
+ prop_accessor :dummy
27
+ end
28
+
29
+ class Pure
30
+ prop_accessor :dummy
31
+ end
32
+
33
+ class Empty
34
+ end
35
+
36
+
37
+ # Tests auto management.
38
+ class TC_MixinsTest < ::Test::Unit::TestCase
39
+ def test_the_unmanaged
40
+ assert( Empty.respond_to?( :__props ) == false )
41
+ assert_respond_to( Pure.new, :dummy )
42
+ end
43
+
44
+ def test_manage_entities
45
+ assert_respond_to( Pure, :__props )
46
+ assert_respond_to( Pure.new, :dummy )
47
+ end
48
+
49
+ def test_managing_mixedonly_classes
50
+ assert_respond_to( MixedOnly.new, :date )
51
+ end
52
+
53
+ def test_managing_mixins
54
+ assert_respond_to( Mixin, :__props )
55
+ end
56
+
57
+ def test_managing_mixed_classes
58
+ obj = MixedWithProp.new
59
+ assert( obj.respond_to?( :date ) )
60
+ assert( obj.respond_to?( :dummy ) )
61
+ end
62
+ end
@@ -0,0 +1,107 @@
1
+ #! /usr/bin/env ruby
2
+ # vim:sw=2:ai
3
+
4
+ # code:
5
+ # * Thomas Quas <tquas@yahoo.com>
6
+ # * George Moschovitis <gm@navel.gr>
7
+ #
8
+ # $Id$
9
+
10
+ $LOAD_PATH.unshift 'lib'
11
+
12
+ require "test/unit"
13
+ require 'glue/logger'
14
+ require 'og/mock'
15
+
16
+ $DBG = false
17
+ $log = Logger.new( STDERR ) unless $log
18
+
19
+ class Dummy
20
+ prop_accessor :date
21
+ attr_reader :call_stack
22
+
23
+ def og_pre_insert( oid )
24
+ @call_stack << "pre_insert"
25
+ end
26
+
27
+ def og_post_insert( oid )
28
+ @call_stack << "post_insert"
29
+ end
30
+
31
+ def og_pre_update( oid )
32
+ @call_stack << "pre_update"
33
+ end
34
+
35
+ def og_post_update( oid )
36
+ @call_stack << "post_update"
37
+ end
38
+
39
+ def self.og_pre_delete( conn, oid )
40
+ raise "undeletable"
41
+ end
42
+
43
+ def initialize
44
+ @call_stack = []
45
+ end
46
+ end
47
+
48
+ # Tests the Og managed objects lifecycle.
49
+ class TC_CallbackTest < ::Test::Unit::TestCase
50
+ def test_insert
51
+ obj = Dummy.new
52
+ obj.save!
53
+
54
+ assert( obj.call_stack.shift == "pre_insert" )
55
+ assert( obj.call_stack.shift == "post_insert" )
56
+ assert( obj.call_stack.empty? )
57
+ end
58
+
59
+ def test_update
60
+ obj = Dummy.new
61
+ obj.save!
62
+ obj.call_stack.shift
63
+ obj.call_stack.shift
64
+
65
+ obj.date = Time.now
66
+ obj.save
67
+ assert( obj.call_stack.shift == "pre_update" )
68
+ assert( obj.call_stack.shift == "post_update" )
69
+ assert( obj.call_stack.empty? )
70
+ end
71
+
72
+ def test_delete
73
+ obj = Dummy.new
74
+ obj.save!
75
+ obj.call_stack.shift
76
+ obj.call_stack.shift
77
+
78
+ assert_raise( RuntimeError, "undeletable" ) { obj.delete! }
79
+ end
80
+
81
+ def setup
82
+ psql = true
83
+
84
+ if psql
85
+ config = {
86
+ :backend => "psql",
87
+ :address => "localhost",
88
+ :database => "test",
89
+ :user => "postgres",
90
+ :password => "navelrulez",
91
+ :connection_count => 1
92
+ }
93
+ else
94
+ config = {
95
+ :backend => "mysql",
96
+ :address => "localhost",
97
+ :database => "test",
98
+ :user => "root",
99
+ :password => "navelrulez",
100
+ :connection_count => 1
101
+ }
102
+ end
103
+ Og::Database.drop_db!( config )
104
+ $og = Og::Database.new( config )
105
+ $og.get_connection()
106
+ end
107
+ end
data/test/tc_og.rb CHANGED
@@ -46,7 +46,7 @@ class Article
46
46
  end
47
47
 
48
48
  class Comment
49
- belongs_to :article, Test::Article
49
+ belongs_to :article, Test::Article, :extra_sql => 'NOT NULL'
50
50
  belongs_to :author, Test::User
51
51
  prop_accessor :body, String
52
52
  prop_accessor :create_time, Time
@@ -65,6 +65,14 @@ class Comment
65
65
  end
66
66
  end
67
67
 
68
+ module MyMixin
69
+ prop_accessor :title, String
70
+ end
71
+
72
+ class MyClass
73
+ include Test::MyMixin
74
+ end
75
+
68
76
  class TC_N_OG < Test::Unit::TestCase
69
77
 
70
78
  def setup
@@ -148,16 +156,17 @@ class TC_N_OG < Test::Unit::TestCase
148
156
 
149
157
  article = Article.new("Title", "Body")
150
158
  article.save!
151
-
159
+
152
160
  comment = Comment.new("This is a comment")
153
161
  comment.article = article
154
162
  comment.author = User["gmosx"]
155
163
  comment.save!
156
164
 
165
+
166
+ # test automatically generated add_commnet
157
167
  comment = Comment.new("This is another comment")
158
- comment.article = article.oid
159
168
  comment.author = User["gmosx"]
160
- comment.save!
169
+ article.add_comment(comment)
161
170
 
162
171
  assert_equal(article.oid, comment.article_oid)
163
172
 
@@ -172,6 +181,24 @@ class TC_N_OG < Test::Unit::TestCase
172
181
  assert_equal(nil, Comment.all)
173
182
 
174
183
  comment.delete!
184
+
185
+ # test extra_sql
186
+
187
+ for p in Comment.__props
188
+ if :article_oid == p.symbol
189
+ assert_equal('NOT NULL', p.meta[:extra_sql])
190
+ break
191
+ end
192
+ end
193
+
194
+ assert($og.managed_classes.include?(Test::Article))
195
+
196
+ # bug: indirectly managed (includes managed Module)
197
+ assert($og.managed_classes.include?(Test::MyClass))
198
+
199
+ # test create
200
+ article = Article.create("title", "body")
201
+ assert_equal(3, article.oid)
175
202
  end
176
203
 
177
204
  end
data/vendor/README ADDED
@@ -0,0 +1,6 @@
1
+ Libraries from other vendors belong here.
2
+
3
+ extensions
4
+ breakpointer
5
+ dev-utils
6
+ postgres-pr
@@ -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) rescue nil
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) if result
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