rubybreaker 0.0.5 → 0.0.6

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.
Files changed (90) hide show
  1. data/ABOUT.md +20 -0
  2. data/NEWS +5 -0
  3. data/README.md +16 -352
  4. data/Rakefile +30 -16
  5. data/TOPICS.md +55 -0
  6. data/TUTORIAL.md +291 -0
  7. data/VERSION +1 -1
  8. data/bin/rubybreaker +32 -14
  9. data/lib/rubybreaker/runtime/monitor.rb +1 -1
  10. data/lib/rubybreaker/runtime.rb +41 -21
  11. data/lib/rubybreaker/task.rb +15 -9
  12. data/lib/rubybreaker/test/rspec.rb +3 -3
  13. data/lib/rubybreaker/test/testcase.rb +3 -3
  14. data/lib/rubybreaker.rb +31 -16
  15. data/test/integrated/{tc_both_broken_breakable.rb → tc_both_documented_and_undocumented.rb} +3 -4
  16. data/test/integrated/tc_class_methods.rb +1 -1
  17. data/test/integrated/tc_inherit_broken.rb +1 -1
  18. data/test/integrated/tc_method_missing.rb +1 -1
  19. data/test/integrated/tc_namespace.rb +1 -1
  20. data/test/integrated/tc_simple1.rb +1 -1
  21. data/test/testtask/tc_testtask.rb +2 -2
  22. data/test/ts_integrated.rb +1 -1
  23. data/test/ts_rspec.rb +1 -1
  24. data/webpage/about.html +50 -0
  25. data/webpage/footer.html +6 -1
  26. data/webpage/header.html +9 -3
  27. data/webpage/images/logo.png +0 -0
  28. data/webpage/images/title.png +0 -0
  29. data/webpage/index.html +31 -367
  30. data/webpage/rdoc/Object.html +3 -103
  31. data/webpage/rdoc/Rake/RubyBreakerTestTask.html +80 -18
  32. data/webpage/rdoc/Rake.html +3 -7
  33. data/webpage/rdoc/RubyBreaker/Breakable.html +4 -8
  34. data/webpage/rdoc/RubyBreaker/Broken.html +4 -8
  35. data/webpage/rdoc/RubyBreaker/Context.html +3 -7
  36. data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +3 -7
  37. data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +3 -7
  38. data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +3 -7
  39. data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +3 -7
  40. data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +3 -7
  41. data/webpage/rdoc/RubyBreaker/Errors/UserError.html +3 -7
  42. data/webpage/rdoc/RubyBreaker/Errors.html +3 -7
  43. data/webpage/rdoc/RubyBreaker/ObjectPosition.html +3 -7
  44. data/webpage/rdoc/RubyBreaker/Position.html +3 -7
  45. data/webpage/rdoc/RubyBreaker/RDocSupport.html +3 -7
  46. data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +3 -7
  47. data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +3 -7
  48. data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +3 -7
  49. data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +3 -7
  50. data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +10 -14
  51. data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +3 -7
  52. data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +3 -7
  53. data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +3 -7
  54. data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +3 -7
  55. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +3 -7
  56. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigUnparser.html +3 -7
  57. data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +3 -7
  58. data/webpage/rdoc/RubyBreaker/Runtime.html +42 -39
  59. data/webpage/rdoc/RubyBreaker/TypeComparer.html +3 -7
  60. data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +3 -7
  61. data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +3 -7
  62. data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +3 -7
  63. data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +3 -7
  64. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +3 -7
  65. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +3 -7
  66. data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +3 -7
  67. data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +3 -7
  68. data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +3 -7
  69. data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +3 -7
  70. data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +3 -7
  71. data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +3 -7
  72. data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +3 -7
  73. data/webpage/rdoc/RubyBreaker/TypeDefs.html +3 -7
  74. data/webpage/rdoc/RubyBreaker/TypeUnparser.html +3 -7
  75. data/webpage/rdoc/RubyBreaker/Typing.html +3 -7
  76. data/webpage/rdoc/RubyBreaker/Util.html +3 -7
  77. data/webpage/rdoc/RubyBreaker.html +48 -15
  78. data/webpage/rdoc/Test/Unit.html +3 -7
  79. data/webpage/rdoc/Test.html +3 -7
  80. data/webpage/rdoc/created.rid +18 -17
  81. data/webpage/rdoc/index.html +3 -7
  82. data/webpage/rdoc/js/search_index.js +1 -1
  83. data/webpage/rdoc/table_of_contents.html +28 -36
  84. data/webpage/rubybreaker.css +8 -6
  85. data/webpage/topics.html +85 -0
  86. data/webpage/tutorial.html +331 -0
  87. metadata +14 -8
  88. data/lib/rubybreaker/doc.rb +0 -3
  89. data/webpage/rdoc/Kernel.html +0 -286
  90. data/webpage/rdoc/Test/Unit/TestCase.html +0 -309
