yard 0.5.8 → 0.6.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.

Files changed (211) hide show
  1. data/.yardopts +2 -0
  2. data/ChangeLog +1064 -0
  3. data/README.md +103 -42
  4. data/Rakefile +3 -2
  5. data/benchmarks/parsing.rb +2 -1
  6. data/bin/yard +4 -0
  7. data/bin/yard-graph +1 -1
  8. data/bin/yard-server +4 -0
  9. data/docs/GettingStarted.md +8 -8
  10. data/docs/Handlers.md +5 -5
  11. data/docs/Overview.md +5 -5
  12. data/docs/Parser.md +1 -1
  13. data/docs/Tags.md +1 -1
  14. data/docs/Templates.md +27 -6
  15. data/docs/WhatsNew.md +222 -2
  16. data/lib/rubygems_plugin.rb +1 -0
  17. data/lib/yard.rb +7 -1
  18. data/lib/yard/autoload.rb +46 -6
  19. data/lib/yard/cli/{base.rb → command.rb} +20 -6
  20. data/lib/yard/cli/command_parser.rb +87 -0
  21. data/lib/yard/cli/diff.rb +176 -0
  22. data/lib/yard/cli/gems.rb +74 -0
  23. data/lib/yard/cli/{yard_graph.rb → graph.rb} +9 -8
  24. data/lib/yard/cli/help.rb +18 -0
  25. data/lib/yard/cli/server.rb +137 -0
  26. data/lib/yard/cli/stats.rb +210 -0
  27. data/lib/yard/cli/yardoc.rb +315 -116
  28. data/lib/yard/cli/yri.rb +45 -4
  29. data/lib/yard/code_objects/base.rb +73 -30
  30. data/lib/yard/code_objects/class_object.rb +9 -1
  31. data/lib/yard/code_objects/method_object.rb +11 -0
  32. data/lib/yard/code_objects/namespace_object.rb +8 -2
  33. data/lib/yard/code_objects/proxy.rb +2 -2
  34. data/lib/yard/core_ext/array.rb +3 -49
  35. data/lib/yard/core_ext/file.rb +7 -0
  36. data/lib/yard/core_ext/insertion.rb +60 -0
  37. data/lib/yard/docstring.rb +34 -7
  38. data/lib/yard/globals.rb +2 -2
  39. data/lib/yard/handlers/base.rb +101 -20
  40. data/lib/yard/handlers/processor.rb +23 -7
  41. data/lib/yard/handlers/ruby/alias_handler.rb +1 -0
  42. data/lib/yard/handlers/ruby/attribute_handler.rb +8 -0
  43. data/lib/yard/handlers/ruby/base.rb +71 -2
  44. data/lib/yard/handlers/ruby/class_condition_handler.rb +10 -0
  45. data/lib/yard/handlers/ruby/class_handler.rb +7 -4
  46. data/lib/yard/handlers/ruby/class_variable_handler.rb +1 -0
  47. data/lib/yard/handlers/ruby/constant_handler.rb +1 -0
  48. data/lib/yard/handlers/ruby/exception_handler.rb +1 -0
  49. data/lib/yard/handlers/ruby/extend_handler.rb +2 -3
  50. data/lib/yard/handlers/ruby/legacy/alias_handler.rb +1 -0
  51. data/lib/yard/handlers/ruby/legacy/attribute_handler.rb +2 -0
  52. data/lib/yard/handlers/ruby/legacy/base.rb +15 -2
  53. data/lib/yard/handlers/ruby/legacy/class_condition_handler.rb +5 -0
  54. data/lib/yard/handlers/ruby/legacy/class_handler.rb +7 -4
  55. data/lib/yard/handlers/ruby/legacy/class_variable_handler.rb +1 -0
  56. data/lib/yard/handlers/ruby/legacy/constant_handler.rb +1 -0
  57. data/lib/yard/handlers/ruby/legacy/exception_handler.rb +1 -0
  58. data/lib/yard/handlers/ruby/legacy/extend_handler.rb +1 -3
  59. data/lib/yard/handlers/ruby/legacy/method_handler.rb +7 -3
  60. data/lib/yard/handlers/ruby/legacy/mixin_handler.rb +2 -1
  61. data/lib/yard/handlers/ruby/legacy/module_handler.rb +1 -0
  62. data/lib/yard/handlers/ruby/legacy/process_handler.rb +1 -0
  63. data/lib/yard/handlers/ruby/legacy/visibility_handler.rb +1 -0
  64. data/lib/yard/handlers/ruby/legacy/yield_handler.rb +1 -0
  65. data/lib/yard/handlers/ruby/method_condition_handler.rb +1 -0
  66. data/lib/yard/handlers/ruby/method_handler.rb +5 -1
  67. data/lib/yard/handlers/ruby/mixin_handler.rb +2 -1
  68. data/lib/yard/handlers/ruby/module_handler.rb +1 -0
  69. data/lib/yard/handlers/ruby/process_handler.rb +7 -1
  70. data/lib/yard/handlers/ruby/struct_handler_methods.rb +3 -0
  71. data/lib/yard/handlers/ruby/visibility_handler.rb +8 -2
  72. data/lib/yard/handlers/ruby/yield_handler.rb +1 -0
  73. data/lib/yard/logging.rb +7 -1
  74. data/lib/yard/parser/base.rb +1 -0
  75. data/lib/yard/parser/c_parser.rb +2 -0
  76. data/lib/yard/parser/ruby/ast_node.rb +82 -63
  77. data/lib/yard/parser/ruby/legacy/ruby_lex.rb +36 -10
  78. data/lib/yard/parser/ruby/legacy/ruby_parser.rb +1 -0
  79. data/lib/yard/parser/ruby/legacy/statement.rb +9 -5
  80. data/lib/yard/parser/ruby/legacy/statement_list.rb +20 -11
  81. data/lib/yard/parser/ruby/ruby_parser.rb +18 -1
  82. data/lib/yard/parser/source_parser.rb +6 -1
  83. data/lib/yard/registry.rb +284 -278
  84. data/lib/yard/registry_store.rb +4 -2
  85. data/lib/yard/serializers/base.rb +30 -13
  86. data/lib/yard/serializers/file_system_serializer.rb +10 -1
  87. data/lib/yard/server/adapter.rb +51 -0
  88. data/lib/yard/server/commands/base.rb +98 -0
  89. data/lib/yard/server/commands/display_file_command.rb +20 -0
  90. data/lib/yard/server/commands/display_object_command.rb +50 -0
  91. data/lib/yard/server/commands/frames_command.rb +31 -0
  92. data/lib/yard/server/commands/library_command.rb +83 -0
  93. data/lib/yard/server/commands/library_index_command.rb +23 -0
  94. data/lib/yard/server/commands/list_command.rb +44 -0
  95. data/lib/yard/server/commands/search_command.rb +67 -0
  96. data/lib/yard/server/commands/static_file_command.rb +45 -0
  97. data/lib/yard/server/doc_server_helper.rb +22 -0
  98. data/lib/yard/server/doc_server_serializer.rb +29 -0
  99. data/lib/yard/server/library_version.rb +86 -0
  100. data/lib/yard/server/rack_adapter.rb +38 -0
  101. data/lib/yard/server/router.rb +110 -0
  102. data/lib/yard/server/static_caching.rb +16 -0
  103. data/lib/yard/server/templates/default/fulldoc/html/css/custom.css +78 -0
  104. data/lib/yard/server/templates/default/fulldoc/html/images/processing.gif +0 -0
  105. data/lib/yard/server/templates/default/fulldoc/html/js/autocomplete.js +12 -0
  106. data/lib/yard/server/templates/default/fulldoc/html/js/live.js +32 -0
  107. data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +46 -0
  108. data/lib/yard/server/templates/default/layout/html/headers.erb +11 -0
  109. data/lib/yard/server/templates/doc_server/frames/html/frames.erb +13 -0
  110. data/lib/yard/server/templates/doc_server/frames/html/setup.rb +3 -0
  111. data/lib/yard/server/templates/doc_server/full_list/html/full_list.erb +34 -0
  112. data/lib/yard/server/templates/doc_server/full_list/html/setup.rb +10 -0
  113. data/lib/yard/server/templates/doc_server/library_list/html/contents.erb +13 -0
  114. data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +26 -0
  115. data/lib/yard/server/templates/doc_server/library_list/html/library_list.erb +12 -0
  116. data/lib/yard/server/templates/doc_server/library_list/html/setup.rb +3 -0
  117. data/lib/yard/server/templates/doc_server/library_list/html/title.erb +2 -0
  118. data/lib/yard/server/templates/doc_server/processing/html/processing.erb +51 -0
  119. data/lib/yard/server/templates/doc_server/processing/html/setup.rb +3 -0
  120. data/lib/yard/server/templates/doc_server/search/html/search.erb +19 -0
  121. data/lib/yard/server/templates/doc_server/search/html/setup.rb +8 -0
  122. data/lib/yard/server/webrick_adapter.rb +38 -0
  123. data/lib/yard/tags/default_factory.rb +0 -5
  124. data/lib/yard/tags/library.rb +61 -22
  125. data/lib/yard/tags/tag.rb +26 -4
  126. data/lib/yard/templates/engine.rb +12 -1
  127. data/lib/yard/templates/erb_cache.rb +2 -1
  128. data/lib/yard/templates/helpers/base_helper.rb +96 -3
  129. data/lib/yard/templates/helpers/filter_helper.rb +5 -0
  130. data/lib/yard/templates/helpers/html_helper.rb +204 -94
  131. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +4 -0
  132. data/lib/yard/templates/helpers/markup_helper.rb +58 -3
  133. data/lib/yard/templates/helpers/method_helper.rb +7 -0
  134. data/lib/yard/templates/helpers/module_helper.rb +5 -0
  135. data/lib/yard/templates/helpers/text_helper.rb +10 -1
  136. data/lib/yard/templates/helpers/uml_helper.rb +13 -0
  137. data/lib/yard/templates/section.rb +106 -0
  138. data/lib/yard/templates/template.rb +20 -19
  139. data/lib/yard/verifier.rb +21 -2
  140. data/spec/cli/command_parser_spec.rb +43 -0
  141. data/spec/cli/diff_spec.rb +170 -0
  142. data/spec/cli/help_spec.rb +22 -0
  143. data/spec/cli/server_spec.rb +140 -0
  144. data/spec/cli/stats_spec.rb +75 -0
  145. data/spec/cli/yardoc_spec.rb +438 -182
  146. data/spec/cli/yri_spec.rb +13 -1
  147. data/spec/code_objects/base_spec.rb +51 -6
  148. data/spec/code_objects/class_object_spec.rb +15 -1
  149. data/spec/code_objects/method_object_spec.rb +29 -0
  150. data/spec/code_objects/namespace_object_spec.rb +150 -129
  151. data/spec/core_ext/array_spec.rb +4 -23
  152. data/spec/core_ext/insertion_spec.rb +37 -0
  153. data/spec/docstring_spec.rb +63 -0
  154. data/spec/handlers/attribute_handler_spec.rb +4 -0
  155. data/spec/handlers/base_spec.rb +98 -26
  156. data/spec/handlers/class_handler_spec.rb +5 -1
  157. data/spec/handlers/examples/attribute_handler_001.rb.txt +5 -0
  158. data/spec/handlers/examples/class_handler_001.rb.txt +4 -0
  159. data/spec/handlers/examples/module_handler_001.rb.txt +6 -1
  160. data/spec/handlers/examples/visibility_handler_001.rb.txt +4 -0
  161. data/spec/handlers/method_handler_spec.rb +5 -0
  162. data/spec/handlers/module_handler_spec.rb +4 -0
  163. data/spec/handlers/visibility_handler_spec.rb +6 -0
  164. data/spec/parser/source_parser_spec.rb +24 -0
  165. data/spec/registry_spec.rb +44 -8
  166. data/spec/server/adapter_spec.rb +38 -0
  167. data/spec/server/commands/base_spec.rb +87 -0
  168. data/spec/server/commands/static_file_command_spec.rb +67 -0
  169. data/spec/server/doc_server_serializer_spec.rb +58 -0
  170. data/spec/server/router_spec.rb +115 -0
  171. data/spec/server/spec_helper.rb +17 -0
  172. data/spec/server/static_caching_spec.rb +39 -0
  173. data/spec/server/webrick_servlet_spec.rb +20 -0
  174. data/spec/templates/constant_spec.rb +40 -0
  175. data/spec/templates/engine_spec.rb +9 -5
  176. data/spec/templates/examples/class002.html +1 -3
  177. data/spec/templates/examples/constant001.txt +25 -0
  178. data/spec/templates/examples/constant002.txt +7 -0
  179. data/spec/templates/examples/constant003.txt +11 -0
  180. data/spec/templates/examples/module001.txt +1 -1
  181. data/spec/templates/examples/module002.html +319 -0
  182. data/spec/templates/helpers/base_helper_spec.rb +2 -2
  183. data/spec/templates/helpers/html_helper_spec.rb +93 -3
  184. data/spec/templates/helpers/html_syntax_highlight_helper_spec.rb +5 -0
  185. data/spec/templates/helpers/markup_helper_spec.rb +94 -67
  186. data/spec/templates/helpers/shared_signature_examples.rb +9 -0
  187. data/spec/templates/helpers/text_helper_spec.rb +12 -0
  188. data/spec/templates/module_spec.rb +21 -4
  189. data/spec/templates/section_spec.rb +146 -0
  190. data/spec/templates/template_spec.rb +9 -20
  191. data/templates/default/class/setup.rb +5 -5
  192. data/templates/default/constant/text/header.erb +11 -0
  193. data/templates/default/constant/text/setup.rb +3 -0
  194. data/templates/default/fulldoc/html/css/style.css +29 -3
  195. data/templates/default/fulldoc/html/js/app.js +67 -1
  196. data/templates/default/fulldoc/html/js/full_list.js +3 -8
  197. data/templates/default/fulldoc/html/js/jquery.js +150 -15
  198. data/templates/default/fulldoc/html/setup.rb +9 -0
  199. data/templates/default/layout/html/footer.erb +1 -1
  200. data/templates/default/layout/html/setup.rb +7 -25
  201. data/templates/default/method_details/html/source.erb +1 -1
  202. data/templates/default/module/html/attribute_summary.erb +2 -2
  203. data/templates/default/module/html/method_summary.erb +2 -2
  204. data/templates/default/module/setup.rb +27 -4
  205. data/templates/default/onefile/html/files.erb +5 -0
  206. data/templates/default/onefile/html/layout.erb +22 -0
  207. data/templates/default/onefile/html/readme.erb +3 -0
  208. data/templates/default/onefile/html/setup.rb +40 -0
  209. data/templates/default/root/html/setup.rb +1 -0
  210. data/templates/default/tags/setup.rb +26 -33
  211. metadata +80 -10
