tomparse 0.3.0 → 0.4.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/.index +2 -2
- data/.yardopts +8 -0
- data/HISTORY.md +26 -1
- data/README.md +92 -7
- data/lib/tomparse.rb +43 -482
- data/lib/tomparse.yml +68 -0
- data/lib/tomparse/argument.rb +69 -0
- data/lib/tomparse/option.rb +30 -0
- data/lib/tomparse/parse_error.rb +30 -0
- data/lib/tomparse/parser.rb +699 -0
- data/test/helper.rb +0 -20
- data/test/test_description.rb +54 -0
- data/test/test_prefixes.rb +3 -3
- data/test/test_signatures.rb +44 -25
- data/test/test_tags.rb +6 -4
- data/test/test_tomdoc.rb +3 -3
- data/test/test_yields.rb +17 -0
- metadata +12 -3
- data/.rubyrc +0 -19
data/.index
CHANGED
data/.yardopts
ADDED
data/HISTORY.md
CHANGED
@@ -1,15 +1,40 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
## 0.4.0 / 2013-02-10
|
4
|
+
|
5
|
+
For this release, the document parser has been substantially refactored.
|
6
|
+
It should be both more robust and more flexible. Arguments can now
|
7
|
+
have an optional `Arguments` heading, and the arguments list can be
|
8
|
+
indented. A new `Options` section is supported to handle Ruby 2.0 style
|
9
|
+
keyword options. Tag labels ony need to be capitalized. They no longer
|
10
|
+
have to be all-caps. And lastly, the `Signature` section has been
|
11
|
+
repurposed for describing variant *parameter signatures*, not dynamic
|
12
|
+
methods. As such `signature_fields` has been deprecated. Yes, these
|
13
|
+
are additions and some deviation from the original TomDoc spec, but
|
14
|
+
when purely conceptual specs go into practice they have a tendency to
|
15
|
+
adapt to practical requirements.
|
16
|
+
|
17
|
+
Changes:
|
18
|
+
|
19
|
+
* Refactor document parser.
|
20
|
+
* Add support for optional Arguments section header.
|
21
|
+
* Add support for Options section.
|
22
|
+
* Modify purpose of Signatures section.
|
23
|
+
* Deprecate signature field list.
|
24
|
+
* Tag labels only need to be capitialized, not all-caps.
|
25
|
+
|
26
|
+
|
3
27
|
## 0.3.0 / 2012-01-21
|
4
28
|
|
5
29
|
This release fixes a bug which prevented descriptions from having
|
6
30
|
multiple paragraphs. In addition it adds support for section tags.
|
7
|
-
See the README for more information
|
31
|
+
See the README for more information on tags.
|
8
32
|
|
9
33
|
Changes:
|
10
34
|
|
11
35
|
* Fix multi-paragraph description parsing.
|
12
36
|
* Add support for tags.
|
37
|
+
* Fix support for option hashes. (Jonas Oberschweiber)
|
13
38
|
|
14
39
|
|
15
40
|
## 0.2.1 / 2012-04-30
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@
|
|
13
13
|
|
14
14
|
TomParse is a TomDoc parser for Ruby. It provides no other functionality than
|
15
15
|
to take a code comment and parse it in to a convenient object-oriented
|
16
|
-
structure in accordance with TomDoc standard. See [TomDoc](https://github.com/mojombo/tomdoc)
|
16
|
+
structure in accordance with the TomDoc standard. See [TomDoc](https://github.com/mojombo/tomdoc)
|
17
17
|
for more information about the TomDoc format.
|
18
18
|
|
19
19
|
|
@@ -59,12 +59,59 @@ looks something like this:
|
|
59
59
|
text * count
|
60
60
|
end
|
61
61
|
|
62
|
-
|
62
|
+
## Extra Features
|
63
63
|
|
64
|
-
Okay, we told a little white lie in the description. TomParse does take
|
65
|
-
|
66
|
-
goodness.
|
67
|
-
|
64
|
+
Okay, we told a little white lie in the description. TomParse does take some
|
65
|
+
liberties with the specification to offer up some additional documentation
|
66
|
+
goodness.
|
67
|
+
|
68
|
+
### Arguments
|
69
|
+
|
70
|
+
The TomDoc specification is rather strict about how arguments are written --they
|
71
|
+
have to be the second section just after the description. TomParse offers a little
|
72
|
+
more flexability by allowing for an `Arguments` header to be used.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# Method to do fooey.
|
76
|
+
#
|
77
|
+
# Examples
|
78
|
+
# foo(name)
|
79
|
+
#
|
80
|
+
# Arguments
|
81
|
+
# name - Name of the fooey.
|
82
|
+
#
|
83
|
+
# Returns nothing.
|
84
|
+
def foo(name)
|
85
|
+
...
|
86
|
+
```
|
87
|
+
|
88
|
+
We still recommend putting the arguments right after the desciption in most cases,
|
89
|
+
but there are times when its reads better to put them lower, such when using a
|
90
|
+
`Signatures` section (see below).
|
91
|
+
|
92
|
+
### Options
|
93
|
+
|
94
|
+
Ruby 2.0 finally introduces *keyword arguments* to the language. TomDoc's current
|
95
|
+
support of argument options, as a secondary argument list of a Hash argument,
|
96
|
+
doesn't take keyoword arguments into good account. To remedy this TomParse provides
|
97
|
+
and `Options` section, and it written just like one would an `Arguments` section.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
# Method to do fooey.
|
101
|
+
#
|
102
|
+
# Options
|
103
|
+
# debug - Turn on debug mode? (default: false)
|
104
|
+
#
|
105
|
+
# Returns nothing.
|
106
|
+
def foo(**options)
|
107
|
+
...
|
108
|
+
```
|
109
|
+
|
110
|
+
### Tags
|
111
|
+
|
112
|
+
One really nice new feature of TomParse is it's ability to recoginize sections
|
113
|
+
starting with a capitalized word followed by a colon and a space, as special *tags*.
|
114
|
+
Here is an example:
|
68
115
|
|
69
116
|
```ruby
|
70
117
|
# Method to do something.
|
@@ -77,7 +124,44 @@ word, followed by a colon and a space, as special tags. Here is an example:
|
|
77
124
|
```
|
78
125
|
|
79
126
|
When this is parsed, rather then lumping the TODO line in with the description,
|
80
|
-
the TomDoc instance will have a `tags`
|
127
|
+
the TomDoc instance will have a `tags` entry containing `['TODO', 'This is a todo note.']`.
|
128
|
+
It is important for consumer applications to recognize this. They can either just
|
129
|
+
add the tags back into the description when generating documentation, or handle
|
130
|
+
them separately. But tags don't have to occur right after the description. They
|
131
|
+
can occur any place in the documentation.
|
132
|
+
|
133
|
+
### Signatures
|
134
|
+
|
135
|
+
TomParse does not support the Signature sections in exactly the same fashion as
|
136
|
+
the TomDoc specification describes. Rather then define dynamic methods, signatures
|
137
|
+
are used to specify alternate argument patterns, one signature per line.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
# Method to do something.
|
141
|
+
#
|
142
|
+
# name - The name of the thing.
|
143
|
+
# pattern - The pattern of the thing.
|
144
|
+
#
|
145
|
+
# Signatures
|
146
|
+
#
|
147
|
+
# dosomething(name)
|
148
|
+
# dosomething(name=>pattern)
|
149
|
+
#
|
150
|
+
# Returns nothing.
|
151
|
+
def dosomething(*args)
|
152
|
+
...
|
153
|
+
```
|
154
|
+
|
155
|
+
Technically the Signatures section can still be used to designate a dynamic method,
|
156
|
+
but as you can see by the above example, TomParse does not support the *field* list.
|
157
|
+
If needed just use the arguments list for these as well.
|
158
|
+
|
159
|
+
This choice was made, btw, because support for signatures as defined by the spec,
|
160
|
+
leads to very non-optimal code. It requires scanning every chunk of documentation
|
161
|
+
for a `Signature` section in order to determine how to treat it. What is needed
|
162
|
+
is a more universal syntax, that can be easily recognized by some clear identifier
|
163
|
+
at the top of a comment --and one that doesn't confuse dynamic method definitions
|
164
|
+
with the more traditional concept of method signatures.
|
81
165
|
|
82
166
|
|
83
167
|
## Resources
|
@@ -107,6 +191,7 @@ the TomDoc instance will have a `tags` property containing `{'todo'=>'This is a
|
|
107
191
|
|
108
192
|
* [Citron](http://rubyworks.github.com/citron) (testing)
|
109
193
|
* [AE](http://rubyworks.github.com/ae) (testing)
|
194
|
+
* [Fire](http://detroit.github.com/fire) (building)
|
110
195
|
* [Detroit](http://detroit.github.com/detroit) (building)
|
111
196
|
|
112
197
|
|
data/lib/tomparse.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
module TomParse
|
2
|
+
require 'tomparse/parser'
|
3
|
+
require 'tomparse/parse_error'
|
4
|
+
require 'tomparse/argument'
|
5
|
+
require 'tomparse/option'
|
2
6
|
|
3
7
|
# Main interface to parser.
|
4
8
|
#
|
@@ -11,9 +15,10 @@ module TomParse
|
|
11
15
|
# Encapsulate parsed tomdoc documentation.
|
12
16
|
#
|
13
17
|
# TODO: Currently uses lazy evaluation, eventually this should
|
14
|
-
# be removed and simply parsed at
|
18
|
+
# be removed and simply parsed all at once.
|
15
19
|
#
|
16
20
|
class TomDoc
|
21
|
+
|
17
22
|
attr_accessor :raw
|
18
23
|
|
19
24
|
# Public: Initialize a TomDoc object.
|
@@ -22,25 +27,15 @@ module TomParse
|
|
22
27
|
#
|
23
28
|
# Returns new TomDoc instance.
|
24
29
|
def initialize(text, parse_options={})
|
25
|
-
@
|
26
|
-
|
27
|
-
@arguments = []
|
28
|
-
@options = [] # TODO
|
29
|
-
@examples = []
|
30
|
-
@returns = []
|
31
|
-
@raises = []
|
32
|
-
@signatures = []
|
33
|
-
@signature_fields = []
|
34
|
-
@tags = {}
|
35
|
-
|
36
|
-
parse unless @raw.empty?
|
30
|
+
@parser = Parser.new(text, parse_options)
|
31
|
+
@parser.parse
|
37
32
|
end
|
38
33
|
|
39
34
|
# Raw documentation text.
|
40
35
|
#
|
41
36
|
# Returns String of raw documentation text.
|
42
37
|
def to_s
|
43
|
-
@raw
|
38
|
+
@parser.raw
|
44
39
|
end
|
45
40
|
|
46
41
|
# Validate given comment text.
|
@@ -54,9 +49,7 @@ module TomParse
|
|
54
49
|
#
|
55
50
|
# Returns true if comment is valid, otherwise false.
|
56
51
|
def valid?
|
57
|
-
|
58
|
-
return false if sections.size < 2
|
59
|
-
true
|
52
|
+
@parser.valid?
|
60
53
|
end
|
61
54
|
|
62
55
|
# Validate raw comment.
|
@@ -64,553 +57,121 @@ module TomParse
|
|
64
57
|
# Returns true if comment is valid.
|
65
58
|
# Raises ParseError if comment is not valid.
|
66
59
|
def validate
|
67
|
-
|
68
|
-
raise ParseError.new("No `Returns' statement.")
|
69
|
-
end
|
70
|
-
|
71
|
-
if sections.size < 2
|
72
|
-
raise ParseError.new("No description section found.")
|
73
|
-
end
|
74
|
-
|
75
|
-
true
|
60
|
+
@parser.validate
|
76
61
|
end
|
77
62
|
|
63
|
+
# TODO: Should we clean the raw documentation here and then pass it on to the parser?
|
64
|
+
|
78
65
|
# The raw comment text cleaned-up and ready for section parsing.
|
79
66
|
#
|
80
67
|
# Returns cleaned-up comment String.
|
81
68
|
def tomdoc
|
82
|
-
|
83
|
-
|
84
|
-
# remove remark symbol
|
85
|
-
if lines.all?{ |line| /^\s*#/ =~ line }
|
86
|
-
lines = lines.map do |line|
|
87
|
-
line =~ /^(\s*#)/ ? line.sub($1, '') : nil
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# for some reason the first line is coming in without indention
|
92
|
-
# regardless, so we temporary remove it
|
93
|
-
first = lines.shift
|
94
|
-
|
95
|
-
# remove indention
|
96
|
-
spaces = lines.map do |line|
|
97
|
-
next if line.strip.empty?
|
98
|
-
md = /^(\s*)/.match(line)
|
99
|
-
md ? md[1].size : nil
|
100
|
-
end.compact
|
101
|
-
|
102
|
-
space = spaces.min || 0
|
103
|
-
lines = lines.map do |line|
|
104
|
-
if line.strip.empty?
|
105
|
-
line.strip
|
106
|
-
else
|
107
|
-
line[space..-1]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# put first line back
|
112
|
-
lines.unshift(first.sub(/^\s*/,'')) if first
|
113
|
-
|
114
|
-
lines.compact.join("\n")
|
69
|
+
return @parser.tomdoc
|
115
70
|
end
|
116
71
|
|
117
72
|
# List of comment sections. These are divided simply on "\n\n".
|
118
73
|
#
|
119
74
|
# Returns Array of comment sections.
|
120
75
|
def sections
|
121
|
-
|
122
|
-
@sections
|
123
|
-
}
|
76
|
+
@parser.sections
|
124
77
|
end
|
125
78
|
|
126
79
|
# Description of method or class/module.
|
127
80
|
#
|
128
81
|
# Returns description String.
|
129
82
|
def description
|
130
|
-
|
131
|
-
@description
|
132
|
-
}
|
83
|
+
@parser.description
|
133
84
|
end
|
134
85
|
|
135
|
-
#
|
86
|
+
# Arguments list.
|
136
87
|
#
|
137
|
-
# Returns
|
88
|
+
# Returns list of arguments.
|
138
89
|
def arguments
|
139
|
-
|
140
|
-
@arguments
|
141
|
-
}
|
90
|
+
@parser.arguments
|
142
91
|
end
|
143
92
|
alias args arguments
|
144
93
|
|
94
|
+
# Keyword arguments, aka Options.
|
95
|
+
#
|
96
|
+
# Returns list of options.
|
97
|
+
def options
|
98
|
+
@parser.options
|
99
|
+
end
|
100
|
+
alias keyword_arguments options
|
101
|
+
|
145
102
|
# List of use examples of a method or class/module.
|
146
103
|
#
|
147
104
|
# Returns String of examples.
|
148
105
|
def examples
|
149
|
-
|
150
|
-
@examples
|
151
|
-
}
|
106
|
+
@parser.examples
|
152
107
|
end
|
153
108
|
|
154
109
|
# Description of a methods yield procedure.
|
155
110
|
#
|
156
111
|
# Returns String decription of yield procedure.
|
157
112
|
def yields
|
158
|
-
|
159
|
-
@yields
|
160
|
-
}
|
113
|
+
@parser.yields
|
161
114
|
end
|
162
115
|
|
163
116
|
# The list of retrun values a method can return.
|
164
117
|
#
|
165
118
|
# Returns Array of method return descriptions.
|
166
119
|
def returns
|
167
|
-
|
168
|
-
@returns
|
169
|
-
}
|
120
|
+
@parser.returns
|
170
121
|
end
|
171
122
|
|
172
123
|
# A list of errors a method might raise.
|
173
124
|
#
|
174
125
|
# Returns Array of method raises descriptions.
|
175
126
|
def raises
|
176
|
-
|
177
|
-
@raises
|
178
|
-
}
|
127
|
+
@parser.raises
|
179
128
|
end
|
180
129
|
|
181
130
|
# A list of alternate method signatures.
|
182
131
|
#
|
183
132
|
# Returns Array of signatures.
|
184
133
|
def signatures
|
185
|
-
|
186
|
-
@signatures
|
187
|
-
}
|
134
|
+
@parser.signatures
|
188
135
|
end
|
189
136
|
|
190
|
-
# A list of signature fields.
|
137
|
+
# Deprecated: A list of signature fields.
|
138
|
+
#
|
139
|
+
# TODO: Presently this will always return an empty list. It will either
|
140
|
+
# be removed or renamed in future version.
|
191
141
|
#
|
192
142
|
# Returns Array of field definitions.
|
193
143
|
def signature_fields
|
194
|
-
|
195
|
-
@signature_fields
|
196
|
-
}
|
144
|
+
@parser.signature_fields
|
197
145
|
end
|
198
146
|
|
199
|
-
#
|
147
|
+
# List of tags.
|
200
148
|
#
|
201
|
-
# Returns
|
149
|
+
# Returns an associatve array of tags. [Array<Array<String>>]
|
202
150
|
def tags
|
203
|
-
|
204
|
-
@tags
|
205
|
-
}
|
151
|
+
@parser.tags
|
206
152
|
end
|
207
153
|
|
208
154
|
# Check if method is public.
|
209
155
|
#
|
210
156
|
# Returns true if method is public.
|
211
157
|
def public?
|
212
|
-
|
213
|
-
@status == 'Public'
|
214
|
-
}
|
158
|
+
@parser.public?
|
215
159
|
end
|
216
160
|
|
217
161
|
# Check if method is internal.
|
218
162
|
#
|
219
163
|
# Returns true if method is internal.
|
220
164
|
def internal?
|
221
|
-
|
222
|
-
@status == 'Internal'
|
223
|
-
}
|
165
|
+
@parser.internal?
|
224
166
|
end
|
225
167
|
|
226
168
|
# Check if method is deprecated.
|
227
169
|
#
|
228
170
|
# Returns true if method is deprecated.
|
229
171
|
def deprecated?
|
230
|
-
|
231
|
-
@status == 'Deprecated'
|
232
|
-
}
|
172
|
+
@parser.deprecated?
|
233
173
|
end
|
234
174
|
|
235
|
-
private
|
236
|
-
|
237
|
-
# Has the comment been parsed yet?
|
238
|
-
def parsed(&block)
|
239
|
-
parse unless @parsed
|
240
|
-
block.call
|
241
|
-
end
|
242
|
-
|
243
|
-
# Internal: Parse the Tomdoc formatted comment.
|
244
|
-
#
|
245
|
-
# Returns true if there was a comment to parse.
|
246
|
-
def parse
|
247
|
-
@parsed = true
|
248
|
-
|
249
|
-
sections = tomdoc.split("\n\n")
|
250
|
-
|
251
|
-
return false if sections.empty?
|
252
|
-
|
253
|
-
# The description is always the first section, but it may have
|
254
|
-
# multiple paragraphs. This routine collects those together.
|
255
|
-
desc = [sections.shift]
|
256
|
-
loop do
|
257
|
-
s = sections.first
|
258
|
-
break if s.nil?
|
259
|
-
break if s =~ /^\w+\s+\-/m
|
260
|
-
break if section_type(s) != nil
|
261
|
-
desc << sections.shift
|
262
|
-
end
|
263
|
-
sections = [desc.join("\n\n")] + sections
|
264
|
-
|
265
|
-
@sections = sections.dup
|
266
|
-
|
267
|
-
parse_description(sections.shift)
|
268
|
-
|
269
|
-
if sections.first && sections.first =~ /^\w+\s+\-/m
|
270
|
-
parse_arguments(sections.shift)
|
271
|
-
end
|
272
|
-
|
273
|
-
current = sections.shift
|
274
|
-
while current
|
275
|
-
case type = section_type(current)
|
276
|
-
when :examples
|
277
|
-
parse_examples(current, sections)
|
278
|
-
when :yields
|
279
|
-
parse_yields(current)
|
280
|
-
when :returns
|
281
|
-
parse_returns(current) # also does raises
|
282
|
-
when :raises
|
283
|
-
parse_returns(current) # also does returns
|
284
|
-
when :signature
|
285
|
-
parse_signature(current, sections)
|
286
|
-
when Symbol
|
287
|
-
parse_tag(current)
|
288
|
-
end
|
289
|
-
current = sections.shift
|
290
|
-
end
|
291
|
-
|
292
|
-
return @parsed
|
293
|
-
end
|
294
|
-
|
295
|
-
#
|
296
|
-
#
|
297
|
-
def section_type(section)
|
298
|
-
case section
|
299
|
-
when /^Examples/
|
300
|
-
:examples
|
301
|
-
when /^Yields/
|
302
|
-
:yields
|
303
|
-
when /^Returns/
|
304
|
-
:returns
|
305
|
-
when /^Raises/
|
306
|
-
:raises
|
307
|
-
when /^Signature/
|
308
|
-
:signature
|
309
|
-
when /^([A-Z]*)\:\ /
|
310
|
-
$1.downcase.to_sym
|
311
|
-
else
|
312
|
-
nil
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# Recognized description status.
|
317
|
-
TOMDOC_STATUS = ['Internal', 'Public', 'Deprecated']
|
318
|
-
|
319
|
-
# Parse description.
|
320
|
-
#
|
321
|
-
# section - String containig description.
|
322
|
-
#
|
323
|
-
# Returns nothing.
|
324
|
-
def parse_description(section)
|
325
|
-
if md = /^([A-Z]\w+\:)/.match(section)
|
326
|
-
@status = md[1].chomp(':')
|
327
|
-
if TOMDOC_STATUS.include?(@status)
|
328
|
-
@description = md.post_match.strip
|
329
|
-
else
|
330
|
-
@description = section.strip
|
331
|
-
end
|
332
|
-
else
|
333
|
-
@description = section.strip
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
# Parse arguments section. Arguments occur subsequent to
|
338
|
-
# the description.
|
339
|
-
#
|
340
|
-
# section - String containing argument definitions.
|
341
|
-
#
|
342
|
-
# Returns nothing.
|
343
|
-
def parse_arguments(section)
|
344
|
-
args = []
|
345
|
-
last_indent = nil
|
346
|
-
|
347
|
-
section.lines.each do |line|
|
348
|
-
next if line.strip.empty?
|
349
|
-
indent = line.scan(/^\s*/)[0].to_s.size
|
350
|
-
|
351
|
-
if last_indent && indent > 0 && indent >= last_indent
|
352
|
-
args.last.description << "\r\n" + line
|
353
|
-
else
|
354
|
-
param, desc = line.split(" - ")
|
355
|
-
args << Argument.new(param.strip, desc.strip) if param && desc
|
356
|
-
end
|
357
|
-
|
358
|
-
last_indent = indent
|
359
|
-
end
|
360
|
-
|
361
|
-
args.each do |arg|
|
362
|
-
arg.parse(arg.description)
|
363
|
-
end
|
364
|
-
|
365
|
-
@arguments = args
|
366
|
-
end
|
367
|
-
|
368
|
-
# Parse examples.
|
369
|
-
#
|
370
|
-
# section - String starting with `Examples`.
|
371
|
-
# sections - All sections subsequent to section.
|
372
|
-
#
|
373
|
-
# Returns nothing.
|
374
|
-
def parse_examples(section, sections)
|
375
|
-
examples = []
|
376
|
-
|
377
|
-
section = section.sub('Examples', '').gsub(/^\s{2}/,'')
|
378
|
-
|
379
|
-
examples << section unless section.empty?
|
380
|
-
while sections.first && sections.first !~ /^\S/
|
381
|
-
examples << sections.shift.gsub(/^\s{2}/,'')
|
382
|
-
end
|
383
|
-
|
384
|
-
@examples = examples
|
385
|
-
end
|
386
|
-
|
387
|
-
# Parse yields section.
|
388
|
-
#
|
389
|
-
# section - String contaning Yields line.
|
390
|
-
#
|
391
|
-
# Returns nothing.
|
392
|
-
def parse_yields(section)
|
393
|
-
@yields = section.strip
|
394
|
-
end
|
395
|
-
|
396
|
-
# Parse returns section.
|
397
|
-
#
|
398
|
-
# section - String contaning Returns and/or Raises lines.
|
399
|
-
#
|
400
|
-
# Returns nothing.
|
401
|
-
def parse_returns(section)
|
402
|
-
returns, raises, current = [], [], []
|
403
|
-
|
404
|
-
lines = section.split("\n")
|
405
|
-
lines.each do |line|
|
406
|
-
case line
|
407
|
-
when /^Returns/
|
408
|
-
returns << line
|
409
|
-
current = returns
|
410
|
-
when /^Raises/
|
411
|
-
raises << line
|
412
|
-
current = raises
|
413
|
-
when /^\s+/
|
414
|
-
current.last << line.squeeze(' ')
|
415
|
-
else
|
416
|
-
current << line # TODO: What to do with non-compliant line?
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
@returns.concat(returns)
|
421
|
-
@raises.concat(raises)
|
422
|
-
end
|
423
|
-
|
424
|
-
# Parse signature section.
|
425
|
-
#
|
426
|
-
# section - String starting with `Signature`.
|
427
|
-
# sections - All sections subsequent to section.
|
428
|
-
#
|
429
|
-
# Returns nothing.
|
430
|
-
def parse_signature(section, sections=[])
|
431
|
-
signatures = []
|
432
|
-
|
433
|
-
section = section.sub('Signature', '').strip
|
434
|
-
|
435
|
-
signatures << section unless section.empty?
|
436
|
-
|
437
|
-
while sections.first && sections.first !~ /^\S/
|
438
|
-
sigs = sections.shift
|
439
|
-
sigs.split("\n").each do |s|
|
440
|
-
signatures << s.strip
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
@signatures = signatures
|
445
|
-
|
446
|
-
if sections.first && sections.first =~ /^\w+\s*\-/m
|
447
|
-
parse_signature_fields(sections.shift)
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
# Subsequent to Signature section there can be field
|
452
|
-
# definitions.
|
453
|
-
#
|
454
|
-
# section - String subsequent to signatures.
|
455
|
-
#
|
456
|
-
# Returns nothing.
|
457
|
-
def parse_signature_fields(section)
|
458
|
-
args = []
|
459
|
-
last_indent = nil
|
460
|
-
|
461
|
-
section.split("\n").each do |line|
|
462
|
-
next if line.strip.empty?
|
463
|
-
indent = line.scan(/^\s*/)[0].to_s.size
|
464
|
-
|
465
|
-
if last_indent && indent > last_indent
|
466
|
-
args.last.description << line.squeeze(" ")
|
467
|
-
else
|
468
|
-
param, desc = line.split(" - ")
|
469
|
-
args << Argument.new(param.strip, desc.strip) if param && desc
|
470
|
-
end
|
471
|
-
|
472
|
-
last_indent = indent
|
473
|
-
end
|
474
|
-
|
475
|
-
@signature_fields = args
|
476
|
-
end
|
477
|
-
|
478
|
-
# Tags are arbitrary sections designated by all cap labels and a colon.
|
479
|
-
#
|
480
|
-
# label - String name of the tag.
|
481
|
-
# section - String of the tag section.
|
482
|
-
#
|
483
|
-
# Returns nothing.
|
484
|
-
def parse_tag(section)
|
485
|
-
|
486
|
-
md = /^([A-Z]*)\:\ /.match(section)
|
487
|
-
|
488
|
-
label = md[1]
|
489
|
-
text = md.post_match
|
490
|
-
|
491
|
-
@tags[label.downcase] = text
|
492
|
-
end
|
493
|
-
|
494
|
-
end
|
495
|
-
|
496
|
-
# Encapsulate a method argument.
|
497
|
-
#
|
498
|
-
class Argument
|
499
|
-
|
500
|
-
attr_accessor :name
|
501
|
-
|
502
|
-
attr_accessor :description
|
503
|
-
|
504
|
-
attr_accessor :options
|
505
|
-
|
506
|
-
# Create new Argument object.
|
507
|
-
#
|
508
|
-
# name - name of argument
|
509
|
-
# description - argument description
|
510
|
-
#
|
511
|
-
def initialize(name, description = '')
|
512
|
-
@name = name.to_s.intern
|
513
|
-
parse(description)
|
514
|
-
end
|
515
|
-
|
516
|
-
# Is this an optional argument?
|
517
|
-
#
|
518
|
-
# Returns Boolean.
|
519
|
-
def optional?
|
520
|
-
@description.downcase.include? 'optional'
|
521
|
-
end
|
522
|
-
|
523
|
-
# Parse arguments section. Arguments occur subsequent to
|
524
|
-
# the description.
|
525
|
-
#
|
526
|
-
# section - String containing argument definitions.
|
527
|
-
#
|
528
|
-
# Returns nothing.
|
529
|
-
def parse(description)
|
530
|
-
desc = []
|
531
|
-
opts = []
|
532
|
-
|
533
|
-
lines = description.lines.to_a
|
534
|
-
|
535
|
-
until lines.empty? or /^\s+\:(\w+)\s+-\s+(.*?)$/ =~ lines.first
|
536
|
-
desc << lines.shift.chomp.squeeze(" ")
|
537
|
-
end
|
538
|
-
|
539
|
-
opts = []
|
540
|
-
last_indent = nil
|
541
|
-
|
542
|
-
lines.each do |line|
|
543
|
-
next if line.strip.empty?
|
544
|
-
indent = line.scan(/^\s*/)[0].to_s.size
|
545
|
-
|
546
|
-
if last_indent && indent > last_indent
|
547
|
-
opts.last.description << line.squeeze(" ")
|
548
|
-
else
|
549
|
-
param, d = line.split(" - ")
|
550
|
-
opts << Option.new(param.strip, d.strip) if param && d
|
551
|
-
end
|
552
|
-
|
553
|
-
last_indent = indent
|
554
|
-
end
|
555
|
-
|
556
|
-
@description = desc.join
|
557
|
-
@options = opts
|
558
|
-
end
|
559
|
-
|
560
|
-
end
|
561
|
-
|
562
|
-
# Encapsulate a named parameter.
|
563
|
-
#
|
564
|
-
class Option
|
565
|
-
|
566
|
-
attr_accessor :name
|
567
|
-
|
568
|
-
attr_accessor :description
|
569
|
-
|
570
|
-
# Create new Argument object.
|
571
|
-
#
|
572
|
-
# name - name of option
|
573
|
-
# description - option description
|
574
|
-
#
|
575
|
-
def initialize(name, description = '')
|
576
|
-
@name = name.to_s.intern
|
577
|
-
@description = description
|
578
|
-
end
|
579
|
-
|
580
|
-
# Is this a required option?
|
581
|
-
#
|
582
|
-
# Returns Boolean.
|
583
|
-
def required?
|
584
|
-
@description.downcase.include? 'required'
|
585
|
-
end
|
586
|
-
|
587
|
-
end
|
588
|
-
|
589
|
-
# Raised when comment can't be parsed, which means it's most
|
590
|
-
# likely not valid TomDoc.
|
591
|
-
#
|
592
|
-
class ParseError < RuntimeError
|
593
|
-
# Create new ParseError object.
|
594
|
-
#
|
595
|
-
# doc - document string
|
596
|
-
#
|
597
|
-
def initialize(doc)
|
598
|
-
@doc = doc
|
599
|
-
end
|
600
|
-
|
601
|
-
# Provide access to document string.
|
602
|
-
#
|
603
|
-
# Returns String.
|
604
|
-
def message
|
605
|
-
@doc
|
606
|
-
end
|
607
|
-
|
608
|
-
# Provide access to document string.
|
609
|
-
#
|
610
|
-
# Returns String.
|
611
|
-
def to_s
|
612
|
-
@doc
|
613
|
-
end
|
614
175
|
end
|
615
176
|
|
616
177
|
end
|