How can I document a subclass method with YARD? - ruby

How can I document a method that a subclass overrides without being redundant? For example...
class Parent
# Is this the parent?
# #return [Boolean]
def parent?
true
end
end
class Child < Parent
def parent?
false
end
end
YARD generates something like this.
Class: Parent
Instance Method Summary
#parent? ⇒ Boolean
Is this the parent?.
Class: Child
Instance Method Summary
#parent? ⇒ Boolean
The generated YARD documentation will not include "Is this the parent?" in the docs for Child#parent?, nor will it indicate that it is an override.
I would like to see something like this:
Class: Parent
Instance Method Summary
#parent? ⇒ Boolean
Is this the parent?.
Class: Child
Instance Method Summary
#parent? ⇒ Boolean
Is this the parent?.
Methods inherited from Parent
#parent?
I would prefer not to have to copy the documentation into every subclass.

Simple Answer
The correct way to document this is to place a free-form description after the return type, and maybe document the method itself. For example:
class Parent
# True when a passed argument has Parent
# as an ancestor.
#
# #return [Boolean] ancestor of subclass
def parent?
true
end
end
A More Comprehensive Example
You can also provide examples of usage and more detailed documentation by leveraging the free-form parts of the tag syntax and your markup language (RDoc by default).
class Parent
# True when passed argument includes
# +Parent+ as an ancestor.
#
# #example Cousin isn't subclass of Parent
# parent? Cousin.new #=> false
# #example Child is a subclass of Parent
# parent? Child.new #=> true
# #param obj [Class, #ancestors] Class,
# instance, or other object that can
# #respond_to?(:ancestors)
# #return [Boolean] ancestors of _obj_
# includes +Parent+ class
def parent? obj
obj.class.ancestors.include? self.class
end
end
There may be more elegant ways to do this, and I'm typing on a phone so caveat emptor with the code as I haven't actually tested the code or validated the formatting of the YARD output. Still, the general approach is sound and I use this type of formatting routinely.
Core Ideas
Basically, the core ideas are that one should:
Place your description of each tagged item in-line if you want it associated with that item. Most tags support free-form descriptions or titles which will be formatted in a way that visually associates them with the tagged element.
Use tags and formatting for classes, methods, arguments, examples, and so on. You don't have to cram it all into a single tag, although in your specific example you probably want to do so to keep things visually associated.
You can wrap long free-form text with indentation in most cases.
There are a few exceptions to wrapping (e.g. example titles vs. indented code blocks is illustrative) but you can often work around that with RDoc or Markdown syntax.
If you can't get what you want, refer to item four for some alternative approaches or find a different tag or markup to represent your intent.
Macros, custom tags, and directives will let you do weird and wonderful things if you can't get what you want out of the box. NB: I've almost never needed to resort to this, but the features are there if you need them.
In most cases, simpler is better. Understanding how and when YARD can't succinctly represent your authorial intent is admittedly a bit of an art form, but code that's hard to tag or render as expected may indicate a need to refactor the code rather than just modifying the YARD tags.
Keep YARD's Main Purpose in Mind
Just remember that YARD is primarily for ** elements of your documentation, and providing hints to a rendering engine or document reader, It's not a full-fledged markup language by itself, can't validate signatures like RBS with Steep, and doesn't enforce any sort of caller/callee contract.
It's all just comments. Make your code as self-explanatory as possible, then treat YARD as just one way to improve necessary comments by tagging documentation elements so the markup language generates better output.

You can use a reference tag on each individual method to refer to the Parent methods.
class Child < Parent
# (see Parent#parent?)
def parent?
false
end
end
Child#parent? will have the same documentation as Parent::parent?

Related

Ruby Yard: documenting a parameter that accept multiple types