@@ -3,6 +3,7 @@ module YARD
3
3
  module Ruby
4
4
  module Legacy
5
5
  # Legacy Ruby parser
6
+ # @since 0.5.6
6
7
  class RubyParser < Parser::Base
7
8
  def initialize(source, filename)
8
9
  @source = source
@@ -2,7 +2,7 @@ module YARD
2
2
  module Parser::Ruby::Legacy
3
3
  class Statement
4
4
  attr_reader :tokens, :comments, :block
5
- attr_accessor :comments_range
5
+ attr_accessor :comments_range, :group
6
6
 
7
7
  def initialize(tokens, block = nil, comments = nil)
8
8
  @tokens = tokens
@@ -11,23 +11,26 @@ module YARD
11
11
  end
12
12
 
13
13
  def first_line
14
- to_s(false)
14
+ to_s.split(/\n/)[0]
15
15
  end
16
16
 
17
17
  def to_s(include_block = true)
18
18
  tokens.map do |token|
19
- RubyToken::TkBlockContents === token ? block.to_s : token.text
19
+ RubyToken::TkBlockContents === token ? (include_block ? block.to_s : '') : token.text
20
20
  end.join
21
21
  end
22
22
  alias source to_s
23
23
 
24
24
  def inspect
25
25
  l = line - 1
