yard-contracts is a YARD plugin that works with the fantastic Contracts gem to automatically document types and descriptions of parameters in your method signatures, saving time, making your code concise and keeping your documentation consistent.
Have you ever got fed up of coding validations, writing error messages and then documenting those things? All this duplication and boilerplate code has always bugged me. Contracts solves the validations and error messages part already, turning many lines of repetitive code into 1 terse readable one. This extension now solves the documentation part making documentation automatically say the same as your validations.
- Types gleaned from Contract and automatically linked with the method signature then added to param documentation.
- to_s is called on the types and where it is useful, added to the param documentation as the description.
- If any params are already documented manually, the extra information from the Contract is merged in rather than overriding or duplicating; allowing full flexibility.
I've used this plugin gem on an existing project and documented 69 methods; it seems to be working great! Note I haven't used all corners of YARD, so there may be advanced features or scenarios that this doesn't work for, please open an issue if you find one. For straightforward projects, however, it's fantastic.
Add this line to your application's Gemfile:
And then execute:
Or install it yourself as:
$ gem install yard-contracts
Compatible with MRI Ruby 2.0+ and JRuby 1.9mode (with additional kramdown gem).
See these two equivalent(ish) examples and be blown away.
Before Contracts and yard-contracts:
# Find the root of a number, like `Math.sqrt` for arbitrary roots. # @param root [Numeric] Root must be greater than zero. # @param num [Numeric] Can't take root of a negative number. # @return [Numeric] Result will be greater than zero. def root(root, num) unless root.is_a?(Numeric) && root > 0 raise "root must be Numeric and > 0." end unless num.is_a?(Numeric) && num >= 0 raise "num must be Numeric and >= 0." end result = num**(1.0/root) unless result.is_a?(Numeric) && result >= 0 raise "return wasn't Numeric or >= 0." end return result end
# Find the root of a number, like `Math.sqrt` for arbitrary roots. # @param num Can't take root of a negative number. Contract Pos, Nat => Nat def root(root, num) num**(1.0/root) end
Isn't that nicer? In the above after example,
@param root ... and
@return ... are automatically added to the documentation with their type and no description was deemed necessary. The
num param has a manual documentation description, that automatically has it's type merged in. This gives you the flexibility to document parameters as explicitly as you like with the minimum of duplication.
There is another option for adding more detailed parameter descriptions without duplication of the parameter name or the problem of keeping documentation in sync across methods... and it's already built into Contracts: custom type classes with
to_s! The above example would be a bad place to do this as there's only one method, but imagine your project had lots of methods that calculate roots on numbers or pass those numbers around---you'd end up duplicating the documentation for the
num parameter. Instead add a custom type class
Rootable or more explicitly
RootableNum if you prefer:
class Rootable def self.valid? val Nat.valid? val end def self.to_s "(Nat) Can't take root of a negative number." end end # Find the root of a number, like `Math.sqrt` for arbitrary roots. Contract Pos, Rootable => Rootable def root(root, num) num**(1.0/root) end
Wow, the code and docstring is now as terse as possible with no duplication at all. Documentation generated will include the type and custom description as before, and now
Rootable can be used all throughout the project making everything concise and clear. Plus, run time errors will have the same description from
Rootable making it easier to debug as your documentation matches your errors. Perfect.
For yard-contracts to pick up these custom type classes they you need to supply the path to the file they are defined in to YARD with the -e flag, and they must be defined directly under the Contracts namespace or the global namespace.
See Contracts for more information on creating custom type classes.
YARD needs to be made aware of this plugin. Simply give it to YARD with the --plugin flag:
bundle exec yardoc --plugin contracts
If you have defined custom type classes, they need to be given to YARD as well. First they must be specified in the global namespace or directly under the Contracts namespace. Give YARD the path to your custom contracts with the -e flag:
bundle exec yardoc --plugin contracts -e lib/my_project/custom_contracts.rb
If using a local fork of yard-contracts you can specify your path to
yard_extensions.rb with the -e flag instead.
bundle exec yardoc -e path/to/lib/yard_extensions.rb
After checking out the repo, run
bin/setup to install dependencies. Then, run
bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run
bundle exec rake install. To release a new version, update the version number in
version.rb, and then run
bundle exec rake release to create a git tag for the version, push git commits and tags, and push the
.gem file to rubygems.org.
Open issues, and if you can please send pull requests:
- Fork it ( https://github.com/sfcgeorge/yard-contracts/fork )
- Create your feature branch (
git checkout -b my-new-feature)
- Commit your changes (
git commit -am 'Add some feature')
- Push to the branch (
git push origin my-new-feature)
- Create a new Pull Request