data/TUTORIAL.md ADDED
@@ -0,0 +1,291 @@
1
+ # Tutorial
2
+
3
+ This tutorial will describe the basic usage of the tool, the RubyBreaker
4
+ Type Annotation Language, and the RubyBreaker Type System.
5
+
6
+ ## Usage
7
+
8
+ RubyBreaker takes advantage of test cases that already come with the source
9
+ program. It is recommended that RubyBreaker is run as a Rake task, which
10
+ does require a minimum change in the Rakefile (but no code change in the
11
+ source program) but is better for a long-term maintenance. Regardless of the
12
+ mode you choose to run, no source code change is required.
13
+
14
+ Let us briefly see how RubyBreaker can be run directly as a command-line
15
+ program to understand the overall concept of the tool. We will explain how
16
+ to use RubyBreaker in a Rakefile later.
17
+
18
+ $ rubybreaker -v -s -l lib.rb -b A,B prog.rb
19
+
20
+ The above command runs RubyBreaker in verbose mode (`-v`) and will display
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`.
23
+ Here is `lib.rb`:
24
+
25
+ class A
26
+ def foo(x)
27
+ x.to_s
28
+ end
29
+ end
30
+ class B
31
+ def bar(y,z)
32
+ y.foo(z)
33
+ end
34
+ end
35
+
36
+ And, `prog.rb` simply imports the library file and executes it:
37
+
38
+ require "lib"
39
+ A.new.foo(1)
40
+
41
+ This example will show how `A#foo` method is given a type by RubyBreaker.
42
+ After running the command shown above, the following output will be
43
+ generated and displayed on the screen:
44
+
45
+ class A
46
+ typesig("foo(fixnum[to_s]) -> string")
47
+ end
48
+
49
+ Here, the `typesig` method call registers `foo` as a method type that takes
50
+ an object that has `Fixnum#to_s` method and returns a `String`. This
51
+ method is made available simply by importing `rubybreaker`. Now, assume
52
+ that an additional code, `B.new.bar(A.new,1)`, is added at the end of
53
+ `prog.rb`. The subsequent run will generate the following result:
54
+
55
+ class A
56
+ typesig("foo(fixnum[to_s]) -> string")
57
+ end
58
+ class B
59
+ typesig("bar(a[foo], fixnum[to_s]) -> string")
60
+ end
61
+
62
+ Keep in mind that RubyBreaker is designed to gather type information based
63
+ on the _actual_ execution of the source program. This means the program
64
+ should be equipped with test cases that have a reasonable program path
65
+ coverage. Additionally, RubyBreaker assumes that test runs are correct and
66
+ the program behaves correctly (for those test runs) as intended by the
67
+ programmer. This assumption is not a strong requirement, but is necessary to
68
+ obtain precise and accurate type information.
69
+
70
+ ### Using Ruby Unit Testing Framework
71
+
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
74
+ is preferred to modifying the source program directly, especially for the
75
+ long term program maintainability. But no worries! This method is as simple
76
+ as the previous one.
77
+
78
+ require "test/unit"
79
+ require "rubybreaker" # This should come after test/unit.
80
+ class TestClassA < Test::Unit::TestCase
81
+ def setup()
82
+ RubyBreaker.break(Class1, Class2, ...)
83
+ ...
84
+ end
85
+ # ...tests!...
86
+ end
87
+
88
+ That's it! The only requirements are to indicate to RubyBreaker which modules
89
+ and classes to "break" and to place `require rubybreaker` _after_
90
+ `require test/unit`.
91
+
92
+ ### Using RSpec
93
+
94
+ The requirement is same for RSpec but use `before` instead of `setup` to
95
+ specify which modules and classes to "break".
96
+
97
+ require "rspec"
98
+ require "rubybreaker"
99
+
100
+ describe "TestClassA Test"
101
+ before { RubyBreaker.break(Class1, Class2, ...) }
102
+ ...
103
+ # ...tests!...
104
+ end
105
+
106
+ ### Using Rakefile
107
+
108
+ By running RubyBreaker along with the Rakefile, you can avoid modifying the
109
+ source program at all. (You no longer need to import `rubybreaker` in the
110
+ test cases neither.) Therefore, this is the recommended way to use
111
+ RubyBreaker. The following code snippet describes how it can be done:
112
+
113
+ require "rubybreaker/task"
114
+ ...
115
+ desc "Run RubyBreaker"
116
+ Rake::RubyBreakerTestTask.new(:"rubybreaker") do |t|
117
+ t.libs << "lib"
118
+ t.test_files = ["test/foo/tc_foo1.rb"]
119
+ # ...Other test task options..
120
+ t.rubybreaker_opts << "-v" # run in verbose mode
121
+ t.break = ["Class1", "Class2", ...] # specify what to monitor
122
+ end
123
+
124
+ Note that `RubyBrakerTestTask` can simply replace your `TestTask` block in
125
+ Rakefile. In fact, the former is a subclass of the latter and includes all
126
+ features supported by the latter. The only additional options are
127
+ `rubybreaker_opts` which is RubyBreaker's command-line options and
128
+ `break` which specifies which modules and classes to monitor. Since
129
+ `Class1` and `Class2` are not _recognized_ by this Rakefile, you must use
130
+ string literals to specify modules and classes (and with full namespace).
131
+
132
+ If this is the route you are taking, there needs no editing of the source
133
+ program whatsoever. This task will take care of instrumenting the specified
134
+ modules and classes at proper moments.
135
+
136
+ ## Type Annotation
137
+
138
+ The annotation language used in RubyBreaker resembles the method
139
+ documentation used by Ruby Core Library Doc. Each type signature
140
+ defines a method type using the name, argument types, block type, and return
141
+ type. But, let us consider a simple case where there is one argument type
142
+ and a return type.
143
+
144
+ class A
145
+ ...
146
+ typesig("foo(fixnum) -> string")
147
+ end
148
+
149
+ In RubyBreaker, a type signature is recognized by the meta-class level
150
+ method `typesig` which takes a string as an argument. This string is the
151
+ actual type signature written in the Ruby Type Annotation Language. This
152
+ language is designed to reflect the common documentation practice used by
153
+ Ruby Core Library Doc. It starts with the name of the method. In the
154
+ above example, `foo` is currently being given a type. The rest of the
155
+ signature takes a typical method type symbol, `(x) -> y` where `x` is the
156
+ argument type and `y` is the return type. In the example shown above, the
157
+ method takes a `Fixnum` object and returns a `String` object. Note that
158
+ these types are in lowercase, indicating they are objects and not modules or
159
+ classes themselves.
160
+
161
+ There are several types that represent an object: nominal, duck, fusion,
162
+ nil, 'any', 'or', optional, variable-length, and block. Each type signature
163
+ itself represents a method type or a method list type (explained below).
164
+
165
+ ### Nominal Type
166
+
167
+ This is the simplest and most intuitive way to represent an object. For
168
+ instance, `fixnum` is an object of type `Fixnum`. Use lower-case letters and
169
+ underscores instead of _camelized_ name. `MyClass`, for example would be
170
+ `my_class` in RubyBreaker type signatures. There is no particular
171
+ reason for this convention other than it is the common practice used in
172
+ RubyDoc. Use `/` to indicate the namespace delimiter `::`. For example,
173
+ `NamspaceA::ClassB` would be represented by `namespace_a/class_b` in
174
+ a RubyBreaker type signature.
175
+
176
+ ### Self Type
177
+
178
+ This type is similar to the nominal type but is referring to the current
179
+ object--that is, the receiver of the method being typed. RubyBreaker will
180
+ auto-document the return type as a self type if the return value is the same
181
+ as the receiver of that call. It is also recommended to use this type over
182
+ a nominal type (if the return value is `self`) since it depicts more
183
+ precise return type.
184
+
185
+ ### Duck Type
186
+
187
+ This type is inspired by the Ruby Language's duck typing, _"if it
188
+ walks like a duck and quacks like a duck, it must be a duck."_ Using this
189
+ type, an object can be represented simply by a list of method names. For
190
+ example `[walks, quacks]` is an object that has `walks` and `quacks`
191
+ methods. Note that these method names do *not* reveal any type
192
+ information for themselves.
193
+
194
+ ### Fusion Type
195
+
196
+ Duck type is very flexible but can be too lenient when trying to restrict
197
+ the type of an object. RubyBreaker provides a type called *the fusion type*
198
+ which lists method names but with respect to a nominal type. For
199
+ example, `fixnum[to_f, to_s]` represents an object that has methods `to_f`
200
+ and `to_s` whose types are same as those of `Fixnum`. This is more
201
+ restrictive (precise) than `[to_f, to_s]` because the two methods must have
202
+ the same types as `to_f` and `to_s` methods, respectively, in `Fixnum`.
203
+
204
+ ### Nil Type
205
+
206
+ A nil type represents a value of nil and is denoted by `nil`.
207
+
208
+ ### Any Type
209
+
210
+ RubyBreaker also provides a way to represent an object that is compatible with
211
+ any type. This type is denoted by `?`. Use caution with this type because
212
+ it should be only used for an object that requires an arbitrary yet most
213
+ specific type--that is, `?` is a subtype of any other type, but any
214
+ other type is not a subtype of `?`. This becomes a bit complicated for
215
+ method or block argument types because of their contra-variance
216
+ characteristic. Please refer to the section *Subtyping*.
217
+
218
+ ### Or Type
219
+
220
+ Any above types can be "or"ed together, using `||`, to represent an object
221
+ that can be either one or the other. It _does_ not represent an object that
222
+ has to be both (which is not supported by RubyBreaker).
223
+
224
+ ### Optional Argument Type and Variable-Length Argument Type
225
+
226
+ Another useful features of Ruby are the optional argument type and the
227
+ variable-length argument type. The former represents an argument that has a
228
+ default value (and therefore does not have to be provided). The latter
229
+ represents zero or more arguments of the same type. These are denoted by
230
+ suffices, `?` and `*`, respectively.
231
+
232
+ ### Block Type
233
+
234
+ One of the Ruby's prominent features is the block argument. It allows
235
+ the caller to pass in a piece of code to be executed inside the callee. This
236
+ code block can be executed by the Ruby construct, `yield`, or by directly
237
+ calling the `call` method of the block object. In RubyBreaker, this type can
238
+ be respresented by curly brackets. For instance, `{|fixnum,string| ->
239
+ string}` represents a block that takes two arguments--one `Fixnum` and one
240
+ `String`--and returns a `String`.
241
+
242
+ RubyBreaker does supports nested blocks as Ruby 1.9 finally allows them.
243
+ However, *keep in mind* that RubyBreaker *cannot* automatically document the
244
+ block types due to `yield` being a language construct rather than a method,
245
+ which means it cannot be captured by meta-programming!
246
+
247
+ ### Method Type and Method List Types
248
+
249
+ Method type is similar to the block type, but it represents an actual method
250
+ and not a block object. It is the "root" type that the type annotation
251
+ language supports, along with method list types. Method _list_ type is a
252
+ collection of method types to represent more than one type information for
253
+ the given method. Why would this type be needed? Consider the following Ruby
254
+ code:
255
+
256
+ def foo(x)
257
+ case x
258
+ when Fixnum
259
+ 1
260
+ when String
261
+ "1"
262
+ end
263
+ end
264
+
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
267
+ closest we can come up with would be `foo(fixnum or string) -> fixnum and
268
+ string`. But RubyBreaker does not have the "and" type in the type annotation
269
+ language because it gives me an headache! (By the way, it needs to be an
270
+ "and" type because the caller must handle both `Fixnum` and `String` return
271
+ values.)
272
+
273
+ It is a dilemma because Ruby programmers actually enjoy using this kind of
274
+ dynamic type checks in their code. To alleviate this headache, RubyBreaker
275
+ supports the method list type to represent different scenarios depending on
276
+ the argument types. Thus, the `foo` method shown above can be given the
277
+ following method list type:
278
+
279
+ typesig("foo(fixnum) -> fixnum")
280
+ typesig("foo(string) -> string")
281
+
282
+ These two type signatures simply tell RubyBreaker that `foo` has two method
283
+ types--one for a `Fixnum` argument and another for a `String` argument.
284
+ Depending on the argument type, the return type is determined. In this
285
+ example, a `Fixnum` is returned when the argument is also a `Fixnum` and a
286
+ `String` is returned when the argument is also a `String`. When
287
+ automatically documenting such a type, RubyBreaker looks for the (subtyping)
288
+ compatibility between the return types and "promote" the method type to a
289
+ method list type by spliting the type signature into two (or more in
290
+ subsequent "promotions").
291
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
data/bin/rubybreaker CHANGED
@@ -23,32 +23,50 @@ module RubyBreaker
23
23
  # Quit if there is program specified.
