yard 0.9.0 → 0.9.1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/Rakefile +1 -1
  4. data/docs/Tags.md +10 -9
  5. data/lib/rubygems_plugin.rb +7 -3
  6. data/lib/yard.rb +5 -6
  7. data/lib/yard/autoload.rb +12 -9
  8. data/lib/yard/cli/stats.rb +11 -1
  9. data/lib/yard/cli/yri.rb +2 -2
  10. data/lib/yard/code_objects/base.rb +12 -2
  11. data/lib/yard/code_objects/class_object.rb +2 -0
  12. data/lib/yard/code_objects/class_variable_object.rb +2 -0
  13. data/lib/yard/code_objects/constant_object.rb +2 -0
  14. data/lib/yard/code_objects/method_object.rb +3 -0
  15. data/lib/yard/code_objects/module_object.rb +2 -0
  16. data/lib/yard/code_objects/namespace_mapper.rb +113 -0
  17. data/lib/yard/code_objects/namespace_object.rb +3 -0
  18. data/lib/yard/docstring.rb +1 -1
  19. data/lib/yard/docstring_parser.rb +28 -1
  20. data/lib/yard/handlers/c/handler_methods.rb +1 -0
  21. data/lib/yard/handlers/c/mixin_handler.rb +7 -1
  22. data/lib/yard/handlers/ruby/alias_handler.rb +4 -3
  23. data/lib/yard/handlers/ruby/constant_handler.rb +6 -1
  24. data/lib/yard/handlers/ruby/dsl_handler_methods.rb +20 -3
  25. data/lib/yard/parser/c/comment_parser.rb +1 -1
  26. data/lib/yard/parser/ruby/ruby_parser.rb +25 -5
  27. data/lib/yard/parser/source_parser.rb +1 -0
  28. data/lib/yard/registry.rb +22 -43
  29. data/lib/yard/registry_resolver.rb +171 -0
  30. data/lib/yard/rubygems/hook.rb +164 -0
  31. data/lib/yard/server.rb +2 -1
  32. data/lib/yard/server/commands/root_request_command.rb +27 -0
  33. data/lib/yard/server/commands/static_file_command.rb +3 -16
  34. data/lib/yard/server/doc_server_helper.rb +1 -1
  35. data/lib/yard/server/router.rb +16 -6
  36. data/lib/yard/tags/default_factory.rb +3 -1
  37. data/lib/yard/tags/directives.rb +4 -0
  38. data/lib/yard/templates/engine.rb +1 -1
  39. data/lib/yard/templates/helpers/html_helper.rb +7 -2
  40. data/lib/yard/templates/helpers/module_helper.rb +1 -0
  41. data/lib/yard/templates/helpers/text_helper.rb +12 -0
  42. data/lib/yard/templates/template_options.rb +1 -1
  43. data/lib/yard/version.rb +1 -1
  44. data/spec/cli/stats_spec.rb +8 -3
  45. data/spec/code_objects/base_spec.rb +9 -1
  46. data/spec/docstring_parser_spec.rb +36 -1
  47. data/spec/docstring_spec.rb +1 -6
  48. data/spec/handlers/c/mixin_handler_spec.rb +16 -0
  49. data/spec/handlers/constant_handler_spec.rb +9 -0
  50. data/spec/handlers/dsl_handler_spec.rb +18 -0
  51. data/spec/handlers/examples/dsl_handler_001.rb.txt +29 -0
  52. data/spec/parser/ruby/ruby_parser_spec.rb +42 -0
  53. data/spec/registry_spec.rb +46 -3
  54. data/spec/server/router_spec.rb +1 -1
  55. data/spec/server_spec.rb +9 -0
  56. data/spec/tags/default_factory_spec.rb +5 -0
  57. data/spec/templates/engine_spec.rb +10 -0
  58. data/spec/templates/examples/constant001.txt +2 -2
  59. data/spec/templates/helpers/html_helper_spec.rb +8 -1
  60. data/spec/templates/helpers/module_helper_spec.rb +35 -0
  61. data/spec/templates/helpers/text_helper_spec.rb +20 -0
  62. data/templates/default/module/setup.rb +1 -1
  63. metadata +7 -4
  64. data/spec/server/commands/static_file_command_spec.rb +0 -84
