diff --git a/lib/utopia/project/inheritance.rb b/lib/utopia/project/inheritance.rb index 915dc3f..0a7895f 100644 --- a/lib/utopia/project/inheritance.rb +++ b/lib/utopia/project/inheritance.rb @@ -13,8 +13,8 @@ class Inheritance # A method inherited from another definition. Method = Struct.new(:name, :definition) - # A group of inherited methods with the same origin. - Group = Struct.new(:definition, :methods) + # Methods inherited through a direct relationship. + Group = Struct.new(:kind, :definition, :methods) # Initialize inheritance resolution for the given definition. # @parameter index [Decode::Index] The index used to resolve relationships. @@ -55,10 +55,10 @@ def relationships end # Enumerate documented public and protected methods inherited by the definition. - # @returns [Array(Group)] Methods grouped by the definition which provides them. + # @returns [Array(Group)] Methods grouped by the direct relationship which provides them. def inherited_methods @groups = [] - @groups_by_definition = {} + @groups_by_relationship = {} collect_instance_methods(@definition, false, {}, {}) collect_class_methods(@definition, false, {}, {}) @@ -113,46 +113,46 @@ def singleton_for(definition) end end - def collect_instance_methods(definition, include_own, seen, visited, display_prefix = "#") + def collect_instance_methods(definition, include_own, seen, visited, display_prefix: "#", kind: nil, provider: nil) key = [definition.qualified_name, display_prefix] return if visited[key] visited[key] = true relationship_definitions(definition, :prepends).reverse_each do |relationship| - collect_instance_methods(relationship, true, seen, visited, display_prefix) + collect_instance_methods(relationship, true, seen, visited, display_prefix: display_prefix, kind: kind || :prepend, provider: provider || relationship) end - collect_methods(definition, "#", display_prefix, include_own, seen) + collect_methods(definition, "#", display_prefix, include_own, seen, kind: kind, provider: provider) relationship_definitions(definition, :includes).reverse_each do |relationship| - collect_instance_methods(relationship, true, seen, visited, display_prefix) + collect_instance_methods(relationship, true, seen, visited, display_prefix: display_prefix, kind: kind || :include, provider: provider || relationship) end if display_prefix == "#" && (super_class = super_class_for(definition)) - collect_instance_methods(super_class, true, seen, visited, display_prefix) + collect_instance_methods(super_class, true, seen, visited, display_prefix: display_prefix, kind: kind || :super_class, provider: provider || super_class) end end - def collect_class_methods(definition, include_own, seen, visited) + def collect_class_methods(definition, include_own, seen, visited, kind: nil, provider: nil) key = definition.qualified_name return if visited[key] visited[key] = true - collect_methods(definition, ".", ".", include_own, seen) + collect_methods(definition, ".", ".", include_own, seen, kind: kind, provider: provider) if singleton = singleton_for(definition) - collect_methods(singleton, "#", ".", include_own, seen, definition) + collect_methods(singleton, "#", ".", include_own, seen, kind: kind, provider: provider) end relationship_definitions(definition, :extends).reverse_each do |relationship| - collect_instance_methods(relationship, true, seen, {}, ".") + collect_instance_methods(relationship, true, seen, {}, display_prefix: ".", kind: kind || :extend, provider: provider || relationship) end if super_class = super_class_for(definition) - collect_class_methods(super_class, true, seen, visited) + collect_class_methods(super_class, true, seen, visited, kind: kind || :super_class, provider: provider || super_class) end end - def collect_methods(definition, source_prefix, display_prefix, include_own, seen, origin = definition) + def collect_methods(definition, source_prefix, display_prefix, include_own, seen, kind:, provider:) methods_for(definition, source_prefix).each do |method| name = "#{display_prefix}#{method.name}" next if seen[name] @@ -168,13 +168,14 @@ def collect_methods(definition, source_prefix, display_prefix, include_own, seen next unless method.documented? next if method.respond_to?(:private?) && method.private? - add_method(origin, name, method) + add_method(kind, provider, name, method) end end - def add_method(definition, name, method) - group = @groups_by_definition[definition.qualified_name] ||= begin - group = Group.new(definition, []) + def add_method(kind, provider, name, method) + key = [kind, provider.qualified_name] + group = @groups_by_relationship[key] ||= begin + group = Group.new(kind, provider, []) @groups << group group end diff --git a/pages/reference/_relationships.xnode b/pages/reference/_relationships.xnode index 685e656..9dfe869 100644 --- a/pages/reference/_relationships.xnode +++ b/pages/reference/_relationships.xnode @@ -5,6 +5,9 @@ inheritance = base.inheritance_for(symbol) relationships = inheritance.relationships inherited_methods = inheritance.inherited_methods +inherited_methods_by_relationship = inherited_methods.to_h do |group| + [[group.kind, group.definition.qualified_name], group.methods] +end relationship_labels = { super_class: "Inherits from", @@ -14,32 +17,35 @@ relationship_labels = { } ?> -
#{relationship_labels.fetch(kind)}
0 - ?>, ; #{base.linkify(relationship.name, symbol)}: 0 + ?>, #{method.name}
- - -
-