24
24
  self.show_banner_and_exit() if ARGV.length != 1
25
25
 
26
+ # It is required to specify at least one library and one module/class to
27
+ # break. Otherwise, it does not make sense to run RubyBreaker. So it
28
+ # will quit.
29
+ if OPTIONS[:libs].empty?
30
+ STDERR.puts "No library is specified."
31
+ exit(1)
32
+ elsif OPTIONS[:break].empty?
33
+ STDERR.puts "No module/class is specified to break."
34
+ exit(1)
35
+ end
36
+
26
37
  # Get the specified program.
27
- prog = ARGV[0]
28
- prog_file = File.expand_path(prog)
38
+ prog_name = ARGV[0]
39
+ prog = File.expand_path(prog_name)
29
40
 
30
- # It is ok to omit .rb extension. So try to see if prog_file.rb exists
31
- if !File.exist?(prog_file) && !File.extname(prog_file) == ".rb"
32
- prog_file = "#{prog_file}.rb"
41
+ # It is ok to omit .rb extension. So try to see if prog.rb exists
42
+ if !File.exist?(prog) && !File.extname(prog) == ".rb"
43
+ prog = "#{prog}.rb"
33
44
  end
34
45
 
35
46
  # Quit the specified program does not exist.
36
- if !File.exist?(prog_file)
47
+ if !File.exist?(prog)
37
48
  fatal("#{ARGV[0]} is an invalid file.")
38
49
  exit(1)
39
50
  end
40
51
 
41
52
  # Remember the program path for later use
42
- OPTIONS[:prog_file] = prog_file
53
+ OPTIONS[:prog] = prog
43
54
 
44
- # Run the program file!
45
- self.verbose("Running #{prog}")
46
- eval "require '#{prog_file}'", TOPLEVEL_BINDING
47
- self.verbose("Done running #{prog}")
55
+ # Import the libraries specified.
56
+ OPTIONS[:libs].each do |lib|
57
+ # Run the program file!
58
+ self.verbose("Importing #{lib}")
59
+ eval "require '#{lib}'", TOPLEVEL_BINDING
60
+ self.verbose("Done importing #{lib}")
61
+ end
48
62
 
49
- # Keep in mind that the source program must specify the entry
50
- # point--using RubyBreaker.run()--in order to observe the type
51
- # information.
63
+ # Now, RubyBreaker shell mode will run this
64
+ RubyBreaker.run()
65
+
66
+ # Run the program file!
67
+ self.verbose("Running #{prog_name}")
68
+ eval "require '#{prog}'", TOPLEVEL_BINDING
69
+ self.verbose("Done running #{prog_name}")
52
70
  end
53
71
 
54
72
  end
@@ -206,7 +206,7 @@ module RubyBreaker
206
206
  end
207
207
 
208
208
  # Installs an module (class) monitor to the object.
209
- def self.install_module_monitor(mod)
209
+ def self.install_monitor(mod)
210
210
 
211
211
  RubyBreaker.log("Installing module monitor for #{mod}")
212
212
 
@@ -24,37 +24,26 @@ module RubyBreaker
24
24
  # This hash maps a (breakable) module to a type monitor
25
25
  MONITOR_MAP = {} # module => monitor
26
26
 
27
- # This set lists modules/classes that are actually instrumented with a
28
- # monitor.
29
- INSTALLED = Set.new
27
+ private
30
28
 
31
- # This method installs a monitor for each breakable module.
32
- # *DEPRECATED*: Use +breakable()+ method instead.
33
- def self.instrument()
34
- BREAKABLES.each do |mod|
35
- # Duplicate checks in place in these calls.
36
- MonitorInstaller.install_module_monitor(mod)
37
- INSTALLED << mod
38
- end
39
- end
40
-
41
- # This method modifies specified modules/classes at the very moment
42
- # (instead of registering them for later).
43
- def self.breakable(*mods)
29
+ # Instruments the monitor to the specified modules/classes.
30
+ #
31
+ # TODO: monitor_type is currently not in use. Plan on using it in future
32
+ # to do type checker instrumentation
33
+ def self.install(monitor_type=:break, *mods)
44
34
  mods.each do |mod|