@@ -0,0 +1,164 @@
1
+ require 'rubygems'
2
+ require 'rubygems/user_interaction'
3
+ require 'fileutils'
4
+
5
+ ##
6
+ # Gem::YARDoc provides methods to generate YARDoc and yri data for installed gems
7
+ # upon gem installation.
8
+ #
9
+ # This file is automatically required by RubyGems 1.9 and newer.
10
+
11
+ module YARD
12
+ class RubygemsHook
13
+
14
+ include Gem::UserInteraction
15
+ extend Gem::UserInteraction
16
+
17
+ @yard_version = nil
18
+
19
+ ##
20
+ # Force installation of documentation?
21
+
22
+ attr_accessor :force
23
+
24
+ ##
25
+ # Generate yard?
26
+
27
+ attr_accessor :generate_yard
28
+
29
+ ##
30
+ # Generate yri data?
31
+
32
+ attr_accessor :generate_yri
33
+
34
+ class << self
35
+
36
+ ##
37
+ # Loaded version of YARD. Set by ::load_yard
38
+
39
+ attr_reader :yard_version
40
+
41
+ end
42
+
43
+ ##
44
+ # Post installs hook that generates documentation for each specification in
45
+ # +specs+
46
+
47
+ def self.generation_hook(installer, specs)
48
+ start = Time.now
49
+ types = installer.document
50
+
51
+ generate_yard = types.include?('yardoc') || types.include?('yard')
52
+ generate_yri = types.include? 'yri'
53
+
54
+ specs.each do |spec|
55
+ new(spec, generate_yard, generate_yri).generate
56
+ end
57
+
58
+ return unless generate_yard or generate_yri
59
+
60
+ duration = (Time.now - start).to_i
61
+ names = specs.map(&:name).join ', '
62
+
63
+ say "Done installing documentation for #{names} after #{duration} seconds"
64
+ end
65
+
66
+ ##
67
+ # Loads the YARD generator
68
+
69
+ def self.load_yard
70
+ return if @yard_version
71
+
72
+ require 'yard'
73
+
74
+ @yard_version = Gem::Version.new ::YARD::VERSION
75
+ end
76
+
77
+ def initialize(spec, generate_yard = false, generate_yri = true)
78
+ @doc_dir = spec.doc_dir
79
+ @force = false
80
+ @spec = spec
81
+
82
+ @generate_yard = generate_yard
83
+ @generate_yri = generate_yri
84
+
85
+ @yard_dir = spec.doc_dir('yard')
86
+ @yri_dir = spec.doc_dir('.yardoc')
87
+ end
88
+
89
+ def run_yardoc(*args)
90
+ args << '--quiet' unless Gem.configuration.really_verbose
91
+ args << '--backtrace' if Gem.configuration.backtrace
92
+ unless File.file?(File.join(@spec.full_gem_path, '.yardopts'))
93
+ args << @spec.require_paths
94
+ if @spec.extra_rdoc_files.size > 0
95
+ args << '-'
96
+ args += @spec.extra_rdoc_files
97
+ end
98
+ end
99
+ args = args.flatten.map {|arg| arg.to_s }
100
+
101
+ Dir.chdir(@spec.full_gem_path) do
102
+ YARD::CLI::Yardoc.run(*args)
103
+ end
104
+ rescue Errno::EACCES => e
105
+ dirname = File.dirname e.message.split("-")[1].strip
106
+ raise Gem::FilePermissionError.new(dirname)
107
+ rescue => ex
108
+ alert_error "While generating documentation for #{@spec.full_name}"
109
+ ui.errs.puts "... MESSAGE: #{ex}"
110
+ ui.errs.puts "... YARDOC args: #{args.join(' ')}"
111
+ ui.errs.puts "\t#{ex.backtrace.join("\n\t")}" if Gem.configuration.backtrace
112
+ ui.errs.puts "(continuing with the rest of the installation)"
113
+ end
114
+
115
+ def install_yard
116
+ FileUtils.rm_rf @yard_dir
117
+
118
+ say "Installing YARD documentation for #{@spec.full_name}..."
119
+ run_yardoc '-o', @yard_dir
120
+ end
121
+
122
+ def install_yri
123
+ FileUtils.rm_rf @yri_dir
124
+
125
+ say "Building YARD (yri) index for #{@spec.full_name}..."
126
+ run_yardoc '-c', '-n', '--db', @yri_dir
127
+ end
128
+
129
+ ##
130
+ # Generates YARD and yri data
131
+
132
+ def generate
133
+ return if @spec.default_gem?
134
+ return unless @generate_yri || @generate_yard
135
+
136
+ setup
137
+
138
+ if @generate_yri && (@force || !File.exist?(@yri_dir))
139
+ install_yri
140
+ end
141
+
142
+ if @generate_yard && (@force || !File.exist?(@yard_dir))
143
+ install_yard
144
+ end
145
+ end
146
+
147
+ ##
148
+ # Prepares the spec for documentation generation
149
+
150
+ def setup
151
+ self.class.load_yard
152
+
153
+ if File.exist?(@doc_dir)
154
+ unless File.writable?(@doc_dir)
155
+ raise Gem::FilePermissionError, @doc_dir
156
+ end
157
+ else
158
+ FileUtils.mkdir_p @doc_dir
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ Gem.done_installing(&YARD::RubygemsHook.method(:generation_hook))
data/lib/yard/server.rb CHANGED
@@ -5,7 +5,8 @@ module YARD
5
5
  # @return [void]
