public
Description: YARD is a Ruby Documentation tool (Yay!)
Homepage: http://yardoc.org
Clone URL: git://github.com/lsegal/yard.git
Click here to lend your support to: yard and make a donation at www.pledgie.com !
yard / lib / yard / handlers / base.rb
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 1 module YARD
2 module Handlers
6919f86d » lsegal 2008-05-18 Add alias_method handler 3 class UndocumentableError < Exception; end
4
cf388725 » lsegal 2008-05-29 Update rdoc formatting 5 # = Handlers
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 6 #
7 # Handlers are pluggable semantic parsers for YARD's code generation
8 # phase. They allow developers to control what information gets
9 # generated by YARD, giving them the ability to, for instance, document
10 # any Ruby DSLs that a customized framework may use. A good example
11 # of this would be the ability to document and generate meta data for
12 # the 'describe' declaration of the RSpec testing framework by simply
13 # adding a handler for such a keyword. Similarly, any Ruby API that
14 # takes advantage of class level declarations could add these to the
15 # documentation in a very explicit format by treating them as first-
16 # class objects in any outputted documentation.
17 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 18 # == Overview of a Typical Handler Scenario
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 19 #
20 # Generally, a handler class will declare a set of statements which
21 # it will handle using the {handles} class declaration. It will then
22 # implement the {#process} method to do the work. The processing would
23 # usually involve the manipulation of the {#namespace}, {#owner}
24 # {CodeObjects::Base code objects} or the creation of new ones, in
25 # which case they would ideally be returned by the method so that
26 # they may be registered by {#register}, a method that sets some basic
27 # attributes for the new objects.
28 #
29 # Handlers are usually simple and take up to a page of code to process
30 # and register a new object or add new attributes to the current +namespace+.
31 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 32 # == Setting up a Handler for Use
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 33 #
34 # A Handler is automatically registered when it is subclassed from the
35 # base class. The only other thing that needs to be done is to specify
36 # which statement the handler will process. This is done with the +handles+
37 # declaration, taking either a {Parser::RubyToken}, {String} or {Regexp}.
38 # Here is a simple example which processes module statements.
39 #
40 # class MyModuleHandler < YARD::Handlers::Base
41 # handles TkMODULE
42 #
43 # def process
44 # # do something
45 # end
46 # end
47 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 48 # == Processing Handler Data
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 49 #
50 # The goal of a specific handler is really up to the developer, and as
51 # such there is no real guideline on how to process the data. However,
52 # it is important to know where the data is coming from to be able to use
53 # it.
54 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 55 # === +statement+ Attribute
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 56 #
57 # The +statement+ attribute pertains to the {Parser::Statement} object
58 # containing a set of tokens parsed in by the parser. This is the main set
59 # of data to be analyzed and processed. The comments attached to the statement
60 # can be accessed by the {Parser::Statement#comments} method, but generally
61 # the data to be processed will live in the +tokens+ attribute. This list
62 # can be converted to a +String+ using +#to_s+ to parse the data with
63 # regular expressions (or other text processing mechanisms), if needed.
64 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 65 # === +namespace+ Attribute
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 66 #
67 # The +namespace+ attribute is a {CodeObjects::NamespaceObject namespace object}
68 # which represents the current namespace that the parser is in. For instance:
69 #
70 # module SomeModule
71 # class MyClass
72 # def mymethod; end
73 # end
74 # end
75 #
76 # If a handler was to parse the 'class MyClass' statement, it would
77 # be necessary to know that it belonged inside the SomeModule module.
78 # This is the value that +namespace+ would return when processing such
79 # a statement. If the class was then entered and another handler was
80 # called on the method, the +namespace+ would be set to the 'MyClass'
81 # code object.
82 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 83 # === +owner+ Attribute
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 84 #
85 # The +owner+ attribute is similar to the +namespace+ attribute in that
86 # it also follows the scope of the code during parsing. However, a namespace
87 # object is loosely defined as a module or class and YARD has the ability
88 # to parse beyond module and class blocks (inside methods, for instance),
89 # so the +owner+ attribute would not be limited to modules and classes.
90 #
91 # To put this into context, the example from above will be used. If a method
92 # handler was added to the mix and decided to parse inside the method body,
93 # the +owner+ would be set to the method object but the namespace would remain
94 # set to the class. This would allow the developer to process any method
95 # definitions set inside a method (def x; def y; 2 end end) by adding them
96 # to the correct namespace (the class, not the method).
97 #
98 # In summary, the distinction between +namespace+ and +owner+ can be thought
99 # of as the difference between first-class Ruby objects (namespaces) and
100 # second-class Ruby objects (methods).
101 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 102 # === +visibility+ and +scope+ Attributes
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 103 #
104 # Mainly needed for parsing methods, the +visibility+ and +scope+ attributes
105 # refer to the public/protected/private and class/instance values (respectively)
106 # of the current parsing position.
107 #
cf388725 » lsegal 2008-05-29 Update rdoc formatting 108 # == Parsing Blocks in Statements
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 109 #
110 # In addition to parsing a statement and creating new objects, some
111 # handlers may wish to continue parsing the code inside the statement's
112 # block (if there is one). In this context, a block means the inside
113 # of any statement, be it class definition, module definition, if
114 # statement or classic 'Ruby block'.
115 #
116 # For example, a class statement would be "class MyClass" and the block
117 # would be a list of statements including the method definitions inside
118 # the class. For a class handler, the programmer would execute the
119 # {#parse_block} method to continue parsing code inside the block, with
120 # the +namespace+ now pointing to the class object the handler created.
121 #
122 # YARD has the ability to continue into any block: class, module, method,
123 # even if statements. For this reason, the block parsing method must be
124 # invoked explicitly out of efficiency sake.
125 #
126 # @see CodeObjects::Base
127 # @see CodeObjects::NamespaceObject
128 # @see handles
129 # @see #namespace
130 # @see #owner
131 # @see #register
132 # @see #parse_block
133 #
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 134 class Base
135 # For accessing convenience, eg. "MethodObject"
136 # instead of the full qualified namespace
137 include YARD::CodeObjects
138
029b3728 » lsegal 2008-05-12 Add spec for #resolve 139 # For tokens like TkDEF, TkCLASS, etc.
140 include YARD::Parser::RubyToken
141
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 142 class << self
96704fe2 » lsegal 2008-05-09 Hook up source parser and a... 143 def clear_subclasses
144 @@subclasses = []
145 end
146
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 147 def subclasses
148 @@subclasses || []
149 end
150
151 def inherited(subclass)
152 @@subclasses ||= []
153 @@subclasses << subclass
154 end
155
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 156 # Declares the statement type which will be processed
157 # by this handler.
158 #
159 # A match need not be unique to a handler. Multiple
160 # handlers can process the same statement. However,
161 # in this case, care should be taken to make sure that
162 # {#parse_block} would only be executed by one of
163 # the handlers, otherwise the same code will be parsed
164 # multiple times and slow YARD down.
165 #
166 # @param [Parser::RubyToken, String, Regexp] match
167 # statements that match the declaration will be
168 # processed by this handler. A {String} match is
169 # equivalent to a +/\Astring/+ regular expression
170 # (match from the beginning of the line), and all
171 # token matches match only the first token of the
172 # statement.
173 #
174 def handles(match)
175 @handler = match
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 176 end
029b3728 » lsegal 2008-05-12 Add spec for #resolve 177
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 178 def handles?(tokens)
179 case @handler
180 when String
181 tokens.first.text == @handler
182 when Regexp
183 tokens.to_s =~ @handler ? true : false
184 else
185 @handler == tokens.first.class
186 end
187 end
188 end
189
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 190 # The main handler method called by the parser on a statement
191 # that matches the {handles} declaration.
192 #
193 # Subclasses should override this method to provide the handling
194 # functionality for the class.
195 #
196 # @return [CodeObjects::Base, Array<CodeObjects::Base>, Object]
197 # If this method returns a code object (or a list of them),
198 # they are passed to the +#register+ method which adds basic
199 # attributes. It is not necessary to return any objects and in
200 # some cases you may want to explicitly avoid the returning of
201 # any objects for post-processing by the register method.
202 #
203 # @see handles
204 # @see #register
205 #
f4a69bf5 » lsegal 2008-05-28 Raise an error if process i... 206 def process
207 raise NotImplementedError, "#{self} did not implement a #process method for handling."
208 end
0a31a796 » lsegal 2008-05-09 Add CodeObjectList class to... 209
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 210 # Do some post processing on a list of code objects
211 # either returned from {#process} or explicitly
212 # called from a handler. Adds basic attributes
213 # to the list of objects like the filename,
214 # line number, {CodeObjects::Base#dynamic},
215 # source code and {CodeObjects::Base#docstring},
216 # but only if they don't exist.
217 #
218 # As mentioned above, this method is automatically
219 # called on the result of {#process}. Sometimes
220 # it may be easier to explicitly call
221 #
222 # @param [Array<CodeObjects::Base>] objects
223 # the list of objects to post-process.
224 #
02815b70 » lsegal 2008-05-29 Add #register method and up... 225 # @return [NilClass]
226 #
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 227 def register(*objects)
228 objects.each do |object|
229 next unless object.is_a?(CodeObjects::Base)
230
231 # Add file and line number
232 object.file ||= parser.file
233 object.line ||= statement.tokens.first.line_no
234
02815b70 » lsegal 2008-05-29 Add #register method and up... 235 # Add docstring if it's not set
236 object.docstring = statement.comments if object.docstring.empty?
237
238 # Add source only to non-class non-module objects
239 unless object.is_a?(ClassObject) || object.is_a?(ModuleObject)
240 object.source ||= statement
241 end
242
243 # Method Object gets signature
244 if object.is_a?(MethodObject)
245 object.signature ||= statement.tokens.to_s
246 end
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 247
248 # Make it dynamic if it's owner is not it's namespace.
249 # This generally means it was defined in a method (or block of some sort)
250 object.dynamic ||= true if owner != namespace
251 end
02815b70 » lsegal 2008-05-29 Add #register method and up... 252 nil # Don't return anything just in case a register call is at the
253 # end of a process method-- we don't want to accidentally do this twice.
d829ffc8 » lsegal 2008-05-29 Lots of docs for Handlers::... 254 end
255
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 256 attr_reader :parser, :statement
257
258 def initialize(source_parser, stmt)
259 @parser = source_parser
260 @statement = stmt
261 end
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 262
029b3728 » lsegal 2008-05-12 Add spec for #resolve 263 def parse_block(opts = nil)
264 opts = {
265 :namespace => nil,
266 :scope => :instance,
267 :owner => nil
268 }.update(opts || {})
269
270 if opts[:namespace]
271 ns, vis, sc = namespace, visibility, scope
272 self.namespace = opts[:namespace]
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 273 self.visibility = :public
029b3728 » lsegal 2008-05-12 Add spec for #resolve 274 self.scope = opts[:scope]
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 275 end
276
029b3728 » lsegal 2008-05-12 Add spec for #resolve 277 self.owner = opts[:owner] ? opts[:owner] : namespace
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 278 parser.parse(statement.block) if statement.block
279
029b3728 » lsegal 2008-05-12 Add spec for #resolve 280 if opts[:namespace]
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 281 self.namespace = ns
521818cb » lsegal 2008-05-12 Add #owner method to source... 282 self.owner = namespace
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 283 self.visibility = vis
029b3728 » lsegal 2008-05-12 Add spec for #resolve 284 self.scope = sc
7fb31eda » lsegal 2008-05-10 Add #parse_block to parse t... 285 end
286 end
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 287
029b3728 » lsegal 2008-05-12 Add spec for #resolve 288 def owner; @parser.owner end
289 def owner=(v) @parser.owner=(v) end
568a1db5 » lsegal 2008-05-09 Add Base handler and some s... 290 def namespace; @parser.namespace end
291 def namespace=(v); @parser.namespace=(v) end
292 def visibility; @parser.visibility end
293 def visibility=(v); @parser.visibility=(v) end
294 def scope; @parser.scope end
295 def scope=(v); @parser.scope=(v) end
296 end
297 end
298 end