fancy 0.3.0
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/AUTHORS +7 -0
- data/LICENSE +19 -0
- data/README +173 -0
- data/Rakefile +255 -0
- data/bin/fancy +40 -0
- data/bin/fdoc +23 -0
- data/bin/fyi +22 -0
- data/bin/ifancy +46 -0
- data/boot/README +12 -0
- data/boot/code_loader.rb +165 -0
- data/boot/compile.fy +3 -0
- data/boot/fancy_ext.rb +13 -0
- data/boot/fancy_ext/block_env.rb +29 -0
- data/boot/fancy_ext/class.rb +26 -0
- data/boot/fancy_ext/kernel.rb +12 -0
- data/boot/fancy_ext/module.rb +89 -0
- data/boot/fancy_ext/object.rb +34 -0
- data/boot/fancy_ext/string_helper.rb +10 -0
- data/boot/load.rb +72 -0
- data/boot/rbx-compiler/README +12 -0
- data/boot/rbx-compiler/compiler.rb +24 -0
- data/boot/rbx-compiler/compiler/ast.rb +23 -0
- data/boot/rbx-compiler/compiler/ast/README +11 -0
- data/boot/rbx-compiler/compiler/ast/array_literal.rb +13 -0
- data/boot/rbx-compiler/compiler/ast/assign.rb +57 -0
- data/boot/rbx-compiler/compiler/ast/block.rb +70 -0
- data/boot/rbx-compiler/compiler/ast/class_def.rb +35 -0
- data/boot/rbx-compiler/compiler/ast/expression_list.rb +57 -0
- data/boot/rbx-compiler/compiler/ast/hash_literal.rb +11 -0
- data/boot/rbx-compiler/compiler/ast/identifier.rb +120 -0
- data/boot/rbx-compiler/compiler/ast/match.rb +81 -0
- data/boot/rbx-compiler/compiler/ast/message_send.rb +71 -0
- data/boot/rbx-compiler/compiler/ast/method_def.rb +116 -0
- data/boot/rbx-compiler/compiler/ast/node.rb +6 -0
- data/boot/rbx-compiler/compiler/ast/range_literal.rb +22 -0
- data/boot/rbx-compiler/compiler/ast/require.rb +20 -0
- data/boot/rbx-compiler/compiler/ast/return.rb +29 -0
- data/boot/rbx-compiler/compiler/ast/ruby_args.rb +35 -0
- data/boot/rbx-compiler/compiler/ast/script.rb +56 -0
- data/boot/rbx-compiler/compiler/ast/singleton_method_def.rb +39 -0
- data/boot/rbx-compiler/compiler/ast/string_literal.rb +14 -0
- data/boot/rbx-compiler/compiler/ast/super.rb +25 -0
- data/boot/rbx-compiler/compiler/ast/try_catch_block.rb +220 -0
- data/boot/rbx-compiler/compiler/ast/tuple_literal.rb +33 -0
- data/boot/rbx-compiler/compiler/command.rb +39 -0
- data/boot/rbx-compiler/compiler/compiler.rb +83 -0
- data/boot/rbx-compiler/compiler/stages.rb +99 -0
- data/boot/rbx-compiler/parser.rb +2 -0
- data/boot/rbx-compiler/parser/README +15 -0
- data/boot/rbx-compiler/parser/Rakefile +54 -0
- data/boot/rbx-compiler/parser/extconf.rb +3 -0
- data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/fancy_parser.c +46 -0
- data/boot/rbx-compiler/parser/fancy_parser.h +8 -0
- data/boot/rbx-compiler/parser/lexer.lex +180 -0
- data/boot/rbx-compiler/parser/parser.rb +356 -0
- data/boot/rbx-compiler/parser/parser.y +711 -0
- data/boot/rsexp_pretty_printer.rb +76 -0
- data/doc/api/fancy.css +93 -0
- data/doc/api/fancy.jsonp +1 -0
- data/doc/api/fdoc.js +187 -0
- data/doc/api/index.html +57 -0
- data/doc/api/underscore-min.js +18 -0
- data/doc/features.md +228 -0
- data/examples/argv.fy +8 -0
- data/examples/arithmetic.fy +7 -0
- data/examples/armstrong_numbers.fy +33 -0
- data/examples/array.fy +52 -0
- data/examples/blocks.fy +15 -0
- data/examples/boolean.fy +24 -0
- data/examples/call_with_receiver.fy +9 -0
- data/examples/class.fy +68 -0
- data/examples/closures.fy +24 -0
- data/examples/constant_access.fy +15 -0
- data/examples/default_args.fy +17 -0
- data/examples/define_methods.fy +15 -0
- data/examples/documentation.fy +57 -0
- data/examples/documentation_formatters.fy +25 -0
- data/examples/echo.fy +16 -0
- data/examples/empty_catch.fy +4 -0
- data/examples/exception.fy +9 -0
- data/examples/factorial.fy +12 -0
- data/examples/fibonacci.fy +16 -0
- data/examples/files.fy +23 -0
- data/examples/finally.fy +5 -0
- data/examples/game_of_life.fy +148 -0
- data/examples/hashes.fy +7 -0
- data/examples/hello_world.fy +6 -0
- data/examples/html_generator.fy +54 -0
- data/examples/implicit_return.fy +3 -0
- data/examples/matchers.fy +6 -0
- data/examples/methods.fy +29 -0
- data/examples/nested_classes.fy +27 -0
- data/examples/nested_try.fy +9 -0
- data/examples/numbers.fy +12 -0
- data/examples/pattern_matching.fy +40 -0
- data/examples/person.fy +65 -0
- data/examples/project-euler/01.fy +8 -0
- data/examples/project-euler/02.fy +21 -0
- data/examples/project-euler/28.fy +33 -0
- data/examples/rbx/and_or.fy +7 -0
- data/examples/rbx/blocks.fy +22 -0
- data/examples/rbx/classes.fy +32 -0
- data/examples/rbx/hello.fy +8 -0
- data/examples/rbx/include.fy +12 -0
- data/examples/rbx/inherit.fy +11 -0
- data/examples/rbx/methods.fy +15 -0
- data/examples/rbx/nested_classes.fy +9 -0
- data/examples/rbx/require.fy +3 -0
- data/examples/rbx/strings.fy +5 -0
- data/examples/regex.fy +7 -0
- data/examples/require.fy +7 -0
- data/examples/retry.fy +12 -0
- data/examples/return.fy +13 -0
- data/examples/ruby_require.fy +7 -0
- data/examples/ruby_send.fy +3 -0
- data/examples/singleton_methods.fy +21 -0
- data/examples/stupid_quicksort.fy +12 -0
- data/examples/threads.fy +18 -0
- data/examples/tuple.fy +8 -0
- data/examples/webserver/webserver.fy +18 -0
- data/lib/argv.fy +36 -0
- data/lib/array.fy +207 -0
- data/lib/block.fy +88 -0
- data/lib/boot.fy +41 -0
- data/lib/class.fy +106 -0
- data/lib/compiler.fy +14 -0
- data/lib/compiler/ast.fy +40 -0
- data/lib/compiler/ast/assign.fy +96 -0
- data/lib/compiler/ast/block.fy +84 -0
- data/lib/compiler/ast/class_def.fy +33 -0
- data/lib/compiler/ast/expression_list.fy +47 -0
- data/lib/compiler/ast/identifier.fy +113 -0
- data/lib/compiler/ast/literals.fy +122 -0
- data/lib/compiler/ast/match.fy +88 -0
- data/lib/compiler/ast/message_send.fy +110 -0
- data/lib/compiler/ast/method_def.fy +90 -0
- data/lib/compiler/ast/node.fy +7 -0
- data/lib/compiler/ast/range.fy +16 -0
- data/lib/compiler/ast/require.fy +15 -0
- data/lib/compiler/ast/return.fy +23 -0
- data/lib/compiler/ast/script.fy +52 -0
- data/lib/compiler/ast/singleton_method_def.fy +35 -0
- data/lib/compiler/ast/super.fy +17 -0
- data/lib/compiler/ast/try_catch.fy +176 -0
- data/lib/compiler/ast/tuple_literal.fy +34 -0
- data/lib/compiler/command.fy +51 -0
- data/lib/compiler/compiler.fy +73 -0
- data/lib/compiler/stages.fy +81 -0
- data/lib/directory.fy +17 -0
- data/lib/documentation.fy +115 -0
- data/lib/enumerable.fy +269 -0
- data/lib/eval.fy +31 -0
- data/lib/fancy_spec.fy +202 -0
- data/lib/fdoc.fy +359 -0
- data/lib/fdoc_hook.fy +10 -0
- data/lib/file.fy +54 -0
- data/lib/hash.fy +56 -0
- data/lib/main.fy +80 -0
- data/lib/method.fy +22 -0
- data/lib/nil_class.fy +56 -0
- data/lib/number.fy +87 -0
- data/lib/object.fy +170 -0
- data/lib/package.fy +61 -0
- data/lib/package/dependency.fy +24 -0
- data/lib/package/installer.fy +180 -0
- data/lib/package/specification.fy +55 -0
- data/lib/package/uninstaller.fy +15 -0
- data/lib/parser.fy +4 -0
- data/lib/parser/ext/README +15 -0
- data/lib/parser/ext/ext.c +42 -0
- data/lib/parser/ext/ext.h +8 -0
- data/lib/parser/ext/extconf.rb +3 -0
- data/lib/parser/ext/lexer.lex +187 -0
- data/lib/parser/ext/parser.y +744 -0
- data/lib/parser/methods.fy +297 -0
- data/lib/rbx.fy +37 -0
- data/lib/rbx/array.fy +237 -0
- data/lib/rbx/bignum.fy +23 -0
- data/lib/rbx/block.fy +9 -0
- data/lib/rbx/class.fy +129 -0
- data/lib/rbx/code_loader.fy +192 -0
- data/lib/rbx/console.fy +63 -0
- data/lib/rbx/directory.fy +46 -0
- data/lib/rbx/documentation.fy +64 -0
- data/lib/rbx/environment_variables.fy +3 -0
- data/lib/rbx/exception.fy +30 -0
- data/lib/rbx/false_class.fy +58 -0
- data/lib/rbx/fiber.fy +25 -0
- data/lib/rbx/file.fy +191 -0
- data/lib/rbx/fixnum.fy +25 -0
- data/lib/rbx/float.fy +14 -0
- data/lib/rbx/hash.fy +38 -0
- data/lib/rbx/integer.fy +15 -0
- data/lib/rbx/io.fy +30 -0
- data/lib/rbx/match_data.fy +9 -0
- data/lib/rbx/method.fy +22 -0
- data/lib/rbx/name_error.fy +3 -0
- data/lib/rbx/no_method_error.fy +15 -0
- data/lib/rbx/object.fy +117 -0
- data/lib/rbx/range.fy +15 -0
- data/lib/rbx/regexp.fy +9 -0
- data/lib/rbx/string.fy +63 -0
- data/lib/rbx/symbol.fy +12 -0
- data/lib/rbx/system.fy +37 -0
- data/lib/rbx/tcp_server.fy +6 -0
- data/lib/rbx/tcp_socket.fy +7 -0
- data/lib/rbx/thread.fy +75 -0
- data/lib/rbx/tuple.fy +37 -0
- data/lib/set.fy +61 -0
- data/lib/stack.fy +51 -0
- data/lib/string.fy +58 -0
- data/lib/struct.fy +13 -0
- data/lib/symbol.fy +23 -0
- data/lib/true_class.fy +43 -0
- data/lib/tuple.fy +68 -0
- data/lib/version.fy +6 -0
- data/tests/argv.fy +13 -0
- data/tests/array.fy +343 -0
- data/tests/assignment.fy +53 -0
- data/tests/block.fy +103 -0
- data/tests/class.fy +409 -0
- data/tests/control_flow.fy +79 -0
- data/tests/documentation.fy +24 -0
- data/tests/exception.fy +115 -0
- data/tests/file.fy +86 -0
- data/tests/hash.fy +101 -0
- data/tests/method.fy +131 -0
- data/tests/nil_class.fy +55 -0
- data/tests/number.fy +128 -0
- data/tests/object.fy +125 -0
- data/tests/parsing/sexp.fy +50 -0
- data/tests/pattern_matching.fy +82 -0
- data/tests/range.fy +11 -0
- data/tests/set.fy +10 -0
- data/tests/stack.fy +22 -0
- data/tests/string.fy +102 -0
- data/tests/symbol.fy +17 -0
- data/tests/true_class.fy +63 -0
- data/tests/tuple.fy +21 -0
- data/tools/fancy-mode.el +63 -0
- metadata +321 -0
data/lib/eval.fy
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require: "compiler"
|
2
|
+
|
3
|
+
def Fancy eval: code binding: bnd (nil) file: file ("(fancy-eval)") line: line (1) {
|
4
|
+
bnd if_nil: {
|
5
|
+
bnd = Binding setup(Rubinius VariableScope of_sender(),
|
6
|
+
Rubinius CompiledMethod of_sender(),
|
7
|
+
Rubinius StaticScope of_sender())
|
8
|
+
}
|
9
|
+
|
10
|
+
# The compiled method
|
11
|
+
cm = Fancy Compiler compile_code: code vars: (bnd variables()) file: file line: line
|
12
|
+
cm scope=(bnd static_scope() dup())
|
13
|
+
cm name=('__fancy_eval__)
|
14
|
+
|
15
|
+
script = Rubinius CompiledMethod Script new(cm, file, true)
|
16
|
+
script eval_binding=(bnd)
|
17
|
+
script eval_source=(code)
|
18
|
+
|
19
|
+
cm scope() script=(script)
|
20
|
+
|
21
|
+
be = Rubinius BlockEnvironment new()
|
22
|
+
be under_context(bnd variables(), cm)
|
23
|
+
|
24
|
+
if: (bnd from_proc?()) then: {
|
25
|
+
be proc_environment=(bnd proc_environment)
|
26
|
+
}
|
27
|
+
|
28
|
+
be from_eval!()
|
29
|
+
|
30
|
+
be call()
|
31
|
+
}
|
data/lib/fancy_spec.fy
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
class FancySpec {
|
2
|
+
"""
|
3
|
+
The FancySpec class is used for defining FancySpec testsuites.
|
4
|
+
Have a look at the tests/ directory to see some examples.
|
5
|
+
"""
|
6
|
+
|
7
|
+
|
8
|
+
def initialize: @description test_obj: @test_obj (@description) {
|
9
|
+
@spec_tests = []
|
10
|
+
}
|
11
|
+
|
12
|
+
def FancySpec describe: test_obj with: block {
|
13
|
+
spec = FancySpec new: test_obj
|
14
|
+
block call_with_receiver: spec
|
15
|
+
spec run
|
16
|
+
}
|
17
|
+
|
18
|
+
def FancySpec describe: description for: test_obj with: block {
|
19
|
+
spec = FancySpec new: description test_obj: test_obj
|
20
|
+
block call_with_receiver: spec
|
21
|
+
spec run
|
22
|
+
}
|
23
|
+
|
24
|
+
def it: spec_info_string when: spec_block {
|
25
|
+
test = SpecTest new: spec_info_string block: spec_block
|
26
|
+
@spec_tests << test
|
27
|
+
}
|
28
|
+
|
29
|
+
def it: spec_info_string for: method_name when: spec_block {
|
30
|
+
test = SpecTest new: spec_info_string block: spec_block
|
31
|
+
# try {
|
32
|
+
# @test_obj method: method_name . if_do: |method| {
|
33
|
+
# method tests << test
|
34
|
+
# }
|
35
|
+
# } catch MethodNotFoundError => e {
|
36
|
+
# # ignore errors
|
37
|
+
# }
|
38
|
+
@spec_tests << test
|
39
|
+
}
|
40
|
+
|
41
|
+
def run {
|
42
|
+
"Running tests for: " ++ @description ++ ": " print
|
43
|
+
@spec_tests each: |test| {
|
44
|
+
test run: @test_obj
|
45
|
+
}
|
46
|
+
# Console newline Console newline
|
47
|
+
# untested_methods = @test_obj methods select: |m| {
|
48
|
+
# m tests size == 0
|
49
|
+
# }
|
50
|
+
# untested_methods empty? if_false: {
|
51
|
+
# ["WARNING: These methods need tests:",
|
52
|
+
# untested_methods map: 'name . select: |m| { m whitespace? not } . join: ", "] println
|
53
|
+
# }
|
54
|
+
Console newline
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
class SpecTest {
|
59
|
+
@@failed_positive = []
|
60
|
+
@@failed_negative = []
|
61
|
+
|
62
|
+
def SpecTest failed_test: actual_and_expected {
|
63
|
+
@@failed_positive << actual_and_expected
|
64
|
+
}
|
65
|
+
|
66
|
+
def SpecTest failed_negative_test: value {
|
67
|
+
@@failed_negative << value
|
68
|
+
}
|
69
|
+
|
70
|
+
def initialize: @info_str block: @block {
|
71
|
+
{ @@failed_positive = [] } unless: @@failed_positive
|
72
|
+
{ @@failed_negative = [] } unless: @@failed_negative
|
73
|
+
}
|
74
|
+
|
75
|
+
def run: test_obj {
|
76
|
+
@@failed_positive = []
|
77
|
+
@@failed_negative = []
|
78
|
+
try {
|
79
|
+
@block call
|
80
|
+
} catch IOError => e {
|
81
|
+
SpecTest failed_test: [e, "UNKNOWN"]
|
82
|
+
}
|
83
|
+
|
84
|
+
any_failure = nil
|
85
|
+
if: (@@failed_positive size > 0) then: {
|
86
|
+
any_failure = true
|
87
|
+
Console newline
|
88
|
+
"> FAILED: " ++ test_obj ++ " " ++ @info_str print
|
89
|
+
self print_failed_positive
|
90
|
+
}
|
91
|
+
|
92
|
+
if: (@@failed_negative size > 0) then: {
|
93
|
+
any_failure = true
|
94
|
+
Console newline
|
95
|
+
"> FAILED: " ++ test_obj ++ " " ++ @info_str print
|
96
|
+
self print_failed_negative
|
97
|
+
}
|
98
|
+
|
99
|
+
{ "." print } unless: any_failure
|
100
|
+
}
|
101
|
+
|
102
|
+
def print_failed_positive {
|
103
|
+
" [" ++ (@@failed_positive size) ++ " unexpected values]" println
|
104
|
+
"Got: " println
|
105
|
+
@@failed_positive each: |f| {
|
106
|
+
" " ++ (f first inspect) ++ " instead of: " ++ (f second inspect) println
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
def print_failed_negative {
|
111
|
+
" [" ++ (@@failed_negative size) ++ " unexpected values]" println
|
112
|
+
"Should not have gotten the following values: " println
|
113
|
+
@@failed_negative each: |f| {
|
114
|
+
" " ++ (f inspect) println
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
class PositiveMatcher {
|
120
|
+
"""PositiveMatcher expects its actual value to be equal to an
|
121
|
+
expected value.
|
122
|
+
If the values are not equal, a SpecTest failure is generated."""
|
123
|
+
|
124
|
+
def initialize: @actual_value {
|
125
|
+
}
|
126
|
+
|
127
|
+
def == expected_value {
|
128
|
+
unless: (@actual_value == expected_value) do: {
|
129
|
+
SpecTest failed_test: [@actual_value, expected_value]
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
def != expected_value {
|
134
|
+
unless: (@actual_value != expected_value) do: {
|
135
|
+
SpecTest failed_negative_test: @actual_value
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
def unknown_message: msg with_params: params {
|
140
|
+
"""Forwardy any other message and parameters to the object itself
|
141
|
+
and checks the return value."""
|
142
|
+
|
143
|
+
unless: (@actual_value send: msg params: params) do: {
|
144
|
+
SpecTest failed_test: [@actual_value, params first]
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
def be: block {
|
149
|
+
unless: (block call: [@actual_value]) do: {
|
150
|
+
SpecTest failed_test: [@actual_value, nil]
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
class NegativeMatcher {
|
156
|
+
"""NegativeMatcher expects its actual value to be unequal to an
|
157
|
+
expected value.
|
158
|
+
If the values are equal, a SpecTest failure is generated."""
|
159
|
+
|
160
|
+
def initialize: @actual_value {
|
161
|
+
}
|
162
|
+
|
163
|
+
def == expected_value {
|
164
|
+
if: (@actual_value == expected_value) then: {
|
165
|
+
SpecTest failed_negative_test: @actual_value
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
def != expected_value {
|
170
|
+
if: (@actual_value != expected_value) then: {
|
171
|
+
SpecTest failed_test: [@actual_value, expected_value]
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
def unknown_message: msg with_params: params {
|
176
|
+
"""Forwardy any other message and parameters to the object itself
|
177
|
+
and checks the return value."""
|
178
|
+
|
179
|
+
if: (@actual_value send: msg params: params) then: {
|
180
|
+
SpecTest failed_negative_test: @actual_value
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
def be: block {
|
185
|
+
if: (block call: [@actual_value]) then: {
|
186
|
+
SpecTest failed_negative_test: @actual_value
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
class Object {
|
193
|
+
def should {
|
194
|
+
"Returns a PositiveMatcher for self."
|
195
|
+
FancySpec PositiveMatcher new: self
|
196
|
+
}
|
197
|
+
|
198
|
+
def should_not {
|
199
|
+
"Returns a NegativeMatcher for self."
|
200
|
+
FancySpec NegativeMatcher new: self
|
201
|
+
}
|
202
|
+
}
|
data/lib/fdoc.fy
ADDED
@@ -0,0 +1,359 @@
|
|
1
|
+
# Load all of fancy.
|
2
|
+
require: "boot"
|
3
|
+
|
4
|
+
class Fancy FDoc {
|
5
|
+
"""
|
6
|
+
|
7
|
+
FDoc is a tool to generate API documentation from Fancy source.
|
8
|
+
|
9
|
+
Works as follows:
|
10
|
+
|
11
|
+
1. We setup a handler to be invoked every time an object is set documentation
|
12
|
+
See fdoc_hook.fy, its loaded even before all of lib/rbx/*.fy so we can
|
13
|
+
Also have documentation for all fancy rubinius.
|
14
|
+
2. We load boot.fy, so we get documentation for all fancy's lib.
|
15
|
+
3. We run FDoc main
|
16
|
+
which can possibly load any file/directory you specify and optionally
|
17
|
+
run specs, effectively associating them with documented objects.
|
18
|
+
4. Generate output file.
|
19
|
+
Currently the plan is to output a json formatted object.
|
20
|
+
To be loaded by an html file and use jquery to build a GUI from it.
|
21
|
+
|
22
|
+
"""
|
23
|
+
|
24
|
+
OUTPUT_DIR = "doc/api/"
|
25
|
+
|
26
|
+
def self main {
|
27
|
+
"""
|
28
|
+
FDoc will load all .fy files you give to it, and optionally run
|
29
|
+
any specified FancySpec, and later produce documentation output.
|
30
|
+
"""
|
31
|
+
|
32
|
+
output_dir = OUTPUT_DIR
|
33
|
+
ARGV for_option: "-o" do: |d| { output_dir = d }
|
34
|
+
require("fileutils")
|
35
|
+
FileUtils mkdir_p(output_dir)
|
36
|
+
|
37
|
+
# Currently we just load any files given on ARGV.
|
38
|
+
ARGV each: |file| { Fancy CodeLoader load_compiled_file(file) }
|
39
|
+
|
40
|
+
# by now simply produce a apidoc/fancy.jsonp file.
|
41
|
+
json = JSON new: @documented_objects
|
42
|
+
json write: (File expand_path("fancy.jsonp", output_dir))
|
43
|
+
|
44
|
+
["Open your browser at " ++ output_dir ++ "index.html.",
|
45
|
+
" " ++ (json classes size) ++ " classes. ",
|
46
|
+
" " ++ (json methods size) ++ " methods. ",
|
47
|
+
" " ++ (json objects size) ++ " other objects. "] println
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
class JSON {
|
52
|
+
|
53
|
+
read_slots: ['classes, 'methods, 'blocks, 'objects]
|
54
|
+
|
55
|
+
def initialize: documented {
|
56
|
+
@documented_objects = documented
|
57
|
+
|
58
|
+
is_class = |o| { o kind_of?: Module }
|
59
|
+
is_method = |o| { o kind_of?: Rubinius CompiledMethod }
|
60
|
+
is_block = |o| { o kind_of?: Rubinius BlockEnvironment }
|
61
|
+
all_other = |o| {
|
62
|
+
[is_class, is_method, is_block] all?() |b| { b call: [o] == false }
|
63
|
+
}
|
64
|
+
|
65
|
+
@classes = @documented_objects keys select: is_class
|
66
|
+
@methods = @documented_objects keys select: is_method
|
67
|
+
@blocks = @documented_objects keys select: is_block
|
68
|
+
@objects = @documented_objects keys select: all_other
|
69
|
+
}
|
70
|
+
|
71
|
+
def string_to_json: obj { obj to_s inspect }
|
72
|
+
def symbol_to_json: obj { obj to_s }
|
73
|
+
|
74
|
+
def array_to_json: obj {
|
75
|
+
str = ["["]
|
76
|
+
obj each: |i| { str << $ to_json: i } in_between: { str << ", " }
|
77
|
+
str << ["]"]
|
78
|
+
str join
|
79
|
+
}
|
80
|
+
|
81
|
+
def hash_to_json: obj {
|
82
|
+
str = ["{"]
|
83
|
+
keys = obj keys
|
84
|
+
keys each: |i| {
|
85
|
+
str << $ to_json: i
|
86
|
+
str << ":"
|
87
|
+
str << $ to_json: (obj at: i)
|
88
|
+
} in_between: { str << ", " }
|
89
|
+
str << "}"
|
90
|
+
str join
|
91
|
+
}
|
92
|
+
|
93
|
+
def to_json: obj {
|
94
|
+
# Reimplement now we have pattern matching dispatch.
|
95
|
+
match obj -> {
|
96
|
+
case Hash -> self hash_to_json: obj
|
97
|
+
case Array -> self array_to_json: obj
|
98
|
+
case Symbol -> self symbol_to_json: obj
|
99
|
+
case String -> self string_to_json: obj
|
100
|
+
case Numeric -> obj
|
101
|
+
case nil -> "null"
|
102
|
+
case _ -> "Dont know how to convert " ++ (obj inspect) ++ " to JSON" raise!
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
def popuplate_methods: cls on: attr type: type known: methods {
|
107
|
+
cls send(type ++ "s", false) each: |n| {
|
108
|
+
mattr = <[]>
|
109
|
+
exec = cls send(type, n) executable()
|
110
|
+
methods delete(exec)
|
111
|
+
mdoc = Fancy Documentation for: exec
|
112
|
+
if: mdoc then: {
|
113
|
+
mattr at: 'doc put: $ mdoc format: 'fdoc
|
114
|
+
if: (mdoc meta) then: {
|
115
|
+
mattr at: 'arg put: $ mdoc meta at: 'argnames
|
116
|
+
}
|
117
|
+
}
|
118
|
+
if: (exec class() == Rubinius CompiledMethod) then: {
|
119
|
+
relative_file = exec file()
|
120
|
+
# HACK: We simply delete everything before lib/
|
121
|
+
# TODO: Fix, either use a -r (root) option or use __FILE__
|
122
|
+
relative_file = relative_file to_s gsub(/.*lib/, "lib")
|
123
|
+
lines = exec lines() to_a()
|
124
|
+
mattr at: 'file put: $ relative_file
|
125
|
+
# TODO calculate line numbers from compiled method
|
126
|
+
# right now we only use the first line of code in the body.
|
127
|
+
mattr at: 'lines put: $ [lines[3], lines[3]]
|
128
|
+
}
|
129
|
+
attr[(type ++ "s") intern()] at: n put: mattr
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
def generate_map {
|
134
|
+
map = <['title => "Fancy Documentation", 'date => Time now() to_s(),
|
135
|
+
'classes => <[]>, 'methods => <[]>, 'objects => <[]> ]>
|
136
|
+
|
137
|
+
methods = @methods dup()
|
138
|
+
|
139
|
+
@classes each: |cls| {
|
140
|
+
name = cls name() gsub("::", " ")
|
141
|
+
doc = Fancy Documentation for: cls
|
142
|
+
attr = <[
|
143
|
+
'doc => doc format: 'fdoc,
|
144
|
+
'instance_methods => <[]>,
|
145
|
+
'methods => <[]>,
|
146
|
+
'ancestors => cls ancestors() map: |c| { c name() gsub("::", " ") }
|
147
|
+
]>
|
148
|
+
popuplate_methods: cls on: attr type: 'instance_method known: methods
|
149
|
+
popuplate_methods: cls on: attr type: 'method known: methods
|
150
|
+
map['classes] at: name put: attr
|
151
|
+
}
|
152
|
+
|
153
|
+
methods each: |cm| {
|
154
|
+
cls = cm scope() module()
|
155
|
+
cls_name = cls name() gsub("::", " ")
|
156
|
+
cls_attr = map['classes] at: cls_name
|
157
|
+
|
158
|
+
full_name = cls_name ++ "#" ++ (cm name())
|
159
|
+
|
160
|
+
doc = Fancy Documentation for: cm
|
161
|
+
attr = <[
|
162
|
+
'args => doc meta at: 'argnames,
|
163
|
+
'doc => doc format: 'fdoc
|
164
|
+
]>
|
165
|
+
|
166
|
+
map['methods] at: full_name put: attr
|
167
|
+
}
|
168
|
+
|
169
|
+
map
|
170
|
+
}
|
171
|
+
|
172
|
+
|
173
|
+
def write: filename call: name ("fancy.fdoc") {
|
174
|
+
map = self generate_map
|
175
|
+
json = self to_json: map
|
176
|
+
js = "(function() { " ++ name ++ "(" ++ json ++ "); })();"
|
177
|
+
File open: filename modes: ['write] with: |out| { out print: js }
|
178
|
+
}
|
179
|
+
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
class Formatter {
|
184
|
+
"""
|
185
|
+
A documentation formater intended to be used by @FDoc@.
|
186
|
+
|
187
|
+
This formatter makes some transformations on a docstring
|
188
|
+
and then converts it to html using markdown.
|
189
|
+
"""
|
190
|
+
|
191
|
+
Fancy Documentation formatter: 'fdoc is: |d| { format: d }
|
192
|
+
|
193
|
+
|
194
|
+
def self format: doc {
|
195
|
+
str = doc to_s
|
196
|
+
tags = <[ ]>
|
197
|
+
str = remove_indentation: str
|
198
|
+
str = remove_tags: str into: tags
|
199
|
+
str = create_tags: str with: tags
|
200
|
+
str = create_class_references: str
|
201
|
+
str = create_method_references: str
|
202
|
+
str = create_code: str
|
203
|
+
str = htmlize: str
|
204
|
+
str
|
205
|
+
}
|
206
|
+
|
207
|
+
def self remove_indentation: str {
|
208
|
+
"""
|
209
|
+
Remove leading white space for multi-line strings.
|
210
|
+
This method expects the first character to be an line return.
|
211
|
+
"""
|
212
|
+
m = /^(\r?\n)*(\s+)/ match(str)
|
213
|
+
str = str strip()
|
214
|
+
if: m then: {
|
215
|
+
pattern = "^ {" ++ (m[2] size()) ++ "}"
|
216
|
+
rex = Regexp.new(pattern)
|
217
|
+
str = str gsub(rex, "");
|
218
|
+
}
|
219
|
+
str
|
220
|
+
}
|
221
|
+
|
222
|
+
def self create_class_references: str {
|
223
|
+
"""
|
224
|
+
Creates class references for Fancy class names.
|
225
|
+
|
226
|
+
A docstring may contain class names sorounded by @
|
227
|
+
without space between the @.
|
228
|
+
|
229
|
+
Nested classes can be indicated by using :: like
|
230
|
+
|
231
|
+
Foo::Bar
|
232
|
+
|
233
|
+
This will create references for both, @Foo and @Bar
|
234
|
+
|
235
|
+
Instance methods should be written:
|
236
|
+
|
237
|
+
Foo::Bar#baz
|
238
|
+
|
239
|
+
Class methods should be written:
|
240
|
+
|
241
|
+
Foo::Bar.baz
|
242
|
+
|
243
|
+
Some examples:
|
244
|
+
|
245
|
+
A simple class reference:
|
246
|
+
|
247
|
+
@Fancy@
|
248
|
+
|
249
|
+
Nested class reference:
|
250
|
+
|
251
|
+
@Fancy::FDoc@
|
252
|
+
|
253
|
+
A fancy method without arguments:
|
254
|
+
|
255
|
+
@Fancy::FDoc::JSON#:generate_map@
|
256
|
+
|
257
|
+
A ruby method reference (will link to ruby docs if available)
|
258
|
+
|
259
|
+
@String#split@
|
260
|
+
|
261
|
+
A fancy method with many arguments:
|
262
|
+
|
263
|
+
@Fancy::Package::Installer#initialize:version:install_path:@
|
264
|
+
|
265
|
+
A singleton method:
|
266
|
+
|
267
|
+
@Fancy::FDoc::Formatter~format:@
|
268
|
+
|
269
|
+
"""
|
270
|
+
str gsub(/@[A-Z][^\r\n\s]+?@/) |cstr| {
|
271
|
+
names = cstr slice(1, cstr size() - 2) split("::")
|
272
|
+
refs = []
|
273
|
+
names each_with_index() |name, idx| {
|
274
|
+
n = name split(/[\#\~]/)
|
275
|
+
clas = names take(idx) + [n[0]] . join(" ")
|
276
|
+
html = "<code data-lang=\"fancy\" data-class-ref=\"" ++ .
|
277
|
+
clas ++ "\" class=\"class-ref selectable\">" ++ (n[0]) ++ "</code>"
|
278
|
+
refs << html
|
279
|
+
|
280
|
+
# Generate a reference for last method if availble.
|
281
|
+
if: (n[1]) then: {
|
282
|
+
method = n[1]
|
283
|
+
if: (method start_with?(":")) then: {
|
284
|
+
method = method sub(/^:/, "")
|
285
|
+
}
|
286
|
+
sigil = ""
|
287
|
+
name =~ (Regexp.new("^#")) . if_do: { sigil = "<small>#</small>" }
|
288
|
+
type = n[1] include?(":") . if_do: {
|
289
|
+
if: (sigil == "") then: {
|
290
|
+
"singleton-method-ref"
|
291
|
+
} else: {
|
292
|
+
"instance-method-ref"
|
293
|
+
}
|
294
|
+
} else: {
|
295
|
+
if: (sigil == "") then: {
|
296
|
+
"ruby-singleton-method-ref"
|
297
|
+
} else: {
|
298
|
+
"ruby-instance-method-ref"
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
html = sigil ++ "<code data-lang=\"fancy\" data-" ++ type ++ "=\"" ++ .
|
303
|
+
(n[1]) ++ "\" " ++ " data-owner-class=\"" ++ clas ++ "\" " ++ .
|
304
|
+
"class=\"" ++ type ++ " selectable\">" ++ method ++ "</code>"
|
305
|
+
refs << html
|
306
|
+
}
|
307
|
+
}
|
308
|
+
refs join(" ")
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
def self remove_tags: str into: map {
|
313
|
+
ary = str split(/\r?\n/) map: |line| {
|
314
|
+
m = /^@([a-z@]\S+?)\s+(.*)/ match(line)
|
315
|
+
if: m then: {
|
316
|
+
map at: (m[1]) put: (m[2])
|
317
|
+
nil
|
318
|
+
} else: {
|
319
|
+
line
|
320
|
+
}
|
321
|
+
}
|
322
|
+
ary compact join("\n")
|
323
|
+
}
|
324
|
+
|
325
|
+
def self create_tags: str with: map {
|
326
|
+
tags = map map: |pair| {
|
327
|
+
name = pair[0]
|
328
|
+
value = pair[1]
|
329
|
+
"<div class=\"doctag\"><label> @" ++ name ++ .
|
330
|
+
" </label><div>" ++ value ++ "</div></div>"
|
331
|
+
}
|
332
|
+
str ++ "\n<div class=\"doctags\">" ++ (tags join()) ++ "</div>"
|
333
|
+
}
|
334
|
+
|
335
|
+
def self create_code: str {
|
336
|
+
str gsub(/@([^\s,\]\)\}\.]+)/,
|
337
|
+
"<code data-lang=\"fancy\">\\1</code>")
|
338
|
+
}
|
339
|
+
|
340
|
+
def self htmlize: str {
|
341
|
+
require("rubygems")
|
342
|
+
require("rdiscount")
|
343
|
+
RDiscount new(str) to_html()
|
344
|
+
}
|
345
|
+
|
346
|
+
def self create_method_references: str {
|
347
|
+
# First methods ending with :
|
348
|
+
str gsub(/@([a-z_:]+:)@/,
|
349
|
+
"<code data-lang=\"fancy\" data-method=\"\\1\" class=\"selectable\">\\1</code>") .
|
350
|
+
# fancy methods starting with : (argless fancy methods)
|
351
|
+
gsub(/@:([a-z_]+)@/,
|
352
|
+
"<code data-lang=\"fancy\" data-method=\":\\1\" class=\"selectable\">\\1</code>")
|
353
|
+
}
|
354
|
+
|
355
|
+
}
|
356
|
+
|
357
|
+
}
|
358
|
+
|
359
|
+
Fancy FDoc main
|