45
35
  case mod
46
36
  when Array
47
- self.breakable(*mod)
37
+ self.install(monitor_type, *mod)
48
38
  when Module, Class
49
- MonitorInstaller.install_module_monitor(mod)
39
+ MonitorInstaller.install_monitor(mod)
50
40
  eigen_class = self.eigen_class(mod)
51
- MonitorInstaller.install_module_monitor(eigen_class)
52
- INSTALLED << mod << eigen_class
41
+ MonitorInstaller.install_monitor(eigen_class)
53
42
  when String, Symbol
54
43
  begin
55
44
  # Get the actual module and install it right now
56
45
  mod = eval("#{mod}", TOPLEVEL_BINDING)
57
- self.breakable(mod) if mod
46
+ self.install(monitor_type, mod) if mod
58
47
  rescue NameError => e
59
48
  RubyBreaker.error("#{mod} cannot be found.")
60
49
  end
@@ -63,6 +52,31 @@ module RubyBreaker
63
52
  end
64
53
  end
65
54
  end
55
+
56
+ public
57
+
58
+ # This method instruments the specified modules/classes at the time of
59
+ # the call.
60
+ def self.break(*mods)
61
+ self.install(:break, *mods)
62
+ end
63
+
64
+ # This method installs a monitor for each breakable module.
65
+ # *DEPRECATED*: Use +breakable()+ method instead.
66
+ def self.instrument()
67
+ BREAKABLES.each do |mod|
68
+ # Duplicate checks in place in these calls.
69
+ MonitorInstaller.install_monitor(mod)
70
+ end
71
+ end
72
+
73
+ # This method modifies specified modules/classes at the very moment
74
+ # (instead of registering them for later).
75
+ # *DEPRECATED*: Use +break()+ method instead
76
+ def self.breakable(*mods)
77
+ self.install(:break, *mods)
78
+ end
79
+
66
80
  end
67
81
 
68
82
  # *DEPRECATED*: Use +RubyBreaker.run()+ to indicate the point of entry.
@@ -70,10 +84,16 @@ module RubyBreaker
70
84
  end
71
85
 
72
86
  # This method just redirects to Runtime's method.
87
+ # *DEPRECATED*: Use +RubyBreaker.break()+ to indicate the point of entry.
73
88
  def self.breakable(*mods)
74
89
  Runtime.breakable(*mods)
75
90
  end
76
91
 
92
+ # This method just redirects to Runtime's method.
93
+ def self.break(*mods)
94
+ Runtime.break(*mods)
95
+ end
96
+
77
97
  # *DEPRECATED*: Use +Runtime.breakable()+ or +RubyBreaker.run()+ method
78
98
  # instead.
79
99
  module Breakable
@@ -16,17 +16,23 @@ module Rake
16
16
  # Rake::RubyBreakerTestTask.new(:"testtask_test") do |t|
17
17
  # t.libs << "lib"
18
18
  # t.test_files = ["test/testtask/tc_testtask.rb"]
19
- # t.breakable = ["SampleClassA"]
19
+ # t.break = ["SampleClassA"]
20
20
  # end
21
21
  #
22
22
  class RubyBreakerTestTask < Rake::TestTask
23
23
 
24
- # List of Breakable modules/classes
25
- attr_accessor :breakable
24
+ # List of modules/classes to break
25
+ attr_accessor :break
26
26
 
27
27
  # RubyBreaker options
28
28
  attr_accessor :rubybreaker_opts
29
29
 
30
+ # DEPRECATED accessor override
31
+ def breakable(); @break end
32
+
33
+ # DEPRECATED accessor override
34
+ def breakable=(*args); self.break(*args) end
35
+
30
36
  # This overrides the testtask's constructor. In addition to the original
31
37
  # behavior, it keeps track of RubyBreaker options and store them in a
32
38
  # yaml file.
@@ -34,7 +40,7 @@ module Rake
34
40
 
35
41
  # Initialize extra instance variables
