In our last post we further refined our Boundary setup. Today we’ll be concentrating on ExDoc.
If you followed along with part 2 you can continue with the code from there. Otherwise you can grab today’s starting off point by:
Terminal
mkdir <some new directory>
cd <the new directory>Terminal
git init
git remote add origin git@github.com:riebeekn/elixir_code_hygiene.git
git fetch origin e29178674c360d9017f49001d5c8a9354ffc5055
git reset --hard FETCH_HEADYou should now see the following git history.
Terminal
git log --pretty=oneline
And now you can install the existing dependencies, and set up the database.
Terminal
mix deps.getTerminal
mix do ecto.drop, ecto.create, ecto.migrateNow let’s take a closer look at ExDoc.
What we currently have
We currently have a default set-up of ExDoc in our project, we can run the docs via a mix task:
Terminal
mix docs
And then view the docs.
Terminal
open doc/index.htmlIf we click the modules tab we’ll see docs for the various modules in our project.

Not bad! I think we could improve things with a bit of organization however.
Organizing the docs
We can provide some organization by updating the mix.exs file.
We’ll make a number of changes to the mix file, let’s go over them one by one.
First off, let’s update the main project section.
/mix.exs
defmodule CodeHygiene.MixProject do
  use Mix.Project
  # these are new
  @version "0.1.0"
  @source_url "https://github.com/riebeekn/elixir_code_hygiene"
  def project do
    [
      app: :code_hygiene,
      # replaced hard-coded version with the @version attribute
      version: @version,
      elixir: "~> 1.12",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:boundary, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps(),
      test_coverage: [tool: ExCoveralls],
      preferred_cli_env: [
        coveralls: :test,
        "coveralls.detail": :test,
        "coveralls.post": :test,
        "coveralls.html": :test
      ],
      # these are also new
      name: "Code Hygiene example app",
      docs: docs()
    ]
  end
  ...We’ve added some attributes for the app version and source code location of the repo.  We then add a name and docs attribute to the project function.  The docs attribute calls out to a new function we’ll write which will handle the actual doc organization:
/mix.exs
defp docs do
  [
    main: "CodeHygiene",
    assets: "docs/assets",
    logo: "docs/assets/images/logo.svg",
    source_ref: "v#{@version}",
    source_url: @source_url,
    formatters: ["html"],
    groups_for_modules: groups_for_modules()
  ]
endEverything is pretty self explanatory.  We set an assets directory for any assets such as images we want to add to our docs; we set a logo for the docs; we set the default page for the docs (via the main attribute); we set the version and source url of the repo the docs refer to; we specifiy that we only want to generate html docs; and finally call out to a new function groups_for_modules which will do the grouping of the docs.
We need to create the directories we’ve specified above:
Terminal
mkdir -p docs/assets/imagesAnd we can throw this image into the docs/assets/images folder, renaming it to logo.svg
Now lets create the groups_for_modules function.
/mix.exs
defp groups_for_modules do
  [
    API: [
      CodeHygiene,
      CodeHygiene.Products,
      CodeHygiene.Repo
    ],
    Accounts: [
      CodeHygiene.Accounts,
      CodeHygiene.Accounts.UserNotifier,
      CodeHygiene.Accounts.UserToken,
      CodeHygiene.Accounts.User
    ],
    "Accounts - Frontend": [
      CodeHygieneWeb.UserAuth,
      CodeHygieneWeb.UserConfirmationController,
      CodeHygieneWeb.UserRegistrationController,
      CodeHygieneWeb.UserResetPasswordController,
      CodeHygieneWeb.UserSessionController,
      CodeHygieneWeb.UserSettingsController
    ],
    Phoenix: [
      CodeHygieneWeb,
      CodeHygieneWeb.Endpoint,
      CodeHygieneWeb.ErrorHelpers,
      CodeHygieneWeb.Gettext,
      CodeHygieneWeb.LiveHelpers,
      CodeHygieneWeb.Router,
      CodeHygieneWeb.Router.Helpers
    ],
    Schemas: [
      CodeHygieneSchema,
      CodeHygieneSchema.Product
    ]
  ]
endSimple, we’re just providing some grouping for how our modules will display in the docs.
There are also a number of modules in our project that don’t currently have moduledocs and which we want to exclude from the generated docs.  We accomplish this by adding a @moduledoc false attribute to these modules, for example:
/lib/code_hygiene_web/controllers/page_controller.ex
defmodule CodeHygieneWeb.PageController do
  @moduledoc falseWe need to add this attribute to the following files:
- lib/code_hygiene_web/controllers/page_controller.ex
- lib/code_hygiene_web/views/error_view.ex
- lib/code_hygiene_web/views/layout_view.ex
- lib/code_hygiene_web/views/page_view.ex
- lib/code_hygiene_web/views/user_confirmation_view.ex
- lib/code_hygiene_web/views/user_registration_view.ex
- lib/code_hygiene_web/views/user_reset_password_view.ex
- lib/code_hygiene_web/views/user_session_view.ex
- lib/code_hygiene_web/views/user_settings_view.ex
Now we’ll see the docs are a little more organized:
Terminal
mix docs
open doc/index.html
Nice! We have a logo, name and version included in our docs and the modules are grouped in a logical manner.
Our final bit of exploration into the configuration of ExDoc is to see how we can go about documenting things that aren’t modules.
Using ExDoc to document more than just modules
Say you have a developer guide for your project or some other sort of documentation that isn’t specific to a module but which you’d like to include in your docs.  ExDoc has you covered.  First off let’s add a changelog to the project.  We’ll just populate it with some fake entries.
Adding a CHANGELOG
Terminal
touch CHANGELOG.md/CHANGELOG.md
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## [1.0.3] - 2022-01-15
### Changed
- Changed stuff!
- Changed more stuff!
## [1.0.2] - 2021-12-15
### Fixed
- Fixed that terrible bug!
### Changed
- Updated the way something works.
### Added
- Added some new stuff!
## [1.0.1] - 2021-11-30
### Added
...To reference this file in our docs, we need a new section in the docs function, we’ll add a line to call out to an extras function.
/mix.exs
defp docs do
  [
    main: "CodeHygiene",
    assets: "docs/assets",
    logo: "docs/assets/images/logo.svg",
    source_ref: "v#{@version}",
    source_url: @source_url,
    formatters: ["html"],
    groups_for_modules: groups_for_modules(),
    extras: extras()
  ]
endWe then need to create the extras function.
/mix.exs
defp extras do
  [
    "CHANGELOG.md"
  ]
endAnd that’s it! You can use the same technique for adding any sort of file to the generated docs.
If we generate our docs we’ll see our CHANGELOG show up under the Pages tab.

The last thing we’ll look at is adding guides to our docs.
Adding guides
We can add guides to our docs via the extras function.  We can further provide grouping for the guides via a groups_for_extras function.
/mix.exs
defp docs do
  [
    main: "CodeHygiene",
    assets: "docs/assets",
    logo: "docs/assets/images/logo.svg",
    source_ref: "v#{@version}",
    source_url: @source_url,
    formatters: ["html"],
    groups_for_modules: groups_for_modules(),
    extras: extras(),
    groups_for_extras: groups_for_extras(),
  ]
end
defp extras do
  [
    "CHANGELOG.md",
    # Guides
    "docs/guides/dev_setup.md"
  ]
end
defp groups_for_extras do
  [
    Guides: ~r{guides/[^\/]+\.md}
  ]
endLet’s now create our “guide”.  All we are going to do is create a fake dev setup guide with some Lorem text.  We’ll also throw the Phoenix logo in there as well just to make sure assets link correctly from the docs.
So first, off we’ll create the markdown file for the guide.
Terminal
mkdir docs/guides
touch docs/guides/dev_setup.mdNow we’ll copy the Phoenix logo into the assets folder we created earlier.
Terminal
mkdir -p docs/assets/images/guides/dev_setup
cp priv/static/images/phoenix.png docs/assets/images/guides/dev_setupAnd finally fill in some fake content.
/guides/dev_setup.md
# Dev Setup
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<img src="assets/images/guides/dev_setup/phoenix.png" />Let’s see what we got!
Terminal
mix docs
open doc/index.html
Looking good!  That does it for our ExDoc organization… now would be a good time to commit our changes, and then before bailing for the day let’s look at how we can host our docs online via GitHub.
Hosting the Docs on GitHub
Generating the documentation locally is fine, but it would be great if it was available online… and luckily this is very simple, we just need a new GitHub action.
We’ll create a new YAML file for the action:
Terminal
touch .github/workflows/docs.yml/.github/workflows/docs.yml
name: Publish docs
on:
  push:
    branches:
      - main
jobs:
  generateDocs:
    name: Generate project documentation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Elixir
        uses: erlef/setup-beam@v1
        with:
          elixir-version: "1.13.3"
          otp-version: "24.2"
      - name: Build docs
        uses: lee-dohm/generate-elixir-docs@v1
      - name: Publish to Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docThis action will create a new branch called gh-pages which will contain the docs.  On any push to main the docs will be regenerated and available online, pretty freaking neat!
Let’s try it out… if we push our code to GitHub we’ll see the new Publish docs action we created.

And if we take a look at the branches in our repo, we now have a new gh_pages branch.

To host the docs we need to go to the Settings –> Pages section of the GitHub repo and set the “source” for the GitHub Pages, we just set the branch to gh_pages and keep the folder as root.

After clicking save, we see a new GitHub action pages-build-deployment is triggered.

After the action completes our docs are live!

Summary
That’s it for this series of posts on code hygiene tools. Getting these tools set up can take a bit of effort but IMO is well worth it!
Today’s code
If you want to retrieve the GitHub commit that corresponds to today’s code:
Terminal
mkdir <some new directory>
cd <the new directory>Terminal
git init
git remote add origin git@github.com:riebeekn/elixir_code_hygiene.git
git fetch origin 600642de0ed22dc49c5d2ce66accb461960bce96
git reset --hard FETCH_HEADYou should now see the expected git history.
Terminal
git log --pretty=oneline
Thanks for reading and I hope you enjoyed the post!
References
A couple of great resources I found helpful regarding how to get the most out of ExDoc are: