lazydoc 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +52 -76
- data/lib/lazydoc.rb +48 -155
- data/lib/lazydoc/arguments.rb +26 -0
- data/lib/lazydoc/attributes.rb +111 -24
- data/lib/lazydoc/comment.rb +160 -353
- data/lib/lazydoc/document.rb +177 -101
- data/lib/lazydoc/method.rb +3 -77
- data/lib/lazydoc/subject.rb +19 -0
- data/lib/lazydoc/trailer.rb +19 -0
- data/lib/lazydoc/utils.rb +232 -0
- metadata +15 -15
data/README
CHANGED
@@ -6,107 +6,80 @@ Tap[http://tap.rubyforge.org] framework.
|
|
6
6
|
|
7
7
|
== Description
|
8
8
|
|
9
|
-
Lazydoc
|
10
|
-
|
9
|
+
Lazydoc allows you to define lazy attributes that act as markers for
|
10
|
+
documentation in a source file. When you call the lazy attribute,
|
11
|
+
Lazydoc pulls out the documentation:
|
11
12
|
|
12
13
|
# Sample::key <value>
|
13
14
|
# This is the comment content. A content
|
14
15
|
# string can span multiple lines...
|
15
|
-
#
|
16
|
-
# code.is_allowed
|
17
|
-
# much.as_in RDoc
|
18
|
-
#
|
19
|
-
# and stops at the next non-comment
|
20
|
-
# line, the next constant attribute,
|
21
|
-
# or an end key
|
22
16
|
class Sample
|
23
17
|
extend Lazydoc::Attributes
|
24
|
-
self.source_file = __FILE__
|
25
|
-
|
26
18
|
lazy_attr :key
|
27
|
-
|
28
|
-
# comment content for a code comment
|
29
|
-
# may similarly span multiple lines
|
30
|
-
def method_one
|
31
|
-
end
|
32
19
|
end
|
33
|
-
|
34
|
-
When a lazy attribute is called, Lazydoc scans <tt>source_file</tt> for
|
35
|
-
the corresponding constant attribute and makes it available as a
|
36
|
-
Lazydoc::Comment.
|
37
|
-
|
20
|
+
|
38
21
|
comment = Sample::key
|
39
|
-
comment.value
|
40
|
-
# => "
|
22
|
+
comment.value # => "<value>"
|
23
|
+
comment.comment # => "This is the comment content. A content string can span multiple lines..."
|
41
24
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
# [" much.as_in RDoc"],
|
48
|
-
# [""],
|
49
|
-
# ["and stops at the next non-comment", "line, the next constant attribute,", "or an end key"]]
|
50
|
-
|
51
|
-
"\n#{'.' * 30}\n" + comment.wrap(30) + "\n#{'.' * 30}\n"
|
25
|
+
Comments support wrapping, allowing for easy presentation:
|
26
|
+
|
27
|
+
thirtydots = "\n#{'.' * 30}\n"
|
28
|
+
|
29
|
+
"#{thirtydots}#{comment.wrap(30)}#{thirtydots}"
|
52
30
|
# => %q{
|
53
31
|
# ..............................
|
54
32
|
# This is the comment content.
|
55
33
|
# A content string can span
|
56
34
|
# multiple lines...
|
57
|
-
#
|
58
|
-
# code.is_allowed
|
59
|
-
# much.as_in RDoc
|
60
|
-
#
|
61
|
-
# and stops at the next
|
62
|
-
# non-comment line, the next
|
63
|
-
# constant attribute, or an end
|
64
|
-
# key
|
65
35
|
# ..............................
|
66
36
|
# }
|
67
37
|
|
68
|
-
In addition,
|
69
|
-
|
70
|
-
doc = Sample.lazydoc.reset
|
71
|
-
comment = doc.register(/method_one/)
|
72
|
-
|
73
|
-
doc.resolve
|
74
|
-
comment.subject # => " def method_one"
|
75
|
-
comment.content # => [["comment content for a code comment", "may similarly span multiple lines"]]
|
76
|
-
|
77
|
-
A variety of helper methods exist to register methods, in particular:
|
38
|
+
In addition, Lazydoc provides helpers to register individual lines of code,
|
39
|
+
particularly method definitions:
|
78
40
|
|
79
41
|
class Helpers
|
80
42
|
extend Lazydoc::Attributes
|
81
43
|
|
82
|
-
|
83
|
-
|
84
|
-
#
|
44
|
+
lazy_register(:method_one)
|
45
|
+
|
46
|
+
# method_one is registered whenever it
|
47
|
+
# gets defined
|
85
48
|
def method_one(a, b='str', &c)
|
86
49
|
end
|
87
50
|
|
88
|
-
#
|
89
|
-
#
|
51
|
+
# register_caller will register the line
|
52
|
+
# that *calls* method_two
|
90
53
|
def method_two
|
91
54
|
Lazydoc.register_caller
|
92
55
|
end
|
93
56
|
end
|
94
57
|
|
95
|
-
# *THIS* is the line that gets
|
96
|
-
# by method_two
|
97
|
-
Helpers.new.method_two
|
58
|
+
# *THIS* is the line that gets
|
59
|
+
# registered by method_two
|
60
|
+
Helpers.const_attrs[:method_two] = Helpers.new.method_two
|
98
61
|
|
99
62
|
doc = Helpers.lazydoc
|
100
63
|
doc.resolve
|
101
64
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
65
|
+
one = Helpers.const_attrs[:method_one]
|
66
|
+
one.method_name # => "method_one"
|
67
|
+
one.arguments # => ["a", "b='str'", "&c"]
|
68
|
+
one.to_s # => "method_one is registered whenever it gets defined"
|
106
69
|
|
107
|
-
|
108
|
-
|
109
|
-
|
70
|
+
two = Helpers.const_attrs[:method_two]
|
71
|
+
two.subject # => "Helpers.const_attrs[:method_two] = Helpers.new.method_two"
|
72
|
+
two.to_s # => "*THIS* is the line that gets registered by method_two"
|
73
|
+
|
74
|
+
Lazy accessors may be defined to map the registered lines as well:
|
75
|
+
|
76
|
+
class Helpers
|
77
|
+
lazy_attr(:one, :method_one)
|
78
|
+
lazy_attr(:two, :method_two)
|
79
|
+
end
|
80
|
+
|
81
|
+
Helpers.one.method_name # => "method_one"
|
82
|
+
Helpers.two.subject # => "Helpers.const_attrs[:method_two] = Helpers.new.method_two"
|
110
83
|
|
111
84
|
Check out these links for development, and bug tracking.
|
112
85
|
|
@@ -117,6 +90,10 @@ Check out these links for development, and bug tracking.
|
|
117
90
|
|
118
91
|
== Usage
|
119
92
|
|
93
|
+
Lazydoc can find two types of documentation, constant attributes and code
|
94
|
+
comments. The distinction is primarily how they are found and parsed; both
|
95
|
+
are represented by Comment objects.
|
96
|
+
|
120
97
|
=== Constant Attributes
|
121
98
|
|
122
99
|
Constant attributes are like constants in Ruby, but with an extra 'key'
|
@@ -134,8 +111,8 @@ While these are not:
|
|
134
111
|
# Const::Name::k@y
|
135
112
|
|
136
113
|
Lazydoc parses a Lazydoc::Comment for each constant attribute by using the
|
137
|
-
remainder of the line as a value (ie subject) and
|
138
|
-
|
114
|
+
remainder of the line as a value (ie subject) and parsing down for content.
|
115
|
+
Parsing continues until a non-comment line, an end key, or a new attribute
|
139
116
|
is reached; the comment is then stored by constant name and key.
|
140
117
|
|
141
118
|
str = %Q{
|
@@ -155,7 +132,7 @@ is reached; the comment is then stored by constant name and key.
|
|
155
132
|
doc = Lazydoc::Document.new
|
156
133
|
doc.resolve(str)
|
157
134
|
|
158
|
-
doc.
|
135
|
+
doc.summarize {|c| [c.value, c.comment] }
|
159
136
|
# => {
|
160
137
|
# 'Const::Name' => {
|
161
138
|
# 'key' => ['value for key', 'comment for key parsed until a non-comment line'],
|
@@ -176,7 +153,7 @@ attribute parsing for a section of documentation, use start/stop keys:
|
|
176
153
|
|
177
154
|
doc = Lazydoc::Document.new
|
178
155
|
doc.resolve(str)
|
179
|
-
doc.
|
156
|
+
doc.summarize {|comment| comment.value } # => {'Const::Name' => {'parsed' => 'value'}}
|
180
157
|
|
181
158
|
To hide attributes from RDoc, make use of the RDoc <tt>:startdoc:</tt>
|
182
159
|
document modifier like this (note that spaces are added to prevent RDoc
|
@@ -194,10 +171,9 @@ from hiding the example):
|
|
194
171
|
#
|
195
172
|
# * This line is also visible in RDoc.
|
196
173
|
|
197
|
-
As a side note,
|
198
|
-
constant (
|
199
|
-
|
200
|
-
<tt>Const::Name.key</tt>.
|
174
|
+
As a side note, 'Const::Name::key' is not a reference to the 'key'
|
175
|
+
constant (that would be invalid). In *very* idiomatic ruby
|
176
|
+
'Const::Name::key' is equivalent to the method call 'Const::Name.key'.
|
201
177
|
|
202
178
|
=== Code Comments
|
203
179
|
|
@@ -224,7 +200,7 @@ the behavior of RDoc).
|
|
224
200
|
doc.register(9)
|
225
201
|
doc.resolve(str)
|
226
202
|
|
227
|
-
doc.comments.collect {|
|
203
|
+
doc.comments.collect {|c| [c.subject, c.comment] }
|
228
204
|
# => [
|
229
205
|
# ['def method', 'comment lines for the method'],
|
230
206
|
# ['def another_method', 'as in RDoc, the comment can be separated from the method']]
|
@@ -233,7 +209,7 @@ Comments may be registered to specific line numbers, or with a Proc or
|
|
233
209
|
Regexp that will determine the line number during resolution. In the case
|
234
210
|
of a Regexp, the first matching line is used; Procs receive an array of
|
235
211
|
lines and should return the line number that should be used. See
|
236
|
-
Lazydoc
|
212
|
+
{Comment#parse_up}[link://classes/Lazydoc/Comment.html] for more details.
|
237
213
|
|
238
214
|
== Installation
|
239
215
|
|
data/lib/lazydoc.rb
CHANGED
@@ -1,58 +1,50 @@
|
|
1
1
|
require 'lazydoc/document'
|
2
2
|
|
3
3
|
module Lazydoc
|
4
|
-
autoload(:Attributes, 'lazydoc/attributes')
|
5
|
-
|
6
|
-
# A regexp matching an attribute start or end. After a match:
|
7
|
-
#
|
8
|
-
# $1:: const_name
|
9
|
-
# $3:: key
|
10
|
-
# $4:: end flag
|
11
|
-
#
|
12
|
-
ATTRIBUTE_REGEXP = /([A-Z][A-z]*(::[A-Z][A-z]*)*)?::([a-z_]+)(-?)/
|
13
|
-
|
14
|
-
# A regexp matching constants from the ATTRIBUTE_REGEXP leader
|
15
|
-
CONSTANT_REGEXP = /#.*?([A-Z][A-z]*(::[A-Z][A-z]*)*)?$/
|
16
|
-
|
17
|
-
# A regexp matching a caller line, to extract the calling file
|
18
|
-
# and line number. After a match:
|
19
|
-
#
|
20
|
-
# $1:: file
|
21
|
-
# $3:: line number (as a string, obviously)
|
22
|
-
#
|
23
|
-
# Note that line numbers in caller start at 1, not 0.
|
24
|
-
CALLER_REGEXP = /^(([A-z]:)?[^:]+):(\d+)/
|
25
|
-
|
26
4
|
module_function
|
27
5
|
|
28
|
-
#
|
29
|
-
# Lazydoc instance for the given source file.
|
6
|
+
# An array of documents registered with Lazydoc.
|
30
7
|
def registry
|
31
8
|
@registry ||= []
|
32
9
|
end
|
33
10
|
|
34
|
-
# Returns the
|
35
|
-
# If no such
|
11
|
+
# Returns the Document in registry for the specified source file.
|
12
|
+
# If no such Document exists, one will be created for it.
|
36
13
|
def [](source_file)
|
14
|
+
source_file = File.expand_path(source_file.to_s)
|
15
|
+
registry.find {|doc| doc.source_file == source_file } || register_file(source_file)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a Document the source_file and default_const_name and adds it to
|
19
|
+
# registry, or returns the document already registered to source_file. An
|
20
|
+
# error is raised if you try to re-register a source_file with an inconsistent
|
21
|
+
# default_const_name.
|
22
|
+
def register_file(source_file, default_const_name=nil)
|
37
23
|
source_file = File.expand_path(source_file.to_s)
|
38
24
|
lazydoc = registry.find {|doc| doc.source_file == source_file }
|
39
|
-
|
40
|
-
|
25
|
+
|
26
|
+
unless lazydoc
|
27
|
+
lazydoc = Document.new(source_file, default_const_name)
|
41
28
|
registry << lazydoc
|
42
29
|
end
|
30
|
+
|
31
|
+
if lazydoc.default_const_name != default_const_name
|
32
|
+
raise ArgumentError, "inconsistent default_const_name specified for #{source_file}: #{lazydoc.default_const_name.inspect} != #{default_const_name.inspect}"
|
33
|
+
end
|
34
|
+
|
43
35
|
lazydoc
|
44
36
|
end
|
45
37
|
|
46
|
-
#
|
47
|
-
#
|
38
|
+
# Registers the line number to the document for source_file and
|
39
|
+
# returns the corresponding comment.
|
48
40
|
def register(source_file, line_number, comment_class=Comment)
|
49
41
|
Lazydoc[source_file].register(line_number, comment_class)
|
50
42
|
end
|
51
43
|
|
52
|
-
# Registers the method at the specified index in the call stack
|
44
|
+
# Registers the method at the specified index in the call stack to
|
53
45
|
# the file where the method was called. Using the default index of
|
54
46
|
# 1, register_caller registers the caller of the method where
|
55
|
-
# register_caller is called. For instance:
|
47
|
+
# register_caller is called (whew!). For instance:
|
56
48
|
#
|
57
49
|
# module Sample
|
58
50
|
# module_function
|
@@ -62,141 +54,42 @@ module Lazydoc
|
|
62
54
|
# end
|
63
55
|
#
|
64
56
|
# # this is the line that gets registered
|
65
|
-
# Sample.method
|
57
|
+
# c = Sample.method
|
58
|
+
#
|
59
|
+
# c.resolve
|
60
|
+
# c.subject # => "c = Sample.method"
|
61
|
+
# c.comment # => "this is the line that gets registered"
|
66
62
|
#
|
67
63
|
def register_caller(comment_class=Comment, caller_index=1)
|
68
64
|
caller[caller_index] =~ CALLER_REGEXP
|
69
65
|
Lazydoc[$1].register($3.to_i - 1, comment_class)
|
70
66
|
end
|
71
67
|
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
# Scans the specified file for attributes keyed by key and stores
|
81
|
-
# the resulting comments in the source_file lazydoc. Returns the
|
82
|
-
# lazydoc.
|
83
|
-
def scan_doc(source_file, key)
|
84
|
-
lazydoc = nil
|
85
|
-
scan(File.read(source_file), key) do |const_name, attr_key, comment|
|
86
|
-
lazydoc = self[source_file] unless lazydoc
|
87
|
-
lazydoc[const_name][attr_key] = comment
|
88
|
-
end
|
89
|
-
lazydoc
|
90
|
-
end
|
91
|
-
|
92
|
-
# Scans the string or StringScanner for attributes matching the key
|
93
|
-
# (keys may be patterns, they are incorporated into a regexp). Yields
|
94
|
-
# each (const_name, key, value) triplet to the mandatory block and
|
95
|
-
# skips regions delimited by the stop and start keys <tt>:-</tt>
|
96
|
-
# and <tt>:+</tt>.
|
97
|
-
#
|
98
|
-
# str = %Q{
|
99
|
-
# # Const::Name::key value
|
100
|
-
# # ::alt alt_value
|
68
|
+
# Parses the usage for a file (ie the first comment in the file
|
69
|
+
# following an optional bang line), wrapped to n cols. For
|
70
|
+
# example, with this:
|
71
|
+
#
|
72
|
+
# [hello_world.rb]
|
73
|
+
# #!/usr/bin/env ruby
|
74
|
+
# # This is your basic hello world
|
75
|
+
# # script:
|
101
76
|
# #
|
102
|
-
# #
|
103
|
-
# # :::-
|
104
|
-
# # Also::Ignored::key value
|
105
|
-
# # :::+
|
106
|
-
# # Another::key another value
|
107
|
-
#
|
108
|
-
# Ignored::key value
|
109
|
-
# }
|
110
|
-
#
|
111
|
-
# results = []
|
112
|
-
# Lazydoc.scan(str, 'key|alt') do |const_name, key, value|
|
113
|
-
# results << [const_name, key, value]
|
114
|
-
# end
|
115
|
-
#
|
116
|
-
# results
|
117
|
-
# # => [
|
118
|
-
# # ['Const::Name', 'key', 'value'],
|
119
|
-
# # ['', 'alt', 'alt_value'],
|
120
|
-
# # ['Another', 'key', 'another value']]
|
121
|
-
#
|
122
|
-
# Returns the StringScanner used during scanning.
|
123
|
-
def scan(str, key) # :yields: const_name, key, value
|
124
|
-
scanner = case str
|
125
|
-
when StringScanner then str
|
126
|
-
when String then StringScanner.new(str)
|
127
|
-
else raise TypeError, "can't convert #{str.class} into StringScanner or String"
|
128
|
-
end
|
129
|
-
|
130
|
-
regexp = /^(.*?)::(:-|#{key})/
|
131
|
-
while !scanner.eos?
|
132
|
-
break if scanner.skip_until(regexp) == nil
|
133
|
-
|
134
|
-
if scanner[2] == ":-"
|
135
|
-
scanner.skip_until(/:::\+/)
|
136
|
-
else
|
137
|
-
next unless scanner[1] =~ CONSTANT_REGEXP
|
138
|
-
key = scanner[2]
|
139
|
-
yield($1.to_s, key, scanner.matched.strip) if scanner.scan(/[ \r\t].*$|$/)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
scanner
|
144
|
-
end
|
145
|
-
|
146
|
-
# Parses constant attributes from the string or StringScanner. Yields
|
147
|
-
# each (const_name, key, comment) triplet to the mandatory block
|
148
|
-
# and skips regions delimited by the stop and start keys <tt>:-</tt>
|
149
|
-
# and <tt>:+</tt>.
|
150
|
-
#
|
151
|
-
# str = %Q{
|
152
|
-
# # Const::Name::key subject for key
|
153
|
-
# # comment for key
|
77
|
+
# # % ruby hello_world.rb
|
154
78
|
#
|
155
|
-
#
|
156
|
-
# # Ignored::key value
|
157
|
-
# # :::+
|
79
|
+
# puts 'hello world'
|
158
80
|
#
|
159
|
-
#
|
160
|
-
# # comment for another
|
161
|
-
# }
|
81
|
+
# You get this:
|
162
82
|
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
# results
|
169
|
-
# # => [
|
170
|
-
# # ['Const::Name', 'key', 'subject for key', 'comment for key'],
|
171
|
-
# # ['', 'another', 'subject for another', 'comment for another']]
|
83
|
+
# "\n" + Lazydoc.usage('hello_world.rb')
|
84
|
+
# # => %Q{
|
85
|
+
# # This is your basic hello world script:
|
86
|
+
# #
|
87
|
+
# # % ruby hello_world.rb}
|
172
88
|
#
|
173
|
-
# Returns the StringScanner used during scanning.
|
174
|
-
def parse(str) # :yields: const_name, key, comment
|
175
|
-
scanner = case str
|
176
|
-
when StringScanner then str
|
177
|
-
when String then StringScanner.new(str)
|
178
|
-
else raise TypeError, "can't convert #{str.class} into StringScanner or String"
|
179
|
-
end
|
180
|
-
|
181
|
-
scan(scanner, '[a-z_]+') do |const_name, key, value|
|
182
|
-
comment = Comment.parse(scanner, false) do |line|
|
183
|
-
if line =~ ATTRIBUTE_REGEXP
|
184
|
-
# rewind to capture the next attribute unless an end is specified.
|
185
|
-
scanner.unscan unless $4 == '-' && $3 == key && $1.to_s == const_name
|
186
|
-
true
|
187
|
-
else false
|
188
|
-
end
|
189
|
-
end
|
190
|
-
comment.subject = value
|
191
|
-
yield(const_name, key, comment)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Parses the usage for a file, ie the first comment in the file
|
196
|
-
# following an optional bang line.
|
197
89
|
def usage(path, cols=80)
|
198
90
|
scanner = StringScanner.new(File.read(path))
|
199
|
-
scanner.scan(
|
200
|
-
|
91
|
+
scanner.scan(/#!.*?\r?\n/)
|
92
|
+
scanner.scan(/\s*#/m)
|
93
|
+
Comment.new.parse_down(scanner, nil, false).wrap(cols, 2).strip
|
201
94
|
end
|
202
95
|
end
|