26
- to_s.split(/\n/).map do |text|
26
+ to_s(false).split(/\n/).map do |text|
27
27
  "\t#{l += 1}: #{text}"
28
28
  end.join("\n")
29
29
  end
30
- alias show inspect
30
+
31
+ def show
32
+ "\t #{line}: #{first_line}"
33
+ end
31
34
 
32
35
  # @return [Fixnum] the first line of Ruby source
33
36
  def line
@@ -35,6 +38,7 @@ module YARD
35
38
  end
36
39
 
37
40
  # @return [Range<Fixnum>] the first to last lines of Ruby source
41
+ # @since 0.5.4
38
42
  def line_range
39
43
  tokens.first.line_no..tokens.last.line_no
40
44
  end
@@ -8,11 +8,11 @@ module YARD
8
8
  OPEN_BLOCK_TOKENS = [TkCLASS, TkDEF, TkMODULE, TkUNTIL,
9
9
  TkIF, TkELSIF, TkUNLESS, TkWHILE, TkFOR, TkCASE]
10
10
 
11
- ##
12
11
  # Creates a new statement list
13
12
  #
14
13
  # @param [TokenList, String] content the tokens to create the list from
15
14
  def initialize(content)
15
+ @group = nil
16
16
  if content.is_a? TokenList
17
17
  @tokens = content.dup
18
18
  elsif content.is_a? String
@@ -30,7 +30,6 @@ module YARD
30
30
  while stmt = next_statement do self << stmt end
31
31
  end
32
32
 
33
- ##
34
33
  # Returns the next statement in the token stream
35
34
  #
36
35
  # @return [Statement] the next statement
@@ -62,6 +61,7 @@ module YARD
62
61
  sanitize_block
63
62
  @statement.pop if [TkNL, TkSPACE, TkSEMICOLON].include?(@statement.last.class)
64
63
  stmt = Statement.new(@statement, @block, @comments)
64
+ stmt.group = @group
65
65
  if @comments && @comments_line
66
66
  stmt.comments_range = (@comments_line..(@comments_line + @comments.size - 1))
67
67
  end
@@ -101,8 +101,24 @@ module YARD
101
101
  end
102
102
  end
103
103
  end
104
+
105
+ def preprocess_token(tk)
106
+ if tk.is_a?(TkCOMMENT)
107
+ case tk.text
108
+ when /\A# @group\s+(.+)\s*\Z/
109
+ @group = $1
110
+ true
111
+ when /\A# @endgroup\s*\Z/
112
+ @group = nil
113
+ true
114
+ else
115
+ false
116
+ end
117
+ else
118
+ false
119
+ end
120
+ end
104
121
 
