rubybreaker 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/NEWS +3 -0
  2. data/README.md +1 -2
  3. data/Rakefile +1 -0
  4. data/TODO +0 -8
  5. data/TUTORIAL.md +33 -7
  6. data/VERSION +1 -1
  7. data/lib/rubybreaker.rb +12 -4
  8. data/lib/rubybreaker/debug/error.rb +1 -1
  9. data/lib/rubybreaker/runtime.rb +16 -12
  10. data/lib/rubybreaker/runtime/monitor.rb +116 -84
  11. data/lib/rubybreaker/runtime/pluggable.rb +8 -2
  12. data/lib/rubybreaker/runtime/type_system.rb +77 -10
  13. data/lib/rubybreaker/task.rb +6 -0
  14. data/lib/rubybreaker/util.rb +23 -0
  15. data/test/integrated/tc_checking.rb +71 -0
  16. data/test/integrated/tc_class_methods.rb +14 -4
  17. data/test/testtask/sample.rb +1 -0
  18. data/test/testtask/tc_testtask.rb +14 -2
  19. data/test/ts_integrated.rb +1 -0
  20. data/webpage/index.html +1 -2
  21. data/webpage/rdoc/Object.html +0 -2
  22. data/webpage/rdoc/Rake.html +0 -2
  23. data/webpage/rdoc/Rake/RubyBreakerTestTask.html +19 -5
  24. data/webpage/rdoc/RubyBreaker.html +35 -3
  25. data/webpage/rdoc/RubyBreaker/Breakable.html +1 -3
  26. data/webpage/rdoc/RubyBreaker/Broken.html +1 -3
  27. data/webpage/rdoc/RubyBreaker/Context.html +0 -2
  28. data/webpage/rdoc/RubyBreaker/Errors.html +0 -2
  29. data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +0 -2
  30. data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +0 -2
  31. data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +0 -2
  32. data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +0 -2
  33. data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +0 -2
  34. data/webpage/rdoc/RubyBreaker/Errors/UserError.html +2 -4
  35. data/webpage/rdoc/RubyBreaker/ObjectPosition.html +0 -2
  36. data/webpage/rdoc/RubyBreaker/Position.html +0 -2
  37. data/webpage/rdoc/RubyBreaker/RDocSupport.html +0 -2
  38. data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +0 -2
  39. data/webpage/rdoc/RubyBreaker/Runtime.html +39 -6
  40. data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +0 -2
  41. data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +0 -2
  42. data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +295 -28
  43. data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +30 -43
  44. data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +4 -6
  45. data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +0 -2
  46. data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +78 -18
  47. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +0 -2
  48. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigUnparser.html +0 -2
  49. data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +172 -28
  50. data/webpage/rdoc/RubyBreaker/TypeComparer.html +0 -2
  51. data/webpage/rdoc/RubyBreaker/TypeDefs.html +0 -2
  52. data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +0 -2
  53. data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +0 -2
  54. data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +0 -2
  55. data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +0 -2
  56. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +0 -2
  57. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +0 -2
  58. data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +0 -2
  59. data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +0 -2
  60. data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +0 -2
  61. data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +0 -2
  62. data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +0 -2
  63. data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +0 -2
  64. data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +0 -2
  65. data/webpage/rdoc/RubyBreaker/TypeUnparser.html +0 -2
  66. data/webpage/rdoc/RubyBreaker/Typing.html +0 -2
  67. data/webpage/rdoc/RubyBreaker/Util.html +80 -4
  68. data/webpage/rdoc/Test.html +0 -2
  69. data/webpage/rdoc/Test/Unit.html +0 -2
  70. data/webpage/rdoc/created.rid +12 -12
  71. data/webpage/rdoc/index.html +0 -2
  72. data/webpage/rdoc/js/search_index.js +1 -1
  73. data/webpage/rdoc/table_of_contents.html +53 -34
  74. data/webpage/tutorial.html +36 -7
  75. metadata +8 -11
  76. data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +0 -402