I'm migrating and documenting an old Ruby (specifically Sketchup) code base but I'm not very familiar with the ecosystem. Since I'm more used to TypeScript, I'm asking what is the equivalent documentation of this in Ruby:
function get_definition(instance: ComponentInstance | Group | Image)
I've tried this:
# #param [ComponentInstance | Group | Image] instance
def self.get_definition(instance)
but I'm unsure if this is correct.
Some Possible YARD Tag Examples for Your Code
def self.get_definition(instance)
There really isn't enough of your code to be sure what the real programmer intent is, so you'll have to adapt a bit as needed. Additionally, it's important to understand that YARD tags are really just documentation, not enforced contracts or even type definitions in the sense of RBS, which can be used to create typed signatures that can be checked with various external utilities.
Since you are defining your own objects, you probably want to use the Class or Module Types tag, with or without sub-types. For example, assuming the instance argument can be any of three completely different classes:
# Class or module method to return the definition of an
# object.
#
# #param instance [ComponentInstance, Group, Image] a single
# injected object of one of the listed classes
# #return [Array<String>] components that define _instance_
def self.get_definition(instance)
end
On the other hand, if Group and Image classes inherit from ComponentInstance, or perhaps are contained by one, then you might use the following instead:
# Class or module method that uses a ComponentInstance to create
# side effects.
#
# #param instance [ComponentInstance<Group, Image>] an injected
# ComponentInstance object holding one or more Group or Image
# objects
# #return [void] because we don't care about the return value,
# just the side effects
def self.get_definition(instance)
end
You might also benefit from duck-typing, where you don't care what the instance object actually is, so long as it conforms to a given API. For example:
# Class or module method to return the definition of a
# serialized object.
#
# #param instance [#define, #inspect, #ancestors] object that
# will #respond_to? one of the listed methods
# #return [String<JSON>] a definition String that has been
# serialized as JSON for consumption by an API
def self.get_definition(instance)
end
There are certainly other tags and macros you might use, depending on the semantics you want to convey, but that should get you started. You might also want to look at RBS or TypeProf if you need real signature delcarations, or if you're planning to use some sort of third-party typing system with your code, e.g. Steep or Sorbet. For documentation, though, plain RDoc or YARD tags with RDoc or Markdown are generally your best bets.

How can I determine what objects a call to ruby require added to the global namespace?

Suppose I have a file example.rb like so:
# example.rb
class Example
def foo
5
end
end
that I load with require or require_relative. If I didn't know that example.rb defined Example, is there a list (other than ObjectSpace) that I could inspect to find any objects that had been defined? I've tried checking global_variables but that doesn't seem to work.
Thanks!
Although Ruby offers a lot of reflection methods, it doesn't really give you a top-level view that can identify what, if anything, has changed. It's only if you have a specific target you can dig deeper.
For example:
def tree(root, seen = { })
seen[root] = true
root.constants.map do |name|
root.const_get(name)
end.reject do |object|
seen[object] or !object.is_a?(Module)
end.map do |object|
seen[object] = true
puts object
[ object.to_s, tree(object, seen) ]
end.to_h
end
p tree(Object)
Now if anything changes in that tree structure you have new things. Writing a diff method for this is possible using seen as a trigger.
The problem is that evaluating Ruby code may not necessarily create all the classes that it will or could create. Ruby allows extensive modification to any and all classes, and it's common that at run-time it will create more, or replace and remove others. Only libraries that forcibly declare all of their modules and classes up front will work with this technique, and I'd argue that's a small portion of them.
It depends on what you mean by "the global namespace". Ruby doesn't really have a "global" namespace (except for global variables). It has a sort-of "root" namespace, namely the Object class. (Although note that Object may have a superclass and mixes in Kernel, and stuff can be inherited from there.)
"Global" constants are just constants of Object. "Global functions" are just private instance methods of Object.
So, you can get reasonably close by examining global_variables, Object.constants, and Object.instance_methods before and after the call to require/require_relative.
Note, however, that, depending on your definition of "global namespace" (private) singleton methods of main might also count, so you check for those as well.
Of course, any of the methods the script added could, when called at a later time, themselves add additional things to the global scope. For example, the following script adds nothing to the scope, but calling the method will:
class String
module MyNonGlobalModule
def self.my_non_global_method
Object.const_set(:MY_GLOBAL_CONSTANT, 'Haha, gotcha!')
end
end
end
Strictly speaking, however, you asked about adding "objects" to the global namespace, and neither constants nor methods nor variables are objects, soooooo … the answer is always "none"?

Hiding a method from YARD (the right way)

