rubybreaker 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +4 -0
- data/README.md +33 -19
- data/Rakefile +31 -13
- data/TODO +6 -4
- data/VERSION +1 -1
- data/bin/rubybreaker +0 -1
- data/lib/rubybreaker/debug/debug.rb +1 -1
- data/lib/rubybreaker/runtime/typesig_unparser.rb +96 -0
- data/lib/rubybreaker/runtime.rb +1 -0
- data/lib/rubybreaker/test/rspec.rb +15 -0
- data/lib/rubybreaker/test/testcase.rb +4 -4
- data/lib/rubybreaker/test.rb +1 -0
- data/lib/rubybreaker/type/type_grammar.treetop +5 -4
- data/lib/rubybreaker/type/type_unparser.rb +69 -25
- data/lib/rubybreaker.rb +9 -77
- data/test/integrated/tc_namespace.rb +56 -0
- data/test/ts_integrated.rb +1 -0
- data/test/ts_rspec.rb +31 -0
- data/test/ts_type.rb +2 -0
- data/test/type/tc_camelize.rb +24 -0
- data/test/type/tc_namespace.rb +25 -0
- data/test/type/tc_unparser.rb +31 -32
- data/webpage/index.html +34 -19
- data/webpage/rdoc/Object.html +308 -0
- data/webpage/rdoc/RubyBreaker/Breakable.html +5 -1
- data/webpage/rdoc/RubyBreaker/Broken/BrokenEigen.html +5 -1
- data/webpage/rdoc/RubyBreaker/Broken.html +5 -1
- data/webpage/rdoc/RubyBreaker/Context.html +5 -1
- data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +4 -0
- data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +4 -0
- data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +4 -0
- data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +4 -0
- data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +4 -0
- data/webpage/rdoc/RubyBreaker/Errors/UserError.html +4 -0
- data/webpage/rdoc/RubyBreaker/Errors.html +4 -0
- data/webpage/rdoc/RubyBreaker/Main.html +17 -132
- data/webpage/rdoc/RubyBreaker/ObjectPosition.html +5 -1
- data/webpage/rdoc/RubyBreaker/Position.html +5 -1
- data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +5 -1
- data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +5 -1
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +5 -1
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/TypePlaceholder.html +5 -1
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +4 -0
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +5 -1
- data/webpage/rdoc/RubyBreaker/Runtime/TypesigUnparser.html +404 -0
- data/webpage/rdoc/RubyBreaker/Runtime.html +5 -0
- data/webpage/rdoc/RubyBreaker/TestCase.html +47 -43
- data/webpage/rdoc/RubyBreaker/TypeComparer.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +11 -6
- data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs.html +4 -0
- data/webpage/rdoc/RubyBreaker/TypeUnparser.html +15 -7
- data/webpage/rdoc/RubyBreaker/Typing.html +4 -0
- data/webpage/rdoc/RubyBreaker/Util.html +4 -0
- data/webpage/rdoc/RubyBreaker.html +6 -6
- data/webpage/rdoc/created.rid +9 -7
- data/webpage/rdoc/index.html +4 -0
- data/webpage/rdoc/js/search_index.js +1 -1
- data/webpage/rdoc/table_of_contents.html +36 -24
- metadata +13 -7
data/NEWS
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Introduction
|
4
4
|
|
5
|
-
RubyBreaker is a dynamic type documentation tool written
|
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
8
|
information. The type documentation generated by RubyBreaker is also an
|
@@ -13,7 +13,7 @@ program.
|
|
13
13
|
The primary goal of RubyBreaker is to assign a type signature to every
|
14
14
|
method in designated modules and classes. A type signature is written in
|
15
15
|
the RubyBreaker Type Annotation Language which resembles the documentation
|
16
|
-
style used in
|
16
|
+
style used in Ruby API Doc. Overall, this tool should help Ruby programmers
|
17
17
|
document their code more rigorously and effectively. Currently, manual
|
18
18
|
modification of the user program is required to run RubyBreaker, but this is
|
19
19
|
kept minimal.
|
@@ -25,10 +25,9 @@ be found in [here](rdoc/index.html).
|
|
25
25
|
|
26
26
|
## Limitations
|
27
27
|
|
28
|
-
* It only works on toy Ruby programs so far :)
|
29
28
|
* Block argument cannot be auto-documented. (Inherent)
|
30
29
|
* Manual modification (minimal) of code is required.
|
31
|
-
*
|
30
|
+
* Parametric polymorphic types are not supported.
|
32
31
|
|
33
32
|
## Requirements
|
34
33
|
|
@@ -129,7 +128,8 @@ In order to use RubyBreaker, there needs two kinds of manual code changes.
|
|
129
128
|
First, the user must indicate which modules are subject to analysis and
|
130
129
|
which modules can be used for the analysis. Next, the user has to indicate
|
131
130
|
where the entry point of the program is. Alternatively, he has to make a
|
132
|
-
small change to the test cases to use RubyBreaker's testing framework.
|
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
133
|
|
134
134
|
### Breakable and Broken
|
135
135
|
|
@@ -209,9 +209,7 @@ auto-documented and manually documented, respectively.
|
|
209
209
|
In Ruby, as soon as a file is `require`d, the execution of that file begins.
|
210
210
|
For RubyBreaker, however, it is not trivial to find the actual starting
|
211
211
|
point of the program because there *has* to be a clear point in time at
|
212
|
-
which monitoring of `Breakable` modules begins.
|
213
|
-
attempting to instrument and monitor at the same time will cause an infinite
|
214
|
-
loop!*
|
212
|
+
which monitoring of `Breakable` modules begins.
|
215
213
|
|
216
214
|
Indicating the program entry point is simply done by inserting the following
|
217
215
|
line at the code (assuming "`require 'rubybreaker'`" is already placed at
|
@@ -226,11 +224,11 @@ run the instrumented code (for `Breakable` modules) which will gather type
|
|
226
224
|
information for methods.
|
227
225
|
|
228
226
|
Although this seems simple and easy, this is not the recommended way for
|
229
|
-
analyzing a program. Why? Because RubyBreaker
|
230
|
-
|
231
|
-
|
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.
|
232
230
|
|
233
|
-
### Using
|
231
|
+
### Using the Built-in Testing Framework
|
234
232
|
|
235
233
|
Instead of manually inserting the entry point indicator into the program,
|
236
234
|
the user can take advantage of the Ruby Unit Test framework. This is the
|
@@ -245,11 +243,27 @@ one.
|
|
245
243
|
# ...tests!...
|
246
244
|
end
|
247
245
|
|
248
|
-
That's it!
|
246
|
+
That's it! Also, remember that classes and modules can be re-opened for
|
247
|
+
"patches" in Ruby. This means that the original classes or modules do not
|
248
|
+
need to be modified to include `Broken` and/or `Breakable`. In each test
|
249
|
+
suite or test case, those classes and modules can be re-opened to include
|
250
|
+
`Broken` and/or `Breakable`. The following shows an example of such a test
|
251
|
+
case.
|
249
252
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
+
require "rubybreaker"
|
254
|
+
require "test/unit"
|
255
|
+
class A
|
256
|
+
include RubyBreaker::Breakable
|
257
|
+
end
|
258
|
+
class TestClassA < Test::Unit::TestCase
|
259
|
+
...
|
260
|
+
end
|
261
|
+
|
262
|
+
### Using RSpec
|
263
|
+
|
264
|
+
If using RSpec, it is even easier! No changes are needed for the test code.
|
265
|
+
Really! _Well, it is still necessary to `require rubybreaker` and manually
|
266
|
+
insert `Breakable` and `Broken` to appropriate classes and modules._
|
253
267
|
|
254
268
|
## Type Annotation
|
255
269
|
|
@@ -286,7 +300,9 @@ instance, `fixnum` is an object of type `Fixnum`. Use lower-case letters and
|
|
286
300
|
underscores instead of _camelized_ name. `MyClass`, for example would be
|
287
301
|
`my_class` in RubyBreaker type signatures. There is no particular
|
288
302
|
reason for this convention other than it is the common practice used in
|
289
|
-
RubyDoc.
|
303
|
+
RubyDoc. Use `/` to indicate the namespace delimiter `::`. For example,
|
304
|
+
`NamspaceA::ClassB` would be represented by `namespace_a/class_b` in
|
305
|
+
a RubyBreaker type signature.
|
290
306
|
|
291
307
|
### Self Type
|
292
308
|
|
@@ -443,8 +459,6 @@ The term, "Fusion Type," is first coined by Professor Michael W. Hicks at
|
|
443
459
|
University of Maryland and represents an object using a structural type with
|
444
460
|
respect to a nominal type.
|
445
461
|
|
446
|
-
* * *
|
447
|
-
|
448
462
|
# Copyright
|
449
463
|
Copyright (c) 2012 Jong-hoon (David) An. All Rights Reserved.
|
450
464
|
|
data/Rakefile
CHANGED
@@ -9,7 +9,13 @@ require "rake/clean"
|
|
9
9
|
begin
|
10
10
|
require "rdiscount" # used to generate the doc html page
|
11
11
|
rescue LoadError => e
|
12
|
-
puts "[WARNING] No rdiscount is installed."
|
12
|
+
puts "[WARNING] No rdiscount is installed on this computer."
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'rspec/core/rake_task'
|
17
|
+
rescue LoadError => e
|
18
|
+
puts "[WARNING] No rspec-core is installed on this computer."
|
13
19
|
end
|
14
20
|
|
15
21
|
# Use rake/clean to remove generated files
|
@@ -23,7 +29,7 @@ CLEAN.concat(FileList["webpage/rdoc",
|
|
23
29
|
task :default => [:test]
|
24
30
|
|
25
31
|
desc "Do all"
|
26
|
-
task :all => [:parser, :test, :rdoc, :webpage, :gem] do |t|
|
32
|
+
task :all => [:parser, :test, :rspec, :rdoc, :webpage, :gem] do |t|
|
27
33
|
end
|
28
34
|
|
29
35
|
desc "Generate gemspec"
|
@@ -39,15 +45,16 @@ end
|
|
39
45
|
|
40
46
|
desc "Generate the webpage"
|
41
47
|
task :webpage do |t|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
if defined?(RDiscount)
|
49
|
+
dir = File.dirname(__FILE__)
|
50
|
+
readme_md = "#{dir}/README.md"
|
51
|
+
output = "#{dir}/webpage/index.html"
|
52
|
+
body = RDiscount.new(File.read(readme_md)).to_html
|
53
|
+
header = File.read("#{dir}/webpage/header.html")
|
54
|
+
footer = File.read("#{dir}/webpage/footer.html")
|
55
|
+
html = header + body + footer
|
56
|
+
File.open(output, "w") { |f| f.write(html) }
|
57
|
+
end
|
51
58
|
end
|
52
59
|
|
53
60
|
desc "Generate parser"
|
@@ -57,9 +64,20 @@ task :parser do |t|
|
|
57
64
|
end
|
58
65
|
|
59
66
|
desc "Run basic tests"
|
60
|
-
Rake::TestTask.new("test") do |t|
|
67
|
+
Rake::TestTask.new(:"test") do |t|
|
61
68
|
dir = File.dirname(__FILE__)
|
62
69
|
t.libs << "lib"
|
63
|
-
|
70
|
+
test_files = FileList["test/ts_*.rb"]
|
71
|
+
test_files.exclude("test/ts_rspec.rb")
|
72
|
+
t.test_files = test_files
|
73
|
+
end
|
74
|
+
|
75
|
+
if defined?(RSpec)
|
76
|
+
desc "Run RSpec test"
|
77
|
+
RSpec::Core::RakeTask.new(:rspec) do |t|
|
78
|
+
t.pattern = ["test/ts_rspec.rb"]
|
79
|
+
t.fail_on_error = false
|
80
|
+
end
|
64
81
|
end
|
65
82
|
|
83
|
+
|
data/TODO
CHANGED
@@ -6,11 +6,13 @@ to be dynamically instrumented (as "breakable" methods are) and inspected
|
|
6
6
|
for each argument type and return type. Although this is still a dynamic
|
7
7
|
type checking, it finds type errors earlier in the program execution.
|
8
8
|
|
9
|
-
#
|
9
|
+
# Additional Testing Frameworks
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
RubyBreaker supports the built-in testing framework and RSpec. There are
|
12
|
+
other testing frameworks such as Cucumber or Shouda. Since RubyBreaker's
|
13
|
+
goal is to help Ruby programmers write better code by taking advantage of
|
14
|
+
existing test suites, it seems appropriate to support as many testing
|
15
|
+
frameworks as possible.
|
14
16
|
|
15
17
|
# RDoc and YARD Documentation Support
|
16
18
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/bin/rubybreaker
CHANGED
@@ -11,7 +11,7 @@ module RubyBreaker
|
|
11
11
|
# This sets up the logger for debugging RubyBreaker
|
12
12
|
def self.setup_logger #:nodoc:
|
13
13
|
return if defined?(LOGGER)
|
14
|
-
out = if (defined?(OPTIONS) && !OPTIONS[:file].empty?)
|
14
|
+
out = if (defined?(OPTIONS) && OPTIONS[:debug] && !OPTIONS[:file].empty?)
|
15
15
|
then "#{OPTIONS[:file]}.log"
|
16
16
|
else STDOUT
|
17
17
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "prettyprint"
|
2
|
+
require_relative "../type"
|
3
|
+
|
4
|
+
module RubyBreaker
|
5
|
+
|
6
|
+
module Runtime
|
7
|
+
|
8
|
+
# This module handles unparsing type signatures.
|
9
|
+
module TypesigUnparser
|
10
|
+
|
11
|
+
include TypeDefs
|
12
|
+
|
13
|
+
# This array lists monitored modules/classes that are outputed.
|
14
|
+
DOCUMENTED = []
|
15
|
+
|
16
|
+
# Pretty prints type information for methods
|
17
|
+
def self.pp_methods(pp, meth_type_map, opts={})
|
18
|
+
meth_type_map.each { |meth_name, meth_type|
|
19
|
+
case meth_type
|
20
|
+
when MethodType
|
21
|
+
pp.breakable()
|
22
|
+
pp.text("typesig(\"")
|
23
|
+
TypeUnparser.unparse_pp(pp, meth_type, opts)
|
24
|
+
pp.text("\")")
|
25
|
+
when MethodListType
|
26
|
+
meth_type.types.each { |real_meth_type|
|
27
|
+
pp.breakable()
|
28
|
+
pp.text("typesig(\"")
|
29
|
+
TypeUnparser.unparse_pp(pp, real_meth_type, opts)
|
30
|
+
pp.text("\")")
|
31
|
+
}
|
32
|
+
else
|
33
|
+
# Can't happen
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Pretty prints type information for the module/class
|
39
|
+
def self.pp_module(pp, mod, opts={})
|
40
|
+
# Skip it if we already have seen it
|
41
|
+
return if DOCUMENTED.include?(mod) || mod.to_s[0..1] == "#<"
|
42
|
+
|
43
|
+
# Remember that we have documented this module/class
|
44
|
+
DOCUMENTED << mod
|
45
|
+
|
46
|
+
# Get the method type mapping
|
47
|
+
meth_type_map = Inspector.inspect_all(mod)
|
48
|
+
|
49
|
+
# Check if this module is a class
|
50
|
+
keyword = mod.instance_of?(Class) ? "class" : "module"
|
51
|
+
|
52
|
+
pp.text("#{keyword} #{mod.to_s}", 80)
|
53
|
+
pp.nest(2) do
|
54
|
+
pp.breakable("")
|
55
|
+
pp.text("include RubyBreaker::Broken", 80)
|
56
|
+
|
57
|
+
# See if there is any class method to show
|
58
|
+
eigen = Runtime.eigen_class(mod)
|
59
|
+
if !DOCUMENTED.include?(eigen)
|
60
|
+
DOCUMENTED << eigen
|
61
|
+
eigen_meth_type_map = Inspector.inspect_all(eigen)
|
62
|
+
if eigen_meth_type_map.size > 0
|
63
|
+
pp.breakable()
|
64
|
+
pp.text("class << self", 80)
|
65
|
+
pp.nest(2) do
|
66
|
+
self.pp_methods(pp, eigen_meth_type_map, :namespace => eigen)
|
67
|
+
end
|
68
|
+
pp.breakable()
|
69
|
+
pp.text("end", 80)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
self.pp_methods(pp, meth_type_map, :namespace => mod)
|
74
|
+
|
75
|
+
end
|
76
|
+
pp.breakable()
|
77
|
+
pp.text("end",80)
|
78
|
+
pp.breakable()
|
79
|
+
end
|
80
|
+
|
81
|
+
# This method unparses the type information in the specified module,
|
82
|
+
# displaying one or more type signatures for each method that is
|
83
|
+
# monitored during runtime.
|
84
|
+
def self.unparse(mod, opts={})
|
85
|
+
str = ""
|
86
|
+
pp = PrettyPrint.new(str)
|
87
|
+
self.pp_module(pp, mod, opts)
|
88
|
+
pp.flush
|
89
|
+
return str
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/lib/rubybreaker/runtime.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
#-
|
2
|
+
# This file overrides the describe method of RSpec to call the RubyBreaker
|
3
|
+
# setup first.
|
4
|
+
|
5
|
+
RUBYBREAKER_RSPEC_PREFIX = "__rubybreaker"
|
6
|
+
|
7
|
+
if defined?(RSpec)
|
8
|
+
alias :"#{RUBYBREAKER_RSPEC_PREFIX}_describe" :describe
|
9
|
+
end
|
10
|
+
|
11
|
+
def describe(*args,&blk)
|
12
|
+
RubyBreaker::Main.setup if defined?(RubyBreaker)
|
13
|
+
send(:"#{RUBYBREAKER_RSPEC_PREFIX}_describe", *args, &blk)
|
14
|
+
end
|
15
|
+
|
@@ -9,11 +9,11 @@ module RubyBreaker
|
|
9
9
|
# class.
|
10
10
|
module TestCase
|
11
11
|
|
12
|
-
def self.
|
12
|
+
def self.__rubybreaker_setup()
|
13
13
|
Main.setup()
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.
|
16
|
+
def self.__rubybreaker_teardown()
|
17
17
|
# Main.output()
|
18
18
|
end
|
19
19
|
|
@@ -25,9 +25,9 @@ module RubyBreaker
|
|
25
25
|
alias :__run :run
|
26
26
|
|
27
27
|
def run(*args,&blk)
|
28
|
-
RubyBreaker::TestCase.
|
28
|
+
RubyBreaker::TestCase.__rubybreaker_setup()
|
29
29
|
__run(*args,&blk)
|
30
|
-
RubyBreaker::TestCase.
|
30
|
+
RubyBreaker::TestCase.__rubybreaker_teardown()
|
31
31
|
end
|
32
32
|
|
33
33
|
EOS
|
data/lib/rubybreaker/test.rb
CHANGED
@@ -227,15 +227,16 @@ grammar TypeGrammar
|
|
227
227
|
end
|
228
228
|
|
229
229
|
rule nominal_type
|
230
|
-
[a-z_]+ {
|
230
|
+
[a-z_/]+ { # FIXME: not really accurate
|
231
231
|
def value
|
232
232
|
pos = RubyBreaker::Position.get()
|
233
233
|
pos.col = interval.first
|
234
234
|
begin
|
235
235
|
mod_name = RubyBreaker::Util.camelize(text_value)
|
236
|
-
mod = eval
|
237
|
-
t = RubyBreaker::NominalType.new(mod,pos)
|
238
|
-
rescue
|
236
|
+
mod = eval(mod_name) # do it at the current binding
|
237
|
+
t = RubyBreaker::NominalType.new(mod, pos)
|
238
|
+
rescue => e
|
239
|
+
puts e
|
239
240
|
t = RubyBreaker::AnyType.new(pos)
|
240
241
|
end
|
241
242
|
return t # RubyBreaker::NominalType.new(text_value,pos)
|
@@ -15,16 +15,45 @@ module RubyBreaker
|
|
15
15
|
|
16
16
|
private
|
17
17
|
|
18
|
+
# This method resolves the mod_name's namespace with respect to the
|
19
|
+
# namespace. For example, if the current namespace is
|
20
|
+
#
|
21
|
+
# A::B
|
22
|
+
#
|
23
|
+
# and the given module is A::B::C::D, then we show
|
24
|
+
#
|
25
|
+
# C::D
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# If the current namespace is
|
29
|
+
#
|
30
|
+
# A::B::C
|
31
|
+
#
|
32
|
+
# and the given module is A::B::D::E, then we show
|
33
|
+
#
|
34
|
+
# D::E
|
35
|
+
#
|
36
|
+
def self.resolve_namespace(namespace, mod_name)
|
37
|
+
return mod_name if namespace == nil || namespace.empty?
|
38
|
+
if mod_name.start_with?(namespace)
|
39
|
+
pattern = "^#{namespace}::"
|
40
|
+
return mod_name.sub(/#{pattern}/, "")
|
41
|
+
end
|
42
|
+
tokens = namespace.split("::")
|
43
|
+
return mod_name if tokens.size <= 1
|
44
|
+
return self.resolve_namespace(tokens[0..-2].join("::"), mod_name)
|
45
|
+
end
|
46
|
+
|
18
47
|
# This method is used to determine if the inner type of +t+ should be
|
19
48
|
# wrapped around a parenthesis. This is for optional type and variable
|
20
49
|
# length type.
|
21
|
-
def self.peek_and_unparse_pp_inner_type(pp,t)
|
50
|
+
def self.peek_and_unparse_pp_inner_type(pp, t, opts={})
|
22
51
|
if t.type.kind_of?(OrType)
|
23
52
|
pp.text("(")
|
24
|
-
self.unparse_pp(pp,t.type)
|
53
|
+
self.unparse_pp(pp, t.type, opts)
|
25
54
|
pp.text(")")
|
26
55
|
else
|
27
|
-
self.unparse_pp(pp,t.type)
|
56
|
+
self.unparse_pp(pp, t.type, opts)
|
28
57
|
end
|
29
58
|
end
|
30
59
|
|
@@ -35,23 +64,34 @@ module RubyBreaker
|
|
35
64
|
|
36
65
|
# This recursive method unparses a RubyBreaker type using the pretty
|
37
66
|
# print method.
|
38
|
-
def self.unparse_pp(pp,t)
|
67
|
+
def self.unparse_pp(pp, t, opts={})
|
39
68
|
if t.instance_of?(NominalType)
|
40
|
-
|
41
|
-
|
42
|
-
|
69
|
+
if opts[:namespace] && opts[:namespace].name
|
70
|
+
# resolve the namespace for the module/class given the current
|
71
|
+
# namespace
|
72
|
+
mod_name = self.resolve_namespace(opts[:namespace].name, t.mod.name)
|
73
|
+
else
|
74
|
+
mod_name = t.mod.name
|
75
|
+
end
|
76
|
+
unless opts[:style] == :camelize
|
77
|
+
tname = Util.underscore(mod_name)
|
78
|
+
else
|
79
|
+
tname = mod_name
|
80
|
+
end
|
81
|
+
# tokens = tname.split("/")
|
82
|
+
# tname = tokens.last if tokens.size > 1
|
43
83
|
pp.text(tname)
|
44
84
|
elsif t.instance_of?(SelfType)
|
45
85
|
pp.text("self")
|
46
86
|
elsif t.instance_of?(DuckType)
|
47
87
|
unparse_pp_object_type(pp,t)
|
48
88
|
elsif t.instance_of?(FusionType)
|
49
|
-
unparse_pp(pp,t.nom_type)
|
89
|
+
unparse_pp(pp, t.nom_type, opts)
|
50
90
|
unparse_pp_object_type(pp,t)
|
51
91
|
elsif t.instance_of?(MethodType)
|
52
92
|
pp.text("#{t.meth_name}(")
|
53
93
|
t.arg_types.each_with_index do |arg_type,i|
|
54
|
-
unparse_pp(pp,arg_type)
|
94
|
+
unparse_pp(pp, arg_type, opts)
|
55
95
|
if i < t.arg_types.size - 1
|
56
96
|
pp.text(",")
|
57
97
|
pp.fill_breakable()
|
@@ -61,17 +101,17 @@ module RubyBreaker
|
|
61
101
|
pp.fill_breakable()
|
62
102
|
if t.blk_type
|
63
103
|
pp.text("{")
|
64
|
-
unparse_pp(pp,t.blk_type)
|
104
|
+
unparse_pp(pp, t.blk_type, opts)
|
65
105
|
pp.text("}")
|
66
106
|
pp.fill_breakable()
|
67
107
|
end
|
68
108
|
pp.text("->")
|
69
109
|
pp.fill_breakable()
|
70
|
-
unparse_pp(pp,t.ret_type)
|
110
|
+
unparse_pp(pp, t.ret_type, opts)
|
71
111
|
elsif t.instance_of?(BlockType)
|
72
112
|
pp.text("|")
|
73
113
|
t.arg_types.each_with_index do |arg_type,i|
|
74
|
-
unparse_pp(pp,arg_type)
|
114
|
+
unparse_pp(pp, arg_type, opts)
|
75
115
|
if i < t.arg_types.size - 1
|
76
116
|
pp.text(",")
|
77
117
|
pp.fill_breakable()
|
@@ -81,33 +121,33 @@ module RubyBreaker
|
|
81
121
|
pp.fill_breakable()
|
82
122
|
if t.blk_type
|
83
123
|
pp.text("{")
|
84
|
-
unparse_pp(pp,t.blk_type)
|
124
|
+
unparse_pp(pp, t.blk_type, opts)
|
85
125
|
pp.text("}")
|
86
126
|
pp.fill_breakable()
|
87
127
|
end
|
88
128
|
pp.text("->")
|
89
129
|
pp.fill_breakable()
|
90
|
-
unparse_pp(pp,t.ret_type)
|
130
|
+
unparse_pp(pp, t.ret_type, opts)
|
91
131
|
elsif t.instance_of?(MethodListType)
|
92
132
|
t.types.each_with_index do |typ,i|
|
93
|
-
unparse_pp(pp,typ)
|
133
|
+
unparse_pp(pp, typ, opts)
|
94
134
|
if i < t.types.size - 1
|
95
135
|
pp.fill_breakable()
|
96
136
|
end
|
97
137
|
end
|
98
138
|
elsif t.instance_of?(OrType)
|
99
139
|
t.types.each_with_index do |typ,i|
|
100
|
-
unparse_pp(pp,typ)
|
140
|
+
unparse_pp(pp, typ, opts)
|
101
141
|
if i < t.types.size - 1
|
102
142
|
pp.text(" ||")
|
103
143
|
pp.fill_breakable()
|
104
144
|
end
|
105
145
|
end
|
106
146
|
elsif t.instance_of?(OptionalType)
|
107
|
-
peek_and_unparse_pp_inner_type(pp,t)
|
147
|
+
peek_and_unparse_pp_inner_type(pp, t, opts)
|
108
148
|
pp.text("?")
|
109
149
|
elsif t.instance_of?(VarLengthType)
|
110
|
-
peek_and_unparse_pp_inner_type(pp,t)
|
150
|
+
peek_and_unparse_pp_inner_type(pp, t, opts)
|
111
151
|
pp.text("*")
|
112
152
|
elsif t.instance_of?(NilType)
|
113
153
|
pp.text("nil")
|
@@ -119,12 +159,16 @@ module RubyBreaker
|
|
119
159
|
|
120
160
|
public
|
121
161
|
|
122
|
-
# This method
|
123
|
-
#
|
124
|
-
|
162
|
+
# This method unparses the RubyBreaker type according to the specified
|
163
|
+
# options.
|
164
|
+
#
|
165
|
+
# t:: RubyBreaker type
|
166
|
+
# opts::
|
167
|
+
#
|
168
|
+
def self.unparse(t, opts={})
|
125
169
|
str = ""
|
126
170
|
pp = PrettyPrint.new(str)
|
127
|
-
self.unparse_pp(pp,t)
|
171
|
+
self.unparse_pp(pp, t, opts)
|
128
172
|
pp.flush
|
129
173
|
return str.strip()
|
130
174
|
end
|
@@ -132,9 +176,9 @@ module RubyBreaker
|
|
132
176
|
|
133
177
|
class TypeDefs::Type
|
134
178
|
|
135
|
-
# This method
|
136
|
-
def unparse()
|
137
|
-
TypeUnparser.unparse(self)
|
179
|
+
# This method is a shorthand for calling TypeUnparser.unparse(t).
|
180
|
+
def unparse(opts={})
|
181
|
+
TypeUnparser.unparse(self, opts)
|
138
182
|
end
|
139
183
|
end
|
140
184
|
|