Elixir/Phoenix Project: Create First Schema & Run Tests in Docker
Tags: elixir, tutorial, sideprojects
Published: 2020-11-10 15:50:58

#### Goal
  - Create Destinations schema under Mapping context
  
#### Create Destinations schema under Mapping context

The goal for this next step was to establish the first schema in the project, with the intention to then write the tests, and finally the views. In my previous projects I had always done this by hand, starting with the migration, then the schema, and then the context; and that was the plan for this work as well. But, plans changed, pretty dramatically. Having the time to explore and hopefully improve on my workflows and knowledge base, I spent some time researching before diving into the code. That research -unsurprisingly- led me to the the Phoenix Context [docs](https://hexdocs.pm/phoenix/contexts.html), which, led me to the [`phx.gen.html`](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Html.html) generator. I've known about generators, but I had no idea that a generator existed that did so much. 

Initially I was on the fence about using the generator, on one hand it is incredibly convenient for the framework to generate _every_ file necessary to get a schema running, tested, and browser-ready. On the other hand, having all the work done automatically can lead to problems down the road, like debugging, because I'm not as intimately familiar with generated files as I would be with files I typed out by hand. Similar to the common idea that most people learn coding principles much better if they type it all out instead of copy/pasting it from the source. Ultimately I decided to use the generator. I have enough experience with the files being generated that I didn't feel the need to write them all out by hand. I also felt that it would be beneficial to know all the necessary bases were covered, and have that as a pull request to utilize as a reference for future work that I may write without a generator.

The `phx.gen.html` generator takes four arguments. The first is the context module name, then the schema module name, third is the schema module's plural name (to be used as the table name), and, optionally, the last is the list of fields and corresponding types. 

The schema I wanted to created had a good number of fields, my generation looked like this:
{% gist https://gist.github.com/noelworden/e89ccc37e64f48c83e9cc08803d6b5f0 %}

In that generation command, `Mapping` is the context, `Destination` is the schema module, `destinations` the table name, and lastly the list of fields and their types. 

- _side note:_ That command could have been typed out as one long string, like:

    {% gist https://gist.github.com/noelworden/7097c36c33dd74de8276ccc3160fceae %}

    But by using the `\` as an escape character, the command can be broken out over several lines to enhance readability.


This one generation command created the context, schema, migration, all tests, and all views. The output showed all the files created and then instructions on two more steps needed to complete the process:

{% gist https://gist.github.com/noelworden/2a4fa7cc47d0f874eba0863efe48b90b %}

Executing the first step from the instructions created all the default the routes for this schema. For this case, the entirety of the default routes works because I intended on utilizing them all. I confirmed the routes generated as expected with this command:

`docker-compose exec web mix phx.routes`

Which returned:

{% gist https://gist.github.com/noelworden/d4d092393f44e75d7e2cd112454a93d3 %}

The second step in the instructions was to execute the migration created in the generation, creating the `destinations` table in the database. 

With the exception of including `null:false` to a few the fields in the migration, all files created by the generator were good to go. I have some work to do to the generated html files, but that will be covered by a ticket down the road. After executing the migration command, I can navigate to `localhost:4000/destinations` and I see the index page of that schema, with the ability to create a new record, as well as all the other record manipulating functionality I expect. It's actually pretty amazing how much was accomplished with that `phx.gen.html` command. There are several `mix.phx` commands, all of course do something different, and save a lot of typing. They can be found in the [Mix Tasks](https://hexdocs.pm/phoenix/mix_tasks.html#content) section of the Phoenix [documentation](https://hexdocs.pm/phoenix/overview.html), or by typing `mix phx` in the terminal.


#### Bonus - Running tests in the Docker container
My initial plan was to separate out this work, but since the tests were included in the files produced by the generator I decided to tackle this and include it in the pull request. I hit some bumps in the road when changing the database configurations in `test.exs`, to get the tests to run within the container. The process was involved enough that it warranted its own [post](https://dev.to/noelworden/a-few-caveats-to-running-elixir-tests-in-containers-and-ci-21ef). The high-level overview is that the `hostname` of the testing database needs to be `"db"` to run the tests in a container, but `"localhost"` for the tests to run in CI. The solution was to use [`System.get_env/2`](https://hexdocs.pm/elixir/System.html#get_env/2) in the database configuration logic, and add an `env` field in the CI testing configuration.

The update to the `elixir.yml` file took this step:

{% gist https://gist.github.com/noelworden/6333c8da1a337873ccfa53737ee99c86 %}

And added an environmental variable of `DB_HOSTNAME`:

{% gist https://gist.github.com/noelworden/d93f2f74101898ade7c8a0fc48e41248 %}

Then the `hostname` field of the testing database configuration found at `test.exs` went from this:

{% gist https://gist.github.com/noelworden/61aed332847a64aa3c9da89e30c80bfc %}

To this:

{% gist https://gist.github.com/noelworden/de70a4d33515f99e0dee5bd6448c8ccf %}

Where `System.get_env/2` is looking for the environmental variable set in the `elixir.yml` CI file. When that environmental variable is present (when tests are being run in the CI) it sets `hostname` to the variable's value, `"localhost"`, allowing the tests to run in the CI build. And if the environmental variable is not found, the `hostname` defaults to `"db"`, allowing for the test to properly run in a container. 

#### Closing Thoughts
With the completion of this work, I can now Create, Read, Update, and Delete (CRUD) records in the `destination` table. The index view is currently a little rough, because of the size of the table that is being displayed, but that task is for another time. I can now also execute tests in the Docker container with this command:

`docker-compose exec web mix test`

And the tests also run as part of the CI check.

Having a working schema sets me up well for my next task, which is to utilize the `seeds.exs` file to populate the new table with sample data.

[This](https://github.com/noelworden/atlas/tree/e33a6f3e2b9f9eda2b2cc6f91a16e55a0367d593) is what my repo looks like at the end of this block of work. 

#### References
- https://elixircasts.io/phoenix-contexts
- https://hexdocs.pm/phoenix/contexts.html
- https://hexdocs.pm/phoenix/mix_tasks.html#content
- https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Html.html
- https://github.community/t/workflow-fails-when-running-steps-in-a-container/16123/2

-----

This post is part of an ongoing [series](https://dev.to/noelworden/new-side-project-new-blog-series-135h). I encourage any critique, feedback, or suggestions in the comments.