Inherited Methods

From #{group.definition.qualified_name}:

-
- diff --git a/test/utopia/project/.fixtures/inheritance.rb b/test/utopia/project/.fixtures/inheritance.rb index 69ca6b0..f38fc28 100644 --- a/test/utopia/project/.fixtures/inheritance.rb +++ b/test/utopia/project/.fixtures/inheritance.rb @@ -32,7 +32,21 @@ def extended_method end end - class Parent + module ParentIncluded + # A method included by the parent. + def parent_included_method + end + end + + class Grandparent + # An inherited grandparent method. + def grandparent_method + end + end + + class Parent < Grandparent + include ParentIncluded + # Initialize the parent. def initialize end diff --git a/test/utopia/project/inheritance.rb b/test/utopia/project/inheritance.rb index 1f05964..b28a4ea 100644 --- a/test/utopia/project/inheritance.rb +++ b/test/utopia/project/inheritance.rb @@ -26,16 +26,21 @@ } end - it "groups inherited methods by their origin" do - methods = inheritance.inherited_methods.to_h do |group| - [group.definition.qualified_name, group.methods.map(&:name)] + it "groups inherited methods by their direct relationship" do + groups = inheritance.inherited_methods + methods = groups.to_h do |group| + [[group.kind, group.definition.qualified_name], group.methods.map(&:name)] end expect(methods).to be == { - "Example::Prepended" => ["#prepended_method"], - "Example::Included" => ["#included_method"], - "Example::Parent" => ["#parent_method", ".parent_class_method", ".singleton_class_method"], - "Example::Extended" => [".extended_method"], + [:prepend, "Example::Prepended"] => ["#prepended_method"], + [:include, "Example::Included"] => ["#included_method"], + [:super_class, "Example::Parent"] => ["#parent_method", "#parent_included_method", "#grandparent_method", ".parent_class_method", ".singleton_class_method"], + [:extend, "Example::Extended"] => [".extended_method"], } + + parent = groups.find{|group| group.definition.qualified_name == "Example::Parent"} + parent_included_method = parent.methods.find{|method| method.name == "#parent_included_method"} + expect(parent_included_method.definition.qualified_name).to be == "Example::ParentIncluded#parent_included_method" end end diff --git a/test/utopia/project/serve.rb b/test/utopia/project/serve.rb index 0e8419d..27ec354 100644 --- a/test/utopia/project/serve.rb +++ b/test/utopia/project/serve.rb @@ -80,9 +80,13 @@ expect(body).to be(:include?, "def release_names") expect(body).to be(:include?, 'class ReleasesDocument < Document') - expect(body).to be(:include?, '
') + expect(body).to be(:include?, '
') expect(body).to be(:include?, "
Inherits from
") - expect(body).to be(:include?, "/reference/Utopia/Project/Document/index#Utopia%3A%3AProject%3A%3ADocument%23root") + + relationships = body[/
.*?<\/dl>/m] + expect(relationships).to be(:include?, "/reference/Utopia/Project/Document/index#Utopia%3A%3AProject%3A%3ADocument%23root") + expect(relationships).to be(:include?, ": Inherited Methods") end it "summarizes nested definitions" do