rubybreaker 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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)