lazydoc 0.2.0 → 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/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
|