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/ABOUT.md ADDED
@@ -0,0 +1,20 @@
1
+ # About
2
+
3
+ RubyBreaker has its root in Rubydust (which stands for Ruby Dynamic
4
+ Unraveling of Static Types), an academic research project designed and
5
+ implemented at University of Maryland. However, unlike Rubydust,
6
+ RubyBreaker aims to be a practical documentation tool for Ruby rather than a
7
+ full-scale type inference tool. Although it is certainly possible that
8
+ RubyBreaker evolves into something more solid in its type system, the
9
+ primary goal of this project is to help Ruby programmers practically as much
10
+ as possible.
11
+
12
+ ## Acknowledgment
13
+
14
+ The term, "Fusion Type," is first coined by Professor Michael W. Hicks at
15
+ University of Maryland and represents an object using a structural type with
16
+ respect to a nominal type.
17
+
18
+ ## Copyright
19
+ Copyright (c) 2012 Jong-hoon (David) An. All Rights Reserved.
20
+
data/NEWS CHANGED
@@ -1,3 +1,8 @@
1
+ # VERSION 0.0.6
2
+ * Running RubyBreaker in shell mode does not require manual code change.
3
+ * Official RubyBreaker logo!
4
+ * Deprecating breakable(). Use break() instead.
5
+
1
6
  # VERSION 0.0.5
2
7
  * Rake::RubyBreakerTestTask is supported.
3
8
  * Manual modification is no longer required if run as a Rake task.
data/README.md CHANGED
@@ -1,33 +1,33 @@
1
- * * *
2
-
3
1
  # Introduction
4
2
 
5
3
  RubyBreaker is a dynamic type documentation tool written in pure Ruby. It
6
4
  provides the framework for dynamically instrumenting a Ruby program to
7
- monitor objects during executions and document the observed type
5
+ monitor objects during the execution and document the observed type
8
6
  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.
7
+ obscurity and wildness (as in "code breaking" or "horse breaking") by
8
+ auto-documenting type information. The type documentation generated by
9
+ RubyBreaker is also an executable Ruby code that can be used as an input to
10
+ subsequent analyses.
13
11
 
14
12
  The primary goal of RubyBreaker is to assign a type signature to every
15
13
  method in selected modules and classes. A type signature is written in the
16
14
  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.
15
+ used in Ruby Core Library Doc. No manual code change is required. Overall,
16
+ this tool should help Ruby programmers document their code more rigorously
17
+ and effectively.
20
18
 
21
- Current limitations are:
19
+ Currently, RubyBreaker *cannot*
22
20
 
23
- * Auto-documentation of block arguments (inherent)
24
- * Parametric polymorphic types
25
- * RDoc or YARD documentation support
21
+ * Auto-document block arguments (inherent)
22
+ * Perform early dynamic type checks
23
+ * Support parametric polymorphic types
24
+ * Support RDoc or YARD output format
26
25
 
27
26
  To contribute to the project, visit RubyBreaker's
