Ruby YARD:记录抽象方法实现

时间:2013-10-15 10:39:13

标签: ruby abstract documentation-generation yard

我有一个典型的OO模式:一个基本抽象类(定义抽象方法)和几个以类特定方式实现这些抽象方法的类。

我习惯在抽象方法中只编写一次文档然后自动传播到几个具体的类(至少它在Javadoc,Scaladoc,Doxygen中以下面的方式工作),即我不需要重复所有具体课程中的描述相同。

然而,我无法找到如何在YARD中进行此类传播。我试过了,例如:

# Some description of abstract class.
# @abstract
class AbstractClass
  # Some method description.
  # @return [Symbol] some return description
  # @abstract
  def do_something
    raise AbstractMethodException.new
  end
end

class ConcreteClass < AbstractClass
  def do_something
    puts "Real implementation here"
    return :foo
  end
end

我得到了什么:

  • 代码按预期工作 - 即在抽象类中调用thr AbstractMethodException,在具体类中完成工作
  • 在YARD中,AbstractClass明确定义为摘要,ConcreteClass是正常的
  • 方法说明和返回类型适用于AbstractClass
  • 据说方法会在AbstractMethodException
  • 中抛出AbstractClass
  • 方法根本没有描述,Object中的泛型ConcreteClass返回类型,基类中不存在抽象方法的通知。

我期望获得:

  • 方法的描述和返回类型从ConcreteClass
  • 的信息继承(即复制)到AbstractClass
  • 理想情况下,此方法在ConcreteClass描述的“已继承”或“已实施”部分中指定,并带有从ConcreteClass#do_somethingAbstractMethod#do_something的参考链接。

是否可以这样做?

2 个答案:

答案 0 :(得分:4)

我认为这个问题归结为你想要做的事情。看起来你正在尝试在Ruby中实现一个接口,如果你来自Java或.NET,这是有道理的,但实际上并不是Ruby开发人员的工作方式。

以下是关于Ruby中接口的典型思想的一些信息:What is java interface equivalent in Ruby?

那就是说,我明白你要做什么。如果您不希望直接实现AbstractClass,但是您想要定义可以在类似AbstractClass规定的类中使用的方法(如Design by Contract中所述),那么您可能希望使用模块。模块可以很好地保存您的代码DRY,但它们并不能完全解决与记录overridden methods相关的问题。所以,在这一点上,我认为你可以重新考虑如何处理文档,或者至少以更加Ruby的方式处理它。

Ruby中的继承(实际上根据我自己的经验)只是出于以下几个原因使用:

  • 可重复使用的代码和属性
  • 默认行为
  • 专业化

显然还有其他边缘情况,但老实说这就是Ruby中常用的继承。这并不意味着你正在做的事情不起作用或违反某些规则,它只是在Ruby(或大多数动态类型语言)中不典型。这种非典型行为可能是YARD(和其他Ruby doc生成器)没有达到预期效果的原因。也就是说,创建一个只定义子类中必须存在的方法的抽象类,从代码的角度来看,实际上只能获得很少的收益。未定义的方法将导致无论如何都会抛出NoMethodError异常,并且您可以使用#respond_to?(:some_method)(或其他方法)以编程方式检查对象是否将响应方法调用(或任何消息)获取元东西的反射工具)。这一切都归功于Ruby对Duck Typing的使用。

对于纯文档,为什么要记录一个实际上没有使用的方法?您不应该真正关心通过调用方法发送或接收的对象的,只是这些对象响应的内容。因此,如果在这里没有添加任何实际值,请不要首先创建您的AbstractClass。如果它包含您实际将直接调用的方法而不覆盖,则创建一个Module,在那里记录它们,然后运行$ yardoc --embed-mixins以包含在混合模块中定义的方法(及其描述)。否则,记录实际实现它们的方法,因为每个实现应该不同(否则为什么要重新实现它)。

以下是我与你正在做的事情类似的事情:

# An awesome Module chock-full of reusable code
module Stuff
  # A powerful method for doing things with stuff, mostly turning stuff into a Symbol
  def do_stuff(thing)
    if thing.kind_of?(String)
      return thing.to_sym
    else
      return thing.to_s.to_sym
    end
  end
end

# Some description of the class
class ConcreteClass
  include Stuff

  # real (and only implementation)
  def do_something
    puts "Real implementation here"
    return :foo
  end
end

an_instance = ConcreteClass.new
an_instance.do_somthing       # => :foo
# > Real implementation here
an_instance.do_stuff("bar")   # => :bar

运行YARD(使用--embed-mixins)将包含Stuff模块中混入的方法(及其描述),您现在知道包括Stuff模块在内的任何对象都将具有您期望的方法。 p>

您可能还想查看Ruby Contracts,因为它可能更接近您正在寻找的绝对强制方法接受并仅返回您想要的对象类型,但我不确定如何与YARD一起玩。

答案 1 :(得分:1)

不理想,但您仍然可以使用(see ParentClass#method)构造(记录为here)。不理想,因为您必须手动为每个重写方法键入此内容。

话虽这么说,我不是院子里的专家,但鉴于其特别可定制的架构,我会感到惊讶的是,只有在Templates的某处扩展Yard就没有简单的方法来实现你所需要的东西。部门我想。