lsegal / yard

YARD is a Ruby Documentation tool. The Y stands for "Yay!"
http://yardoc.org
MIT License
1.95k stars 398 forks source link

Question: How can I document for the `Array<Hash>` param? #1458

Closed remy727 closed 1 year ago

remy727 commented 2 years ago

Params:

{
  "invites": [
    {
      "email": "foo@bar.com",
      "org_admin": false
    }
  ]
}

Controller:

class OrganizationController < ApplicationController
  # @param invites :required [Array<Hash>]
  #   @option :required [String] :email
  #   @option :required [Boolean] :org_admin
  # @return [Hash]
  def create
  end
end

Not sure these are the correct document for Array of Hashes

lsegal commented 1 year ago

Sorry for the late reply to this, and hopefully you have a working solution by now.

Generally speaking, the way to document bare hashes like this is by modeling those hashes with classes. The TypeScript approach here would be to create a declaration file with a type declaration, but Ruby doesn't quite have this (Ruby 3 has .rbs but it's not quite the same), so instead you can model with class objects as a reasonable abstraction; it's not an exact 1:1 match, but it would probably work to provide the overall structure of the hashes.

The other approach is to use some kind of lower level schema library abstraction to deal with this type of validation. The reality here is that this is not a "Ruby typing" issue, but a wire format schema in your API. In fact, your #create method doesn't even take an invites param, so the documentation you're writing is already invalid for a method declaration. You actually have a params hash defined in the Rails API that just so happens to be filled with some object. Your method has nothing to do with that API. The validation and API for params is actually declared somewhere else-- and, in general, you're treating Rails' params method as a context-dependent type (dependent on the method it's called from), which is almost impossible to model, even with strongly typed languages-- this is much more than just a YARD limitation.

The good news is that libraries like Smithy, OpenAPI, or Swagger do a good job of creating this type of abstraction for you and allow you to validate the input as well as provide APIs to access this data in a consistent way (specifically the conversion to proper classes / objects).

tl;dr you shouldn't be documenting the shape of your API params input as a @param and may want to consider using some kind of JSON API declaration library (Swagger, OpenAPI, Smithy) to do this type of validation / documentation. The shape of your API is typically not specific to your Ruby documentation unless you were creating a client side library, at which point your abstraction would likely be more explicit than just a bunch of hashes anyway. Consider documenting your API separately from your Ruby code.