diff --git a/lib/yard/code_objects/constant_object.rb b/lib/yard/code_objects/constant_object.rb index b7904648b..b09e00f88 100644 --- a/lib/yard/code_objects/constant_object.rb +++ b/lib/yard/code_objects/constant_object.rb @@ -12,5 +12,23 @@ class ConstantObject < Base def value=(value) @value = format_source(value) end + + # @return [Base, nil] the target object the constant points to + def target + return @target if instance_variable_defined?(:@target) + + if !value.empty? && + (target = P(namespace, value)) && + !target.is_a?(YARD::CodeObjects::Proxy) && + target != self + @target = target + else + @target = nil + end + @target + rescue YARD::Parser::UndocumentableError + # means the value isn't an alias to another object + @target = nil + end end end diff --git a/lib/yard/templates/helpers/html_helper.rb b/lib/yard/templates/helpers/html_helper.rb index 9f7dae9b4..61a785bf1 100644 --- a/lib/yard/templates/helpers/html_helper.rb +++ b/lib/yard/templates/helpers/html_helper.rb @@ -301,6 +301,17 @@ def insert_include(text, markup = options.markup) def link_object(obj, title = nil, anchor = nil, relative = true) return title if obj.nil? obj = Registry.resolve(object, obj, true, true) if obj.is_a?(String) + + was_const = false + # Re-link references to constants that are aliases to their target. But keep + # their current title. + while obj.is_a?(CodeObjects::ConstantObject) && obj.target + title ||= h(object.relative_path(obj)).to_s + was_const = true + obj = obj.target + end + return link_object(obj, title, anchor, relative) if was_const + if title title = title.to_s elsif object.is_a?(CodeObjects::Base) diff --git a/spec/code_objects/constant_object_spec.rb b/spec/code_objects/constant_object_spec.rb new file mode 100644 index 000000000..50b68aee6 --- /dev/null +++ b/spec/code_objects/constant_object_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +require File.dirname(__FILE__) + '/spec_helper' + +RSpec.describe YARD::CodeObjects::ConstantObject do + before do + Registry.clear + end + + describe "#target" do + it "resolves" do + const1 = ConstantObject.new(:root, :A) + const2 = ConstantObject.new(:root, :B) + const2.value = "A" + expect(const2.target).to eq const1 + end + + it "returns nil for an integer value" do + const = ConstantObject.new(:root, :A) + const.value = "1" + expect(const.target).to be_nil + end + + it "returns nil for a string value" do + const = ConstantObject.new(:root, :A) + const.value = '"String"' + expect(const.target).to be_nil + end + + it "returns nil for an empty value" do + const = ConstantObject.new(:root, :A) + const.value = "" + expect(const.target).to be_nil + end + + it "returns nil for an explicit self-referential constant" do + const = ConstantObject.new(:root, :A) + const.value = "A" + expect(const.target).to be_nil + end + + it "returns nil for an explicit self-referential constant" do + mod = ModuleObject.new(:root, :M) + const = ConstantObject.new(mod, :A) + const.value = "self" + expect(const.target).to be_nil + end + end +end diff --git a/spec/templates/examples/module006.html b/spec/templates/examples/module006.html new file mode 100644 index 000000000..499513e5c --- /dev/null +++ b/spec/templates/examples/module006.html @@ -0,0 +1,54 @@ +
1
<%= format_constant cnst.value %>
<%= format_constant cnst.value %><% end %>