6
6
  # @since 0.6.2
7
7
  def self.register_static_path(path)
8
- Commands::StaticFileCommand::STATIC_PATHS.push(path)
8
+ static_paths = Commands::StaticFileCommand::STATIC_PATHS
9
+ static_paths.push(path) unless static_paths.include?(path)
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,27 @@
1
+ module YARD
2
+ module Server
3
+ module Commands
4
+ # Serves requests from the root of the server
5
+ class RootRequestCommand < Base
6
+ def run
7
+ favicon?
8
+
9
+ self.body = "Could not find: #{request.path}"
10
+ self.status = 404
11
+ end
12
+
13
+ private
14
+
15
+ # Return an empty favicon.ico if it does not exist so that
16
+ # browsers don't complain.
17
+ def favicon?
18
+ return unless request.path == '/favicon.ico'
19
+ self.headers['Content-Type'] = 'image/png'
20
+ self.status = 200
21
+ self.body = ''
22
+ raise FinishRequest
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -4,7 +4,7 @@ module YARD
4
4
  module Server
5
5
  module Commands
6
6
  # Serves static content when no other router matches a request
7
- class StaticFileCommand < Base
7
+ class StaticFileCommand < LibraryCommand
8
8
  include WEBrick::HTTPUtils
9
9
 
10
10
  DefaultMimeTypes['js'] = 'text/javascript'
@@ -17,7 +17,6 @@ module YARD
17
17
 
18
18
  def run
19
19
  assets_template = Templates::Engine.template(:default, :fulldoc, :html)
20
- path = File.cleanpath(request.path).gsub(%r{^(../)+}, '')
21
20
 
22
21
  file = nil
23
22
  ([adapter.document_root] + STATIC_PATHS.reverse).compact.each do |path_prefix|
@@ -30,27 +29,15 @@ module YARD
30
29
  file ||= assets_template.find_file(path)
31
30
 
32
31
  if file
33
- ext = "." + (request.path[/\.(\w+)$/, 1] || "html")
32
+ ext = "." + (path[/\.(\w+)$/, 1] || "html")
34
33
  headers['Content-Type'] = mime_type(ext, DefaultMimeTypes)
35
34
  self.body = File.read(file)
36
35
  return
37
36
  end
38
37
 
39
- favicon?
38
+ self.body = "Could not find: #{request.path}"
40
39
  self.status = 404
41
40
  end
42
-
43
- private
44
-
45
- # Return an empty favicon.ico if it does not exist so that
46
- # browsers don't complain.
47
- def favicon?
48
- return unless request.path == '/favicon.ico'
49
- self.headers['Content-Type'] = 'image/png'
50
- self.status = 200
51
- self.body = ''
52
- raise FinishRequest
53
- end
54
41
  end
55
42
  end
56
43
  end
@@ -10,7 +10,7 @@ module YARD
10
10
  def url_for(obj, anchor = nil, relative = false)
11
11
  return '' if obj.nil?
12
12
  return url_for_index if obj == '_index.html'
13
- return "/#{obj}" if String === obj
13
+ return "/#{base_path(router.static_prefix)}/#{obj}" if String === obj
14
14
  url = super(obj, anchor, false)
15
15
  return unless url