36
42
  @rubybreaker_opts = []
37
- @breakable = nil
43
+ @break = nil
38
44
 
39
45
  # Call the original constructor first
40
46
  super(taskname, *args, &blk)
@@ -51,14 +57,14 @@ module Rake
51
57
 
52
58
  # Construct the task configuration hash
53
59
  config = {
54
- name: taskname,
55
- rubybreaker_opts: opts,
56
- breakable: [], # Set doesn't work well with YAML; just use an array
57
- test_files: @test_files,
60
+ :name => taskname,
61
+ :rubybreaker_opts => opts,
62
+ :break => [], # Set doesn't work well with YAML; just use an array
63
+ :test_files => @test_files,
58
64
  }
59
65
 
60
66
  # This allows a bulk declaration of Breakable modules/classes
61
- @breakable.each { |b| config[:breakable] << b } if @breakable
67
+ @break.each { |b| config[:break] << b } if @break
62
68
 
63
69
  # This code segment is a clever way to store yaml data in a ruby file
64
70
  # that reads its own yaml data after __END__ when loaded.
@@ -2,13 +2,13 @@
2
2
  # This file overrides the describe method of RSpec to call the RubyBreaker
3
3
  # setup first.
4
4
 
5
- RUBYBREAKER_RSPEC_PREFIX = "__rubybreaker"
5
+ RUBYBREAKER_RSPEC_PREFIX = "__rubybreaker" #:nodoc:
6
6
 
7
7
  if defined?(RSpec)
8
- alias :"#{RUBYBREAKER_RSPEC_PREFIX}_describe" :describe
8
+ alias :"#{RUBYBREAKER_RSPEC_PREFIX}_describe" :describe #:nodoc:
9
9
  end
10
10
 
11
- def describe(*args,&blk)
11
+ def describe(*args,&blk) #:nodoc:
12
12
  RubyBreaker.run if defined?(RubyBreaker)
13
13
  send(:"#{RUBYBREAKER_RSPEC_PREFIX}_describe", *args, &blk)
14
14
  end
@@ -6,14 +6,14 @@
6
6
  if defined?(Test) && defined?(Test::Unit)
7
7
 
8
8
  # This class is patched to run RubyBreaker along with the test cases.
9
- class Test::Unit::TestCase
9
+ class Test::Unit::TestCase #:nodoc:
10
10
 
11
11
  # Save the original constructor method.
12
- alias :__rubybreaker_initialize :initialize
12
+ alias :__rubybreaker_initialize :initialize #:nodoc:
13
13
 
14
14
  # This method overrides the original constructor to run RubyBreaker before
15
15
  # calling the original constructor.
16
- def initialize(*args, &blk)
16
+ def initialize(*args, &blk) #:nodoc:
17
17
  RubyBreaker.run()
18
18
  return send(:__rubybreaker_initialize, *args, &blk)
19
19
  end
data/lib/rubybreaker.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  #--
2
- # This library dynamically profiles and resolves the type information
3
- # observed at runtime and generates type annotation at the end. It can be
4
- # run as either a stand-alone script or as a Ruby library.
2
+ # This library dynamically instruments the source program, runs the program
3
+ # on Ruby, and produces type documentation based on the observation made
4
+ # during runtime. Although it is possible to use this as a typical Ruby
5
+ # library, our recommendation is to use it in Rakefile or use it as a
6
+ # command-line program. See TUTORIAL for more detail.
5
7
 
6
8
  require "set"
7
9
  require "optparse"
@@ -10,21 +12,23 @@ require_relative "rubybreaker/runtime"
10
12
  require_relative "rubybreaker/test"
11
13
 
12
14
  # RubyBreaker is a dynamic instrumentation and monitoring tool that
13
- # generates type documentation for Ruby programs.
15
+ # generates type documentation automatically for Ruby programs.
14
16
  module RubyBreaker
15
17
  include TypeDefs
16
18
  include Runtime
17
19
 
18
20
  # Options for RubyBreaker
