yard 0.2.1 → 0.2.2

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.

Files changed (264) hide show
  1. data/{LICENSE.txt → LICENSE} +1 -1
  2. data/README +211 -0
  3. data/Rakefile +31 -0
  4. data/benchmarks/builtins_vs_eval.rb +23 -0
  5. data/benchmarks/erb_vs_erubis.rb +53 -0
  6. data/benchmarks/generation.rb +37 -0
  7. data/benchmarks/parsing.rb +33 -0
  8. data/bin/view_generator +17 -0
  9. data/bin/yard-graph +4 -0
  10. data/bin/yardoc +1 -93
  11. data/bin/yri +12 -3
  12. data/lib/yard.rb +10 -5
  13. data/lib/yard/autoload.rb +116 -0
  14. data/lib/yard/cli/yard_graph.rb +86 -0
  15. data/lib/yard/cli/yardoc.rb +131 -0
  16. data/lib/yard/code_objects/base.rb +321 -0
  17. data/lib/yard/code_objects/class_object.rb +89 -0
  18. data/lib/yard/code_objects/class_variable_object.rb +4 -0
  19. data/lib/yard/code_objects/constant_object.rb +4 -0
  20. data/lib/yard/code_objects/method_object.rb +51 -0
  21. data/lib/yard/code_objects/module_object.rb +4 -0
  22. data/lib/yard/code_objects/namespace_object.rb +88 -0
  23. data/lib/yard/code_objects/proxy.rb +183 -0
  24. data/lib/yard/code_objects/root_object.rb +8 -0
  25. data/lib/yard/core_ext/file.rb +26 -0
  26. data/lib/yard/core_ext/logger.rb +5 -0
  27. data/lib/yard/core_ext/module.rb +9 -0
  28. data/lib/yard/core_ext/string.rb +13 -0
  29. data/lib/yard/core_ext/symbol_hash.rb +24 -0
  30. data/lib/yard/generators/attributes_generator.rb +22 -0
  31. data/lib/yard/generators/base.rb +285 -0
  32. data/lib/yard/generators/class_generator.rb +25 -0
  33. data/lib/yard/generators/constants_generator.rb +73 -0
  34. data/lib/yard/generators/constructor_generator.rb +25 -0
  35. data/lib/yard/generators/deprecated_generator.rb +15 -0
  36. data/lib/yard/generators/docstring_generator.rb +15 -0
  37. data/lib/yard/generators/full_doc_generator.rb +59 -0
  38. data/lib/yard/generators/helpers/base_helper.rb +52 -0
  39. data/lib/yard/generators/helpers/filter_helper.rb +21 -0
  40. data/lib/yard/generators/helpers/html_helper.rb +137 -0
  41. data/lib/yard/generators/helpers/method_helper.rb +27 -0
  42. data/lib/yard/generators/helpers/uml_helper.rb +16 -0
  43. data/lib/yard/generators/inheritance_generator.rb +16 -0
  44. data/lib/yard/generators/method_details_generator.rb +18 -0
  45. data/lib/yard/generators/method_generator.rb +31 -0
  46. data/lib/yard/generators/method_listing_generator.rb +105 -0
  47. data/lib/yard/generators/method_missing_generator.rb +25 -0
  48. data/lib/yard/generators/method_signature_generator.rb +19 -0
  49. data/lib/yard/generators/method_summary_generator.rb +21 -0
  50. data/lib/yard/generators/mixins_generator.rb +15 -0
  51. data/lib/yard/generators/module_generator.rb +22 -0
  52. data/lib/yard/generators/quick_doc_generator.rb +31 -0
  53. data/lib/yard/generators/source_generator.rb +26 -0
  54. data/lib/yard/generators/tags_generator.rb +50 -0
  55. data/lib/yard/generators/uml_generator.rb +92 -0
  56. data/lib/yard/generators/visibility_group_generator.rb +26 -0
  57. data/lib/yard/handlers/alias_handler.rb +32 -0
  58. data/lib/yard/handlers/attribute_handler.rb +54 -0
  59. data/lib/yard/handlers/base.rb +509 -0
  60. data/lib/yard/handlers/class_handler.rb +44 -0
  61. data/lib/yard/handlers/class_variable_handler.rb +13 -0
  62. data/lib/yard/handlers/constant_handler.rb +13 -0
  63. data/lib/yard/handlers/exception_handler.rb +12 -0
  64. data/lib/yard/handlers/method_handler.rb +27 -0
  65. data/lib/yard/handlers/mixin_handler.rb +16 -0
  66. data/lib/yard/handlers/module_handler.rb +9 -0
  67. data/lib/yard/handlers/visibility_handler.rb +14 -0
  68. data/lib/yard/handlers/yield_handler.rb +26 -0
  69. data/lib/yard/logging.rb +27 -0
  70. data/lib/yard/parser/ruby_lex.rb +1344 -0
  71. data/lib/yard/parser/source_parser.rb +109 -0
  72. data/lib/yard/parser/statement.rb +36 -0
  73. data/lib/yard/parser/statement_list.rb +167 -0
  74. data/lib/yard/parser/token_list.rb +58 -0
  75. data/lib/yard/rake/yardoc_task.rb +30 -0
  76. data/lib/yard/registry.rb +136 -0
  77. data/lib/yard/serializers/base.rb +16 -0
  78. data/lib/yard/serializers/file_system_serializer.rb +48 -0
  79. data/lib/yard/serializers/process_serializer.rb +14 -0
  80. data/lib/yard/serializers/stdout_serializer.rb +21 -0
  81. data/lib/yard/tags/default_factory.rb +98 -0
  82. data/lib/yard/tags/library.rb +109 -0
  83. data/lib/yard/tags/merbdoc_factory.rb +47 -0
  84. data/lib/yard/tags/tag.rb +35 -0
  85. data/spec/code_objects/base_spec.rb +219 -0
  86. data/spec/code_objects/class_object_spec.rb +176 -0
  87. data/spec/code_objects/code_object_list_spec.rb +33 -0
  88. data/spec/code_objects/constants_spec.rb +79 -0
  89. data/spec/code_objects/method_object_spec.rb +30 -0
  90. data/spec/code_objects/module_object_spec.rb +73 -0
  91. data/spec/code_objects/namespace_object_spec.rb +129 -0
  92. data/spec/code_objects/proxy_spec.rb +80 -0
  93. data/spec/code_objects/spec_helper.rb +3 -0
  94. data/spec/core_ext/file_spec.rb +20 -0
  95. data/spec/core_ext/string_spec.rb +4 -0
  96. data/spec/core_ext/symbol_hash_spec.rb +80 -0
  97. data/spec/generators/base_spec.rb +64 -0
  98. data/spec/generators/helpers/base_helper_spec.rb +15 -0
  99. data/spec/generators/helpers/html_helper_spec.rb +56 -0
  100. data/spec/generators/quick_doc_generator_spec.rb +13 -0
  101. data/spec/handlers/alias_handler_spec.rb +50 -0
  102. data/spec/handlers/attribute_handler_spec.rb +78 -0
  103. data/spec/handlers/base_spec.rb +165 -0
  104. data/spec/handlers/class_handler_spec.rb +68 -0
  105. data/spec/handlers/class_variable_handler_spec.rb +9 -0
  106. data/spec/handlers/constant_handler_spec.rb +13 -0
  107. data/spec/handlers/examples/alias_handler_001.rb.txt +24 -0
  108. data/spec/handlers/examples/attribute_handler_001.rb.txt +19 -0
  109. data/spec/handlers/examples/class_handler_001.rb.txt +39 -0
  110. data/spec/handlers/examples/class_variable_handler_001.rb.txt +9 -0
  111. data/spec/handlers/examples/constant_handler_001.rb.txt +10 -0
  112. data/spec/handlers/examples/exception_handler_001.rb.txt +42 -0
  113. data/spec/handlers/examples/method_handler_001.rb.txt +35 -0
  114. data/spec/handlers/examples/mixin_handler_001.rb.txt +12 -0
  115. data/spec/handlers/examples/module_handler_001.rb.txt +16 -0
  116. data/spec/handlers/examples/visibility_handler_001.rb.txt +20 -0
  117. data/spec/handlers/examples/yield_handler_001.rb.txt +55 -0
  118. data/spec/handlers/exception_handler_spec.rb +35 -0
  119. data/spec/handlers/method_handler_spec.rb +35 -0
  120. data/spec/handlers/mixin_handler_spec.rb +30 -0
  121. data/spec/handlers/module_handler_spec.rb +25 -0
  122. data/spec/handlers/spec_helper.rb +21 -0
  123. data/spec/handlers/visibility_handler_spec.rb +24 -0
  124. data/spec/handlers/yield_handler_spec.rb +51 -0
  125. data/spec/parser/examples/example1.rb.txt +8 -0
  126. data/spec/parser/examples/tag_handler_001.rb.txt +8 -0
  127. data/spec/parser/source_parser_spec.rb +43 -0
  128. data/spec/parser/tag_parsing_spec.rb +18 -0
  129. data/spec/parser/token_list_spec.rb +35 -0
  130. data/spec/registry_spec.rb +70 -0
  131. data/spec/serializers/file_system_serializer_spec.rb +91 -0
  132. data/spec/serializers/spec_helper.rb +2 -0
  133. data/spec/spec_helper.rb +77 -0
  134. data/templates/default/attributes/html/header.erb +35 -0
  135. data/templates/default/attributes/text/header.erb +10 -0
  136. data/templates/default/class/html/header.erb +4 -0
  137. data/templates/default/constants/html/constants.erb +9 -0
  138. data/templates/default/constants/html/header.erb +3 -0
  139. data/templates/default/constants/html/included.erb +9 -0
  140. data/templates/default/constants/html/inherited.erb +9 -0
  141. data/templates/default/constructor/html/header.erb +10 -0
  142. data/templates/default/deprecated/html/main.erb +4 -0
  143. data/templates/default/deprecated/text/main.erb +3 -0
  144. data/templates/default/docstring/html/main.erb +3 -0
  145. data/templates/default/docstring/text/main.erb +3 -0
  146. data/templates/default/fulldoc/html/all_methods.erb +25 -0
  147. data/templates/default/fulldoc/html/all_namespaces.erb +19 -0
  148. data/templates/default/fulldoc/html/app.js +18 -0
  149. data/templates/default/fulldoc/html/header.erb +15 -0
  150. data/templates/default/fulldoc/html/html_head.erb +3 -0
  151. data/templates/default/fulldoc/html/index.erb +18 -0
  152. data/templates/default/fulldoc/html/jquery.js +11 -0
  153. data/templates/default/fulldoc/html/readme.erb +15 -0
  154. data/templates/default/fulldoc/html/style.css +65 -0
  155. data/templates/default/fulldoc/html/syntax_highlight.css +21 -0
  156. data/templates/default/inheritance/html/header.erb +8 -0
  157. data/templates/default/inheritance/text/header.erb +3 -0
  158. data/templates/default/method/html/aliases.erb +6 -0
  159. data/templates/default/method/html/header.erb +3 -0
  160. data/templates/default/method/html/title.erb +3 -0
  161. data/templates/default/methoddetails/html/header.erb +8 -0
  162. data/templates/default/methoddetails/html/method_header.erb +3 -0
  163. data/templates/default/methodmissing/html/header.erb +12 -0
  164. data/templates/default/methodsignature/html/main.erb +8 -0
  165. data/templates/default/methodsignature/text/main.erb +5 -0
  166. data/templates/default/methodsummary/html/header.erb +5 -0
  167. data/templates/default/methodsummary/html/included.erb +9 -0
  168. data/templates/default/methodsummary/html/inherited.erb +9 -0
  169. data/templates/default/methodsummary/html/summary.erb +25 -0
  170. data/templates/default/methodsummary/text/header.erb +5 -0
  171. data/templates/default/methodsummary/text/included.erb +0 -0
  172. data/templates/default/methodsummary/text/inherited.erb +0 -0
  173. data/templates/default/methodsummary/text/summary.erb +3 -0
  174. data/templates/default/mixins/html/header.erb +4 -0
  175. data/templates/default/module/html/header.erb +4 -0
  176. data/templates/default/quickdoc/html/header.erb +15 -0
  177. data/templates/default/quickdoc/text/header.erb +12 -0
  178. data/templates/default/source/html/main.erb +15 -0
  179. data/templates/default/source/text/main.erb +4 -0
  180. data/templates/default/tags/html/header.erb +4 -0
  181. data/templates/default/tags/html/see.erb +13 -0
  182. data/templates/default/tags/html/tags.erb +20 -0
  183. data/templates/default/tags/text/header.erb +3 -0
  184. data/templates/default/tags/text/see.erb +5 -0
  185. data/templates/default/tags/text/tags.erb +7 -0
  186. data/templates/default/uml/dot/child.erb +1 -0
  187. data/templates/default/uml/dot/dependencies.erb +10 -0
  188. data/templates/default/uml/dot/header.erb +6 -0
  189. data/templates/default/uml/dot/info.erb +14 -0
  190. data/templates/default/uml/dot/subgraph.erb +6 -0
  191. data/templates/default/uml/dot/superclasses.erb +9 -0
  192. data/templates/default/uml/dot/unknown.erb +3 -0
  193. data/templates/default/uml/dot/unknown_child.erb +1 -0
  194. data/templates/default/visibilitygroup/html/header.erb +6 -0
  195. data/templates/javadoc/attributes/html/header.erb +16 -0
  196. data/templates/javadoc/class/html/header.erb +4 -0
  197. data/templates/javadoc/constants/html/constants.erb +9 -0
  198. data/templates/javadoc/constants/html/header.erb +3 -0
  199. data/templates/javadoc/constants/html/included.erb +12 -0
  200. data/templates/javadoc/constants/html/inherited.erb +12 -0
  201. data/templates/javadoc/constructor/html/header.erb +10 -0
  202. data/templates/javadoc/deprecated/html/main.erb +0 -0
  203. data/templates/javadoc/docstring/html/main.erb +6 -0
  204. data/templates/javadoc/fulldoc/html/all_methods.erb +25 -0
  205. data/templates/javadoc/fulldoc/html/all_namespaces.erb +19 -0
  206. data/templates/javadoc/fulldoc/html/app.js +18 -0
  207. data/templates/javadoc/fulldoc/html/header.erb +15 -0
  208. data/templates/javadoc/fulldoc/html/html_head.erb +3 -0
  209. data/templates/javadoc/fulldoc/html/index.erb +18 -0
  210. data/templates/javadoc/fulldoc/html/jquery.js +11 -0
  211. data/templates/javadoc/fulldoc/html/readme.erb +15 -0
  212. data/templates/javadoc/fulldoc/html/style.css +22 -0
  213. data/templates/javadoc/fulldoc/html/syntax_highlight.css +21 -0
  214. data/templates/javadoc/inheritance/html/header.erb +6 -0
  215. data/templates/javadoc/method/html/aliases.erb +6 -0
  216. data/templates/javadoc/method/html/header.erb +4 -0
  217. data/templates/javadoc/method/html/title.erb +4 -0
  218. data/templates/javadoc/methoddetails/html/header.erb +8 -0
  219. data/templates/javadoc/methoddetails/html/method_header.erb +0 -0
  220. data/templates/javadoc/methodmissing/html/header.erb +12 -0
  221. data/templates/javadoc/methodsignature/html/main.erb +8 -0
  222. data/templates/javadoc/methodsummary/html/header.erb +5 -0
  223. data/templates/javadoc/methodsummary/html/included.erb +12 -0
  224. data/templates/javadoc/methodsummary/html/inherited.erb +12 -0
  225. data/templates/javadoc/methodsummary/html/summary.erb +25 -0
  226. data/templates/javadoc/mixins/html/header.erb +5 -0
  227. data/templates/javadoc/module/html/header.erb +4 -0
  228. data/templates/javadoc/source/html/main.erb +15 -0
  229. data/templates/javadoc/tags/html/header.erb +5 -0
  230. data/templates/javadoc/tags/html/see.erb +8 -0
  231. data/templates/javadoc/tags/html/tags.erb +19 -0
  232. data/templates/javadoc/visibilitygroup/html/header.erb +5 -0
  233. metadata +352 -50
  234. data/README.pdf +0 -0
  235. data/lib/code_object.rb +0 -337
  236. data/lib/extra.rb +0 -8
  237. data/lib/formatter.rb +0 -90
  238. data/lib/handlers/all_handlers.rb +0 -2
  239. data/lib/handlers/attribute_handler.rb +0 -51
  240. data/lib/handlers/class_handler.rb +0 -30
  241. data/lib/handlers/class_variable_handler.rb +0 -9
  242. data/lib/handlers/code_object_handler.rb +0 -104
  243. data/lib/handlers/constant_handler.rb +0 -11
  244. data/lib/handlers/exception_handler.rb +0 -20
  245. data/lib/handlers/method_handler.rb +0 -28
  246. data/lib/handlers/mixin_handler.rb +0 -15
  247. data/lib/handlers/module_handler.rb +0 -9
  248. data/lib/handlers/visibility_handler.rb +0 -7
  249. data/lib/handlers/yield_handler.rb +0 -33
  250. data/lib/logger.rb +0 -19
  251. data/lib/namespace.rb +0 -98
  252. data/lib/quick_doc.rb +0 -104
  253. data/lib/ruby_lex.rb +0 -1321
  254. data/lib/source_parser.rb +0 -253
  255. data/lib/tag_library.rb +0 -175
  256. data/lib/tag_type.rb +0 -155
  257. data/templates/default/html/_fulldoc.erb +0 -64
  258. data/templates/default/html/class.erb +0 -226
  259. data/templates/default/html/method.erb +0 -20
  260. data/templates/default/html/module.erb +0 -126
  261. data/test/fixtures/docstring.txt +0 -23
  262. data/test/fixtures/docstring2.txt +0 -4
  263. data/test/test_code_object.rb +0 -66
  264. data/test/test_namespace.rb +0 -10