16
16
  File.join('', base_path(router.docs_prefix), url)
@@ -8,8 +8,8 @@ module YARD
8
8
  # options through {Adapter#initialize} or by directly modifying {Adapter#router}.
9
9
  #
10
10
  # The most general customization is to change the URL prefixes recognized by
11
- # routing, which can be done by overriding {#docs_prefix}, {#list_prefix}
12
- # and {#search_prefix}.
11
+ # routing, which can be done by overriding {#docs_prefix}, {#list_prefix},
12
+ # {#static_prefix}, and {#search_prefix}.
13
13
  #
14
14
  # == Implementing Custom Caching
15
15
  # By default, the Router class performs static disk-based caching on all
@@ -22,6 +22,7 @@ module YARD
22
22
  # class MyRouter < YARD::Server::Router
23
23
  # def docs_prefix; 'mydocs' end
24
24
  # def list_prefix; 'mylist' end
25
+ # def static_prefix; 'mystatic' end
25
26
  # def search_prefix; 'mysearch' end
26
27
  # end
27
28
  #
@@ -45,7 +46,7 @@ module YARD
45
46
  end
46
47
 
47
48
  # Perform routing on a specific request, serving the request as a static
48
- # file through {Commands::StaticFileCommand} if no route is found.
49
+ # file through {Commands::RootRequestCommand} if no route is found.
49
50
  #
50
51
  # @param [Adapter Dependent] request the request object
51
52
  # @return [Array(Numeric,Hash,Array)] the Rack-style server response data
@@ -54,7 +55,7 @@ module YARD
54
55
  if result = (check_static_cache || route)
55
56
  result
56
57
  else
57
- StaticFileCommand.new(adapter.options).call(request)
58
+ RootRequestCommand.new(adapter.options).call(request)
58
59
  end
59
60
  end
60
61
 
@@ -69,6 +70,9 @@ module YARD
69
70
  # @return [String] the URI prefix for all search requests
70
71
  def search_prefix; 'search' end
71
72
 
73
+ # @return [String] the URI prefix for all static assets (templates)
74
+ def static_prefix; 'static' end
75
+
72
76
  # @group Routing Methods
73
77
 
74
78
  # @return [Array(LibraryVersion, Array<String>)] the library followed
