Hiding a method from YARD (the right way) - ruby

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.

Related

How can I document a subclass method with YARD?

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?

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.

Is it possible to dynamically inspect the method visibility scope (private/public/protected)?

As mentioned in this answer, in Ruby 2.1 or later, this code:
class SimpleTest
private
define_method :foo do
42
end
end
will define foo as a private method of SimpleTest instances. (In Ruby 2.0 and earlier it won't be private.) However, I'm looking to do something a little less trivial. I would like to define a DSL that classes can extend, and would like the methods that the DSL defines internally to respect the private/protected visibility of the calling context. That may not be clear, so here's an example:
module Dsl
def has_a(name)
define_method name do
42
end
end
end
class Test
extend Dsl
private
has_a :thing
end
As written, that code will define a public thing method on Test instances. Instead, I would like has_a to be able to reflect on the method visibility where it was called (private in this case), and define thing under that same method visibility.
I'm not familiar with Ruby's C source code, but I took a quick look and found this function which seems like it might do what I want, but I don't think it's accessible from Ruby. (It seems to only be used here.) I also looked up the documentation for define_method (since the first example works as desired) here and it seems like the noex variable declared and set here:
int noex = NOEX_PUBLIC;
const NODE *cref = rb_vm_cref_in_context(mod, mod);
if (cref) {
noex = (int)cref->nd_visi;
}
could be the value I want, but again I don't know how I would get that in Ruby, or even if it would be able to reflect back on the calling scope (in Test). Assuming I had the visibility, then I could simply call private name (or protected name) after the define_method call inside has_a if it wasn't called in a public context.
Thoughts? Is there any way to do this, or am I out of luck?
I think this question has a similar answer to what you are looking for: https://stackoverflow.com/a/28075865/5129208
It looks like the author of that made a custom module to get the behavior you're after.

Mostly private methods

I am trying to define methods that are private most of the time, but not always. For example:
class Service
def initialize(repo)
#repo = repo
end
private def repo
#repo
end
def all
repo.all
end
end
My class Service will implement features around a repository object. I want to completely hide away the repo object to prevent its leakage to my production code.
However when debugging or testing I want to be lazy and call service.repo directly instead using service.send :repo.
I thought about loading a class extension or dirty ENV var check for my tests and console, however that prevents my tests from breaking if code inside ./lib/* makes bad use of private methods during a test run.
Now I am exploring with refinements, but that requires too much boilerplate and I want to do some decorator like this:
+LibPrivate
def repo
#repo
end
That is a Python like decorator, but I believe since Ruby 2.1 it might be possible to define custom modifiers beyond private/public without that gem
Simply change the visibility of the method in your test helper or in your test file.
Service.class_eval do
public :repo
end
Just keep in mind that private method are in theory implementation details. You should not test private methods. In general, you test the behavior of the first public method that relies on the private.
If you feel that you need, then it may be the case where the method should not be private.
This rule doesn't have to be applied blindly, but it's good to keep it in mind.

Ruby reserved class method names

In Ruby, are there any methods that are reserved or have default meanings? I recently discovered that initialize is one. Are there any others that I should be aware of when naming my methods? (VI is not giving me the coloring clues that other IDEs give for reserved names.)
In particular, names that have meaning in other languages like run, main, toString, onExit, etc.
You can always see a list of the methods implemented by default for every class:
class Try
end
t = Try.new
puts t.methods.sort
EDIT: actually you may also want to look at the private methods (where initialize is):
puts t.private_methods.sort
Also check out the list of reserver keywords here
If you're working in Rails, you may also want to take a look at this list
you should be aware of those:
keywords
Despite nothing prevents declaring methods like public or private, I highly do not recommend doing so with any of the method names defined in core classes, such as Object and Module. Otherwise weird things can happen:
class Message
def self.private
puts 'private'
end
private
end
Message.private
Output:
private
private
Private class method of a class Module was redefined as public.

Resources