@@ -0,0 +1,32 @@
1
+ class YARD::Handlers::AliasHandler < YARD::Handlers::Base
2
+ handles /\Aalias(_method)?(\s|\()/
3
+
4
+ def process
5
+ if TkALIAS === statement.tokens.first
6
+ tokens = statement.tokens[2..-1].to_s.split(/\s+/)
7
+ names = [tokens[0], tokens[1]].map {|t| t.gsub(/^:/, '') }
8
+ else
9
+ names = tokval_list(statement.tokens[2..-1], :attr)
10
+ end
11
+ raise YARD::Handlers::UndocumentableError, statement.tokens.first.text if names.size != 2
12
+
13
+ new_meth, old_meth = names[0].to_sym, names[1].to_sym
14
+ old_obj = namespace.child(:name => old_meth, :scope => scope)
15
+ new_obj = register MethodObject.new(namespace, new_meth, scope) do |o|
16
+ o.visibility = visibility
17
+ o.scope = scope
18
+ o.line = statement.tokens.first.line_no
19
+ o.file = parser.file
20
+ o.docstring = statement.comments
21
+
22
+ if old_obj
23
+ o.signature = old_obj.signature
24
+ o.source = old_obj.source
25
+ else
26
+ o.signature = "def #{new_meth}" # this is all we know.
27
+ end
28
+ end
29
+
30
+ namespace.aliases[new_obj] = old_meth
31
+ end
32
+ end
@@ -0,0 +1,54 @@
1
+ class YARD::Handlers::AttributeHandler < YARD::Handlers::Base
2
+ handles /\Aattr(?:_(?:reader|writer|accessor))?(?:\s|\()/
3
+
4
+ def process
5
+ begin
6
+ attr_type = statement.tokens.first.text.to_sym
7
+ symbols = tokval_list statement.tokens[2..-1], :attr, TkTRUE, TkFALSE
8
+ read, write = true, false
9
+ rescue SyntaxError
10
+ raise YARD::Handlers::UndocumentableError, attr_type
11
+ end
12
+
13
+ # Change read/write based on attr_reader/writer/accessor
14
+ case attr_type
15
+ when :attr
16
+ # In the case of 'attr', the second parameter (if given) isn't a symbol.
17
+ write = symbols.pop if symbols.size == 2
18
+ when :attr_accessor
19
+ write = true
20
+ when :attr_reader
21
+ # change nothing
22
+ when :attr_writer
23
+ read, write = false, true
24
+ end
25
+
26
+ # Add all attributes
27
+ symbols.each do |name|
28
+ namespace.attributes[scope][name] = SymbolHash[:read => nil, :write => nil]
29
+
30
+ # Show their methods as well
31
+ {:read => name, :write => "#{name}="}.each do |type, meth|
32
+ next unless (type == :read ? read : write)
33
+
34
+ namespace.attributes[scope][name][type] = MethodObject.new(namespace, meth, scope) do |o|
35
+ if type == :write
36
+ src = "def #{meth}(value)"
37
+ full_src = "#{src}\n @#{name} = value\nend"
38
+ doc = "Sets the attribute +#{name}+\n@param value the value to set the attribute +#{name}+ to."
39
+ else
40
+ src = "def #{meth}"
41
+ full_src = "#{src}\n @#{name}\nend"
42
+ doc = "Returns the value of attribute +#{name}+"
43
+ end
44
+ o.source ||= full_src
45
+ o.signature ||= src
46
+ o.docstring = statement.comments.to_s.empty? ? doc : statement.comments
47
+ end
48
+
49
+ # Register the objects explicitly
50
+ register namespace.attributes[scope][name][type]
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,509 @@
1
+ module YARD
2
+ module Handlers
3
+ class UndocumentableError < Exception; end
4
+
5
+ # = 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
+ #
18
+ # == Overview of a Typical Handler Scenario
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 should be registered by {#register}, a method that
26
+ # sets some basic attributes for the new objects.
27
+ #
28
+ # Handlers are usually simple and take up to a page of code to process
29
+ # and register a new object or add new attributes to the current +namespace+.
30
+ #
31
+ # == Setting up a Handler for Use
32
+ #
33
+ # A Handler is automatically registered when it is subclassed from the
34
+ # base class. The only other thing that needs to be done is to specify
35
+ # which statement the handler will process. This is done with the +handles+
36
+ # declaration, taking either a {Parser::RubyToken}, {String} or {Regexp}.
37
+ # Here is a simple example which processes module statements.
38
+ #
39
+ # class MyModuleHandler < YARD::Handlers::Base
40
+ # handles TkMODULE
41
+ #
42
+ # def process
43
+ # # do something
44
+ # end
45
+ # end
46
+ #
47
+ # == Processing Handler Data
48
+ #
49
+ # The goal of a specific handler is really up to the developer, and as
50
+ # such there is no real guideline on how to process the data. However,
51
+ # it is important to know where the data is coming from to be able to use
52
+ # it.
53
+ #
54
+ # === +statement+ Attribute
55
+ #
56
+ # The +statement+ attribute pertains to the {Parser::Statement} object
57
+ # containing a set of tokens parsed in by the parser. This is the main set
58
+ # of data to be analyzed and processed. The comments attached to the statement
59
+ # can be accessed by the {Parser::Statement#comments} method, but generally
60
+ # the data to be processed will live in the +tokens+ attribute. This list
61
+ # can be converted to a +String+ using +#to_s+ to parse the data with
62
+ # regular expressions (or other text processing mechanisms), if needed.
63
+ #
64
+ # === +namespace+ Attribute
65
+ #
66
+ # The +namespace+ attribute is a {CodeObjects::NamespaceObject namespace object}
67
+ # which represents the current namespace that the parser is in. For instance:
68
+ #
69
+ # module SomeModule
70
+ # class MyClass
71
+ # def mymethod; end
72
+ # end
73
+ # end
74
+ #
75
+ # If a handler was to parse the 'class MyClass' statement, it would
76
+ # be necessary to know that it belonged inside the SomeModule module.
77
+ # This is the value that +namespace+ would return when processing such
78
+ # a statement. If the class was then entered and another handler was
79
+ # called on the method, the +namespace+ would be set to the 'MyClass'
80
+ # code object.
81
+ #
82
+ # === +owner+ Attribute
83
+ #
84
+ # The +owner+ attribute is similar to the +namespace+ attribute in that
85
+ # it also follows the scope of the code during parsing. However, a namespace
86
+ # object is loosely defined as a module or class and YARD has the ability
87
+ # to parse beyond module and class blocks (inside methods, for instance),
88
+ # so the +owner+ attribute would not be limited to modules and classes.
89
+ #
90
+ # To put this into context, the example from above will be used. If a method
91
+ # handler was added to the mix and decided to parse inside the method body,
92
+ # the +owner+ would be set to the method object but the namespace would remain
93
+ # set to the class. This would allow the developer to process any method
94
+ # definitions set inside a method (def x; def y; 2 end end) by adding them
95
+ # to the correct namespace (the class, not the method).
96
+ #
97
+ # In summary, the distinction between +namespace+ and +owner+ can be thought
98
+ # of as the difference between first-class Ruby objects (namespaces) and
99
+ # second-class Ruby objects (methods).
100
+ #
101
+ # === +visibility+ and +scope+ Attributes
102
+ #
103
+ # Mainly needed for parsing methods, the +visibility+ and +scope+ attributes
104
+ # refer to the public/protected/private and class/instance values (respectively)
105
+ # of the current parsing position.
106
+ #
107
+ # == Parsing Blocks in Statements
108
+ #
109
+ # In addition to parsing a statement and creating new objects, some
110
+ # handlers may wish to continue parsing the code inside the statement's
111
+ # block (if there is one). In this context, a block means the inside
112
+ # of any statement, be it class definition, module definition, if
113
+ # statement or classic 'Ruby block'.
114
+ #
115
+ # For example, a class statement would be "class MyClass" and the block
116
+ # would be a list of statements including the method definitions inside
117
+ # the class. For a class handler, the programmer would execute the
118
+ # {#parse_block} method to continue parsing code inside the block, with
119
+ # the +namespace+ now pointing to the class object the handler created.
120
+ #
121
+ # YARD has the ability to continue into any block: class, module, method,
122
+ # even if statements. For this reason, the block parsing method must be
123
+ # invoked explicitly out of efficiency sake.
124
+ #
125
+ # @see CodeObjects::Base
126
+ # @see CodeObjects::NamespaceObject
127
+ # @see handles
128
+ # @see #namespace
129
+ # @see #owner
130
+ # @see #register
131
+ # @see #parse_block
132
+ #
133
+ class Base
134
+ attr_accessor :__context__
135
+
136
+ # For accessing convenience, eg. "MethodObject"
137
+ # instead of the full qualified namespace
138
+ include YARD::CodeObjects
139
+
140
+ # For tokens like TkDEF, TkCLASS, etc.
141
+ include YARD::Parser::RubyToken
142
+
143
+ class << self
144
+ def clear_subclasses
145
+ @@subclasses = []
146
+ end
147
+
148
+ def subclasses
149
+ @@subclasses || []
150
+ end
151
+
152
+ def inherited(subclass)
153
+ @@subclasses ||= []
154
+ @@subclasses << subclass
155
+ end
156
+
157
+ # Declares the statement type which will be processed
158
+ # by this handler.
159
+ #
160
+ # A match need not be unique to a handler. Multiple
161
+ # handlers can process the same statement. However,
162
+ # in this case, care should be taken to make sure that
163
+ # {#parse_block} would only be executed by one of
164
+ # the handlers, otherwise the same code will be parsed
165
+ # multiple times and slow YARD down.
166
+ #
167
+ # @param [Parser::RubyToken, String, Regexp] match
168
+ # statements that match the declaration will be
169
+ # processed by this handler. A {String} match is
170
+ # equivalent to a +/\Astring/+ regular expression
171
+ # (match from the beginning of the line), and all
172
+ # token matches match only the first token of the
173
+ # statement.
174
+ #
175
+ def handles(match)
176
+ @handler = match
177
+ end
178
+
179
+ def handles?(tokens)
180
+ case @handler
181
+ when String
182
+ tokens.first.text == @handler
183
+ when Regexp
184
+ tokens.to_s =~ @handler ? true : false
185
+ else
186
+ @handler == tokens.first.class
187
+ end
188
+ end
189
+ end
190
+
191
+ def initialize(source_parser, stmt)
192
+ @parser = source_parser
193
+ @statement = stmt
194
+ end
195
+
196
+ # The main handler method called by the parser on a statement
197
+ # that matches the {handles} declaration.
198
+ #
199
+ # Subclasses should override this method to provide the handling
200
+ # functionality for the class.
201
+ #
202
+ # @return [Array<CodeObjects::Base>, CodeObjects::Base, Object]
203
+ # If this method returns a code object (or a list of them),
204
+ # they are passed to the +#register+ method which adds basic
205
+ # attributes. It is not necessary to return any objects and in
206
+ # some cases you may want to explicitly avoid the returning of
207
+ # any objects for post-processing by the register method.
208
+ #
209
+ # @see handles
210
+ # @see #register
211
+ #
212
+ def process
213
+ raise NotImplementedError, "#{self} did not implement a #process method for handling."
214
+ end
215
+
216
+ protected
217
+
218
+ attr_reader :parser, :statement
219
+ attr_accessor :owner, :namespace, :visibility, :scope
220
+
221
+ # Do some post processing on a list of code objects.
222
+ # Adds basic attributes to the list of objects like
223
+ # the filename, line number, {CodeObjects::Base#dynamic},
224
+ # source code and {CodeObjects::Base#docstring},
225
+ # but only if they don't exist.
226
+ #
227
+ # @param [Array<CodeObjects::Base>] objects
228
+ # the list of objects to post-process.
229
+ #
230
+ # @return [CodeObjects::Base, Array<CodeObjects::Base>]
231
+ # returns whatever is passed in, for chainability.
232
+ #
233
+ def register(*objects)
234
+ objects.flatten.each do |object|
235
+ next unless object.is_a?(CodeObjects::Base)
236
+
237
+ ensure_namespace_loaded!(object)
238
+
239
+ # Yield the object to the calling block because ruby will parse the syntax
240
+ #
241
+ # register obj = ClassObject.new {|o| ... }
242
+ #
243
+ # as the block for #register. We need to make sure this gets to the object.
244
+ yield(object) if block_given?
245
+
246
+ # Add file and line number, but for class/modules this is
247
+ # only done if there is a docstring for this specific definition.
248
+ if (object.is_a?(NamespaceObject) && statement.comments) || !object.is_a?(NamespaceObject)
249
+ object.file = parser.file
250
+ object.line = statement.tokens.first.line_no
251
+ elsif object.is_a?(NamespaceObject) && !statement.comments
252
+ object.file ||= parser.file
253
+ object.line ||= statement.tokens.first.line_no
254
+ end
255
+
256
+ # Add docstring if there is one.
257
+ object.docstring = statement.comments if statement.comments
258
+
259
+ # Add source only to non-class non-module objects
260
+ unless object.is_a?(NamespaceObject)
261
+ object.source ||= statement
262
+ end
263
+
264
+ # Make it dynamic if it's owner is not it's namespace.
265
+ # This generally means it was defined in a method (or block of some sort)
266
+ object.dynamic = true if owner != namespace
267
+ end
268
+ objects.size == 1 ? objects.first : objects
269
+ end
270
+
271
+ def parse_block(opts = nil)
272
+ opts = {
273
+ :namespace => nil,
274
+ :scope => :instance,
275
+ :owner => nil
276
+ }.update(opts || {})
277
+
278
+ if opts[:namespace]
279
+ ns, vis, sc = namespace, visibility, scope
280
+ self.namespace = opts[:namespace]
281
+ self.visibility = :public
282
+ self.scope = opts[:scope]
283
+ end
284
+
285
+ self.owner = opts[:owner] ? opts[:owner] : namespace
286
+ parser.parse(statement.block) if statement.block
287
+
288
+ if opts[:namespace]
289
+ self.namespace = ns
290
+ self.owner = namespace
291
+ self.visibility = vis
292
+ self.scope = sc
293
+ end
294
+ end
295
+
296
+ def owner; @parser.owner end
297
+ def owner=(v) @parser.owner=(v) end
298
+ def namespace; @parser.namespace end
299
+ def namespace=(v); @parser.namespace=(v) end
300
+ def visibility; @parser.visibility end
301
+ def visibility=(v); @parser.visibility=(v) end
302
+ def scope; @parser.scope end
303
+ def scope=(v); @parser.scope=(v) end
304
+
305
+ def ensure_namespace_loaded!(object, max_retries = 1)
306
+ unless parser.load_order_errors
307
+ return object.parent.is_a?(Proxy) ? load_order_warn(object.parent) : nil
308
+ end
309
+
310
+ raise NotImplementedError if RUBY_PLATFORM =~ /java/
311
+ return unless object.parent.is_a?(Proxy)
312
+
313
+ retries, context = 0, nil
314
+ callcc {|c| context = c }
315
+
316
+ retries += 1
317
+
318
+ if object.parent.is_a?(Proxy)
319
+ if retries <= max_retries
320
+ log.debug "Missing object #{object.parent} in file `#{parser.file}', moving it to the back of the line."
321
+ raise Parser::LoadOrderError, context
322
+ end
323
+
324
+ if retries > max_retries && !object.parent.is_a?(Proxy) && !BUILTIN_ALL.include?(object.path)
325
+ load_order_warn(object.parent)
326
+ end
327
+ else
328
+ log.debug "Object #{object} successfully resolved. Adding item to #{object.parent}'s children"
329
+ object.namespace.children << object
330
+ end
331
+
332
+ rescue NotImplementedError
333
+ log.warn "JRuby does not implement Kernel#callcc and cannot load files in order. You must specify the correct order manually."
334
+ load_order_warn(object.parent)
335
+ end
336
+
337
+ def load_order_warn(object)
338
+ log.warn "The #{object.type} #{object.path} has not yet been recognized."
339
+ log.warn "If this class/method is part of your source tree, this will affect your documentation results."
340
+ log.warn "You can correct this issue by loading the source file for this object before `#{parser.file}'"
341
+ log.warn
342
+ end
343
+
344
+ # The string value of a token. For example, the return value for the symbol :sym
345
+ # would be :sym. The return value for a string "foo #{bar}" would be the literal
346
+ # "foo #{bar}" without any interpolation. The return value of the identifier
347
+ # 'test' would be the same value: 'test'. Here is a list of common types and
348
+ # their return values:
349
+ #
350
+ # @example
351
+ # tokval(TokenList.new('"foo"').first) => "foo"
352
+ # tokval(TokenList.new(':foo').first) => :foo
353
+ # tokval(TokenList.new('CONSTANT').first, RubyToken::TkId) => "CONSTANT"
354
+ # tokval(TokenList.new('identifier').first, RubyToken::TkId) => "identifier"
355
+ # tokval(TokenList.new('3.25').first) => 3.25
356
+ # tokval(TokenList.new('/xyz/i').first) => /xyz/i
357
+ #
358
+ # @param [Token] token The token of the class
359
+ #
360
+ # @param [Array<Class<Token>>, Symbol] accepted_types
361
+ # The allowed token types that this token can be. Defaults to [{TkVal}].
362
+ # A list of types would be, for example, [{TkSTRING}, {TkSYMBOL}], to return
363
+ # the token's value if it is either of those types. If +TkVal+ is accepted,
364
+ # +TkNode+ is also accepted.
365
+ #
366
+ # Certain symbol keys are allowed to specify multiple types in one fell swoop.
367
+ # These symbols are:
368
+ # :string => +TkSTRING+, +TkDSTRING+, +TkDXSTRING+ and +TkXSTRING+
369
+ # :attr => +TkSYMBOL+ and +TkSTRING+
370
+ # :identifier => +TkIDENTIFIER, +TkFID+ and +TkGVAR+.
371
+ # :number => +TkFLOAT+, +TkINTEGER+
372
+ #
373
+ # @return [Object] if the token is one of the accepted types, in its real value form.
374
+ # It should be noted that identifiers and constants are kept in String form.
375
+ # @return [nil] if the token is not any of the specified accepted types
376
+ def tokval(token, *accepted_types)
377
+ accepted_types = [TkVal] if accepted_types.empty?
378
+ accepted_types.push(TkNode) if accepted_types.include? TkVal
379
+
380
+ if accepted_types.include?(:attr)
381
+ accepted_types.push(TkSTRING, TkSYMBOL)
382
+ end
383
+
384
+ if accepted_types.include?(:string)
385
+ accepted_types.push(TkSTRING, TkDSTRING, TkXSTRING, TkDXSTRING)
386
+ end
387
+
388
+ if accepted_types.include?(:identifier)
389
+ accepted_types.push(TkIDENTIFIER, TkFID, TkGVAR)
390
+ end
391
+
392
+ if accepted_types.include?(:number)
393
+ accepted_types.push(TkFLOAT, TkINTEGER)
394
+ end
395
+
396
+ return unless accepted_types.any? {|t| t === token }
397
+
398
+ case token
399
+ when TkSTRING, TkDSTRING, TkXSTRING, TkDXSTRING
400
+ token.text[1..-2]
401
+ when TkSYMBOL
402
+ token.text[1..-1].to_sym
403
+ when TkFLOAT
404
+ token.text.to_f
405
+ when TkINTEGER
406
+ token.text.to_i
407
+ when TkREGEXP
408
+ token.text =~ /\A\/(.+)\/([^\/])\Z/
409
+ Regexp.new($1, $2)
410
+ when TkTRUE
411
+ true
412
+ when TkFALSE
413
+ false
414
+ when TkNIL
415
+ nil
416
+ else
417
+ token.text
418
+ end
419
+ end
420
+
421
+ # Returns a list of symbols or string values from a statement.
422
+ # The list must be a valid comma delimited list, and values
423
+ # will only be returned to the end of the list only.
424
+ #
425
+ # Example:
426
+ # attr_accessor :a, 'b', :c, :d => ['a', 'b', 'c', 'd']
427
+ # attr_accessor 'a', UNACCEPTED_TYPE, 'c' => ['a', 'c']
428
+ #
429
+ # The tokval list of a {TokenList} of the above
430
+ # code would be the {#tokval} value of :a, 'b',
431
+ # :c and :d.
432
+ #
433
+ # It should also be noted that this function stops immediately at
434
+ # any ruby keyword encountered:
435
+ # "attr_accessor :a, :b, :c if x == 5" => ['a', 'b', 'c']
436
+ #
437
+ # @param [TokenList] tokenlist The list of tokens to process.
438
+ # @param [Array<Class<Token>>] accepted_types passed to {#tokval}
439
+ # @return [Array<String>] the list of tokvalues in the list.
440
+ # @return [Array<EMPTY>] if there are no symbols or Strings in the list
441
+ # @see #tokval
442
+ def tokval_list(tokenlist, *accepted_types)
443
+ return [] unless tokenlist
444
+ out = [[]]
445
+ parencount, beforeparen = 0, 0
446
+ needcomma = false
447
+ seen_comma = true
448
+ tokenlist.each do |token|
449
+ tokval = tokval(token, *accepted_types)
450
+ parencond = !out.last.empty? && tokval != nil
451
+ #puts "#{seen_comma.inspect} #{parencount} #{token.class.class_name} #{out.inspect}"
452
+ case token
453
+ when TkCOMMA
454
+ if parencount == 0
455
+ out << [] unless out.last.empty?
456
+ needcomma = false
457
+ seen_comma = true
458
+ else
459
+ out.last << token.text if parencond
460
+ end
461
+ when TkLPAREN
462
+ if seen_comma
463
+ beforeparen += 1
464
+ else
465
+ parencount += 1
466
+ out.last << token.text if parencond
467
+ end
468
+ when TkRPAREN
469
+ if beforeparen > 0
470
+ beforeparen -= 1
471
+ else
472
+ out.last << token.text if parencount > 0 && tokval != nil
473
+ parencount -= 1
474
+ end
475
+ when TkLBRACE, TkLBRACK, TkDO
476
+ parencount += 1
477
+ out.last << token.text if tokval != nil
478
+ when TkRBRACE, TkRBRACK, TkEND
479
+ out.last << token.text if tokval != nil
480
+ parencount -= 1
481
+ else
482
+ break if TkKW === token && ![TkTRUE, TkFALSE, TkSUPER, TkSELF, TkNIL].include?(token.class)
483
+
484
+ seen_comma = false unless TkWhitespace === token
485
+ if parencount == 0
486
+ next if needcomma
487
+ next if TkWhitespace === token
488
+ if tokval != nil
489
+ out.last << tokval
490
+ else
491
+ out.last.clear
492
+ needcomma = true
493
+ end
494
+ elsif parencond
495
+ needcomma = true
496
+ out.last << token.text
497
+ end
498
+ end
499
+
500
+ if beforeparen == 0 && parencount < 0
501
+ break
502
+ end
503
+ end
504
+ # Flatten any single element lists
505
+ out.map {|e| e.empty? ? nil : (e.size == 1 ? e.pop : e.flatten.join) }.compact
506
+ end
507
+ end
508
+ end
509
+ end