28
27
  [GitHub page](http://github.com/rockalizer/rubybreaker) and
29
- [RubyGems page](http://rubygems.org/gems/rubybreaker). RubyBreaker RDoc can
30
- be found in [here](rdoc/index.html).
28
+ [RubyGems page](http://rubygems.org/gems/rubybreaker). The web version of
29
+ this document can be found
30
+ [here](http://rockalizer.webfactional.com/projects/rubybreaker).
31
31
 
32
32
  ## Requirements
33
33
 
@@ -43,339 +43,3 @@ It is as simple as running the following command:
43
43
 
44
44
  $ gem install rubybreaker
45
45
 
46
- * * *
47
-
48
- # Tutorial
49
-
50
- This tutorial will describe the basic usage of the tool, the RubyBreaker
51
- Type Annotation Language, and the RubyBreaker Type System.
52
-
53
- ## Usage
54
-
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.
63
-
64
- $ rubybreaker -v prog.rb
65
-
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:
71
-
72
- require "rubybreaker" # required if using "ruby" instead
73
- class A
74
- def foo(x)
75
- x.to_s
76
- end
77
- end
78
- class B
79
- def bar(y,z)
80
- y.foo(z)
81
- end
82
- end
83
- RubyBreaker.run(A, B)
84
- A.new.foo(1)
85
-
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`.
89
-
90
- # This file is auto-generated by RubyBreaker
91
- require "rubybreaker"
92
- class A
93
- typesig("foo(fixnum[to_s]) -> string")
94
- end
95
-
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:
101
-
102
- # This file is auto-generated by RubyBreaker
103
- require "rubybreaker"
104
- class A
105
- typesig("foo(fixnum[to_s]) -> string")
106
- end
107
- class B
108
- typesig("bar(a[foo], fixnum[to_s]) -> string")
109
- end
110
-
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.
118
-
119
- ### Using Ruby Unit Testing Framework
120
-
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.
126
-
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
- ...
133
- end
134
- # ...tests!...
135
- end
136
-
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`.
140
-
141
- ### Using RSpec
142
-
143
- The requirement is same for RSpec but use `before` instead of `setup` to
144
- specify which modules and classes to "break".
145
-
146
- require "rspec"
147
- require "rubybreaker"
148
-
149
- describe "TestClassA Test"
150
- before { RubyBreaker.breakable(Class1, Class2, ...) }
151
- ...
152
- # ...tests!...
153
- end
154
-
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
171
- end
172
-
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).
180
-
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.
184
-
185
- ## Type Annotation
186
-
187
- The annotation language used in RubyBreaker resembles the method
188
- documentation used by Ruby Standard Library Doc. Each type signature
189
- defines a method type using the name, argument types, block type, and return
190
- type. But, let us consider a simple case where there is one argument type
191
- and a return type.
192
-
193
- class A
194
- ...
195
- typesig("foo(fixnum) -> string")
196
- end
197
-
198
- In RubyBreaker, a type signature is recognized by the meta-class level
199
- method `typesig` which takes a string as an argument. This string is the
200
- actual type signature written in the Ruby Type Annotation Language. This
201
- language is designed to reflect the common documentation practice used by
202
- RubyDoc. It starts with the name of the method. In the above example, `foo`
203
- is currently being given a type. The rest of the signature takes a typical
204
- method type symbol, `(x) -> y` where `x` is the argument type and `y` is the
205
- return type. In the example shown above, the method takes a `Fixnum` object
206
- and returns a `String` object. Note that these types are in lowercase,
207
- indicating they are objects and not modules or classes themselves.
208
-
209
- There are several types that represent an object: nominal, duck, fusion,
210
- nil, 'any', 'or', optional, variable-length, and block. Each type signature
211
- itself represents a method type or a method list type (explained below).
212
-
213
- ### Nominal Type
214
-
215
- This is the simplest and most intuitive way to represent an object. For
216
- instance, `fixnum` is an object of type `Fixnum`. Use lower-case letters and
217
- underscores instead of _camelized_ name. `MyClass`, for example would be
218
- `my_class` in RubyBreaker type signatures. There is no particular
219
- reason for this convention other than it is the common practice used in
220
- RubyDoc. Use `/` to indicate the namespace delimiter `::`. For example,
221
- `NamspaceA::ClassB` would be represented by `namespace_a/class_b` in
222
- a RubyBreaker type signature.
223
-
224
- ### Self Type
225
-
226
- This type is similar to the nominal type but is referring to the current
227
- object--that is, the receiver of the method being typed. RubyBreaker will
228
- auto-document the return type as a self type if the return value is the same
229
- as the receiver of that call. It is also recommended to use this type over
230
- a nominal type (if the return value is `self`) since it depicts more
231
- precise return type.
232
-
233
- ### Duck Type
234
-
235
- This type is inspired by the Ruby Language's duck typing, _"if it
236
- walks like a duck and quacks like a duck, it must be a duck."_ Using this
237
- type, an object can be represented simply by a list of method names. For
238
- example `[walks, quacks]` is an object that has `walks` and `quacks`
239
- methods. Note that these method names do *not* reveal any type
240
- information for themselves.
241
-
242
- ### Fusion Type
243
-
244
- Duck type is very flexible but can be too lenient when trying to restrict
245
- the type of an object. RubyBreaker provides a type called *the fusion type*
246
- which lists method names but with respect to a nominal type. For
247
- example, `fixnum[to_f, to_s]` represents an object that has methods `to_f`
248
- and `to_s` whose types are same as those of `Fixnum`. This is more
249
- restrictive (precise) than `[to_f, to_s]` because the two methods must have
250
- the same types as `to_f` and `to_s` methods, respectively, in `Fixnum`.
251
-
252
- ### Nil Type
253
-
254
- A nil type represents a value of nil and is denoted by `nil`.
255
-
256
- ### Any Type
257
-
258
- RubyBreaker also provides a way to represent an object that is compatible with
259
- any type. This type is denoted by `?`. Use caution with this type because
260
- it should be only used for an object that requires an arbitrary yet most
261
- specific type--that is, `?` is a subtype of any other type, but any
262
- other type is not a subtype of `?`. This becomes a bit complicated for
263
- method or block argument types because of their contra-variance
264
- characteristic. Please refer to the section *Subtyping*.
265
-
266
- ### Or Type
267
-
268
- Any above types can be "or"ed together, using `||`, to represent an object
269
- that can be either one or the other. It _does_ not represent an object that
270
- has to be both (which is not supported by RubyBreaker).
271
-
272
- ### Optional Argument Type and Variable-Length Argument Type
273
-
274
- Another useful features of Ruby are the optional argument type and the
275
- variable-length argument type. The former represents an argument that has a
276
- default value (and therefore does not have to be provided). The latter
277
- represents zero or more arguments of the same type. These are denoted by
278
- suffices, `?` and `*`, respectively.
279
-
280
- ### Block Type
281
-
282
- One of the Ruby's prominent features is the block argument. It allows
283
- the caller to pass in a piece of code to be executed inside the callee. This
284
- code block can be executed by the Ruby construct, `yield`, or by directly
285
- calling the `call` method of the block object. In RubyBreaker, this type can
286
- be respresented by curly brackets. For instance, `{|fixnum,string| ->
287
- string}` represents a block that takes two arguments--one `Fixnum` and one
288
- `String`--and returns a `String`.
289
-
290
- RubyBreaker does supports nested blocks as Ruby 1.9 finally allows them.
291
- However, *keep in mind* that RubyBreaker *cannot* automatically document the
292
- block types due to `yield` being a language construct rather than a method,
293
- which means it cannot be captured by meta-programming!
294
-
295
- ### Method Type and Method List Types
296
-
297
- Method type is similar to the block type, but it represents an actual method
298
- and not a block object. It is the "root" type that the type annotation
299
- language supports, along with method list types. Method _list_ type is a
300
- collection of method types to represent more than one type information for
301
- the given method. Why would this type be needed? Consider the following Ruby
302
- code:
303
-
304
- def foo(x)
305
- case x
306
- when Fixnum
307
- 1
308
- when String
309
- "1"
310
- end
311
- end
312
-
313
- There is no way to document the type of `foo` without using a method list
314
- type. Let's try to give a method type to `foo` without a method list. The
315
- closest we can come up with would be `foo(fixnum or string) -> fixnum and
316
- string`. But RubyBreaker does not have the "and" type in the type annotation
317
- language because it gives me an headache! (By the way, it needs to be an
318
- "and" type because the caller must handle both `Fixnum` and `String` return
319
- values.)
320
-
321
- It is a dilemma because Ruby programmers actually enjoy using this kind of
322
- dynamic type checks in their code. To alleviate this headache, RubyBreaker
323
- supports the method list type to represent different scenarios depending on
324
- the argument types. Thus, the `foo` method shown above can be given the
325
- following method list type:
326
-
327
- typesig("foo(fixnum) -> fixnum")
328
- typesig("foo(string) -> string")
329
-
330
- These two type signatures simply tell RubyBreaker that `foo` has two method
331
- types--one for a `Fixnum` argument and another for a `String` argument.
332
- Depending on the argument type, the return type is determined. In this
333
- example, a `Fixnum` is returned when the argument is also a `Fixnum` and a
334
- `String` is returned when the argument is also a `String`. When
335
- automatically documenting such a type, RubyBreaker looks for the (subtyping)
336
- compatibility between the return types and "promote" the method type to a
337
- method list type by spliting the type signature into two (or more in
338
- subsequent "promotions").
339
-
340
- ## Type System
341
-
342
- RubyBreaker comes with its own type system to auto-document the type
343
- information. Each method in a "breakable" module is dynamically instrumented
344
- to be monitored during runtime. This monitoring code observes the types of
345
- the arguments, block, and return value of each method. Once this information
346
- is gathered, RubyBreaker will compare it to the information gathered so far.
347
- If these two types are "compatiable", RubyBreaker will choose more general
348
- type of the two. Otherwise, RubyBreaker will use the method list type to
349
- accommodate two "incompatible" types.
350
-
351
- ### Subtyping and Subclassing
352
-
353
- RubyBreaker uses subtyping to choose one from the two "compatible" types.
354
- Two types are "compatible" if one is subtype of another. This means that the
355
- _subtype_ can be represented using the _supertype_ instead. This is why
356
- RubyBrekaer chooses the latter to document both types. RubyBreaker relies on
357
- subclassing of Ruby to determine a subtyping relationship between two types.
358
- For example, `Fixnum` is considered to be subtype of `Numeric` since the
359
- former is subclass of the latter. (Strictly speaking, `Fixnum` is not really
360
- subtype of `Numeric` because some methods are overriden in `Fixnum` with
361
- method types that are not subtype of the counterparts in `Numeric`. But,
362
- RubyBreaker is lenient and considers them compatible--that is, `Numeric` can
363
- represent any `Fixnum`.
364
-
365
- ### Pluggable Type System (Advanced)
366
-
367
- Yes, RubyBreaker was designed with the replaceable type system in mind. In
368
- other words, anyone can write his own type system and plug it into
369
- RubyBreaker. *Technical documentation coming soon...*
370
-
371
- * * *
372
-
373
- # Acknowledgment
374
-
375
- The term, "Fusion Type," is first coined by Professor Michael W. Hicks at
376
- University of Maryland and represents an object using a structural type with
377
- respect to a nominal type.
378
-
379
- # Copyright
380
- Copyright (c) 2012 Jong-hoon (David) An. All Rights Reserved.
381
-
data/Rakefile CHANGED
@@ -19,6 +19,16 @@ rescue LoadError => e
19
19
  puts "[WARNING] No rspec-core is installed on this computer."
20
20
  end
21
21
 
22
+ # This method generates html pages with header and footer.
23
+ def gen_page(md_file, html_file)
24
+ dir = File.dirname(__FILE__)
25
+ header = File.read("#{dir}/webpage/header.html")
26
+ footer = File.read("#{dir}/webpage/footer.html")
27
+ body = RDiscount.new(File.read(md_file)).to_html
28
+ html = header + body + footer
29
+ File.open(html_file, "w") { |f| f.write(html) }
30
+ end
31
+
22
32
  # Use rake/clean to remove generated files
23
33
  CLEAN.concat(FileList["webpage/rdoc",
24
34
  "rubybreaker-*.gem",
@@ -29,38 +39,42 @@ CLEAN.concat(FileList["webpage/rdoc",
29
39
  # If no task specified, do test
30
40
  task :default => [:test, :testtask_test]
31
41
 
42
+ # The complete list of tasks
43
+ all_tasks = [:parser,
44
+ :test,
45
+ :testtask_test,
46
+ :rspec,
47
+ :rdoc,
48
+ :webpage,
49
+ :gem]
50
+
51
+ # Remove rspec if RSpec is not defined (rspec is not installed)
52
+ all_tasks.delete(:rspec) if !defined?(RSpec)
53
+
32
54
  desc "Do all"
33
- task :all => [:parser,
34
- :test,
35
- :testtask_test,
36
- :rspec,
37
- :rdoc,
38
- :webpage,
39
- :gem] do |t|
40
- end
55
+ task :all => all_tasks
41
56
 
42
57
  desc "Generate gemspec"
43
58
  task :gem do |t|
44
59
  sh "gem build rubybreaker.gemspec"
45
60
  end
46
61
 
62
+ desc "Generate RDoc"
47
63
  Rake::RDocTask.new do |rd|
48
64
  rd.rdoc_dir = "#{File.dirname(__FILE__)}/webpage/rdoc"
49
65
  rd.rdoc_files.include("lib/**/*.rb")
50
66
  rd.rdoc_files.exclude("lib/rubybreaker/type/type_grammar.rb")
67
+ rd.options << "README.md" << "TUTORIAL.md"
51
68
  end
52
69
 
53
70
  desc "Generate the webpage"
54
71
  task :webpage do |t|
55
72
  if defined?(RDiscount)
56
73
  dir = File.dirname(__FILE__)
57
- readme_md = "#{dir}/README.md"
58
- output = "#{dir}/webpage/index.html"
59
- body = RDiscount.new(File.read(readme_md)).to_html
60
- header = File.read("#{dir}/webpage/header.html")
61
- footer = File.read("#{dir}/webpage/footer.html")
62
- html = header + body + footer
63
- File.open(output, "w") { |f| f.write(html) }
74
+ gen_page("#{dir}/README.md","#{dir}/webpage/index.html")
75
+ gen_page("#{dir}/TUTORIAL.md","#{dir}/webpage/tutorial.html")
76
+ gen_page("#{dir}/TOPICS.md","#{dir}/webpage/topics.html")
77
+ gen_page("#{dir}/ABOUT.md","#{dir}/webpage/about.html")
64
78
  end
65
79
  end
66
80
 
@@ -84,7 +98,7 @@ desc "Run rubybreaker testtask test"
84
98
  Rake::RubyBreakerTestTask.new(:"testtask_test") do |t|
85
99
  t.libs << "lib" << "test/tc_testtask/sample.rb"
86
100
  t.test_files = ["test/testtask/tc_testtask.rb"]
87
- t.breakable = ["SampleClassA"]
101
+ t.break = ["SampleClassA"]
88
102
  end
89
103
 
90
104
  if defined?(RSpec)
data/TOPICS.md ADDED
@@ -0,0 +1,55 @@
1
+ # Advanced Topics
2
+
3
+ ## RubyBreaker Type System
4
+
5
+ RubyBreaker comes with its own type system to auto-document the type
6
+ information. Each method in a "breakable" module is dynamically instrumented
7
+ to be monitored during runtime. This monitoring code observes the types of
8
+ the arguments, block, and return value of each method. Once type information
9
+ for a method is gathered, RubyBreaker will compare it to the information
10
+ gathered so far for the method. If these two method types are
11
+ "compatiable", RubyBreaker will choose more general type from the two.
12
+
13
+ If two method types are not "compatible", RubyBreaker will "promote" the
14
+ method type to a method list type to accommodate more than one
15
+ "incompatible" types. Let's first understand what RubyBreaker considers two
16
+ types as "compatible".
17
+
18
+ ### Subtyping and Subclassing
19
+
20
+ RubyBreaker uses _subtyping_ to determine two "compatible" method types,
21
+ which holds true if one is subtype or supertype of another. It chooses
22
+ the supertype of the two for the method because the objective is to find (1)
23
+ the most general type of the two and (2) the least general type that can
24
+ handle both. Note that, if the objective (2) is not required, we can
25
+ always use the most general method type:
26
+
27
+ (?*) -> basic_object
28
+
29
+ A method of this type takes any number of any objects and returns a
30
+ `BasicObject`. But this is _too_ general to use for type documetation.
31
+ Instead, we want to find the most specific general type possible (and
32
+ therefore, the least upper bound of the types observed for the method).
33
+
34
+ For simplicity (and practicality), RubyBreaker uses _subclassing_ to
35
+ determine subtyping for nominal types. For instance, `Fixnum` is considered
36
+ subtype of `Numeric` because the former is subclass of the latter. However,
37
+ keep in mind this is not necessarily true in the true subtyping theory
38
+ because some methods in the former override the counterparts in the latter,
39
+ resulting in different types that no longer hold the subtyping relationship.
40
+
41
+ If a method has some type information either from the manual documentation
42
+ or from the current documentation process, this information will be used
43
+ in addition to subclassing. For example, consider method types
44
+ `foo(class1[to_s]) -> string` and `foo(class2[to_s]) -> string`. Let's also
45
+ assume that classes `Class1` and `Class2` are being auto-documented. The
46
+ method `foo` will have the type `foo(class1[to_s])` if `Class1#to_s` is a
47
+ subtype of `Class2#to_s`. (The direction is not mistaken, it is due to the
48
+ contra-variant property of a method argument.) If these classes are neither
49
+ being auto-documented nor manaully documented, this holds only if `Class1`
50
+ is subclass of `Class2`.
51
+
52
+ ## Writing a Custom Type System
53
+
54
+ _Coming soon_
55
+