19
21
  OPTIONS = {
20
- :debug => false, # in debug mode?
21
- :style => :underscore, # type signature style-underscore or camelize
22
- :io_file => nil, # generate input/output other than default?
23
- :append => false, # append to the input file (if there is)?
24
- :stdout => false, # also display on the screen?
25
- :verbose => false, # in RubyBreaker.verbose mode?
22
+ :debug => false, # in debug mode?
23
+ :style => :underscore, # type signature style-underscore or camelize
24
+ :io_file => nil, # generate input/output other than default?
25
+ :append => false, # append to the input file (if there is)?
26
+ :stdout => false, # also display on the screen?
27
+ :verbose => false, # in RubyBreaker.verbose mode?
26
28
  :save_output => true, # save output to a file?
27
- :prog_file => nil, # INTERNAL USE ONLY
29
+ :break => [], # modules to break
30
+ :libs => [], # list of library files to import
31
+ :prog => nil, # program or test file
28
32
  }
29
33
 
30
34
  # This option parser may be used for the command-line mode or for the
@@ -34,6 +38,16 @@ module RubyBreaker
34
38
 
35
39
  opts.banner = "Usage: #{File.basename(__FILE__)} [options] prog[.rb]"
36
40
 
41
+ opts.on("-b MODULES", "--break MODULES", "Specify modules/classes to 'break'") do |s|
42
+ tokens = s.split(/[;,]/)
43
+ tokens.each {|t| OPTIONS[:break] << t}
44
+ end
45
+
46
+ opts.on("-l LIBRARIES", "--libs LIBRARIES", "Specify libraries to load") do |s|
47
+ tokens = s.split(":")
48
+ tokens.each {|t| OPTIONS[:libs] << t}
49
+ end
50
+
37
51
  opts.on("--debug", "Run in debug mode") do
38
52
  OPTIONS[:debug] = true
39
53
  end
@@ -104,7 +118,7 @@ module RubyBreaker
104
118
  code = ""
105
119
 
106
120
  # Document each module that was monitored.
107
- INSTALLED.each { |mod|
121
+ MONITOR_MAP.each_key { |mod|
108
122
  str = Runtime::TypeSigUnparser.unparse(mod)
109
123
  code << str
110
124
  print str if OPTIONS[:stdout] # display on the screen if requested
@@ -157,12 +171,13 @@ module RubyBreaker
157
171
  RubyBreaker.verbose("Running RubyBreaker within a testcase")
158
172
  task = self.task
159
173
  OPTION_PARSER.parse(*task[:rubybreaker_opts])
160
- Runtime.breakable(*task[:breakable])
174
+ Runtime.break(*task[:break])
161
175
  task_name = task[:name]
162
176
  RubyBreaker.verbose("Done reading task information")
163
177
  io_file = self.io_file(task_name)
164
- elsif OPTIONS[:prog_file] # running in shell mode
165
- Runtime.breakable(*mods)
178
+ elsif OPTIONS[:prog] # running in shell mode
179
+ Runtime.break(*mods) # should not happen but for backward-compatibility
180
+ Runtime.break(*OPTIONS[:break])
166
181
  io_file = self.io_file(OPTIONS[:prog_file])
167
182
  else
168
183
  # Otherwise, assume there are no explicit IO files.
@@ -179,7 +194,7 @@ module RubyBreaker
179
194
  end
180
195
 
181
196
  # This method is available by default.
182
- module Kernel
197
+ module Kernel #:nodoc:
183
198
 
184
199
  def typesig(str)
185
200
  _TypeDefs = RubyBreaker::TypeDefs
@@ -1,21 +1,20 @@
1
1
  require "test/unit"
2
2
  require_relative "../../lib/rubybreaker"
3
3
 
4
- class IntegratedBothBrokenBreakableTest < Test::Unit::TestCase
4
+ class IntegratedBothDocumentedAndUndocumented < Test::Unit::TestCase
5
5
  include RubyBreaker
6
6
 
7
7
  class A
8
- # include RubyBreaker::Breakable
9
8
  typesig("foo(fixnum[to_s]) -> string")
10
9
  def foo(x); x.to_s end
11
10
  def bar(x); x.to_sym end
12
11
  end
13
12
 
14
13
  def setup
15
- RubyBreaker.breakable(A)
14
+ RubyBreaker.break(A)
16
15
  end
17
16
 
18
- def test_both_broken_and_breakable
17
+ def test_both_documented_and_undocumented
19
18
  A.new.bar("abc")
20
19
  a_foo_meth_type = Runtime::Inspector.inspect_meth(A, :foo)
21
20
  a_bar_meth_type = Runtime::Inspector.inspect_meth(A, :bar)