105
- ##
106
122
  # Processes a single token
107
123
  #
108
124
  # @param [RubyToken::Token] tk the token to process
@@ -110,6 +126,7 @@ module YARD
110
126
  # p tk.class, tk.text, @state, @level, @current_block, "<br/>"
111
127
  case @state
112
128
  when :first_statement
129
+ return if preprocess_token(tk)
113
130
  return if process_initial_comment(tk)
114
131
  return if @statement.empty? && [TkSPACE, TkNL, TkCOMMENT].include?(tk.class)
115
132
  @comments_last_line = nil
@@ -152,7 +169,6 @@ module YARD
152
169
  end
153
170
  end
154
171
 
155
- ##
156
172
  # Processes a token in a block
157
173
  #
158
174
  # @param [RubyToken::Token] tk the token to process
@@ -171,7 +187,6 @@ module YARD
171
187
  end
172
188
  end
173
189
 
174
- ##
175
190
  # Processes a comment token that comes before a statement
176
191
  #
177
192
  # @param [RubyToken::Token] tk the token to process
@@ -204,7 +219,6 @@ module YARD
204
219
  true
205
220
  end
206
221
 
207
- ##
208
222
  # Processes a simple block-opening token;
209
223
  # that is, a block opener such as +begin+ or +do+
210
224
  # that isn't followed by an expression
@@ -230,7 +244,6 @@ module YARD
230
244
  true
231
245
  end
232
246
 
233
- ##
234
247
  # Processes a complex block-opening token;
235
248
  # that is, a block opener such as +while+ or +for+
236
249
  # that is followed by an expression
@@ -246,7 +259,6 @@ module YARD
246
259
  true
247
260
  end
248
261
 
249
- ##
250
262
  # Processes a token that closes a statement
251
263
  #
252
264
  # @param [RubyToken::Token] tk the token to process
@@ -300,7 +312,6 @@ module YARD
300
312
  end
301
313
  end
302
314
 
303
- ##
304
315
  # Handles the balancing of parentheses and blocks
305
316
  #
306
317
  # @param [RubyToken::Token] tk the token to process
@@ -318,7 +329,6 @@ module YARD
318
329
  @level == 0
319
330
  end
320
331
 
321
- ##
322
332
  # Adds a token to the current statement,
323
333
  # unless it's a newline, semicolon, or comment
324
334
  #
@@ -328,7 +338,6 @@ module YARD
328
338
  @statement << tk unless @level == 0 && [TkCOMMENT].include?(tk.class)
329
339
  end
330
340
 
331
- ##
332
341
  # Returns the next token in the stream that's not a space
333
342
  #
334
343
  # @return [RubyToken::Token] the next non-space token
@@ -15,6 +15,7 @@ module YARD
15
15
  end
16
16
 
17
17
  # Internal parser class
18
+ # @since 0.5.6
18
19
  class RipperParser < Ripper
19
20
  attr_reader :ast, :charno, :comments, :file, :tokens
20
21
  alias root ast
@@ -30,6 +31,7 @@ module YARD
30
31
  @ns_charno = 0
31
32
  @list = []
32
33
  @charno = 0
34
+ @groups = []
33
35
  end
34
36
 
35
37
  def parse
@@ -346,7 +348,15 @@ module YARD
346
348
 
347
349
  def on_comment(comment)
348
350
  visit_ns_token(:comment, comment)