data/NEWS CHANGED
@@ -1,3 +1,6 @@
1
+ # VERSION 0.0.7
2
+ * Early dynamic type check is implemented.
3
+
1
4
  # VERSION 0.0.6
2
5
  * Running RubyBreaker in shell mode does not require manual code change.
3
6
  * Official RubyBreaker logo!
data/README.md CHANGED
@@ -19,14 +19,13 @@ and effectively.
19
19
  Currently, RubyBreaker *cannot*
20
20
 
21
21
  * Auto-document block arguments (inherent)
22
- * Perform early dynamic type checks
23
22
  * Support parametric polymorphic types
24
23
  * Support RDoc or YARD output format
25
24
 
26
25
  To contribute to the project, visit RubyBreaker's
27
26
  [GitHub page](http://github.com/rockalizer/rubybreaker) and
28
27
  [RubyGems page](http://rubygems.org/gems/rubybreaker). The web version of
29
- this document can be found
28
+ this document and the tutorial can be found
30
29
  [here](http://rockalizer.webfactional.com/projects/rubybreaker).
31
30
 
32
31
  ## Requirements
data/Rakefile CHANGED
@@ -99,6 +99,7 @@ Rake::RubyBreakerTestTask.new(:"testtask_test") do |t|
99
99
  t.libs << "lib" << "test/tc_testtask/sample.rb"
100
100
  t.test_files = ["test/testtask/tc_testtask.rb"]
101
101
  t.break = ["SampleClassA"]
102
+ t.check = ["SampleClassB"]
102
103
  end
103
104
 
104
105
  if defined?(RSpec)
data/TODO CHANGED
@@ -1,11 +1,3 @@
1
- # Dynamic Type Checker
2
-
3
- Type documentation generated by RubyBreaker can be more useful if the types
4
- are actually enforced during runtime. In order to do this, each method has
5
- to be dynamically instrumented (as "breakable" methods are) and inspected
6
- for each argument type and return type. Although this is still a dynamic
7
- type checking, it finds type errors earlier in the program execution.
8
-
9
1
  # Additional Testing Frameworks
10
2
 
11
3
  RubyBreaker supports the built-in testing framework and RSpec. There are
data/TUTORIAL.md CHANGED
@@ -19,7 +19,7 @@ to use RubyBreaker in a Rakefile later.
19
19
 
20
20
  The above command runs RubyBreaker in verbose mode (`-v`) and will display
21
21
  the output on the screen (`-s`). Before RubyBreaker runs `prog.rb`, it will
22
- import (`-l`) `lib.rb` and instrument (`-b`) classes `A` and `B`.
22
+ import (`-l`) `lib.rb` and _break_ (`-b`) classes `A` and `B`.
23
23
  Here is `lib.rb`:
24
24
 
25
25
  class A
@@ -70,7 +70,7 @@ obtain precise and accurate type information.
70
70
  ### Using Ruby Unit Testing Framework
71
71
 
72
72
  Instead of manually inserting the entry point indicator in the source
73
- program, you can take advantage of Ruby's built-in testing framework. This
73
+ program, you can take advantage of Ruby&rsquo;s built-in testing framework. This
74
74
  is preferred to modifying the source program directly, especially for the
75
75
  long term program maintainability. But no worries! This method is as simple
76
76
  as the previous one.
@@ -85,7 +85,7 @@ as the previous one.
85
85
  # ...tests!...
86
86
  end
87
87
 
88
- That's it! The only requirements are to indicate to RubyBreaker which modules
88
+ That&rsquo;s it! The only requirements are to indicate to RubyBreaker which modules
89
89
  and classes to "break" and to place `require rubybreaker` _after_
90
90
  `require test/unit`.
91
91
 
@@ -124,7 +124,7 @@ RubyBreaker. The following code snippet describes how it can be done:
124
124
  Note that `RubyBrakerTestTask` can simply replace your `TestTask` block in
125
125
  Rakefile. In fact, the former is a subclass of the latter and includes all
126
126
  features supported by the latter. The only additional options are
127
- `rubybreaker_opts` which is RubyBreaker's command-line options and
127
+ `rubybreaker_opts` which is RubyBreaker command-line options and
128
128
  `break` which specifies which modules and classes to monitor. Since
129
129
  `Class1` and `Class2` are not _recognized_ by this Rakefile, you must use
130
130
  string literals to specify modules and classes (and with full namespace).
@@ -184,7 +184,7 @@ precise return type.
184
184
 
185
185
  ### Duck Type
186
186
 
187
- This type is inspired by the Ruby Language's duck typing, _"if it
187
+ This type is inspired by the Ruby Language&rsquo;s duck typing, _"if it
188
188
  walks like a duck and quacks like a duck, it must be a duck."_ Using this
189
189
  type, an object can be represented simply by a list of method names. For
190
190
  example `[walks, quacks]` is an object that has `walks` and `quacks`
@@ -231,7 +231,7 @@ suffices, `?` and `*`, respectively.
231
231
 
232
232
  ### Block Type
233
233
 
234
- One of the Ruby's prominent features is the block argument. It allows
234
+ One of the Ruby&rsquo;s prominent features is the block argument. It allows
235
235
  the caller to pass in a piece of code to be executed inside the callee. This
236
236
  code block can be executed by the Ruby construct, `yield`, or by directly
237
237
  calling the `call` method of the block object. In RubyBreaker, this type can
@@ -263,7 +263,7 @@ code:
263
263
  end
264
264
 
265
265
  There is no way to document the type of `foo` without using a method list
266
- type. Let's try to give a method type to `foo` without a method list. The
266
+ type. Let&rsquo;s try to give a method type to `foo` without a method list. The
267
267
  closest we can come up with would be `foo(fixnum or string) -> fixnum and
268
268
  string`. But RubyBreaker does not have the "and" type in the type annotation
269
269
  language because it gives me an headache! (By the way, it needs to be an
@@ -289,3 +289,29 @@ compatibility between the return types and "promote" the method type to a
289
289
  method list type by spliting the type signature into two (or more in
290
290
  subsequent "promotions").
291
291
 
292
+ ### Early Dynamic Type Checking
293
+
294
+ RubyBreaker gives you an option to run the early dynamic type check. If a
295
+ method is documented and its module is specified to be _type checked_, then
296
+ RubyBreaker will verify if the argument types and the return type are
297
+ appropriate at runtime. To allow this feature in command-line, use `-c`
298
+ (checking) option:
299
+
300
+ rubybreaker -l lib.rb -c A prog.rb
301
+
302
+ Or use it in Rakefile:
303
+
304
+ require "rubybreaker/task"
305
+ ...
306
+ desc "Run RubyBreaker"
307
+ Rake::RubyBreakerTestTask.new(:"rubybreaker") do |t|
308
+ t.libs << "lib"
309
+ t.test_files = ["test/foo/tc_foo1.rb"]
310
+ # ...Other test task options..
311
+ t.rubybreaker_opts << "-v" # run in verbose mode
312
+ t.break = ["Class1", "Class2", ...] # specify classes to monitor
313
+ t.check = ["Class3", ....] # specify classes to check
314
+ end
315
+
316
+ Alternatively, you can use `RubyBreaker.check()` to specify classes to type
317
+ check in the setup code of the test case.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.0.7
data/lib/rubybreaker.rb CHANGED
@@ -25,10 +25,11 @@ module RubyBreaker
25
25
  :append => false, # append to the input file (if there is)?
26
26
  :stdout => false, # also display on the screen?
27
27
  :verbose => false, # in RubyBreaker.verbose mode?
28
- :save_output => true, # save output to a file?
29
- :break => [], # modules to break
30
- :libs => [], # list of library files to import
31
- :prog => nil, # program or test file
28
+ :save_output => true, # save output to a file?
29
+ :break => [], # modules/classes to break
30
+ :check => [], # modules/classes to check
31
+ :libs => [], # list of library files to import
32
+ :prog => nil, # program or test file
32
33
  }
33
34
 
34
35
  # This option parser may be used for the command-line mode or for the
@@ -43,6 +44,11 @@ module RubyBreaker
43
44
  tokens.each {|t| OPTIONS[:break] << t}
44
45
  end
45
46
 
47
+ opts.on("-c MODULES", "--check MODULES", "Specify modules/classes to check") do |s|
48
+ tokens = s.split(/[;,]/)
49
+ tokens.each {|t| OPTIONS[:check] << t}
50
+ end
51
+
46
52
  opts.on("-l LIBRARIES", "--libs LIBRARIES", "Specify libraries to load") do |s|
47
53
  tokens = s.split(":")
48
54
  tokens.each {|t| OPTIONS[:libs] << t}
@@ -172,12 +178,14 @@ module RubyBreaker
172
178
  task = self.task
173
179
  OPTION_PARSER.parse(*task[:rubybreaker_opts])
174
180
  Runtime.break(*task[:break])
181
+ Runtime.check(*task[:check])
175
182
  task_name = task[:name]
176
183
  RubyBreaker.verbose("Done reading task information")
177
184
  io_file = self.io_file(task_name)
178
185
  elsif OPTIONS[:prog] # running in shell mode
179
186
  Runtime.break(*mods) # should not happen but for backward-compatibility
180
187
  Runtime.break(*OPTIONS[:break])
188
+ Runtime.check(*OPTIONS[:check])
181
189
  io_file = self.io_file(OPTIONS[:prog_file])
182
190
  else
183
191
  # Otherwise, assume there are no explicit IO files.
@@ -41,7 +41,7 @@ module RubyBreaker
41
41
  # program.
42
42
  class UserError < ::Exception
43
43
 
44
- def initialize(msg, ctx)
44
+ def initialize(msg, ctx=nil)
45
45
  super(msg)
46
46
  @ctx = ctx
47
47
  end
@@ -10,20 +10,13 @@ require_relative "runtime/inspector"
10
10
 
11
11
  module RubyBreaker
12
12
 
13
+ # This module contains things that are needed at runtime.
13
14
  module Runtime
14
15
 
15
16
  # This set keeps track of modules/classes that will be monitored.
16
17
  # *DEPRECATED* : Use +breakable+ method instead.
17
18
  BREAKABLES = Set.new
18
19
 
19
- # This hash maps a module to a nested hash that maps a method name to a
20
- # method type. This hash is shared between breakable modules/classes and
21
- # non-breakable modules/classes.
22
- TYPE_MAP = {} # module => {:meth_name => type}
23
-
24
- # This hash maps a (breakable) module to a type monitor
25
- MONITOR_MAP = {} # module => monitor
26
-
27
20
  private
28
21
 
29
22
  # Instruments the monitor to the specified modules/classes.
@@ -36,9 +29,10 @@ module RubyBreaker
36
29
  when Array
37
30
  self.install(monitor_type, *mod)
38
31
  when Module, Class
39
- MonitorInstaller.install_monitor(mod)
32
+ # Install both instance and its eigen class
33
+ MonitorInstaller.install_monitor(monitor_type, mod)
40
34
  eigen_class = self.eigen_class(mod)
41
- MonitorInstaller.install_monitor(eigen_class)
35
+ MonitorInstaller.install_monitor(monitor_type, eigen_class)
42
36
  when String, Symbol
43
37
  begin
44
38
  # Get the actual module and install it right now
@@ -56,17 +50,23 @@ module RubyBreaker
56
50
  public
57
51
 
58
52
  # This method instruments the specified modules/classes at the time of
59
- # the call.
53
+ # the call so they are monitored for type documentation.
60
54
  def self.break(*mods)
61
55
  self.install(:break, *mods)
62
56
  end
63
57
 
58
+ # This method instruments the specified modules/classes at the time of
59
+ # the call so that they are type checked during runtime.
60
+ def self.check(*mods)
61
+ self.install(:check, *mods)
62
+ end
63
+
64
64
  # This method installs a monitor for each breakable module.
65
65
  # *DEPRECATED*: Use +breakable()+ method instead.
66
66
  def self.instrument()
67
67
  BREAKABLES.each do |mod|
68
68
  # Duplicate checks in place in these calls.
69
- MonitorInstaller.install_monitor(mod)
69
+ MonitorInstaller.install_monitor(:break, mod)
70
70
  end
71
71
  end
72
72
 
@@ -94,6 +94,10 @@ module RubyBreaker
94
94
  Runtime.break(*mods)
95
95
  end
96
96
 
97
+ def self.check(*mods)
98
+ Runtime.check(*mods)
99
+ end
100
+
97
101
  # *DEPRECATED*: Use +Runtime.breakable()+ or +RubyBreaker.run()+ method
98
102
  # instead.
99
103
  module Breakable
@@ -13,6 +13,15 @@ module RubyBreaker
13
13
 
14
14
  module Runtime
15
15
 
16
+ # This hash maps a module to a nested hash that maps a method name to a
17
+ # method type. This hash is shared between breakable modules/classes and
18
+ # non-breakable modules/classes.
19
+ TYPE_MAP = {} # module => {:meth_name => type}
20
+
21
+ # This hash maps a (breakable) module to a type monitor
22
+ MONITOR_MAP = {} # module => monitor
23
+
24
+ # The default type system for RubyBreaker
16
25
  DEFAULT_TYPE_SYSTEM = TypeSystem.new
17
26
 
18
27
  # This class monitors method calls before and after. It simply reroutes
@@ -20,73 +29,12 @@ module RubyBreaker
20
29
  # the actual work of gathering type information.
21
30
  class Monitor
22
31
 
23
- # attr_accessor :mod
24
32
  attr_accessor :pluggable
25
33
 
26
- public
27
-
28
- def initialize(mod, pluggable)
29
- # @mod = mod
30
- @pluggable = pluggable
31
- end
32
-
33
- # Starts monitoring of a method; it wraps each argument so that they
34
- # can gather type information in the callee.
35
- def monitor_before_method(obj, meth_info)
36
- @pluggable.before_method(obj, meth_info)
37
- end
38
-
39
- # This method is invoked after the actual method is invoked.
40
- def monitor_after_method(obj, meth_info)
41
- @pluggable.after_method(obj, meth_info)
42
- end
43
-
44
- end
45
-
46
- # This class is a switch to turn on and off the type monitoring system.
47
- # It is important to turn off the monitor once the process is inside the
48
- # monitor; otherwise, it WILL fall into an infinite loop.
49
- class MonitorSwitch
50
-
51
- attr_accessor :switch
52
-
53
- def initialize(); @switch = true end
54
-
55
- def turn_on();
56
- RubyBreaker.log("Switch turned on")
57
- @switch = true;
58
- end
59
-
60
- def turn_off();
61
- RubyBreaker.log("Switch turned off")
62
- @switch = false;
63
- end
64
-
65
- def set_to(mode); @switch = mode; end
66
- end
67
-
68
- # TODO:For now, we use a global switch; but in future, a switch per
69
- # object should be used for multi-process apps. However, there is still
70
- # a concern for module tracking in which case, there isn't really a way
71
- # to do this unless we track with the process or some unique id for that
72
- # process.
73
- GLOBAL_MONITOR_SWITCH = MonitorSwitch.new
74
-
75
- # This context is used for keeping track of context in the user code.
76
- # This will ignore the context within the RubyBreaker code, so it is
77
- # easy to pinpoint program locations without distraction from the
78
- # RubyBreaker code.
79
- CONTEXT = Context.new(ObjectPosition.new(self,"main"))
80
-
81
- # This module contains helper methods for monitoring objects and
82
- # modules.
83
- module MonitorUtils
84
-
85
- public
86
-
87
34
  # This will do the actual routing work for a particular "monitored"
88
35
  # method call.
89
36
  #
37
+ # route_type:: :break or :check
90
38
  # obj:: is the object receiving the message; is never wrapped object
91
39
  # meth_name:: is the original method name being called args:: is a
92
40
  # list of arguments for the original method call blk:: is the block
@@ -96,7 +44,7 @@ module RubyBreaker
96
44
  # NOTE: This method should not assume that obj is a monitored
97
45
  # object. That is, no special method should be called to obj unless it
98
46
  # checks first.
99
- def self.route(obj,meth_name,*args,&blk)
47
+ def self.route(route_type, obj, meth_name, *args, &blk)
100
48
 
101
49
  # remember the switch mode before turning it off
102
50
  switch = GLOBAL_MONITOR_SWITCH.switch
@@ -138,9 +86,21 @@ module RubyBreaker
138
86
 
139
87
  meth_info = MethodInfo.new(meth_name, args, blk, nil)
140
88
 
141
- mm.monitor_before_method(obj, meth_info)
89
+ begin
90
+ case route_type
91
+ when :break
92
+ mm.break_before_method(obj, meth_info)
93
+ when :check
94
+ mm.check_before_method(obj, meth_info)
95
+ end
96
+ rescue Errors::TypeError => e
97
+ # Trap it, turn on the global monitor and then re-raise the
98
+ # exception
99
+ GLOBAL_MONITOR_SWITCH.turn_on()
100
+ raise e
101
+ end
142
102
 
143
- RubyBreaker.log("monitor_before_method ended")
103
+ RubyBreaker.log("break_before_method ended")
144
104
 
145
105
  # we are going to turn the switch back on
146
106
  GLOBAL_MONITOR_SWITCH.turn_on()
@@ -153,7 +113,21 @@ module RubyBreaker
153
113
  GLOBAL_MONITOR_SWITCH.turn_off()
154
114
 
155
115
  meth_info.ret = retval
156
- mm.monitor_after_method(obj, meth_info)
116
+
117
+ begin
118
+ case route_type
119
+ when :break
120
+ mm.break_after_method(obj, meth_info)
121
+ when :check
122
+ mm.check_after_method(obj, meth_info)
123
+ end
124
+ rescue Errors::TypeError => e
125
+ # Trap it, turn on the global monitor and then re-raise the
126
+ # exception
127
+ GLOBAL_MONITOR_SWITCH.turn_on()
128
+ raise e
129
+ end
130
+
157
131
  retval = meth_info.ret # Return value may have been altered by the
158
132
  # after_method monitoring code
159
133
 
@@ -177,36 +151,91 @@ module RubyBreaker
177
151
  return meth_name[2..-1]
178
152
  end
179
153
 
154
+ def initialize(pluggable)
155
+ @pluggable = pluggable
156
+ end
157
+
158
+ # This method is invoked before the original method is executed.
159
+ def check_before_method(obj, meth_info)
160
+ @pluggable.check_before_method(obj, meth_info)
161
+ end
162
+
163
+ # This method is invoked after the original method is executed.
164
+ def check_after_method(obj, meth_info)
165
+ @pluggable.check_after_method(obj, meth_info)
166
+ end
167
+
168
+ # This method is invoked before the original method is executed.
169
+ def break_before_method(obj, meth_info)
170
+ @pluggable.break_before_method(obj, meth_info)
171
+ end
172
+
173
+ # This method is invoked after the original method is executed.
174
+ def break_after_method(obj, meth_info)
175
+ @pluggable.break_after_method(obj, meth_info)
176
+ end
177
+
180
178
  end
181
179
 
180
+ # This class is a switch to turn on and off the type monitoring system.
181
+ # It is important to turn off the monitor once the process is inside the
182
+ # monitor; otherwise, it WILL fall into an infinite loop.
183
+ class MonitorSwitch
184
+
185
+ attr_accessor :switch
186
+
187
+ def initialize(); @switch = true end
188
+
189
+ def turn_on();
190
+ RubyBreaker.log("Switch turned on")
191
+ @switch = true;
192
+ end
193
+
194
+ def turn_off();
195
+ RubyBreaker.log("Switch turned off")
196
+ @switch = false;
197
+ end
198
+
199
+ def set_to(mode); @switch = mode; end
200
+ end
201
+
202
+ # TODO:For now, we use a global switch; but in future, a switch per
203
+ # object should be used for multi-process apps. However, there is still
204
+ # a concern for module tracking in which case, there isn't really a way
205
+ # to do this unless we track with the process or some unique id for that
206
+ # process.
207
+ GLOBAL_MONITOR_SWITCH = MonitorSwitch.new
208
+
209
+ # This context is used for keeping track of context in the user code.
210
+ # This will ignore the context within the RubyBreaker code, so it is
211
+ # easy to pinpoint program locations without distraction from the
212
+ # RubyBreaker code.
213
+ CONTEXT = Context.new(ObjectPosition.new(self,"main"))
214
+
182
215
  # This module installs a monitor in the object.
183
216
  module MonitorInstaller
184
217
 
185
- include MonitorUtils
186
-
187
218
  # returns true if the receiver is a module or a class
188
- def self.is_module?(recv)
189
- return recv.respond_to?(:class) && recv.kind_of?(Module)
219
+ def self.is_module?(mod)
220
+ return mod.respond_to?(:class) && mod.kind_of?(Module)
190
221
  end
191
222
 
192
223
  # renames the method in essence; this method also "installs" the
193
224
  # module monitor for the class
194
- def self.rename_meth(recv, meth_name)
195
- alt_meth_name = MonitorUtils.get_alt_meth_name(meth_name)
196
- recv.module_eval("alias :\"#{alt_meth_name}\" :\"#{meth_name}\"")
225
+ def self.monkey_patch_meth(monitor_type, mod, meth_name)
226
+ alt_meth_name = Monitor.get_alt_meth_name(meth_name)
227
+ mod.module_eval("alias :\"#{alt_meth_name}\" :\"#{meth_name}\"")
197
228
  RubyBreaker.log("Adding alternate method for #{meth_name}")
198
- recv.module_eval <<-EOF
229
+ route_call = "RubyBreaker::Runtime::Monitor.route"
230
+ mod.module_eval <<-EOF
199
231
  def #{meth_name}(*args, &blk)
200
- RubyBreaker::Runtime::MonitorUtils.route(self,
201
- "#{meth_name}",
202
- *args,
203
- &blk)
232
+ #{route_call}(:#{monitor_type}, self,"#{meth_name}",*args,&blk)
204
233
  end
205
234
  EOF
206
235
  end
207
236
 
208
237
  # Installs an module (class) monitor to the object.
209
- def self.install_monitor(mod)
238
+ def self.install_monitor(monitor_type, mod)
210
239
 
211
240
  RubyBreaker.log("Installing module monitor for #{mod}")
212
241
 
@@ -216,7 +245,7 @@ module RubyBreaker
216
245
  return
217
246
  end
218
247
 
219
- MONITOR_MAP[mod] = Monitor.new(mod, DEFAULT_TYPE_SYSTEM)
248
+ MONITOR_MAP[mod] = Monitor.new(DEFAULT_TYPE_SYSTEM)
220
249
 
221
250
  # Create the type map if it does not exist already. Remember, this
222
251
  # map could have been made by typesig().
@@ -226,12 +255,15 @@ module RubyBreaker
226
255
  # methods. Those are part of the owner's not this module.
227
256
  meths = mod.instance_methods(false)
228
257
 
229
- # See if any method is already broken (explicitly typesig'ed)
230
- broken_mt_map = Inspector.inspect_all(mod)
231
- broken_meths = broken_mt_map.keys
258
+ # See if any method is already documented (explicitly typesig'ed)
259
+ doc_mt_map = Inspector.inspect_all(mod)
260
+ doc_meths = doc_mt_map.keys
232
261
 
233
262
  meths.each do |m|
234
- self.rename_meth(mod,m) unless broken_meths.include?(m)
263
+ # Documented method will not be monkey-patched for "breaking"
264
+ unless monitor_type == :break && doc_meths.include?(m)
265
+ self.monkey_patch_meth(monitor_type, mod, m)
266
+ end
235
267
  end
236
268
 
237
269
  end