yard 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of yard might be problematic. Click here for more details.

data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2007 Loren Segal
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.pdf ADDED
Binary file
data/bin/yardoc ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/ruby
2
+ require File.dirname(__FILE__) + '/../lib/yard'
3
+ include YARD
4
+
5
+ if ARGV[0] == "-h"
6
+ puts "yardoc 0.2.0"
7
+ return
8
+ end
9
+
10
+ Dir.mkdir("doc") unless FileTest.directory?("doc")
11
+ Namespace.load(Namespace::DEFAULT_YARDOC_FILE, true)
12
+ ac = File.open("doc/all-classes.html", "w")
13
+ ac.puts <<-eof
14
+ <html>
15
+ <head>
16
+ <base target="main" />
17
+ </head>
18
+ <body>
19
+ <h3>All Classes</h3>
20
+ eof
21
+ meths = []
22
+ Namespace.all.sort.each do |path|
23
+ object = Namespace.at(path)
24
+ if object.is_a?(MethodObject) && object.visibility == :public
25
+ meths << [object.name, object]
26
+ end
27
+
28
+ next unless object.is_a? ClassObject
29
+ ac.puts "<a href='" + path.gsub("::","_") + ".html'>" + path + "</a><br />"
30
+ File.open("doc/#{path.gsub('::','_')}.html", "w") {|f| f.write(object.format) }
31
+ end
32
+ ac.puts "</body></html>"
33
+ ac.close
34
+
35
+ File.open("doc/all-methods.html", "w") do |f|
36
+ f.puts <<-eof
37
+ <html>
38
+ <head>
39
+ <base target="main" />
40
+ </head>
41
+ <body>
42
+ <h3>All Methods</h3>
43
+ eof
44
+ meths.sort {|a,b| a.first <=> b.first }.each do |name, object|
45
+ f.puts "<a href='" + object.parent.path.gsub("::", "_") + ".html##{object.scope}_method-#{name}'>#{name}</a><br />"
46
+ end
47
+ end
48
+
49
+ if ARGV[0]
50
+ main_page = ARGV[0]
51
+ else
52
+ main_page = Namespace.all.sort.find {|name| Namespace.at(name).is_a? YARD::ClassObject }
53
+ end
54
+ main_page = main_page.gsub("::", "_") + ".html"
55
+ File.open("doc/index.html", "w") do |f|
56
+ f.puts <<-eof
57
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
58
+ "http://www.w3.org/TR/html4/loose.dtd">
59
+ <html>
60
+ <head>
61
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
62
+ <title>Ruby Classes</title>
63
+ </head>
64
+ <frameset cols="250,*">
65
+ <frameset rows="*,40%">
66
+ <frame src="all-classes.html">
67
+ <frame src="all-methods.html">
68
+ </frameset>
69
+ <frame name="main" src="#{main_page}">
70
+ </frameset>
71
+ </html>
72
+ eof
73
+ end
data/bin/yri ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/ruby
2
+ require File.dirname(__FILE__) + '/../lib/yard'
3
+ require File.dirname(__FILE__) + '/../lib/quick_doc'
4
+ YARD::QuickDoc.new(ARGV[0])
@@ -0,0 +1,340 @@
1
+ require File.dirname(__FILE__) + '/tag_library'
2
+ require File.dirname(__FILE__) + '/formatter'
3
+
4
+ module YARD #:nodoc:
5
+ ##
6
+ # The documentation parser is responsible for parsing the docstring into
7
+ # text and saving the meta tags attached to it
8
+ #
9
+ # @author Loren Segal
10
+ class CodeObject
11
+ attr_reader :source, :full_source, :file, :line, :docstring, :attributes
12
+
13
+ attr_reader :name, :type
14
+ attr_accessor :visibility, :scope
15
+ attr_accessor :parent, :children
16
+
17
+ ##
18
+ # Creates a new code object with necessary information such as the object's name (not full path),
19
+ # it's type (:class, :module, :method, etc.), visibility (:public, :private, :protected) and scope
20
+ # (:instance or :class level). Optionally you can specify a parent object and comments too, but these
21
+ # can also be assigned later through the {#parent=} and {#attach_docstring} methods.
22
+ #
23
+ # @param [String] name the name of the object, not including its namespace ('initialize' for this method)
24
+ # @param [Symbol] type the type of object this is, including (but not limited to):
25
+ # :module, :class, :method, :constant
26
+ # @param [Symbol] visibility :public, :protected or :private depending on the visibility of the object
27
+ # @param [Symbol] scope :instance or :class depending on if the object is instance level or class level.
28
+ # Instance level objects use the '#' character to separate from their parent instead of '::'
29
+ # @param [CodeObject] parent The parent of this object. Without a parent this object will not be registered
30
+ # in the {Namespace}
31
+ # @param [String] comments Comments to be parsed as a docstring for the object.
32
+ # @return [CodeObject] the created code object
33
+ # @yieldparam [CodeObject] _self the object is yielded during initialization to perform any initialization operations
34
+ # on it more conveniently.
35
+ # @see #attach_docstring
36
+ # @see #parent=
37
+ def initialize(name, type, visibility = :public, scope = :instance, parent = nil, comments = nil)
38
+ @name, @type, @visibility, @scope = name, type, visibility.to_sym, scope.to_sym
39
+ @tags, @attributes, @children = [], {}, []
40
+ self.parent = parent
41
+ attach_docstring(comments)
42
+ yield(self) if block_given?
43
+ end
44
+
45
+ def to_s
46
+ "#{visibility} #{type} #{path}"
47
+ end
48
+
49
+ ##
50
+ # Attaches source code to a code object with an optional file location
51
+ #
52
+ # @param [Statement, String] statement the +Statement+ holding the source code
53
+ # or the raw source as a +String+ for the
54
+ # definition of the code object only (not the block)
55
+ # @param [String] file the filename the source resides in
56
+ def attach_source(statement, file = nil)
57
+ if statement.is_a? String
58
+ @source = statement
59
+ else
60
+ @source = statement.tokens.to_s
61
+ @line = statement.tokens.first.line_no
62
+ attach_full_source statement.tokens.to_s + (statement.block.to_s rescue "")
63
+ end
64
+ @file = file
65
+ end
66
+
67
+ ##
68
+ # Manually attaches full source code for an object given the source
69
+ # as a +String+
70
+ #
71
+ # @param [String] source the source code for the object
72
+ def attach_full_source(source)
73
+ @full_source = source
74
+ end
75
+
76
+ ##
77
+ # Attaches a docstring to a code oject by parsing the comments attached to the statement
78
+ # and filling the {#tags} and {#docstring} methods with the parsed information.
79
+ #
80
+ # @param [String, Array<String>] comments the comments attached to the code object to be
81
+ # parsed into a docstring and meta tags.
82
+ def attach_docstring(comments)
83
+ parse_comments(comments) if comments
84
+ end
85
+
86
+ def [](key)
87
+ @attributes[key.to_sym]
88
+ end
89
+
90
+ def []=(key, value)
91
+ @attributes[key.to_sym] = value
92
+ end
93
+
94
+ ##
95
+ # Sets the parent object and registers the object path with
96
+ # the {Namespace}. If the object was already registered
97
+ # to an old path, it will be removed from the namespace.
98
+ #
99
+ # @param [CodeObject] value the new parent object
100
+ # @see Namespace
101
+ def parent=(value)
102
+ # Delete old object path if there was one
103
+ Namespace.instance.namespace.delete(path) if parent
104
+
105
+ @parent = value
106
+
107
+ # Register new path with namespace
108
+ Namespace.add_object(self) if value
109
+ end
110
+
111
+ ##
112
+ # See if the method call exists in the attributes hash, and return
113
+ # it. Otherwise send the missing method call up the stack.
114
+ #
115
+ # @param meth the method name called. This method is checked in the
116
+ # attributes hash
117
+ # @param args the arguments to the call
118
+ # @param block an optional block for the call
119
+ def method_missing(meth, *args, &block)
120
+ return self[meth] if self[meth]
121
+ super
122
+ end
123
+
124
+ ##
125
+ # Returns the unique path for this code object. The resulting path will be
126
+ # a Ruby style path name of the namespace the object resides in plus the
127
+ # object name delimited by a "::" or "#" depending on if the object is an
128
+ # instance level object or a class level object.
129
+ #
130
+ # Example:
131
+ #
132
+ #
133
+ #
134
+ def path
135
+ [(parent.path if parent && parent.type != :root), name].join(scope == :instance ? "#" : "::").gsub(/^::/, '')
136
+ end
137
+
138
+ ##
139
+ # Convenience method to return the first tag
140
+ # object in the list of tag objects of that name
141
+ #
142
+ # Example:
143
+ # doc = YARD::Documentation.new("@return zero when nil")
144
+ # doc.tag("return").text # => "zero when nil"
145
+ #
146
+ # @param [#to_s] name the tag name to return data for
147
+ # @return [BaseTag] the first tag in the list of {#tags}
148
+ def tag(name)
149
+ name = name.to_s
150
+ @tags.find {|tag| tag.tag_name == name }
151
+ end
152
+
153
+ ##
154
+ # Returns a list of tags specified by +name+ or all tags if +name+ is not specified.
155
+ #
156
+ # @param name the tag name to return data for, or nil for all tags
157
+ # @return [Array<BaseTag>] the list of tags by the specified tag name
158
+ def tags(name = nil)
159
+ return @tags if name.nil?
160
+ name = name.to_s
161
+ @tags.select {|tag| tag.tag_name == name }
162
+ end
163
+
164
+ ##
165
+ # Returns true if at least one tag by the name +name+ was declared
166
+ #
167
+ # @param [String] name the tag name to search for
168
+ # @return [Boolean] whether or not the tag +name+ was declared
169
+ def has_tag?(name)
170
+ name = name.to_s
171
+ @tags.any? {|tag| tag.tag_name == name }
172
+ end
173
+
174
+ ##
175
+ # Returns a code object formatted as a given type, defaults to html.
176
+ #
177
+ # @param [Symbol] format the output format to generate
178
+ # @return [String] the code object formatted by the specified +format+
179
+ def format(type = :html)
180
+ Formatter.new.format(self, type)
181
+ end
182
+
183
+ private
184
+ ##
185
+ # Parses out comments split by newlines into a new code object
186
+ #
187
+ # @param [Array<String>, String] comments the newline delimited
188
+ # array of comments. If the comments
189
+ # are passed as a String, they will
190
+ # be split by newlines.
191
+ def parse_comments(comments)
192
+ return if comments.empty?
193
+ meta_match = /^\s*@(\S+)\s*(.*)/
194
+ comments = comments.split(/\r?\n/) if comments.is_a? String
195
+ @tags, @docstring = [], ""
196
+
197
+ indent, last_indent = comments.first[/^\s*/].length, 0
198
+ tag_name, tag_klass, tag_buf = nil, nil, ""
199
+
200
+ # Add an extra line to catch a meta directive on the last line
201
+ (comments+['']).each do |line|
202
+ indent = line[/^\s*/].length
203
+
204
+ if (indent < last_indent && tag_name) || line == '' || line =~ meta_match
205
+ tag_method = "#{tag_name}_tag"
206
+ if tag_name && TagLibrary.respond_to?(tag_method)
207
+ @tags << TagLibrary.send(tag_method, tag_buf.squeeze(" "))
208
+ end
209
+ tag_name, tag_buf = nil, ''
210
+ end
211
+
212
+ # Found a meta tag
213
+ if line =~ meta_match
214
+ tag_name, tag_buf = $1, $2
215
+ elsif indent >= last_indent && tag_name
216
+ # Extra data added to the tag on the next line
217
+ tag_buf << line
218
+ else
219
+ # Regular docstring text
220
+ @docstring << line << "\n"
221
+ end
222
+
223
+ last_indent = indent
224
+ end
225
+
226
+ # Remove trailing/leading whitespace / newlines
227
+ @docstring.gsub!(/\A[\r\n\s]+|[\r\n\s]+\Z/, '')
228
+ end
229
+ end
230
+
231
+ class CodeObjectWithMethods < CodeObject
232
+ def initialize(name, type, parent = nil, comments = nil)
233
+ super(name, type, :public, :class, parent, comments) do |obj|
234
+ obj[:instance_methods] = {}
235
+ obj[:class_methods] = {}
236
+ obj[:constants] = {}
237
+ obj[:class_variables] = {}
238
+ obj[:mixins] = []
239
+ yield(obj) if block_given?
240
+ end
241
+ end
242
+
243
+ def inherited_class_methods
244
+ inherited_methods(:class)
245
+ end
246
+
247
+ def inherited_instance_methods
248
+ inherited_methods(:instance)
249
+ end
250
+
251
+ def inherited_methods(scopes = [:class, :instance])
252
+ [scopes].flatten.each do |scope|
253
+ full_mixins.inject({}) {|hash, mixin| hash.update(mixin.send(scope + "_methods")) }
254
+ end
255
+ end
256
+
257
+ def full_mixins
258
+ mixins.collect {|mixin| Namespace.find_from_path(self, mixin).path rescue mixin }
259
+ end
260
+ end
261
+
262
+ class ModuleObject < CodeObjectWithMethods
263
+ def initialize(name, *args)
264
+ super(name, :module, *args) do |obj|
265
+ yield(obj) if block_given?
266
+ end
267
+ end
268
+ end
269
+
270
+ class ClassObject < CodeObjectWithMethods
271
+ BASE_OBJECT = "Object"
272
+
273
+ def initialize(name, superclass = BASE_OBJECT, *args)
274
+ super(name, :class, *args) do |obj|
275
+ obj[:attributes] = {}
276
+ obj[:superclass] = superclass
277
+ yield(obj) if block_given?
278
+ end
279
+ end
280
+
281
+ def inherited_methods(scopes = [:class, :instance])
282
+ inherited_methods = super
283
+ superobject = Namespace.find_from_path(path, superclass)
284
+ if superobject && superobject.path != path # avoid infinite loop
285
+ [scopes].flatten.each do |scope|
286
+ inherited_methods.update(superobject.send(scope + "_methods"))
287
+ inherited_methods.update(superobject.send("inherited_#{scope}_methods"))
288
+ end
289
+ end
290
+ inherited_methods
291
+ end
292
+
293
+ def superclasses
294
+ superobject = Namespace.find_from_path(path, superclass)
295
+ return [superclass] if superobject.nil?
296
+ [superobject.path] + superobject.superclasses
297
+ end
298
+
299
+ def inheritance_tree
300
+ full_mixins.reverse + superclasses
301
+ end
302
+ end
303
+
304
+ class MethodObject < CodeObject
305
+ # @param [String] name the name of the method
306
+ # @param visibility the object visibility (:public, :private, :protected)
307
+ # @param [String] scope the object scope (:instance, :class)
308
+ # @param [CodeObjectWithMethods] parent the object that holds this method
309
+ def initialize(name, visibility, scope, parent, comments = nil)
310
+ super(name, :method, visibility, scope, parent, comments) do |obj|
311
+ parent["#{scope}_methods".to_sym].update(name.to_s => obj)
312
+ yield(obj) if block_given?
313
+ end
314
+ end
315
+ end
316
+
317
+ class ConstantObject < CodeObject
318
+ def initialize(name, parent = nil, statement = nil)
319
+ super(name, :constant, :public, :class, parent) do |obj|
320
+ if statement
321
+ obj.attach_docstring(statement.comments)
322
+ obj.attach_source(statement)
323
+ parent[:constants].update(name.to_s => obj)
324
+ yield(obj) if block_given?
325
+ end
326
+ end
327
+ end
328
+ end
329
+
330
+ class ClassVariableObject < CodeObject
331
+ def initialize(statement, parent)
332
+ name, value = *statement.tokens.to_s.gsub(/\r?\n/, '').split(/\s*=\s*/, 2)
333
+ super(name, :class_variable, :public, :class, parent) do |obj|
334
+ obj.parent[:class_variables].update(name => obj)
335
+ obj.attach_docstring(statement.comments)
336
+ obj.attach_source("#{name} = #{value}")
337
+ end
338
+ end
339
+ end
340
+ end
data/lib/formatter.rb ADDED
@@ -0,0 +1,78 @@
1
+ require 'erb'
2
+ require 'rdoc/markup/simple_markup'
3
+ require 'rdoc/markup/simple_markup/to_html'
4
+
5
+ SMP = SM::SimpleMarkup.new
6
+ SMH = SM::ToHtml.new
7
+
8
+ module YARD
9
+ ##
10
+ # Formats the code objects in the {Namespace} in a variety of formats
11
+ #
12
+ # @author Loren Segal
13
+ # @version 1.0
14
+ class Formatter
15
+ OUTPUT_FORMATS = [ :html, :xhtml, :xml ]
16
+
17
+ ##
18
+ # Formats an object as a specified output format. Default is +:html+.
19
+ #
20
+ # @param [String, CodeObject] object the code object to format or the path to the code object
21
+ # @param [Symbol] format the output format to generate documentation in.
22
+ # Defaults to +:html+, which is a synonym for +:xhtml+.
23
+ # @see OUTPUT_FORMATS
24
+ def format(object, format = :html)
25
+ object = Namespace.at(object) if object.is_a? String
26
+ erb = File.join(template_directory, "#{format}_formatter.erb")
27
+
28
+ @object = object
29
+ ERB.new(IO.read(erb), nil, ">").result(binding)
30
+ end
31
+
32
+ ##
33
+ # Directory for templates. Override this to load your own templates
34
+ def template_directory
35
+ File.join(File.dirname(__FILE__), '..', 'templates')
36
+ end
37
+ end
38
+
39
+ protected
40
+ def link_to_path(name, from_path = nil, label = nil)
41
+ return "<a href='#instance_method-#{name[1..-1]}'>#{label || name}</a>" if name =~ /^\#/ && from_path.nil?
42
+
43
+ if from_path
44
+ obj = Namespace.find_from_path(from_path, name)
45
+ else
46
+ obj = Namespace.at(name)
47
+ end
48
+
49
+ label = name if label.nil?
50
+ if obj
51
+ file = obj.parent.path.gsub("::","_") + ".html"
52
+ case obj
53
+ when ConstantObject
54
+ "<a href='#{file}#const-#{obj.name}'>#{label}</a>"
55
+ when ClassVariableObject
56
+ "<a href='#{file}#cvar-#{obj.name}'>#{label}</a>"
57
+ when MethodObject
58
+ "<a href='#{file}##{obj.scope}_method-#{obj.name}'>#{label}</a>"
59
+ else
60
+ "<a href='#{obj.path.gsub("::","_")}.html'>#{label}</a>"
61
+ end
62
+ else
63
+ name
64
+ end
65
+ end
66
+
67
+ def to_html(text, path = @object)
68
+ resolve_links(SMP.convert(text || "", SMH).gsub(/\A<p>|<\/p>\Z/,''), path)
69
+ end
70
+
71
+ def resolve_links(text, path)
72
+ t, re = text, /\{(.+?)\}/
73
+ while t =~ re
74
+ t.sub!(re, "<tt>" + link_to_path($1, path) + "</tt>")
75
+ end
76
+ t
77
+ end
78
+ end