349
-
351
+ case comment
352
+ when /\A# @group\s+(.+)\s*\Z/
353
+ @groups.unshift [lineno, $1]
354
+ return
355
+ when /\A# @endgroup\s*\Z/
356
+ @groups.unshift [lineno, nil]
357
+ return
358
+ end
359
+
350
360
  comment = comment.gsub(/^\#{1,2}\s{0,1}/, '').chomp
351
361
  append_comment = @comments[lineno - 1]
352
362
 
@@ -391,6 +401,13 @@ module YARD
391
401
  break
392
402
  end
393
403
  end
404
+ if node.type == :def || node.type == :defs || node.call?
405
+ @groups.each do |group|
406
+ if group.first < node.line
407
+ break node.group = group.last
408
+ end
409
+ end
410
+ end
394
411
  end
395
412
  end
396
413
 
@@ -103,6 +103,7 @@ module YARD
103
103
 
104
104
  # @return [Hash{Symbol=>Object}] a list of registered parser types
105
105
  # @private
106
+ # @since 0.5.6
106
107
  attr_reader :parser_types
107
108
  undef parser_types
108
109
  def parser_types; @@parser_types ||= {} end
@@ -110,6 +111,7 @@ module YARD
110
111
 
111
112
  # @return [Hash] a list of registered parser type extensions
112
113
  # @private
114
+ # @since 0.5.6
113
115
  attr_reader :parser_type_extensions
114
116
  undef parser_type_extensions
115
117
  def parser_type_extensions; @@parser_type_extensions ||= {} end
@@ -119,6 +121,7 @@ module YARD
119
121
  # type is found, the default Ruby type is returned.
120
122
  #
121
123
  # @return [Symbol] the parser type to be used for the extension
124
+ # @since 0.5.6
122
125
  def parser_type_for_extension(extension)
123
126
  type = parser_type_extensions.find do |t, exts|
124
127
  [exts].flatten.any? {|ext| ext === extension }
@@ -230,9 +233,10 @@ module YARD
230
233
  private
231
234
 
232
235
  # Searches for encoding line and forces encoding
236
+ # @since 0.5.3
233
237
  def convert_encoding(content)
234
238
  return content if RUBY18
235
- if content =~ /\A\s*#.*coding:\s*(\S+)\s*$/
239
+ if content =~ /\A\s*#.*coding[:=]\s*(\S+)\s*$/
236
240
  content.force_encoding($1)
237
241
  else
238
242
  content
@@ -262,6 +266,7 @@ module YARD
262
266
  parser_type == :ruby18 && type == :ruby ? :ruby18 : type
263
267
  end
264
268
 
269
+ # @since 0.5.6
265
270
  def parser_class
266
271
  klass = self.class.parser_types[parser_type]
267
272
  raise ArgumentError, "invalid parser type '#{parser_type}' or unrecognized file", caller[1..-1] if !klass
data/lib/yard/registry.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'singleton'
2
1
  require 'fileutils'
3
2
  require 'digest/sha1'
4
3
 
@@ -7,34 +6,23 @@ module YARD
7
6
  # during parsing. The storage is a key value store with the object's path
8
7
  # (see {CodeObjects::Base#path}) as the key and the object itself as the value.
9
8
  # Object paths must be unique to be stored in the Registry. All lookups for
10
- # objects are done on the singleton Registry instance using the {Registry#at}
11
- # or {Registry#resolve} methods.
9
+ # objects are done on the singleton Registry instance using the {Registry.at}
10
+ # or {Registry.resolve} methods.
12
11
  #
13
12
  # The registry is saved to a "yardoc" file, which can be loaded back to
14
13
  # perform any lookups.
15
14
  #
16
15
  # This class is a singleton class. Any method called on the class will be
17
16
  # delegated to the instance.
18
- class Registry
17
+ module Registry
19
18
  DEFAULT_YARDOC_FILE = ".yardoc"
20
19
  LOCAL_YARDOC_INDEX = File.expand_path('~/.yard/gem_index')
21
-
22
- include Singleton
23
-
24
- @objects = {}
20
+ @yardoc_file = DEFAULT_YARDOC_FILE
21
+
22
+ extend Enumerable
25
23
 
26
24
  class << self
27
- # Holds the objects cache. This attribute should never be accessed
28
- # directly.
29
- # @return [Array<CodeObjects::Base>] the objects cache
30
- attr_reader :objects
31
-
32
- # Clears the registry and cache
33
- # @return [void]
34
- def clear
35
- instance.clear
36
- objects.clear
37
- end
25
+ # @group Getting .yardoc File Locations
38
26
 
39
27
  # Returns the .yardoc file associated with a gem.
40
28
  #
@@ -51,12 +39,12 @@ module YARD
51
39
  spec = Gem.source_index.find_name(gem, ver_require)
52
40
  return if spec.empty?
53
41
  spec = spec.first
54
-
42
+
55
43
  if gem =~ /^yard-doc-/
56
44
  path = File.join(spec.full_gem_path, DEFAULT_YARDOC_FILE)
57
45
  return File.exist?(path) && !for_writing ? path : nil
58
46
  end
59
-
47
+
60
48
  if for_writing
61
49
  global_yardoc_file(spec, for_writing) ||
62
50
  local_yardoc_file(spec, for_writing)
@@ -65,292 +53,310 @@ module YARD
65
53
  global_yardoc_file(spec, for_writing)
66
54
  end
67
55
  end
68
-
69
- private
70
-
71
- def global_yardoc_file(spec, for_writing = false)
72
- path = spec.full_gem_path
73
- yfile = File.join(path, DEFAULT_YARDOC_FILE)
74
- if for_writing && File.writable?(path)
75
- return yfile
76
- elsif !for_writing && File.exist?(yfile)
77
- return yfile
78
- end
79
- end
80
-
81
- def local_yardoc_file(spec, for_writing = false)
82
- path = Registry::LOCAL_YARDOC_INDEX
83
- FileUtils.mkdir_p(path) if for_writing
84
- path = File.join(path, "#{spec.full_name}.yardoc")
85
- if for_writing
86
- path
56
+
57
+ # Gets/sets the yardoc filename
58
+ # @return [String] the yardoc filename
59
+ # @see DEFAULT_YARDOC_FILE
60
+ attr_accessor :yardoc_file
61
+
62
+ # @group Loading Data from Disk
63
+
64
+ # Loads the registry and/or parses a list of files
65
+ #
66
+ # @example Loads the yardoc file or parses files 'a', 'b' and 'c' (but not both)
67
+ # Registry.load(['a', 'b', 'c'])
68
+ # @example Reparses files 'a' and 'b' regardless of whether yardoc file exists
69
+ # Registry.load(['a', 'b'], true)
70
+ # @param [String, Array] files if +files+ is an Array, it should represent
71
+ # a list of files that YARD should parse into the registry. If reload is
72
+ # set to false and the yardoc file already exists, these files are skipped.
73
+ # If files is a String, it should represent the yardoc file to load
74
+ # into the registry.
75
+ # @param [Boolean] reparse if reparse is false and a yardoc file already
76
+ # exists, any files passed in will be ignored.
77
+ # @return [Registry] the registry object (for chaining)
78
+ # @raise [ArgumentError] if files is not a String or Array
79
+ def load(files = [], reparse = false)
80
+ if files.is_a?(Array)
81
+ if File.exists?(yardoc_file) && !reparse
82
+ load_yardoc
83
+ else
84
+ size = @store.keys.size
85
+ YARD.parse(files)
86
+ save if @store.keys.size > size
87
+ end
88
+ elsif files.is_a?(String)
89
+ load_yardoc(files)
87
90
  else
88
- File.exist?(path) ? path : nil
91
+ raise ArgumentError, "Must take a list of files to parse or the .yardoc file to load."
89
92
  end
93
+ self
90
94
  end
91
- end
92
-
93
- # Gets/sets the yardoc filename
94
- # @return [String] the yardoc filename
95
- # @see DEFAULT_YARDOC_FILE
96
- attr_accessor :yardoc_file
97
95
 
98
- # The assumed types of a list of paths
99
- # @return [{String => Symbol}] a set of unresolved paths and their assumed type
100
- def proxy_types
101
- @store.proxy_types
102
- end
96
+ # Loads a yardoc file directly
97
+ #
98
+ # @param [String] file the yardoc file to load.
99
+ # @return [Registry] the registry object (for chaining)
100
+ def load_yardoc(file = yardoc_file)
101
+ clear
102
+ @store.load(file)
103
+ self
104
+ end
103
105
 
104
- # Loads the registry and/or parses a list of files
105
- #
106
- # @example Loads the yardoc file or parses files 'a', 'b' and 'c' (but not both)
107
- # Registry.load(['a', 'b', 'c'])
108
- # @example Reparses files 'a' and 'b' regardless if yardoc file exists
109
- # Registry.load(['a', 'b'], true)
110
- # @param [String, Array] files if +files+ is an Array, it should represent
111
- # a list of files that YARD should parse into the registry. If reload is
112
- # set to false and the yardoc file already exists, these files are skipped.
113
- # If files is a String, it should represent the yardoc file to load
114
- # into the registry.
115
- # @param [Boolean] reload if reload is false and a yardoc file already
116
- # exists, any files passed in will be ignored.
117
- # @return [Boolean] true if the registry was successfully loaded
118
- # @raise [ArgumentError] if files is not a String or Array
119
- def load(files = [], reload = false)
120
- if files.is_a?(Array)
121
- if File.exists?(yardoc_file) && !reload
122
- load_yardoc
123
- else
124
- size = @store.keys.size
125
- YARD.parse(files)
126
- save if @store.keys.size > size
127
- end
128
- true
129
- elsif files.is_a?(String)
130
- load_yardoc(files)
131
- true
132
- else
133
- raise ArgumentError, "Must take a list of files to parse or the .yardoc file to load."
106
+ # Loads a yardoc file and forces all objects cached on disk into
107
+ # memory. Equivalent to calling {load_yardoc} followed by {load_all}
108
+ #
109
+ # @param [String] file the yardoc file to load
110
+ # @return [Registry] the registry object (for chaining)
111
+ # @see #load_yardoc
112
+ # @see #load_all
113
+ # @since 0.5.1
114
+ def load!(file = yardoc_file)
115
+ clear
116
+ @store.load!(file)
117
+ self
134
118
  end
135
- end
136
119
 
137
- # Loads a yardoc file directly
138
- #
139
- # @param [String] file the yardoc file to load.
140
- # @return [void]
141
- def load_yardoc(file = yardoc_file)
142
- clear
143
- @store.load(file)
144
- end
120
+ # Forces all objects cached on disk into memory
121
+ #
122
+ # @example Loads all objects from disk
123
+ # Registry.load
124
+ # Registry.all.count #=> 0
125
+ # Registry.load_all
126
+ # Registry.all.count #=> 17
127
+ # @return [Registry] the registry object (for chaining)
128
+ # @since 0.5.1
129
+ def load_all
130
+ @store.load_all
131
+ self
132
+ end
145
133
 
146
- # Loads a yardoc file and forces all objects cached on disk into
147
- # memory. Equivalent to calling {#load_yardoc} followed by {#load_all}
148
- #
149
- # @param [String] file the yardoc file to load
150
- # @return [void]
151
- # @see #load_yardoc
152
- # @see #load_all
153
- def load!(file = yardoc_file)
154
- clear
155
- @store.load!(file)
156
- end
134
+ # @group Saving and Deleting Data from Disk
135
+
136
+ # Saves the registry to +file+
137
+ #
138
+ # @param [String] file the yardoc file to save to
139
+ # @return [Boolean] true if the file was saved
140
+ def save(merge = false, file = yardoc_file)
141
+ @store.save(merge, file)
142
+ end
157
143
 
158
- # Forces all objects cached on disk into memory
159
- #
160
- # @example Loads all objects from disk
161
- # Registry.load
162
- # Registry.all.count #=> 0
163
- # Registry.load_all
164
- # Registry.all.count #=> 17
165
- # @return [void]
166
- def load_all
167
- @store.load_all
168
- end
144
+ # Deletes the yardoc file from disk
145
+ # @return [void]
146
+ def delete_from_disk
147
+ @store.destroy
148
+ end
149
+
150
+ # @group Adding and Deleting Objects from the Registry
169
151
 
170
- # Saves the registry to +file+
171
- #
172
- # @param [String] file the yardoc file to save to
173
- # @return [Boolean] true if the file was saved
174
- def save(merge = false, file = yardoc_file)
175
- @store.save(merge, file)
176
- end
152
+ # Registers a new object with the registry
153
+ #
154
+ # @param [CodeObjects::Base] object the object to register
155
+ # @return [CodeObjects::Base] the registered object
156
+ def register(object)
157
+ return if object.is_a?(CodeObjects::Proxy)
158
+ @store[object.path] = object
159
+ end
177
160
 
178
- # @return [Hash{String => String}] a set of checksums for files
179
- def checksums
180
- @store.checksums
181
- end
161
+ # Deletes an object from the registry
162
+ # @param [CodeObjects::Base] object the object to remove
163
+ # @return [void]
164
+ def delete(object)
165
+ @store.delete(object.path)
166
+ end
167
+
168
+ # Clears the registry
169
+ # @return [void]
170
+ def clear
171
+ @store = RegistryStore.new
172
+ end
173
+
174
+ # @group Accessing Objects in the Registry
175
+
176
+ # Iterates over {all} with no arguments
177
+ def each(&block)
178
+ all.each(&block)
179
+ end
182
180
 
183
- # @param [String] data data to checksum
184
- # @return [String] the SHA1 checksum for data
185
- def checksum_for(data)
186
- Digest::SHA1.hexdigest(data)
187
- end
181
+ # Returns all objects in the registry that match one of the types provided
182
+ # in the +types+ list (if +types+ is provided).
183
+ #
184
+ # @example Returns all objects
185
+ # Registry.all
186
+ # @example Returns all classes and modules
187
+ # Registry.all(:class, :module)
188
+ # @param [Array<Symbol>] types an optional list of types to narrow the
189
+ # objects down by. Equivalent to performing a select:
190
+ # +Registry.all.select {|o| types.include(o.type) }+
191
+ # @return [Array<CodeObjects::Base>] the list of objects found
192
+ # @see CodeObjects::Base#type
193
+ def all(*types)
194
+ @store.values.select do |obj|
195
+ if types.empty?
196
+ obj != root
197
+ else
198
+ obj != root &&
199
+ types.any? do |type|
200
+ type.is_a?(Symbol) ? obj.type == type : obj.is_a?(type)
201
+ end
202
+ end
203
+ end + (types.include?(:root) ? [root] : [])
204
+ end
188
205
 
189
- # Deletes the yardoc file from disk
190
- # @return [void]
191
- def delete_from_disk
192
- @store.destroy
193
- end
206
+ # Returns the paths of all of the objects in the registry.
207
+ # @param [Boolean] reload whether to load entire database
208
+ # @return [Array<String>] all of the paths in the registry.
209
+ def paths(reload = false)
210
+ @store.keys(reload).map {|k| k.to_s }
211
+ end
212
+
213
+ # Returns the object at a specific path.
214
+ # @param [String, :root] path the pathname to look for. If +path+ is +root+,
215
+ # returns the {root} object.
216
+ # @return [CodeObjects::Base] the object at path
217
+ # @return [nil] if no object is found
218
+ def at(path) @store[path] end
219
+ alias_method :[], :at
220
+
221
+ # The root namespace object.
222
+ # @return [CodeObjects::RootObject] the root object in the namespace
223
+ def root; @store[:root] end
224
+
225
+ # Attempts to find an object by name starting at +namespace+, performing
226
+ # a lookup similar to Ruby's method of resolving a constant in a namespace.
227
+ #
228
+ # @example Looks for instance method #reverse starting from A::B::C
229
+ # Registry.resolve(P("A::B::C"), "#reverse")
230
+ # @example Looks for a constant in the root namespace
231
+ # Registry.resolve(nil, 'CONSTANT')
232
+ # @example Looks for a class method respecting the inheritance tree
233
+ # Registry.resolve(myclass, 'mymethod', true)
234
+ # @example Looks for a constant but returns a proxy if not found
235
+ # Registry.resolve(P('A::B::C'), 'D', false, true) # => #<yardoc proxy A::B::C::D>
236
+ # @example Looks for a complex path from a namespace
237
+ # Registry.resolve(P('A::B'), 'B::D') # => #<yardoc class A::B::D>
238
+ # @param [CodeObjects::NamespaceObject, nil] namespace the starting namespace
239
+ # (module or class). If +nil+ or +:root+, starts from the {root} object.
240
+ # @param [String, Symbol] name the name (or complex path) to look for from
241
+ # +namespace+.
242
+ # @param [Boolean] inheritance Follows inheritance chain (mixins, superclass)
243
+ # when performing name resolution if set to +true+.
244
+ # @param [Boolean] proxy_fallback If +true+, returns a proxy representing
245
+ # the unresolved path (namespace + name) if no object is found.
246
+ # @return [CodeObjects::Base] the object if it is found
247
+ # @return [CodeObjects::Proxy] a Proxy representing the object if
248
+ # +proxy_fallback+ is +true+.
249
+ # @return [nil] if +proxy_fallback+ is +false+ and no object was found.
250
+ # @see P
251
+ def resolve(namespace, name, inheritance = false, proxy_fallback = false)
252
+ if namespace.is_a?(CodeObjects::Proxy)
253
+ return proxy_fallback ? CodeObjects::Proxy.new(namespace, name) : nil
254
+ end
255
+
256
+ if namespace == :root || !namespace
257
+ namespace = root
258
+ else
259
+ namespace = namespace.parent until namespace.is_a?(CodeObjects::NamespaceObject)
260
+ end
261
+ orignamespace = namespace
194
262
 
195
- # Returns all objects in the registry that match one of the types provided
196
- # in the +types+ list (if +types+ is provided).
197
- #
198
- # @example Returns all objects
199
- # Registry.all
200
- # @example Returns all classes and modules
201
- # Registry.all(:class, :module)
202
- # @param [Array<Symbol>] types an optional list of types to narrow the
203
- # objects down by. Equivalent to performing a select:
204
- # +Registry.all.select {|o| types.include(o.type) }+
205
- # @return [Array<CodeObjects::Base>] the list of objects found
206
- # @see CodeObjects::Base#type
207
- def all(*types)
208
- @store.values.select do |obj|
209
- if types.empty?
210
- obj != root
263
+ name = name.to_s
264
+ if name =~ /^#{CodeObjects::NSEPQ}/
265
+ [name, name[2..-1]].each do |n|
266
+ return at(n) if at(n)
267
+ end
211
268
  else
212
- obj != root &&
213
- types.any? do |type|
214
- type.is_a?(Symbol) ? obj.type == type : obj.is_a?(type)
269
+ while namespace
270
+ if namespace.is_a?(CodeObjects::NamespaceObject)
271
+ nss = inheritance ? namespace.inheritance_tree(true) : [namespace]
272
+ nss.each do |ns|
273
+ next if ns.is_a?(CodeObjects::Proxy)
274
+ found = partial_resolve(ns, name)
275
+ return found if found
276
+ end
215
277
  end
278
+ namespace = namespace.parent
279
+ end
216
280
  end
217
- end + (types.include?(:root) ? [root] : [])
218
- end
219
-
220
- # Returns the paths of all of the objects in the registry.
221
- # @param [Boolean] reload whether to load entire database
222
- # @return [Array<String>] all of the paths in the registry.
223
- def paths(reload = false)
224
- @store.keys(reload).map {|k| k.to_s }
225
- end
281
+ proxy_fallback ? CodeObjects::Proxy.new(orignamespace, name) : nil
282
+ end
283
+
284
+ # @group Managing Source File Checksums
226
285
 
227
- # Returns the object at a specific path.
228
- # @param [String, :root] path the pathname to look for. If +path+ is +root+,
229
- # returns the {#root} object.
230
- # @return [CodeObjects::Base] the object at path
231
- # @return [nil] if no object is found
232
- def at(path) @store[path] end
233
- alias_method :[], :at
286
+ # @return [Hash{String => String}] a set of checksums for files
287
+ def checksums
288
+ @store.checksums
289
+ end
234
290
 
235
- # The root namespace object.
236
- # @return [CodeObjects::RootObject] the root object in the namespace
237
- def root; @store[:root] end
291
+ # @param [String] data data to checksum
292
+ # @return [String] the SHA1 checksum for data
293
+ def checksum_for(data)
294
+ Digest::SHA1.hexdigest(data)
295
+ end
238
296
 
239
- # Deletes an object from the registry
240
- # @param [CodeObjects::Base] object the object to remove
241
- # @return [void]
242
- def delete(object)
243
- @store.delete(object.path)
244
- self.class.objects.delete(object.path)
245
- end
246
-
247
- # Clears the registry
248
- # @return [void]
249
- def clear
250
- @store = RegistryStore.new
251
- end
252
-
253
- # Creates the Registry
254
- # @return [Registry]
255
- def initialize
256
- @yardoc_file = DEFAULT_YARDOC_FILE
257
- clear
258
- end
259
-
260
- # Registers a new object with the registry
261
- #
262
- # @param [CodeObjects::Base] object the object to register
263
- # @return [CodeObjects::Base] the registered object
264
- def register(object)
265
- self.class.objects[object.path] = object
266
- return if object.is_a?(CodeObjects::Proxy)
267
- @store[object.path] = object
268
- end
297
+ # @group Managing Internal State (Testing Only)
269
298
 
270
- # Attempts to find an object by name starting at +namespace+, performing
271
- # a lookup similar to Ruby's method of resolving a constant in a namespace.
272
- #
273
- # @example Looks for instance method #reverse starting from A::B::C
274
- # Registry.resolve(P("A::B::C"), "#reverse")
275
- # @example Looks for a constant in the root namespace
276
- # Registry.resolve(nil, 'CONSTANT')
277
- # @example Looks for a class method respecting the inheritance tree
278
- # Registry.resolve(myclass, 'mymethod', true)
279
- # @example Looks for a constant but returns a proxy if not found
280
- # Registry.resolve(P('A::B::C'), 'D', false, true) # => #<yardoc proxy A::B::C::D>
281
- # @example Looks for a complex path from a namespace
282
- # Registry.resolve(P('A::B'), 'B::D') # => #<yardoc class A::B::D>
283
- # @param [CodeObjects::NamespaceObject, nil] namespace the starting namespace
284
- # (module or class). If +nil+ or +:root+, starts from the {#root} object.
285
- # @param [String, Symbol] name the name (or complex path) to look for from
286
- # +namespace+.
287
- # @param [Boolean] inheritance Follows inheritance chain (mixins, superclass)
288
- # when performing name resolution if set to +true+.
289
- # @param [Boolean] proxy_fallback If +true+, returns a proxy representing
290
- # the unresolved path (namespace + name) if no object is found.
291
- # @return [CodeObjects::Base] the object if it is found
292
- # @return [CodeObjects::Proxy] a Proxy representing the object if
293
- # +proxy_fallback+ is +true+.
294
- # @return [nil] if +proxy_fallback+ is +false+ and no object was found.
295
- # @see P
296
- def resolve(namespace, name, inheritance = false, proxy_fallback = false)
297
- if namespace.is_a?(CodeObjects::Proxy)
298
- return proxy_fallback ? CodeObjects::Proxy.new(namespace, name) : nil
299
+ # The assumed types of a list of paths. This method is used by CodeObjects::Base
300
+ # @return [{String => Symbol}] a set of unresolved paths and their assumed type
301
+ # @private
302
+ def proxy_types
303
+ @store.proxy_types
299
304
  end
300
305
 
301
- if namespace == :root || !namespace
302
- namespace = root
303
- else
304
- namespace = namespace.parent until namespace.is_a?(CodeObjects::NamespaceObject)
305
- end
306
- orignamespace = namespace
306
+ # @group Legacy Methods
307
+
308
+ # The registry singleton instance.
309
+ #
310
+ # @deprecated use Registry.methodname directly.
311
+ # @return [Registry] returns the registry instance
312
+ def instance; self end
313
+
314
+ private
315
+
316
+ # @group Accessing Objects in the Registry
307
317
 
308
- name = name.to_s
309
- if name =~ /^#{CodeObjects::NSEPQ}/
310
- [name, name[2..-1]].each do |n|
311
- return at(n) if at(n)
312
- end
313
- else
314
- while namespace
315
- if namespace.is_a?(CodeObjects::NamespaceObject)
316
- nss = inheritance ? namespace.inheritance_tree(true) : [namespace]
317
- nss.each do |ns|
318
- next if ns.is_a?(CodeObjects::Proxy)
319
- found = partial_resolve(ns, name)
320
- return found if found
321
- end
318
+ # Attempts to resolve a name in a namespace
319
+ #
320
+ # @param [CodeObjects::NamespaceObject] namespace the starting namespace
321
+ # @param [String] name the name to look for
322
+ def partial_resolve(namespace, name)
323
+ return at(name) || at('#' + name) if namespace.root?
324
+ [CodeObjects::NSEP, CodeObjects::CSEP, ''].each do |s|
325
+ next if s.empty? && name =~ /^\w/
326
+ path = name
327
+ if namespace != root
328
+ path = [namespace.path, name].join(s)
322
329
  end
323
- namespace = namespace.parent
330
+ found = at(path)
331
+ return found if found
324
332
  end
333
+ nil
325
334
  end
326
- proxy_fallback ? CodeObjects::Proxy.new(orignamespace, name) : nil
327
- end
328
335
 
329
- # Define all instance methods as singleton methods on instance
330
- (public_instance_methods(false) - public_methods(false)).each do |meth|
331
- module_eval(<<-eof, __FILE__, __LINE__ + 1)
332
- def self.#{meth}(*args, &block) instance.send(:#{meth}, *args, &block) end
333
- eof
334
- end
335
-
336
- private
337
-
338
- # Attempts to resolve a name in a namespace
339
- #
340
- # @param [CodeObjects::NamespaceObject] namespace the starting namespace
341
- # @param [String] name the name to look for
342
- def partial_resolve(namespace, name)
343
- return at(name) || at('#' + name) if namespace.root?
344
- [CodeObjects::NSEP, CodeObjects::CSEP, ''].each do |s|
345
- next if s.empty? && name =~ /^\w/
346
- path = name
347
- if namespace != root
348
- path = [namespace.path, name].join(s)
336
+ # @group Retrieving yardoc File Locations
337
+
338
+ def global_yardoc_file(spec, for_writing = false)
339
+ path = spec.full_gem_path
340
+ yfile = File.join(path, DEFAULT_YARDOC_FILE)
341
+ if for_writing && File.writable?(path)
342
+ return yfile
343
+ elsif !for_writing && File.exist?(yfile)
344
+ return yfile
345
+ end
346
+ end
347
+
348
+ def local_yardoc_file(spec, for_writing = false)
349
+ path = Registry::LOCAL_YARDOC_INDEX
350
+ FileUtils.mkdir_p(path) if for_writing
351
+ path = File.join(path, "#{spec.full_name}.yardoc")
352
+ if for_writing
353
+ path
354
+ else
355
+ File.exist?(path) ? path : nil
349
356
  end
350
- found = at(path)
351
- return found if found
352
357
  end
353
- nil
354
358
  end
359
+
360
+ clear
355
361
  end
356
- end
362
+ end