sfcgeorge / yard-contracts

YARD Plugin for Automatic Param Docs from Contracts.
MIT License
26 stars 3 forks source link

How to document a constructor #9

Closed fappelman closed 9 years ago

fappelman commented 9 years ago

Given a constructor I would like to see in the documentation that it returns the type it is creating. Take the following as an example:

class Operation
  Contract Any => Operation
  def initialize(record)
  end
end

This example does provide the proper documentation but it will fail during runtime stating that the method returns nil or whatever the last statement of the method is.

So this may be a question I should ask the contracts developer but I'm not sure about that. Solutions so far are:

The first workaround is really bad, either for coding or documentation. The second workaround is fine for coding but not documentation.

Any suggestion?

Regards,

Fred

egonSchiele commented 9 years ago

The initialize method does not return the type it is creating. See this FAQ question: https://github.com/egonSchiele/contracts.ruby/wiki/FAQ#its-not-obvious-what-a-class-initialize-should-return-self-or-nil

fappelman commented 9 years ago

Understood but the yard default (if you do not document it at all) is to have it match the class name. Although this is technically not correct it is the default and what I would expect. This is probably because new is not documented just the initialize.

sfcgeorge commented 9 years ago

yard-contracts simply follows Contracts, so any change to this behaviour would have to be done in Contracts. But actually Contracts is just following the standard way Ruby works.

new is implemented roughly as follows:

def self.new(*args)
  obj = self.allocate
  obj.initialize(*args)
  obj
end

The return value of initialize isn't used for anything, because the actual object comes from allocate and that is then returned by new after modification by initialize. To meaningfully contract the return value you'd need to do it on allocate or new, but you can't as that's inside Ruby. Semantically, initialize returns whatever the last statement is, which isn't used, so Any or Object are the most correct return value. Or explicitly return self and then using the class name is correct as that really is what's being returned (despite not being used).

I'm doing the latter in my docs, saying the return type is my class name and explicitly returning self. It would be nice to get rid of the self but that would require adding a special case to Contracts that isn't semantically correct because it doesn't actually follow what Ruby is doing. I don't think we're going to do that as edge cases can get out of hand, for example what if you override new to do something else but Contracts has a special case based on the default behaviour?

You've identified the two work arounds so I'll close the issue and you can choose whichever you prefer. Sorry it can't be cleaner, it's just a quirk of how Ruby handles constructors. Thank you for using yard-contracts and taking the time to open this discussion :purple_heart:

waterlink commented 9 years ago

I actually don't see a big deal in returning self at the end of initialize. Don't affect runtime in any way, but improves docs with yard-contracts and contracts.

egonSchiele commented 9 years ago

Doesn't seem like a big deal to me either, but this is an inconsistency in the yard gem. I have opened an issue there, lets see what they say.

sfcgeorge commented 9 years ago

I feel I should explain that my ideology in yard-contracts is to stay true to Contracts rather than YARD. For example YARD calls any return value Object whereas Contracts calls it Any and yard-contracts uses that.

My reasoning is that this way runtime errors are identical to what the documentation shows, and this is blissful. However I can see that in a project only partially annotated with Contracts then this could look a mess. Of course you can make your contract say => Object to make it match in this case.

The second reason is that it makes implementing yard-contracts simpler; I bet there are a lot of other YARD edgecases that would have to be coded in to follow the YARD way.