@@ -101,7 +105,7 @@ module YARD
101
105
  path = path.gsub(%r{//+}, '/').gsub(%r{^/|/$}, '')
102
106
  return route_index if path.empty? || path == docs_prefix
103
107
  case path
104
- when /^(#{docs_prefix}|#{list_prefix}|#{search_prefix})(\/.*|$)/
108
+ when /^(#{docs_prefix}|#{list_prefix}|#{search_prefix}|#{static_prefix})(\/.*|$)/
105
109
  prefix = $1
106
110
  paths = $2.gsub(%r{^/|/$}, '').split('/')
107
111
  library, paths = *parse_library_from_path(paths)
@@ -110,6 +114,7 @@ module YARD
110
114
  when docs_prefix; route_docs(library, paths)
111
115
  when list_prefix; route_list(library, paths)
112
116
  when search_prefix; route_search(library, paths)
117
+ when static_prefix; route_static(library, paths)
113
118
  end
114
119
  end
115
120
  nil
@@ -161,6 +166,10 @@ module YARD
161
166
  SearchCommand.new(final_options(library, paths)).call(request)
162
167
  end
163
168
 
169
+ def route_static(library, paths)
170
+ StaticFileCommand.new(final_options(library, paths)).call(request)
171
+ end
172
+
164
173
  # @group Utility Methods
165
174
 
166
175
  # Adds extra :library/:path option keys to the adapter options.
@@ -169,7 +178,8 @@ module YARD
169
178
  # @param (see #route_docs)
170
179
  # @return [Hash] finalized options
171
180
  def final_options(library, paths)
172
- adapter.options.merge(:library => library, :path => paths.join('/'))
181
+ path = File.cleanpath(paths.join('/')).gsub(%r{^(\.\./)+}, '')
182
+ adapter.options.merge(:library => library, :path => path)
173
183
  end
174
184
  end
175
185
  end
@@ -162,10 +162,12 @@ module YARD
162
162
  elsif c =~ /\S/ && level == 0
163
163
  break e = i if seen_space && list == ['']
164
164
  before << c
165
- elsif c =~ /\s/ && level == 0 && !before.empty?
165
+ elsif c =~ /[ \t]/ && level == 0 && !before.empty?
166
166
  seen_space = true
167
167
  elsif level >= 1
168
168
  list.last << c
169
+ elsif level == 0 && c == "\n"
170
+ break e = i
169
171
  end
170
172
  last_seen = c
171
173
  i += 1
@@ -408,6 +408,10 @@ module YARD
408
408
  obj.docstring = Docstring.new!(parser.text, parser.tags, obj,
409
409
  parser.raw_text, parser.reference)
410
410
  handler.register_module_function(obj)
411
+ old_obj = parser.object
412
+ parser.object = obj
413
+ parser.post_process
414
+ parser.object = old_obj
411
415
  obj
412
416
  end
413
417
  end
@@ -18,7 +18,7 @@ module YARD
18
18
  # @param [String] path a new template path
19
19
  # @return [void]
20
20
  def register_template_path(path)
21
- template_paths.push path
21
+ template_paths.push(path) unless template_paths.include?(path)
22
22
  end
23
23
 
24
24
  # Creates a template module representing the path. Searches on disk
@@ -242,12 +242,17 @@ module YARD
242
242
  file = CodeObjects::ExtraFileObject.new(file)
243
243
  end
244
244
  file.attributes[:markup] ||= markup_for_file('', file.filename)
245
- htmlify(file.contents, file.attributes[:markup] || options.markup)
245
+ insert_include(file.contents, file.attributes[:markup] || options.markup)
246
246
  end
247
247
 
248
248
  # (see BaseHelper#link_include_object)
249
249
  def link_include_object(obj)
250
- htmlify(obj.docstring)
250
+ insert_include(obj.docstring)
251
+ end
252
+
253
+ # Inserts an include link while respecting inlining
254
+ def insert_include(text, markup = options.markup)
255
+ htmlify(text, markup).gsub(/\A\s*<p>|<\/p>\s*\Z/, '')
251
256
  end
252
257
 
253
258
  # (see BaseHelper#link_object)
@@ -9,6 +9,7 @@ module YARD
9
9
  # @return [Array<CodeObjects::Base>] a pruned list of methods
10
10
  def prune_method_listing(list, hide_attributes = true)
11
11
  list = run_verifier(list)
12
+ list = list.reject {|o| run_verifier([o.parent]).empty? }
12
13
  list = list.reject {|o| o.is_alias? unless CodeObjects::Proxy === o.namespace }
13
14
  list = list.reject {|o| o.is_attribute? unless CodeObjects::Proxy === o.namespace } if hide_attributes
14
15
  list
@@ -6,6 +6,7 @@ module YARD
6
6
  # @return [String] escapes text
7
7
  def h(text)
8
8
  out = ""
9
+ text = resolve_links(text)
9
10
  text = text.split(/\n/)
10
11
  text.each_with_index do |line, i|
11
12
  out <<
@@ -89,6 +90,17 @@ module YARD
89
90
  title = "%s%s%s %s%s%s" % [scope, name, args, blk, type, extras_text]
90
91
  title.gsub(/\s+/, ' ')
91
92
  end
93
+
94
+ private
95
+
96
+ def resolve_links(text)
97
+ text.gsub(/(\\|!)?\{(?!\})(\S+?)(?:\s([^\}]*?\S))?\}(?=[\W]|$)/m) do |str|
98
+ escape, name, title, match = $1, $2, $3, $&
99
+ next(match[1..-1]) if escape
100
+ next(match) if name[0,1] == '|'
101
+ linkify(name, title)
102
+ end
103
+ end
92
104
  end
93
105
  end
94
106
  end
@@ -75,8 +75,8 @@ module YARD
75
75
  # @return [Boolean] whether a mixin matches the embed_mixins list
76
76
  # @return [nil] if the mixin is not a module object
77
77
  def embed_mixins_match?(mixin)
78
+ return true if mixin == object # the method is not inherited
78
79
  return nil unless mixin.is_a?(CodeObjects::ModuleObject)
79
- return nil if mixin == object # the method is not inherited
80
80
  embed_mixins.any? do |embed_mixin|
81
81
  re = /\A#{Regexp.quote(embed_mixin).gsub('\*', '.*')}\Z/
82
82
  matchstr = embed_mixin.include?("::") ? mixin.path : mixin.name