I am using YARD to document one of my Ruby projects. I have certain methods that I do not want to be included in the documentation, things like #inspect and #to_s that you expect to exist and return a reasonable result.
It is possible to hide these methods using the #private tag and the yardoc --no-private command-line option:
# #private let's not document this
def inspect; ...; end
However, the YARD documentation on #private explicitly states:
Note: This method is not recommended for hiding undocumented or “unimportant” methods. This tag should only be used to mark objects private when Ruby visibility rules cannot do so.
If I use #api private instead, YARD (nicely) tags the methods with a private badge in the documentation, but still shows them.
Is there a "legal" way to hide methods from YARD output?
From my limited tests, I noticed the following works well (and doesn't have the explicit note in the documentation to avoid for your use case):
# #!visibility private
def inspect; ...; end
According to the yard documentation:
#!visibility public | protected | private
Modifies the current parsing visibility (public, protected, or private).
Hope that helps.

What are empty-body methods used for in Ruby?

Currently reading a Ruby style guide and I came across an example:
def no_op; end
What is the purpose of empty body methods?
There are a number of reasons you might create an empty method:
Stub a method that you will fill in later.
Stub a method that a descendant class will override.
Ensure a class or object will #respond_to? a method without necessarily doing anything other than returning nil.
Undefine an inherited method's behavior while still allowing it to #respond_to? the message, as opposed to using undef foo on public methods and surprising callers.
There are possibly other reasons, too, but those are the ones that leapt to mind. Your mileage may vary.
There may be several reasons.
One case is when a class is expected to implement a specific interface (virtually speaking, given that in Ruby there are no interfaces), but in that specific class that method would not make sense. In this case, the method is left for consistency.
class Foo
def say
"foo"
end
end
class Bar
def say
"bar"
end
end
class Null
def say
end
end
In other cases, it is left as a temporary placeholder or reminder.
There are also cases where the method is left blank on purpose, as a hook for developers using that library. The method it is called somewhere at runtime, and developers using that library can override the blank method in order to execute some custom callback. This approach was used in the past by some Rails libraries.

Ruby YARD: documenting abstract methods implementations

I have a typical OO pattern: one base abstract class (that defines abstract methods) and several classes that implement these abstract methods in class-specific way.
I'm used to write documentation only once in abstract methods and then it automatically propagates to several concrete classes (at least it works the following way in Javadoc, in Scaladoc, in Doxygen), i.e. I don't need to repeat the same description in all concrete classes.
However, I couldn't find how to do such propagation in YARD. I've tried, for example:
# 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
What I get:
Code works as expected - i.e. throws AbstractMethodException is called in abstract class, does the job in concrete class
In YARD, AbstractClass is clearly defined as abstract, ConcreteClass is normal
Method description and return type is good in AbstractClass
Method is said to throw AbstractMethodException in AbstractClass
Method has no description at all and generic Object return type in ConcreteClass, there's not a single notice of that an abstract method exists in base class.
What I expect to get:
Method's description and return type are inherited (i.e. copied) to ConcreteClass from info at AbstractClass
Ideally, this method is specified in "inherited" or "implemented" section of ConcreteClass description, with some reference link from ConcreteClass#do_something to AbstractMethod#do_something.
Is it possible to do so?
I think the issue boils down to what you're trying to do. It looks like you're trying to implement an Interface in Ruby, which makes sense if you're coming from Java or .NET, but isn't really how Ruby developers tend to work.
Here is some info about how the typical thought on Interfaces in Ruby: What is java interface equivalent in Ruby?
That said, I understand what you're trying to do. If you don't want your AbstractClass to be implemented directly, but you want to define methods that can be used in a class that behaves like the AbstractClass stipulates (as in Design by Contract), then you probably want to use a Module. Modules work very well for keeping your code DRY, but they don't quite solve your problem related to documenting overridden methods. So, at this point I think you can reconsider how you approach documentation, or at least approach it in a more Ruby-ish way.
Inheritance in Ruby is really (generally speaking from my own experience) only used for a few reasons:
Reusable code and attributes
Default behaviors
Specialization
There are obviously other edge cases, but honestly this is what inheritance tends to be used for in Ruby. That doesn't mean what you're doing won't work or violates some rule, it just isn't typical in Ruby (or most dynamically typed languages). This atypical behavior is probably why YARD (and other Ruby doc generators) doesn't do what you expect. That said, creating an abstract class that only defines the methods that must exist in a subclass really gains you very little from a code perspective. Methods not defined will result in a NoMethodError exception being thrown anyway, and you could programmatically check if an object will respond to a method call (or any message for that matter) from whatever calls the method, using #respond_to?(:some_method) (or other reflective tools for getting meta stuff). It all comes back Ruby's use of Duck Typing.
For pure documentation, why document a method that you don't actually use? You shouldn't really care about the class of the object being sent or received from calling a method, just what those objects respond to. So don't bother creating your AbstractClass in the first place if it adds no real value here. If it contains methods you actually will call directly without overriding, then create a Module, document them there, and run $ yardoc --embed-mixins to include methods (and their descriptions) defined in mixed-in Modules. Otherwise, document methods where you actually implement them, as each implementation should be different (otherwise why re-implement it).
Here is how I would something similar to what you're doing:
# 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
Running YARD (with --embed-mixins) will include the methods mixed-in from the Stuff module (along with their descriptions) and you now know that any object including the Stuff module will have the method you expect.
You may also want to look at Ruby Contracts, as it may be closer to what you're looking for to absolutely force methods to accept and return only the types of objects you want, but I'm not sure how that will play with YARD.
Not ideal, but you can still use the (see ParentClass#method) construct (documented here). Not ideal because you have to type this manually for every overriding method.
That being said, I'm no Yard specialist but given its especially customizable architecture, I'd be surprised that there would be no easy way to implement what you need just by extending Yard, somewhere in the Templates department I guess.

Resources