rubybreaker 0.0.4 → 0.0.5
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.
- data/NEWS +5 -0
- data/NOTES +9 -0
- data/README.md +100 -183
- data/Rakefile +21 -6
- data/VERSION +1 -1
- data/bin/rubybreaker +34 -50
- data/lib/rubybreaker/debug/debug.rb +6 -0
- data/lib/rubybreaker/doc/rdoc.rb +37 -0
- data/lib/rubybreaker/doc.rb +3 -0
- data/lib/rubybreaker/runtime/inspector.rb +6 -28
- data/lib/rubybreaker/runtime/monitor.rb +21 -17
- data/lib/rubybreaker/runtime/object_wrapper.rb +7 -2
- data/lib/rubybreaker/runtime/type_system.rb +3 -5
- data/lib/rubybreaker/runtime/typesig_unparser.rb +1 -4
- data/lib/rubybreaker/runtime.rb +64 -105
- data/lib/rubybreaker/task.rb +97 -0
- data/lib/rubybreaker/test/rspec.rb +1 -1
- data/lib/rubybreaker/test/testcase.rb +13 -28
- data/lib/rubybreaker/type/type.rb +1 -1
- data/lib/rubybreaker/typing/subtyping.rb +10 -5
- data/lib/rubybreaker/util.rb +0 -1
- data/lib/rubybreaker.rb +163 -115
- data/test/integrated/tc_both_broken_breakable.rb +5 -4
- data/test/integrated/tc_class_methods.rb +4 -3
- data/test/integrated/tc_inherit_broken.rb +4 -3
- data/test/integrated/tc_method_missing.rb +4 -4
- data/test/integrated/tc_namespace.rb +4 -2
- data/test/integrated/tc_simple1.rb +4 -3
- data/test/runtime/tc_obj_wrapper.rb +25 -6
- data/test/runtime/tc_typesig_parser.rb +0 -1
- data/test/testtask/sample.rb +10 -0
- data/test/testtask/tc_testtask.rb +25 -0
- data/test/ts_rspec.rb +21 -15
- data/test/typing/tc_typing.rb +2 -3
- data/webpage/index.html +105 -193
- data/webpage/rdoc/Kernel.html +286 -0
- data/webpage/rdoc/Object.html +17 -11
- data/webpage/rdoc/Rake/RubyBreakerTestTask.html +374 -0
- data/webpage/rdoc/Rake.html +212 -0
- data/webpage/rdoc/RubyBreaker/Breakable.html +24 -40
- data/webpage/rdoc/RubyBreaker/Broken.html +21 -69
- data/webpage/rdoc/RubyBreaker/Context.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/UserError.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors.html +16 -10
- data/webpage/rdoc/RubyBreaker/ObjectPosition.html +16 -10
- data/webpage/rdoc/RubyBreaker/Position.html +16 -10
- data/webpage/rdoc/RubyBreaker/{TestCase.html → RDocSupport.html} +81 -82
- data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +25 -44
- data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +19 -13
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +37 -25
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +20 -14
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +21 -15
- data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +23 -12
- data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/{TypesigUnparser.html → TypeSigUnparser.html} +19 -16
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +18 -14
- data/webpage/rdoc/RubyBreaker/Runtime.html +145 -11
- data/webpage/rdoc/RubyBreaker/TypeComparer.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +17 -11
- data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +17 -11
- data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeUnparser.html +16 -10
- data/webpage/rdoc/RubyBreaker/Typing.html +17 -11
- data/webpage/rdoc/RubyBreaker/Util.html +16 -10
- data/webpage/rdoc/RubyBreaker.html +167 -34
- data/webpage/rdoc/{RubyBreaker/Runtime/TypePlaceholder.html → Test/Unit/TestCase.html} +68 -39
- data/webpage/rdoc/Test/Unit.html +211 -0
- data/webpage/rdoc/Test.html +211 -0
- data/webpage/rdoc/created.rid +18 -17
- data/webpage/rdoc/index.html +16 -10
- data/webpage/rdoc/js/search_index.js +1 -1
- data/webpage/rdoc/table_of_contents.html +61 -48
- metadata +21 -12
- data/lib/rubybreaker/rubylib/core.rb +0 -2483
- data/lib/rubybreaker/rubylib.rb +0 -3
- data/lib/rubybreaker/runtime/type_placeholder.rb +0 -23
- data/webpage/rdoc/RubyBreaker/Broken/BrokenEigen.html +0 -305
- data/webpage/rdoc/RubyBreaker/Main.html +0 -458
data/NEWS
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
# VERSION 0.0.5
|
|
2
|
+
* Rake::RubyBreakerTestTask is supported.
|
|
3
|
+
* Manual modification is no longer required if run as a Rake task.
|
|
4
|
+
* Deprecating Breakable and Broken modules. Use breakable() method instead.
|
|
5
|
+
|
|
1
6
|
# VERSION 0.0.4
|
|
2
7
|
* RSpec is supported.
|
|
3
8
|
* Namespace is recognized in type signatures.
|
data/NOTES
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
# Adding RDoc support:
|
|
3
|
+
* Use :rubybreaker: directive. This is needed.
|
|
4
|
+
* Register it using RDoc::Markup::Preprocess#register,preprocess.
|
|
5
|
+
* Use YAML to store the RubyBreaker result and load it when RDoc is run.
|
|
6
|
+
* The user program would use it in Rakefile.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
data/README.md
CHANGED
|
@@ -5,30 +5,30 @@
|
|
|
5
5
|
RubyBreaker is a dynamic type documentation tool written in pure Ruby. It
|
|
6
6
|
provides the framework for dynamically instrumenting a Ruby program to
|
|
7
7
|
monitor objects during executions and document the observed type
|
|
8
|
-
information.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
information. In other words, RubyBreaker "breaks" Ruby code out of its
|
|
9
|
+
obscurity and wildness (as in "code breaking" or "horse
|
|
10
|
+
breaking") by auto-documenting type information. The type documentation
|
|
11
|
+
generated by RubyBreaker is also an executable Ruby code that can be used as
|
|
12
|
+
an input to subsequent analyses.
|
|
12
13
|
|
|
13
14
|
The primary goal of RubyBreaker is to assign a type signature to every
|
|
14
|
-
method in
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
method in selected modules and classes. A type signature is written in the
|
|
16
|
+
RubyBreaker Type Annotation Language which resembles the documentation style
|
|
17
|
+
used in Ruby API Doc. Manual code change is _not_ required if used in
|
|
18
|
+
Rakefile and is kept minimal if otherwise. Overall, this tool should help
|
|
19
|
+
Ruby programmers document their code more rigorously and effectively.
|
|
20
|
+
|
|
21
|
+
Current limitations are:
|
|
22
|
+
|
|
23
|
+
* Auto-documentation of block arguments (inherent)
|
|
24
|
+
* Parametric polymorphic types
|
|
25
|
+
* RDoc or YARD documentation support
|
|
20
26
|
|
|
21
27
|
To contribute to the project, visit RubyBreaker's
|
|
22
28
|
[GitHub page](http://github.com/rockalizer/rubybreaker) and
|
|
23
29
|
[RubyGems page](http://rubygems.org/gems/rubybreaker). RubyBreaker RDoc can
|
|
24
30
|
be found in [here](rdoc/index.html).
|
|
25
31
|
|
|
26
|
-
## Limitations
|
|
27
|
-
|
|
28
|
-
* Block argument cannot be auto-documented. (Inherent)
|
|
29
|
-
* Manual modification (minimal) of code is required.
|
|
30
|
-
* Parametric polymorphic types are not supported.
|
|
31
|
-
|
|
32
32
|
## Requirements
|
|
33
33
|
|
|
34
34
|
Ruby 1.9.x and TreeTop 1.x
|
|
@@ -39,15 +39,10 @@ following URL: [TreeTop](http://treetop.rubyforge.org/)
|
|
|
39
39
|
|
|
40
40
|
## Installation
|
|
41
41
|
|
|
42
|
-
It is as simple as running the following:
|
|
42
|
+
It is as simple as running the following command:
|
|
43
43
|
|
|
44
44
|
$ gem install rubybreaker
|
|
45
45
|
|
|
46
|
-
You probably want to test out your installation by running
|
|
47
|
-
`rake test` in your RubyBreaker directory:
|
|
48
|
-
|
|
49
|
-
$ rake test
|
|
50
|
-
|
|
51
46
|
* * *
|
|
52
47
|
|
|
53
48
|
# Tutorial
|
|
@@ -57,213 +52,135 @@ Type Annotation Language, and the RubyBreaker Type System.
|
|
|
57
52
|
|
|
58
53
|
## Usage
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
RubyBreaker takes advantage of test cases that already come with the source
|
|
56
|
+
program. It is recommended that RubyBreaker is run as a Rake task, which
|
|
57
|
+
requires a minimum code change in the Rakefile and no code change in the
|
|
58
|
+
source program. If not used as a Rake task, it requires a minimum code
|
|
59
|
+
change in each test case or the source program but should not affect the
|
|
60
|
+
development process much. Let's briefly see how RubyBreaker can be run
|
|
61
|
+
directly as a command-line program to understand the general concept of the
|
|
62
|
+
tool. We will explain how to use RubyBreaker in a Rakefile later.
|
|
65
63
|
|
|
66
|
-
$
|
|
64
|
+
$ rubybreaker -v prog.rb
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
analysis is done on the program.
|
|
66
|
+
This runs RubyBreaker in verbose mode on `prog.rb`. Note that RubyBreaker
|
|
67
|
+
will actually run `prog.rb` (by simply `require`ing the program file).
|
|
68
|
+
Somewhere in the program, there has to be a _program entry point_ to
|
|
69
|
+
indicate where the _monitoring_ of objects starts. Let's assume `prog.rb`
|
|
70
|
+
as the following:
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
require "rubybreaker"
|
|
72
|
+
require "rubybreaker" # required if using "ruby" instead
|
|
78
73
|
class A
|
|
79
|
-
include RubyBreaker::Breakable
|
|
80
74
|
def foo(x)
|
|
81
75
|
x.to_s
|
|
82
76
|
end
|
|
83
77
|
end
|
|
84
78
|
class B
|
|
85
|
-
# include RubyBreaker::Breakable
|
|
86
79
|
def bar(y,z)
|
|
87
80
|
y.foo(z)
|
|
88
81
|
end
|
|
89
82
|
end
|
|
90
|
-
RubyBreaker.
|
|
83
|
+
RubyBreaker.run(A, B)
|
|
91
84
|
A.new.foo(1)
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`prog.rubybreaker`.
|
|
86
|
+
This example will show how `A#foo` method is given a type by RubyBreaker.
|
|
87
|
+
After running `rubybreaker -v prog.rb`, the following output will be
|
|
88
|
+
generated and saved into `prog.rubybreaker.rb`.
|
|
97
89
|
|
|
90
|
+
# This file is auto-generated by RubyBreaker
|
|
98
91
|
require "rubybreaker"
|
|
99
92
|
class A
|
|
100
|
-
include RubyBreaker::Broken
|
|
101
93
|
typesig("foo(fixnum[to_s]) -> string")
|
|
102
94
|
end
|
|
103
95
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
Here, the `typesig` method call registers `foo` as a method type that takes
|
|
97
|
+
an object that has `Fixnum#to_s` method and returns a `String`. This
|
|
98
|
+
method is made available by importing `rubybreaker`. Now, assume that an
|
|
99
|
+
additional code, `B.new.bar(A.new,1)`, is added at the end of `prog.rb`. The
|
|
100
|
+
subsequent run will generate the following result:
|
|
107
101
|
|
|
108
|
-
# This is auto-generated by RubyBreaker
|
|
102
|
+
# This file is auto-generated by RubyBreaker
|
|
109
103
|
require "rubybreaker"
|
|
110
104
|
class A
|
|
111
|
-
include RubyBreaker::Broken
|
|
112
105
|
typesig("foo(fixnum[to_s]) -> string")
|
|
113
106
|
end
|
|
114
107
|
class B
|
|
115
|
-
include RubyBreaker::Broken
|
|
116
108
|
typesig("bar(a[foo], fixnum[to_s]) -> string")
|
|
117
109
|
end
|
|
118
110
|
|
|
119
|
-
RubyBreaker is designed to gather type information based
|
|
120
|
-
execution of
|
|
121
|
-
test
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
In order to use RubyBreaker, there needs two kinds of manual code changes.
|
|
128
|
-
First, the user must indicate which modules are subject to analysis and
|
|
129
|
-
which modules can be used for the analysis. Next, the user has to indicate
|
|
130
|
-
where the entry point of the program is. Alternatively, he has to make a
|
|
131
|
-
small change to the test cases to use RubyBreaker's testing framework. (If
|
|
132
|
-
you are using RSpec, there is no need for this change.)
|
|
133
|
-
|
|
134
|
-
### Breakable and Broken
|
|
135
|
-
|
|
136
|
-
In order to indicate modules and classes that already have type information
|
|
137
|
-
or to designate those that need to be auto-documented, the user must be
|
|
138
|
-
familiar with the two most important modules of RubyBreaker--`Breakable` and
|
|
139
|
-
`Broken`. The former refers to a module (or a class) that needs dynamic
|
|
140
|
-
instrumentation and monitoring for getting type information. The latter
|
|
141
|
-
refers to a module that have type information already documented in type
|
|
142
|
-
signature form.
|
|
143
|
-
|
|
144
|
-
For example, consider the following Ruby code:
|
|
111
|
+
Keep in mind that RubyBreaker is designed to gather type information based
|
|
112
|
+
on the _actual_ execution of the source program. This means the program
|
|
113
|
+
should be equipped with test cases that have a reasonable program path
|
|
114
|
+
coverage. Additionally, RubyBreaker assumes that test runs are correct and
|
|
115
|
+
the program behaves correctly (for those test runs) as intended by the
|
|
116
|
+
programmer. This assumption is not a strong requirement, but is necessary to
|
|
117
|
+
obtain precise and accurate type information.
|
|
145
118
|
|
|
146
|
-
|
|
147
|
-
class A
|
|
148
|
-
include RubyBreaker::Breakable
|
|
149
|
-
def foo(x)
|
|
150
|
-
x.to_s
|
|
151
|
-
end
|
|
152
|
-
end
|
|
119
|
+
### Using Ruby Unit Testing Framework
|
|
153
120
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
121
|
+
Instead of manually inserting the entry point indicator into the program,
|
|
122
|
+
you can take advantage of Ruby's built-in testing framework. This is
|
|
123
|
+
preferred to modifying the source program directly, especially for the long
|
|
124
|
+
term program maintainability. But no worries! This method is as simple as
|
|
125
|
+
the previous one.
|
|
157
126
|
|
|
158
|
-
require "
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
127
|
+
require "test/unit"
|
|
128
|
+
require "rubybreaker" # This should come after test/unit.
|
|
129
|
+
class TestClassA < Test::Unit::TestCase
|
|
130
|
+
def setup()
|
|
131
|
+
RubyBreaker.breakable(Class1, Class2, ...)
|
|
132
|
+
...
|
|
164
133
|
end
|
|
134
|
+
# ...tests!...
|
|
165
135
|
end
|
|
166
136
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
signature `bar(fixnum[to_s]) -> string`, which means it takes an object that
|
|
171
|
-
has `Fixnum`'s `to_s` method and returns a string. More detail on the type
|
|
172
|
-
annotation language will be explained in later section.
|
|
137
|
+
That's it! The only requirements are to indicate to RubyBreaker which modules
|
|
138
|
+
and classes to "break" and to place `require rubybreaker` _after_
|
|
139
|
+
`require test/unit`.
|
|
173
140
|
|
|
174
|
-
###
|
|
175
|
-
|
|
176
|
-
Starting from version 0.0.3, RubyBreaker allows a module to be declared as
|
|
177
|
-
`Breakable` and `Broken` at the same time. If a method has a corresponding
|
|
178
|
-
type signature (manually documented) somewhere, then RubyBreaker will not
|
|
179
|
-
monitor it during runtime. All other methods will be instrumented and
|
|
180
|
-
monitored by RubyBreaker.
|
|
181
|
-
|
|
182
|
-
### Class Methods
|
|
141
|
+
### Using RSpec
|
|
183
142
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
support auto-documentation and manual documentation of class methods if the
|
|
187
|
-
original module is declared as `Breakable` and/or `Broken`,
|
|
188
|
-
respectively--that is, up to immediate eigen class level of a "nominal"
|
|
189
|
-
module. The following example shows how class methods can be
|
|
190
|
-
auto-documented and manually documented, respectively.
|
|
143
|
+
The requirement is same for RSpec but use `before` instead of `setup` to
|
|
144
|
+
specify which modules and classes to "break".
|
|
191
145
|
|
|
146
|
+
require "rspec"
|
|
192
147
|
require "rubybreaker"
|
|
193
|
-
class A
|
|
194
|
-
include RubyBreaker::Breakable
|
|
195
|
-
class << self
|
|
196
|
-
def foo(x); x.to_s end
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
class B
|
|
200
|
-
include RubyBreaker::Broken
|
|
201
|
-
class << self
|
|
202
|
-
typesig("foo(fixnum[to_s]) -> string")
|
|
203
|
-
def foo(x); x.to_s end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
### Program Entry Point
|
|
208
|
-
|
|
209
|
-
In Ruby, as soon as a file is `require`d, the execution of that file begins.
|
|
210
|
-
For RubyBreaker, however, it is not trivial to find the actual starting
|
|
211
|
-
point of the program because there *has* to be a clear point in time at
|
|
212
|
-
which monitoring of `Breakable` modules begins.
|
|
213
|
-
|
|
214
|
-
Indicating the program entry point is simply done by inserting the following
|
|
215
|
-
line at the code (assuming "`require 'rubybreaker'`" is already placed at
|
|
216
|
-
the top of the file):
|
|
217
|
-
|
|
218
|
-
RubyBreaker.monitor()
|
|
219
|
-
|
|
220
|
-
It basically tells RubyBreaker to start monitoring. What really happens at
|
|
221
|
-
this point is that all `Breakable` modules are dynamically instrumented so
|
|
222
|
-
that they are ready to be monitored. Any execution after this point will
|
|
223
|
-
run the instrumented code (for `Breakable` modules) which will gather type
|
|
224
|
-
information for methods.
|
|
225
|
-
|
|
226
|
-
Although this seems simple and easy, this is not the recommended way for
|
|
227
|
-
analyzing a program. Why? Because RubyBreaker comes with a replacement for
|
|
228
|
-
the built-in testing framework for Ruby. Even better, if you are using
|
|
229
|
-
RSpec, there is no need to change any test code.
|
|
230
|
-
|
|
231
|
-
### Using the Built-in Testing Framework
|
|
232
148
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
maintainability. But no worries! This method is as simple as the previous
|
|
237
|
-
one.
|
|
238
|
-
|
|
239
|
-
require "rubybreaker"
|
|
240
|
-
require "test/unit"
|
|
241
|
-
class TestClassA < Test::Unit::TestCase
|
|
242
|
-
include RubyBreaker::TestCase
|
|
149
|
+
describe "TestClassA Test"
|
|
150
|
+
before { RubyBreaker.breakable(Class1, Class2, ...) }
|
|
151
|
+
...
|
|
243
152
|
# ...tests!...
|
|
244
153
|
end
|
|
245
154
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
require "rubybreaker"
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
155
|
+
### Using Rakefile
|
|
156
|
+
|
|
157
|
+
By running RubyBreaker along with the Rakefile, you can avoid modifying the
|
|
158
|
+
source program at all. (You no longer need to import `rubybreaker` in the
|
|
159
|
+
test cases neither.) Therefore, this is the recommended way to use
|
|
160
|
+
RubyBreaker. The following code snippet describes how it can be done:
|
|
161
|
+
|
|
162
|
+
require "rubybreaker/task"
|
|
163
|
+
...
|
|
164
|
+
desc "Run RubyBreaker"
|
|
165
|
+
Rake::RubyBreakerTestTask.new(:"rubybreaker") do |t|
|
|
166
|
+
t.libs << "lib"
|
|
167
|
+
t.test_files = ["test/foo/tc_foo1.rb"]
|
|
168
|
+
# ...Other test task options..
|
|
169
|
+
t.rubybreaker_opts << "-v" # run in verbose mode
|
|
170
|
+
t.breakable = ["Class1", "Class2", ...] # specify what to monitor
|
|
260
171
|
end
|
|
261
172
|
|
|
262
|
-
|
|
173
|
+
Note that `RubyBrakerTestTask` can simply replace your `TestTask` block in
|
|
174
|
+
Rakefile. In fact, the former is a subclass of the latter and includes all
|
|
175
|
+
features supported by the latter. The only additional options are
|
|
176
|
+
`rubybreaker_opts` which is RubyBreaker's command-line options and
|
|
177
|
+
`breakable` which specifies which modules and classes to monitor. Since
|
|
178
|
+
`Class1` and `Class2` are not _recognized_ by this Rakefile, you must use
|
|
179
|
+
string literals to specify modules and classes (and with full namespace).
|
|
263
180
|
|
|
264
|
-
If
|
|
265
|
-
|
|
266
|
-
|
|
181
|
+
If this is the route you are taking, there needs no editing of the source
|
|
182
|
+
program whatsoever. This task will take care of instrumenting the specified
|
|
183
|
+
modules and classes at proper moments.
|
|
267
184
|
|
|
268
185
|
## Type Annotation
|
|
269
186
|
|
|
@@ -423,7 +340,7 @@ subsequent "promotions").
|
|
|
423
340
|
## Type System
|
|
424
341
|
|
|
425
342
|
RubyBreaker comes with its own type system to auto-document the type
|
|
426
|
-
information. Each method in a
|
|
343
|
+
information. Each method in a "breakable" module is dynamically instrumented
|
|
427
344
|
to be monitored during runtime. This monitoring code observes the types of
|
|
428
345
|
the arguments, block, and return value of each method. Once this information
|
|
429
346
|
is gathered, RubyBreaker will compare it to the information gathered so far.
|
data/Rakefile
CHANGED
|
@@ -5,6 +5,7 @@ require "rake"
|
|
|
5
5
|
require "rake/testtask"
|
|
6
6
|
require "rdoc/task"
|
|
7
7
|
require "rake/clean"
|
|
8
|
+
require_relative "lib/rubybreaker/task"
|
|
8
9
|
|
|
9
10
|
begin
|
|
10
11
|
require "rdiscount" # used to generate the doc html page
|
|
@@ -21,15 +22,21 @@ end
|
|
|
21
22
|
# Use rake/clean to remove generated files
|
|
22
23
|
CLEAN.concat(FileList["webpage/rdoc",
|
|
23
24
|
"rubybreaker-*.gem",
|
|
24
|
-
"webpage/index.html",
|
|
25
|
-
"
|
|
26
|
-
|
|
25
|
+
"webpage/index.html",
|
|
26
|
+
"*.rubybreaker.rb",
|
|
27
|
+
"lib/rubybreaker/type/type_grammar.rb"])
|
|
27
28
|
|
|
28
29
|
# If no task specified, do test
|
|
29
|
-
task :default => [:test]
|
|
30
|
+
task :default => [:test, :testtask_test]
|
|
30
31
|
|
|
31
32
|
desc "Do all"
|
|
32
|
-
task :all => [:parser,
|
|
33
|
+
task :all => [:parser,
|
|
34
|
+
:test,
|
|
35
|
+
:testtask_test,
|
|
36
|
+
:rspec,
|
|
37
|
+
:rdoc,
|
|
38
|
+
:webpage,
|
|
39
|
+
:gem] do |t|
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
desc "Generate gemspec"
|
|
@@ -40,7 +47,7 @@ end
|
|
|
40
47
|
Rake::RDocTask.new do |rd|
|
|
41
48
|
rd.rdoc_dir = "#{File.dirname(__FILE__)}/webpage/rdoc"
|
|
42
49
|
rd.rdoc_files.include("lib/**/*.rb")
|
|
43
|
-
rd.rdoc_files.exclude("lib/rubybreaker/
|
|
50
|
+
rd.rdoc_files.exclude("lib/rubybreaker/type/type_grammar.rb")
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
desc "Generate the webpage"
|
|
@@ -69,9 +76,17 @@ Rake::TestTask.new(:"test") do |t|
|
|
|
69
76
|
t.libs << "lib"
|
|
70
77
|
test_files = FileList["test/ts_*.rb"]
|
|
71
78
|
test_files.exclude("test/ts_rspec.rb")
|
|
79
|
+
test_files.exclude("test/ts_testtask.rb")
|
|
72
80
|
t.test_files = test_files
|
|
73
81
|
end
|
|
74
82
|
|
|
83
|
+
desc "Run rubybreaker testtask test"
|
|
84
|
+
Rake::RubyBreakerTestTask.new(:"testtask_test") do |t|
|
|
85
|
+
t.libs << "lib" << "test/tc_testtask/sample.rb"
|
|
86
|
+
t.test_files = ["test/testtask/tc_testtask.rb"]
|
|
87
|
+
t.breakable = ["SampleClassA"]
|
|
88
|
+
end
|
|
89
|
+
|
|
75
90
|
if defined?(RSpec)
|
|
76
91
|
desc "Run RSpec test"
|
|
77
92
|
RSpec::Core::RakeTask.new(:rspec) do |t|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.5
|
data/bin/rubybreaker
CHANGED
|
@@ -1,72 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
require "optparse"
|
|
3
|
-
|
|
4
2
|
require_relative "../lib/rubybreaker"
|
|
5
3
|
|
|
6
4
|
module RubyBreaker
|
|
7
5
|
|
|
8
|
-
# This method
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
opts.banner = "Usage: #{File.basename(__FILE__)} [options] in_file[.rb]"
|
|
15
|
-
|
|
16
|
-
opts.on("--debug", "Run in debug mode") do
|
|
17
|
-
OPTIONS[:debug] = true
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
opts.on("-f","--io-file FILE","Specify an input/output file") do |f|
|
|
21
|
-
OPTIONS[:output] = f
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
opts.on("-s","--[no-]stdout","Show output on the screen") do |b|
|
|
25
|
-
OPTIONS[:stdout] = b
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
opts.on("-a", "--[no-]append", "Append output to the input file") do |b|
|
|
29
|
-
OPTIONS[:append] = b
|
|
30
|
-
end
|
|
6
|
+
# This method shows the banner and exits with code 1.
|
|
7
|
+
def self.show_banner_and_exit()
|
|
8
|
+
puts OPTION_PARSER.banner
|
|
9
|
+
exit(1)
|
|
10
|
+
end
|
|
31
11
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
# This method prepares shell mode execution of RubyBreaker.
|
|
13
|
+
def self.main()
|
|
14
|
+
RubyBreaker.setup_logger()
|
|
15
|
+
RubyBreaker.verbose("Running RubyBreaker in shell mode")
|
|
35
16
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
end
|
|
17
|
+
# parse the command-line arguments
|
|
18
|
+
OPTION_PARSER.parse!
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
exit
|
|
43
|
-
end
|
|
20
|
+
# Show copyright info if verbose
|
|
21
|
+
puts COPYRIGHT if OPTIONS[:verbose]
|
|
44
22
|
|
|
45
|
-
|
|
23
|
+
# Quit if there is program specified.
|
|
24
|
+
self.show_banner_and_exit() if ARGV.length != 1
|
|
46
25
|
|
|
47
|
-
|
|
26
|
+
# Get the specified program.
|
|
27
|
+
prog = ARGV[0]
|
|
28
|
+
prog_file = File.expand_path(prog)
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
puts COPYRIGHT
|
|
54
|
-
puts
|
|
55
|
-
end
|
|
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"
|
|
33
|
+
end
|
|
56
34
|
|
|
57
|
-
#
|
|
58
|
-
if
|
|
59
|
-
|
|
35
|
+
# Quit the specified program does not exist.
|
|
36
|
+
if !File.exist?(prog_file)
|
|
37
|
+
fatal("#{ARGV[0]} is an invalid file.")
|
|
60
38
|
exit(1)
|
|
61
39
|
end
|
|
62
40
|
|
|
63
|
-
|
|
41
|
+
# Remember the program path for later use
|
|
42
|
+
OPTIONS[:prog_file] = prog_file
|
|
64
43
|
|
|
65
|
-
|
|
44
|
+
# Run the program file!
|
|
45
|
+
self.verbose("Running #{prog}")
|
|
46
|
+
eval "require '#{prog_file}'", TOPLEVEL_BINDING
|
|
47
|
+
self.verbose("Done running #{prog}")
|
|
66
48
|
|
|
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.
|
|
67
52
|
end
|
|
68
53
|
|
|
69
54
|
end
|
|
70
55
|
|
|
71
56
|
RubyBreaker.main()
|
|
72
|
-
|
|
@@ -8,6 +8,12 @@ require_relative "context"
|
|
|
8
8
|
|
|
9
9
|
module RubyBreaker
|
|
10
10
|
|
|
11
|
+
# This method returns true if the logger is already created and false
|
|
12
|
+
# otherwise.
|
|
13
|
+
def self.defined_logger?()
|
|
14
|
+
return defined?(LOGGER)
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
# This sets up the logger for debugging RubyBreaker
|
|
12
18
|
def self.setup_logger #:nodoc:
|
|
13
19
|
return if defined?(LOGGER)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require_relative "../type"
|
|
3
|
+
|
|
4
|
+
module RubyBreaker
|
|
5
|
+
|
|
6
|
+
# This module has functionalities that are necessary for supporting RDoc
|
|
7
|
+
# output
|
|
8
|
+
module RDocSupport
|
|
9
|
+
|
|
10
|
+
include TypeDefs
|
|
11
|
+
|
|
12
|
+
# This array keeps track of modules/classes whose type information is
|
|
13
|
+
# documented.
|
|
14
|
+
DOCUMENTED = {} # module => method map
|
|
15
|
+
|
|
16
|
+
# This method exports the RubyBreaker output into a yaml file.
|
|
17
|
+
def self.export_to_yaml(yaml_file, breakable_modules, broken_modules)
|
|
18
|
+
hash = {
|
|
19
|
+
breakable: breakable_modules,
|
|
20
|
+
broken: broken_modules
|
|
21
|
+
}
|
|
22
|
+
File.open(yaml_file, "w") do |f|
|
|
23
|
+
f.puts YAML.dump(hash)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# This method imports the RubyBreaker output from a yaml file.
|
|
28
|
+
def self.import_from_yaml(yaml_file)
|
|
29
|
+
File.open(yaml_file, "r") do |f|
|
|
30
|
+
raw = f.read
|
|
31
|
+
hash = YAML.to_hash(raw)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|